[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gnuradio-patch
Subject:    [Patch-gnuradio] DBS-RX C++ Driver
From:       Gregory W Heckler <gheckler () pop500 ! gsfc ! nasa ! gov>
Date:       2007-04-10 14:00:17
Message-ID: 461B9871.802 () pop500 ! gsfc ! nasa ! gov
[Download RAW message or body]

Attached is the C++ driver for the DBS-RX daughtercard. Use and enjoy!

["db_dbs_rx.h" (text/plain)]

/* -*- c++ -*- */
/*
 * Copyright 2007 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

/*
 * ----------------------------------------------------------------------
 * DB-SRX Daughter-card Interface
 *
 * This class allows a user to control the configuration of the DB-SRX
 * daughter-card. A handle to a usrp_standard_rx object, and which RX
 * board this object will control is passed to the constructor. The
 * DB-SRX is initialized with 0 dB of gain. The tune() function allows a
 * user to command the LO downconvert frequency of the Max2118 chip. The
 * gain() functions allows control of the 105 dB of gain on the
 * DB-SRX daughtercard. Further user gain control is provided via the 
 * rf_gain() and if_gain() functions. The rf_gain() function allows a
 * user to control the gain, prior to mixing, from 0 to 56 dB. The
 * if_gain() function provides a further 49 dB of gain. The Max2118
 * also contains a programmable bandwidth low-pass filter after the
 * down-conversion. The low-pass filter's 3 dB cutoff point can be
 * changed from 1 to 33 MHz. The effect total bandwidth of the DB-SRX
 * (assuming the frequency of interest is mixed to DC) is twice the 3 dB
 * cutoff frequency. Appropriate accessor methods are also included.
 * ----------------------------------------------------------------------
 */

#include <usrp_standard.h>
#include <usrp_i2c_addr.h>
#include <fpga_regs_standard.h>

class db_dbs_rx
{

	private:

			/* Max2118 Downcovert chip parameters */
			int n;
			int div2;
			int osc;
			int cp;
			int r;
			int r_int;
			int fdac;
			int m;
			int adl;
			int ade;
			int diag;

			/* Gain stages, from 0 to 104 dB of gain */
			int gc1;			/* GC1 = 0 to 56 dB gain,   controllable via DAC */
			int gc2;			/* GC2 = 0 to 24 dB gain,   controllable via I2C */
			int dl;				/* dl  = 0 OR 4.58 dB gain, controllable via I2C */
			double pga;			/* pga = 0 to 20 dB gain,   controllable via DAC */

			/* Board RXA or Board RXB */
			int which;

			/* I2C Address for this daughtercard */
			int I2C_ADDR;

			/* Reference Clock Register */
			int REFCLK_REG;
			int refclk_divisor;

			double db_freq;		/* LO frequency (Hz) */
			double db_bw;		/* Lowpass filter bandwidth (Hz) */
			double db_gain;		/* Total gain (0 to 105 dB) */
			double db_rf_gain;	/* RF gain (0 to 56 dB) */
			double db_if_gain;	/* IF gain (0 to 49 dB) */

			usrp_standard_rx *urx; /* Pointer to usrp_standard_rx object */

	protected:

			/*! Write to MAX2118 Registers */
			void write_MAX2118();					
			/*! Send a single register */
			bool send_reg(int _reg);				
			/*! Read PLL ADC */
			int  read_adc();						
			/*! Set clock frequency into the Max2118 */
			void set_refclk_divisor(int _div);		
			/*! Enable the refclk */
			void enable_refclk(bool _enable);	
			/*! Return commanded refclk frequency */
			double refclk_freq();					
			/*! Not sure what this is for */
			void bypass_adc_buffers(bool _bypass);	

			/*! Write the GC1 value (FPGA) */
			void set_gc1(int _gc1);					
			/*! Write the GC2 value (register 5) */
			void set_gc2(int _gc2);					
			/*! Write the PGA value (FPGA) */
			void set_pga(double _pga);				
			
			/*! Write the N	   value (registers 0 and 1) */
			void set_n(int _n);						
			/*! Write the DIV2  value (register 0) */
			void set_div2(int _div2);				
			/*! Write the OSC   value (register 2) */
			void set_osc(int _osc);					
			/*! Write the CP    value (register 2) */
			void set_cp(int _cp);					
			/*! Write the R     value (register 2) */
			void set_r(int _r);						
			/*! Write the FDAC  value (register 3) */
			void set_fdac(int _fdac);				
			/*! Write the M     value (register 4) */
			void set_m(int _m);						
			/*! Write the DL    value (register 4) */
			void set_dl(int _dl);					
			/*! Write the ADE   value (register 4) */
			void set_ade(int _ade);					
			/*! Write the ADL   value (register 4) */
			void set_adl(int _adl);					
			/*! Write the DIAG  value (register 5) */
			void set_diag(int _diag);				

	public:

			/*! Constructor */
			db_dbs_rx(usrp_standard_rx *_urx, int _which);		

			/*! Destructor */
			~db_dbs_rx();										

			/* Accessors */

			/*!	Return the LO frequency	*/
			double freq()			const {return db_freq;}		
			/*!	Return the lowpass filter bandwidth	*/
			double bw()				const {return db_bw;}		
			/*!	Return the total gain */
			double gain()			const {return db_gain;}		
			/*!	Return the RF gain */
			double rf_gain()		const {return db_rf_gain;}	
			/*!	Return the IF gain */
			double if_gain()		const {return db_if_gain;}	
			/*!	Return the minimum gain */
			double min_gain()		const {return 0.0;}			
			/*!	Return the maximum gain */
			double max_gain()		const {return 105.0;}		
			/*!	Return the minimum RF gain */
			double min_rf_gain()	const {return 0.0;}			
			/*!	Return the maximum RF gain */
			double max_rf_gain()	const {return 56.0;}		
			/*!	Return the minimum IF gain */
			double min_if_gain()	const {return 0.0;}			
			/*!	Return the maximum IF gain */
			double max_if_gain()	const {return 49.0;}		

			/* Modifiers */

			/*! Tune Max2118 to frequency _freq Hz */
			double tune(double _freq);							
			/*! Set lowpass filter in Max2118 to _bw Hz */
			double bandwidth(double _bw);					
			/*! Set the gain */
			double gain(double _gain);						
			/*! Set the RF gain */
			double rf_gain(double _gain);					
			/*! Set the IF gain */
			double if_gain(double _gain);					
			

};
["db_dbs_rx.cc" (text/plain)]

/* -*- c++ -*- */
/*
 * Copyright 2007 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */


#include "db_dbs_rx.h"
#include <math.h>


/*!
	db_dbs_rx(usrp_standard_rx *_urx, int _which)

	_urx must be a pointer to an already invoked usrp_standard_rx object
	_which is either 0 or 1, for RX-A or RX-B, respectively
*/
db_dbs_rx::db_dbs_rx(usrp_standard_rx *_urx, int _which)
{

	/* Get a pointer to the object */
	urx = _urx;

	/* Which board (0 for RXA, 1 for RXB) */
	which = _which;

	/* Set Default Values */
	n = 950;
	div2 = 0;
	osc = 5;
	cp = 3;
	r = 4;
	r_int = 1;
	fdac = 127;
	m = 2;
	dl = 0;
	ade = 0;
	adl = 0;
	gc2 = 31;
	diag = 5;

	/* Gain stages, from 0 to 104 dB of gain */
	gc1 = 3227;
	gc2 = 31;
	dl = 0;
	pga = 0;

	db_freq    = 0;	/* LO frequency (Hz) */
	db_bw      = 0;	/* Lowpass filter bandwidth (Hz) */
	db_gain    = 0;	/* Gain (0 to 105 dB) */
	db_rf_gain = 0;	/* RF gain (0 to 56 dB) */
	db_if_gain = 0;	/* IF gain (0 to 49 dB) */

	usrp_standard_rx *urx; /* Pointer to usrp_standard_rx object */

	/* This should be dependant on which RX board this is (0 or 1) */
	which ? I2C_ADDR = 0x65 : I2C_ADDR = 0x67;

	/* Refclk Address, same deal */
	REFCLK_REG = which ? FR_RX_B_REFCLK : FR_RX_A_REFCLK;

	/* Set the refclk divisor */
	set_refclk_divisor(8);

	/* Enable the refclk */
	enable_refclk(true);

	/* Bypass ADC Buffers */
	bypass_adc_buffers(true);

	/* Write to the MAX2118 to initialize the board */
	write_MAX2118(); 

	/* set gain to 0 dB initially */
	gain(0);

}


/*!
	~db_dbs_rx()

	Nothing to do here
*/
db_dbs_rx::~db_dbs_rx()
{






}


/*!
	set_m(int _m)

	Sets M, the register that controls the lowpass filter width
*/
void db_dbs_rx::set_m(int _m)
{
	if((_m>0) && (_m<32))
	{
        m = _m;
        send_reg(4);
	}
}


/*!
	set_fdac(int _fdac)

	Sets FADC, the value for the lowpass filter -3 dB cutoff frequency
*/
void db_dbs_rx::set_fdac(int _fdac)
{
	if((_fdac>=0) && (_fdac<128))
	{
		fdac = _fdac;
		send_reg(3);
	}
}


/*!
	bandwidth(double _bw)

	Sets the bandwidth of the lowpass filter
*/
double db_dbs_rx::bandwidth(double _bw)
{

	double thresh;
	double fxtal = refclk_freq();

	if((_bw<1e6) || (_bw>33e6))
		return(0);

	int m_max = 0;

    if(_bw >= 4e6)
	{

		thresh = (double)floor(refclk_freq()/1e6);
		m_max = (int)(31.0 < thresh ? 31.0 : thresh);
	}
    else if (_bw >= 2e6)
	{
		thresh = (double)floor(refclk_freq()/.5e6);
		m_max = (int)(31.0 < thresh ? 31.0 : thresh);
	}
    else
	{
		thresh = (double)floor(refclk_freq()/.25e6);
		m_max = (int)(31.0 < thresh ? 31.0 : thresh);
	}
    

    int m_min = (int) ceil(refclk_freq()/2.5e6);

    int m_test = m_max;
	int fdac_test = 0;

    while(m_test >= m_min)
	{
        fdac_test = int(floor(((_bw * m_test / refclk_freq())-4)/.145) + 0.5);

        if(fdac_test>127)
            m_test = m_test - 1;
        else
            break;
	}

    if ((m_test>=m_min) && (fdac_test >=0))
	{
        set_m(m_test);
        set_fdac(fdac_test);
		db_bw = refclk_freq()/m*(4+0.145*(double)fdac);
		return(db_bw);
	}
    else
	{
		db_bw = 0;
		return(0);
	}
}


/*!
	set_gc1(int _gc1)

	Sets GC1, which controls the RF gain
*/
void db_dbs_rx::set_gc1(int _gc1)
{

	gc1 = _gc1;
	urx->write_aux_dac(which, 0, gc1);

}


/*!
	set_gc2(int _gc2)

	Sets GC2, which controls the IF gain
*/
void db_dbs_rx::set_gc2(int _gc2)
{

	if((_gc2>=0) && (_gc2<32))
	{
		gc2 = _gc2;
		send_reg(5);
	}

}


/*!
	set_dl(int _dl)

	Sets DL, which adds 0 or 4.58 dB of IF gain
*/
void db_dbs_rx::set_dl(int _dl)
{

	if((_dl==0) || (_dl==1))
	{
		dl = _dl;
		send_reg(4);
	}

}


/*!
	set_pga(double _pga)

	Sets the PGA on the 
*/
void db_dbs_rx::set_pga(double _pga)
{
	if((_pga>0.0) && (_pga<=20.0))
	{
		urx->set_pga(which, _pga);
		urx->set_pga(which+1, _pga);
	}
}


/*!
	gain(double _gain)

	Sets the total gain of the DBS-RX daughter board
*/
double db_dbs_rx::gain(double _gain)
{

	/* Check for limits */
	if((_gain < 0) || (_gain > 105))
	{
		db_gain = 0;
		db_rf_gain = 0;
		db_if_gain = 0;
		gc1 = 3227;
		gc2 = 31;
		dl = 0;
		pga = 0;
		return(-1);
	}

	if(_gain < 56)
	{
		rf_gain(_gain);
		db_gain = db_rf_gain + db_if_gain;
	}
	else
	{
		rf_gain(56);
		_gain = _gain - 56;
		if_gain(_gain);
		db_gain = db_rf_gain + db_if_gain;
	}

	return(db_gain);

}


/*!
	rf_gain(double _gain)

	Sets only the RF gain of the DBS-RX daughter board
*/
double db_dbs_rx::rf_gain(double _gain)
{

	/* Check for limits */
	if((_gain < 0) || (_gain > 56))
	{
		gc1 = 3227;
		return(-1);
	}
	else
		db_rf_gain = _gain;

	/* Calculate voltage */
	gc1 = int((2.6 - (_gain*1.85/56.0))*4096.0/3.3);

	/* Write out */
	set_gc1(gc1);

	/* Recalculate total gain */
	db_gain = db_if_gain + db_rf_gain;

	return(db_rf_gain);

}


/*!
	if_gain(double _gain)

	Sets only the IF gain of the DBS-RX daughter board
*/
double db_dbs_rx::if_gain(double _gain)
{

	/* Check for limits */
	if((_gain < 0) || (_gain > 49))
	{
		db_if_gain = 0;
		gc2 = 31;
		dl = 0;
		pga = 0;
		return(-1);
	}
	else
		db_if_gain = _gain;

	/* Set to minimum gain */
	gc2 = 31;
	dl = 0;
	pga = 0;

	/* GC2 of Max2118 */
	if(_gain < 24)
	{
		gc2 = int(floor(31.0*(1-_gain/24.0)+0.5));
		_gain = 0;
	}
	else
	{
		gc2 = 0;
		_gain = _gain - 24;
	}

	/* DL of Max2118 */
	if(_gain > 4.58)
	{
		dl = 1;
		_gain = _gain - 4.58;
	}

	/* PGA On AD3841 Chip */
	if(_gain < 20)
	{
		pga = _gain;
		_gain = 0;
	}
	else
	{
		pga = 20;
		_gain = _gain - 20;
	}

	/* Write out values */
	set_gc2(gc2);
	set_dl(dl);
	set_pga(pga);

	/* Recalculate total gain */
	db_gain = db_if_gain + db_rf_gain;

	return(db_if_gain);

}


/*!
	set_osc(int _osc)

	Chooses which oscillator source to use on Max2118
*/
void db_dbs_rx::set_osc(int _osc)
{
	if((_osc>=0) && (_osc<8))
	{
		osc = _osc;
		send_reg(2);
	}
}


/*!
	set_cp(int _cp)

	Sets the charge pump register of the Max2118
*/
void db_dbs_rx::set_cp(int _cp)
{
	if((_cp>=0) && (_cp<4))
	{
		cp = _cp;
		send_reg(2);
	}
}


/*!
	set_r(int _r)

	Sets the R divider used to phase lock the LO
*/
void db_dbs_rx::set_r(int _r)
{

	/* r_int = log2(r)-1 */
	if((_r >= 0) && (_r < 256))
	{
		r = _r;
		_r >>= 1;
		r_int = 0;
		while(_r > 1)
		{
			r_int++;
			_r >>= 1;
		}
		send_reg(2);
	}
	
}


/*!
	set_div2(int _div2)

	Sets DIV2 register of Max2118
*/
void db_dbs_rx::set_div2(int _div2)
{

	if((div2 == 0) || (div2 == 1))
	{
		div2 = _div2;
		send_reg(0);
	}

}


/*!
	set_n(int _n)

	Sets the N divider used to phase lock the LO
*/
void db_dbs_rx::set_n(int _n)
{

	if((_n > 256) && (_n < 32768))
	{
		n = _n;
		send_reg(0);
		send_reg(1);
	}

}


/*!
	set_ade(int _ade)

	Sets the AD enable bit
*/
void db_dbs_rx::set_ade(int _ade)
{

	if((_ade==0) || (_ade==1))
	{
		ade = _ade;
		send_reg(4);
	}

}


/*!
	set_adl(int _adl)

	Latches the AD value
*/
void db_dbs_rx::set_adl(int _adl)
{

	if((_adl==0) || (_adl==1))
	{
		adl = _adl;
		send_reg(4);
	}

}


/*!
	set_diag(int _diag)

	Sets the DIAG register
*/
void db_dbs_rx::set_diag(int _diag)
{

	if((_diag>=0) && (_diag<8) && (_diag!=4))
	{
		diag = _diag;
		send_reg(5);
	}

}


/*!
	tune(double _freq)

	Sets the LO frequency of the DBS-RX daughterbaord
*/
double db_dbs_rx::tune(double _freq)
{

	int vco = 0;
	int n_guess = 0;
	int best_r = 0;
	int best_n = 0;
	double vcofreq = 0;
	double best_delta = 100e6;
	double delta = 0;

	db_freq = 0;

	/* Make sure the _freq is in range of the Max2118 */
	if((_freq<500e6) || (_freq>2.6e9))
		return(-1);

	/* Set the VCO divider */
	if(_freq < 1125e6)
	{
		set_div2(0);
		vcofreq = 4*_freq;
	}
	else
	{
		set_div2(1);
		vcofreq = 2*_freq;
	}


	/* Set maximum value of r, to gaurantee that the comparator frequency is at least 250 kHz
		(anything less than this introduces instability in the Max2118 PLL) */
	int max_r = 256;
	while((refclk_freq()/(double)max_r) < 250000.0)
		max_r >>= 1;

	/* Find r and n to produce to closest possible frequency */
	for(max_r; max_r >= 2; max_r >>= 1)
	{

		/* Find n for this r */
		n_guess = (int) floor(((double)max_r*_freq)/refclk_freq() + 0.5);

		/* Calculate difference from desired and actual */
		delta = (double)n_guess*refclk_freq()/(double)max_r;
		delta -= _freq;
		delta < 0 ? delta = -delta : delta = delta;

		if((n_guess > 256) && (n_guess < 32768))
		{
			if(delta < best_delta)
			{
				best_r = max_r;
				best_n = n_guess;
				best_delta = delta;
			}
		}

	}

	/* Set the value of r and n */
	set_r(best_r);
	set_n(best_n);

	 /* Choose the VCO depending on frequency */
	if(vcofreq < 2433e6)
		vco = 0;
	else if(vcofreq < 2711e6)
		vco = 1;
	else if(vcofreq < 3025e6) 
		vco = 2;
	else if(vcofreq < 3341e6)
		vco = 3;
	else if(vcofreq < 3727e6)
		vco = 4;
	else if(vcofreq < 4143e6)
		vco = 5;
	else if(vcofreq < 4493e6)
		vco = 6;
	else
		vco = 7;

	/* Set the chosen VCO */
	set_osc(vco);

	/* Enable the PLL ADC */
	set_ade(1);

    /* Set charge pump current */
    int adc_val = 0;
	while((adc_val==0) || (adc_val==7))
	{
		adc_val = read_adc();
		if(adc_val==0)
		{
			if(vco == 0)
				return(0);
			else
			{
				vco = vco - 1;
				set_osc(vco);
			}
		}

		if(adc_val==7)
		{
			if(vco == 7)
				return(0);
			else
			{
				vco = vco + 1;
				set_osc(vco);
			}
		}
	}

	/* Disable the PLL ADC */
	set_ade(0);

	/* Set the charge pump value */
    if((adc_val==1) || (adc_val==2))
        set_cp(1);
	else if((adc_val==3) || (adc_val==4))
        set_cp(2);
    else
        set_cp(3);

	/* Actual frequency */
	db_freq = (double)n*refclk_freq()/double(r);

	return (db_freq);

}


/*!
	write_MAX2118()

	Writes the Max2118 registers
*/
void db_dbs_rx::write_MAX2118()
{

	send_reg(0);
	send_reg(1);
	send_reg(2);
	send_reg(3);
	send_reg(4);
	send_reg(5);

}


/*!
	read_adc()

	Read the PLL ADC value, used to tune the LO 
*/
int db_dbs_rx::read_adc()
{

	std::string read_buff;
	int _adc;

	/* Latch the PLL ADC Value */
	set_adl(1);

	/* Read PLL ADC Value */
	read_buff = urx->read_i2c(I2C_ADDR, 2);

	/* Unlatch PLL ADC Value */
	set_adl(0);

	if(read_buff.length() != 2)
		return(0);

	_adc = (int)read_buff[0];
	_adc >>= 2;
	_adc &= 0x7;

	return(_adc);
}


/*!
	send_reg(int _reg)

	Send a Max2118 register
*/
bool db_dbs_rx::send_reg(int _reg)
{

	std::string buf;


	if((_reg>=0) && (_reg<6))
	{

		buf.append(1, (char)_reg);

		switch(_reg)
		{
			case 0:
				buf.append(1, (char)((div2<<7) + ((n>>8) & 0x7f)));
				break;
			case 1:
				buf.append(1, (char)(n & 0xff));
				break;
			case 2:
				buf.append(1, (char)(((osc) + (cp<<3) + (r_int<<5)) & 0xff));
				break;
			case 3:
				buf.append(1, (char)(fdac & 0xff));
				break;
			case 4:
				buf.append(1, (char)((m + (dl<<5) + (ade<<6) + (adl<<7)) & 0xff));
				break;
			case 5:
				buf.append(1, (char)((gc2 + (diag<<5)) & 0xff));
				break;
			default:
				break;
		}

		return(urx->write_i2c(I2C_ADDR, buf));

	}
	else
		return(false);




}


/*!
	set_refclk_divisor(int _div)

	Sets the refclk_divisor, which divides down the 64 MHz board clock
*/
void db_dbs_rx::set_refclk_divisor(int _div)
{
	refclk_divisor = _div;
}


/*!
	enable_refclk(bool _enable)

	Enables the refclk input to the DBS-RX daughterboard
*/
void db_dbs_rx::enable_refclk(bool _enable)
{
    
	unsigned int CLOCK_OUT = 1; //# Clock is on lowest bit
	unsigned int REFCLK_ENABLE = 0x80;
	unsigned int REFCLK_DIVISOR_MASK = 0x7f;
	if(_enable)
	{
		urx->_write_oe(which,CLOCK_OUT,CLOCK_OUT);
		urx->_write_fpga_reg(REFCLK_REG,((refclk_divisor & REFCLK_DIVISOR_MASK)|REFCLK_ENABLE));
	}
	else	
	{
		urx->_write_fpga_reg(REFCLK_REG, 0x0);
	}
}


/*!
	refclk_freq()

	Returns the refclk frequency
*/
double db_dbs_rx::refclk_freq()
{
	return(urx->fpga_master_clock_freq()/refclk_divisor);
}


/*!
	bypass_adc_buffers(bool _bypass)

	Bypasses ADC buffers, not sure why, but this
	was done in the Python driver
*/
void db_dbs_rx::bypass_adc_buffers(bool _bypass)
{

	urx->set_adc_buffer_bypass(2*which+0, _bypass);
	urx->set_adc_buffer_bypass(2*which+1, _bypass);

}

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic