/******************************************************************************* * 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(); 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 { 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"); } } }