mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 17:47:32 -05:00
#1340 Updates tuner and frequency controllers to apply locking to ensure multi-threaded access to freuqency control planes doesn't allow center tuned frequency to become indeterminant which can result in channels being mistuned and stop decoding. (#1341)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -4,7 +4,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="liberica-19.0.1.fx" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="liberica-19.0.1.fx" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -197,7 +197,7 @@ public class SourceEvent
|
|||||||
/**
|
/**
|
||||||
* Creates a new locked state change event
|
* Creates a new locked state change event
|
||||||
*/
|
*/
|
||||||
public static SourceEvent lockedState()
|
public static SourceEvent lockedSampleRateState()
|
||||||
{
|
{
|
||||||
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED, 1);
|
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED, 1);
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ public class SourceEvent
|
|||||||
/**
|
/**
|
||||||
* Creates a new unlocked state change event
|
* Creates a new unlocked state change event
|
||||||
*/
|
*/
|
||||||
public static SourceEvent unlockedState()
|
public static SourceEvent unlockedSampleRateState()
|
||||||
{
|
{
|
||||||
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED, 0);
|
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
|||||||
import io.github.dsheirer.source.tuner.frequency.FrequencyController;
|
import io.github.dsheirer.source.tuner.frequency.FrequencyController;
|
||||||
import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable;
|
import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable;
|
||||||
import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager;
|
import io.github.dsheirer.source.tuner.manager.FrequencyErrorCorrectionManager;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener,
|
public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener,
|
||||||
INativeBufferProvider, Listener<INativeBuffer>, ITunerErrorListener
|
INativeBufferProvider, Listener<INativeBuffer>, ITunerErrorListener
|
||||||
@@ -70,6 +70,17 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
mFrequencyErrorCorrectionManager = new FrequencyErrorCorrectionManager(this);
|
mFrequencyErrorCorrectionManager = new FrequencyErrorCorrectionManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock for the frequency controller. This should only be used by the channel source manager to lock access to the
|
||||||
|
* frequency controller while creating a channel source, to block multi-threaded access to the frequency controller
|
||||||
|
* which might put the center tuned frequency value in an indeterminant state.
|
||||||
|
* @return frequency controller lock
|
||||||
|
*/
|
||||||
|
public ReentrantLock getFrequencyControllerLock()
|
||||||
|
{
|
||||||
|
return mFrequencyController.getFrequencyControllerLock();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frequency correction manager for this tuner controller.
|
* Frequency correction manager for this tuner controller.
|
||||||
*/
|
*/
|
||||||
@@ -182,29 +193,30 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the frequency and sample rate controls are locked by another process. User interface controls
|
* Indicates if the sample rate control is locked by another process. User interface controls should monitor source
|
||||||
* should monitor source events and check the locked state via this method to correctly render the enabled state
|
* events and check the locked state via this method to correctly render the enabled state of the sample rate control.
|
||||||
* of the frequency and sample rate controls.
|
|
||||||
*
|
*
|
||||||
* @return true if the tuner controller is locked.
|
* @return true if the tuner controller is locked.
|
||||||
*/
|
*/
|
||||||
public boolean isLocked()
|
public boolean isLockedSampleRate()
|
||||||
{
|
{
|
||||||
return mFrequencyController.isLocked();
|
//Note: this access is not protected by the mFrequencyControllerLock
|
||||||
|
return mFrequencyController.isSampleRateLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the frequency and sample rate locked state to the locked argument value. This should only be changed
|
* Sets the sample rate locked state to the locked argument value. This should only be changed by a downstream
|
||||||
* by a downstream consumer of samples to prevent users or other processes from modifying the center frequency
|
* consumer of samples to prevent users or other processes from modifying the sample rate of the tuner while
|
||||||
* and/or sample rate of the tuner while processing samples.
|
* processing samples.
|
||||||
*
|
*
|
||||||
* @param locked true to indicate the tuner controller is in a locked state, otherwise false.
|
* @param locked true to indicate the tuner controller is in a locked state, otherwise false.
|
||||||
*/
|
*/
|
||||||
public void setLocked(boolean locked)
|
public void setLockedSampleRate(boolean locked)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mFrequencyController.setLocked(locked);
|
//Note: this access is not protected by the mFrequencyControllerLock
|
||||||
|
mFrequencyController.setSampleRateLocked(locked);
|
||||||
}
|
}
|
||||||
catch(SourceException se)
|
catch(SourceException se)
|
||||||
{
|
{
|
||||||
@@ -214,6 +226,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
|
|
||||||
public int getBandwidth()
|
public int getBandwidth()
|
||||||
{
|
{
|
||||||
|
//Note: this access is not protected by the mFrequencyControllerLock
|
||||||
return mFrequencyController.getBandwidth();
|
return mFrequencyController.getBandwidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +238,15 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public void setFrequency(long frequency) throws SourceException
|
public void setFrequency(long frequency) throws SourceException
|
||||||
{
|
{
|
||||||
mFrequencyController.setFrequency(frequency);
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
mFrequencyController.setFrequency(frequency);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -235,28 +256,73 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public long getFrequency()
|
public long getFrequency()
|
||||||
{
|
{
|
||||||
return mFrequencyController.getFrequency();
|
long frequency;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
frequency = mFrequencyController.getFrequency();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTune(long frequency)
|
public boolean canTune(long frequency)
|
||||||
{
|
{
|
||||||
return mFrequencyController.canTune(frequency);
|
boolean canTune;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
canTune = mFrequencyController.canTune(frequency);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return canTune;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getSampleRate()
|
public double getSampleRate()
|
||||||
{
|
{
|
||||||
|
//Note: this access is not protected by the mFrequencyControllerLock
|
||||||
return mFrequencyController.getSampleRate();
|
return mFrequencyController.getSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getFrequencyCorrection()
|
public double getFrequencyCorrection()
|
||||||
{
|
{
|
||||||
return mFrequencyController.getFrequencyCorrection();
|
double correction;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
correction = mFrequencyController.getFrequencyCorrection();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return correction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFrequencyCorrection(double correction) throws SourceException
|
public void setFrequencyCorrection(double correction) throws SourceException
|
||||||
{
|
{
|
||||||
mFrequencyController.setFrequencyCorrection(correction);
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
mFrequencyController.setFrequencyCorrection(correction);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,7 +331,19 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public long getMinimumFrequency()
|
public long getMinimumFrequency()
|
||||||
{
|
{
|
||||||
return mFrequencyController.getMinimumFrequency();
|
long minimum;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
minimum = mFrequencyController.getMinimumFrequency();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return minimum;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -274,7 +352,15 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public void setMinimumFrequency(long minimum)
|
public void setMinimumFrequency(long minimum)
|
||||||
{
|
{
|
||||||
mFrequencyController.setMinimumFrequency(minimum);
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
mFrequencyController.setMinimumFrequency(minimum);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -283,7 +369,19 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public long getMaximumFrequency()
|
public long getMaximumFrequency()
|
||||||
{
|
{
|
||||||
return mFrequencyController.getMaximumFrequency();
|
long maximum;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
maximum = mFrequencyController.getMaximumFrequency();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return maximum;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -292,17 +390,49 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public void setMaximumFrequency(long maximum)
|
public void setMaximumFrequency(long maximum)
|
||||||
{
|
{
|
||||||
mFrequencyController.setMaximumFrequency(maximum);
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
mFrequencyController.setMaximumFrequency(maximum);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMinTunedFrequency() throws SourceException
|
public long getMinTunedFrequency() throws SourceException
|
||||||
{
|
{
|
||||||
return mFrequencyController.getFrequency() - (getUsableBandwidth() / 2);
|
long minTuned;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
minTuned = mFrequencyController.getFrequency() - (getUsableBandwidth() / 2);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return minTuned;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxTunedFrequency() throws SourceException
|
public long getMaxTunedFrequency() throws SourceException
|
||||||
{
|
{
|
||||||
return mFrequencyController.getFrequency() + (getUsableBandwidth() / 2);
|
long maxTuned;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().lock();
|
||||||
|
maxTuned = mFrequencyController.getFrequency() + (getUsableBandwidth() / 2);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxTuned;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -425,7 +555,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public void addListener( ISourceEventProcessor processor )
|
public void addListener( ISourceEventProcessor processor )
|
||||||
{
|
{
|
||||||
mFrequencyController.addListener(processor);
|
mFrequencyController.addSourceEventProcessor(processor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -433,7 +563,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
|||||||
*/
|
*/
|
||||||
public void removeListener( ISourceEventProcessor processor )
|
public void removeListener( ISourceEventProcessor processor )
|
||||||
{
|
{
|
||||||
mFrequencyController.removeFrequencyChangeProcessor(processor);
|
mFrequencyController.removeSourceEventProcessor(processor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ 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.manager.TunerStatus;
|
import io.github.dsheirer.source.tuner.manager.TunerStatus;
|
||||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.util.List;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -40,9 +43,6 @@ import javax.swing.JSlider;
|
|||||||
import javax.swing.SpinnerNumberModel;
|
import javax.swing.SpinnerNumberModel;
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Airspy tuner editor/controller
|
* Airspy tuner editor/controller
|
||||||
@@ -102,7 +102,7 @@ public class AirspyTunerEditor extends TunerEditor<AirspyTuner, AirspyTunerConfi
|
|||||||
getTunerStatusLabel().setText(status);
|
getTunerStatusLabel().setText(status);
|
||||||
getButtonPanel().updateControls();
|
getButtonPanel().updateControls();
|
||||||
getFrequencyPanel().updateControls();
|
getFrequencyPanel().updateControls();
|
||||||
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked());
|
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
|
||||||
getTunerInfoButton().setEnabled(hasTuner());
|
getTunerInfoButton().setEnabled(hasTuner());
|
||||||
updateGainComponents((hasTuner() && hasConfiguration()) ? getConfiguration().getGain() : null);
|
updateGainComponents((hasTuner() && hasConfiguration()) ? getConfiguration().getGain() : null);
|
||||||
|
|
||||||
@@ -626,7 +626,7 @@ public class AirspyTunerEditor extends TunerEditor<AirspyTuner, AirspyTunerConfi
|
|||||||
*/
|
*/
|
||||||
private void updateSampleRateToolTip()
|
private void updateSampleRateToolTip()
|
||||||
{
|
{
|
||||||
if(hasTuner() && getTuner().getController().isLocked())
|
if(hasTuner() && getTuner().getController().isLockedSampleRate())
|
||||||
{
|
{
|
||||||
mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,11 @@ import io.github.dsheirer.source.ISourceEventProcessor;
|
|||||||
import io.github.dsheirer.source.InvalidFrequencyException;
|
import io.github.dsheirer.source.InvalidFrequencyException;
|
||||||
import io.github.dsheirer.source.SourceEvent;
|
import io.github.dsheirer.source.SourceEvent;
|
||||||
import io.github.dsheirer.source.SourceException;
|
import io.github.dsheirer.source.SourceException;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class FrequencyController
|
public class FrequencyController
|
||||||
{
|
{
|
||||||
@@ -40,8 +39,12 @@ public class FrequencyController
|
|||||||
private long mMaximumFrequency = 0;
|
private long mMaximumFrequency = 0;
|
||||||
private double mFrequencyCorrection = 0.0d;
|
private double mFrequencyCorrection = 0.0d;
|
||||||
private double mSampleRate = 0.0d;
|
private double mSampleRate = 0.0d;
|
||||||
private boolean mLocked = false;
|
private boolean mSampleRateLocked = false;
|
||||||
private List<ISourceEventProcessor> mProcessors = new ArrayList<>();
|
private List<ISourceEventProcessor> mProcessors = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock protecting access to frequency control plane, source event processor subscriptions and event broadcasting
|
||||||
|
*/
|
||||||
private ReentrantLock mLock = new ReentrantLock();
|
private ReentrantLock mLock = new ReentrantLock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,6 +56,15 @@ public class FrequencyController
|
|||||||
mTunable = tunable;
|
mTunable = tunable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock for controlling access to frequency control plane, event processor subscriptions and source event broadcasts.
|
||||||
|
*/
|
||||||
|
public ReentrantLock getFrequencyControllerLock()
|
||||||
|
{
|
||||||
|
return mLock;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare for disposal of this instance.
|
* Prepare for disposal of this instance.
|
||||||
*/
|
*/
|
||||||
@@ -63,36 +75,36 @@ public class FrequencyController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this frequency controller is locked by a remote process. A locked state indicates that neither
|
* Indicates if the sample rate for this frequency controller is locked by a remote process. A locked state
|
||||||
* the frequency nor the sample rate should be changed. The remote process will typically be a downstream
|
* indicates that the sample rate cannot be changed. The remote process will typically be a downstream
|
||||||
* consumer of the data produced by the device being controlled (ie polyphase channelizer) that does not want
|
* consumer of the data produced by the device being controlled (ie polyphase channelizer) that does not want
|
||||||
* the sample rate or frequency to be changed while processing is ongoing.
|
* the sample rate to be changed while processing (ie channels have been allocated) is ongoing.
|
||||||
*
|
*
|
||||||
* @return true if locked or false if not locked.
|
* @return true if locked or false if not locked.
|
||||||
*/
|
*/
|
||||||
public boolean isLocked()
|
public boolean isSampleRateLocked()
|
||||||
{
|
{
|
||||||
return mLocked;
|
return mSampleRateLocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the lock state for this frequency controller. Set to true when a consumer process requires that the
|
* Sets the lock state for this frequency controller. Set to true when a consumer process requires that the
|
||||||
* frequency and sample rate controls be locked so that only the singular consumer process makes changes. This
|
* sample rate control be locked so that only the single consumer process makes changes to the sample rate. This
|
||||||
* is primarily to lock down user interface controls when the polyphase channelizer is producing channels.
|
* is primarily to lock down user interface controls when the polyphase channelizer is producing channels.
|
||||||
*
|
*
|
||||||
* @param locked state
|
* @param sampleRateLocked state
|
||||||
*/
|
*/
|
||||||
public void setLocked(boolean locked) throws SourceException
|
public void setSampleRateLocked(boolean sampleRateLocked) throws SourceException
|
||||||
{
|
{
|
||||||
mLocked = locked;
|
mSampleRateLocked = sampleRateLocked;
|
||||||
|
|
||||||
if(mLocked)
|
if(mSampleRateLocked)
|
||||||
{
|
{
|
||||||
broadcast(SourceEvent.lockedState());
|
broadcast(SourceEvent.lockedSampleRateState());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
broadcast(SourceEvent.unlockedState());
|
broadcast(SourceEvent.unlockedSampleRateState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +123,7 @@ public class FrequencyController
|
|||||||
{
|
{
|
||||||
if(sampleRate != mSampleRate)
|
if(sampleRate != mSampleRate)
|
||||||
{
|
{
|
||||||
if(!mLocked)
|
if(!mSampleRateLocked)
|
||||||
{
|
{
|
||||||
mSampleRate = sampleRate;
|
mSampleRate = sampleRate;
|
||||||
|
|
||||||
@@ -284,7 +296,7 @@ public class FrequencyController
|
|||||||
/**
|
/**
|
||||||
* Adds listener to receive frequency change events
|
* Adds listener to receive frequency change events
|
||||||
*/
|
*/
|
||||||
public void addListener(ISourceEventProcessor processor)
|
public void addSourceEventProcessor(ISourceEventProcessor processor)
|
||||||
{
|
{
|
||||||
mLock.lock();
|
mLock.lock();
|
||||||
|
|
||||||
@@ -304,7 +316,7 @@ public class FrequencyController
|
|||||||
/**
|
/**
|
||||||
* Removes listener from receiving frequency change events
|
* Removes listener from receiving frequency change events
|
||||||
*/
|
*/
|
||||||
public void removeFrequencyChangeProcessor(ISourceEventProcessor processor)
|
public void removeSourceEventProcessor(ISourceEventProcessor processor)
|
||||||
{
|
{
|
||||||
mLock.lock();
|
mLock.lock();
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public class HackRFTunerEditor extends TunerEditor<HackRFTuner,HackRFTunerConfig
|
|||||||
getTunerStatusLabel().setText(status);
|
getTunerStatusLabel().setText(status);
|
||||||
getButtonPanel().updateControls();
|
getButtonPanel().updateControls();
|
||||||
getFrequencyPanel().updateControls();
|
getFrequencyPanel().updateControls();
|
||||||
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked());
|
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
|
||||||
updateSampleRateToolTip();
|
updateSampleRateToolTip();
|
||||||
getTunerInfoButton().setEnabled(hasTuner());
|
getTunerInfoButton().setEnabled(hasTuner());
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ public class HackRFTunerEditor extends TunerEditor<HackRFTuner,HackRFTunerConfig
|
|||||||
*/
|
*/
|
||||||
private void updateSampleRateToolTip()
|
private void updateSampleRateToolTip()
|
||||||
{
|
{
|
||||||
if(hasTuner() && getTuner().getController().isLocked())
|
if(hasTuner() && getTuner().getController().isLockedSampleRate())
|
||||||
{
|
{
|
||||||
mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,38 +93,48 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(CenterFrequencyCalculator.canTune(tunerChannel, mTunerController, mTunerChannels))
|
TunerChannelSource source = null;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
mTunerController.getFrequencyControllerLock().lock();
|
||||||
|
if(CenterFrequencyCalculator.canTune(tunerChannel, mTunerController, mTunerChannels))
|
||||||
{
|
{
|
||||||
//Attempt to create the channel source first, in case we get a filter design exception
|
try
|
||||||
HalfBandTunerChannelSource tunerChannelSource = new HalfBandTunerChannelSource(mChannelSourceEventProcessor,
|
{
|
||||||
tunerChannel, mTunerController.getSampleRate(), channelSpecification);
|
//Attempt to create the channel source first, in case we get a filter design exception
|
||||||
|
HalfBandTunerChannelSource tunerChannelSource = new HalfBandTunerChannelSource(mChannelSourceEventProcessor,
|
||||||
|
tunerChannel, mTunerController.getSampleRate(), channelSpecification);
|
||||||
|
|
||||||
//Add to the list of channel sources so that it will receive the tuner frequency change
|
//Add to the list of channel sources so that it will receive the tuner frequency change
|
||||||
mChannelSources.add(tunerChannelSource);
|
mChannelSources.add(tunerChannelSource);
|
||||||
|
|
||||||
//Set the current tuner frequency
|
//Set the current tuner frequency
|
||||||
tunerChannelSource.setFrequency(mTunerController.getFrequency());
|
tunerChannelSource.setFrequency(mTunerController.getFrequency());
|
||||||
|
|
||||||
//Add to the channel list and update the tuner center frequency as needed
|
//Add to the channel list and update the tuner center frequency as needed
|
||||||
mTunerChannels.add(tunerChannel);
|
mTunerChannels.add(tunerChannel);
|
||||||
updateTunerFrequency();
|
updateTunerFrequency();
|
||||||
|
|
||||||
//Lock the tuner controller frequency and sample rate
|
//Lock the tuner controller frequency and sample rate
|
||||||
mTunerController.setLocked(true);
|
mTunerController.setLockedSampleRate(true);
|
||||||
|
|
||||||
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
|
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
|
||||||
|
|
||||||
return tunerChannelSource;
|
source = tunerChannelSource;
|
||||||
}
|
}
|
||||||
catch(FilterDesignException fde)
|
catch(FilterDesignException fde)
|
||||||
{
|
{
|
||||||
mLog.error("Error creating CIC tuner channel source - couldn't design cleanup filter", fde);
|
mLog.error("Error creating CIC tuner channel source - couldn't design cleanup filter", fde);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
mTunerController.getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -294,7 +304,7 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
|
|||||||
//Unlock the tuner controller if there are no more channels
|
//Unlock the tuner controller if there are no more channels
|
||||||
if(getTunerChannelCount() == 0)
|
if(getTunerChannelCount() == 0)
|
||||||
{
|
{
|
||||||
mTunerController.setLocked(false);
|
mTunerController.setLockedSampleRate(false);
|
||||||
}
|
}
|
||||||
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
|
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,18 +82,30 @@ public class PassThroughSourceManager extends ChannelSourceManager
|
|||||||
@Override
|
@Override
|
||||||
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(!mRunning)
|
if(!mRunning)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(),
|
TunerChannelSource source = null;
|
||||||
mTunerController, tunerChannel);
|
|
||||||
|
|
||||||
mTunerChannels.add(tunerChannel);
|
try
|
||||||
mTunerChannelSources.add(channelSource);
|
{
|
||||||
|
mTunerController.getFrequencyControllerLock().lock();
|
||||||
|
PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(),
|
||||||
|
mTunerController, tunerChannel);
|
||||||
|
|
||||||
return channelSource;
|
mTunerChannels.add(tunerChannel);
|
||||||
|
mTunerChannelSources.add(channelSource);
|
||||||
|
source = channelSource;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
mTunerController.getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -25,12 +25,11 @@ import io.github.dsheirer.source.tuner.TunerController;
|
|||||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||||
|
import java.util.SortedSet;
|
||||||
import org.apache.commons.math3.util.FastMath;
|
import org.apache.commons.math3.util.FastMath;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.SortedSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PolyphaseChannelSourceManager is responsible for managing the tuner's center tuned frequency and providing access to
|
* PolyphaseChannelSourceManager is responsible for managing the tuner's center tuned frequency and providing access to
|
||||||
* polyphase tuner channel sources (ie DDCs). This class is responsible for determining IF a requested channel can
|
* polyphase tuner channel sources (ie DDCs). This class is responsible for determining IF a requested channel can
|
||||||
@@ -416,48 +415,56 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
|
|||||||
@Override
|
@Override
|
||||||
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
||||||
{
|
{
|
||||||
if(isTunable(tunerChannel))
|
TunerChannelSource tunerChannelSource = null;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
//Get a new set of currently tuned channels
|
mTunerController.getFrequencyControllerLock().lock();
|
||||||
SortedSet<TunerChannel> tunerChannels = getTunerChannels();
|
if(isTunable(tunerChannel))
|
||||||
|
|
||||||
//Add the requested channel to the list
|
|
||||||
tunerChannels.add(tunerChannel);
|
|
||||||
|
|
||||||
if(canTune(tunerChannels))
|
|
||||||
{
|
{
|
||||||
long currentCenterFrequency = mTunerController.getFrequency();
|
//Get a new set of currently tuned channels
|
||||||
long updatedCenterFrequency = 0;
|
SortedSet<TunerChannel> tunerChannels = getTunerChannels();
|
||||||
|
|
||||||
//Attempt to adjust the center frequency before we allocate the channel
|
//Add the requested channel to the list
|
||||||
try
|
tunerChannels.add(tunerChannel);
|
||||||
|
|
||||||
|
if(canTune(tunerChannels))
|
||||||
{
|
{
|
||||||
updatedCenterFrequency = getCenterFrequency(tunerChannels, currentCenterFrequency);
|
long currentCenterFrequency = mTunerController.getFrequency();
|
||||||
|
long updatedCenterFrequency = 0;
|
||||||
|
|
||||||
if(updatedCenterFrequency != currentCenterFrequency && updatedCenterFrequency != 0)
|
//Attempt to adjust the center frequency before we allocate the channel
|
||||||
|
try
|
||||||
{
|
{
|
||||||
mTunerController.setFrequency(updatedCenterFrequency);
|
updatedCenterFrequency = getCenterFrequency(tunerChannels, currentCenterFrequency);
|
||||||
}
|
|
||||||
|
|
||||||
//If we're successful to here, allocate the channel
|
if(updatedCenterFrequency != currentCenterFrequency && updatedCenterFrequency != 0)
|
||||||
return mPolyphaseChannelManager.getChannel(tunerChannel);
|
{
|
||||||
}
|
mTunerController.setFrequency(updatedCenterFrequency);
|
||||||
catch(SourceException se)
|
}
|
||||||
{
|
|
||||||
//Tuner controller threw an error trying to tune to the updated frequency
|
//If we're successful to here, allocate the channel
|
||||||
mLog.error("Error while updating tuner controller with new center frequency [" +
|
tunerChannelSource = mPolyphaseChannelManager.getChannel(tunerChannel);
|
||||||
updatedCenterFrequency + "] - unable to allocate new tuner channel", se);
|
}
|
||||||
}
|
catch(SourceException se)
|
||||||
catch(IllegalArgumentException iae)
|
{
|
||||||
{
|
//Tuner controller threw an error trying to tune to the updated frequency
|
||||||
//Center frequency calculation failed
|
mLog.error("Error while updating tuner controller with new center frequency [" +
|
||||||
// mLog.debug("Couldn't calculate new tuner center frequency for new tuner channel [" +
|
updatedCenterFrequency + "] - unable to allocate new tuner channel", se);
|
||||||
// tunerChannel + "] unable to allocate new tuner channel");
|
}
|
||||||
|
catch(IllegalArgumentException iae)
|
||||||
|
{
|
||||||
|
//Center frequency calculation failed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
mTunerController.getFrequencyControllerLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return tunerChannelSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -474,7 +481,7 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
|
|||||||
broadcast(sourceEvent);
|
broadcast(sourceEvent);
|
||||||
//Lock the frequency and sample rate controls on the tuner controller so users can't change them
|
//Lock the frequency and sample rate controls on the tuner controller so users can't change them
|
||||||
//when the polyphase manager has channels allocated
|
//when the polyphase manager has channels allocated
|
||||||
mTunerController.setLocked(getTunerChannelCount() > 0);
|
mTunerController.setLockedSampleRate(getTunerChannelCount() > 0);
|
||||||
break;
|
break;
|
||||||
case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED:
|
case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED:
|
||||||
//Rebroadcast these frequency measurement errors to the tuner and tuner controller
|
//Rebroadcast these frequency measurement errors to the tuner and tuner controller
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ 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.E4KLNAGain;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain;
|
import io.github.dsheirer.source.tuner.rtl.e4k.E4KEmbeddedTuner.E4KMixerGain;
|
||||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -41,8 +43,6 @@ import javax.swing.JOptionPane;
|
|||||||
import javax.swing.JSeparator;
|
import javax.swing.JSeparator;
|
||||||
import javax.swing.SpinnerNumberModel;
|
import javax.swing.SpinnerNumberModel;
|
||||||
import javax.usb.UsbException;
|
import javax.usb.UsbException;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* E4000 tuner editor
|
* E4000 tuner editor
|
||||||
@@ -383,7 +383,7 @@ public class E4KTunerEditor extends TunerEditor<RTL2832Tuner, E4KTunerConfigurat
|
|||||||
*/
|
*/
|
||||||
private void updateSampleRateToolTip()
|
private void updateSampleRateToolTip()
|
||||||
{
|
{
|
||||||
if(hasTuner() && getTuner().getTunerController().isLocked())
|
if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
|
||||||
{
|
{
|
||||||
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ public class R820TTunerEditor extends TunerEditor<RTL2832Tuner,R820TTunerConfigu
|
|||||||
*/
|
*/
|
||||||
private void updateSampleRateToolTip()
|
private void updateSampleRateToolTip()
|
||||||
{
|
{
|
||||||
if(hasTuner() && getTuner().getTunerController().isLocked())
|
if(hasTuner() && getTuner().getTunerController().isLockedSampleRate())
|
||||||
{
|
{
|
||||||
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
|||||||
import io.github.dsheirer.source.tuner.manager.DiscoveredUSBTuner;
|
import io.github.dsheirer.source.tuner.manager.DiscoveredUSBTuner;
|
||||||
import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener;
|
import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerStatus;
|
import io.github.dsheirer.source.tuner.manager.TunerStatus;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.table.AbstractTableModel;
|
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -36,6 +32,10 @@ import java.util.List;
|
|||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model for discovered tuners
|
* Model for discovered tuners
|
||||||
@@ -449,7 +449,7 @@ public class DiscoveredTunerModel extends AbstractTableModel implements Listener
|
|||||||
if(discoveredTuner.hasTuner())
|
if(discoveredTuner.hasTuner())
|
||||||
{
|
{
|
||||||
int channelCount = discoveredTuner.getTuner().getChannelSourceManager().getTunerChannelCount();
|
int channelCount = discoveredTuner.getTuner().getChannelSourceManager().getTunerChannelCount();
|
||||||
return channelCount + " (" + (discoveredTuner.getTuner().getTunerController().isLocked() ? "LOCKED)" : "UNLOCKED)");
|
return channelCount + " (" + (discoveredTuner.getTuner().getTunerController().isLockedSampleRate() ? "LOCKED)" : "UNLOCKED)");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -619,8 +619,8 @@ public abstract class TunerEditor<T extends Tuner,C extends TunerConfiguration>
|
|||||||
{
|
{
|
||||||
getFrequencyControl().clearListeners();
|
getFrequencyControl().clearListeners();
|
||||||
getFrequencyControl().addListener(mFrequencyAndCorrectionChangeListener);
|
getFrequencyControl().addListener(mFrequencyAndCorrectionChangeListener);
|
||||||
getFrequencyControl().setEnabled(hasTuner() && !getTuner().getTunerController().isLocked());
|
getFrequencyControl().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
|
||||||
getTunerLockedStatusLabel().setVisible(hasTuner() && getTuner().getTunerController().isLocked());
|
getTunerLockedStatusLabel().setVisible(hasTuner() && getTuner().getTunerController().isLockedSampleRate());
|
||||||
getFrequencyCorrectionSpinner().setEnabled(hasTuner());
|
getFrequencyCorrectionSpinner().setEnabled(hasTuner());
|
||||||
getAutoPPMCheckBox().setEnabled(hasTuner());
|
getAutoPPMCheckBox().setEnabled(hasTuner());
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class TunerViewPanel extends JPanel
|
|||||||
|
|
||||||
if(selectedTuner != null && selectedTuner.hasTuner() && tunerEvent.getTuner() == selectedTuner.getTuner())
|
if(selectedTuner != null && selectedTuner.hasTuner() && tunerEvent.getTuner() == selectedTuner.getTuner())
|
||||||
{
|
{
|
||||||
mDiscoveredTunerEditor.setTunerLockState(selectedTuner.getTuner().getTunerController().isLocked());
|
mDiscoveredTunerEditor.setTunerLockState(selectedTuner.getTuner().getTunerController().isLockedSampleRate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user