#!   /usr/bin/env   python

# Copyright Creotech SA, 2015
# Author: Marek Guminski <marek.guminski@creotech.pl>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org


import sys
import time
import os
import time

from spec import *
from ad9516 import *
from adf4002 import *
from dac import *
from ptsexcept import *

import matplotlib.pyplot as plt
import pylab
import numpy

from spec_identification import *
from utilFunctions import *


"""
test04:
    Test of ADF4002 phase detector
        Communication
        Clock input connectivity
        Charge pump and its filter
    Test of AD7980 ADC
        Data readout        

"""

FIRMWARE_PATH = '/../gateware/syn/spec_top.bin'

CHIP = "IC29 (ADF4002 Phase detector)"
INPUT_TIMEOUT  = 30;


def test_muxout_spi(dev, carrier):
    # force 1 or 0 on MUXOUT
    # i can't see possibility to check them separately
    
    print "*\n*\n*"
    print "Testing %s SPI and MUXOUT (Multi-Purpose Output) pin" % CHIP
    print "*\n*\n*"
    
    print "Force VDD on muxout"
    dev = adf4002(carrier, 3)
    
    time.sleep(0.001)
    tmp = dev.gpio_get(dev.PIN_LD)
    
    # verify
    if tmp == 0:
        raise PtsError("ADF4002: Lock Detect line didn't go high. Communication lines or LD line might be corrupted.")
    else:
        print "LD pin read by the FPGA is high"
    
    msg = "Please check if PD LED on FmcDac600m12b1chaDDS is ON and type [y/n]."
    print >> sys.__stdout__, msg
    tmp = yesnoquestion( msg, INPUT_TIMEOUT )
    
    if ( tmp == 0 ):
        raise PtsError("User verification of PD LED on FmcDac600m12b1chaDDS failed. LED didn\'t turn on.")
    if ( tmp == 1 ):
        print "User verification of PLL LED on FmcDac600m12b1chaDDS succeeded"
    else:
        raise PtsError("User didn\'t verify FMC's PLL LED")


    print "Force GND on muxout"
    dev = adf4002(carrier, 7)
    
    time.sleep(0.001)
    # read value on LD pin
    tmp = dev.gpio_get(dev.PIN_LD)
    
    # verify
    if tmp > 0:
        raise PtsError("ADF4002: Lock Detect line didn't go low. Communication lines or LD line might be corrupted.")
    else:
        print "LD pin read by the FPGA is low"


    msg = "Please check if PD LED on FmcDac600m12b1chaDDS is OFF and type [y/n]."
    print >> sys.__stdout__, msg
    tmp = yesnoquestion( msg, INPUT_TIMEOUT )
    
    if ( tmp == 0 ):
        raise PtsError("User verification of PD LED on FmcDac600m12b1chaDDS failed. LED didn\'t turn OFF.")
    if ( tmp == 1 ):
        print "User verification of PLL LED on FmcDac600m12b1chaDDS succeeded"
    else:
        raise PtsError("User didn\'t verify FMC's PLL LED")

    print "*Test complete*"


def test_pd_inputs(dev,carrier, vcxo_pll, input):
    
    # sampling in the FPGA
    refclk_freq = 125e6
    # DAC output
    rfin_freq = 10e6
    # IC26 out 3
    refin_freq = 50e6

    div1 = 100
    div2 = 500

    div_rfin_freq = rfin_freq/div1
    div_refin_freq = refin_freq/div2


    MAXDIFF = 20;

    # R divider output
    if input == 0:
        input = 4
        input_str = "Reference Clock Input"
        EXPECTED_PERIOD = refclk_freq/div_refin_freq;
    # N divider output
    else: 
        input = 2
        input_str = "RF Clock Input"
        EXPECTED_PERIOD = refclk_freq/div_rfin_freq;


    print "*\n*\n*"
    print "Testing %s %s" % (CHIP, input_str)
    print "*\n*\n*"


    print "Setting up the AD9516"
    vcxo_pll.test_config();   
    time.sleep(5)

    if( vcxo_pll.gpio_get(vcxo_pll.PIN_STATUS) ):
        print "VCXO PLL locked on ref (DAC) clock"
    else:
        raise PtsError("VCXO PLL is not locked on ref clock. Can't continue PD input tests")
        return 1
        
    print "Setting the ADF4002 to divide the input clock and output it on MUXOUT pin"
    dev = adf4002(carrier, input, div1, div2 );   
    time.sleep(0.1)
    period = carrier.readl(DDS_REG_PDTEST)
       
    print "Measured signal period: %d" % period
    if abs( period - EXPECTED_PERIOD ) > MAXDIFF :
        raise PtsError("%s: period didn't match the expected value. This line might be corrupted." % input_str )
    else:
        print "ADF4002: %s signal detected." % input_str
    
    print "Repeateing with different frequency to make sure that period detector didn't freeze."
    dev = adf4002(carrier, input, div1/2, div2/2 ); 
    time.sleep(0.1)
    period = carrier.readl(DDS_REG_PDTEST)
       
    print "Measured signal period: %d" % period
    if abs( period - EXPECTED_PERIOD/2 ) > MAXDIFF :
        raise PtsError("Period didn't match the expected value. This line might be corrupted." % input_str )
    else:
        print "ADF4002: %s signal detected." % input_str
    period = carrier.readl(DDS_REG_PDTEST)

    print "*Test Complete*"
    

    
