#!   /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 csr
import time

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

import matplotlib.pyplot as plt
import pylab
import numpy


import calibr_box
import find_usb_tty
import calibration_box_connections

from spec_identification import *


"""
test04:
    communication + LockDetect lines

        
        
"""

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


def test_muxout_spi(dev, carrier):
    # force 1 or 0 on MUXOUT
    # i can't see possibility to check them separately
    
    # force VDD on muxout
    dev = adf4002(carrier, 3)
    
    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 high. Communication lines or LD line might be corrupted.")
        
    
    # 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.")
    

def test_pd_inputs(dev,carrier, vcxo_pll, input):
     
    CENTER = 1250;
    MAXDIFF = 20;
     
    # R divider output
    if input == 0:
        input = 4
        input_str = "Reference Clock Input"
    # N divider output
    else: 
        input = 2
        input_str = "RF Clock Input"


    dev = adf4002(carrier, input, 100, 500 );   
    time.sleep(1)
    vcxo_pll.test_config();
    time.sleep(1)
    
    
    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 test PD input tests")
        return 1
        
    mean = 0
    for i in range(10):
        mean += carrier.readl(DDS_REG_PDTEST);
        time.sleep(0.01)        
    
    mean /= 10
    
    print "Measured signal frequency: %d" % mean
    if abs( mean - CENTER ) > MAXDIFF :
        raise PtsError("ADF4002: %s signal not detected. This line might be corrupted." % input_str )
    else:
        print "ADF4002: %s signal detected." % input_str
    
def resync(vcxo_pll):
    vcxo_pll.write_reg(0x58, 1<<2 );
    vcxo_pll.write_reg(0x5A, 1 );
    time.sleep(0.5);
    vcxo_pll.write_reg(0x58, 0<<2 );
    vcxo_pll.write_reg(0x5A, 1 );
    
def test_cp(dev,carrier, vcxo_pll ):
   
    VAR_OF_MEAN_THD = 1e3;
    MEAN_OF_VAR_THD = 400;
   
    nsamples = 1e4;
    nr = 10;
   
    dev = adf4002(carrier, 1, 2, 10 );   
    time.sleep(1)
    vcxo_pll.test_config();
    time.sleep(1)
    
    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 Charge Pump and ADC test")
        return
        
    
    # if VCXO PLL is locked then phase shift is constant
    # this should produce constant voltage on PD output (random value)
    
    std_arr=[];
    
    for i in range( nr ):
        # acquire samples
        samples = get_adc( carrier, nsamples ); 
        std_arr.append( numpy.std( samples ) );
        
#     print "srednia variancji " + str( numpy.mean(std_arr) )        
    
 
    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 ) ))
    
    
    # to ensure that ADC doesn't measure noise i will change phase shift
    # PLL is still locked, so output should remain constant (but with different value)
    
    
    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 Charge Pump and ADC test")
        
    mean_arr=[];
    std_arr=[];
    
    for i in range( nr ):
        # acquire samples
        samples = get_adc( carrier, nsamples ); 
        mean_arr.append( numpy.mean( samples ) );
        std_arr.append( numpy.std( samples ) );
                
        # change phase shift
        resync(vcxo_pll)
        
    print "Variance of mean values " + str( numpy.std(mean_arr) ) + " threshold is set to: " + str( VAR_OF_MEAN_THD )
    print "Mean variance: " + str( numpy.mean(std_arr) ) + " threshold is set to: " + 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):

    if AUTOMATION:
        BOX_USB_VENDOR_ID = 0x10c4 # Cygnal Integrated Products, Inc.
        BOX_USB_DEVICE_ID = 0xea60 # CP210x Composite Device

        print("Looking for calibration box")

        box_tty = find_usb_tty.CttyUSB().find_usb_tty(BOX_USB_VENDOR_ID, BOX_USB_DEVICE_ID)

        if len( box_tty ) < 1:
            raise PtsCritical( "Can't find calibration box. Make sure that it is connected, and that CP210x driver was correctly mounted");
        
        # print("Calibration Box device: %s" % box_tty[0])
        box = calibr_box.CCalibr_box(box_tty[0])
    
        box.select_trigger_loopback(0)
        box.select_output(calibration_box_connections.connections['ref_clk'])
        
    else:
        box = 0;
    
    specid = spec_identification()[0];
    if specid < 0 :
        raise PtsCritical("Bus number of FmcDac600m12b1chadds was not found")

    # init spec
    carrier=spec( FIRMWARE_PATH, specid, True );
    
    #configure main clock source
    sys_pll = ad9516(carrier);
    
    
     # set DAC value to zero
    carrier.writel(DDS_REG_OSCDAC, 0x3FFF );
    # send value
    carrier.writel(DDS_REG_OSCDAC, 0x3FFF | ( 1<<16) );
    # wait for it to take effect
    time.sleep(0.5)
    carrier.writel(DDS_REG_OSCDAC, 0x3FFF );
    time.sleep(0.5);
    
    # 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
    vcxo_pll = ad9510(carrier, 1);
    
    
    # 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__' :
    main()

