

import sys
import random
import time
import csr
import i2c
import math
import os
import signal

from ctypes import *
from ptsexcept import *

from regs_lib import *
from ad9510_lib import *

#     print massage that have to be logged
def log( msg ):
    print msg

#     print question for user - doesn't need to be logged
def info( msg ):
    print msg
    

RF_MODE_MASTER = 0;
RF_MODE_SLAVE = 1;


class FMCdds:

    speclib_path = '/../software/spec-sw/tools/libspec.so';
    BASE_ADDR = 0x80000;
    UIDEL = 1;

    CONF_AD9516 = ( [ [0x0000, 0x99],
            [0x0001, 0x00],
            [0x0002, 0x10],
            [0x0003, 0xC3],
            [0x0004, 0x00],
            [0x0010, 0x7C],
            [0x0011, 0x05],
            [0x0012, 0x00],
            [0x0013, 0x0C],
            [0x0014, 0x12],
            [0x0015, 0x00],
            [0x0016, 0x05],
            [0x0017, 0x00],
            [0x0018, 0x07],
            [0x0019, 0x00],
            [0x001A, 0x00],
            [0x001B, 0x20],
            [0x001C, 0x02],
            [0x001D, 0x00],
            [0x001E, 0x00],
            [0x001F, 0x0E],
            [0x00A0, 0x01],
            [0x00A1, 0x00],
            [0x00A2, 0x00],
            [0x00A3, 0x01],
            [0x00A4, 0x00],
            [0x00A5, 0x00],
            [0x00A6, 0x01],
            [0x00A7, 0x00],
            [0x00A8, 0x00],
            [0x00A9, 0x01],
            [0x00AA, 0x00],
            [0x00AB, 0x00],
            [0x00F0, 0x08],
            [0x00F1, 0x0A],
            [0x00F2, 0x0A],
            [0x00F3, 0x0A],
            [0x00F4, 0x0A],
            [0x00F5, 0x0A],
            [0x0140, 0x46],
            [0x0141, 0x46],
            [0x0142, 0x46],
            [0x0143, 0x46],
            [0x0190, 0x00],
            [0x0191, 0x80],
            [0x0192, 0x00],
            [0x0193, 0xBB],
            [0x0194, 0x00],
            [0x0195, 0x00],
            [0x0196, 0x00],
            [0x0197, 0x00],
            [0x0198, 0x00],
            [0x0199, 0x11],
            [0x019A, 0x00],
            [0x019B, 0x11],
            [0x019C, 0x20],
            [0x019D, 0x00],
            [0x019E, 0x11],
            [0x019F, 0x00],
            [0x01A0, 0x11],
            [0x01A1, 0x20],
            [0x01A2, 0x00],
            [0x01A3, 0x00],
            [0x01E0, 0x01],
            [0x01E1, 0x02],
            [0x0230, 0x00],
            [0x0231, 0x00],
            [0x0232, 0x00] ]
        )

    
    def __init__(self, firmware, bus = -1):
        
        self.cwd = os.path.dirname(__file__)
        
        self.bus = bus;
        self.firmware = firmware;
        
        self.speclib = CDLL(self.cwd + self.speclib_path);
        self.speclib.spec_readl.restype = c_uint32;
        
        log( "Initialising SPEC board..." )
        self.card = self.speclib.spec_open( c_int(self.bus), c_int(-1) );
        if(self.card < 1):
            raise PtsCritical("SPEC enumeration failed");
        
    
        log("Firmware loading")
        if( self.speclib.spec_load_bitstream(c_voidp( self.card ), c_char_p( self.cwd + self.firmware ) ) == 0 ):
            raise PtsCritical("Firmware loader failure");
        
        time.sleep(5);
        
        self.mode= RF_MODE_MASTER
        
        self.sdbid = self.speclib.spec_readl( c_void_p(self.card), c_uint32(0) );
        self.gwid = self.readl(DDS_REG_GW);
        self.gwid = self.readl(DDS_REG_GW);
        self.get_fmc_present();
        
#         Set SPI/I2C pins to inactive state
        self.writel( DDS_REG_CR, 0);
        self.gpio_set( DDS_GPIOR_PLL_SYS_RESET_N(), 1);
        self.gpio_set( DDS_GPIOR_PLL_SYS_CS_N(), 1);
        self.gpio_set( DDS_GPIOR_PLL_VCXO_CS_N(), 1);
        self.gpio_set( DDS_GPIOR_PLL_VCXO_FUNCTION(), 1);
        
