#!   /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 ctypes import *

from ptsexcept import *

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


"""
test19: Calibration

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

GN4124_CSR = 0x0

# 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
RS232_BAUD = 57600

NB_CHANNELS = 4

AWG_SET_SLEEP = 1
SSR_SET_SLEEP = 0.05
BOX_SET_SLEEP = 1
DAC_SET_SLEEP = 0.1

ACQ_TIMEOUT = 10

MAX_FIRMWARE_RELOAD = 10

PRE_TRIG_SAMPLES = 1000
POST_TRIG_SAMPLES = 100000
NB_SHOTS = 1

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

ADC_NBITS = 16 # ADC chip is 14 bits, but shifted to 16 bits in the firmware
DAC_NBITS = 16
DAC_FS = 10 # DAC full scale range is 10V


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 + '\n'
    os.system( firmware_loader + ' ' + bitstream )

    time.sleep(2);


def disconnect_channels(fmc):
    for i in range(1,NB_CHANNELS+1):
        fmc.set_ssr(i, 0x00)
        time.sleep(SSR_SET_SLEEP)


def fmc_adc_init(spec, fmc):
    print('Initialise FMC board\n')
    fmc.__init__(spec)
    # Reset offset DACs
    fmc.dc_offset_reset()
    # Make sure all switches are OFF
    disconnect_channels(fmc)
    # Reset offset DACs
    fmc.dc_offset_reset()
    # 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 acquisition_all(fmc, spec_fmc):
    # 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
    trig_pos = fmc.get_trig_pos()
    #print('Trigger position; 0x%X')%(trig_pos)
    channels_data = spec_fmc.get_data((trig_pos<<3), ACQ_LENGTH*8)
    #channels_data = spec_fmc.get_data(0x0, ACQ_LENGTH*8)
    return channels_data


def plot_all(data, mean, ylimit):
    sample = arange(len(data)/4)
    clf()
    plot(sample, data[0::4], 'b', label='Channel 1')
    plot(sample, data[1::4], 'g', label='Channel 2')
    plot(sample, data[2::4], 'c', label='Channel 3')
    plot(sample, data[3::4], 'm', label='Channel 4')
    plot(sample, [mean[0]]*len(sample), 'r')
    plot(sample, [mean[1]]*len(sample), 'r')
    plot(sample, [mean[2]]*len(sample), 'r')
    plot(sample, [mean[3]]*len(sample), 'r')
    ylim(-ylimit-(ylimit/10.0), ylimit+(ylimit/10.0))
    grid(which='both')
    legend()
    draw()
    show()
    return 0


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

# Converts signed to two's complement hex
def signed2hex(value):
    if value < 0:
        return (((abs(value) ^ 0xffff) + 1) & 0xffff)
    else:
        return value


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


# Converts volts to digital value with half full range offset
def volt2digital(value, full_scale, nb_bit):
    digital = (value + full_scale/2) * 2**nb_bit/full_scale
    if(digital > 2**nb_bit - 1):
        digital = 2**nb_bit - 1
    if(digital < 0):
        digital = 0
    #print('volt2digital: %2.9f > %2.9f')%(value,digital)
    return int(digital)


# Converts volts to digital value
def volt2digital_without_offset(value, full_scale, nb_bit):
    if(value > (2**nb_bit)/2 - 1):
        value = (2**nb_bit)/2 - 1
    if(value < -((2**nb_bit)/2)):
        value = -((2**nb_bit)/2)
    digital = (value) * 2**nb_bit/full_scale
    #print('volt2digital: %2.9f > %2.9f')%(value,digital)
    return int(digital)


def set_offset_dac(fmc, dac_fs, dac_nbits, channel, offset_volt, dac_corr_flag=False):
    dac_v = offset_volt
    dac_d = volt2digital(dac_v,dac_fs,dac_nbits)
    #print('DAC value: 0x%X (%fV)')%(dac_d, dac_v)
    if(True == dac_corr_flag):
        fmc.set_dc_offset_corrected(channel,dac_d)
    else:
        fmc.set_dc_offset(channel,dac_d)
    time.sleep(DAC_SET_SLEEP)


def get_mean_value(adc_fs, adc_nbits, acq):
    mean_d = []
    for channel in range(1,NB_CHANNELS+1):
        mean_d.append(mean(acq[channel-1::4]))
    mean_v = [digital2volt(item,adc_fs,adc_nbits) for item in mean_d]
    return mean_v


def set_box_dac_range(box, fmc, box_out, dac_value, in_range, dac_corr_flag=False):
    # Set calibration box output
    box.select_output(box_out)
    time.sleep(BOX_SET_SLEEP)

    # Set offset DACs
    for channel in range(1,NB_CHANNELS+1):
        set_offset_dac(fmc, DAC_FS, DAC_NBITS, channel, dac_value, dac_corr_flag)

    # Set channels input range
    for channel in range(1,NB_CHANNELS+1):
        fmc.set_input_range(channel, in_range)
        time.sleep(SSR_SET_SLEEP)


def channels_mean(spec_fmc, fmc, ADC_FS, print_flag, plot_flag=False):
    # Measures value on each channel
    acq_d = acquisition_all(fmc, spec_fmc)
    acq_d = [hex2signed(item) for item in acq_d]
    acq_v = [digital2volt(item,ADC_FS,ADC_NBITS) for item in acq_d]
    mean_v = get_mean_value(ADC_FS, ADC_NBITS, acq_d)
    if(True == print_flag):
        print('\n')
        for channel in range(1,NB_CHANNELS+1):
            print('Channel %d: mean voltage = %2.9fV')%(channel, mean_v[channel-1])
    if(True == plot_flag):
        plot_all(acq_v, mean_v, ADC_FS/2.0)
    return mean_v


# Calculates ADC + input stage gain
def calc_ga(Vm1, Vm2, Vref1):
    return ((Vm2-Vm1)/Vref1)

# Calculates ADC + input stage offset
def calc_oa(Vm2, Vm3, Vm4):
    return (Vm2 + Vm3 - Vm4)

# Calculates DAC gain
def calc_gd(Vm1, Vm2, Vm3, Vref1, Vref2):
    return ((Vref1*(Vm3-Vm1))/(Vref2*(Vm1-Vm2)))

# Calculates DAC offset
def calc_od(Vm1, Vm2, Vm3, Vm4, Vref1):
    return ((Vref1*(Vm1-Vm2-Vm3+Vm4))/(Vm1-Vm2))

def dump_file_to_eeprom(fmc, filename):
    eeprom_content = []
    eeprom_addr = []
    print filename
    f = open(filename,"r+")
    for line in f:
        addr,data=line.split()
        eeprom_content.append(int(data,2))
        eeprom_addr.append(int(addr,2))
    fmc.sys_i2c_eeprom_write(eeprom_addr[0],eeprom_content)
    return 0


def main (default_directory = '.'):

    # Load firmware to FPGA
    load_firmware(default_directory)

    # 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)
    #print "AWG:%s"%awg_tty[0]
    #print "BOX:%s"%box_tty[0]
    gen = Agilent33250A(device=awg_tty[0], bauds=RS232_BAUD)
    sine = SineWaveform()
    box = calibr_box.CCalibr_box(box_tty[0])
    box_eeprom = cp210x_eeprom.CCP210x_Eeprom("%X"%BOX_USB_VENDOR_ID, "%X"%BOX_USB_PRODUCT_ID)
    box_calibr_data = box_eeprom.get_calibr_data()

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

    # Initialise fmc adc
    fmc_adc_init(spec, fmc)

    # Connect to AWG
    gen.connect()

    # Switch AWG output OFF
    gen.output = False

    # Measure FMC and carrier temperature
    print('SPEC temperature: %3.3f°C') % spec_fmc.get_temp()
    print('FMC temperature : %3.3f°C') % fmc.get_temp()

    # Open all switches, reset offset DAC to mid-scale (0V)
    for channel in range(1,NB_CHANNELS+1):
        fmc.set_input_range(channel, 'OPEN')
        fmc.set_input_term(channel, 'OFF')
    fmc.dc_offset_reset()

    adc_corr_data = {'10V':{'offset':[],'gain':[]},
                     '1V':{'offset':[],'gain':[]},
                     '100mV':{'offset':[],'gain':[]}}

    # Number of repeatition of the measurement, then the results are averaged
    REPEAT = 5

    ############################################################################
    # 10V range calibration
    ############################################################################

    print('\n10V range calibration\n----------------------------------')

    # ADC full scale is 100mV
    ADC_FS = 10.0

    # Reference voltage for calibration
    Vref1 = float(box_calibr_data['10V']) # from box cailbration data in CP2103 EEPROM
    Vref2 = 4.096  # reference voltage to set offset DAC

    #---------------------------------------------------------------------------
    # Measure 1
    # Channel input = 0V, offset DAC = 0V
    #---------------------------------------------------------------------------

    print('\nMeasurement 1: channel input = 0V, offset DAC = 0V')

    set_box_dac_range(box, fmc, 'AWG', 0.0, 'CAL_10V')

    vm1 = []
    for i in range(REPEAT):
        vm1.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm1 = [mean([row[n] for row in vm1]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm1=%02.9fV')%(channel, Vm1[channel-1])

    #---------------------------------------------------------------------------
    # Measure 2
    # Channel input = Vref = 4.096V, offset DAC = 0V
    #---------------------------------------------------------------------------

    print('\nMeasurement 2: channel input = Vref = %1.8fV, offset DAC = 0V')%(Vref1)

    set_box_dac_range(box, fmc, '10V', 0.0, '10V')

    vm2 = []
    for i in range(REPEAT):
        vm2.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm2 = [mean([row[n] for row in vm2]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm2=%02.9fV')%(channel, Vm2[channel-1])

    #---------------------------------------------------------------------------
    # Measure 3
    # Channel input = 0V, offset DAC = Vref = 4.096V
    #---------------------------------------------------------------------------

    print('\nMeasurement 3: channel input = 0V, offset DAC = Vref = %1.8fV')%(Vref2)

    set_box_dac_range(box, fmc, 'AWG', Vref2, 'CAL_10V')

    vm3 = []
    for i in range(REPEAT):
        vm3.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm3 = [mean([row[n] for row in vm3]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm3=%02.9fV')%(channel, Vm3[channel-1])

    #---------------------------------------------------------------------------
    # Measure 4
    # Channel input = Vref = 4.096, offset DAC = Vref = 4.096
    #---------------------------------------------------------------------------

    print('\nMeasurement 4: channel input = Vref = %1.8fV, offset DAC = Vref = %1.8fV')%(Vref1, Vref2)

    set_box_dac_range(box, fmc, '10V', Vref2, '10V')

    vm4 = []
    for i in range(REPEAT):
        vm4.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm4 = [mean([row[n] for row in vm4]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm4=%02.9fV')%(channel, Vm4[channel-1])

    #---------------------------------------------------------------------------
    # Calculate gain and offset parameters
    # ga = ADC + input stage gain
    # oa = ADC + input stage offset
    # gd = DAC gain
    # od = DAC offset
    #---------------------------------------------------------------------------
    print('\n10V range correction parameters\n----------------------------------')

    ga = []
    for channel in range(1,NB_CHANNELS+1):
        ga.append(calc_ga(Vm1[channel-1], Vm2[channel-1], Vref1))
        print('Channel %d ADC gain coeff: %02.9f')%(channel, ga[channel-1])

    oa = []
    for channel in range(1,NB_CHANNELS+1):
        oa.append(calc_oa(Vm2[channel-1], Vm3[channel-1], Vm4[channel-1]))
        print('Channel %d ADC offset    : %02.9f')%(channel, oa[channel-1])

    gd = []
    for channel in range(1,NB_CHANNELS+1):
        gd.append(calc_gd(Vm1[channel-1], Vm2[channel-1], Vm3[channel-1], Vref1, Vref2))
        print('Channel %d DAC gain coeff: %02.9f')%(channel, gd[channel-1])

    od = []
    for channel in range(1,NB_CHANNELS+1):
        od.append(calc_od(Vm1[channel-1], Vm2[channel-1], Vm3[channel-1], Vm4[channel-1], Vref1))
        print('Channel %d DAC offset    : %02.9f')%(channel, od[channel-1])

    #---------------------------------------------------------------------------
    # Calculate correction register values
    #---------------------------------------------------------------------------

    # 0x8000 corresponds to 1.0
    # Gain correction register is written with 1/gain * 1.0
    adc_corr_data['10V']['gain'] = [int(round((1/item)*0x8000)) for item in ga]

    # oa is in volts and has to be converted to digital raw value,
    # to be written in offset correction register
    adc_corr_data['10V']['offset'] = [-(volt2digital_without_offset(item,ADC_FS,ADC_NBITS)) for item in oa]


    ############################################################################
    # 1V range calibration
    ############################################################################

    print('\n1V range calibration\n----------------------------------')

    # ADC full scale is 100mV
    ADC_FS = 1.0

    # Reference voltage for calibration
    Vref1 = float(box_calibr_data['1V']) # from box cailbration data in CP2103 EEPROM
    Vref2 = 0.4096  # reference voltage to set offset DAC

    #---------------------------------------------------------------------------
    # Measure 1
    # Channel input = 0V, offset DAC = 0V
    #---------------------------------------------------------------------------

    print('\nMeasurement 1: channel input = 0V, offset DAC = 0V')

    set_box_dac_range(box, fmc, 'AWG', 0.0, 'CAL_1V')

    vm1 = []
    for i in range(REPEAT):
        vm1.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm1 = [mean([row[n] for row in vm1]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm1=%02.9fV')%(channel, Vm1[channel-1])

    #---------------------------------------------------------------------------
    # Measure 2
    # Channel input = Vref = 0.4096V, offset DAC = 0V
    #---------------------------------------------------------------------------

    print('\nMeasurement 2: channel input = Vref = %1.8fV, offset DAC = 0V')%(Vref1)

    set_box_dac_range(box, fmc, '1V', 0.0, '1V')

    vm2 = []
    for i in range(REPEAT):
        vm2.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm2 = [mean([row[n] for row in vm2]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm2=%02.9fV')%(channel, Vm2[channel-1])

    #---------------------------------------------------------------------------
    # Measure 3
    # Channel input = 0V, offset DAC = Vref = 0.4096V
    #---------------------------------------------------------------------------

    print('\nMeasurement 3: channel input = 0V, offset DAC = Vref = %1.8fV')%(Vref2)

    set_box_dac_range(box, fmc, 'AWG', Vref2, 'CAL_1V')

    vm3 = []
    for i in range(REPEAT):
        vm3.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm3 = [mean([row[n] for row in vm3]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm3=%02.9fV')%(channel, Vm3[channel-1])

    #---------------------------------------------------------------------------
    # Measure 4
    # Channel input = Vref = 0.4096, offset DAC = Vref = 0.4096
    #---------------------------------------------------------------------------

    print('\nMeasurement 4: channel input = Vref = %1.8fV, offset DAC = Vref = %1.8fV')%(Vref1, Vref2)

    set_box_dac_range(box, fmc, '1V', Vref2, '1V')

    vm4 = []
    for i in range(REPEAT):
        vm4.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm4 = [mean([row[n] for row in vm4]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm4=%02.9fV')%(channel, Vm4[channel-1])

    #---------------------------------------------------------------------------
    # Calculate gain and offset parameters
    # ga = ADC + input stage gain
    # oa = ADC + input stage offset
    # gd = DAC gain
    # od = DAC offset
    #---------------------------------------------------------------------------
    print('\n1V range correction parameters\n----------------------------------')

    ga = []
    for channel in range(1,NB_CHANNELS+1):
        ga.append(calc_ga(Vm1[channel-1], Vm2[channel-1], Vref1))
        print('Channel %d ADC gain coeff: %02.9f')%(channel, ga[channel-1])

    oa = []
    for channel in range(1,NB_CHANNELS+1):
        oa.append(calc_oa(Vm2[channel-1], Vm3[channel-1], Vm4[channel-1]))
        print('Channel %d ADC offset    : %02.9f')%(channel, oa[channel-1])

    gd = []
    for channel in range(1,NB_CHANNELS+1):
        gd.append(calc_gd(Vm1[channel-1], Vm2[channel-1], Vm3[channel-1], Vref1, Vref2))
        print('Channel %d DAC gain coeff: %02.9f')%(channel, gd[channel-1])

    od = []
    for channel in range(1,NB_CHANNELS+1):
        od.append(calc_od(Vm1[channel-1], Vm2[channel-1], Vm3[channel-1], Vm4[channel-1], Vref1))
        print('Channel %d DAC offset    : %02.9f')%(channel, od[channel-1])

    #---------------------------------------------------------------------------
    # Calculate correction register values
    #---------------------------------------------------------------------------

    # 0x8000 corresponds to 1.0
    # Gain correction register is written with 1/gain * 1.0
    adc_corr_data['1V']['gain'] = [int(round((1/item)*0x8000)) for item in ga]

    # oa is in volts and has to be converted to digital raw value,
    # to be written in offset correction register
    adc_corr_data['1V']['offset'] = [-(volt2digital_without_offset(item,ADC_FS,ADC_NBITS)) for item in oa]


    ############################################################################
    # 100mV range calibration
    ############################################################################

    print('\n100mV range calibration\n----------------------------------')

    # ADC full scale is 100mV
    ADC_FS = 0.1

    # Reference voltage for calibration
    Vref1 = float(box_calibr_data['100mV']) # from box cailbration data in CP2103 EEPROM
    Vref2 = 0.04096  # reference voltage to set offset DAC

    #---------------------------------------------------------------------------
    # Measure 1
    # Channel input = 0V, offset DAC = 0V
    #---------------------------------------------------------------------------

    print('\nMeasurement 1: channel input = 0V, offset DAC = 0V')

    set_box_dac_range(box, fmc, 'AWG', 0.0, 'CAL_100mV')

    vm1 = []
    for i in range(REPEAT):
        vm1.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm1 = [mean([row[n] for row in vm1]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm1=%02.9fV')%(channel, Vm1[channel-1])

    #---------------------------------------------------------------------------
    # Measure 2
    # Channel input = Vref = 0.04096V, offset DAC = 0V
    #---------------------------------------------------------------------------

    print('\nMeasurement 2: channel input = Vref = %1.8fV, offset DAC = 0V')%(Vref1)

    set_box_dac_range(box, fmc, '100mV', 0.0, '100mV')

    vm2 = []
    for i in range(REPEAT):
        vm2.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm2 = [mean([row[n] for row in vm2]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm2=%02.9fV')%(channel, Vm2[channel-1])

    #---------------------------------------------------------------------------
    # Measure 3
    # Channel input = 0V, offset DAC = Vref = 0.04096V
    #---------------------------------------------------------------------------

    print('\nMeasurement 3: channel input = 0V, offset DAC = Vref = %1.8fV')%(Vref2)

    set_box_dac_range(box, fmc, 'AWG', Vref2, 'CAL_100mV')

    vm3 = []
    for i in range(REPEAT):
        vm3.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm3 = [mean([row[n] for row in vm3]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm3=%02.9fV')%(channel, Vm3[channel-1])

    #---------------------------------------------------------------------------
    # Measure 4
    # Channel input = Vref = 0.04096, offset DAC = Vref = 0.04096
    #---------------------------------------------------------------------------

    print('\nMeasurement 4: channel input = Vref = %1.8fV, offset DAC = Vref = %1.8fV')%(Vref1, Vref2)

    set_box_dac_range(box, fmc, '100mV', Vref2, '100mV')

    vm4 = []
    for i in range(REPEAT):
        vm4.append(channels_mean(spec_fmc, fmc, ADC_FS, False))
    Vm4 = [mean([row[n] for row in vm4]) for n in range(NB_CHANNELS)]
    for channel in range(1,NB_CHANNELS+1):
        print('Channel %d: Vm4=%02.9fV')%(channel, Vm4[channel-1])

    #---------------------------------------------------------------------------
    # Calculate gain and offset parameters
    # ga = ADC + input stage gain
    # oa = ADC + input stage offset
    # gd = DAC gain
    # od = DAC offset
    #---------------------------------------------------------------------------
    print('\n100mV range correction parameters\n----------------------------------')

    ga = []
    for channel in range(1,NB_CHANNELS+1):
        ga.append(calc_ga(Vm1[channel-1], Vm2[channel-1], Vref1))
        print('Channel %d ADC gain coeff: %02.9f')%(channel, ga[channel-1])

    oa = []
    for channel in range(1,NB_CHANNELS+1):
        oa.append(calc_oa(Vm2[channel-1], Vm3[channel-1], Vm4[channel-1]))
        print('Channel %d ADC offset    : %02.9f')%(channel, oa[channel-1])

    gd = []
    for channel in range(1,NB_CHANNELS+1):
        gd.append(calc_gd(Vm1[channel-1], Vm2[channel-1], Vm3[channel-1], Vref1, Vref2))
        print('Channel %d DAC gain coeff: %02.9f')%(channel, gd[channel-1])

    od = []
    for channel in range(1,NB_CHANNELS+1):
        od.append(calc_od(Vm1[channel-1], Vm2[channel-1], Vm3[channel-1], Vm4[channel-1], Vref1))
        print('Channel %d DAC offset    : %02.9f')%(channel, od[channel-1])

    #---------------------------------------------------------------------------
    # Calculate correction register values
    #---------------------------------------------------------------------------

    # 0x8000 corresponds to 1.0
    # Gain correction register is written with 1/gain * 1.0
    adc_corr_data['100mV']['gain'] = [int(round((1/item)*0x8000)) for item in ga]

    # oa is in volts and has to be converted to digital raw value,
    # to be written in offset correction register
    adc_corr_data['100mV']['offset'] = [-(volt2digital_without_offset(item,ADC_FS,ADC_NBITS)) for item in oa]


    ############################################################################
    # Write correction data tp file
    ############################################################################
    out_filename = "test19_corr_data.txt"
    f_out = open(out_filename, 'w')
    # 10V range
    print "10V range"
    for item in adc_corr_data['10V']['offset']:
        print '0x%.4X,yes'%(signed2hex(item))
        f_out.write('0x%.4X,yes\n'%(signed2hex(item)))
    for item in adc_corr_data['10V']['gain']:
        print '0x%.4X,yes'%(signed2hex(item))
        f_out.write('0x%.4X,yes\n'%(signed2hex(item)))
    # 1V range
    print "1V range"
    for item in adc_corr_data['1V']['offset']:
        print '0x%.4X,yes'%(signed2hex(item))
        f_out.write('0x%.4X,yes\n'%(signed2hex(item)))
    for item in adc_corr_data['1V']['gain']:
        print '0x%.4X,yes'%(signed2hex(item))
        f_out.write('0x%.4X,yes\n'%(signed2hex(item)))
    # 100mV range
    print "100mV range"
    for item in adc_corr_data['100mV']['offset']:
        print '0x%.4X,yes'%(signed2hex(item))
        f_out.write('0x%.4X,yes\n'%(signed2hex(item)))
    for item in adc_corr_data['100mV']['gain']:
        print '0x%.4X,yes'%(signed2hex(item))
        f_out.write('0x%.4X,yes\n'%(signed2hex(item)))

    ############################################################################
    # Format FMC EEPROM data
    ############################################################################
    os.system("python ../../fmceeprom/python/fmc_eeprom_gen.py -i ../../fmceeprom/python/eeprom_input.xml -o test19_formatted_data.txt -r test19_corr_data.txt")

    ############################################################################
    # Write data to FMC EEPROM
    ############################################################################
    in_filename = "test19_formatted_data.txt"
    eeprom_content = []
    eeprom_addr = []
    f_in = open(in_filename,"r+")
    for line in f_in:
        addr,data=line.split()
        eeprom_content.append(int(data,2))
        eeprom_addr.append(int(addr,2))
    fmc.sys_i2c_eeprom_write(eeprom_addr[0],eeprom_content)

    ############################################################################
    # Verify EEPROM data
    ############################################################################
    eeprom_content_read = fmc.sys_i2c_eeprom_read(eeprom_addr[0],len(eeprom_content))
    for i in len(eeprom_content):
        if eeprom_content[i] != eeprom_content_read[i]:
            print "[ERROR] eeprom data corruption @ addr:%d expect:0x%.2X read:0x%.2X"%(i, eeprom_content[i], eeprom_content_read[i])


    # Open all switches, reset offset DAC to mid-scale (0V)
    for channel in range(1,NB_CHANNELS+1):
        fmc.set_input_range(channel, 'OPEN')
        fmc.set_input_term(channel, 'OFF')
    fmc.dc_offset_reset()

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