mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
1297 lines
41 KiB
Java
1297 lines
41 KiB
Java
/*******************************************************************************
|
|
* SDR Trunk
|
|
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
*
|
|
* Java port of librtlsdr <https://github.com/steve-m/librtlsdr>
|
|
*
|
|
* Copyright (C) 2013 Mauro Carvalho Chehab <mchehab@redhat.com>
|
|
* Copyright (C) 2013 Steve Markgraf <steve@steve-m.de>
|
|
*
|
|
* This program 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>
|
|
******************************************************************************/
|
|
package source.tuner.rtl.r820t;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.usb4java.Device;
|
|
import org.usb4java.DeviceDescriptor;
|
|
import org.usb4java.LibUsbException;
|
|
import source.SourceException;
|
|
import source.tuner.TunerType;
|
|
import source.tuner.configuration.TunerConfiguration;
|
|
import source.tuner.rtl.RTL2832TunerController;
|
|
|
|
import javax.swing.*;
|
|
import javax.usb.UsbException;
|
|
import java.nio.ByteBuffer;
|
|
|
|
public class R820TTunerController extends RTL2832TunerController
|
|
{
|
|
private final static Logger mLog =
|
|
LoggerFactory.getLogger(R820TTunerController.class);
|
|
|
|
public static final long MIN_FREQUENCY = 3180000;
|
|
public static final long MAX_FREQUENCY = 1782030000;
|
|
public static final double USABLE_BANDWIDTH_PERCENT = 1.0;
|
|
public static final int DC_SPIKE_AVOID_BUFFER = 5000;
|
|
|
|
public static final int R820T_IF_FREQUENCY = 3570000;
|
|
public static final byte VERSION = (byte) 49;
|
|
|
|
public static final byte[] BIT_REV_LOOKUP_TABLE =
|
|
{(byte) 0x0, (byte) 0x8, (byte) 0x4, (byte) 0xC,
|
|
(byte) 0x2, (byte) 0xA, (byte) 0x6, (byte) 0xE,
|
|
(byte) 0x1, (byte) 0x9, (byte) 0x5, (byte) 0xD,
|
|
(byte) 0x3, (byte) 0xB, (byte) 0x7, (byte) 0xF};
|
|
|
|
/* R820T I2C address */
|
|
private byte mI2CAddress = (byte) 0x34;
|
|
|
|
/* Shadow register is used to keep a cached (in-memory) copy of all
|
|
* registers, so that we don't have to read a full byte from a register in
|
|
* order to apply a masked value and then re-write the full byte. With the
|
|
* shadow register, we can apply the masked value to the cached value, and
|
|
* then just write the masked byte, skipping the need to read the byte
|
|
* first. */
|
|
private int[] mShadowRegister =
|
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x32, 0x75,
|
|
0xC0, 0x40, 0xD6, 0x6C, 0xF5, 0x63, 0x75, 0x68,
|
|
0x6C, 0x83, 0x80, 0x00, 0x0F, 0x00, 0xC0, 0x30,
|
|
0x48, 0xCC, 0x60, 0x00, 0x54, 0xAE, 0x4A, 0xC0};
|
|
|
|
private JPanel mEditor;
|
|
|
|
public R820TTunerController(Device device, DeviceDescriptor deviceDescriptor) throws SourceException
|
|
{
|
|
super(device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY, DC_SPIKE_AVOID_BUFFER,
|
|
USABLE_BANDWIDTH_PERCENT);
|
|
}
|
|
|
|
@Override
|
|
public TunerType getTunerType()
|
|
{
|
|
return TunerType.RAFAELMICRO_R820T;
|
|
}
|
|
|
|
@Override
|
|
public void setSampleRateFilters(int sampleRate) throws LibUsbException
|
|
{
|
|
//TODO: why is this being forced as an abstract method on sub classes?
|
|
}
|
|
|
|
@Override
|
|
public void apply(TunerConfiguration tunerConfig) throws SourceException
|
|
{
|
|
if(tunerConfig != null &&
|
|
tunerConfig instanceof R820TTunerConfiguration)
|
|
{
|
|
R820TTunerConfiguration config = (R820TTunerConfiguration) tunerConfig;
|
|
|
|
try
|
|
{
|
|
SampleRate sampleRate = config.getSampleRate();
|
|
setSampleRate(sampleRate);
|
|
|
|
double correction = config.getFrequencyCorrection();
|
|
getFrequencyController().setFrequencyCorrection(correction);
|
|
|
|
R820TGain masterGain = config.getMasterGain();
|
|
setGain(masterGain, true);
|
|
|
|
if(masterGain == R820TGain.MANUAL)
|
|
{
|
|
R820TMixerGain mixerGain = config.getMixerGain();
|
|
setMixerGain(mixerGain, true);
|
|
|
|
R820TLNAGain lnaGain = config.getLNAGain();
|
|
setLNAGain(lnaGain, true);
|
|
|
|
R820TVGAGain vgaGain = config.getVGAGain();
|
|
setVGAGain(vgaGain, true);
|
|
}
|
|
|
|
try
|
|
{
|
|
getFrequencyController().setFrequency(config.getFrequency());
|
|
}
|
|
catch(SourceException se)
|
|
{
|
|
//Do nothing, we couldn't set the frequency
|
|
}
|
|
}
|
|
catch(UsbException e)
|
|
{
|
|
throw new SourceException("R820TTunerController - usb error "
|
|
+ "while applying tuner config", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Not implemented.
|
|
*/
|
|
@Override
|
|
public long getTunedFrequency() throws SourceException
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the center frequency. Setting the frequency is a two-part process
|
|
* of setting the multiplexer and then setting the Oscillator (PLL).
|
|
*/
|
|
@Override
|
|
public void setTunedFrequency(long frequency) throws SourceException
|
|
{
|
|
try
|
|
{
|
|
enableI2CRepeater(mDeviceHandle, true);
|
|
|
|
boolean controlI2C = false;
|
|
|
|
long offsetFrequency = frequency + R820T_IF_FREQUENCY;
|
|
|
|
setMux(offsetFrequency, controlI2C);
|
|
|
|
setPLL(offsetFrequency, controlI2C);
|
|
|
|
enableI2CRepeater(mDeviceHandle, false);
|
|
}
|
|
catch(UsbException e)
|
|
{
|
|
throw new SourceException("R820TTunerController - exception "
|
|
+ "while setting frequency [" + frequency + "] - " +
|
|
e.getLocalizedMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overrides the same method from the RTL2832 tuner controller to apply
|
|
* settings specific to the R820T tuner.
|
|
*/
|
|
public void setSamplingMode(SampleMode mode) throws LibUsbException
|
|
{
|
|
switch(mode)
|
|
{
|
|
case QUADRATURE:
|
|
/* Set intermediate frequency to R820T IF frequency */
|
|
setIFFrequency(R820T_IF_FREQUENCY);
|
|
|
|
/* Enable spectrum inversion */
|
|
writeDemodRegister(mDeviceHandle,
|
|
Page.ONE,
|
|
(short) 0x15,
|
|
(short) 0x01,
|
|
1);
|
|
|
|
/* Set default i/q path */
|
|
writeDemodRegister(mDeviceHandle,
|
|
Page.ZERO,
|
|
(short) 0x06,
|
|
(short) 0x80,
|
|
1);
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the multiplexer for the desired center frequency.
|
|
*/
|
|
private void setMux(long frequency, boolean controlI2C) throws UsbException
|
|
{
|
|
FrequencyRange range = FrequencyRange.getRangeForFrequency(frequency);
|
|
|
|
/* Set open drain */
|
|
writeR820TRegister(Register.DRAIN, range.getOpenDrain(), controlI2C);
|
|
|
|
/* RF_MUX, Polymux */
|
|
writeR820TRegister(Register.RF_POLY_MUX, range.getRFMuxPolyMux(), controlI2C);
|
|
|
|
/* TF Band */
|
|
writeR820TRegister(Register.TF_BAND, range.getTFC(), controlI2C);
|
|
|
|
/* XTAL CAP & Drive */
|
|
writeR820TRegister(Register.PLL_XTAL_CAPACITOR_AND_DRIVE,
|
|
range.getXTALHighCap0P(), controlI2C);
|
|
|
|
/* Register 8 - what is it? */
|
|
writeR820TRegister(Register.UNKNOWN_REGISTER_8, (byte) 0x00, controlI2C);
|
|
|
|
/* Register 9 - what is it? */
|
|
writeR820TRegister(Register.UNKNOWN_REGISTER_9, (byte) 0x00, controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Initializes the tuner for use.
|
|
*/
|
|
public void init() throws SourceException
|
|
{
|
|
/* Initialize the super class to open and claim the usb interface*/
|
|
super.init();
|
|
|
|
try
|
|
{
|
|
initBaseband(mDeviceHandle);
|
|
|
|
enableI2CRepeater(mDeviceHandle, true);
|
|
|
|
boolean i2CRepeaterControl = false;
|
|
|
|
initTuner(i2CRepeaterControl);
|
|
|
|
enableI2CRepeater(mDeviceHandle, false);
|
|
}
|
|
catch(UsbException e)
|
|
{
|
|
throw new SourceException("error during init()", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the tuner section.
|
|
*/
|
|
public void initTuner(boolean controlI2C) throws UsbException
|
|
{
|
|
/* Disable zero IF mode */
|
|
writeDemodRegister(mDeviceHandle,
|
|
Page.ONE,
|
|
(short) 0xB1,
|
|
(short) 0x1A,
|
|
1);
|
|
|
|
/* Only enable in-phase ADC input */
|
|
writeDemodRegister(mDeviceHandle,
|
|
Page.ZERO,
|
|
(short) 0x08,
|
|
(short) 0x4D,
|
|
1);
|
|
|
|
/* Set intermediate frequency to R820T IF frequency (3.57 MHz) */
|
|
setIFFrequency(R820T_IF_FREQUENCY);
|
|
|
|
/* Enable spectrum inversion */
|
|
writeDemodRegister(mDeviceHandle,
|
|
Page.ONE,
|
|
(short) 0x15,
|
|
(short) 0x01,
|
|
1);
|
|
|
|
initializeRegisters(controlI2C);
|
|
|
|
setTVStandard(controlI2C);
|
|
|
|
systemFrequencySelect(0, controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Partially implements the r82xx_set_tv_standard() method from librtlsdr.
|
|
* Sets standard to digital tv to support sdr operations only.
|
|
*/
|
|
private void setTVStandard(boolean controlI2C) throws UsbException
|
|
{
|
|
/* Init Flag & Xtal check Result */
|
|
writeR820TRegister(Register.XTAL_CHECK, (byte) 0x00, controlI2C);
|
|
|
|
/* Set version */
|
|
writeR820TRegister(Register.VERSION, VERSION, controlI2C);
|
|
|
|
/* LT Gain Test */
|
|
writeR820TRegister(Register.LNA_TOP, (byte) 0x00, controlI2C);
|
|
|
|
int calibrationCode = 0;
|
|
|
|
for(int x = 0; x < 2; x++)
|
|
{
|
|
/* Set filter cap */
|
|
writeR820TRegister(Register.FILTER_CAPACITOR, (byte) 0x6B, controlI2C);
|
|
|
|
/* Set calibration clock on */
|
|
writeR820TRegister(Register.CALIBRATION_CLOCK, (byte) 0x04, controlI2C);
|
|
|
|
/* XTAL capacitor 0pF for PLL */
|
|
writeR820TRegister(Register.PLL_XTAL_CAPACITOR, (byte) 0x00, controlI2C);
|
|
|
|
setPLL(56000 * 1000, controlI2C);
|
|
|
|
/* Start trigger */
|
|
writeR820TRegister(Register.CALIBRATION_TRIGGER, (byte) 0x10, controlI2C);
|
|
|
|
/* Stop trigger */
|
|
writeR820TRegister(Register.CALIBRATION_TRIGGER, (byte) 0x00, controlI2C);
|
|
|
|
/* Set calibration clock off */
|
|
writeR820TRegister(Register.CALIBRATION_CLOCK, (byte) 0x00, controlI2C);
|
|
|
|
calibrationCode = getCalibrationCode(controlI2C);
|
|
|
|
if(!calibrationSuccessful(calibrationCode))
|
|
{
|
|
mLog.error("Calibration NOT successful - code: " +
|
|
calibrationCode);
|
|
}
|
|
}
|
|
|
|
if(calibrationCode == 0x0F)
|
|
{
|
|
calibrationCode = 0;
|
|
}
|
|
|
|
/* Write calibration code */
|
|
byte filt_q = 0x10;
|
|
|
|
writeR820TRegister(Register.FILTER_CALIBRATION_CODE,
|
|
(byte) (calibrationCode | filt_q), controlI2C);
|
|
|
|
/* Set BW, Filter gain & HP Corner */
|
|
writeR820TRegister(Register.BANDWIDTH_FILTER_GAIN_HIGHPASS_FILTER_CORNER,
|
|
(byte) 0x6B, controlI2C);
|
|
|
|
/* Set Image_R */
|
|
writeR820TRegister(Register.IMAGE_REVERSE, (byte) 0x00, controlI2C);
|
|
|
|
/* Set filter_3db, V6MHz */
|
|
writeR820TRegister(Register.FILTER_GAIN, (byte) 0x10, controlI2C);
|
|
|
|
/* Channel filter extension */
|
|
writeR820TRegister(Register.CHANNEL_FILTER_EXTENSION, (byte) 0x60, controlI2C);
|
|
|
|
/* Loop through */
|
|
writeR820TRegister(Register.LOOP_THROUGH, (byte) 0x00, controlI2C);
|
|
|
|
/* Loop through attenuation */
|
|
writeR820TRegister(Register.LOOP_THROUGH_ATTENUATION, (byte) 0x00, controlI2C);
|
|
|
|
/* Filter extension widest */
|
|
writeR820TRegister(Register.FILTER_EXTENSION_WIDEST, (byte) 0x00, controlI2C);
|
|
|
|
/* RF poly filter current */
|
|
writeR820TRegister(Register.RF_POLY_FILTER_CURRENT, (byte) 0x60, controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Indicates if a calibration request was successful.
|
|
*/
|
|
private boolean calibrationSuccessful(int calibrationCode)
|
|
{
|
|
return calibrationCode != 0 && calibrationCode != 0x0F;
|
|
}
|
|
|
|
/**
|
|
* Returns the calibration code resulting from a calibration request.
|
|
*/
|
|
private int getCalibrationCode(boolean controlI2C) throws UsbException
|
|
{
|
|
return getStatusRegister(4, controlI2C) & 0x0F;
|
|
}
|
|
|
|
/**
|
|
* Sets the system IF frequency
|
|
*/
|
|
private void systemFrequencySelect(long frequency, boolean controlI2C)
|
|
throws UsbException
|
|
{
|
|
/* LNA top? */
|
|
writeR820TRegister(Register.LNA_TOP2, (byte) 0xE5, controlI2C);
|
|
|
|
byte mixer_top;
|
|
byte cp_cur;
|
|
byte div_buf_cur;
|
|
|
|
if(frequency == 506000000 ||
|
|
frequency == 666000000 ||
|
|
frequency == 818000000)
|
|
{
|
|
mixer_top = (byte) 0x14;
|
|
cp_cur = (byte) 0x28;
|
|
div_buf_cur = (byte) 0x20;
|
|
}
|
|
else
|
|
{
|
|
mixer_top = (byte) 0x24;
|
|
cp_cur = (byte) 0x38;
|
|
div_buf_cur = (byte) 0x30;
|
|
}
|
|
|
|
writeR820TRegister(Register.MIXER_TOP, mixer_top, controlI2C);
|
|
|
|
writeR820TRegister(Register.LNA_VTH_L, (byte) 0x53, controlI2C);
|
|
|
|
writeR820TRegister(Register.MIXER_VTH_L, (byte) 0x75, controlI2C);
|
|
|
|
/* Air-In only for Astrometa */
|
|
writeR820TRegister(Register.AIR_CABLE1_INPUT_SELECTOR, (byte) 0x00, controlI2C);
|
|
|
|
writeR820TRegister(Register.CABLE2_INPUT_SELECTOR, (byte) 0x00, controlI2C);
|
|
|
|
writeR820TRegister(Register.CP_CUR, cp_cur, controlI2C);
|
|
|
|
writeR820TRegister(Register.DIVIDER_BUFFER_CURRENT, div_buf_cur, controlI2C);
|
|
|
|
writeR820TRegister(Register.FILTER_CURRENT, (byte) 0x40, controlI2C);
|
|
|
|
/* if( type != TUNER_ANALOG_TV ) ... */
|
|
|
|
writeR820TRegister(Register.LNA_TOP, (byte) 0x00, controlI2C);
|
|
|
|
writeR820TRegister(Register.MIXER_TOP2, (byte) 0x00, controlI2C);
|
|
|
|
writeR820TRegister(Register.PRE_DETECT, (byte) 0x00, controlI2C);
|
|
|
|
writeR820TRegister(Register.AGC_CLOCK, (byte) 0x30, controlI2C);
|
|
|
|
writeR820TRegister(Register.LNA_TOP, (byte) 0x18, controlI2C);
|
|
|
|
writeR820TRegister(Register.MIXER_TOP2, mixer_top, controlI2C);
|
|
|
|
/* LNA discharge current */
|
|
writeR820TRegister(Register.LNA_DISCHARGE_CURRENT, (byte) 0x14, controlI2C);
|
|
|
|
/* AGC clock 1 khz, external det1 cap 1u */
|
|
writeR820TRegister(Register.AGC_CLOCK, (byte) 0x20, controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Sets the tuner's Phase-Locked-Loop (PLL) oscillator used for frequency
|
|
* (tuning) control
|
|
*
|
|
* @param frequency - desired center frequency
|
|
* @param controlI2C - control the I2C repeater locally
|
|
* @throws UsbException - if unable to set any of the R820T registers
|
|
*/
|
|
private void setPLL(long frequency, boolean controlI2C) throws UsbException
|
|
{
|
|
/* Set reference divider to 0 */
|
|
writeR820TRegister(Register.REFERENCE_DIVIDER_2, (byte) 0x00, controlI2C);
|
|
|
|
/* Set PLL autotune to 128kHz */
|
|
writeR820TRegister(Register.PLL_AUTOTUNE, (byte) 0x00, controlI2C);
|
|
|
|
/* Set VCO current to 100 */
|
|
writeR820TRegister(Register.VCO_CURRENT, (byte) 0x80, controlI2C);
|
|
|
|
/* Set the frequency divider - adjust for vco_fine_tune status */
|
|
FrequencyDivider divider = FrequencyDivider.fromFrequency(frequency);
|
|
|
|
int statusRegister4 = getStatusRegister(4, controlI2C);
|
|
|
|
int vco_fine_tune = (statusRegister4 & 0x30) >> 4;
|
|
|
|
int div_num = divider.getDividerNumber(vco_fine_tune);
|
|
|
|
writeR820TRegister(Register.DIVIDER, (byte) (div_num << 5), controlI2C);
|
|
|
|
/* Get the integral number for this divider and frequency */
|
|
Integral integral = divider.getIntegral(frequency);
|
|
|
|
writeR820TRegister(Register.PLL, integral.getRegisterValue(), controlI2C);
|
|
|
|
/* Calculate the sigma-delta modulator fractional setting. If it's
|
|
* non-zero, power up the sdm and apply the fractional setting,
|
|
* otherwise turn it off */
|
|
int sdm = divider.getSDM(integral, frequency);
|
|
|
|
if(sdm != 0)
|
|
{
|
|
writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER,
|
|
(byte) 0x00, controlI2C);
|
|
|
|
writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_MSB,
|
|
(byte) ((sdm >> 8) & 0xFF), controlI2C);
|
|
|
|
writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_LSB,
|
|
(byte) (sdm & 0xFF), controlI2C);
|
|
}
|
|
else
|
|
{
|
|
writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER,
|
|
(byte) 0x08, controlI2C);
|
|
}
|
|
|
|
/* Check to see if the PLL locked with these divider, integral and sdm
|
|
* settings */
|
|
if(!isPLLLocked(controlI2C))
|
|
{
|
|
/* Increase VCO current */
|
|
writeR820TRegister(Register.VCO_CURRENT, (byte) 0x60, controlI2C);
|
|
|
|
if(!isPLLLocked(controlI2C))
|
|
{
|
|
throw new UsbException("R820T Tuner Controller - couldn't "
|
|
+ "achieve PLL lock on frequency [" + frequency + "]");
|
|
}
|
|
}
|
|
|
|
/* set pll autotune to 8kHz */
|
|
writeR820TRegister(Register.PLL_AUTOTUNE_VARIANT, (byte) 0x08, controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Indicates if the Phase Locked Loop (PLL) oscillator is locked following
|
|
* a change in the tuned center frequency. Checks status register 2 to see
|
|
* if the PLL locked indicator bit is set.
|
|
*/
|
|
private boolean isPLLLocked(boolean controlI2C) throws UsbException
|
|
{
|
|
int register = getStatusRegister(2, controlI2C);
|
|
|
|
return (register & 0x40) == 0x40;
|
|
}
|
|
|
|
/**
|
|
* Writes initial starting value of registers 0x05 through 0x1F using the
|
|
* default value initialized in the shadow register array. This method only
|
|
* needs to be called once, upon initialization.
|
|
*
|
|
* @throws UsbException
|
|
*/
|
|
private void initializeRegisters(boolean controlI2C) throws UsbException
|
|
{
|
|
for(int x = 5; x < mShadowRegister.length; x++)
|
|
{
|
|
writeI2CRegister(mDeviceHandle,
|
|
mI2CAddress,
|
|
(byte) x,
|
|
(byte) mShadowRegister[x],
|
|
controlI2C);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the contents of status registers 0 through 4
|
|
*/
|
|
private int getStatusRegister(int register, boolean controlI2C) throws UsbException
|
|
{
|
|
ByteBuffer buffer = ByteBuffer.allocateDirect(5);
|
|
|
|
read(mDeviceHandle, mI2CAddress, Block.I2C, buffer);
|
|
|
|
return bitReverse(buffer.get(register) & 0xFF);
|
|
}
|
|
|
|
/**
|
|
* Assumes value is a byte value and reverses the bits in the byte.
|
|
*/
|
|
private static int bitReverse(int value)
|
|
{
|
|
return BIT_REV_LOOKUP_TABLE[value & 0x0F] << 4 |
|
|
BIT_REV_LOOKUP_TABLE[(value & 0xF0) >> 4];
|
|
}
|
|
|
|
/**
|
|
* Writes the byte value to the specified register, optionally controlling
|
|
* the I2C repeater as needed.
|
|
*/
|
|
public void writeR820TRegister(Register register,
|
|
byte value,
|
|
boolean controlI2C) throws UsbException
|
|
{
|
|
if(register.isMasked())
|
|
{
|
|
int current = mShadowRegister[register.getRegister()];
|
|
|
|
value = (byte) ((current & ~register.getMask()) |
|
|
(value & register.getMask()));
|
|
}
|
|
|
|
writeI2CRegister(mDeviceHandle, mI2CAddress,
|
|
(byte) register.getRegister(), value, controlI2C);
|
|
|
|
mShadowRegister[register.getRegister()] = value;
|
|
|
|
// Log.info( "R820T writing register " +
|
|
// String.format( "%02X", register.getRegister() ) + " value " +
|
|
// String.format( "%02X", value ) );
|
|
}
|
|
|
|
/**
|
|
* Reads the specified register, optionally controlling the I2C repeater
|
|
*/
|
|
public int readR820TRegister(Register register, boolean controlI2C)
|
|
throws UsbException
|
|
{
|
|
int value = readI2CRegister(mDeviceHandle,
|
|
mI2CAddress,
|
|
(byte) register.getRegister(),
|
|
controlI2C);
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Sets master gain by applying gain component values to LNA, Mixer and
|
|
* VGA gain registers.
|
|
*/
|
|
public void setGain(R820TGain gain, boolean controlI2C) throws UsbException
|
|
{
|
|
setLNAGain(gain.getLNAGain(), controlI2C);
|
|
setMixerGain(gain.getMixerGain(), controlI2C);
|
|
setVGAGain(gain.getVGAGain(), controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Sets LNA gain
|
|
*/
|
|
public void setLNAGain(R820TLNAGain gain, boolean controlI2C) throws UsbException
|
|
{
|
|
writeR820TRegister(Register.LNA_GAIN, gain.getSetting(), controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Sets Mixer gain
|
|
*/
|
|
public void setMixerGain(R820TMixerGain gain, boolean controlI2C) throws UsbException
|
|
{
|
|
writeR820TRegister(Register.MIXER_GAIN, gain.getSetting(), controlI2C);
|
|
}
|
|
|
|
/**
|
|
* Sets VGA gain
|
|
*/
|
|
public void setVGAGain(R820TVGAGain gain, boolean controlI2C) throws UsbException
|
|
{
|
|
writeR820TRegister(Register.VGA_GAIN, gain.getSetting(), controlI2C);
|
|
}
|
|
|
|
/**
|
|
* R820T VGA gain settings
|
|
*/
|
|
public enum R820TVGAGain
|
|
{
|
|
GAIN_0("0", 0x00),
|
|
GAIN_26("26", 0x01),
|
|
GAIN_52("52", 0x02),
|
|
GAIN_82("82", 0x03),
|
|
GAIN_124("124", 0x04),
|
|
GAIN_159("159", 0x05),
|
|
GAIN_183("183", 0x06),
|
|
GAIN_196("196", 0x07),
|
|
GAIN_210("210", 0x08),
|
|
GAIN_242("242", 0x09),
|
|
GAIN_278("278", 0x0A),
|
|
GAIN_312("312", 0x0B),
|
|
GAIN_347("347", 0x0C),
|
|
GAIN_384("384", 0x0D),
|
|
GAIN_419("419", 0x0E),
|
|
GAIN_455("455", 0x0F);
|
|
|
|
private String mLabel;
|
|
private int mSetting;
|
|
|
|
private R820TVGAGain(String label, int setting)
|
|
{
|
|
mLabel = label;
|
|
mSetting = setting;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
return mLabel;
|
|
}
|
|
|
|
public byte getSetting()
|
|
{
|
|
return (byte) mSetting;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* R820T Low Noise Amplifier gain settings
|
|
*/
|
|
public enum R820TLNAGain
|
|
{
|
|
AUTOMATIC("Automatic", 0x00),
|
|
GAIN_0("0", 0x10),
|
|
GAIN_9("9", 0x11),
|
|
GAIN_21("21", 0x12),
|
|
GAIN_61("61", 0x13),
|
|
GAIN_99("99", 0x14),
|
|
GAIN_112("112", 0x15),
|
|
GAIN_143("143", 0x16),
|
|
GAIN_165("165", 0x17),
|
|
GAIN_191("191", 0x18),
|
|
GAIN_222("222", 0x19),
|
|
GAIN_248("248", 0x1A),
|
|
GAIN_262("262", 0x1B),
|
|
GAIN_281("281", 0x1C),
|
|
GAIN_286("286", 0x1D),
|
|
GAIN_321("321", 0x1E),
|
|
GAIN_334("334", 0x1F);
|
|
|
|
private String mLabel;
|
|
private int mSetting;
|
|
|
|
private R820TLNAGain(String label, int setting)
|
|
{
|
|
mLabel = label;
|
|
mSetting = setting;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
return mLabel;
|
|
}
|
|
|
|
public byte getSetting()
|
|
{
|
|
return (byte) mSetting;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* R820T mixer gain settings
|
|
*/
|
|
public enum R820TMixerGain
|
|
{
|
|
AUTOMATIC("Automatic", 0x10),
|
|
GAIN_0("0", 0x00),
|
|
GAIN_5("5", 0x01),
|
|
GAIN_15("15", 0x02),
|
|
GAIN_25("25", 0x03),
|
|
GAIN_44("44", 0x04),
|
|
GAIN_53("53", 0x05),
|
|
GAIN_63("63", 0x06),
|
|
GAIN_88("88", 0x07),
|
|
GAIN_105("105", 0x08),
|
|
GAIN_115("115", 0x09),
|
|
GAIN_123("123", 0x0A),
|
|
GAIN_139("139", 0x0B),
|
|
GAIN_152("152", 0x0C),
|
|
GAIN_158("158", 0x0D),
|
|
GAIN_161("161", 0x0E),
|
|
GAIN_153("153", 0x0F);
|
|
|
|
private String mLabel;
|
|
private int mSetting;
|
|
|
|
private R820TMixerGain(String label, int setting)
|
|
{
|
|
mLabel = label;
|
|
mSetting = setting;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
return mLabel;
|
|
}
|
|
|
|
public byte getSetting()
|
|
{
|
|
return (byte) mSetting;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* R820T Master gain settings
|
|
*/
|
|
public enum R820TGain
|
|
{
|
|
AUTOMATIC("Automatic", R820TVGAGain.GAIN_312, R820TLNAGain.AUTOMATIC, R820TMixerGain.AUTOMATIC),
|
|
MANUAL("Manual", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_248, R820TMixerGain.GAIN_123),
|
|
GAIN_0("0", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_0, R820TMixerGain.GAIN_0),
|
|
GAIN_9("9", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_9, R820TMixerGain.GAIN_0),
|
|
GAIN_14("14", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_9, R820TMixerGain.GAIN_5),
|
|
GAIN_26("26", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_21, R820TMixerGain.GAIN_5),
|
|
GAIN_36("36", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_21, R820TMixerGain.GAIN_15),
|
|
GAIN_76("76", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_61, R820TMixerGain.GAIN_15),
|
|
GAIN_86("86", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_61, R820TMixerGain.GAIN_25),
|
|
GAIN_124("124", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_99, R820TMixerGain.GAIN_25),
|
|
GAIN_143("143", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_99, R820TMixerGain.GAIN_44),
|
|
GAIN_156("156", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_112, R820TMixerGain.GAIN_44),
|
|
GAIN_165("165", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_112, R820TMixerGain.GAIN_53),
|
|
GAIN_196("196", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_143, R820TMixerGain.GAIN_53),
|
|
GAIN_208("208", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_143, R820TMixerGain.GAIN_63),
|
|
GAIN_228("228", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_165, R820TMixerGain.GAIN_63),
|
|
GAIN_253("253", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_165, R820TMixerGain.GAIN_88),
|
|
GAIN_279("279", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_191, R820TMixerGain.GAIN_88),
|
|
GAIN_296("296", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_191, R820TMixerGain.GAIN_105),
|
|
GAIN_327("327", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_222, R820TMixerGain.GAIN_105),
|
|
GAIN_337("337", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_222, R820TMixerGain.GAIN_115),
|
|
GAIN_363("363", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_248, R820TMixerGain.GAIN_115),
|
|
GAIN_371("371", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_248, R820TMixerGain.GAIN_123),
|
|
GAIN_385("385", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_262, R820TMixerGain.GAIN_123),
|
|
GAIN_401("401", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_262, R820TMixerGain.GAIN_139),
|
|
GAIN_420("420", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_281, R820TMixerGain.GAIN_139),
|
|
GAIN_433("433", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_281, R820TMixerGain.GAIN_152),
|
|
GAIN_438("438", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_286, R820TMixerGain.GAIN_152),
|
|
GAIN_444("444", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_286, R820TMixerGain.GAIN_158),
|
|
GAIN_479("479", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_321, R820TMixerGain.GAIN_158),
|
|
GAIN_482("482", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_321, R820TMixerGain.GAIN_161),
|
|
GAIN_495("495", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_334, R820TMixerGain.GAIN_161);
|
|
|
|
private String mLabel;
|
|
private R820TVGAGain mVGAGain;
|
|
private R820TLNAGain mLNAGain;
|
|
private R820TMixerGain mMixerGain;
|
|
|
|
private R820TGain(String label, R820TVGAGain vga, R820TLNAGain lna, R820TMixerGain mixer)
|
|
{
|
|
mLabel = label;
|
|
mVGAGain = vga;
|
|
mLNAGain = lna;
|
|
mMixerGain = mixer;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
return mLabel;
|
|
}
|
|
|
|
public R820TVGAGain getVGAGain()
|
|
{
|
|
return mVGAGain;
|
|
}
|
|
|
|
public R820TLNAGain getLNAGain()
|
|
{
|
|
return mLNAGain;
|
|
}
|
|
|
|
public R820TMixerGain getMixerGain()
|
|
{
|
|
return mMixerGain;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* R820T tuner registers and register mask values
|
|
*/
|
|
public enum Register
|
|
{
|
|
LNA_GAIN(0x05, 0x1F),
|
|
AIR_CABLE1_INPUT_SELECTOR(0x05, 0x60),
|
|
LOOP_THROUGH(0x05, 0x80),
|
|
CABLE2_INPUT_SELECTOR(0x06, 0x08),
|
|
FILTER_GAIN(0x06, 0x30),
|
|
PRE_DETECT(0x06, 0x40),
|
|
MIXER_GAIN(0x07, 0x1F),
|
|
IMAGE_REVERSE(0x07, 0x80),
|
|
UNKNOWN_REGISTER_8(0x08, 0x3F),
|
|
UNKNOWN_REGISTER_9(0x09, 0x3F),
|
|
FILTER_CALIBRATION_CODE(0x0A, 0x1F),
|
|
FILTER_CURRENT(0x0A, 0x60),
|
|
CALIBRATION_TRIGGER(0x0B, 0x10),
|
|
FILTER_CAPACITOR(0x0B, 0x60),
|
|
BANDWIDTH_FILTER_GAIN_HIGHPASS_FILTER_CORNER(0x0B, 0xEF),
|
|
XTAL_CHECK(0x0C, 0x0F),
|
|
VGA_GAIN(0x0C, 0x9F),
|
|
LNA_VTH_L(0x0D, 0x0),
|
|
MIXER_VTH_L(0x0E, 0x0),
|
|
CALIBRATION_CLOCK(0x0F, 0x04),
|
|
FILTER_EXTENSION_WIDEST(0x0F, 0x80),
|
|
PLL_XTAL_CAPACITOR(0x10, 0x03),
|
|
UNKNOWN_REGISTER_10(0x10, 0x04),
|
|
PLL_XTAL_CAPACITOR_AND_DRIVE(0x10, 0x0B),
|
|
REFERENCE_DIVIDER_2(0x10, 0x10),
|
|
CAPACITOR_SELECTOR(0x10, 0x1B),
|
|
DIVIDER(0x10, 0xE0),
|
|
CP_CUR(0x11, 0x38),
|
|
SIGMA_DELTA_MODULATOR_POWER(0x12, 0x08),
|
|
VCO_CURRENT(0x12, 0xE0),
|
|
VERSION(0x13, 0x3F),
|
|
PLL(0x14, 0x0),
|
|
SIGMA_DELTA_MODULATOR_LSB(0x15, 0x0),
|
|
SIGMA_DELTA_MODULATOR_MSB(0x16, 0x0),
|
|
DRAIN(0x17, 0x08),
|
|
DIVIDER_BUFFER_CURRENT(0x17, 0x30),
|
|
RF_POLY_FILTER_CURRENT(0x19, 0x60),
|
|
PLL_AUTOTUNE(0x1A, 0x0C),
|
|
PLL_AUTOTUNE_VARIANT(0x1A, 0x08),
|
|
AGC_CLOCK(0x1A, 0x30),
|
|
RF_POLY_MUX(0x1A, 0xC3),
|
|
TF_BAND(0x1B, 0x0),
|
|
MIXER_TOP(0x1C, 0xF8),
|
|
MIXER_TOP2(0X1C, 0x04),
|
|
LNA_TOP(0x1D, 0x38),
|
|
LNA_TOP2(0x1D, 0xC7),
|
|
CHANNEL_FILTER_EXTENSION(0x1E, 0x60),
|
|
LNA_DISCHARGE_CURRENT(0x1E, 0x1F),
|
|
LOOP_THROUGH_ATTENUATION(0x1F, 0x80);
|
|
|
|
private int mRegister;
|
|
private int mMask;
|
|
|
|
private Register(int register, int mask)
|
|
{
|
|
mRegister = register;
|
|
mMask = mask;
|
|
}
|
|
|
|
public int getRegister()
|
|
{
|
|
return mRegister;
|
|
}
|
|
|
|
public byte getMask()
|
|
{
|
|
return (byte) mMask;
|
|
}
|
|
|
|
public boolean isMasked()
|
|
{
|
|
return mMask != 0;
|
|
}
|
|
}
|
|
|
|
public enum FrequencyRange
|
|
{
|
|
RANGE_024(24000000, 49999999, 0x08, 0x02, 0xDF, 0x02, 0x01),
|
|
RANGE_050(50000000, 54999999, 0x08, 0x02, 0xBE, 0x02, 0x01),
|
|
RANGE_055(55000000, 59999999, 0x08, 0x02, 0x8B, 0x02, 0x01),
|
|
RANGE_060(60000000, 64999999, 0x08, 0x02, 0x7B, 0x02, 0x01),
|
|
RANGE_065(65000000, 69999999, 0x08, 0x02, 0x69, 0x02, 0x01),
|
|
RANGE_070(70000000, 74999999, 0x08, 0x02, 0x58, 0x02, 0x01),
|
|
RANGE_075(75000000, 79999999, 0x00, 0x02, 0x44, 0x02, 0x01),
|
|
RANGE_080(80000000, 89999999, 0x00, 0x02, 0x44, 0x02, 0x01),
|
|
RANGE_090(90000000, 99999999, 0x00, 0x02, 0x34, 0x01, 0x01),
|
|
RANGE_100(100000000, 109999999, 0x00, 0x02, 0x34, 0x01, 0x01),
|
|
RANGE_110(110000000, 119999999, 0x00, 0x02, 0x24, 0x01, 0x01),
|
|
RANGE_120(120000000, 139999999, 0x00, 0x02, 0x24, 0x01, 0x01),
|
|
RANGE_140(140000000, 179999999, 0x00, 0x02, 0x14, 0x01, 0x01),
|
|
RANGE_180(180000000, 219999999, 0x00, 0x02, 0x13, 0x00, 0x00),
|
|
RANGE_220(220000000, 249999999, 0x00, 0x02, 0x13, 0x00, 0x00),
|
|
RANGE_250(250000000, 279999999, 0x00, 0x02, 0x11, 0x00, 0x00),
|
|
RANGE_280(280000000, 309999999, 0x00, 0x02, 0x00, 0x00, 0x00),
|
|
RANGE_310(310000000, 449999999, 0x00, 0x41, 0x00, 0x00, 0x00),
|
|
RANGE_450(450000000, 587999999, 0x00, 0x41, 0x00, 0x00, 0x00),
|
|
RANGE_588(588000000, 649999999, 0x00, 0x40, 0x00, 0x00, 0x00),
|
|
RANGE_650(650000000, 1766000000, 0x00, 0x40, 0x00, 0x00, 0x00),
|
|
RANGE_UNK(0, 0, 0, 0, 0, 0, 0);
|
|
|
|
private long mMinFrequency;
|
|
private long mMaxFrequency;
|
|
private int mOpenDrain;
|
|
private int mRFMux_PolyMux;
|
|
private int mTF_c;
|
|
private int mXtalCap20p;
|
|
private int mXtalCap10p;
|
|
|
|
private FrequencyRange(long minFrequency,
|
|
long maxFrequency,
|
|
int openDrain,
|
|
int rfMuxPloy,
|
|
int tf_c,
|
|
int xtalCap20p,
|
|
int xtalCap10p)
|
|
{
|
|
mMinFrequency = minFrequency;
|
|
mMaxFrequency = maxFrequency;
|
|
mOpenDrain = openDrain;
|
|
mRFMux_PolyMux = rfMuxPloy;
|
|
mTF_c = tf_c;
|
|
mXtalCap20p = xtalCap20p;
|
|
mXtalCap10p = xtalCap10p;
|
|
}
|
|
|
|
public boolean contains(long frequency)
|
|
{
|
|
return mMinFrequency <= frequency && frequency <= mMaxFrequency;
|
|
}
|
|
|
|
public static FrequencyRange getRangeForFrequency(long frequency)
|
|
{
|
|
for(FrequencyRange range : values())
|
|
{
|
|
if(range.contains(frequency))
|
|
{
|
|
return range;
|
|
}
|
|
}
|
|
|
|
return RANGE_UNK;
|
|
}
|
|
|
|
public long getMinFrequency()
|
|
{
|
|
return mMinFrequency;
|
|
}
|
|
|
|
public long getMaxFrequency()
|
|
{
|
|
return mMaxFrequency;
|
|
}
|
|
|
|
public byte getOpenDrain()
|
|
{
|
|
return (byte) mOpenDrain;
|
|
}
|
|
|
|
public byte getRFMuxPolyMux()
|
|
{
|
|
return (byte) mRFMux_PolyMux;
|
|
}
|
|
|
|
public byte getTFC()
|
|
{
|
|
return (byte) mTF_c;
|
|
}
|
|
|
|
public byte getXTALCap20P()
|
|
{
|
|
return (byte) mXtalCap20p;
|
|
}
|
|
|
|
public byte getXTALCap10P()
|
|
{
|
|
return (byte) mXtalCap10p;
|
|
}
|
|
|
|
public byte getXTALLowCap0P()
|
|
{
|
|
return (byte) 0x08;
|
|
}
|
|
|
|
public byte getXTALHighCap0P()
|
|
{
|
|
return (byte) 0x00;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Frequency Divider Ranges
|
|
*
|
|
* Actual Tuned Frequency Ranges (after subtracting IF = 3.57 MHz )
|
|
*
|
|
* Divider 0: 860.43 to 1782.03 MHz
|
|
* Divider 1: 428.43 to 889.23 MHz
|
|
* Divider 2: 212.43 to 457.23 MHz
|
|
* Divider 3: 104.43 to 219.63 MHz
|
|
* Divider 4: 50.43 to 108.03 MHz
|
|
* Divider 5: 23.43 to 52.23 MHz
|
|
* Divider 6: 9.93 to 24.33 MHz
|
|
* Divider 7: 3.18 to 10.38 MHz
|
|
*/
|
|
public enum FrequencyDivider
|
|
{
|
|
DIVIDER_0(0, 2, 864000000, 1785600000, 0x00, 28800000),
|
|
DIVIDER_1(1, 4, 432000000, 892800000, 0x20, 14400000),
|
|
DIVIDER_2(2, 8, 216000000, 460800000, 0x40, 7200000),
|
|
DIVIDER_3(3, 16, 108000000, 223200000, 0x60, 3600000),
|
|
DIVIDER_4(4, 32, 54000000, 111600000, 0x80, 1800000),
|
|
DIVIDER_5(5, 64, 27000000, 55800000, 0xA0, 900000),
|
|
DIVIDER_6(6, 128, 13500000, 27900000, 0xC0, 450000),
|
|
DIVIDER_7(7, 256, 6750000, 13950000, 0xE0, 225000);
|
|
|
|
private int mDividerNumber;
|
|
private int mMixerDivider;
|
|
private long mMinimumFrequency;
|
|
private long mMaximumFrequency;
|
|
private int mRegisterSetting;
|
|
private int mIntegralValue;
|
|
private static final int mVCOPowerReference = 2;
|
|
|
|
private FrequencyDivider(int dividerNumber,
|
|
int mixerDivider,
|
|
long minimumFrequency,
|
|
long maximumFrequency,
|
|
int registerSetting,
|
|
int integralValue)
|
|
{
|
|
mDividerNumber = dividerNumber;
|
|
mMixerDivider = mixerDivider;
|
|
mMinimumFrequency = minimumFrequency;
|
|
mMaximumFrequency = maximumFrequency;
|
|
mRegisterSetting = registerSetting;
|
|
mIntegralValue = integralValue;
|
|
}
|
|
|
|
public int getDividerNumber(int vcoFineTune)
|
|
{
|
|
if(vcoFineTune == mVCOPowerReference)
|
|
{
|
|
return mDividerNumber;
|
|
}
|
|
else if(vcoFineTune < mVCOPowerReference)
|
|
{
|
|
return mDividerNumber - 1;
|
|
}
|
|
else if(vcoFineTune > mVCOPowerReference)
|
|
{
|
|
return mDividerNumber + 1;
|
|
}
|
|
|
|
return mDividerNumber;
|
|
}
|
|
|
|
public int getMixerDivider()
|
|
{
|
|
return mMixerDivider;
|
|
}
|
|
|
|
public long getMinimumFrequency()
|
|
{
|
|
return mMinimumFrequency;
|
|
}
|
|
|
|
public long getMaximumFrequency()
|
|
{
|
|
return mMaximumFrequency;
|
|
}
|
|
|
|
public byte getDividerRegisterSetting()
|
|
{
|
|
return (byte) mRegisterSetting;
|
|
}
|
|
|
|
public boolean contains(long frequency)
|
|
{
|
|
return mMinimumFrequency <= frequency &&
|
|
frequency <= mMaximumFrequency;
|
|
}
|
|
|
|
/**
|
|
* Returns the correct frequency divider for the specified frequency,
|
|
* or returns divider #5, if the frequency is outside of the specified
|
|
* frequency ranges.
|
|
*
|
|
* @param frequency - desired frequency
|
|
* @return - FrequencyDivider to use for the specified frequency
|
|
*/
|
|
public static FrequencyDivider fromFrequency(long frequency)
|
|
{
|
|
for(FrequencyDivider divider : FrequencyDivider.values())
|
|
{
|
|
if(divider.contains(frequency))
|
|
{
|
|
return divider;
|
|
}
|
|
}
|
|
|
|
return FrequencyDivider.DIVIDER_5;
|
|
}
|
|
|
|
/**
|
|
* Returns the integral to use for this frequency and divider
|
|
*/
|
|
public Integral getIntegral(long frequency)
|
|
{
|
|
if(contains(frequency))
|
|
{
|
|
int delta = (int) (frequency - mMinimumFrequency);
|
|
|
|
int integral = (int) ((double) delta / (double) mIntegralValue);
|
|
|
|
return Integral.fromValue(integral);
|
|
}
|
|
|
|
throw new IllegalArgumentException("PLL frequency [" + frequency +
|
|
"] is not valid for this frequency divider " + this.toString());
|
|
}
|
|
|
|
/**
|
|
* Calculates the 16-bit value of the sigma-delta modulator setting
|
|
* which represents the fractional portion of the requested frequency
|
|
* that is left over after subtracting the divider minimum frequency
|
|
* and the integral frequency units. That residual value is divided
|
|
* by the integral unit value to derive a 16-bit fractional value,
|
|
* returned as an integer
|
|
*/
|
|
public int getSDM(Integral integral, long frequency)
|
|
{
|
|
if(contains(frequency))
|
|
{
|
|
int delta = (int) (frequency - mMinimumFrequency -
|
|
(integral.getNumber() * mIntegralValue));
|
|
|
|
double fractional = (double) delta / (double) mIntegralValue;
|
|
|
|
//Left shift the double value 16 bits and truncate to an integer
|
|
return (int) (fractional * 0x10000) & 0xFFFF;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PLL Integral values. Each value represents one unit of the divided
|
|
* crystal frequency as follows:
|
|
*
|
|
* Divider Value
|
|
* 64 I * 0.9 MHz
|
|
* 32 I * 1.8 MHz
|
|
* 16 I * 3.6 MHz
|
|
* 8 I * 7.2 MHz
|
|
* 4 I * 14.4 MHz
|
|
* 2 I * 28.8 MHz
|
|
*/
|
|
public enum Integral
|
|
{
|
|
I00(0, 0x44),
|
|
I01(1, 0x84),
|
|
I02(2, 0xC4),
|
|
I03(3, 0x05),
|
|
I04(4, 0x45),
|
|
I05(5, 0x85),
|
|
I06(6, 0xC5),
|
|
I07(7, 0x06),
|
|
I08(8, 0x46),
|
|
I09(9, 0x86),
|
|
I10(10, 0xC6),
|
|
I11(11, 0x07),
|
|
I12(12, 0x47),
|
|
I13(13, 0x87),
|
|
I14(14, 0xC7),
|
|
I15(15, 0x08),
|
|
I16(16, 0x48),
|
|
I17(17, 0x88),
|
|
I18(18, 0xC8),
|
|
I19(19, 0x09),
|
|
I20(20, 0x49),
|
|
I21(21, 0x89),
|
|
I22(22, 0xC9),
|
|
I23(23, 0x0A),
|
|
I24(24, 0x4A),
|
|
I25(25, 0x8A),
|
|
I26(26, 0xCA),
|
|
I27(27, 0x0B),
|
|
I28(28, 0x4B),
|
|
I29(29, 0x8B),
|
|
I30(30, 0xCB),
|
|
I31(31, 0x0C);
|
|
|
|
private int mNumber;
|
|
private int mRegister;
|
|
|
|
private Integral(int number, int register)
|
|
{
|
|
mNumber = number;
|
|
mRegister = register;
|
|
}
|
|
|
|
public int getNumber()
|
|
{
|
|
return mNumber;
|
|
}
|
|
|
|
public byte getRegisterValue()
|
|
{
|
|
return (byte) mRegister;
|
|
}
|
|
|
|
public static Integral fromValue(int value)
|
|
{
|
|
if(0 <= value && value <= 31)
|
|
{
|
|
return Integral.values()[value];
|
|
}
|
|
|
|
throw new IllegalArgumentException("PLL integral value [" + value +
|
|
"] must be in the range 0 - 31");
|
|
}
|
|
}
|
|
}
|