#         self.ad9516_init();
        self.dds=dds(self);
#         self.dds.update_hw();
        self.gpio_set(DDS_GPIOR_ADF_LE(), 1);
    
        
    def reset_core(self):
        self.writel( DDS_REG_RSTR, DDS_RSTR_PLL_RST() )
        time.sleep(0.001)
        self.writel( DDS_REG_RSTR, 0 )
        time.sleep(0.001)
        self.writel( DDS_REG_RSTR, DDS_RSTR_SW_RST() )
        time.sleep(0.001)
        self.writel( DDS_REG_RSTR, 0 )
        time.sleep(1)

    
    def ad9516_init(self):
    
        # remove reset signal
        self.gpio_set( DDS_GPIOR_PLL_SYS_SYNC_N(), 1);
        
        # set reset signal
        self.gpio_set( DDS_GPIOR_PLL_SYS_RESET_N(), 0);
        time.sleep(1);
        
        # remove reset signal
        self.gpio_set( DDS_GPIOR_PLL_SYS_RESET_N(), 1);
        time.sleep(1);
        
        # set communication method
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0, 0x99);
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x232, 1);

        time.sleep(1);
 
        # verify SPI communication with AD9516
        if( self.ad951x_read_reg(DDS_GPIOR_PLL_SYS_CS_N(), 0x3) != 0xc3):   
            raise PtsCritical("AD9516 PLL not responding! SPI communication lines might be faulty.")
        else:    
            print "Got respond from AD9516 PLL"
        
        # load configuration
        for i in range( len( self.CONF_AD9516 ) ):
            self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), self.CONF_AD9516[i][0], self.CONF_AD9516[i][1]);
           
        # update loaded registers
        self.ad951x_write_reg(DDS_GPIOR_PLL_SYS_CS_N(), 0x232, 0);
        time.sleep(0.001);
        self.ad951x_write_reg(DDS_GPIOR_PLL_SYS_CS_N(), 0x232, 1);
        
        # delay
        time.sleep(3);
#         print "Readback"
#         for i in range( len( self.CONF_AD9516 ) ):
#             print "a:%x d:%x" % ( self.CONF_AD9516[i][0], self.ad951x_read_reg( DDS_GPIOR_PLL_SYS_CS_N(), self.CONF_AD9516[i][0] ) )
        
        # check if referency clock clock frequency is higher then 1 MHz (if osc is correctly placed)
        if(self.ad951x_read_reg(DDS_GPIOR_PLL_SYS_CS_N(), 0x1f) & (1 << 1)):
            print "AD9516 reference clock detected"
        else:
            raise PtsCritical("AD9516: reference frequency lower then 1 MHz! OSC1/OSC2 or their connections to AD9516 might be broken.")
        
        # wait for PLL lock
        for i in range(10):
            if(self.ad951x_read_reg(DDS_GPIOR_PLL_SYS_CS_N(), 0x1f) & (1 << 0)):
                print "AD9516 PLL locked"
                break    
            if(i == 9):
                raise PtsCritical("AD9516 PLL does NOT lock. This might be caused by placing wrong OSC1/OSC2 oscillator.")
        
        # software sync all outputs
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x230, 1);
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x232, 1);
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x230, 0);
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x232, 1);
        
        if( (self.readl( DDS_REG_FPGACTRL ) & DDS_FPGACTRL_MAINPLL() ) == 0 ):
            raise PtsCritical("PLL in FPGA did not lock on clock signal from AD9516. Clock line might be corrupted.") 
        
    def ad9516_meas_clk(self):
        
        # configure test
        samples=50;
        delay=0.25
        
        # set DAC value to zero
        self.writel(DDS_REG_OSCDAC, 0 );
        # send value
        self.writel(DDS_REG_OSCDAC, 0 | ( 1<<16) );
        # wait for it to take effect
        time.sleep(0.5)
        self.writel(DDS_REG_OSCDAC, 0 );
        time.sleep(0.5)
        
#         take few measurements of received frequency
        min = 0
        for i in range(samples):
            min += self.readl(DDS_REG_CLK1);
            time.sleep(delay)
#         average result
        min /= samples;
        
        # set DAC value to max
        self.writel(DDS_REG_OSCDAC, 0xffff );
        # send value
        self.writel(DDS_REG_OSCDAC, 0xffff | ( 1<<16) );
        # wait for it to take effect
        time.sleep(delay)
        self.writel(DDS_REG_OSCDAC, 0 );
        time.sleep(delay)
        
        # take few measurements of received frequency
        max = 0
        for i in range(samples):
            max += self.readl(DDS_REG_CLK1);
            time.sleep(delay)
        # average result
        max /= samples;
        
        # set medium value
        self.writel(DDS_REG_OSCDAC, 0x00ff );
        self.writel(DDS_REG_OSCDAC, 0x00ff | ( 1<<16) );
        time.sleep(delay)
        self.writel(DDS_REG_OSCDAC, 0x00ff );
        time.sleep(delay)
        
        # force float type
        min = float(min)
        max = float(max)
        
        
        # define control range
        # i am changing sampling clock frequency
        # so control direction is swapped
        control = (min-max)/((min+max)/2);
        print "Measured AD9516 frequency min:%d max:%d" % ( min, max )
        print "Measured AD9516 frequency control:%f" % control
        
        if(max > min):
            raise PtsWarning("Voltage controlled oscillator polarity might be wrong")

        if(control < 0.00000001 ):
            raise PtsError("Oscillator control range is too low")
        
    def ad9516_test_ld(self):
        # connect LD output to VCC
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x01A, 0x030 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x232, 1);
        time.sleep(0.5)
        if( self.gpio_get(DDS_GPIOR_PLL_SYS_LD()) == 0 ):
            raise PtsCritical("FPGA did not correct value of AD9516 LD pin. This might be caused by interrupted trace.")
        
        print "Please check if PLL LED on FmcDac600m12b1chaDDS is on."
        tmp = raw_input();
        
        if ( (tmp.upper() == 'N') or (tmp.upper() == 'NO') ):
            raise PtsError("User verification of PLL LED on FmcDac600m12b1chaDDS failed")
        else:
            print "User verification of PLL LED on FmcDac600m12b1chaDDS succeeded"
        
        # connect LD output to GND
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x01A, 0x020 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_SYS_CS_N(), 0x232, 1);
        time.sleep(0.5)
        if( self.gpio_get(DDS_GPIOR_PLL_SYS_LD()) != 0 ):
            raise PtsCritical("FPGA did not correct value of AD9516 LD pin. This might be caused by interrupted trace.")
        
        print "Please check if PLL LED on FmcDac600m12b1chaDDS is off."
        tmp = raw_input();
               
        if ( (tmp.upper() == 'N') or (tmp.upper() == 'NO') ):
            raise PtsError("User verification of PLL LED on FmcDac600m12b1chaDDS failed")
        else:
            print "User verification of PLL LED on FmcDac600m12b1chaDDS succeeded"
    
    def ad9516_test_reset(self):
        #should be performed after ad9516 init
        # FPGA PLL must be locked if this method didnt fail
        self.ad9516_init();
        
        # reset configuration to default
        self.gpio_set( DDS_GPIOR_PLL_SYS_RESET_N(), 0 )
        
        if( (self.readl( DDS_REG_FPGACTRL ) & DDS_FPGACTRL_MAINPLL() ) != 0 ):
            raise PtsCritical("AD9516 didn\'t reset after receive reset signal. This line might be corrupted.") 
        
        print "Reinitializing AD5916 after Reset"
        
        self.ad9516_init()
        
                
        
    def ad9510m_init(self):

        self.gpio_set( DDS_GPIOR_PLL_VCXO_FUNCTION(), 0);
        self.gpio_set( DDS_GPIOR_PLL_VCXO_FUNCTION(), 1);

        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N(), 0,
            ( SDO_INACTIVE(0) | LSB_FIRST(0) | SOFT_RESET(0) | LONG_INSTRUCTION(1) )
            );

        print "AD9510 configuration start.\n"

        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
            0,
            ( SDO_INACTIVE(0) | LSB_FIRST(0) | SOFT_RESET(0) | LONG_INSTRUCTION(1) )
            );

        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
            0x5A,
            UPDATE_REGISTERS(1)
            );

