mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 17:47:32 -05:00
Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
@@ -127,6 +127,7 @@ dependencies {
|
|||||||
implementation 'io.github.dsheirer:jbladerf:2.6.0' //bladeRF tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jbladerf:2.6.0' //bladeRF tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jairspy:1.0.12' //Airspy tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jairspy:1.0.12' //Airspy tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jhydrasdr:1.1.4' //HydraSDR tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jhydrasdr:1.1.4' //HydraSDR tuners - GitHub release, see settings.gradle
|
||||||
|
implementation 'io.github.dsheirer:jrtl:1.4.0.1' //RTL-SDR tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
||||||
implementation 'io.github.dsheirer:usb4java-native-libraries:1.3.3' //OSX & Windows aarch64 native libs
|
implementation 'io.github.dsheirer:usb4java-native-libraries:1.3.3' //OSX & Windows aarch64 native libs
|
||||||
implementation 'io.github.dsheirer:usb4java:1.3.5' //Fork with Loader support for OSX Multi-Version
|
implementation 'io.github.dsheirer:usb4java:1.3.5' //Fork with Loader support for OSX Multi-Version
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ dependencyResolutionManagement {
|
|||||||
content { includeModule 'io.github.dsheirer', 'jairspy' }
|
content { includeModule 'io.github.dsheirer', 'jairspy' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//jRTL is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||||
|
ivy {
|
||||||
|
url = 'https://github.com/DSheirer/jRTL/releases/download'
|
||||||
|
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
|
||||||
|
metadataSources { artifact() }
|
||||||
|
content { includeModule 'io.github.dsheirer', 'jrtl' }
|
||||||
|
}
|
||||||
|
|
||||||
//jHydraSDR is published as a GitHub release, with the native libraries for all platforms in the jar
|
//jHydraSDR is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||||
ivy {
|
ivy {
|
||||||
url = 'https://github.com/DSheirer/jHydraSDR/releases/download'
|
url = 'https://github.com/DSheirer/jHydraSDR/releases/download'
|
||||||
|
|||||||
@@ -118,6 +118,23 @@ public enum TunerClass
|
|||||||
case 0x0CCD00D3: //TERRATEC CINERGY T REV3
|
case 0x0CCD00D3: //TERRATEC CINERGY T REV3
|
||||||
case 0x0CCD00D7: //TERRATEC T STICK PLUS
|
case 0x0CCD00D7: //TERRATEC T STICK PLUS
|
||||||
case 0x0CCD00E0: //TERRATEC NOXON REV2
|
case 0x0CCD00E0: //TERRATEC NOXON REV2
|
||||||
|
case 0x04136680: //DIGITALNOW QUAD DVB-T PCI-E CARD
|
||||||
|
case 0x04136F0F: //LEADTEK WINFAST DTV DONGLE MINI D
|
||||||
|
case 0x0458707F: //GENIUS TVGO DVB-T03 USB DONGLE (VER. B)
|
||||||
|
case 0x0CCD00B5: //TERRATEC NOXON DAB STICK - RADIO ENERGY
|
||||||
|
case 0x0CCD00B8: //TERRATEC BR DAB STICK
|
||||||
|
case 0x0CCD00B9: //TERRATEC WDR DAB STICK
|
||||||
|
case 0x0CCD00C0: //TERRATEC MUELLERVERLAG DAB STICK
|
||||||
|
case 0x15545020: //PIXELVIEW PV-DT235U(RN)
|
||||||
|
case 0x15F40131: //ASTROMETA DVB-T/DVB-T2
|
||||||
|
case 0x15F40133: //HANFTEK DAB+FM+DVB-T
|
||||||
|
case 0x1B80D394: //DIKOM USB-DVBT HD
|
||||||
|
case 0x1B80D397: //KWORLD KW-UB450-T USB DVB-T PICO TV
|
||||||
|
case 0x1B80D3A8: //ASUS U3100MINI_PLUS_V2
|
||||||
|
case 0x1B80D3AF: //SVEON STV27 DVB-T USB & FM
|
||||||
|
case 0x1B80D3B0: //SVEON STV21 DVB-T USB & FM
|
||||||
|
case 0x1D191104: //MSI DIGIVOX MICRO HD
|
||||||
|
case 0x1F4DA803: //SWEEX DVB-T USB
|
||||||
return RTL2832;
|
return RTL2832;
|
||||||
case 0x1D506089: //HACKRF ONE
|
case 0x1D506089: //HACKRF ONE
|
||||||
case 0x1D50CC15: //HACKRF ONE
|
case 0x1D50CC15: //HACKRF ONE
|
||||||
|
|||||||
@@ -56,20 +56,15 @@ import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
|||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
||||||
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
|
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.recording.RecordingTunerEditor;
|
import io.github.dsheirer.source.tuner.recording.RecordingTunerEditor;
|
||||||
import io.github.dsheirer.source.tuner.rtl.EmbeddedTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner;
|
import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner;
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerEditor;
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832UnknownTunerEditor;
|
import io.github.dsheirer.source.tuner.rtl.RTL2832UnknownTunerEditor;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerEditor;
|
import io.github.dsheirer.source.tuner.rtl.fc0012.FC0012TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013EmbeddedTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerEditor;
|
import io.github.dsheirer.source.tuner.rtl.fc2580.FC2580TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.R8xTunerEditor;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TEmbeddedTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DEmbeddedTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.sdrplay.DiscoveredRspTuner;
|
import io.github.dsheirer.source.tuner.sdrplay.DiscoveredRspTuner;
|
||||||
import io.github.dsheirer.source.tuner.sdrplay.RspTuner;
|
import io.github.dsheirer.source.tuner.sdrplay.RspTuner;
|
||||||
@@ -394,25 +389,6 @@ public class TunerFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RTL-2832 embedded tuner instance.
|
|
||||||
* @param tunerType to create
|
|
||||||
* @param adapter exposing the RTL-2832 tuner controller functions
|
|
||||||
* @return embedded tuner
|
|
||||||
*/
|
|
||||||
public static EmbeddedTuner getRtlEmbeddedTuner(TunerType tunerType,
|
|
||||||
RTL2832TunerController.ControllerAdapter adapter) throws SourceException
|
|
||||||
{
|
|
||||||
return switch(tunerType)
|
|
||||||
{
|
|
||||||
case ELONICS_E4000 -> new E4KEmbeddedTuner(adapter);
|
|
||||||
case FITIPOWER_FC0013 -> new FC0013EmbeddedTuner(adapter);
|
|
||||||
case RAFAELMICRO_R820T -> new R820TEmbeddedTuner(adapter);
|
|
||||||
case RAFAELMICRO_R828D -> new R828DEmbeddedTuner(adapter);
|
|
||||||
default -> throw new SourceException("Unsupported/Unrecognized Tuner Type: " + tunerType);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a tuner configuration for the specified tuner type, unique ID and name
|
* Creates a tuner configuration for the specified tuner type, unique ID and name
|
||||||
*/
|
*/
|
||||||
@@ -428,8 +404,12 @@ public class TunerFactory
|
|||||||
return new BladeRfTunerConfiguration(uniqueID);
|
return new BladeRfTunerConfiguration(uniqueID);
|
||||||
case ELONICS_E4000:
|
case ELONICS_E4000:
|
||||||
return new E4KTunerConfiguration(uniqueID);
|
return new E4KTunerConfiguration(uniqueID);
|
||||||
|
case FITIPOWER_FC0012:
|
||||||
|
return new FC0012TunerConfiguration(uniqueID);
|
||||||
case FITIPOWER_FC0013:
|
case FITIPOWER_FC0013:
|
||||||
return new FC0013TunerConfiguration(uniqueID);
|
return new FC0013TunerConfiguration(uniqueID);
|
||||||
|
case FCI_FC2580:
|
||||||
|
return new FC2580TunerConfiguration(uniqueID);
|
||||||
case FUNCUBE_DONGLE_PRO:
|
case FUNCUBE_DONGLE_PRO:
|
||||||
return new FCD1TunerConfiguration(uniqueID);
|
return new FCD1TunerConfiguration(uniqueID);
|
||||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||||
@@ -532,12 +512,12 @@ public class TunerFactory
|
|||||||
switch(discoveredTuner.getTuner().getTunerType())
|
switch(discoveredTuner.getTuner().getTunerType())
|
||||||
{
|
{
|
||||||
case ELONICS_E4000:
|
case ELONICS_E4000:
|
||||||
return new E4KTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
case FCI_FC2580:
|
||||||
|
case FITIPOWER_FC0012:
|
||||||
case FITIPOWER_FC0013:
|
case FITIPOWER_FC0013:
|
||||||
return new FC0013TunerEditor(userPreferences, tunerManager, discoveredTuner);
|
|
||||||
case RAFAELMICRO_R820T:
|
case RAFAELMICRO_R820T:
|
||||||
case RAFAELMICRO_R828D:
|
case RAFAELMICRO_R828D:
|
||||||
return new R8xTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
return new RTL2832TunerEditor(userPreferences, tunerManager, discoveredTuner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new RTL2832UnknownTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
return new RTL2832UnknownTunerEditor(userPreferences, tunerManager, discoveredTuner);
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ import io.github.dsheirer.source.tuner.hackrf.HackRFTunerConfiguration;
|
|||||||
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerConfiguration;
|
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
|
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.fc0012.FC0012TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.fc2580.FC2580TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.sdrplay.RspTunerConfiguration;
|
import io.github.dsheirer.source.tuner.sdrplay.RspTunerConfiguration;
|
||||||
@@ -47,7 +49,9 @@ import io.github.dsheirer.source.tuner.sdrplay.RspTunerConfiguration;
|
|||||||
@JsonSubTypes.Type(value = AirspyHfTunerConfiguration.class, name = "airspyHfTunerConfiguration"),
|
@JsonSubTypes.Type(value = AirspyHfTunerConfiguration.class, name = "airspyHfTunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = BladeRfTunerConfiguration.class, name = "bladeRfTunerConfiguration"),
|
@JsonSubTypes.Type(value = BladeRfTunerConfiguration.class, name = "bladeRfTunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = E4KTunerConfiguration.class, name = "e4KTunerConfiguration"),
|
@JsonSubTypes.Type(value = E4KTunerConfiguration.class, name = "e4KTunerConfiguration"),
|
||||||
|
@JsonSubTypes.Type(value = FC0012TunerConfiguration.class, name = "fc0012TunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = FC0013TunerConfiguration.class, name = "fc0013TunerConfiguration"),
|
@JsonSubTypes.Type(value = FC0013TunerConfiguration.class, name = "fc0013TunerConfiguration"),
|
||||||
|
@JsonSubTypes.Type(value = FC2580TunerConfiguration.class, name = "fc2580TunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = FCD1TunerConfiguration.class, name = "fcd1TunerConfiguration"),
|
@JsonSubTypes.Type(value = FCD1TunerConfiguration.class, name = "fcd1TunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = FCD2TunerConfiguration.class, name = "fcd2TunerConfiguration"),
|
@JsonSubTypes.Type(value = FCD2TunerConfiguration.class, name = "fcd2TunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = HackRFTunerConfiguration.class, name = "hackRFTunerConfiguration"),
|
@JsonSubTypes.Type(value = HackRFTunerConfiguration.class, name = "hackRFTunerConfiguration"),
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* *****************************************************************************
|
|
||||||
* Copyright (C) 2014-2022 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 <http://www.gnu.org/licenses/>
|
|
||||||
* ****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.github.dsheirer.source.tuner.rtl;
|
|
||||||
|
|
||||||
import io.github.dsheirer.source.SourceException;
|
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
|
||||||
import org.usb4java.LibUsbException;
|
|
||||||
|
|
||||||
import javax.usb.UsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tuner embedded in an RTL-2832 USB tuner.
|
|
||||||
*
|
|
||||||
* Note: since the RTL2832TunerController instance is constructed without knowing the embedded tuner type, we construct
|
|
||||||
* an instance of the actual tuner once the type is discovered and we use an adapter to access the non-public USB
|
|
||||||
* controls and interfaces needed by the embedded tuner. This access is through the RtlTunerControllerAdapter.
|
|
||||||
*/
|
|
||||||
public abstract class EmbeddedTuner
|
|
||||||
{
|
|
||||||
private RTL2832TunerController.ControllerAdapter mAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance
|
|
||||||
* @param adapter exposing the interfaces of the parent RTL2832TunerController
|
|
||||||
*/
|
|
||||||
public EmbeddedTuner(RTL2832TunerController.ControllerAdapter adapter)
|
|
||||||
{
|
|
||||||
mAdapter = adapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access the interfaces of the parent RTL tuner controller via the adapter
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected RTL2832TunerController.ControllerAdapter getAdapter()
|
|
||||||
{
|
|
||||||
return mAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize this embedded tuner. The calling method should ensure that the I2C repeater is enabled before
|
|
||||||
* calling this method.
|
|
||||||
*/
|
|
||||||
protected abstract void initTuner() throws UsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of embedded tuner
|
|
||||||
*/
|
|
||||||
public abstract TunerType getTunerType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the tuner configuration settings to this embedded tuner
|
|
||||||
* @param tunerConfig containing settings
|
|
||||||
* @throws SourceException if there is an error in applying the settings
|
|
||||||
*/
|
|
||||||
public abstract void apply(TunerConfiguration tunerConfig) throws SourceException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimum frequency supported by this tuner
|
|
||||||
*/
|
|
||||||
public abstract long getMinimumFrequencySupported();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum frequency supported by this tuner
|
|
||||||
*/
|
|
||||||
public abstract long getMaximumFrequencySupported();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bandwidth of the DC spike that is unusable for this tuner
|
|
||||||
*/
|
|
||||||
//TODO: can we get rid of this now?
|
|
||||||
public abstract int getDcSpikeHalfBandwidth();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Percentage of the bandwidth that is usable for this tuner
|
|
||||||
*/
|
|
||||||
public abstract double getUsableBandwidthPercent();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the center tuned frequency
|
|
||||||
* @param frequency in Hertz
|
|
||||||
* @throws SourceException if there is an error
|
|
||||||
*/
|
|
||||||
public abstract void setTunedFrequency(long frequency) throws SourceException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sample mode for the tuner
|
|
||||||
* @param mode to set
|
|
||||||
* @throws LibUsbException
|
|
||||||
*/
|
|
||||||
public abstract void setSamplingMode(RTL2832TunerController.SampleMode mode) throws LibUsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the filters according to the specified sample rate
|
|
||||||
* @param sampleRate for filter setup
|
|
||||||
* @throws SourceException if there is an error
|
|
||||||
*/
|
|
||||||
public abstract void setSampleRateFilters(int sampleRate) throws SourceException;
|
|
||||||
}
|
|
||||||
@@ -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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,7 +24,9 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.fc0012.FC0012TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.fc2580.FC2580TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DTunerConfiguration;
|
||||||
|
|
||||||
@@ -34,7 +36,9 @@ import io.github.dsheirer.source.tuner.rtl.r8x.r828d.R828DTunerConfiguration;
|
|||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||||
@JsonSubTypes({
|
@JsonSubTypes({
|
||||||
@JsonSubTypes.Type(value = E4KTunerConfiguration.class, name = "e4KTunerConfiguration"),
|
@JsonSubTypes.Type(value = E4KTunerConfiguration.class, name = "e4KTunerConfiguration"),
|
||||||
|
@JsonSubTypes.Type(value = FC0012TunerConfiguration.class, name = "fc0012TunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = FC0013TunerConfiguration.class, name = "fc0013TunerConfiguration"),
|
@JsonSubTypes.Type(value = FC0013TunerConfiguration.class, name = "fc0013TunerConfiguration"),
|
||||||
|
@JsonSubTypes.Type(value = FC2580TunerConfiguration.class, name = "fc2580TunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = R820TTunerConfiguration.class, name = "r820TTunerConfiguration"),
|
@JsonSubTypes.Type(value = R820TTunerConfiguration.class, name = "r820TTunerConfiguration"),
|
||||||
@JsonSubTypes.Type(value = R828DTunerConfiguration.class, name = "r828DTunerConfiguration"),
|
@JsonSubTypes.Type(value = R828DTunerConfiguration.class, name = "r828DTunerConfiguration"),
|
||||||
})
|
})
|
||||||
@@ -43,20 +47,92 @@ public abstract class RTL2832TunerConfiguration extends TunerConfiguration
|
|||||||
{
|
{
|
||||||
private RTL2832TunerController.SampleRate mSampleRate = RTL2832TunerController.SampleRate.RATE_2_400MHZ;
|
private RTL2832TunerController.SampleRate mSampleRate = RTL2832TunerController.SampleRate.RATE_2_400MHZ;
|
||||||
private boolean mBiasTEnabled = false;
|
private boolean mBiasTEnabled = false;
|
||||||
|
private int mGain;
|
||||||
|
private boolean mAGC = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor to support Jackson
|
* Default constructor to support Jackson
|
||||||
* @param minimumFrequency tunable
|
* @param minimumFrequency tunable
|
||||||
* @param maximumFrequency tunable
|
* @param maximumFrequency tunable
|
||||||
|
* @param defaultGain in tenths of a dB
|
||||||
*/
|
*/
|
||||||
public RTL2832TunerConfiguration(long minimumFrequency, long maximumFrequency)
|
public RTL2832TunerConfiguration(long minimumFrequency, long maximumFrequency, int defaultGain)
|
||||||
{
|
{
|
||||||
super(minimumFrequency, maximumFrequency);
|
super(minimumFrequency, maximumFrequency);
|
||||||
|
mGain = defaultGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RTL2832TunerConfiguration(String uniqueID)
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
* @param uniqueID for the tuner
|
||||||
|
* @param defaultGain in tenths of a dB
|
||||||
|
*/
|
||||||
|
public RTL2832TunerConfiguration(String uniqueID, int defaultGain)
|
||||||
{
|
{
|
||||||
super(uniqueID);
|
super(uniqueID);
|
||||||
|
mGain = defaultGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuner gain, applied when AGC is off.
|
||||||
|
* @return gain in tenths of a dB.
|
||||||
|
*/
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "gain")
|
||||||
|
public int getGain()
|
||||||
|
{
|
||||||
|
return mGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tuner gain, applied when AGC is off.
|
||||||
|
* @param gain in tenths of a dB
|
||||||
|
*/
|
||||||
|
public void setGain(int gain)
|
||||||
|
{
|
||||||
|
mGain = gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the tuner's automatic gain control is enabled.
|
||||||
|
*/
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "agc")
|
||||||
|
public boolean isAGC()
|
||||||
|
{
|
||||||
|
return mAGC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the enabled state of the tuner's automatic gain control.
|
||||||
|
*/
|
||||||
|
public void setAGC(boolean agc)
|
||||||
|
{
|
||||||
|
mAGC = agc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a gain value from the name of a gain enumeration entry used by legacy tuner configurations, for
|
||||||
|
* example GAIN_327, GAIN_PLUS_200, GAIN_MINUS_50, PLUS_240 or MINUS_10.
|
||||||
|
* @param name of the legacy enumeration entry
|
||||||
|
* @return gain value, or null if the name doesn't contain a gain value.
|
||||||
|
*/
|
||||||
|
protected static Integer parseLegacyGain(String name)
|
||||||
|
{
|
||||||
|
if(name != null)
|
||||||
|
{
|
||||||
|
String value = name.substring(name.lastIndexOf('_') + 1);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int gain = Integer.parseInt(value);
|
||||||
|
return name.contains("MINUS") ? -gain : gain;
|
||||||
|
}
|
||||||
|
catch(NumberFormatException nfe)
|
||||||
|
{
|
||||||
|
//Not a gain value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "sample_rate")
|
@JacksonXmlProperty(isAttribute = true, localName = "sample_rate")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,508 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* 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 <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.source.tuner.rtl;
|
||||||
|
|
||||||
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
|
import io.github.dsheirer.source.SourceException;
|
||||||
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
|
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
||||||
|
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.r8x.R8xTunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
||||||
|
import javax.swing.DefaultComboBoxModel;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JSeparator;
|
||||||
|
import javax.swing.JToggleButton;
|
||||||
|
import javax.swing.SpinnerNumberModel;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RTL-2832 tuner editor for all supported embedded tuner types (E4000, FC0012, FC0013, FC2580, R820T and R828D).
|
||||||
|
*
|
||||||
|
* Gain options are the gains that librtlsdr reports for the embedded tuner, plus the tuner's automatic gain control.
|
||||||
|
* The E4000 additionally has a total IF gain that is distributed across its IF gain stages.
|
||||||
|
*/
|
||||||
|
public class RTL2832TunerEditor extends TunerEditor<RTL2832Tuner, RTL2832TunerConfiguration>
|
||||||
|
{
|
||||||
|
private static final Logger mLog = LoggerFactory.getLogger(RTL2832TunerEditor.class);
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private JButton mTunerInfoButton;
|
||||||
|
private JToggleButton mBiasTButton;
|
||||||
|
private JComboBox<SampleRate> mSampleRateCombo;
|
||||||
|
private JToggleButton mAgcToggleButton;
|
||||||
|
private JComboBox<Gain> mGainCombo;
|
||||||
|
private JLabel mIFGainLabel;
|
||||||
|
private JComboBox<Integer> mIFGainCombo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
* @param userPreferences for wide-band recordings
|
||||||
|
* @param tunerManager for saving configurations
|
||||||
|
* @param discoveredTuner to edit
|
||||||
|
*/
|
||||||
|
public RTL2832TunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner)
|
||||||
|
{
|
||||||
|
super(userPreferences, tunerManager, discoveredTuner);
|
||||||
|
init();
|
||||||
|
tunerStatusUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMinimumTunableFrequency()
|
||||||
|
{
|
||||||
|
if(hasTuner())
|
||||||
|
{
|
||||||
|
return getTuner().getController().getMinimumFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
return R8xTunerConfiguration.MINIMUM_TUNABLE_FREQUENCY_HZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMaximumTunableFrequency()
|
||||||
|
{
|
||||||
|
if(hasTuner())
|
||||||
|
{
|
||||||
|
return getTuner().getController().getMaximumFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
return R8xTunerConfiguration.MAXIMUM_TUNABLE_FREQUENCY_HZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLogPrefix()
|
||||||
|
{
|
||||||
|
return (hasTuner() ? getTuner().getController().getTunerType().getLabel() : "RTL-2832") + " Tuner Controller - ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the tuner has an E4000 embedded tuner, which supports IF gain control.
|
||||||
|
*/
|
||||||
|
private boolean isE4K()
|
||||||
|
{
|
||||||
|
return hasTuner() && getTuner().getController().getTunerType() == TunerType.ELONICS_E4000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]",
|
||||||
|
"[][][][][][][][][][][][][][][][grow]"));
|
||||||
|
|
||||||
|
add(new JLabel("Tuner:"));
|
||||||
|
add(getTunerIdLabel());
|
||||||
|
add(getTunerInfoButton());
|
||||||
|
|
||||||
|
add(new JLabel("Status:"));
|
||||||
|
add(getTunerStatusLabel());
|
||||||
|
add(getBiasTButton(), "wrap");
|
||||||
|
|
||||||
|
add(getButtonPanel(), "span,align left");
|
||||||
|
|
||||||
|
add(new JSeparator(), "span,growx,push");
|
||||||
|
|
||||||
|
add(new JLabel("Frequency (MHz):"));
|
||||||
|
add(getFrequencyPanel(), "wrap");
|
||||||
|
|
||||||
|
add(new JLabel("Sample Rate:"));
|
||||||
|
add(getSampleRateCombo(), "wrap");
|
||||||
|
|
||||||
|
add(new JSeparator(), "span,growx,push");
|
||||||
|
add(new JLabel("Gain Control"), "wrap");
|
||||||
|
|
||||||
|
add(new JLabel("Gain:"));
|
||||||
|
add(getGainCombo());
|
||||||
|
add(getAgcToggleButton(), "wrap");
|
||||||
|
|
||||||
|
add(getIFGainLabel());
|
||||||
|
add(getIFGainCombo(), "wrap");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tunerStatusUpdated()
|
||||||
|
{
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
if(hasTuner())
|
||||||
|
{
|
||||||
|
getTunerIdLabel().setText(getTuner().getPreferredName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
getTunerIdLabel().setText(getDiscoveredTuner().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
String status = getDiscoveredTuner().getTunerStatus().toString();
|
||||||
|
if(getDiscoveredTuner().hasErrorMessage())
|
||||||
|
{
|
||||||
|
status += " - " + getDiscoveredTuner().getErrorMessage();
|
||||||
|
}
|
||||||
|
getTunerStatusLabel().setText(status);
|
||||||
|
getButtonPanel().updateControls();
|
||||||
|
getFrequencyPanel().updateControls();
|
||||||
|
|
||||||
|
DefaultComboBoxModel<Gain> gainModel = new DefaultComboBoxModel<>();
|
||||||
|
|
||||||
|
if(hasTuner())
|
||||||
|
{
|
||||||
|
RTL2832TunerController controller = getTuner().getController();
|
||||||
|
|
||||||
|
if(controller.hasGainControl())
|
||||||
|
{
|
||||||
|
for(int gain : controller.getTunerGains())
|
||||||
|
{
|
||||||
|
gainModel.addElement(new Gain(gain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getGainCombo().setModel(gainModel);
|
||||||
|
|
||||||
|
boolean e4k = isE4K();
|
||||||
|
getIFGainLabel().setVisible(e4k);
|
||||||
|
getIFGainCombo().setVisible(e4k);
|
||||||
|
|
||||||
|
if(hasTuner() && hasConfiguration())
|
||||||
|
{
|
||||||
|
RTL2832TunerConfiguration config = getConfiguration();
|
||||||
|
getBiasTButton().setEnabled(true);
|
||||||
|
getBiasTButton().setSelected(config.isBiasT());
|
||||||
|
getTunerInfoButton().setEnabled(true);
|
||||||
|
getSampleRateCombo().setEnabled(true);
|
||||||
|
getSampleRateCombo().setSelectedItem(config.getSampleRate());
|
||||||
|
RTL2832TunerController controller = getTuner().getController();
|
||||||
|
//The FC2580 always uses its internal AGC and the FC0012 doesn't support AGC
|
||||||
|
boolean agc = controller.hasGainControl() ? controller.hasTunerAGC() && config.isAGC() : true;
|
||||||
|
getAgcToggleButton().setEnabled(controller.hasTunerAGC());
|
||||||
|
getAgcToggleButton().setSelected(agc);
|
||||||
|
getAgcToggleButton().setToolTipText(getAgcToolTip(controller));
|
||||||
|
getGainCombo().setEnabled(!agc && gainModel.getSize() > 0);
|
||||||
|
getGainCombo().setSelectedItem(new Gain(getTuner().getController().getClosestTunerGain(config.getGain())));
|
||||||
|
|
||||||
|
if(e4k && config instanceof E4KTunerConfiguration e4kConfig)
|
||||||
|
{
|
||||||
|
getIFGainCombo().setEnabled(true);
|
||||||
|
getIFGainCombo().setSelectedItem(e4kConfig.getIFGainDb());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
getBiasTButton().setEnabled(false);
|
||||||
|
getBiasTButton().setSelected(false);
|
||||||
|
getTunerInfoButton().setEnabled(false);
|
||||||
|
getSampleRateCombo().setEnabled(false);
|
||||||
|
getAgcToggleButton().setEnabled(false);
|
||||||
|
getGainCombo().setEnabled(false);
|
||||||
|
getIFGainCombo().setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSampleRateToolTip();
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bias-T toggle button
|
||||||
|
*/
|
||||||
|
private JToggleButton getBiasTButton()
|
||||||
|
{
|
||||||
|
if(mBiasTButton == null)
|
||||||
|
{
|
||||||
|
mBiasTButton = new JToggleButton("Bias-T");
|
||||||
|
mBiasTButton.setEnabled(false);
|
||||||
|
mBiasTButton.addActionListener(e -> {
|
||||||
|
if(!isLoading())
|
||||||
|
{
|
||||||
|
getTuner().getController().setBiasT(mBiasTButton.isSelected());
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mBiasTButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button that provides tuner information
|
||||||
|
*/
|
||||||
|
private JButton getTunerInfoButton()
|
||||||
|
{
|
||||||
|
if(mTunerInfoButton == null)
|
||||||
|
{
|
||||||
|
mTunerInfoButton = new JButton("Info");
|
||||||
|
mTunerInfoButton.setEnabled(false);
|
||||||
|
mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(RTL2832TunerEditor.this,
|
||||||
|
getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
return mTunerInfoButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the current AGC and gain settings to the tuner and saves the configuration.
|
||||||
|
*/
|
||||||
|
private void applyGain()
|
||||||
|
{
|
||||||
|
if(isLoading() || !hasTuner())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean agc = getAgcToggleButton().isSelected();
|
||||||
|
Gain gain = (Gain)getGainCombo().getSelectedItem();
|
||||||
|
getGainCombo().setEnabled(!agc && getGainCombo().getItemCount() > 0);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getTuner().getController().setGain(agc, gain != null ? gain.value() : getConfiguration().getGain());
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
catch(SourceException se)
|
||||||
|
{
|
||||||
|
JOptionPane.showMessageDialog(RTL2832TunerEditor.this, getLogPrefix() +
|
||||||
|
"couldn't apply the gain setting - " + se.getLocalizedMessage());
|
||||||
|
mLog.error(getLogPrefix() + "couldn't apply gain setting", se);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tool tip for the AGC button that explains when the tuner doesn't support switching AGC.
|
||||||
|
*/
|
||||||
|
private static String getAgcToolTip(RTL2832TunerController controller)
|
||||||
|
{
|
||||||
|
if(!controller.hasGainControl())
|
||||||
|
{
|
||||||
|
return "The " + controller.getTunerType().getLabel() + " tuner always uses its automatic gain control (AGC)";
|
||||||
|
}
|
||||||
|
else if(!controller.hasTunerAGC())
|
||||||
|
{
|
||||||
|
return "The " + controller.getTunerType().getLabel() + " tuner doesn't support automatic gain control (AGC)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Turn on the tuner's automatic gain control (AGC)";
|
||||||
|
}
|
||||||
|
|
||||||
|
private JToggleButton getAgcToggleButton()
|
||||||
|
{
|
||||||
|
if(mAgcToggleButton == null)
|
||||||
|
{
|
||||||
|
mAgcToggleButton = new JToggleButton("AGC");
|
||||||
|
mAgcToggleButton.setEnabled(false);
|
||||||
|
mAgcToggleButton.setToolTipText("Turn on the tuner's automatic gain control (AGC)");
|
||||||
|
mAgcToggleButton.addActionListener(e -> applyGain());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mAgcToggleButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JComboBox<Gain> getGainCombo()
|
||||||
|
{
|
||||||
|
if(mGainCombo == null)
|
||||||
|
{
|
||||||
|
mGainCombo = new JComboBox<>();
|
||||||
|
mGainCombo.setEnabled(false);
|
||||||
|
mGainCombo.setToolTipText("Tuner gain. Turn off AGC to enable adjustment.");
|
||||||
|
mGainCombo.addActionListener(e -> applyGain());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mGainCombo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JLabel getIFGainLabel()
|
||||||
|
{
|
||||||
|
if(mIFGainLabel == null)
|
||||||
|
{
|
||||||
|
mIFGainLabel = new JLabel("IF Gain (dB):");
|
||||||
|
}
|
||||||
|
|
||||||
|
return mIFGainLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JComboBox<Integer> getIFGainCombo()
|
||||||
|
{
|
||||||
|
if(mIFGainCombo == null)
|
||||||
|
{
|
||||||
|
mIFGainCombo = new JComboBox<>();
|
||||||
|
|
||||||
|
for(int gain = RTL2832TunerController.E4K_IF_GAIN_MINIMUM; gain <= RTL2832TunerController.E4K_IF_GAIN_MAXIMUM; gain++)
|
||||||
|
{
|
||||||
|
mIFGainCombo.addItem(gain);
|
||||||
|
}
|
||||||
|
|
||||||
|
mIFGainCombo.setEnabled(false);
|
||||||
|
mIFGainCombo.setToolTipText("E4000 IF gain, distributed across the six IF gain stages");
|
||||||
|
mIFGainCombo.addActionListener(e ->
|
||||||
|
{
|
||||||
|
Integer gain = (Integer)mIFGainCombo.getSelectedItem();
|
||||||
|
|
||||||
|
if(!isLoading() && hasTuner() && gain != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getTuner().getController().setE4KIFGain(gain);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
catch(SourceException se)
|
||||||
|
{
|
||||||
|
JOptionPane.showMessageDialog(RTL2832TunerEditor.this, getLogPrefix() +
|
||||||
|
"couldn't apply the IF gain setting - " + se.getLocalizedMessage());
|
||||||
|
mLog.error(getLogPrefix() + "couldn't apply IF gain setting", se);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mIFGainCombo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JComboBox<SampleRate> 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 se)
|
||||||
|
{
|
||||||
|
JOptionPane.showMessageDialog(RTL2832TunerEditor.this,
|
||||||
|
getLogPrefix() + "couldn't apply the sample rate setting [" +
|
||||||
|
sampleRate.getLabel() + "] " + se.getLocalizedMessage());
|
||||||
|
|
||||||
|
mLog.error(getLogPrefix() + "couldn't apply sample rate setting [" + sampleRate.getLabel() +
|
||||||
|
"]", se);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mSampleRateCombo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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("<html><h3>RTL-2832 with ").append(getTuner().getController().getTunerType().getLabel())
|
||||||
|
.append(" Tuner</h3>");
|
||||||
|
|
||||||
|
if(descriptor == null)
|
||||||
|
{
|
||||||
|
sb.append("No EEPROM Descriptor Available");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append("<b>USB ID: </b>").append(descriptor.getVendorID()).append(":")
|
||||||
|
.append(descriptor.getProductID()).append("<br>");
|
||||||
|
sb.append("<b>Vendor: </b>").append(descriptor.getVendorLabel()).append("<br>");
|
||||||
|
sb.append("<b>Product: </b>").append(descriptor.getProductLabel()).append("<br>");
|
||||||
|
sb.append("<b>Serial: </b>").append(descriptor.getSerial()).append("<br>");
|
||||||
|
sb.append("<b>IR Enabled: </b>").append(descriptor.irEnabled()).append("<br>");
|
||||||
|
sb.append("<b>Remote Wake: </b>").append(descriptor.remoteWakeupEnabled()).append("<br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save()
|
||||||
|
{
|
||||||
|
if(hasConfiguration() && !isLoading())
|
||||||
|
{
|
||||||
|
RTL2832TunerConfiguration config = getConfiguration();
|
||||||
|
config.setBiasT(getTuner().getController().isBiasT());
|
||||||
|
config.setFrequency(getFrequencyControl().getFrequency());
|
||||||
|
config.setMinimumFrequency(getMinimumFrequencyTextField().getFrequency());
|
||||||
|
config.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());
|
||||||
|
|
||||||
|
if(getGainCombo().getSelectedItem() instanceof Gain gain)
|
||||||
|
{
|
||||||
|
config.setGain(gain.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config instanceof E4KTunerConfiguration e4k && getIFGainCombo().getSelectedItem() instanceof Integer ifGain)
|
||||||
|
{
|
||||||
|
e4k.setIFGainDb(ifGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveConfiguration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuner gain combo box entry.
|
||||||
|
* @param value in tenths of a dB
|
||||||
|
*/
|
||||||
|
private record Gain(int value)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%.1f dB", value / 10.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ package io.github.dsheirer.source.tuner.rtl;
|
|||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.R8xEmbeddedTuner;
|
import io.github.dsheirer.source.tuner.rtl.r8x.R8xTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
@@ -70,14 +70,14 @@ public class RTL2832UnknownTunerEditor extends TunerEditor<RTL2832Tuner, RTL2832
|
|||||||
public long getMinimumTunableFrequency()
|
public long getMinimumTunableFrequency()
|
||||||
{
|
{
|
||||||
//Bogus value.
|
//Bogus value.
|
||||||
return R8xEmbeddedTuner.MINIMUM_TUNABLE_FREQUENCY_HZ;
|
return R8xTunerConfiguration.MINIMUM_TUNABLE_FREQUENCY_HZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getMaximumTunableFrequency()
|
public long getMaximumTunableFrequency()
|
||||||
{
|
{
|
||||||
//Bogus value.
|
//Bogus value.
|
||||||
return R8xEmbeddedTuner.MAXIMUM_TUNABLE_FREQUENCY_HZ;
|
return R8xTunerConfiguration.MAXIMUM_TUNABLE_FREQUENCY_HZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,30 +19,53 @@
|
|||||||
package io.github.dsheirer.source.tuner.rtl.e4k;
|
package io.github.dsheirer.source.tuner.rtl.e4k;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KGain;
|
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KLNAGain;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.IFGain;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RTL-2832 with embedded E4000 tuner configuration
|
* RTL-2832 with embedded E4000 tuner configuration
|
||||||
*/
|
*/
|
||||||
public class E4KTunerConfiguration extends RTL2832TunerConfiguration
|
public class E4KTunerConfiguration extends RTL2832TunerConfiguration
|
||||||
{
|
{
|
||||||
private E4KGain mMasterGain = E4KGain.MANUAL;
|
public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 52_000_000;
|
||||||
private E4KMixerGain mMixerGain = E4KMixerGain.GAIN_4;
|
public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 2_200_000_000l;
|
||||||
private E4KLNAGain mLNAGain = E4KLNAGain.GAIN_PLUS_200;
|
public static final double USABLE_BANDWIDTH_PERCENT = 0.95;
|
||||||
private IFGain mIFGain = IFGain.LINEARITY_8;
|
public static final int DC_SPIKE_AVOID_BUFFER = 15000;
|
||||||
|
/**
|
||||||
|
* Default gain in tenths of a dB
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_GAIN = 240;
|
||||||
|
/**
|
||||||
|
* Default total IF gain in dB
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_IF_GAIN = 24;
|
||||||
|
|
||||||
|
private int mIFGain = DEFAULT_IF_GAIN;
|
||||||
|
|
||||||
|
//Legacy gain settings, used to convert configurations from the previous E4000 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 E4KTunerConfiguration()
|
public E4KTunerConfiguration()
|
||||||
{
|
{
|
||||||
super(E4KEmbeddedTuner.MINIMUM_TUNABLE_FREQUENCY_HZ, E4KEmbeddedTuner.MAXIMUM_TUNABLE_FREQUENCY_HZ);
|
super(MINIMUM_TUNABLE_FREQUENCY_HZ, MAXIMUM_TUNABLE_FREQUENCY_HZ, DEFAULT_GAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
*
|
||||||
|
* @param uniqueID for the tuner
|
||||||
|
*/
|
||||||
|
public E4KTunerConfiguration(String uniqueID)
|
||||||
|
{
|
||||||
|
super(uniqueID, DEFAULT_GAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@@ -53,56 +76,95 @@ public class E4KTunerConfiguration extends RTL2832TunerConfiguration
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an instance
|
* Total IF gain, distributed across the six IF gain stages.
|
||||||
*
|
* @return gain in dB
|
||||||
* @param uniqueID for the tuner
|
|
||||||
*/
|
*/
|
||||||
public E4KTunerConfiguration(String uniqueID)
|
@JacksonXmlProperty(isAttribute = true, localName = "if_gain_db")
|
||||||
{
|
public int getIFGainDb()
|
||||||
super(uniqueID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "master_gain")
|
|
||||||
public E4KGain getMasterGain()
|
|
||||||
{
|
|
||||||
return mMasterGain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMasterGain(E4KGain gain)
|
|
||||||
{
|
|
||||||
mMasterGain = gain;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "mixer_gain")
|
|
||||||
public E4KMixerGain getMixerGain()
|
|
||||||
{
|
|
||||||
return mMixerGain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMixerGain(E4KMixerGain mixerGain)
|
|
||||||
{
|
|
||||||
mMixerGain = mixerGain;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "lna_gain")
|
|
||||||
public E4KLNAGain getLNAGain()
|
|
||||||
{
|
|
||||||
return mLNAGain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLNAGain(E4KLNAGain lnaGain)
|
|
||||||
{
|
|
||||||
mLNAGain = lnaGain;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "if_gain")
|
|
||||||
public IFGain getIFGain()
|
|
||||||
{
|
{
|
||||||
return mIFGain;
|
return mIFGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIFGain(IFGain ifGain)
|
/**
|
||||||
|
* Sets the total IF gain
|
||||||
|
* @param gain in dB, in the range E4K_IF_GAIN_MINIMUM to E4K_IF_GAIN_MAXIMUM
|
||||||
|
*/
|
||||||
|
public void setIFGainDb(int gain)
|
||||||
{
|
{
|
||||||
mIFGain = ifGain;
|
mIFGain = Math.clamp(gain, RTL2832TunerController.E4K_IF_GAIN_MINIMUM,
|
||||||
|
RTL2832TunerController.E4K_IF_GAIN_MAXIMUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the legacy IF gain setting, such as LINEARITY_8 or SENSITIVITY_24, where the value is in dB.
|
||||||
|
*/
|
||||||
|
@JsonSetter("ifgain")
|
||||||
|
public void setLegacyIFGain(String ifGain)
|
||||||
|
{
|
||||||
|
Integer gain = parseLegacyGain(ifGain);
|
||||||
|
|
||||||
|
if(gain != null)
|
||||||
|
{
|
||||||
|
setIFGainDb(gain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the legacy master gain setting: AUTOMATIC, MANUAL (using the LNA and mixer gains) or a gain value
|
||||||
|
* such as PLUS_240 or MINUS_10 in tenths of a dB.
|
||||||
|
*/
|
||||||
|
@JsonSetter("masterGain")
|
||||||
|
public void setLegacyMasterGain(String masterGain)
|
||||||
|
{
|
||||||
|
if("AUTOMATIC".equals(masterGain))
|
||||||
|
{
|
||||||
|
setAGC(true);
|
||||||
|
}
|
||||||
|
else if("MANUAL".equals(masterGain))
|
||||||
|
{
|
||||||
|
mLegacyManualGain = true;
|
||||||
|
updateLegacyManualGain();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Integer gain = parseLegacyGain(masterGain);
|
||||||
|
|
||||||
|
if(gain != null)
|
||||||
|
{
|
||||||
|
setGain(gain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the legacy LNA gain setting, such as GAIN_PLUS_200 or GAIN_MINUS_50 in tenths of a dB.
|
||||||
|
*/
|
||||||
|
@JsonSetter("lnagain")
|
||||||
|
public void setLegacyLNAGain(String lnaGain)
|
||||||
|
{
|
||||||
|
mLegacyLNAGain = parseLegacyGain(lnaGain);
|
||||||
|
updateLegacyManualGain();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the legacy mixer gain setting, GAIN_4 or GAIN_12 in dB.
|
||||||
|
*/
|
||||||
|
@JsonSetter("mixerGain")
|
||||||
|
public void setLegacyMixerGain(String mixerGain)
|
||||||
|
{
|
||||||
|
Integer gain = parseLegacyGain(mixerGain);
|
||||||
|
mLegacyMixerGain = gain != null ? gain * 10 : null;
|
||||||
|
updateLegacyManualGain();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the gain to the sum of the legacy LNA and mixer gains when the legacy master gain is MANUAL.
|
||||||
|
*/
|
||||||
|
private void updateLegacyManualGain()
|
||||||
|
{
|
||||||
|
if(mLegacyManualGain && mLegacyLNAGain != null && mLegacyMixerGain != null)
|
||||||
|
{
|
||||||
|
setGain(mLegacyLNAGain + mLegacyMixerGain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,516 +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 <http://www.gnu.org/licenses/>
|
|
||||||
* ****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.source.tuner.rtl.e4k;
|
|
||||||
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import io.github.dsheirer.source.SourceException;
|
|
||||||
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KGain;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KLNAGain;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain;
|
|
||||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.usb4java.LibUsbException;
|
|
||||||
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JSeparator;
|
|
||||||
import javax.swing.JToggleButton;
|
|
||||||
import javax.swing.SpinnerNumberModel;
|
|
||||||
import javax.usb.UsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E4000 tuner editor
|
|
||||||
*/
|
|
||||||
public class E4KTunerEditor extends TunerEditor<RTL2832Tuner, E4KTunerConfiguration>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(E4KTunerEditor.class);
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private JButton mTunerInfoButton;
|
|
||||||
private JToggleButton mBiasTButton;
|
|
||||||
private JComboBox<SampleRate> mSampleRateCombo;
|
|
||||||
private JComboBox<E4KGain> mMasterGainCombo;
|
|
||||||
private JComboBox<E4KMixerGain> mMixerGainCombo;
|
|
||||||
private JComboBox<E4KLNAGain> mLNAGainCombo;
|
|
||||||
private JComboBox<E4KEmbeddedTuner.IFGain> mIfGainCombo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance
|
|
||||||
* @param userPreferences for wide-band recordings
|
|
||||||
* @param tunerManager to save configuration
|
|
||||||
* @param discoveredTuner to control
|
|
||||||
*/
|
|
||||||
public E4KTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner)
|
|
||||||
{
|
|
||||||
super(userPreferences, tunerManager, discoveredTuner);
|
|
||||||
init();
|
|
||||||
tunerStatusUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMinimumTunableFrequency()
|
|
||||||
{
|
|
||||||
return E4KEmbeddedTuner.MINIMUM_TUNABLE_FREQUENCY_HZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMaximumTunableFrequency()
|
|
||||||
{
|
|
||||||
return E4KEmbeddedTuner.MAXIMUM_TUNABLE_FREQUENCY_HZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access to the E4000 embedded tuner
|
|
||||||
* @return E4000 tuner if there is a tuner, or null otherwise
|
|
||||||
*/
|
|
||||||
public E4KEmbeddedTuner getEmbeddedTuner()
|
|
||||||
{
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
return (E4KEmbeddedTuner)getTuner().getController().getEmbeddedTuner();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]",
|
|
||||||
"[][][][][][][][][][][][][][][][grow]"));
|
|
||||||
|
|
||||||
add(new JLabel("Tuner:"));
|
|
||||||
add(getTunerIdLabel());
|
|
||||||
add(getTunerInfoButton());
|
|
||||||
|
|
||||||
add(new JLabel("Status:"));
|
|
||||||
add(getTunerStatusLabel());
|
|
||||||
add(getBiasTButton(), "wrap");
|
|
||||||
|
|
||||||
add(getButtonPanel(), "span,align left");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span,growx,push");
|
|
||||||
|
|
||||||
add(new JLabel("Frequency (MHz):"));
|
|
||||||
add(getFrequencyPanel(), "span 2");
|
|
||||||
|
|
||||||
add(new JLabel("Sample Rate:"));
|
|
||||||
add(getSampleRateCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span,growx,push");
|
|
||||||
add(new JLabel("Mixer/LNA Gain Control"), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Master:"));
|
|
||||||
add(getMasterGainCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Mixer:"));
|
|
||||||
add(getMixerGainCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("LNA:"));
|
|
||||||
add(getLNAGainCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span,growx,push");
|
|
||||||
|
|
||||||
add(new JLabel("IF Gain:"));
|
|
||||||
add(getIfGainCombo(), "wrap");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tunerStatusUpdated()
|
|
||||||
{
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
getTunerIdLabel().setText(getTuner().getPreferredName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getTunerIdLabel().setText(getDiscoveredTuner().getId());
|
|
||||||
}
|
|
||||||
getTunerStatusLabel().setText(getDiscoveredTuner().getTunerStatus().toString());
|
|
||||||
getButtonPanel().updateControls();
|
|
||||||
getFrequencyPanel().updateControls();
|
|
||||||
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
getBiasTButton().setEnabled(true);
|
|
||||||
getBiasTButton().setSelected(getConfiguration().isBiasT());
|
|
||||||
getTunerInfoButton().setEnabled(true);
|
|
||||||
getSampleRateCombo().setEnabled(true);
|
|
||||||
getSampleRateCombo().setSelectedItem(getConfiguration().getSampleRate());
|
|
||||||
getMasterGainCombo().setEnabled(true);
|
|
||||||
getIfGainCombo().setEnabled(true);
|
|
||||||
getIfGainCombo().setSelectedItem(getConfiguration().getIFGain());
|
|
||||||
|
|
||||||
E4KGain gain = getConfiguration().getMasterGain();
|
|
||||||
getMasterGainCombo().setEnabled(true);
|
|
||||||
getMasterGainCombo().setSelectedItem(gain);
|
|
||||||
|
|
||||||
if(gain == E4KGain.MANUAL)
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setSelectedItem(getConfiguration().getMixerGain());
|
|
||||||
getMixerGainCombo().setEnabled(true);
|
|
||||||
|
|
||||||
getLNAGainCombo().setSelectedItem(getConfiguration().getLNAGain());
|
|
||||||
getLNAGainCombo().setEnabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setEnabled(false);
|
|
||||||
getMixerGainCombo().setSelectedItem(gain.getMixerGain());
|
|
||||||
|
|
||||||
getLNAGainCombo().setEnabled(false);
|
|
||||||
getLNAGainCombo().setSelectedItem(gain.getLNAGain());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getBiasTButton().setEnabled(false);
|
|
||||||
getBiasTButton().setSelected(false);
|
|
||||||
getTunerInfoButton().setEnabled(false);
|
|
||||||
getSampleRateCombo().setEnabled(false);
|
|
||||||
getMasterGainCombo().setEnabled(false);
|
|
||||||
getMixerGainCombo().setEnabled(false);
|
|
||||||
getLNAGainCombo().setEnabled(false);
|
|
||||||
getIfGainCombo().setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSampleRateToolTip();
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bias-T toggle button
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private JToggleButton getBiasTButton()
|
|
||||||
{
|
|
||||||
if(mBiasTButton == null)
|
|
||||||
{
|
|
||||||
mBiasTButton = new JToggleButton("Bias-T");
|
|
||||||
mBiasTButton.setEnabled(false);
|
|
||||||
mBiasTButton.addActionListener(e -> {
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
getTuner().getController().setBiasT(mBiasTButton.isSelected());
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return mBiasTButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getIfGainCombo()
|
|
||||||
{
|
|
||||||
if(mIfGainCombo == null)
|
|
||||||
{
|
|
||||||
mIfGainCombo = new JComboBox<>(E4KEmbeddedTuner.IFGain.values());
|
|
||||||
mIfGainCombo.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
E4KEmbeddedTuner.IFGain selected = (E4KEmbeddedTuner.IFGain) getIfGainCombo().getSelectedItem();
|
|
||||||
getEmbeddedTuner().setIFGain(selected, true);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(LibUsbException lue)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - "
|
|
||||||
+ "couldn't apply the IF setting - " + lue.getLocalizedMessage());
|
|
||||||
mLog.error("E4000 Tuner Controller - couldn't apply IF gain setting", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mIfGainCombo.setToolTipText("Linear IF Gain");
|
|
||||||
mIfGainCombo.setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mIfGainCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getLNAGainCombo()
|
|
||||||
{
|
|
||||||
if(mLNAGainCombo == null)
|
|
||||||
{
|
|
||||||
mLNAGainCombo = new JComboBox<>(E4KLNAGain.values());
|
|
||||||
mLNAGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
E4KLNAGain lnaGain = (E4KLNAGain) mLNAGainCombo.getSelectedItem();
|
|
||||||
getEmbeddedTuner().setLNAGain(lnaGain, true);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - "
|
|
||||||
+ "couldn't apply the LNA gain setting - " + e.getLocalizedMessage());
|
|
||||||
mLog.error("E4000 Tuner Controller - couldn't apply LNA gain setting - ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mLNAGainCombo.setToolTipText("<html>LNA Gain. Set master gain to <b>MANUAL</b> to enable adjustment</html>");
|
|
||||||
mLNAGainCombo.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mLNAGainCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getMixerGainCombo()
|
|
||||||
{
|
|
||||||
if(mMixerGainCombo == null)
|
|
||||||
{
|
|
||||||
mMixerGainCombo = new JComboBox<>(E4KMixerGain.values());
|
|
||||||
mMixerGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
E4KMixerGain mixerGain = (E4KMixerGain) mMixerGainCombo.getSelectedItem();
|
|
||||||
getEmbeddedTuner().setMixerGain(mixerGain, true);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - "
|
|
||||||
+ "couldn't apply the mixer gain setting - " + e.getLocalizedMessage());
|
|
||||||
mLog.error("E4000 Tuner Controller - couldn't apply mixer gain setting", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mMixerGainCombo.setToolTipText("<html>Mixer Gain. Set master gain to <b>MASTER</b> to enable adjustment</html>");
|
|
||||||
mMixerGainCombo.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mMixerGainCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getMasterGainCombo()
|
|
||||||
{
|
|
||||||
if(mMasterGainCombo == null)
|
|
||||||
{
|
|
||||||
mMasterGainCombo = new JComboBox<>(E4KGain.values());
|
|
||||||
mMasterGainCombo.setEnabled(false);
|
|
||||||
mMasterGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
E4KGain gain = (E4KGain) mMasterGainCombo.getSelectedItem();
|
|
||||||
getEmbeddedTuner().setGain((E4KGain) mMasterGainCombo.getSelectedItem(), true);
|
|
||||||
if(gain == E4KGain.MANUAL)
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setSelectedItem(getEmbeddedTuner().getMixerGain(true));
|
|
||||||
getMixerGainCombo().setEnabled(true);
|
|
||||||
getLNAGainCombo().setSelectedItem(getEmbeddedTuner().getLNAGain(true));
|
|
||||||
getLNAGainCombo().setEnabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setEnabled(false);
|
|
||||||
getMixerGainCombo().setSelectedItem(gain.getMixerGain());
|
|
||||||
getLNAGainCombo().setEnabled(false);
|
|
||||||
getLNAGainCombo().setSelectedItem(gain.getLNAGain());
|
|
||||||
}
|
|
||||||
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(E4KTunerEditor.this, "E4000 Tuner Controller - "
|
|
||||||
+ "couldn't apply the gain setting - " + e.getLocalizedMessage());
|
|
||||||
mLog.error("E4000 Tuner Controller - couldn't apply gain setting", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mMasterGainCombo.setToolTipText("<html>Select <b>AUTOMATIC</b> for auto gain, <b>MANUAL</b> to enable<br> " +
|
|
||||||
"independent control of <i>Mixer</i>, <i>LNA</i> and <i>Enhance</i> gain<br>settings, or one of the " +
|
|
||||||
"individual gain settings for<br>semi-manual gain control</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
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("<html><h3>RTL-2832 with E4000 Tuner</h3>");
|
|
||||||
|
|
||||||
if(descriptor == null)
|
|
||||||
{
|
|
||||||
sb.append("No EEPROM Descriptor Available");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.append("<b>USB ID: </b>");
|
|
||||||
sb.append(descriptor.getVendorID());
|
|
||||||
sb.append(":");
|
|
||||||
sb.append(descriptor.getProductID());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Vendor: </b>");
|
|
||||||
sb.append(descriptor.getVendorLabel());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Product: </b>");
|
|
||||||
sb.append(descriptor.getProductLabel());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Serial: </b>");
|
|
||||||
sb.append(descriptor.getSerial());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>IR Enabled: </b>");
|
|
||||||
sb.append(descriptor.irEnabled());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Remote Wake: </b>");
|
|
||||||
sb.append(descriptor.remoteWakeupEnabled());
|
|
||||||
sb.append("<br>");
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.source.tuner.rtl.fc0012;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RTL-2832 with embedded FC0012 tuner configuration. The librtlsdr FC0012 driver supports manual LNA gain only.
|
||||||
|
*/
|
||||||
|
public class FC0012TunerConfiguration extends RTL2832TunerConfiguration
|
||||||
|
{
|
||||||
|
public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 22_000_000;
|
||||||
|
public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 948_600_000;
|
||||||
|
public static final double USABLE_BANDWIDTH_PERCENT = 0.95;
|
||||||
|
public static final int DC_SPIKE_AVOID_BUFFER = 15000;
|
||||||
|
/**
|
||||||
|
* Default gain in tenths of a dB
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_GAIN = 179;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor for Jackson
|
||||||
|
*/
|
||||||
|
public FC0012TunerConfiguration()
|
||||||
|
{
|
||||||
|
super(MINIMUM_TUNABLE_FREQUENCY_HZ, MAXIMUM_TUNABLE_FREQUENCY_HZ, DEFAULT_GAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
*
|
||||||
|
* @param uniqueID for the tuner
|
||||||
|
*/
|
||||||
|
public FC0012TunerConfiguration(String uniqueID)
|
||||||
|
{
|
||||||
|
super(uniqueID, DEFAULT_GAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public TunerType getTunerType()
|
||||||
|
{
|
||||||
|
return TunerType.FITIPOWER_FC0012;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,842 +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 <http://www.gnu.org/licenses/>
|
|
||||||
* ****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.source.tuner.rtl.fc0013;
|
|
||||||
|
|
||||||
import io.github.dsheirer.source.SourceException;
|
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.EmbeddedTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.usb4java.LibUsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fitipower FC0013 Tuner
|
|
||||||
* <p>
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
* 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
|
* 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;
|
package io.github.dsheirer.source.tuner.rtl.fc0013;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
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.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
|
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
|
public class FC0013TunerConfiguration extends RTL2832TunerConfiguration
|
||||||
{
|
{
|
||||||
private FC0013EmbeddedTuner.LNAGain mLnaGain = FC0013EmbeddedTuner.LNAGain.G10;
|
public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 13_500_000;
|
||||||
private boolean mAgc = false;
|
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()
|
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
|
@JsonIgnore
|
||||||
@@ -47,50 +70,15 @@ public class FC0013TunerConfiguration extends RTL2832TunerConfiguration
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an instance
|
* Converts the legacy LNA gain setting, G00 (lowest) to G23 (highest), to the librtlsdr gain for the same
|
||||||
*
|
* LNA register value.
|
||||||
* @param uniqueID for the tuner
|
|
||||||
*/
|
*/
|
||||||
public FC0013TunerConfiguration(String uniqueID)
|
@JsonSetter("lnaGain")
|
||||||
|
public void setLegacyLNAGain(String lnaGain)
|
||||||
{
|
{
|
||||||
super(uniqueID);
|
if(lnaGain != null && lnaGain.matches("G\\d\\d"))
|
||||||
}
|
{
|
||||||
|
setGain(LEGACY_GAINS[Math.min(Integer.parseInt(lnaGain.substring(1)), LEGACY_GAINS.length - 1)]);
|
||||||
/**
|
}
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>
|
|
||||||
* ****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.source.tuner.rtl.fc0013;
|
|
||||||
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import io.github.dsheirer.source.SourceException;
|
|
||||||
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate;
|
|
||||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.usb4java.LibUsbException;
|
|
||||||
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JSeparator;
|
|
||||||
import javax.swing.JToggleButton;
|
|
||||||
import javax.swing.SpinnerNumberModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FC0013 Tuner Editor
|
|
||||||
*/
|
|
||||||
public class FC0013TunerEditor extends TunerEditor<RTL2832Tuner, FC0013TunerConfiguration>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(FC0013TunerEditor.class);
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private static final FC0013EmbeddedTuner.LNAGain DEFAULT_LNA_GAIN = FC0013EmbeddedTuner.LNAGain.G14;
|
|
||||||
private JButton mTunerInfoButton;
|
|
||||||
private JToggleButton mBiasTButton;
|
|
||||||
private JComboBox<SampleRate> mSampleRateCombo;
|
|
||||||
|
|
||||||
private JToggleButton mAgcToggleButton;
|
|
||||||
private JComboBox<FC0013EmbeddedTuner.LNAGain> mLNAGainCombo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance
|
|
||||||
* @param userPreferences for wide-band recordings
|
|
||||||
* @param tunerManager for saving configurations
|
|
||||||
* @param discoveredTuner to edit
|
|
||||||
*/
|
|
||||||
public FC0013TunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner)
|
|
||||||
{
|
|
||||||
super(userPreferences, tunerManager, discoveredTuner);
|
|
||||||
init();
|
|
||||||
tunerStatusUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMinimumTunableFrequency()
|
|
||||||
{
|
|
||||||
return FC0013EmbeddedTuner.MINIMUM_TUNABLE_FREQUENCY_HZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMaximumTunableFrequency()
|
|
||||||
{
|
|
||||||
return FC0013EmbeddedTuner.MAXIMUM_TUNABLE_FREQUENCY_HZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access the FC0013 embedded tuner
|
|
||||||
* @return tuner if there is a tuner, or null otherwise
|
|
||||||
*/
|
|
||||||
private FC0013EmbeddedTuner getEmbeddedTuner()
|
|
||||||
{
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
return (FC0013EmbeddedTuner) getTuner().getController().getEmbeddedTuner();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getLogPrefix()
|
|
||||||
{
|
|
||||||
return getEmbeddedTuner().getTunerType().getLabel() + " Tuner Controller - ";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]",
|
|
||||||
"[][][][][][][][][][][][][][][][grow]"));
|
|
||||||
|
|
||||||
add(new JLabel("Tuner:"));
|
|
||||||
add(getTunerIdLabel());
|
|
||||||
add(getTunerInfoButton());
|
|
||||||
|
|
||||||
add(new JLabel("Status:"));
|
|
||||||
add(getTunerStatusLabel());
|
|
||||||
add(getBiasTButton(), "wrap");
|
|
||||||
|
|
||||||
add(getButtonPanel(), "span,align left");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span,growx,push");
|
|
||||||
|
|
||||||
add(new JLabel("Frequency (MHz):"));
|
|
||||||
add(getFrequencyPanel(), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Sample Rate:"));
|
|
||||||
add(getSampleRateCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span,growx,push");
|
|
||||||
|
|
||||||
JPanel gainPanel = new JPanel();
|
|
||||||
gainPanel.add(new JLabel("Gain"));
|
|
||||||
gainPanel.add(getAgcToggleButton());
|
|
||||||
gainPanel.add(new JLabel("LNA:"));
|
|
||||||
add(gainPanel);
|
|
||||||
add(getLNAGainCombo(), "wrap");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tunerStatusUpdated()
|
|
||||||
{
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
getTunerIdLabel().setText(getTuner().getPreferredName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getTunerIdLabel().setText(getDiscoveredTuner().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
String status = getDiscoveredTuner().getTunerStatus().toString();
|
|
||||||
if(getDiscoveredTuner().hasErrorMessage())
|
|
||||||
{
|
|
||||||
status += " - " + getDiscoveredTuner().getErrorMessage();
|
|
||||||
}
|
|
||||||
getTunerStatusLabel().setText(status);
|
|
||||||
getButtonPanel().updateControls();
|
|
||||||
getFrequencyPanel().updateControls();
|
|
||||||
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
getBiasTButton().setEnabled(true);
|
|
||||||
getBiasTButton().setSelected(getConfiguration().isBiasT());
|
|
||||||
getTunerInfoButton().setEnabled(true);
|
|
||||||
getSampleRateCombo().setEnabled(true);
|
|
||||||
getSampleRateCombo().setSelectedItem(getConfiguration().getSampleRate());
|
|
||||||
getAgcToggleButton().setEnabled(true);
|
|
||||||
getAgcToggleButton().setSelected(getConfiguration().getAGC());
|
|
||||||
getLNAGainCombo().setEnabled(!getConfiguration().getAGC());
|
|
||||||
getLNAGainCombo().setSelectedItem(getConfiguration().getLnaGain());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getBiasTButton().setEnabled(false);
|
|
||||||
getBiasTButton().setSelected(false);
|
|
||||||
getTunerInfoButton().setEnabled(false);
|
|
||||||
getSampleRateCombo().setEnabled(false);
|
|
||||||
getAgcToggleButton().setEnabled(false);
|
|
||||||
getLNAGainCombo().setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSampleRateToolTip();
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bias-T toggle button
|
|
||||||
* @return bias-t button
|
|
||||||
*/
|
|
||||||
private JToggleButton getBiasTButton()
|
|
||||||
{
|
|
||||||
if(mBiasTButton == null)
|
|
||||||
{
|
|
||||||
mBiasTButton = new JToggleButton("Bias-T");
|
|
||||||
mBiasTButton.setEnabled(false);
|
|
||||||
mBiasTButton.addActionListener(e -> {
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
getTuner().getController().setBiasT(mBiasTButton.isSelected());
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return mBiasTButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hyperlink button that provides tuner information
|
|
||||||
*/
|
|
||||||
private JButton getTunerInfoButton()
|
|
||||||
{
|
|
||||||
if(mTunerInfoButton == null)
|
|
||||||
{
|
|
||||||
mTunerInfoButton = new JButton("Info");
|
|
||||||
mTunerInfoButton.setEnabled(false);
|
|
||||||
mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(FC0013TunerEditor.this,
|
|
||||||
getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE));
|
|
||||||
}
|
|
||||||
|
|
||||||
return mTunerInfoButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getLNAGainCombo()
|
|
||||||
{
|
|
||||||
if(mLNAGainCombo == null)
|
|
||||||
{
|
|
||||||
mLNAGainCombo = new JComboBox<>(FC0013EmbeddedTuner.LNAGain.values());
|
|
||||||
mLNAGainCombo.setEnabled(false);
|
|
||||||
mLNAGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FC0013EmbeddedTuner.LNAGain lnaGain = (FC0013EmbeddedTuner.LNAGain) mLNAGainCombo.getSelectedItem();
|
|
||||||
|
|
||||||
if(lnaGain == null)
|
|
||||||
{
|
|
||||||
lnaGain = DEFAULT_LNA_GAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mLNAGainCombo.isEnabled())
|
|
||||||
{
|
|
||||||
getEmbeddedTuner().setGain(getAgcToggleButton().isSelected(), lnaGain);
|
|
||||||
}
|
|
||||||
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(FC0013TunerEditor.this, getLogPrefix() +
|
|
||||||
"couldn't apply the LNA gain setting - " + e.getLocalizedMessage());
|
|
||||||
mLog.error(getLogPrefix() + "couldn't apply LNA gain setting - ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mLNAGainCombo.setToolTipText("<html>LNA Gain. Set master gain to <b>MANUAL</b> to enable adjustment</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
return mLNAGainCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(FC0013TunerEditor.this,
|
|
||||||
getLogPrefix() + "couldn't apply the sample rate setting [" +
|
|
||||||
sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage());
|
|
||||||
|
|
||||||
mLog.error(getLogPrefix() + "couldn't apply sample rate setting [" + sampleRate.getLabel() +
|
|
||||||
"]", eSampleRate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return mSampleRateCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JToggleButton getAgcToggleButton()
|
|
||||||
{
|
|
||||||
if(mAgcToggleButton == null)
|
|
||||||
{
|
|
||||||
mAgcToggleButton = new JToggleButton("AGC");
|
|
||||||
mAgcToggleButton.setEnabled(false);
|
|
||||||
mAgcToggleButton.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
boolean agc = getAgcToggleButton().isSelected();
|
|
||||||
FC0013EmbeddedTuner.LNAGain lnaGain = (FC0013EmbeddedTuner.LNAGain)getLNAGainCombo().getSelectedItem();
|
|
||||||
getEmbeddedTuner().setGain(agc, lnaGain);
|
|
||||||
getLNAGainCombo().setEnabled(!agc);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(FC0013TunerEditor.this, getLogPrefix() +
|
|
||||||
"couldn't set AGC" + e.getLocalizedMessage());
|
|
||||||
mLog.error(getLogPrefix() + "couldn't set AGC", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mAgcToggleButton.setToolTipText("<html>Automatic Gain Control (AGC). </html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
return mAgcToggleButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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("<html><h3>RTL-2832 with " + getEmbeddedTuner().getTunerType().getLabel() + " Tuner</h3>");
|
|
||||||
|
|
||||||
if(descriptor == null)
|
|
||||||
{
|
|
||||||
sb.append("No EEPROM Descriptor Available");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.append("<b>USB ID: </b>");
|
|
||||||
sb.append(descriptor.getVendorID());
|
|
||||||
sb.append(":");
|
|
||||||
sb.append(descriptor.getProductID());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Vendor: </b>");
|
|
||||||
sb.append(descriptor.getVendorLabel());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Product: </b>");
|
|
||||||
sb.append(descriptor.getProductLabel());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Serial: </b>");
|
|
||||||
sb.append(descriptor.getSerial());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>IR Enabled: </b>");
|
|
||||||
sb.append(descriptor.irEnabled());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Remote Wake: </b>");
|
|
||||||
sb.append(descriptor.remoteWakeupEnabled());
|
|
||||||
sb.append("<br>");
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.source.tuner.rtl.fc2580;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RTL-2832 with embedded FC2580 tuner configuration. The librtlsdr FC2580 driver doesn't support gain control and the
|
||||||
|
* tuner always uses its internal AGC. The tuner covers 146-308 MHz and 438-924 MHz.
|
||||||
|
*/
|
||||||
|
public class FC2580TunerConfiguration extends RTL2832TunerConfiguration
|
||||||
|
{
|
||||||
|
public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 146_000_000;
|
||||||
|
public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 924_000_000;
|
||||||
|
public static final double USABLE_BANDWIDTH_PERCENT = 0.95;
|
||||||
|
public static final int DC_SPIKE_AVOID_BUFFER = 15000;
|
||||||
|
/**
|
||||||
|
* Default gain in tenths of a dB. Not used, since the tuner doesn't support gain control.
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_GAIN = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor for Jackson
|
||||||
|
*/
|
||||||
|
public FC2580TunerConfiguration()
|
||||||
|
{
|
||||||
|
super(MINIMUM_TUNABLE_FREQUENCY_HZ, MAXIMUM_TUNABLE_FREQUENCY_HZ, DEFAULT_GAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
*
|
||||||
|
* @param uniqueID for the tuner
|
||||||
|
*/
|
||||||
|
public FC2580TunerConfiguration(String uniqueID)
|
||||||
|
{
|
||||||
|
super(uniqueID, DEFAULT_GAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public TunerType getTunerType()
|
||||||
|
{
|
||||||
|
return TunerType.FCI_FC2580;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
* 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
|
* 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;
|
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;
|
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
|
public abstract class R8xTunerConfiguration extends RTL2832TunerConfiguration
|
||||||
{
|
{
|
||||||
private R8xEmbeddedTuner.MasterGain mMasterMasterGain = R8xEmbeddedTuner.MasterGain.GAIN_327;
|
public static final long MINIMUM_TUNABLE_FREQUENCY_HZ = 3_180_000;
|
||||||
private R8xEmbeddedTuner.MixerGain mMixerGain = R8xEmbeddedTuner.MixerGain.GAIN_105;
|
public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 1_782_030_000;
|
||||||
private R8xEmbeddedTuner.LNAGain mLNAGain = R8xEmbeddedTuner.LNAGain.GAIN_222;
|
public static final double USABLE_BANDWIDTH_PERCENT = 0.98;
|
||||||
private R8xEmbeddedTuner.VGAGain mVGAGain = R8xEmbeddedTuner.VGAGain.GAIN_210;
|
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()
|
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)
|
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;
|
if(mLegacyManualGain && mLegacyLNAGain != null && mLegacyMixerGain != null)
|
||||||
}
|
{
|
||||||
|
setGain(mLegacyLNAGain + mLegacyMixerGain);
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>
|
|
||||||
* ****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.source.tuner.rtl.r8x;
|
|
||||||
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import io.github.dsheirer.source.SourceException;
|
|
||||||
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate;
|
|
||||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.usb4java.LibUsbException;
|
|
||||||
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JSeparator;
|
|
||||||
import javax.swing.JToggleButton;
|
|
||||||
import javax.swing.SpinnerNumberModel;
|
|
||||||
import javax.usb.UsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* R8xxx Tuner Editor
|
|
||||||
*/
|
|
||||||
public class R8xTunerEditor extends TunerEditor<RTL2832Tuner, R8xTunerConfiguration>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(R8xTunerEditor.class);
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private static final R8xEmbeddedTuner.MasterGain DEFAULT_GAIN = R8xEmbeddedTuner.MasterGain.GAIN_279;
|
|
||||||
private JButton mTunerInfoButton;
|
|
||||||
private JToggleButton mBiasTButton;
|
|
||||||
private JComboBox<SampleRate> mSampleRateCombo;
|
|
||||||
private JComboBox<R8xEmbeddedTuner.MasterGain> mMasterGainCombo;
|
|
||||||
private JComboBox<R8xEmbeddedTuner.MixerGain> mMixerGainCombo;
|
|
||||||
private JComboBox<R8xEmbeddedTuner.LNAGain> mLNAGainCombo;
|
|
||||||
private JComboBox<R8xEmbeddedTuner.VGAGain> mVGAGainCombo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance
|
|
||||||
* @param userPreferences for wide-band recordings
|
|
||||||
* @param tunerManager for saving configurations
|
|
||||||
* @param discoveredTuner to edit
|
|
||||||
*/
|
|
||||||
public R8xTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner)
|
|
||||||
{
|
|
||||||
super(userPreferences, tunerManager, discoveredTuner);
|
|
||||||
init();
|
|
||||||
tunerStatusUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMinimumTunableFrequency()
|
|
||||||
{
|
|
||||||
return R8xEmbeddedTuner.MINIMUM_TUNABLE_FREQUENCY_HZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMaximumTunableFrequency()
|
|
||||||
{
|
|
||||||
return R8xEmbeddedTuner.MAXIMUM_TUNABLE_FREQUENCY_HZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access the R8xxx embedded tuner
|
|
||||||
* @return R8xxx tuner if there is a tuner, or null otherwise
|
|
||||||
*/
|
|
||||||
private R8xEmbeddedTuner getEmbeddedTuner()
|
|
||||||
{
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
return (R8xEmbeddedTuner) getTuner().getController().getEmbeddedTuner();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getLogPrefix()
|
|
||||||
{
|
|
||||||
return getEmbeddedTuner().getTunerType().getLabel() + " Tuner Controller - ";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]",
|
|
||||||
"[][][][][][][][][][][][][][][][grow]"));
|
|
||||||
|
|
||||||
add(new JLabel("Tuner:"));
|
|
||||||
add(getTunerIdLabel());
|
|
||||||
add(getTunerInfoButton());
|
|
||||||
|
|
||||||
add(new JLabel("Status:"));
|
|
||||||
add(getTunerStatusLabel());
|
|
||||||
add(getBiasTButton(), "wrap");
|
|
||||||
|
|
||||||
add(getButtonPanel(), "span,align left");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span,growx,push");
|
|
||||||
|
|
||||||
add(new JLabel("Frequency (MHz):"));
|
|
||||||
add(getFrequencyPanel(), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Sample Rate:"));
|
|
||||||
add(getSampleRateCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span,growx,push");
|
|
||||||
add(new JLabel("Gain Control"), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Master:"));
|
|
||||||
add(getMasterGainCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Mixer:"));
|
|
||||||
add(getMixerGainCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("LNA:"));
|
|
||||||
add(getLNAGainCombo(), "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("VGA:"));
|
|
||||||
add(getVGAGainCombo(), "wrap");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tunerStatusUpdated()
|
|
||||||
{
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
getTunerIdLabel().setText(getTuner().getPreferredName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getTunerIdLabel().setText(getDiscoveredTuner().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
String status = getDiscoveredTuner().getTunerStatus().toString();
|
|
||||||
if(getDiscoveredTuner().hasErrorMessage())
|
|
||||||
{
|
|
||||||
status += " - " + getDiscoveredTuner().getErrorMessage();
|
|
||||||
}
|
|
||||||
getTunerStatusLabel().setText(status);
|
|
||||||
getButtonPanel().updateControls();
|
|
||||||
getFrequencyPanel().updateControls();
|
|
||||||
|
|
||||||
if(hasTuner())
|
|
||||||
{
|
|
||||||
getBiasTButton().setEnabled(true);
|
|
||||||
getBiasTButton().setSelected(getConfiguration().isBiasT());
|
|
||||||
getTunerInfoButton().setEnabled(true);
|
|
||||||
getSampleRateCombo().setEnabled(true);
|
|
||||||
getSampleRateCombo().setSelectedItem(getConfiguration().getSampleRate());
|
|
||||||
getMasterGainCombo().setEnabled(true);
|
|
||||||
R8xEmbeddedTuner.MasterGain gain = getConfiguration().getMasterGain();
|
|
||||||
getMasterGainCombo().setEnabled(true);
|
|
||||||
getMasterGainCombo().setSelectedItem(gain);
|
|
||||||
|
|
||||||
if(gain == R8xEmbeddedTuner.MasterGain.MANUAL)
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setSelectedItem(getConfiguration().getMixerGain());
|
|
||||||
getMixerGainCombo().setEnabled(true);
|
|
||||||
|
|
||||||
getLNAGainCombo().setSelectedItem(getConfiguration().getLNAGain());
|
|
||||||
getLNAGainCombo().setEnabled(true);
|
|
||||||
|
|
||||||
getVGAGainCombo().setSelectedItem(getConfiguration().getVGAGain());
|
|
||||||
getVGAGainCombo().setEnabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setEnabled(false);
|
|
||||||
getMixerGainCombo().setSelectedItem(gain.getMixerGain());
|
|
||||||
|
|
||||||
getLNAGainCombo().setEnabled(false);
|
|
||||||
getLNAGainCombo().setSelectedItem(gain.getLNAGain());
|
|
||||||
|
|
||||||
getVGAGainCombo().setEnabled(false);
|
|
||||||
getVGAGainCombo().setSelectedItem(gain.getVGAGain());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getBiasTButton().setEnabled(false);
|
|
||||||
getBiasTButton().setSelected(false);
|
|
||||||
getTunerInfoButton().setEnabled(false);
|
|
||||||
getSampleRateCombo().setEnabled(false);
|
|
||||||
getMasterGainCombo().setEnabled(false);
|
|
||||||
getMixerGainCombo().setEnabled(false);
|
|
||||||
getLNAGainCombo().setEnabled(false);
|
|
||||||
getVGAGainCombo().setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSampleRateToolTip();
|
|
||||||
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bias-T toggle button
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private JToggleButton getBiasTButton()
|
|
||||||
{
|
|
||||||
if(mBiasTButton == null)
|
|
||||||
{
|
|
||||||
mBiasTButton = new JToggleButton("Bias-T");
|
|
||||||
mBiasTButton.setEnabled(false);
|
|
||||||
mBiasTButton.addActionListener(e -> {
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
getTuner().getController().setBiasT(mBiasTButton.isSelected());
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return mBiasTButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hyperlink button that provides tuner information
|
|
||||||
*/
|
|
||||||
private JButton getTunerInfoButton()
|
|
||||||
{
|
|
||||||
if(mTunerInfoButton == null)
|
|
||||||
{
|
|
||||||
mTunerInfoButton = new JButton("Info");
|
|
||||||
mTunerInfoButton.setEnabled(false);
|
|
||||||
mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(R8xTunerEditor.this,
|
|
||||||
getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE));
|
|
||||||
}
|
|
||||||
|
|
||||||
return mTunerInfoButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getVGAGainCombo()
|
|
||||||
{
|
|
||||||
if(mVGAGainCombo == null)
|
|
||||||
{
|
|
||||||
mVGAGainCombo = new JComboBox<>(R8xEmbeddedTuner.VGAGain.values());
|
|
||||||
mVGAGainCombo.setEnabled(false);
|
|
||||||
mVGAGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
R8xEmbeddedTuner.VGAGain vgaGain = (R8xEmbeddedTuner.VGAGain) mVGAGainCombo.getSelectedItem();
|
|
||||||
|
|
||||||
if(vgaGain == null)
|
|
||||||
{
|
|
||||||
vgaGain = DEFAULT_GAIN.getVGAGain();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mVGAGainCombo.isEnabled())
|
|
||||||
{
|
|
||||||
getEmbeddedTuner().setVGAGain(vgaGain, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(R8xTunerEditor.this, getLogPrefix() +
|
|
||||||
"couldn't apply the VGA gain setting - " + e.getLocalizedMessage());
|
|
||||||
mLog.error(getLogPrefix() + "couldn't apply VGA gain setting", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mVGAGainCombo.setToolTipText("<html>VGA Gain. Set master gain to <b>MANUAL</b> to enable adjustment</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
return mVGAGainCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getLNAGainCombo()
|
|
||||||
{
|
|
||||||
if(mLNAGainCombo == null)
|
|
||||||
{
|
|
||||||
mLNAGainCombo = new JComboBox<>(R8xEmbeddedTuner.LNAGain.values());
|
|
||||||
mLNAGainCombo.setEnabled(false);
|
|
||||||
mLNAGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
R8xEmbeddedTuner.LNAGain lnaGain = (R8xEmbeddedTuner.LNAGain) mLNAGainCombo.getSelectedItem();
|
|
||||||
|
|
||||||
if(lnaGain == null)
|
|
||||||
{
|
|
||||||
lnaGain = DEFAULT_GAIN.getLNAGain();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mLNAGainCombo.isEnabled())
|
|
||||||
{
|
|
||||||
getEmbeddedTuner().setLNAGain(lnaGain, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(R8xTunerEditor.this, getLogPrefix() +
|
|
||||||
"couldn't apply the LNA gain setting - " + e.getLocalizedMessage());
|
|
||||||
mLog.error(getLogPrefix() + "couldn't apply LNA " + "gain setting - ", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mLNAGainCombo.setToolTipText("<html>LNA Gain. Set master gain to <b>MANUAL</b> to enable adjustment</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
return mLNAGainCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(R8xTunerEditor.this,
|
|
||||||
getLogPrefix() + "couldn't apply the sample rate setting [" +
|
|
||||||
sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage());
|
|
||||||
|
|
||||||
mLog.error(getLogPrefix() + "couldn't apply sample rate setting [" + sampleRate.getLabel() +
|
|
||||||
"]", eSampleRate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return mSampleRateCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getMixerGainCombo()
|
|
||||||
{
|
|
||||||
if(mMixerGainCombo == null)
|
|
||||||
{
|
|
||||||
mMixerGainCombo = new JComboBox<>(R8xEmbeddedTuner.MixerGain.values());
|
|
||||||
mMixerGainCombo.setEnabled(false);
|
|
||||||
mMixerGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
R8xEmbeddedTuner.MixerGain mixerGain = (R8xEmbeddedTuner.MixerGain) mMixerGainCombo.getSelectedItem();
|
|
||||||
|
|
||||||
if(mixerGain == null)
|
|
||||||
{
|
|
||||||
mixerGain = DEFAULT_GAIN.getMixerGain();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mMixerGainCombo.isEnabled())
|
|
||||||
{
|
|
||||||
getEmbeddedTuner().setMixerGain(mixerGain, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(R8xTunerEditor.this, getLogPrefix() +
|
|
||||||
"couldn't apply the mixer gain setting - " + e.getLocalizedMessage());
|
|
||||||
|
|
||||||
mLog.error(getLogPrefix() + "couldn't apply mixer gain setting - ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mMixerGainCombo.setToolTipText("<html>Mixer Gain. Set master gain to <b>MANUAL</b> to enable adjustment</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
return mMixerGainCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JComboBox getMasterGainCombo()
|
|
||||||
{
|
|
||||||
if(mMasterGainCombo == null)
|
|
||||||
{
|
|
||||||
mMasterGainCombo = new JComboBox<>(R8xEmbeddedTuner.MasterGain.values());
|
|
||||||
mMasterGainCombo.setEnabled(false);
|
|
||||||
mMasterGainCombo.addActionListener(arg0 ->
|
|
||||||
{
|
|
||||||
if(!isLoading())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
R8xEmbeddedTuner.MasterGain gain = (R8xEmbeddedTuner.MasterGain)getMasterGainCombo().getSelectedItem();
|
|
||||||
getEmbeddedTuner().setGain((R8xEmbeddedTuner.MasterGain)getMasterGainCombo().getSelectedItem(), true);
|
|
||||||
|
|
||||||
if(gain == R8xEmbeddedTuner.MasterGain.MANUAL)
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setSelectedItem(gain.getMixerGain());
|
|
||||||
getMixerGainCombo().setEnabled(true);
|
|
||||||
|
|
||||||
getLNAGainCombo().setSelectedItem(gain.getLNAGain());
|
|
||||||
getLNAGainCombo().setEnabled(true);
|
|
||||||
|
|
||||||
getVGAGainCombo().setSelectedItem(gain.getVGAGain());
|
|
||||||
getVGAGainCombo().setEnabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getMixerGainCombo().setEnabled(false);
|
|
||||||
getMixerGainCombo().setSelectedItem(gain.getMixerGain());
|
|
||||||
|
|
||||||
getLNAGainCombo().setEnabled(false);
|
|
||||||
getLNAGainCombo().setSelectedItem(gain.getLNAGain());
|
|
||||||
|
|
||||||
getVGAGainCombo().setEnabled(false);
|
|
||||||
getVGAGainCombo().setSelectedItem(gain.getVGAGain());
|
|
||||||
}
|
|
||||||
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(R8xTunerEditor.this, getLogPrefix() +
|
|
||||||
"couldn't apply the gain setting - " + e.getLocalizedMessage());
|
|
||||||
mLog.error(getLogPrefix() + "couldn't apply gain setting - ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mMasterGainCombo.setToolTipText("<html>Select <b>AUTOMATIC</b> for auto gain, <b>MANUAL</b> to enable<br> " +
|
|
||||||
"independent control of <i>Mixer</i>, <i>LNA</i> and <i>Enhance</i> gain<br>settings, or one of the " +
|
|
||||||
"individual gain settings for<br>semi-manual gain control</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
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("<html><h3>RTL-2832 with " + getEmbeddedTuner().getTunerType().getLabel() + " Tuner</h3>");
|
|
||||||
|
|
||||||
if(descriptor == null)
|
|
||||||
{
|
|
||||||
sb.append("No EEPROM Descriptor Available");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.append("<b>USB ID: </b>");
|
|
||||||
sb.append(descriptor.getVendorID());
|
|
||||||
sb.append(":");
|
|
||||||
sb.append(descriptor.getProductID());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Vendor: </b>");
|
|
||||||
sb.append(descriptor.getVendorLabel());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Product: </b>");
|
|
||||||
sb.append(descriptor.getProductLabel());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Serial: </b>");
|
|
||||||
sb.append(descriptor.getSerial());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>IR Enabled: </b>");
|
|
||||||
sb.append(descriptor.irEnabled());
|
|
||||||
sb.append("<br>");
|
|
||||||
|
|
||||||
sb.append("<b>Remote Wake: </b>");
|
|
||||||
sb.append(descriptor.remoteWakeupEnabled());
|
|
||||||
sb.append("<br>");
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 <http://www.gnu.org/licenses/>
|
|
||||||
* ****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.source.tuner.rtl.r8x.r820t;
|
|
||||||
|
|
||||||
import io.github.dsheirer.source.SourceException;
|
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.R8xEmbeddedTuner;
|
|
||||||
|
|
||||||
import javax.usb.UsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rafael Micro R820T and R820T2 Embedded Tuner implementation
|
|
||||||
*/
|
|
||||||
public class R820TEmbeddedTuner extends R8xEmbeddedTuner
|
|
||||||
{
|
|
||||||
private static final byte I2C_WRITE_ADDRESS = (byte) 0x34;
|
|
||||||
private static final byte I2C_READ_ADDRESS = (byte) 0x35;
|
|
||||||
private static final int VCO_POWER_REF = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance
|
|
||||||
* @param adapter for accessing RTL2832USBController interfaces
|
|
||||||
*/
|
|
||||||
public R820TEmbeddedTuner(RTL2832TunerController.ControllerAdapter adapter)
|
|
||||||
{
|
|
||||||
super(adapter, VCO_POWER_REF);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TunerType getTunerType()
|
|
||||||
{
|
|
||||||
return TunerType.RAFAELMICRO_R820T;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getI2CWriteAddress()
|
|
||||||
{
|
|
||||||
return I2C_WRITE_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getI2CReadAddress()
|
|
||||||
{
|
|
||||||
return I2C_READ_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the center frequency. Setting the frequency is a two-part process
|
|
||||||
* of setting the multiplexer and then setting the Oscillator (PLL).
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public synchronized void setTunedFrequency(long frequency) throws SourceException
|
|
||||||
{
|
|
||||||
getAdapter().getLock().lock();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
getAdapter().enableI2CRepeater();
|
|
||||||
boolean controlI2C = false;
|
|
||||||
long offsetFrequency = frequency + IF_FREQUENCY;
|
|
||||||
setMux(offsetFrequency, controlI2C);
|
|
||||||
setPLL(offsetFrequency, controlI2C);
|
|
||||||
getAdapter().disableI2CRepeater();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
throw new SourceException("R820TTunerController - exception while setting frequency [" + frequency + "] - " +
|
|
||||||
e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
getAdapter().getLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,134 +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 <http://www.gnu.org/licenses/>
|
|
||||||
* ****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.source.tuner.rtl.r8x.r828d;
|
|
||||||
|
|
||||||
import io.github.dsheirer.source.SourceException;
|
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController;
|
|
||||||
import io.github.dsheirer.source.tuner.rtl.r8x.R8xEmbeddedTuner;
|
|
||||||
|
|
||||||
import javax.usb.UsbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rafael Micro R828D Embedded Tuner implementation
|
|
||||||
*/
|
|
||||||
public class R828DEmbeddedTuner extends R8xEmbeddedTuner
|
|
||||||
{
|
|
||||||
private static final byte I2C_WRITE_ADDRESS = (byte) 0x74;
|
|
||||||
private static final byte I2C_READ_ADDRESS = (byte) 0x75;
|
|
||||||
private static final int VCO_POWER_REF = 1;
|
|
||||||
/**
|
|
||||||
* RTL-SDR.com blog V4 dongle indicator.
|
|
||||||
*/
|
|
||||||
private final boolean mIsV4Dongle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance
|
|
||||||
* @param adapter for accessing RTL2832USBController interfaces
|
|
||||||
*/
|
|
||||||
public R828DEmbeddedTuner(RTL2832TunerController.ControllerAdapter adapter)
|
|
||||||
{
|
|
||||||
super(adapter, VCO_POWER_REF);
|
|
||||||
|
|
||||||
//Set flag to indicate if this is a V4 dongle that supports notch filtering.
|
|
||||||
mIsV4Dongle = adapter.isV4Dongle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TunerType getTunerType()
|
|
||||||
{
|
|
||||||
return TunerType.RAFAELMICRO_R828D;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getI2CWriteAddress()
|
|
||||||
{
|
|
||||||
return I2C_WRITE_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getI2CReadAddress()
|
|
||||||
{
|
|
||||||
return I2C_READ_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the center frequency. Setting the frequency is a two-part process
|
|
||||||
* of setting the multiplexer and then setting the Oscillator (PLL).
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public synchronized void setTunedFrequency(long frequency) throws SourceException
|
|
||||||
{
|
|
||||||
getAdapter().getLock().lock();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
getAdapter().enableI2CRepeater();
|
|
||||||
boolean controlI2C = false;
|
|
||||||
long offsetFrequency = frequency + IF_FREQUENCY;
|
|
||||||
setMux(offsetFrequency, controlI2C);
|
|
||||||
setPLL(offsetFrequency, controlI2C);
|
|
||||||
|
|
||||||
//Select the RF input
|
|
||||||
if(mIsV4Dongle)
|
|
||||||
{
|
|
||||||
//Disable notch filtering if frequency falls into a notched band, otherwise enable it.
|
|
||||||
boolean isNotchFilterFrequency = (frequency < 2_200_000) ||
|
|
||||||
(85_000_000 < frequency && frequency < 112_000_000) ||
|
|
||||||
(172_000_000 < frequency && frequency < 242_000_000);
|
|
||||||
writeRegister(Register.DRAIN, isNotchFilterFrequency ? (byte)0x00 : (byte)0x08, controlI2C);
|
|
||||||
|
|
||||||
if(frequency <= 28_800_000) //Use cable 2 input
|
|
||||||
{
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_AIR, (byte)0x20, controlI2C); //disabled
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_CABLE_1, (byte)0x00, controlI2C); //disabled
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_CABLE_2, (byte)0x08, controlI2C); //enabled
|
|
||||||
}
|
|
||||||
else if(frequency < 250_000_000)
|
|
||||||
{
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_AIR, (byte)0x20, controlI2C); //disabled
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_CABLE_1, (byte)0x40, controlI2C); //enabled
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_CABLE_2, (byte)0x00, controlI2C); //disabled
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_AIR, (byte)0x00, controlI2C); //enabled
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_CABLE_1, (byte)0x00, controlI2C); //disabled
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_CABLE_2, (byte)0x00, controlI2C); //disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
byte air_cable_in = (frequency > 345_000_000) ? (byte)0x00 : (byte)0x60;
|
|
||||||
writeRegister(Register.INPUT_SELECTOR_AIR_AND_CABLE_1, air_cable_in, controlI2C);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAdapter().disableI2CRepeater();
|
|
||||||
}
|
|
||||||
catch(UsbException e)
|
|
||||||
{
|
|
||||||
throw new SourceException("R828DTunerController - exception while setting frequency [" + frequency + "] - " +
|
|
||||||
e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
getAdapter().getLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -90,3 +90,47 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d395", MODE:="066
|
|||||||
# SVEON STV20 DVB-T USB & FM (FC0012)
|
# SVEON STV20 DVB-T USB & FM (FC0012)
|
||||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d39d", MODE:="0666"
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d39d", MODE:="0666"
|
||||||
|
|
||||||
|
# DigitalNow Quad DVB-T PCI-E card (4x FC0012?)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0413", ATTRS{idProduct}=="6680", MODE:="0666"
|
||||||
|
|
||||||
|
# Leadtek WinFast DTV Dongle mini D (FC0012)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0413", ATTRS{idProduct}=="6f0f", MODE:="0666"
|
||||||
|
|
||||||
|
# Terratec NOXON DAB Stick - Radio Energy (FC0013)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b5", MODE:="0666"
|
||||||
|
|
||||||
|
# Terratec BR DAB Stick (FC0013)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b8", MODE:="0666"
|
||||||
|
|
||||||
|
# Terratec WDR DAB Stick (FC0013)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b9", MODE:="0666"
|
||||||
|
|
||||||
|
# Terratec MuellerVerlag DAB Stick (FC0013)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00c0", MODE:="0666"
|
||||||
|
|
||||||
|
# PixelView PV-DT235U(RN) (FC0012)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1554", ATTRS{idProduct}=="5020", MODE:="0666"
|
||||||
|
|
||||||
|
# Astrometa DVB-T/DVB-T2 (R828D)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0131", MODE:="0666"
|
||||||
|
|
||||||
|
# HanfTek DAB+FM+DVB-T
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0133", MODE:="0666"
|
||||||
|
|
||||||
|
# KWorld KW-UB450-T USB DVB-T Pico TV (TUA9001)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d397", MODE:="0666"
|
||||||
|
|
||||||
|
# ASUS U3100MINI_PLUS_V2 (FC0013)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3a8", MODE:="0666"
|
||||||
|
|
||||||
|
# SVEON STV27 DVB-T USB & FM (FC0013)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3af", MODE:="0666"
|
||||||
|
|
||||||
|
# SVEON STV21 DVB-T USB & FM
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3b0", MODE:="0666"
|
||||||
|
|
||||||
|
# MSI DigiVox Micro HD (FC2580)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1104", MODE:="0666"
|
||||||
|
|
||||||
|
# Sweex DVB-T USB (FC0012)
|
||||||
|
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="a803", MODE:="0666"
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* 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 <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.source.tuner.rtl;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.github.dsheirer.source.tuner.TunerFactory;
|
||||||
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.fc0012.FC0012TunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.fc2580.FC2580TunerConfiguration;
|
||||||
|
import io.github.dsheirer.source.tuner.rtl.r8x.r820t.R820TTunerConfiguration;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests conversion of tuner configurations saved by the previous RTL-2832 driver and the E4000 IF gain distribution.
|
||||||
|
*/
|
||||||
|
public class RTL2832TunerConfigurationTest
|
||||||
|
{
|
||||||
|
//Same settings as TunerConfigurationManager
|
||||||
|
private final ObjectMapper mObjectMapper = new ObjectMapper()
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
|
||||||
|
private RTL2832TunerConfiguration read(String json) throws Exception
|
||||||
|
{
|
||||||
|
return (RTL2832TunerConfiguration)mObjectMapper.readValue(json, TunerConfiguration.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void legacyR8xMasterGain() throws Exception
|
||||||
|
{
|
||||||
|
RTL2832TunerConfiguration config = read("{\"type\":\"r820TTunerConfiguration\",\"masterGain\":\"GAIN_296\"," +
|
||||||
|
"\"mixerGain\":\"GAIN_105\",\"lnagain\":\"GAIN_222\",\"vgagain\":\"GAIN_210\",\"sampleRate\":\"RATE_2_048MHZ\"," +
|
||||||
|
"\"biasT\":true,\"uniqueID\":\"RTL2832/R820T 00000001\",\"frequency\":101100000}");
|
||||||
|
assertInstanceOf(R820TTunerConfiguration.class, config);
|
||||||
|
assertEquals(296, config.getGain());
|
||||||
|
assertFalse(config.isAGC());
|
||||||
|
assertTrue(config.isBiasT());
|
||||||
|
assertEquals(RTL2832TunerController.SampleRate.RATE_2_048MHZ, config.getSampleRate());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void legacyR8xManualAndAutomaticGain() throws Exception
|
||||||
|
{
|
||||||
|
//Stage gains before the master gain, to check that property order doesn't matter
|
||||||
|
RTL2832TunerConfiguration manual = read("{\"type\":\"r828DTunerConfiguration\",\"lnagain\":\"GAIN_222\"," +
|
||||||
|
"\"mixerGain\":\"GAIN_105\",\"masterGain\":\"MANUAL\",\"uniqueID\":\"x\"}");
|
||||||
|
assertEquals(327, manual.getGain());
|
||||||
|
assertFalse(manual.isAGC());
|
||||||
|
|
||||||
|
RTL2832TunerConfiguration automatic = read("{\"type\":\"r820TTunerConfiguration\",\"masterGain\":\"AUTOMATIC\"," +
|
||||||
|
"\"uniqueID\":\"x\"}");
|
||||||
|
assertTrue(automatic.isAGC());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void legacyE4K() throws Exception
|
||||||
|
{
|
||||||
|
RTL2832TunerConfiguration manual = read("{\"type\":\"e4KTunerConfiguration\",\"masterGain\":\"MANUAL\"," +
|
||||||
|
"\"mixerGain\":\"GAIN_12\",\"lnagain\":\"GAIN_PLUS_200\",\"ifgain\":\"SENSITIVITY_30\",\"uniqueID\":\"x\"}");
|
||||||
|
assertEquals(320, manual.getGain());
|
||||||
|
assertEquals(30, ((E4KTunerConfiguration)manual).getIFGainDb());
|
||||||
|
|
||||||
|
//Legacy IF gains above the saturation limit are capped
|
||||||
|
TunerConfiguration high = read("{\"type\":\"e4KTunerConfiguration\",\"ifgain\":\"LINEARITY_60\",\"uniqueID\":\"x\"}");
|
||||||
|
assertEquals(RTL2832TunerController.E4K_IF_GAIN_MAXIMUM, ((E4KTunerConfiguration)high).getIFGainDb());
|
||||||
|
|
||||||
|
RTL2832TunerConfiguration minus = read("{\"type\":\"e4KTunerConfiguration\",\"masterGain\":\"MINUS_10\"," +
|
||||||
|
"\"uniqueID\":\"x\"}");
|
||||||
|
assertEquals(-10, minus.getGain());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void legacyFC0013() throws Exception
|
||||||
|
{
|
||||||
|
RTL2832TunerConfiguration config = read("{\"type\":\"fc0013TunerConfiguration\",\"lnaGain\":\"G23\"," +
|
||||||
|
"\"agc\":true,\"uniqueID\":\"x\"}");
|
||||||
|
assertInstanceOf(FC0013TunerConfiguration.class, config);
|
||||||
|
assertEquals(197, config.getGain());
|
||||||
|
assertTrue(config.isAGC());
|
||||||
|
|
||||||
|
//Legacy settings map by LNA register value: G03 and G04 are both -6.3 dB
|
||||||
|
int[] expected = {-99, -73, -65, -63, -63, -60, -58, -54, 58};
|
||||||
|
|
||||||
|
for(int x = 0; x < expected.length; x++)
|
||||||
|
{
|
||||||
|
RTL2832TunerConfiguration legacy = read("{\"type\":\"fc0013TunerConfiguration\",\"lnaGain\":\"G0" + x +
|
||||||
|
"\",\"uniqueID\":\"x\"}");
|
||||||
|
assertEquals(expected[x], legacy.getGain(), "G0" + x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void roundTrip() throws Exception
|
||||||
|
{
|
||||||
|
E4KTunerConfiguration config = new E4KTunerConfiguration("RTL2832/E4000 1");
|
||||||
|
config.setGain(340);
|
||||||
|
config.setAGC(true);
|
||||||
|
config.setIFGainDb(41);
|
||||||
|
String json = mObjectMapper.writeValueAsString(config);
|
||||||
|
E4KTunerConfiguration copy = (E4KTunerConfiguration)read(json);
|
||||||
|
assertEquals(340, copy.getGain());
|
||||||
|
assertTrue(copy.isAGC());
|
||||||
|
assertEquals(41, copy.getIFGainDb());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void e4kIFStageGains()
|
||||||
|
{
|
||||||
|
for(int gain = RTL2832TunerController.E4K_IF_GAIN_MINIMUM; gain <= RTL2832TunerController.E4K_IF_GAIN_MAXIMUM; gain++)
|
||||||
|
{
|
||||||
|
int[] stages = RTL2832TunerController.getE4KIFStageGains(gain);
|
||||||
|
assertEquals(gain, Arrays.stream(stages).sum(), "Total IF gain " + gain);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertArrayEquals(new int[]{-3, 0, 0, 0, 3, 3}, RTL2832TunerController.getE4KIFStageGains(0));
|
||||||
|
assertArrayEquals(new int[]{6, 9, 9, 2, 15, 15}, RTL2832TunerController.getE4KIFStageGains(99));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fc0012AndFC2580RoundTrip() throws Exception
|
||||||
|
{
|
||||||
|
TunerConfiguration fc0012 = TunerFactory.getTunerConfiguration(TunerType.FITIPOWER_FC0012, "RTL-2832/FC0012 1");
|
||||||
|
assertInstanceOf(FC0012TunerConfiguration.class, fc0012);
|
||||||
|
assertEquals(FC0012TunerConfiguration.DEFAULT_GAIN, ((RTL2832TunerConfiguration)fc0012).getGain());
|
||||||
|
((RTL2832TunerConfiguration)fc0012).setGain(71);
|
||||||
|
RTL2832TunerConfiguration fc0012Copy = read(mObjectMapper.writeValueAsString(fc0012));
|
||||||
|
assertInstanceOf(FC0012TunerConfiguration.class, fc0012Copy);
|
||||||
|
assertEquals(71, fc0012Copy.getGain());
|
||||||
|
|
||||||
|
TunerConfiguration fc2580 = TunerFactory.getTunerConfiguration(TunerType.FCI_FC2580, "RTL-2832/FC2580 1");
|
||||||
|
assertInstanceOf(FC2580TunerConfiguration.class, fc2580);
|
||||||
|
RTL2832TunerConfiguration fc2580Copy = read(mObjectMapper.writeValueAsString(fc2580));
|
||||||
|
assertInstanceOf(FC2580TunerConfiguration.class, fc2580Copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user