- * Note: requires I2C repeater enabled - * - * @param gain - * @throws UsbException - */ - public void setLNAGain(E4KLNAGain gain, boolean controlI2CRepeater) throws UsbException - { - getAdapter().getLock().lock(); - - try - { - if(controlI2CRepeater) - { - getAdapter().enableI2CRepeater(); - } - - if(gain == E4KLNAGain.AUTOMATIC) - { - writeMaskedE4KRegister(Register.AGC1, AGC1_MOD_MASK, AGCMode.IF_SERIAL_LNA_AUTON.getValue(), false); - } - else - { - writeMaskedE4KRegister(Register.AGC1, AGC1_MOD_MASK, AGCMode.SERIAL.getValue(), false); - writeMaskedE4KRegister(E4KLNAGain.getRegister(), E4KLNAGain.getMask(), gain.getValue(), false); - } - - if(controlI2CRepeater) - { - getAdapter().disableI2CRepeater(); - } - } - finally - { - getAdapter().getLock().unlock(); - } - } - - /** - * Reads LNA gain from E4K tuner - *
- * Note: requires I2C repeater enabled - */ - public E4KLNAGain getLNAGain(boolean controlI2CRepeater) throws UsbException - { - return E4KLNAGain.fromRegisterValue(readE4KRegister(E4KLNAGain.getRegister(), controlI2CRepeater)); - } - - /** - * Sets enhanced gain for E4K repeater. - *
- * Note: requires I2C repeater enabled - * - * @param gain - * @throws UsbException - */ - public void setEnhanceGain(E4KEnhanceGain gain, boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(E4KEnhanceGain.getRegister(), E4KEnhanceGain.getMask(), gain.getValue(), controlI2CRepeater); - } - - /** - * Gets the enhanced gain setting in the E4K tuner. - *
- * Note: requires I2C repeater - * - * @return - * @throws UsbException - */ - public E4KEnhanceGain getEnhanceGain(boolean controlI2CRepeater) throws UsbException - { - return E4KEnhanceGain.fromRegisterValue(readE4KRegister(E4KEnhanceGain.getRegister(), controlI2CRepeater)); - } - - /** - * Sets the mixer gain - * - * @param gain to apply - * @param controlI2CRepeater set to true to turn on/off repeater within this method or false if already enabled. - * @throws UsbException on error - */ - public void setMixerGain(E4KMixerGain gain, boolean controlI2CRepeater) throws UsbException - { - getAdapter().getLock().lock(); - - try - { - if(controlI2CRepeater) - { - getAdapter().enableI2CRepeater(); - } - - boolean repeaterControl = false; - - if(gain == E4KMixerGain.AUTOMATIC) - { - writeMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, AGC7_MIX_GAIN_AUTO, repeaterControl); - } - else - { - writeMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, AGC7_MIX_GAIN_MANUAL, repeaterControl); - /* Set the desired manual gain setting */ - writeMaskedE4KRegister(E4KMixerGain.getRegister(), E4KMixerGain.getMask(), gain.getValue(), repeaterControl); - } - - if(controlI2CRepeater) - { - getAdapter().disableI2CRepeater(); - } - } - finally - { - getAdapter().getLock().unlock(); - } - } - - public E4KMixerGain getMixerGain(boolean controlI2CRepeater) throws UsbException - { - byte autoOrManual = readMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, controlI2CRepeater); - - if(autoOrManual == AGC7_MIX_GAIN_AUTO) - { - return E4KMixerGain.AUTOMATIC; - } - else - { - int register = readE4KRegister(E4KMixerGain.getRegister(), controlI2CRepeater); - return E4KMixerGain.fromRegisterValue(register); - } - } - - /** - * Sets IF Gains 1 - 6 using a set of linear gain values defined in the IFGain enumeration - * - * @param gain to apply - * @param controlI2CRepeater set false if I2C repeater is already enabled or true if the I2C repeater should - * be turned on and off within this method. - * @throws LibUsbException if the write operation doesn't complete successfully - */ - public void setIFGain(IFGain gain, boolean controlI2CRepeater) throws LibUsbException - { - getAdapter().getLock().lock(); - - try - { - byte linearityMode = gain.isLinearity() ? (byte) 0x00 : (byte) 0x10; - writeMaskedE4KRegister(Register.AGC1, gain.getLinearityModeMask(), linearityMode, controlI2CRepeater); - writeMaskedE4KRegister(Register.GAIN3, gain.getGain3Mask(), gain.getGain3(), controlI2CRepeater); - writeMaskedE4KRegister(Register.GAIN4, gain.getGain4Mask(), gain.getGain4(), controlI2CRepeater); - } - finally - { - getAdapter().getLock().unlock(); - } - } - - /** - * Sets the IF Stage 1 gain value. - */ - public void setIFStage1Gain(IFStage1Gain gain, boolean controlI2CRepeater) - { - if(controlI2CRepeater) - { - getAdapter().enableI2CRepeater(); - } - - writeMaskedE4KRegister(Register.GAIN3, gain.getValue(), gain.getMask(), false); - - if(controlI2CRepeater) - { - getAdapter().disableI2CRepeater(); - } - } - - public void setMixerFilter(MixerFilter filter, boolean controlI2CRepeater) throws LibUsbException - { - writeMaskedE4KRegister(MixerFilter.getRegister(), MixerFilter.getMask(), filter.getValue(), controlI2CRepeater); - } - - public MixerFilter getMixerFilter(boolean controlI2CRepeater) throws UsbException - { - int value = readE4KRegister(MixerFilter.getRegister(), controlI2CRepeater); - return MixerFilter.fromRegisterValue(value); - } - - public void setRCFilter(RCFilter filter, boolean controlI2CRepeater) throws LibUsbException - { - writeMaskedE4KRegister(RCFilter.getRegister(), RCFilter.getMask(), filter.getValue(), controlI2CRepeater); - } - - public RCFilter getRCFilter(boolean controlI2CRepeater) throws UsbException - { - int value = readE4KRegister(RCFilter.getRegister(), controlI2CRepeater); - return RCFilter.fromRegisterValue(value); - } - - public void setChannelFilter(ChannelFilter filter, boolean controlI2CRepeater) throws LibUsbException - { - writeMaskedE4KRegister(ChannelFilter.getRegister(), ChannelFilter.getMask(), filter.getValue(), controlI2CRepeater); - } - - public ChannelFilter getChannelFilter(boolean controlI2CRepeater) throws UsbException - { - int value = readE4KRegister(ChannelFilter.getRegister(), controlI2CRepeater); - return ChannelFilter.fromRegisterValue(value); - } - - public void setChannelFilterEnabled(boolean enabled, boolean controlI2CRepeater) throws UsbException - { - if(enabled) - { - writeMaskedE4KRegister(Register.FILT3, FILT3_MASK, FILT3_ENABLE, controlI2CRepeater); - } - else - { - writeMaskedE4KRegister(Register.FILT3, FILT3_MASK, FILT3_DISABLE, controlI2CRepeater); - } - } - - /** - * Enables or disables VCO auto-calibration. - */ - public void setVcoAutoCalibration(boolean enabled, boolean controlI2CRepeater) - { - byte value = enabled ? (byte) 0x01 : (byte) 0x00; - byte mask = (byte) 0x3; - writeMaskedE4KRegister(Register.SYNTH8, mask, value, controlI2CRepeater); - } - - public void setBand(long frequency, boolean controlI2CRepeater) throws UsbException - { - if(controlI2CRepeater) - { - getAdapter().enableI2CRepeater(); - } - - FrequencyBand frequencyBand = FrequencyBand.fromFrequency(frequency); - - /* Set the bias */ - switch(frequencyBand) - { - case UHF: - case VHF2: - case VHF3: - writeE4KRegister(Register.BIAS, (byte) 0x3, false); - break; - case L_BAND: - default: - writeE4KRegister(Register.BIAS, (byte) 0x0, false); - break; - - } - - /** - * Workaround - reset register (set value to 0) before writing to it, to - * avoid a gap around 325-350 MHz - */ - writeMaskedE4KRegister(Register.SYNTH1, FrequencyBand.getMask(), (byte) 0x0, false); - writeMaskedE4KRegister(Register.SYNTH1, FrequencyBand.getMask(), frequencyBand.getValue(), false); - - if(controlI2CRepeater) - { - getAdapter().disableI2CRepeater(); - } - } - - private void setRFFilter(long frequency, boolean controlI2CRepeater) throws UsbException - { - RFFilter filter = RFFilter.fromFrequency(frequency); - writeMaskedE4KRegister(Register.FILT1, RFFilter.getMask(), filter.getValue(), controlI2CRepeater); - } - - /** - * Sets the master gain combination setting. - * Note: does not respond to the MANUAL setting. Use the manual setting to configure gui components to expose the - * individual Mixer, LNA and Enhance gain settings, to allow the user to change those settings individually. - * - * @param gain - requested gain setting - * @throws UsbException if there are any errors - */ - public void setGain(E4KGain gain, boolean controlI2CRepeater) throws UsbException - { - getAdapter().getLock().lock(); - - try - { - if(gain != E4KGain.MANUAL) - { - if(controlI2CRepeater) - { - getAdapter().enableI2CRepeater(); - } - - boolean i2CRepeaterControl = false; - - setLNAGain(gain.getLNAGain(), i2CRepeaterControl); - setMixerGain(gain.getMixerGain(), i2CRepeaterControl); - - if(controlI2CRepeater) - { - getAdapter().disableI2CRepeater(); - } - } - } - finally - { - getAdapter().getLock().unlock(); - } - } - - /* - * Performs magic initialization ... and that's all we need to know, I guess - */ - private void magicInit(boolean controlI2CRepeater) throws UsbException - { - if(controlI2CRepeater) - { - getAdapter().enableI2CRepeater(); - } - - writeE4KRegister(Register.MAGIC_1, (byte) 0x01, false); - writeE4KRegister(Register.MAGIC_2, (byte) 0xFE, false); - writeE4KRegister(Register.MAGIC_3, (byte) 0x00, false); - writeE4KRegister(Register.MAGIC_4, (byte) 0x50, false); //Polarity A - writeE4KRegister(Register.MAGIC_5, (byte) 0x20, false); - writeE4KRegister(Register.MAGIC_6, (byte) 0x01, false); - writeE4KRegister(Register.MAGIC_7, (byte) 0x7F, false); - writeE4KRegister(Register.MAGIC_8, (byte) 0x07, false); - - if(controlI2CRepeater) - { - getAdapter().disableI2CRepeater(); - } - } - - private byte readMaskedE4KRegister(Register register, byte mask, boolean controlI2CRepeater) throws UsbException - { - int temp = readE4KRegister(register, controlI2CRepeater); - return (byte) (temp & mask); - } - - private void writeMaskedE4KRegister(Register register, byte mask, byte value, boolean controlI2CRepeater) throws LibUsbException - { - int temp = readE4KRegister(register, controlI2CRepeater); - - /* If the register is not set to the masked value, then change it */ - if((byte) (temp & mask) != value) - { - writeE4KRegister(register, (byte) ((temp & ~mask) | (value & mask)), controlI2CRepeater); - readE4KRegister(register, controlI2CRepeater); - } - } - - private int readE4KRegister(Register register, boolean controlI2CRepeater) throws LibUsbException - { - return getAdapter().readI2CRegister(Register.I2C_REGISTER.getValue(), register.getValue(), controlI2CRepeater); - } - - - private void writeE4KRegister(Register register, byte value, boolean controlI2CRepeater) throws LibUsbException - { - getAdapter().writeI2CRegister(Register.I2C_REGISTER.getValue(), register.getValue(), value, controlI2CRepeater); - } - - /** - * Frequency bands enumeration - */ - public enum FrequencyBand - { - VHF2(0), //64-108 MHz - VHF3(2), //170-240 MHz - UHF(4), //470-858 MHz - L_BAND(6); //1452-1680 MHz - - private int mValue; - - FrequencyBand(int value) - { - mValue = value; - } - - public static byte getMask() - { - return (byte) 0x06; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return toString(); - } - - public static FrequencyBand fromFrequency(long frequency) - { - if(frequency < 140000000) //140 MHz - { - return VHF2; - } - else if(frequency < 350000000) //350 MHz - { - return VHF3; - } - else if(frequency < 1135000000) //1135 MHz - { - return UHF; - } - else - { - return L_BAND; - } - } - } - - /** - * PLL enumeration for SYNTH7 (0x0D) Register to control the 'R' VCO output divider and 3-phase mixing. - *
- * Note: 3-phase mixing (1-bit) and R index (3-bits) are combined in the index value that is written to the register. - */ - public enum PLL - { - PLL_72M4(0x0F, 72400000, 48, 600000, true, "72.4 MHz"), - PLL_81M2(0x0E, 81200000, 40, 720000, true, "81.2 MHz"), - PLL_108M3(0x0D, 108300000, 32, 900000, true, "108.3 MHz"), - PLL_162M5(0x0C, 162500000, 24, 1200000, true, "162.5 MHz"), - PLL_216M6(0x0B, 216600000, 16, 1800000, true, "216.6 MHz"), - PLL_325M0(0x0A, 325000000, 12, 2400000, true, "325.0 MHz"), - PLL_350M0(0x09, 350000000, 8, 3600000, true, "350.0 MHz"), - PLL_432M0(0x03, 432000000, 8, 3600000, false, "432.0 MHz"), - PLL_667M0(0x02, 667000000, 6, 4800000, false, "667.0 MHz"), - PLL_1200M0(0x01, 1200000000, 4, 7200000, false, "1200.0 MHz"); - - private int mIndex; - private long mFrequency; - private int mMultiplier; - private int mScaledOscillator; - private boolean mRequires3PhaseMixing; - private String mLabel; - - PLL(int index, long frequency, int multiplier, int scaledOscillator, boolean requires3Phase, String label) - { - mIndex = index; - mFrequency = frequency; - mMultiplier = multiplier; - mScaledOscillator = scaledOscillator; - mRequires3PhaseMixing = requires3Phase; - mLabel = label; - } - - public byte getIndex() - { - return (byte) mIndex; - } - - public long getFrequency() - { - return mFrequency; - } - - public byte getMultiplier() - { - return (byte) mMultiplier; - } - - public int getScaledOscillator() - { - return mScaledOscillator; - } - - public boolean requires3PhaseMixing() - { - return mRequires3PhaseMixing; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - /** - * Returns the PLL setting with the closest frequency that is greater than the frequency argument - */ - public static PLL fromFrequency(long frequency) - { - for(PLL pll : values()) - { - if(frequency < pll.getFrequency()) - { - return pll; - } - } - - //Default - return PLL.PLL_72M4; - } - - public static PLL fromSetting(int setting) - { - int value = setting & 0xF; - - for(PLL pll : values()) - { - if(value == pll.getIndex()) - { - return pll; - } - } - - //Default - return PLL.PLL_72M4; - } - } - - /** - * RF filters - UHF Band. Each enum entry defines the center frequency for - * the filter and the applicable frequency minimums and maximums for the - * filter. - *
- * Note: max frequency overlaps with the next minimum frequency, so valid
- * frequencies should be less than the maximum frequency defined for the
- * filter.
- */
- public enum RFFilter
- {
- NO_FILTER(0, 0, 0),
-
- //VHF-II Filters (0.0 - 140.0) Optimal (64-108)
- LP268(0, MINIMUM_TUNABLE_FREQUENCY_HZ, 86_000_000), //Values 0 - 7 are all the same
- LP299(8, 86_000_000, 140_000_000), //Values 8 - 15 are all the same
-
- //VHF-III Filters (140.0 - 350.0) Optimal (170-240)
- LP509(0, 140_000_000, 245_000_000), //Values 0 - 7 are all the same
- LP656(8, 245_000_000, 350_000_000), //Values 8 - 15 are all the same
-
- //UHF Filters (350.0 - 1135) Optimal (470-858)
- BP360(0, 350000000, 370000000),
- BP380(1, 370000000, 392500000),
- BP405(2, 392500000, 417500000),
- BP425(3, 417500000, 437500000),
- BP450(4, 437500000, 462500000),
- BP475(5, 462500000, 490000000),
- BP505(6, 490000000, 522500000),
- BP540(7, 522500000, 557500000),
- BP575(8, 557500000, 595000000),
- BP615(9, 595000000, 642500000),
- BP670(10, 642500000, 695000000),
- BP720(11, 695000000, 740000000),
- BP760(12, 740000000, 800000000),
- BP840(13, 800000000, 865000000),
- BP890(14, 865000000, 930000000),
- BP970(15, 930000000, 1135000000),
-
- //L Band Filters (1135.0 - 2147.0) Optimal (1452-1680)
- BP1300(0, 1135000000, 1310000000),
- BP1320(1, 1310000000, 1340000000),
- BP1360(2, 1340000000, 1385000000),
- BP1410(3, 1385000000, 1427500000),
- BP1445(4, 1427500000, 1452500000),
- BP1460(5, 1452500000, 1475000000),
- BP1490(6, 1475000000, 1510000000),
- BP1530(7, 1510000000, 1545000000),
- BP1560(8, 1545000000, 1575000000),
- BP1590(9, 1575000000, 1615000000),
- BP1640(10, 1615000000, 1650000000),
- BP1660(11, 1650000000, 1670000000),
- BP1680(12, 1670000000, 1690000000),
- BP1700(13, 1690000000, 1710000000),
- BP1720(14, 1710000000, 1735000000),
- BP1750(15, 1735000000, MAXIMUM_TUNABLE_FREQUENCY_HZ);
-
- private int mValue;
- private long mMinFrequency;
- private long mMaxFrequency;
-
- RFFilter(int value, long minFrequency, long maxFrequency)
- {
- mValue = value;
- mMinFrequency = minFrequency;
- mMaxFrequency = maxFrequency;
- }
-
- public static Register getRegister()
- {
- return Register.FILT1;
- }
-
- public static byte getMask()
- {
- return (byte) 0xF;
- }
-
- public byte getValue()
- {
- return (byte) mValue;
- }
-
- /**
- * Minimum frequency for this filter.
- */
- public long getMinimumFrequency()
- {
- return mMinFrequency;
- }
-
- /**
- * Maximum frequency for this filter is less than this value, but does
- * not include this value.
- */
- public long getMaximumFrequency()
- {
- return mMaxFrequency;
- }
-
- /**
- * Filter ranges by frequency band
- */
- public static EnumSet
- * Manual is an override that keeps each of the settings intact and allows
- * the user to change each of them individually
- */
- public enum E4KGain
- {
- AUTOMATIC("Auto", E4KMixerGain.AUTOMATIC, E4KLNAGain.AUTOMATIC),
- MANUAL("Manual", null, null),
- MINUS_10("-1.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_50),
- PLUS_15("1.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_25),
- PLUS_40("4.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_0),
- PLUS_65("6.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_25),
- PLUS_90("9.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_50),
- PLUS_115("11.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_75),
- PLUS_140("14.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_100),
- PLUS_165("16.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_125),
- PLUS_190("19.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_150),
- PLUS_215("21.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_175),
- PLUS_240("24.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_200),
- PLUS_290("29.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_250),
- PLUS_340("34.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_300),
- PLUS_420("42.0 db", E4KMixerGain.GAIN_12, E4KLNAGain.GAIN_PLUS_300);
-
- private String mLabel;
- private E4KMixerGain mMixerGain;
- private E4KLNAGain mLNAGain;
-
- E4KGain(String label, E4KMixerGain mixerGain, E4KLNAGain lnaGain)
- {
- mLabel = label;
- mMixerGain = mixerGain;
- mLNAGain = lnaGain;
- }
-
- public String getLabel()
- {
- return mLabel;
- }
-
- public String toString()
- {
- return mLabel;
- }
-
- public E4KMixerGain getMixerGain()
- {
- return mMixerGain;
- }
-
- public E4KLNAGain getLNAGain()
- {
- return mLNAGain;
- }
- }
-
- public enum E4KEnhanceGain
- {
- AUTOMATIC(0, "Auto"),
- GAIN_1(1, "10 db"),
- GAIN_3(3, "30 db"),
- GAIN_5(5, "50 db"), //Recommended
- GAIN_7(7, "70 db");
-
- private int mValue;
- private String mLabel;
-
- E4KEnhanceGain(int value, String label)
- {
- mValue = value;
- mLabel = label;
- }
-
- public static Register getRegister()
- {
- return Register.AGC11;
- }
-
- public static byte getMask()
- {
- return ENH_GAIN_MOD_MASK;
- }
-
- public byte getValue()
- {
- return (byte) mValue;
- }
-
- public String getLabel()
- {
- return mLabel;
- }
-
- public String toString()
- {
- return mLabel;
- }
-
- public static E4KEnhanceGain fromRegisterValue(int registerValue)
- {
- int value = registerValue & getMask();
-
- for(E4KEnhanceGain setting : values())
- {
- if(value == setting.getValue())
- {
- return setting;
- }
- }
-
- throw new IllegalArgumentException("E4KTunerController - unrecognized Enhance Gain value [" + value + "]");
- }
- }
-
- public enum DCGainCombination
- {
- LOOKUP_TABLE_0(Register.QLUT0, Register.ILUT0, E4KMixerGain.GAIN_4, IFStage1Gain.GAIN_MINUS3),
- LOOKUP_TABLE_1(Register.QLUT1, Register.ILUT1, E4KMixerGain.GAIN_4, IFStage1Gain.GAIN_PLUS6),
- LOOKUP_TABLE_2(Register.QLUT2, Register.ILUT2, E4KMixerGain.GAIN_12, IFStage1Gain.GAIN_MINUS3),
- LOOKUP_TABLE_3(Register.QLUT3, Register.ILUT3, E4KMixerGain.GAIN_12, IFStage1Gain.GAIN_PLUS6);
-
- private Register mQRegister;
- private Register mIRegister;
- private E4KMixerGain mMixerGain;
- private IFStage1Gain mIFStage1Gain;
-
- DCGainCombination(Register qRegister, Register iRegister, E4KMixerGain mixer, IFStage1Gain stage1)
- {
- mQRegister = qRegister;
- mIRegister = iRegister;
- mMixerGain = mixer;
- mIFStage1Gain = stage1;
- }
-
- public Register getQRegister()
- {
- return mQRegister;
- }
-
- public Register getIRegister()
- {
- return mIRegister;
- }
-
- public E4KMixerGain getMixerGain()
- {
- return mMixerGain;
- }
-
- public IFStage1Gain getIFStage1Gain()
- {
- return mIFStage1Gain;
- }
- }
-
-
- public enum E4KMixerGain
- {
- AUTOMATIC(-1, "Auto"),
- GAIN_4(0, "4 db"),
- GAIN_12(1, "12 db");
-
- private int mValue;
- private String mLabel;
-
- E4KMixerGain(int value, String label)
- {
- mValue = value;
- mLabel = label;
- }
-
- public static Register getRegister()
- {
- return Register.GAIN2;
- }
-
- public static byte getMask()
- {
- return MIXER_GAIN_MASK;
- }
-
- public byte getValue()
- {
- return (byte) mValue;
- }
-
- public String getLabel()
- {
- return mLabel;
- }
-
- public String toString()
- {
- return mLabel;
- }
-
- public static E4KMixerGain fromRegisterValue(int registerValue)
- {
- int value = registerValue & getMask();
-
- for(E4KMixerGain setting : values())
- {
- if(value == setting.getValue())
- {
- return setting;
- }
- }
-
- throw new IllegalArgumentException("E4KTunerController - unrecognized Mixer Gain value [" + value + "]");
- }
- }
-
- /**
- * Combines IF Gains 1 - 4 (register 0x16h GAIN3) and IF Gains 5 - 6 (register 0x17h GAIN4) into a contiguous
- * range if IF gains, per data sheet table page 38, for both Linearity and Sensitivity modes.
- */
- public enum IFGain
- {
- LINEARITY_6((byte) 0x00, (byte) 0x00, "6"),
- LINEARITY_7((byte) 0x20, (byte) 0x00, "7"),
- LINEARITY_8((byte) 0x40, (byte) 0x00, "8"),
- LINEARITY_9((byte) 0x00, (byte) 0x08, "9"),
- LINEARITY_10((byte) 0x20, (byte) 0x08, "10"),
- LINEARITY_11((byte) 0x40, (byte) 0x08, "11"),
- LINEARITY_12((byte) 0x00, (byte) 0x10, "12"),
- LINEARITY_13((byte) 0x20, (byte) 0x10, "13"),
- LINEARITY_14((byte) 0x40, (byte) 0x10, "14"),
- LINEARITY_15((byte) 0x00, (byte) 0x18, "15"),
- LINEARITY_16((byte) 0x20, (byte) 0x18, "16"),
- LINEARITY_17((byte) 0x40, (byte) 0x18, "17"),
- LINEARITY_18((byte) 0x00, (byte) 0x20, "18"),
- LINEARITY_19((byte) 0x20, (byte) 0x20, "19"),
- LINEARITY_20((byte) 0x40, (byte) 0x20, "20"),
- LINEARITY_21((byte) 0x00, (byte) 0x21, "21"),
- LINEARITY_22((byte) 0x20, (byte) 0x21, "22"),
- LINEARITY_23((byte) 0x40, (byte) 0x21, "23"),
- LINEARITY_24((byte) 0x00, (byte) 0x22, "24"),
- LINEARITY_25((byte) 0x20, (byte) 0x22, "25"),
- LINEARITY_26((byte) 0x40, (byte) 0x22, "26"),
- LINEARITY_27((byte) 0x00, (byte) 0x23, "27"),
- LINEARITY_28((byte) 0x20, (byte) 0x23, "28"),
- LINEARITY_29((byte) 0x40, (byte) 0x23, "29"),
- LINEARITY_30((byte) 0x00, (byte) 0x24, "30"),
- LINEARITY_31((byte) 0x20, (byte) 0x24, "31"),
- LINEARITY_32((byte) 0x40, (byte) 0x24, "32"),
- LINEARITY_33((byte) 0x08, (byte) 0x24, "33"),
- LINEARITY_34((byte) 0x28, (byte) 0x24, "34"),
- LINEARITY_35((byte) 0x48, (byte) 0x24, "35"),
- LINEARITY_36((byte) 0x10, (byte) 0x24, "36"),
- LINEARITY_37((byte) 0x30, (byte) 0x24, "37"),
- LINEARITY_38((byte) 0x50, (byte) 0x24, "38"),
- LINEARITY_39((byte) 0x18, (byte) 0x24, "39"),
- LINEARITY_40((byte) 0x38, (byte) 0x24, "40"),
- LINEARITY_41((byte) 0x58, (byte) 0x24, "41"),
- LINEARITY_42((byte) 0x1A, (byte) 0x24, "42"),
- LINEARITY_43((byte) 0x3A, (byte) 0x24, "43"),
- LINEARITY_44((byte) 0x5A, (byte) 0x24, "44"),
- LINEARITY_45((byte) 0x1C, (byte) 0x24, "45"),
- LINEARITY_46((byte) 0x3C, (byte) 0x24, "46"),
- LINEARITY_47((byte) 0x5C, (byte) 0x24, "47"),
- LINEARITY_48((byte) 0x19, (byte) 0x24, "48"),
- LINEARITY_49((byte) 0x39, (byte) 0x24, "49"),
- LINEARITY_50((byte) 0x59, (byte) 0x24, "50"),
- LINEARITY_51((byte) 0x1B, (byte) 0x24, "51"),
- LINEARITY_52((byte) 0x3B, (byte) 0x24, "52"),
- LINEARITY_53((byte) 0x5B, (byte) 0x24, "53"),
- LINEARITY_54((byte) 0x1D, (byte) 0x24, "54"),
- LINEARITY_55((byte) 0x3D, (byte) 0x24, "55"),
- LINEARITY_56((byte) 0x5D, (byte) 0x24, "56"),
- LINEARITY_57((byte) 0x1F, (byte) 0x24, "57"),
- LINEARITY_58((byte) 0x3F, (byte) 0x24, "58"),
- LINEARITY_59((byte) 0x5F, (byte) 0x24, "59"),
- LINEARITY_60((byte) 0x7F, (byte) 0x24, "60"),
-
- SENSITIVITY_6((byte) 0x00, (byte) 0x00, "6"),
- SENSITIVITY_7((byte) 0x20, (byte) 0x00, "7"),
- SENSITIVITY_8((byte) 0x40, (byte) 0x00, "8"),
- SENSITIVITY_9((byte) 0x02, (byte) 0x00, "9"),
- SENSITIVITY_10((byte) 0x22, (byte) 0x00, "10"),
- SENSITIVITY_11((byte) 0x44, (byte) 0x00, "11"),
- SENSITIVITY_12((byte) 0x04, (byte) 0x00, "12"),
- SENSITIVITY_13((byte) 0x24, (byte) 0x00, "13"),
- SENSITIVITY_14((byte) 0x44, (byte) 0x00, "14"),
- SENSITIVITY_15((byte) 0x01, (byte) 0x00, "15"),
- SENSITIVITY_16((byte) 0x21, (byte) 0x00, "16"),
- SENSITIVITY_17((byte) 0x41, (byte) 0x00, "17"),
- SENSITIVITY_18((byte) 0x03, (byte) 0x00, "18"),
- SENSITIVITY_19((byte) 0x23, (byte) 0x00, "19"),
- SENSITIVITY_20((byte) 0x43, (byte) 0x00, "20"),
- SENSITIVITY_21((byte) 0x05, (byte) 0x00, "21"),
- SENSITIVITY_22((byte) 0x25, (byte) 0x00, "22"),
- SENSITIVITY_23((byte) 0x45, (byte) 0x00, "23"),
- SENSITIVITY_24((byte) 0x07, (byte) 0x00, "24"),
- SENSITIVITY_25((byte) 0x27, (byte) 0x00, "25"),
- SENSITIVITY_26((byte) 0x47, (byte) 0x00, "26"),
- SENSITIVITY_27((byte) 0x0F, (byte) 0x00, "27"),
- SENSITIVITY_28((byte) 0x2F, (byte) 0x00, "28"),
- SENSITIVITY_29((byte) 0x4F, (byte) 0x00, "29"),
- SENSITIVITY_30((byte) 0x17, (byte) 0x00, "30"),
- SENSITIVITY_31((byte) 0x37, (byte) 0x00, "31"),
- SENSITIVITY_32((byte) 0x57, (byte) 0x00, "32"),
- SENSITIVITY_33((byte) 0x1F, (byte) 0x00, "33"),
- SENSITIVITY_34((byte) 0x3F, (byte) 0x00, "34"),
- SENSITIVITY_35((byte) 0x5F, (byte) 0x00, "35"),
- SENSITIVITY_36((byte) 0x1F, (byte) 0x01, "36"),
- SENSITIVITY_37((byte) 0x3F, (byte) 0x01, "37"),
- SENSITIVITY_38((byte) 0x5F, (byte) 0x01, "38"),
- SENSITIVITY_39((byte) 0x1F, (byte) 0x02, "39"),
- SENSITIVITY_40((byte) 0x3F, (byte) 0x02, "40"),
- SENSITIVITY_41((byte) 0x5F, (byte) 0x02, "41"),
- SENSITIVITY_42((byte) 0x1F, (byte) 0x03, "42"),
- SENSITIVITY_43((byte) 0x3F, (byte) 0x03, "43"),
- SENSITIVITY_44((byte) 0x5F, (byte) 0x03, "44"),
- SENSITIVITY_45((byte) 0x1F, (byte) 0x04, "45"),
- SENSITIVITY_46((byte) 0x3F, (byte) 0x04, "46"),
- SENSITIVITY_47((byte) 0x5F, (byte) 0x04, "47"),
- SENSITIVITY_48((byte) 0x1F, (byte) 0x0C, "48"),
- SENSITIVITY_49((byte) 0x3F, (byte) 0x0C, "49"),
- SENSITIVITY_50((byte) 0x5F, (byte) 0x0C, "50"),
- SENSITIVITY_51((byte) 0x1F, (byte) 0x14, "51"),
- SENSITIVITY_52((byte) 0x3F, (byte) 0x14, "52"),
- SENSITIVITY_53((byte) 0x5F, (byte) 0x14, "53"),
- SENSITIVITY_54((byte) 0x1F, (byte) 0x1C, "54"),
- SENSITIVITY_55((byte) 0x3F, (byte) 0x1C, "55"),
- SENSITIVITY_56((byte) 0x5F, (byte) 0x1C, "56"),
- SENSITIVITY_57((byte) 0x1F, (byte) 0x24, "57"),
- SENSITIVITY_58((byte) 0x3F, (byte) 0x24, "58"),
- SENSITIVITY_59((byte) 0x5F, (byte) 0x24, "59"),
- SENSITIVITY_60((byte) 0x7F, (byte) 0x24, "60");
-
- private byte mGain3;
- private byte mGain4;
- private String mLabel;
-
- IFGain(byte gain3, byte gain4, String label)
- {
- mGain3 = gain3;
- mGain4 = gain4;
- mLabel = label;
- }
-
- /**
- * Value to apply to AGC3 register
- */
- public byte getGain3()
- {
- return mGain3;
- }
-
- /**
- * Value to apply to AGC4 register
- */
- public byte getGain4()
- {
- return mGain4;
- }
-
- /**
- * Register mask for AGC3
- */
- public byte getGain3Mask()
- {
- return (byte) 0x7F;
- }
-
- /**
- * Register mask for AGC4
- */
- public byte getGain4Mask()
- {
- return (byte) 0x3F;
- }
-
- /**
- * Linearity mode mask for AGC1 (0x1A) register
- */
- public byte getLinearityModeMask()
- {
- return (byte) 0x10;
- }
-
- /**
- * Set of linearity gains
- */
- public static EnumSet
- * Ported from: https://github.com/osmocom/rtl-sdr/blob/master/src/tuner_fc0013.c
- *
- * Register: Original Contents / Shadow Contents - Comments
- * 0x00: .. / 00 - Dummy register for 0-index
- * 0x01: 05 / 09
- * 0x02: 10 / 16
- * 0x03: 00 / 00
- * 0x04: 00 / 00
- * 0x05: 0A / 17
- * 0x06: 0A / 02 - Low Pass Filter Bandwidth
- * 0x07: 00 / 2A - 0x2x setup for 28.8MHz crystal
- * 0x08: B0 / FF - AGC clock divide by 256, AGC gain 1/256, Loop bw 1/8
- * 0x09: 6E / 6E - Disable loop through, enable loop through: 0x6F
- * 0x0A: B8 / B8 - Disable LO Test Buffer
- * 0x0B: 82 / 82
- * 0x0C: 7C / FC - Depending on AGC Up-Down mode, may need 0xF8
- * 0x0D: 00 / 01 - AGC not forcing & LNA forcing, we may need 0x02
- * 0x0E: 00 / 00
- * 0x0F: 00 / 00
- * 0x10: 00 / 00
- * 0x11: 08 / 00
- * 0x12: 00 / 00
- * 0x13: 00 / 00
- * 0x14: C0 / 50 - DVB-t=High Gain, UHF=middle gain (0x48), or low gain (0x40)
- * 0x15: 01 / 01
- */
-public class FC0013EmbeddedTuner extends EmbeddedTuner
-{
- private final static Logger mLog = LoggerFactory.getLogger(FC0013EmbeddedTuner.class);
- private DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.000000");
- public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 13_500_000;
- public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 1_907_999_890l;
- private static final double USABLE_BANDWIDTH_PERCENT = 0.95;
- private static final int DC_SPIKE_AVOID_BUFFER = 15000;
- //Hardware I2C address
- private static final byte I2C_ADDRESS = (byte) 0xC6;
- //Hardware crystal oscillator frequency - used by the frequency divider
- private static final int XTAL_FREQUENCY = 28_800_000;
- private static final int XTAL_FREQUENCY_DIVIDED_BY_2 = XTAL_FREQUENCY / 2;
- //Initial register settings to apply to the tuner
- private static byte[] REGISTERS = {(byte) 0x00, (byte) 0x09, (byte) 0x16, (byte) 0x00, (byte) 0x00, (byte) 0x17,
- (byte) 0x02, (byte) 0x2A, (byte) 0xFF, (byte) 0x6E, (byte) 0xB8, (byte) 0x82, (byte) 0xFE, (byte) 0x01,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x50, (byte) 0x01};
- private long mTunedFrequency = MINIMUM_TUNABLE_FREQUENCY_HZ;
-
- /**
- * Constructs an instance
- *
- * @param adapter for controlling the RTL2832
- */
- public FC0013EmbeddedTuner(RTL2832TunerController.ControllerAdapter adapter)
- {
- super(adapter);
- }
-
- @Override
- public TunerType getTunerType()
- {
- return TunerType.FITIPOWER_FC0013;
- }
-
- @Override
- public long getMinimumFrequencySupported()
- {
- return MINIMUM_TUNABLE_FREQUENCY_HZ;
- }
-
- @Override
- public long getMaximumFrequencySupported()
- {
- return MAXIMUM_TUNABLE_FREQUENCY_HZ;
- }
-
- @Override
- public int getDcSpikeHalfBandwidth()
- {
- return DC_SPIKE_AVOID_BUFFER;
- }
-
- @Override
- public double getUsableBandwidthPercent()
- {
- return USABLE_BANDWIDTH_PERCENT;
- }
-
- /**
- * Applies the tuner configuration settings.
- * @param tunerConfig containing settings to apply
- * @throws SourceException if there is an error.
- */
- @Override
- public void apply(TunerConfiguration tunerConfig) throws SourceException
- {
- if(tunerConfig instanceof FC0013TunerConfiguration config)
- {
- try
- {
- setGain(config.getAGC(), config.getLnaGain());
- }
- catch(Exception e)
- {
- throw new SourceException("Error while applying tuner config", e);
- }
- }
- else
- {
- throw new IllegalArgumentException("Unrecognized tuner config [" + tunerConfig.getClass() + "]");
- }
- }
-
- /**
- * Writes the value to the FC0013 register and optionally controls the I2C repeater
- *
- * @param register to write
- * @param value to write
- * @param controlI2CRepeater true to turn on the I2C repeater before and after the write operation. If this is
- * set to false, you need to have previously enabled the I2C repeater before invoking this method call.
- */
- private void write(Register register, byte value, boolean controlI2CRepeater)
- {
- getAdapter().writeI2CRegister(I2C_ADDRESS, register.byteValue(), value, controlI2CRepeater);
- }
-
- /**
- * Writes the value to the FC0013 register and optionally controls the I2C repeater
- *
- * @param register to write
- * @param value to write
- * @param mask to use when reading the full register byte that has ones in the bits that should be untouched and
- * zeros in the bits that should be cleared prior to applying (or'ing) the value into the register.
- * @param controlI2CRepeater true to turn on the I2C repeater before and after the write operation. If this is
- * set to false, you need to have previously enabled the I2C repeater before invoking this method call.
- */
- public void writeMaskedRegister(Register register, byte value, byte mask, boolean controlI2CRepeater)
- {
- byte content = (byte) (readRegister(register, controlI2CRepeater) & 0xFF);
- content &= mask;
- content |= value;
- write(register, content, controlI2CRepeater);
- }
-
- /**
- * Writes the value to a field within an FC0013 register and optionally controls the I2C repeater
- *
- * @param field to write
- * @param value to write
- * @param controlI2CRepeater true to turn on the I2C repeater before and after the write operation. If this is
- * set to false, you need to have previously enabled the I2C repeater before invoking this method call.
- */
- private void write(Field field, byte value, boolean controlI2CRepeater)
- {
- writeMaskedRegister(field.getRegister(), value, field.getMask(), controlI2CRepeater);
- }
-
- /**
- * Reads the FC0013 tuner register value.
- *
- * @param register to read
- * @param controlI2CRepeater true to turn on the I2C repeater before and after the write operation. If this is
- * set to false, you need to have previously enabled the I2C repeater before invoking this method call.
- * @return register value.
- */
- private int readRegister(Register register, boolean controlI2CRepeater)
- {
- return getAdapter().readI2CRegister(I2C_ADDRESS, register.byteValue(), controlI2CRepeater);
- }
-
- @Override
- public void setSamplingMode(RTL2832TunerController.SampleMode mode) throws LibUsbException
- {
- //No-op
- }
-
- @Override
- public void setSampleRateFilters(int sampleRate) throws SourceException
- {
- //No-op
- }
-
- /**
- * Currently tuned frequency.
- * @return frequency
- * @throws SourceException never
- */
- public long getTunedFrequency() throws SourceException
- {
- return mTunedFrequency;
- }
-
- /**
- * Sets the frequency value to the tuner. Note: this is managed by the frequency controller along with PPM adjust.
- * @param frequency in Hertz to apply to the tuner
- * @throws SourceException if there is an error.
- */
- @Override
- public synchronized void setTunedFrequency(long frequency) throws SourceException
- {
- getAdapter().getLock().lock();
-
- try
- {
- //Capture current I2C repeater state and enable if necessary.
- boolean i2CEnabledState = getAdapter().isI2CRepeaterEnabled();
- if(!i2CEnabledState)
- {
- getAdapter().enableI2CRepeater();
- }
-
- setVhfTracking(frequency);
- setFilters(frequency);
-
- FrequencyDivider divider = FrequencyDivider.fromFrequency(frequency);
- boolean vcoSelect = ((double)frequency / (double)divider.getIntegralFrequencyMultiplier()) >= 212.5;
- int integral = (int)(frequency / divider.getIntegralFrequencyMultiplier());
- int pm = integral / 8; //pm is units of 8x integrals
- pm = Math.max(pm, 11);
- pm = Math.min(pm, 31);
-
- int am = integral - (pm * 8); //am is units of 1x integrals
- if(am < 2)
- {
- am += 8;
- pm--;
- }
- am = Math.min(am, 15);
- integral = pm * 8 + am;
-
- int residual = (int)(frequency - (integral * divider.getIntegralFrequencyMultiplier()));
-
- //fractional is units of 1/65536th of 2x integrals
- int fractional = (int)Math.round(residual / divider.getFractionalFrequencyMultiplier());
-
- if(pm < 11 || pm > 31 || am < 2 || am > 15 || fractional < 0 || fractional > 65535)
- {
- String message = "FC0013 - no valid PLL combination for frequency [" + frequency + "] using divider [" +
- divider + "] pm [" + pm + "] am [" + am + "] fractional [" + fractional + "]";
- mLog.error(message);
- throw new SourceException(message);
- }
-
- setFrequencyValues(divider, pm, am, fractional, vcoSelect);
-
- //Restore the I2C repeater to previous state
- if(!i2CEnabledState)
- {
- getAdapter().disableI2CRepeater();
- }
- }
- catch(LibUsbException e)
- {
- mLog.error("FC0013 tuner error while setting tuned frequency [" + frequency + "]", e);
- }
- finally
- {
- getAdapter().getLock().unlock();
- }
-
- mTunedFrequency = frequency;
- }
-
- /**
- * Apply initial settings to the tuner
- */
- protected void initTuner()
- {
- getAdapter().enableI2CRepeater();
- boolean i2CRepeaterControl = false;
-
- //Write initial register settings to the tuner
- for(int x = 1; x < REGISTERS.length; x++)
- {
- write(Register.values()[x], REGISTERS[x], i2CRepeaterControl);
- }
-
- write(Field.R0D_AGC, (byte)0x08, false);
- write(Register.R13, (byte)0x0A, false);
- getAdapter().disableI2CRepeater();
- }
-
- /**
- * Applies the frequency settings and calibrates the PLL. Note: I2C repeater must be enabled before invoking.
- * @param divider to use
- * @param pm units of 8x integrals
- * @param am units of 1x integrals
- * @param fractional value.
- * @param vcoSelect to enable (true) or disable (false)
- * @throws SourceException if the settings are invalid or the PLL can't be calibrated with the settings.
- * @return true if the frequency settings were applied successfully.
- */
- private boolean setFrequencyValues(FrequencyDivider divider, int pm, int am, int fractional, boolean vcoSelect)
- throws SourceException
- {
- if(pm < 11 || pm > 31)
- {
- throw new IllegalArgumentException("PM value [" + pm + "] must be in range 11-31");
- }
- if(am < 2 || am > 15)
- {
- throw new IllegalArgumentException("AM value [" + am + "] must be in range 2-15");
- }
- if(fractional < 0 || fractional > 65535)
- {
- throw new IllegalArgumentException("Fractional value [" + fractional + "] must be in range 0-65,535");
- }
- double exactFrequency = divider.calculate(pm, am, fractional);
- long frequency = (long)exactFrequency;
- byte register5 = divider.getRegister5();
- register5 |= 0x07; //modified for Realtek demod
-
- byte register6 = divider.getRegister6();
- if(vcoSelect)
- {
- register6 |= 0x08;
- }
-
- //Write the integral units to the PLL
- write(Register.R01, (byte)(am & 0xFF), false);
- write(Register.R02, (byte)(pm & 0xFF), false);
-
- //Write the fractional units to the PLL
- write(Register.R03, (byte)((fractional >> 8) & 0xFF), false);
- write(Register.R04, (byte)(fractional & 0xFF), false);
-
- //Set the frequency divider, bandwidth, and enable the clock output
- write(Register.R05, register5, false);
- write(Register.R06, register6, false);
-
- //Whatever register 11 does ... seems like it's a gain thing.
- int tmp = readRegister(Register.R11, false);
- if(divider == FrequencyDivider.D64)
- {
- write(Register.R11, (byte)(tmp | 0x04), false);
- }
- else
- {
- write(Register.R11, (byte)(tmp & 0xFB), false);
- }
-
- //Perform VCO Calibration
- write(Register.R0E, (byte)0x80, false);
- write(Register.R0E, (byte)0x00, false);
- write(Register.R0E, (byte)0x00, false);
- int calibration = readRegister(Register.R0E, false) & 0x3F;
-
- boolean recalibrateRequired = false;
- if(vcoSelect && calibration > 0x3C)
- {
- register6 &= ~0x08;
- recalibrateRequired = true;
- }
- else if(!vcoSelect && calibration < 0x02)
- {
- register6 |= 0x08;
- recalibrateRequired = true;
- }
-
- if(recalibrateRequired)
- {
- write(Register.R06, register6, false);
- write(Register.R0E, (byte)0x80, false);
- write(Register.R0E, (byte)0x00, false);
- write(Register.R0E, (byte)0x00, false);
- calibration = readRegister(Register.R0E, false) & 0x3F;
- if((!vcoSelect & calibration < 0x02) || (vcoSelect & calibration > 0x3C))
- {
- String msg = "Unable to tune frequency [" + fractional + "] PLL calibration [" + Integer.toHexString(calibration).toUpperCase() + "] out of limits [02-3C]";
- mLog.error(msg);
- throw new SourceException(msg);
- }
- }
-
- return true;
- }
-
- /**
- * Sets the tuner filters according to the tuned frequency. Note: I2C repeater must be enabled before invoking.
- * @param frequency of the tuner
- */
- private void setFilters(long frequency)
- {
- byte vhf = (byte)0x00; //default to disabled
- byte uhf = (byte)0x00; //default to disabled
-
- if(frequency < 300_000_000)
- {
- vhf = (byte)0x10; //enable
- }
- else if(frequency < 862_000_000)
- {
- uhf = (byte)0x40;
- }
- else
- {
- //The original source code seems to target a GPS filter, but it's using the same uhf filter setting.
- uhf = (byte)0x40;
- }
-
- write(Field.R07_VHF_FILTER, vhf, false);
- write(Field.R14_UHF_FILTER, uhf, false);
- }
-
- /**
- * Sets VHF tracking according to the tuned frequency. Note: I2C repeater must be enabled before invoking.
- * @param frequency for the tuner
- */
- private void setVhfTracking(long frequency)
- {
- int tmp = readRegister(Register.R1D, false);
-
- //Clear the middle 3x bits that we're going to set and leave the remaining bits untouched.
- tmp &= 0xE3;
-
- if(frequency <= 177_500_000)
- {
- tmp |= 0x1C; //VHF Track 7
- }
- else if(frequency <= 184_500_000)
- {
- tmp |= 0x18; //VHF Track 6
- }
- else if(frequency <= 191_500_000)
- {
- tmp |= 0x14; //VHF Track 5
- }
- else if(frequency <= 198_500_000)
- {
- tmp |= 0x10; //VHF Track 4
- }
- else if(frequency <= 205_500_000)
- {
- tmp |= 0x0C; //VHF Track 3
- }
- else if(frequency <= 219_500_000)
- {
- tmp |= 0x08; //VHF Track 2
- }
- else if(frequency <= 300_000_000)
- {
- tmp |= 0x04; //VHF Track 1
- }
- else
- {
- tmp |= 0x1C; //UHF & GPS
- }
-
- write(Register.R1D, (byte) tmp, false);
- }
-
- /**
- * Sets the gain for the tuner. Note: if agc=true, the manual gain setting is ignored.
- * @param agc automatic gain control.
- * @param manualGain setting.
- */
- public void setGain(boolean agc, LNAGain manualGain)
- {
- getAdapter().getLock().lock();
-
- try
- {
- boolean repeaterState = getAdapter().isI2CRepeaterEnabled();
- if(!repeaterState)
- {
- getAdapter().enableI2CRepeater();
- }
-
- if(agc)
- {
- write(Field.R0D_AGC, (byte)0x00, false);
- write(Register.R13, (byte)0x0A, false);
- }
- else
- {
- write(Field.R14_LNA_GAIN, manualGain.byteValue(), false);
- write(Field.R0D_AGC, (byte)0x08, false);
- write(Register.R13, (byte)0x0A, false);
- }
-
- if(!repeaterState)
- {
- getAdapter().disableI2CRepeater();
- }
- }
- finally
- {
- getAdapter().getLock().unlock();
- }
- }
-
- /**
- * Enumeration of FC0013 registers. Provides convenience method for accessing the entry's ordinal position as a
- * byte value for reading/writing.
- */
- private enum Register
- {
- R00, R01, R02, R03, R04, R05, R06, R07, R08, R09, R0A, R0B, R0C, R0D, R0E, R0F, R10, R11, R12, R13, R14, R15,
- R16, R17, R18, R19, R1A, R1B, R1C, R1D;
-
- /**
- * Returns the ordinal position of the entry within the enumeration as a byte value.
- */
- public byte byteValue()
- {
- return (byte) ordinal();
- }
- }
-
- /**
- * Masked tuner register fields enumeration
- */
- private enum Field
- {
- R06_BANDWIDTH(Register.R06, 0xC0),
- R07_VHF_FILTER(Register.R07, 0xEF),
- R0D_AGC(Register.R0D, 0xF7),
- R14_LNA_GAIN(Register.R14, 0xE0),
- R14_UHF_FILTER(Register.R14, 0x1F);
-
- private Register mRegister;
- private byte mMask;
-
- /**
- * Constructs an instance
- * @param register for the field
- * @param mask for the field
- */
- Field(Register register, int mask)
- {
- mRegister = register;
- mMask = (byte) mask;
- }
-
- /**
- * Register for the field.
- * @return register
- */
- public Register getRegister()
- {
- return mRegister;
- }
-
- /**
- * Bit field mask for the register that masks out the target field bits prior to applying a value.
- * @return mask value.
- */
- public byte getMask()
- {
- return mMask;
- }
- }
-
- /**
- * LNA gain values enumeration
- */
- public enum LNAGain
- {
- G00("0 LOW", 0x02),
- G01("1", 0x03),
- G02("2", 0x05),
- G03("3", 0x04),
- G04("4", 0x00),
- G05("5", 0x07),
- G06("6", 0x01),
- G07("7", 0x06),
-
- G08("8", 0x0F),
- G09("9", 0x0E),
- G10("10", 0x0D),
- G11("11", 0x0C),
- G12("12", 0x0B),
- G13("13", 0x0A),
- G14("14", 0x09),
- G15("15", 0x08),
-
- G16("16", 0x17),
- G17("17", 0x16),
- G18("18", 0x15),
- G19("19", 0x14),
- G20("20", 0x13),
- G21("21", 0x12),
- G22("22", 0x11),
- G23("23 HIGH", 0x10);
-
- private String mLabel;
- private byte mValue;
-
- /**
- * Constructs an instance
- *
- * @param label to show to the user
- * @param value to apply to the register
- */
- LNAGain(String label, int value)
- {
- mLabel = label;
- mValue = (byte) value;
- }
-
- /**
- * Byte value for the gain setting.
- *
- * @return byte value.
- */
- public byte byteValue()
- {
- return mValue;
- }
-
- @Override
- public String toString()
- {
- return mLabel;
- }
- }
-
- /**
- * SDM PLL Frequency Divider and Supported Frequency Ranges
- *
- * Note: each divider frequency range is overlapping with other divider frequency range(s) and also within each
- * frequency divider there are overlapping frequency range across each PM value. Each unit of PM is 8x the integral
- * and each unit of AM is 1x the integral. You can specify a range of 2-15 units of AM which causes overlap when
- * each unit of PM represents 8x units of AM.
- *
- * So, for a requested frequency, there are likely multiple combinations of Divider/PM/AM/Fractional that will
- * tune the frequency, each with varying degrees of correctness.
- *
- * For each divider range, minimum and maximum frequency is calculated as:
- * Min: PM = 11, AM = 2, FRACTIONAL = 0
- * Max: PM = 31, AM = 15, FRACTIONAL = 65,535, or the highest setting that can be calibrated by the VCO.
- *
- * integral = XTAL_FREQ / 2 / divider
- *
- * Where: (integral * ((PM * 8) + AM)) + (2 * integral / 65,536 * fractional)
- *
- * Minimum tunable frequency step size is (2 * integral / 65,536).
- */
- public enum FrequencyDivider
- {
- D96(96, true, 0x82, 13_500_000, 39_749_997),
- D64(64, false, 0x02, 20_250_000, 59_624_996), //Special one, why?
- D48(48, true, 0x42, 27_000_000, 79_499_995),
- D32(32, false, 0x82, 40_500_000, 119_249_993),
- D24(24, true, 0x22, 54_000_000, 158_999_990),
- D16(16, false, 0x42, 81_000_000, 238_499_986),
- D12(12, true, 0x12, 108_000_000, 317_999_981),
- D08(8, false, 0x22, 162_000_000, 476_999_972),
- D06(6, true, 0x0A, 235_200_000, 635_999_963),
- D04(4, false, 0x12, 514_900_000, 953_999_945),
- D02(2, false, 0x0A, 648_000_000, 1_907_999_890);
-
- private int mDivider;
- private boolean mIs3xMode;
- private int mRegister5;
- private long mMinimumFrequency;
- private long mMaximumFrequency;
-
- /**
- * Constructs an instance
- * @param divider quantity
- * @param is3xMode indicates 3x divider (true) or 2x divider (false)
- * @param register5 setting
- * @param minimumFrequency supported by the divider
- * @param maximumFrequency supported by the divider
- */
- FrequencyDivider(int divider, boolean is3xMode, int register5, long minimumFrequency, long maximumFrequency)
- {
- mDivider = divider;
- mIs3xMode = is3xMode;
- mRegister5 = register5;
- mMinimumFrequency = minimumFrequency;
- mMaximumFrequency = maximumFrequency;
- }
-
- /**
- * Register 5 frequency multiplier setting
- */
- public byte getRegister5()
- {
- return (byte)(mRegister5 & 0xFF);
- }
-
- /**
- * Register 6 multiplier mode setting (2x or 3x)
- */
- public byte getRegister6()
- {
- //0xAx OR Enable clock (0x20) OR bandwidth set to 6 MHz (0x80).
- return is3xMode() ? (byte)0xA0 : (byte)0xA2;
- }
-
- /**
- * Calculates the frequency for this frequency divider.
- * @param pm - 8x integral units
- * @param am - 1x integral units
- * @param fractional integral units
- * @param vcoSelect to indicate if the frequency requires VCO select (ie AM + 2)
- * @return frequency
- */
- public double getFrequency(int pm, int am, int fractional, boolean vcoSelect)
- {
- if(vcoSelect)
- {
- return calculate(pm, am, fractional);
- }
- else
- {
- return calculate(pm, am - 2, fractional);
- }
- }
-
- /**
- * Indicates if the frequency requires VCO select for this divider.
- * @param frequency to evaluate
- * @return true if vco select
- */
- public boolean isVcoSelect(double frequency)
- {
- return (frequency * getDivider()) > 3_060_000_000l;
- }
-
- private double calculate(int pm, int am, int fractional)
- {
- return (((8l * pm) + am) * getIntegralFrequencyMultiplier()) + (getFractionalFrequencyMultiplier() * fractional);
- }
-
- /**
- * Integral unit of frequency multiplier supported by the PLL for the specified frequency divider.
- * @return integral frequency multiplier.
- */
- public int getIntegralFrequencyMultiplier()
- {
- return XTAL_FREQUENCY_DIVIDED_BY_2 / mDivider;
- }
-
- /**
- * Fractional (1/65536th) unit of frequency of 2x integrals that is supported by the 16-bit fractional PLL.
- * @return fractional frequency multiplier
- */
- public double getFractionalFrequencyMultiplier()
- {
- return XTAL_FREQUENCY / mDivider / 65_536d;
- }
-
- /**
- * Indicates if this divider is mode 2x (false) or mode 3x (true) as a divider of the base crystal frequency.
- * @return true if 3x mode.
- */
- public boolean is3xMode()
- {
- return mIs3xMode;
- }
-
- /**
- * Divider value (of the base crystal frequency).
- * @return divider
- */
- public int getDivider()
- {
- return mDivider;
- }
-
- /**
- * Minimum frequency supported by this divider.
- * @return minimum frequency (Hz).
- */
- public long getMinimumFrequency()
- {
- return mMinimumFrequency;
- }
-
- /**
- * Maximum frequency supported by this divider.
- * @return maximum frequency (Hz).
- */
- public long getMaximumFrequency()
- {
- return mMaximumFrequency;
- }
-
- /**
- * Indicates if the frequency is contained by the min/max values of the current frequency divider entry.
- * @param frequency to test
- * @return true if contained.
- */
- public boolean contains(long frequency)
- {
- return mMinimumFrequency <= frequency && frequency <= mMaximumFrequency;
- }
-
- /**
- * Returns the matching frequency divider for the specified frequency, or returns divider 16x, if the frequency
- * is outside any supported 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.D16;
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0013/FC0013TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0013/FC0013TunerConfiguration.java
index c30eabfa..1a5067f0 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0013/FC0013TunerConfiguration.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0013/FC0013TunerConfiguration.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2024 Dennis Sheirer
+ * Copyright (C) 2014-2026 Dennis Sheirer
*
* 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
@@ -19,7 +19,7 @@
package io.github.dsheirer.source.tuner.rtl.fc0013;
import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
import io.github.dsheirer.source.tuner.TunerType;
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
@@ -28,15 +28,38 @@ import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
*/
public class FC0013TunerConfiguration extends RTL2832TunerConfiguration
{
- private FC0013EmbeddedTuner.LNAGain mLnaGain = FC0013EmbeddedTuner.LNAGain.G10;
- private boolean mAgc = false;
+ public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 13_500_000;
+ public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 1_907_999_890l;
+ public static final double USABLE_BANDWIDTH_PERCENT = 0.95;
+ public static final int DC_SPIKE_AVOID_BUFFER = 15000;
+ /**
+ * Gains in tenths of a dB for the legacy LNA gain settings G00 to G23, from the librtlsdr FC0013 LNA gain table.
+ * The legacy settings used the same 24 register values in the same order. Two of them (G03 and G04) are both
+ * listed as -6.3 dB, so librtlsdr reports only 23 distinct gains.
+ */
+ private static final int[] LEGACY_GAINS = {-99, -73, -65, -63, -63, -60, -58, -54, 58, 61, 63, 65, 67, 68, 70, 71,
+ 179, 181, 182, 184, 186, 188, 191, 197};
+ /**
+ * Default gain in tenths of a dB
+ */
+ public static final int DEFAULT_GAIN = 67;
/**
- * Default constructor for JAXB
+ * Default constructor for Jackson
*/
public FC0013TunerConfiguration()
{
- super(FC0013EmbeddedTuner.MINIMUM_TUNABLE_FREQUENCY_HZ, FC0013EmbeddedTuner.MAXIMUM_TUNABLE_FREQUENCY_HZ);
+ super(MINIMUM_TUNABLE_FREQUENCY_HZ, MAXIMUM_TUNABLE_FREQUENCY_HZ, DEFAULT_GAIN);
+ }
+
+ /**
+ * Constructs an instance
+ *
+ * @param uniqueID for the tuner
+ */
+ public FC0013TunerConfiguration(String uniqueID)
+ {
+ super(uniqueID, DEFAULT_GAIN);
}
@JsonIgnore
@@ -47,50 +70,15 @@ public class FC0013TunerConfiguration extends RTL2832TunerConfiguration
}
/**
- * Constructs an instance
- *
- * @param uniqueID for the tuner
+ * Converts the legacy LNA gain setting, G00 (lowest) to G23 (highest), to the librtlsdr gain for the same
+ * LNA register value.
*/
- public FC0013TunerConfiguration(String uniqueID)
+ @JsonSetter("lnaGain")
+ public void setLegacyLNAGain(String lnaGain)
{
- super(uniqueID);
- }
-
- /**
- * LNA gain value.
- * @return value
- */
- @JacksonXmlProperty(isAttribute = true, localName = "lna_gain")
- public FC0013EmbeddedTuner.LNAGain getLnaGain()
- {
- return mLnaGain;
- }
-
- /**
- * Sets the LNA gain value.
- * @param lnaGain value.
- */
- public void setLnaGain(FC0013EmbeddedTuner.LNAGain lnaGain)
- {
- mLnaGain = lnaGain;
- }
-
- /**
- * Automatic Gain Control (AGC) state
- * @return AGC enabled state
- */
- @JacksonXmlProperty(isAttribute = true, localName = "agc")
- public boolean getAGC()
- {
- return mAgc;
- }
-
- /**
- * Sets the AGC
- * @param enabled state of the AGC
- */
- public void setAGC(boolean enabled)
- {
- mAgc = enabled;
+ if(lnaGain != null && lnaGain.matches("G\\d\\d"))
+ {
+ setGain(LEGACY_GAINS[Math.min(Integer.parseInt(lnaGain.substring(1)), LEGACY_GAINS.length - 1)]);
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0013/FC0013TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0013/FC0013TunerEditor.java
deleted file mode 100644
index 1dd0f488..00000000
--- a/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0013/FC0013TunerEditor.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2024 Dennis Sheirer
- *
- * 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
- * Note: gain labels were changed from measured dB values to simple 1-16 value labels to conform
- * with the ICD listing the values from 0-15 as minimum to maximum.
- */
- public enum MixerGain
- {
- AUTOMATIC("Automatic", 0x10),
- GAIN_0("1", 0x00),
- GAIN_5("2", 0x01),
- GAIN_15("3", 0x02),
- GAIN_25("4", 0x03),
- GAIN_44("5", 0x04),
- GAIN_53("6", 0x05),
- GAIN_63("7", 0x06),
- GAIN_88("8", 0x07),
- GAIN_105("9", 0x08),
- GAIN_115("10", 0x09),
- GAIN_123("11", 0x0A),
- GAIN_139("12", 0x0B),
- GAIN_152("13", 0x0C),
- GAIN_158("14", 0x0D),
- GAIN_161("15", 0x0E),
- GAIN_153("16", 0x0F);
-
- private String mLabel;
- private int mSetting;
-
- MixerGain(String label, int setting)
- {
- mLabel = label;
- mSetting = setting;
- }
-
- public String toString()
- {
- return mLabel;
- }
-
- public byte getSetting()
- {
- return (byte) mSetting;
- }
- }
-
- /**
- * Master gain settings
- */
- public enum MasterGain
- {
- AUTOMATIC("Automatic", VGAGain.GAIN_312, LNAGain.AUTOMATIC, MixerGain.AUTOMATIC),
- MANUAL("Manual", VGAGain.GAIN_210, LNAGain.GAIN_248, MixerGain.GAIN_123),
- GAIN_0("0", VGAGain.GAIN_210, LNAGain.GAIN_0, MixerGain.GAIN_0),
- GAIN_9("9", VGAGain.GAIN_210, LNAGain.GAIN_9, MixerGain.GAIN_0),
- GAIN_14("14", VGAGain.GAIN_210, LNAGain.GAIN_9, MixerGain.GAIN_5),
- GAIN_26("26", VGAGain.GAIN_210, LNAGain.GAIN_21, MixerGain.GAIN_5),
- GAIN_36("36", VGAGain.GAIN_210, LNAGain.GAIN_21, MixerGain.GAIN_15),
- GAIN_76("76", VGAGain.GAIN_210, LNAGain.GAIN_61, MixerGain.GAIN_15),
- GAIN_86("86", VGAGain.GAIN_210, LNAGain.GAIN_61, MixerGain.GAIN_25),
- GAIN_124("124", VGAGain.GAIN_210, LNAGain.GAIN_99, MixerGain.GAIN_25),
- GAIN_143("143", VGAGain.GAIN_210, LNAGain.GAIN_99, MixerGain.GAIN_44),
- GAIN_156("156", VGAGain.GAIN_210, LNAGain.GAIN_112, MixerGain.GAIN_44),
- GAIN_165("165", VGAGain.GAIN_210, LNAGain.GAIN_112, MixerGain.GAIN_53),
- GAIN_196("196", VGAGain.GAIN_210, LNAGain.GAIN_143, MixerGain.GAIN_53),
- GAIN_208("208", VGAGain.GAIN_210, LNAGain.GAIN_143, MixerGain.GAIN_63),
- GAIN_228("228", VGAGain.GAIN_210, LNAGain.GAIN_165, MixerGain.GAIN_63),
- GAIN_253("253", VGAGain.GAIN_210, LNAGain.GAIN_165, MixerGain.GAIN_88),
- GAIN_279("279", VGAGain.GAIN_210, LNAGain.GAIN_191, MixerGain.GAIN_88),
- GAIN_296("296", VGAGain.GAIN_210, LNAGain.GAIN_191, MixerGain.GAIN_105),
- GAIN_327("327", VGAGain.GAIN_210, LNAGain.GAIN_222, MixerGain.GAIN_105),
- GAIN_337("337", VGAGain.GAIN_210, LNAGain.GAIN_222, MixerGain.GAIN_115),
- GAIN_363("363", VGAGain.GAIN_210, LNAGain.GAIN_248, MixerGain.GAIN_115),
- GAIN_371("371", VGAGain.GAIN_210, LNAGain.GAIN_248, MixerGain.GAIN_123),
- GAIN_385("385", VGAGain.GAIN_210, LNAGain.GAIN_262, MixerGain.GAIN_123),
- GAIN_401("401", VGAGain.GAIN_210, LNAGain.GAIN_262, MixerGain.GAIN_139),
- GAIN_420("420", VGAGain.GAIN_210, LNAGain.GAIN_281, MixerGain.GAIN_139),
- GAIN_433("433", VGAGain.GAIN_210, LNAGain.GAIN_281, MixerGain.GAIN_152),
- GAIN_438("438", VGAGain.GAIN_210, LNAGain.GAIN_286, MixerGain.GAIN_152),
- GAIN_444("444", VGAGain.GAIN_210, LNAGain.GAIN_286, MixerGain.GAIN_158),
- GAIN_479("479", VGAGain.GAIN_210, LNAGain.GAIN_321, MixerGain.GAIN_158),
- GAIN_482("482", VGAGain.GAIN_210, LNAGain.GAIN_321, MixerGain.GAIN_161),
- GAIN_495("495", VGAGain.GAIN_210, LNAGain.GAIN_334, MixerGain.GAIN_161);
-
- private String mLabel;
- private VGAGain mVGAGain;
- private LNAGain mLNAGain;
- private MixerGain mMixerGain;
-
- MasterGain(String label, VGAGain vga, LNAGain lna, MixerGain mixer)
- {
- mLabel = label;
- mVGAGain = vga;
- mLNAGain = lna;
- mMixerGain = mixer;
- }
-
- public String toString()
- {
- return mLabel;
- }
-
- public VGAGain getVGAGain()
- {
- return mVGAGain;
- }
-
- public LNAGain getLNAGain()
- {
- return mLNAGain;
- }
-
- public MixerGain getMixerGain()
- {
- return mMixerGain;
- }
- }
-
- /**
- * R8xxx tuner registers and register mask values
- */
- public enum Register
- {
- LNA_GAIN(0x05, 0x1F),
- INPUT_SELECTOR_AIR(0x05, 0x20),
- INPUT_SELECTOR_CABLE_1(0x05, 0x40),
- INPUT_SELECTOR_AIR_AND_CABLE_1(0x05, 0x60),
- LOOP_THROUGH(0x05, 0x80),
- INPUT_SELECTOR_CABLE_2(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;
-
- 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;
-
- 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;
- }
-
- /**
- * Indicates if the frequency is contained by the frequency range of this band
- */
- public boolean contains(long frequency)
- {
- return mMinFrequency <= frequency && frequency <= mMaxFrequency;
- }
-
- /**
- * Finds the correct frequency range that contains the frequency
- *
- * @param frequency to lookup
- * @return frequency range
- */
- 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;
-
- 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, int vcoPowerRef)
- {
- if(vcoFineTune == vcoPowerRef)
- {
- return mDividerNumber;
- }
- else if(vcoFineTune < vcoPowerRef)
- {
- return mDividerNumber - 1;
- }
- else if(vcoFineTune > vcoPowerRef)
- {
- 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 : R8xEmbeddedTuner.FrequencyDivider.values())
- {
- if(divider.contains(frequency))
- {
- return divider;
- }
- }
-
- return R8xEmbeddedTuner.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 R8xEmbeddedTuner.Integral.fromValue(integral);
- }
-
- throw new IllegalArgumentException("PLL frequency [" + frequency + "] is not valid for this frequency " +
- "divider " + this);
- }
-
- /**
- * 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.
- */
- 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;
-
- 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 R8xEmbeddedTuner.Integral.values()[value];
- }
-
- throw new IllegalArgumentException("PLL integral value [" + value + "] must be in the range 0 - 31");
- }
- }
-}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/R8xTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/R8xTunerConfiguration.java
index a81ae865..9d407838 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/R8xTunerConfiguration.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/R8xTunerConfiguration.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2024 Dennis Sheirer
+ * Copyright (C) 2014-2026 Dennis Sheirer
*
* 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
@@ -18,25 +18,34 @@
*/
package io.github.dsheirer.source.tuner.rtl.r8x;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
/**
- * RTL-2832 with embedded R8xxx tuner configuration
+ * RTL-2832 with embedded R820T or R828D tuner configuration
*/
public abstract class R8xTunerConfiguration extends RTL2832TunerConfiguration
{
- private R8xEmbeddedTuner.MasterGain mMasterMasterGain = R8xEmbeddedTuner.MasterGain.GAIN_327;
- private R8xEmbeddedTuner.MixerGain mMixerGain = R8xEmbeddedTuner.MixerGain.GAIN_105;
- private R8xEmbeddedTuner.LNAGain mLNAGain = R8xEmbeddedTuner.LNAGain.GAIN_222;
- private R8xEmbeddedTuner.VGAGain mVGAGain = R8xEmbeddedTuner.VGAGain.GAIN_210;
+ public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 3_180_000;
+ public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 1_782_030_000;
+ public static final double USABLE_BANDWIDTH_PERCENT = 0.98;
+ public static final int DC_SPIKE_AVOID_BUFFER = 5000;
+ /**
+ * Default gain in tenths of a dB
+ */
+ public static final int DEFAULT_GAIN = 328;
+
+ //Legacy gain settings, used to convert configurations from the previous R8x driver's master, LNA and mixer gains
+ private boolean mLegacyManualGain = false;
+ private Integer mLegacyLNAGain;
+ private Integer mLegacyMixerGain;
/**
- * Default constructor for JAXB
+ * Default constructor for Jackson
*/
public R8xTunerConfiguration()
{
- super(R8xEmbeddedTuner.MINIMUM_TUNABLE_FREQUENCY_HZ, R8xEmbeddedTuner.MAXIMUM_TUNABLE_FREQUENCY_HZ);
+ super(MINIMUM_TUNABLE_FREQUENCY_HZ, MAXIMUM_TUNABLE_FREQUENCY_HZ, DEFAULT_GAIN);
}
/**
@@ -46,50 +55,64 @@ public abstract class R8xTunerConfiguration extends RTL2832TunerConfiguration
*/
public R8xTunerConfiguration(String uniqueID)
{
- super(uniqueID);
+ super(uniqueID, DEFAULT_GAIN);
}
- @JacksonXmlProperty(isAttribute = true, localName = "master_gain")
- public R8xEmbeddedTuner.MasterGain getMasterGain()
+ /**
+ * Converts the legacy master gain setting: AUTOMATIC, MANUAL (using the LNA and mixer gains) or a gain value
+ * such as GAIN_327 in tenths of a dB.
+ */
+ @JsonSetter("masterGain")
+ public void setLegacyMasterGain(String masterGain)
{
- return mMasterMasterGain;
+ if("AUTOMATIC".equals(masterGain))
+ {
+ setAGC(true);
+ }
+ else if("MANUAL".equals(masterGain))
+ {
+ mLegacyManualGain = true;
+ updateLegacyManualGain();
+ }
+ else
+ {
+ Integer gain = parseLegacyGain(masterGain);
+
+ if(gain != null)
+ {
+ setGain(gain);
+ }
+ }
}
- public void setMasterGain(R8xEmbeddedTuner.MasterGain masterGain)
+ /**
+ * Converts the legacy LNA gain setting, such as GAIN_222 in tenths of a dB.
+ */
+ @JsonSetter("lnagain")
+ public void setLegacyLNAGain(String lnaGain)
{
- mMasterMasterGain = masterGain;
+ mLegacyLNAGain = parseLegacyGain(lnaGain);
+ updateLegacyManualGain();
}
- @JacksonXmlProperty(isAttribute = true, localName = "mixer_gain")
- public R8xEmbeddedTuner.MixerGain getMixerGain()
+ /**
+ * Converts the legacy mixer gain setting, such as GAIN_105 in tenths of a dB.
+ */
+ @JsonSetter("mixerGain")
+ public void setLegacyMixerGain(String mixerGain)
{
- return mMixerGain;
+ mLegacyMixerGain = parseLegacyGain(mixerGain);
+ updateLegacyManualGain();
}
- public void setMixerGain(R8xEmbeddedTuner.MixerGain mixerGain)
+ /**
+ * Sets the gain to the sum of the legacy LNA and mixer gains when the legacy master gain is MANUAL.
+ */
+ private void updateLegacyManualGain()
{
- mMixerGain = mixerGain;
- }
-
- @JacksonXmlProperty(isAttribute = true, localName = "lna_gain")
- public R8xEmbeddedTuner.LNAGain getLNAGain()
- {
- return mLNAGain;
- }
-
- public void setLNAGain(R8xEmbeddedTuner.LNAGain lnaGain)
- {
- mLNAGain = lnaGain;
- }
-
- @JacksonXmlProperty(isAttribute = true, localName = "vga_gain")
- public R8xEmbeddedTuner.VGAGain getVGAGain()
- {
- return mVGAGain;
- }
-
- public void setVGAGain(R8xEmbeddedTuner.VGAGain vgaGain)
- {
- mVGAGain = vgaGain;
+ if(mLegacyManualGain && mLegacyLNAGain != null && mLegacyMixerGain != null)
+ {
+ setGain(mLegacyLNAGain + mLegacyMixerGain);
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/R8xTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/R8xTunerEditor.java
deleted file mode 100644
index db3713fc..00000000
--- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/R8xTunerEditor.java
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2024 Dennis Sheirer
- *
- * 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
" +
- "independent control of Mixer, LNA and Enhance gain
settings, or one of the " +
- "individual gain settings for
semi-manual gain control");
- }
-
- return mMasterGainCombo;
- }
-
- private JComboBox getSampleRateCombo()
- {
- if(mSampleRateCombo == null)
- {
- mSampleRateCombo = new JComboBox<>(SampleRate.values());
- mSampleRateCombo.setEnabled(false);
- mSampleRateCombo.addActionListener(e ->
- {
- if(!isLoading())
- {
- SampleRate sampleRate = (SampleRate) mSampleRateCombo.getSelectedItem();
-
- try
- {
- getTuner().getController().setSampleRate(sampleRate);
- //Adjust the min/max values for the sample rate.
- adjustForSampleRate(sampleRate.getRate());
- save();
- }
- catch(SourceException | LibUsbException eSampleRate)
- {
- JOptionPane.showMessageDialog(E4KTunerEditor.this,
- "E4000 Tuner Controller - couldn't apply the sample rate setting [" +
- sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage());
-
- mLog.error("E4000 Tuner Controller - couldn't apply sample rate setting [" +
- sampleRate.getLabel() + "]", eSampleRate);
- }
- }
- });
- }
-
- return mSampleRateCombo;
- }
-
- private JButton getTunerInfoButton()
- {
- if(mTunerInfoButton == null)
- {
- mTunerInfoButton = new JButton("Tuner Info");
- mTunerInfoButton.setEnabled(false);
- mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(E4KTunerEditor.this,
- getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE));
- }
-
- return mTunerInfoButton;
- }
-
- /**
- * Updates the sample rate tooltip according to the tuner controller's lock state.
- */
- /**
- * Updates the sample rate tooltip according to the tuner controller's lock state.
- */
- private void updateSampleRateToolTip()
- {
- if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
- {
- getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
- }
- else if(hasTuner())
- {
- getSampleRateCombo().setToolTipText("Select a sample rate for the tuner");
- }
- else
- {
- getSampleRateCombo().setToolTipText("No tuner available");
- }
- }
-
- @Override
- public void setTunerLockState(boolean locked)
- {
- getFrequencyPanel().updateControls();
- getSampleRateCombo().setEnabled(!locked);
- updateSampleRateToolTip();
- }
-
- private String getTunerInfo()
- {
- StringBuilder sb = new StringBuilder();
-
- RTL2832TunerController.Descriptor descriptor = getTuner().getController().getDescriptor();
-
- sb.append("RTL-2832 with E4000 Tuner
");
-
- if(descriptor == null)
- {
- sb.append("No EEPROM Descriptor Available");
- }
- else
- {
- sb.append("USB ID: ");
- sb.append(descriptor.getVendorID());
- sb.append(":");
- sb.append(descriptor.getProductID());
- sb.append("
");
-
- sb.append("Vendor: ");
- sb.append(descriptor.getVendorLabel());
- sb.append("
");
-
- sb.append("Product: ");
- sb.append(descriptor.getProductLabel());
- sb.append("
");
-
- sb.append("Serial: ");
- sb.append(descriptor.getSerial());
- sb.append("
");
-
- sb.append("IR Enabled: ");
- sb.append(descriptor.irEnabled());
- sb.append("
");
-
- sb.append("Remote Wake: ");
- sb.append(descriptor.remoteWakeupEnabled());
- sb.append("
");
- }
-
- return sb.toString();
- }
-
- @Override
- public void save()
- {
- if(hasConfiguration() && !isLoading())
- {
- E4KTunerConfiguration config = getConfiguration();
- config.setFrequency(getFrequencyControl().getFrequency());
- getConfiguration().setMinimumFrequency(getMinimumFrequencyTextField().getFrequency());
- getConfiguration().setMaximumFrequency(getMaximumFrequencyTextField().getFrequency());
- double value = ((SpinnerNumberModel)getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue();
- config.setFrequencyCorrection(value);
- config.setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected());
- config.setBiasT(getTuner().getController().isBiasT());
- config.setSampleRate((SampleRate)getSampleRateCombo().getSelectedItem());
- config.setMasterGain((E4KGain)getMasterGainCombo().getSelectedItem());
- config.setMixerGain((E4KMixerGain)getMixerGainCombo().getSelectedItem());
- config.setLNAGain((E4KLNAGain)getLNAGainCombo().getSelectedItem());
- config.setIFGain((E4KEmbeddedTuner.IFGain)getIfGainCombo().getSelectedItem());
- saveConfiguration();
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0012/FC0012TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0012/FC0012TunerConfiguration.java
new file mode 100644
index 00000000..60498bd3
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/fc0012/FC0012TunerConfiguration.java
@@ -0,0 +1,63 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2026 Dennis Sheirer
+ *
+ * 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 RTL-2832 with " + getEmbeddedTuner().getTunerType().getLabel() + " Tuner
");
-
- if(descriptor == null)
- {
- sb.append("No EEPROM Descriptor Available");
- }
- else
- {
- sb.append("USB ID: ");
- sb.append(descriptor.getVendorID());
- sb.append(":");
- sb.append(descriptor.getProductID());
- sb.append("
");
-
- sb.append("Vendor: ");
- sb.append(descriptor.getVendorLabel());
- sb.append("
");
-
- sb.append("Product: ");
- sb.append(descriptor.getProductLabel());
- sb.append("
");
-
- sb.append("Serial: ");
- sb.append(descriptor.getSerial());
- sb.append("
");
-
- sb.append("IR Enabled: ");
- sb.append(descriptor.irEnabled());
- sb.append("
");
-
- sb.append("Remote Wake: ");
- sb.append(descriptor.remoteWakeupEnabled());
- sb.append("
");
- }
-
- return sb.toString();
- }
-
- @Override
- public void save()
- {
- if(hasConfiguration() && !isLoading())
- {
- FC0013TunerConfiguration config = getConfiguration();
- config.setBiasT(getTuner().getController().isBiasT());
- config.setFrequency(getFrequencyControl().getFrequency());
- getConfiguration().setMinimumFrequency(getMinimumFrequencyTextField().getFrequency());
- getConfiguration().setMaximumFrequency(getMaximumFrequencyTextField().getFrequency());
- double value = ((SpinnerNumberModel)getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue();
- config.setFrequencyCorrection(value);
- config.setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected());
-
- config.setSampleRate((SampleRate)getSampleRateCombo().getSelectedItem());
- config.setAGC(getAgcToggleButton().isSelected());
- FC0013EmbeddedTuner.LNAGain lnaGain = (FC0013EmbeddedTuner.LNAGain)getLNAGainCombo().getSelectedItem();
- config.setLnaGain(lnaGain);
- saveConfiguration();
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/fc2580/FC2580TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/fc2580/FC2580TunerConfiguration.java
new file mode 100644
index 00000000..8ade79de
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/fc2580/FC2580TunerConfiguration.java
@@ -0,0 +1,64 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2026 Dennis Sheirer
+ *
+ * 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
" +
- "independent control of Mixer, LNA and Enhance gain
settings, or one of the " +
- "individual gain settings for
semi-manual gain control");
- }
-
- return mMasterGainCombo;
- }
-
- /**
- * Updates the sample rate tooltip according to the tuner controller's lock state.
- */
- private void updateSampleRateToolTip()
- {
- if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
- {
- getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
- }
- else if(hasTuner())
- {
- getSampleRateCombo().setToolTipText("Select a sample rate for the tuner");
- }
- else
- {
- getSampleRateCombo().setToolTipText("No tuner available");
- }
- }
-
- @Override
- public void setTunerLockState(boolean locked)
- {
- getFrequencyPanel().updateControls();
- getSampleRateCombo().setEnabled(!locked);
- updateSampleRateToolTip();
- }
-
- private String getTunerInfo()
- {
- StringBuilder sb = new StringBuilder();
- RTL2832TunerController.Descriptor descriptor = getTuner().getController().getDescriptor();
- sb.append("RTL-2832 with " + getEmbeddedTuner().getTunerType().getLabel() + " Tuner
");
-
- if(descriptor == null)
- {
- sb.append("No EEPROM Descriptor Available");
- }
- else
- {
- sb.append("USB ID: ");
- sb.append(descriptor.getVendorID());
- sb.append(":");
- sb.append(descriptor.getProductID());
- sb.append("
");
-
- sb.append("Vendor: ");
- sb.append(descriptor.getVendorLabel());
- sb.append("
");
-
- sb.append("Product: ");
- sb.append(descriptor.getProductLabel());
- sb.append("
");
-
- sb.append("Serial: ");
- sb.append(descriptor.getSerial());
- sb.append("
");
-
- sb.append("IR Enabled: ");
- sb.append(descriptor.irEnabled());
- sb.append("
");
-
- sb.append("Remote Wake: ");
- sb.append(descriptor.remoteWakeupEnabled());
- sb.append("
");
- }
-
- return sb.toString();
- }
-
- @Override
- public void save()
- {
- if(hasConfiguration() && !isLoading())
- {
- R8xTunerConfiguration config = getConfiguration();
- config.setBiasT(getTuner().getController().isBiasT());
- config.setFrequency(getFrequencyControl().getFrequency());
- getConfiguration().setMinimumFrequency(getMinimumFrequencyTextField().getFrequency());
- getConfiguration().setMaximumFrequency(getMaximumFrequencyTextField().getFrequency());
- double value = ((SpinnerNumberModel)getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue();
- config.setFrequencyCorrection(value);
- config.setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected());
-
- config.setSampleRate((SampleRate)getSampleRateCombo().getSelectedItem());
- R8xEmbeddedTuner.MasterGain gain = (R8xEmbeddedTuner.MasterGain)getMasterGainCombo().getSelectedItem();
- config.setMasterGain(gain);
- R8xEmbeddedTuner.MixerGain mixerGain = (R8xEmbeddedTuner.MixerGain)getMixerGainCombo().getSelectedItem();
- config.setMixerGain(mixerGain);
- R8xEmbeddedTuner.LNAGain lnaGain = (R8xEmbeddedTuner.LNAGain)getLNAGainCombo().getSelectedItem();
- config.setLNAGain(lnaGain);
- R8xEmbeddedTuner.VGAGain vgaGain = (R8xEmbeddedTuner.VGAGain)getVGAGainCombo().getSelectedItem();
- config.setVGAGain(vgaGain);
- saveConfiguration();
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/r820t/R820TEmbeddedTuner.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/r820t/R820TEmbeddedTuner.java
deleted file mode 100644
index c340210b..00000000
--- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r8x/r820t/R820TEmbeddedTuner.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2023 Dennis Sheirer
- *
- * 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