#         # page 29: A and B Counters
#         # in FD mode A counter is not used
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
            0x04,
            A_COUNTER(0)
            );

        # page 29: A and B Counters
        # in FD mode B counter can be bypassed by writing in 0x06 - B_BYPASS
        # bypass is disabled here
        # fVCO = (fREF/R) *P*B)
        # 500 = 10M/1 * 1 * 50
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
            0x05,
    
            B_COUNTER_MSB(0) # def
            );
    
        if ( self.mode == RF_MODE_MASTER ):
                self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                    0x06,
                    B_COUNTER_LSB(0) # def
                    );
        
        else:
            # on slave B counter is used to divide OSC clk
            # since prescaller is enabled
            # B counter needs to count to 25
            # N = PxB = 2x25 = 50
            self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x06,
                B_COUNTER_LSB(25)
                );
        
        
        
        # default
        # dunno what this is
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x07,
                ( LOR_DEL(0) | LOR_EN(0) )
                );
        
        # default
        # dunno what should be here
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x08,
                ( PFD_POLARITY(0) | PLL_MUX_ON_STATUS(0) | CP_MODE(0) )
                );
        
        #    default
        #    CP current and counters reset
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x09,
                ( CP_CURRENT(0) | RESET_R(0) | RESET_N(0) | RESET_ALL_CNTRS(0) )
                );
        
        if ( self.mode == RF_MODE_MASTER ):
                # only power down has different default value (1)
                self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                        0x0A,
                        ( B_BYPASS(0) | PRESCALLER(0) | POWER_DOWN(1) )
                        );
        
        else:
            # PLL enabled in FD mode (implicated by prescaller value)
            # B is NOT bypassed
            self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                    0x0A,
                    ( B_BYPASS(0) | PRESCALLER(1) | POWER_DOWN(0) )
                    );
        
        
        
        # default
        #    R divider disabled
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x0B,
                R_DIVIDER_MSB(0)
                );
        
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x0C,
                R_DIVIDER_LSB(0)
                );
        
        #    default
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x0D,
                ( DIGIT_LOCK_DET_EN(0) | DIGIT_LOCK_DET_WND(0) | ANTIBACKSLASH_PLS_WDTH(0) )
                );
        
        # delays
        # left default
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x34,1 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x35,0 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x36,0 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x37,4 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x38,1 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x39,0 );
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x3A,0 );
        
        # all outputs are enabled if load is connected
        # all default
        # out0
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x3C, 0x08 );
        # out1
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x3D, 0x08 );
        # out2
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x3E, 0x08 );
        # out3
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x3F, 0x08 );
        
        # out4 lvds - off
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x40, 0x02 );
        # out5 lvds - off
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x41, 0x02);
        # out6 lvds - on
        # LVDS, power ON, default current
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x42, CMOS_OUTPUT_POWER( 0 ) | CMOS_OUTPUT_LEVEL( 1 ) | LOGIC_SEL( 0 )  );
        # out7 lvds - off
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x43, 0x03 );
        
        ####### important!!!!!!!!!!!!!!!
        if( self.mode == RF_MODE_MASTER ):
            # default
            self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                    0x45,
                    ( SEL_CLK_IN(1) | CLK1_PD(0) | CLK2_PD( 0 ) |
                        CLK_PLL_PD( 0 ) | REFIN_PD(0) | CLKS_IN_PD(0) )
                    );
        
        else:
            self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                    0x45,
                    ( SEL_CLK_IN(0) | CLK1_PD(0) | CLK2_PD( 0 ) |
                        CLK_PLL_PD( 0 ) | REFIN_PD(0) | CLKS_IN_PD(0) )
                    );
        
        
        
        # output 3
        # default
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x4E, 0x33 );
        
        # bypass is different then default
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x4F,
                ( BYPASS(1) | NO_SYNC(0) | FORCE(0) | START_HL(0) | PHASE_OFFSET(0) )
                );
        
        
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x5A,
                UPDATE_REGISTERS(1)
                );
        
        
        # output 6
        # connected directly to FPGA
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N, 0x54, 0x00 );
        
        # divider bypassed
        self.ad951x_write_reg( DDS_GPIOR_PLL_VCXO_CS_N,
                0x55,
                ( BYPASS(1) | NO_SYNC(0) | FORCE(0) | START_HL(0) | PHASE_OFFSET(0) )
                );
        

    
    def ad951x_read_reg( self, cs_pin, reg ):
        rval = self.spi_txrx( cs_pin, 24, ( (reg & 0xfff) << 8) | (1<<23) )
        return rval & 0xff;

    
    def ad951x_write_reg(self, cs_pin, reg, val):
        self.spi_txrx( cs_pin, 24, ((reg & 0xfff) << 8) | val );   

    
    def ledr(self, val):
        self.writel(DDS_REG_TEST_COMM, DDS_TEST_COMM_LED_R() )
    
    def ledg(self, val):
        self.writel(DDS_REG_TEST_COMM, DDS_TEST_COMM_LED_G() )
    
    
    def get_fmc_present(self):