def test_cp(dev,carrier, vcxo_pll ):
   
    print "*\n*\n*"
    print "Testing %s Charge Pump" % (CHIP)
    print "*\n*\n*"


    VAR_OF_MEAN_THD = 1e3;
    MEAN_OF_VAR_THD = 100;
   
    nsamples = 1e4;
    nr = 50;
   
    print "DAC clock is connected to RFIN"
    dev = adf4002(carrier, 1, ndiv=2, rdiv=10 );   
    time.sleep(1)

    
    print "Configure the IC26 PLL to lock on DAC clock (10 MHz) and output 50 MHz (out 3) to Phase Detector REFIN"
    vcxo_pll.test_config();

    print "RFIN divided: 10 MHz/2 = 5 Mhz"
    print "REFIN divided: 50 Mhz/10 = 5 Mhz"

    time.sleep(1)
    
    print "The test: Both REF and RF PD input clocks should have constant phase shift, becaues REF is locked to DAC clock (that is split and connected to RF)"
    print "The phase should be constant, but shuould differ after PLL resynchronization"
    print "The test resynchronizes the PLL, waits for the lock and acqires some phase samples."
    print "Samples of single lock should have low deviation."
    print "Mean value of different locks should differ."
    
    mean_arr=[];
    std_arr=[];
    
    print "Data acquisition"
    for i in range( nr ):
        
        # resynchronize
        vcxo_pll.sync_outputs()

        # unfortunately only taking into account the PLL lock (below) isn't enought
        # it there is no additional delay the vairance of samples in some of the sets may turn out high
        time.sleep(0.3)

        # wait for the lock
        for i in xrange(100):
            if vcxo_pll.gpio_get(vcxo_pll.PIN_STATUS) :
                break
            else:
                time.sleep(0.1)
        else:
            raise PtsError("Timeout on VCXO PLL lock. Abording the test.")

        # acquire samples
        samples = get_adc( carrier, nsamples ); 
        # store mean value of the set
        mean_arr.append( numpy.mean( samples ) );
        # store deviation of the set
        std_arr.append( numpy.std( samples ) );

    var_of_mean = numpy.std(mean_arr)
    mean_of_var = numpy.mean(std_arr)
        
    print "Variance of mean values for each set " + str( var_of_mean ) + ". Should be bigger than: " + str( VAR_OF_MEAN_THD )
    print "Mean variance over all sets: " + str( numpy.mean(std_arr) ) + ". Should be smaller than : " + str( MEAN_OF_VAR_THD )
    
    if ( numpy.std(mean_arr) < VAR_OF_MEAN_THD ):
        raise PtsError("Resynchronization of VCXO PLL didn't cause significant Phase Detector output change. This might be caused by corrupted PD or ADC")
    
    if ( numpy.mean(std_arr) > MEAN_OF_VAR_THD ):
        raise PtsError("Measured variance ( %s ) on Phase Detector output was bigger then threshold ( %s ). This might be caused by corrupted PD or ADC" % (str( numpy.mean(std_arr) ), str( MEAN_OF_VAR_THD ) ) )
    
    
def get_adc(carrier, nr = 1e4):
    
    # set triggering to PC and set trigger to 0
    carrier.writel( DDS_REG_ADCINTERFACE, DDS_ADCINTERFACE_SOURCE() )
    
    # make the fifo empty
    while ( ( carrier.readl( DDS_REG_PD_FIFO_CSR ) & DDS_PD_FIFO_CSR_EMPTY() ) == 0 ):
        carrier.readl( DDS_REG_PD_FIFO_R0 );
    
    
    # set trigger 
    carrier.writel( DDS_REG_ADCINTERFACE, DDS_ADCINTERFACE_SOURCE() | DDS_ADCINTERFACE_TRIGGER() )
    samples=[];
    
    cnt = 0;
    
    # acquire nr of samples
    while cnt < nr:
        if ( ( carrier.readl( DDS_REG_PD_FIFO_CSR ) & DDS_PD_FIFO_CSR_EMPTY() ) == 0 ) :
            samples.append( carrier.readl( DDS_REG_PD_FIFO_R0 ) );
            cnt += 1;
    
    return samples

    
     
    
def main (card=None, default_directory='.',suite=None, configure_fpga=True):

    specid = spec_identification()[0];
    if specid < 0 :
        raise PtsCritical("Bus number of FmcDac600m12b1chadds was not found")

    # init spec
    carrier=spec( FIRMWARE_PATH, specid, configure_fpga );
    
    #configure main clock source
    sys_pll = IC9(carrier);
    
     # set DAC value to zero
    carrier.sys_osc_dac(0x7fff)
    
    # init dac
    dacinst = dac(carrier);
    # setup dac configuration (reference clock for PD)
    time.sleep(1)
    dacinst.set_ampl(1.0);
    dacinst.set_frequency(10e6);
    
    # init of vcxo pll

    cwd=os.path.dirname(os.path.realpath(__file__))
    vcxo_pll = IC26(carrier);
    vcxo_pll.write_config(load_config(cwd+'/../doc/new_ad9516_pts_test.stp'))
    
    
    # default phase detector configuration
    pd = adf4002(carrier,1)


    ################## TESTS ##################
    
    # SPI communication test
    test_muxout_spi(pd, carrier)

    # check if input clocks are present
        
    # test reference clock input line
    test_pd_inputs(pd, carrier, vcxo_pll, 0);
    #test rf clock input line
    test_pd_inputs(pd, carrier, vcxo_pll, 1);

    
    # test phase detector CP output and ADC measurements
    test_cp(pd, carrier, vcxo_pll );
    
    
    return 0
        
if __name__ == '__main__' :
    
    import argparse

    parser = argparse.ArgumentParser(description='Start test04')
    parser.add_argument('-p', action='store_true')
    args = vars(parser.parse_args())

    if args['p']:
        main(configure_fpga=False)
    else:
        main()