#         fmcpresent is pulled low on FMC board
#         the same line is pulled high on spec
#         if line is low then fmc must be connected 
        self.fmc_present = False if ( self.readl(DDS_REG_GPIOR) & DDS_GPIOR_FMC_PRESENT() ) else True;
     
    
    

#     SPI communication        
    def spi_txrx(self, cs_pin, nbits, value ):
#         10 us
        DEL = 0.00001
        
        time.sleep(DEL)
        self.gpio_set(DDS_GPIOR_PLL_SCLK(), 0)
        time.sleep(DEL)
        self.gpio_set(cs_pin, 0)
        time.sleep(DEL)
        self.gpio_set(DDS_GPIOR_PLL_SCLK(), 0)
        
        rv = 0
        
        for i in range(nbits):
            value <<= 1;
            self.gpio_set(DDS_GPIOR_PLL_SDIO(), value & (1<<nbits));
            time.sleep(DEL)
            self.gpio_set(DDS_GPIOR_PLL_SCLK(), 1);
            time.sleep(DEL)
            rv <<= 1;
            rv |= 1 if self.gpio_get(DDS_GPIOR_PLL_SDIO()) else 0
            time.sleep(DEL)
            self.gpio_set(DDS_GPIOR_PLL_SCLK(), 0);
            time.sleep(DEL)
            
        self.gpio_set( cs_pin, 1);

        return rv



######################## LOWEST LEVEL FUNCTIONS ####################################

    
    def gpio_set(self, pin, val ):
        # set value on single gpio pin
        curr = self.readl(DDS_REG_GPIOR)
        if( val ):
            self.writel(DDS_REG_GPIOR, pin|curr)
        else:
            self.writel(DDS_REG_GPIOR, curr&(~ pin) )
        
            
    def gpio_get(self, pin):
        # get current value of gpio pin
        return self.readl(DDS_REG_GPIOR) & pin;


    def writel(self, addr, data ):
#         basic write function from speclib
#   void spec_writel(void *card, uint32_t data, uint32_t addr)        
        self.speclib.spec_writel( c_void_p(self.card), c_uint32(data), c_uint32(self.BASE_ADDR + addr) );
    
    def readl(self, addr ):
#         basic read function from speclib
#         uint32_t spec_readl(void *card, uint32_t addr)
        return self.speclib.spec_readl( c_void_p(self.card), c_uint32(self.BASE_ADDR + addr) );
    
   
class dds(object):
    def __init__(self,parent):
        self.en = 1;
        self.sine = 1;
        self.ampl = 0x3FFF;
        self.sine_freq = 10e6
        self.sample_rate = 500e6
        self.phase_shift_gain = 0;
        self.parent = parent;
    
    def update_hw(self):
#         custom dds control
#         amplitude control : 3ff is max ampliture
        tmp = DDS_DAC_AMPL_MASK() & self.ampl;
        
#         enable/disable output
        if( self.en ):
            tmp |= DDS_DAC_EN();
#         constant signal of amplitude value is outputed 
#         if sine is 0 and output is enabled
        if( self.sine ):
            tmp |= DDS_DAC_SINE();
        
#         registers updated in fpga
        self.parent.writel(DDS_REG_DAC, tmp );
#         gain is used to control phase shift influence on signal freq
        self.parent.writel(DDS_REG_GAIN, self.phase_shift_gain )
        
#         default freqency setup
#         the bigger phase_shift_gain the more it might be offset by 
#         phase shift detected by external phase detector
        tune = (1<<45)/self.sample_rate;
        tune *= self.sine_freq;
        
        tune = int( tune );
      
#       registers updated in fpga
        self.parent.writel(DDS_REG_FREQ_HI, (tune >> 32) & 0xffffffff );
        self.parent.writel(DDS_REG_FREQ_LO, tune & 0xffffffff);
                
        