mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
#1190 Tuner management redesign. Updates all tuners with direct threaded support. Incorporates USB Hotplug support. Adds black/white listing of tuners. Updates all tuner editors to have a common look and feel. Changes location of tuner configuration file and changes format to JSON. (#1208)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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 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.
|
||||
* 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/>
|
||||
*
|
||||
******************************************************************************/
|
||||
* 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.manager;
|
||||
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
@@ -133,7 +136,7 @@ public class CenterFrequencyCalculator
|
||||
public static boolean canTune(TunerChannel channel, TunerController tunerController, SortedSet<TunerChannel> channels)
|
||||
{
|
||||
//Make sure we're within the tunable frequency range of this tuner
|
||||
if(tunerController.getMinFrequency() < channel.getMinFrequency() && tunerController.getMaxFrequency() > channel.getMaxFrequency())
|
||||
if(tunerController.getMinimumFrequency() < channel.getMinFrequency() && tunerController.getMaximumFrequency() > channel.getMaxFrequency())
|
||||
{
|
||||
//If this is the first lock, then we're good
|
||||
if(channels.isEmpty())
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.manager;
|
||||
|
||||
@@ -50,6 +47,11 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor
|
||||
*/
|
||||
public abstract int getTunerChannelCount();
|
||||
|
||||
/**
|
||||
* Tells this channel source manager to shutdown all tuner channel sources to prepare for tuner shutdown.
|
||||
*/
|
||||
public abstract void stopAllChannels();
|
||||
|
||||
/**
|
||||
* Obtains a source for the tuner channel or returns null if the channel cannot be sourced by this tuner.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.manager;
|
||||
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
||||
import io.github.dsheirer.source.tuner.Tuner;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* A discovered tuner that may be accessible for use.
|
||||
*/
|
||||
public abstract class DiscoveredTuner implements ITunerErrorListener
|
||||
{
|
||||
private Logger mLog = LoggerFactory.getLogger(DiscoveredTuner.class);
|
||||
private TunerStatus mTunerStatus = TunerStatus.ENABLED;
|
||||
private boolean mEnabled = true;
|
||||
private String mErrorMessage;
|
||||
private List<IDiscoveredTunerStatusListener> mListeners = new CopyOnWriteArrayList();
|
||||
protected Tuner mTuner;
|
||||
protected TunerConfiguration mTunerConfiguration;
|
||||
|
||||
/**
|
||||
* Tuner Class
|
||||
*/
|
||||
public abstract TunerClass getTunerClass();
|
||||
|
||||
/**
|
||||
* Current status of the discovered tuner
|
||||
*/
|
||||
public TunerStatus getTunerStatus()
|
||||
{
|
||||
return mTunerStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status of the discovered tuner and notifies registered listeners of the status change.
|
||||
* @param tunerStatus to set
|
||||
*/
|
||||
protected void setTunerStatus(TunerStatus tunerStatus)
|
||||
{
|
||||
TunerStatus previous = mTunerStatus;
|
||||
mTunerStatus = tunerStatus;
|
||||
broadcast(this, previous, mTunerStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this discovered tuner is enabled and usable.
|
||||
*/
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enabled state of this discovered tuner
|
||||
*/
|
||||
public void setEnabled(boolean enabled)
|
||||
{
|
||||
//If there was a change in state
|
||||
if(mEnabled ^ enabled)
|
||||
{
|
||||
mEnabled = enabled;
|
||||
|
||||
if(mEnabled)
|
||||
{
|
||||
setTunerStatus(TunerStatus.ENABLED);
|
||||
start();
|
||||
}
|
||||
else
|
||||
{
|
||||
stop();
|
||||
setTunerStatus(TunerStatus.DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this discovered tuner is available and usable. Use this method to check if a discovered tuner is
|
||||
* available prior to access the tuner directly via the getTuner() method.
|
||||
*/
|
||||
public boolean isAvailable()
|
||||
{
|
||||
return getTunerStatus().isAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* An identifier for this discovered tuner where this identifier when combined with the discovered tuner type
|
||||
* is a globally unique value.
|
||||
*
|
||||
* Note: this identifier will be used to persist the enabled/disabled state of each discoverable tuner. Therefore,
|
||||
* this identifier must be consistent across application run cycles in order to correctly manage disabled tuners.
|
||||
* @return globally unique string identifier for the discovered tuner.
|
||||
*/
|
||||
public abstract String getId();
|
||||
|
||||
/**
|
||||
* Access a started and initialized.
|
||||
*
|
||||
* Use the isAvailable() method to check if this tuner is available prior to invoking this method to avoid a
|
||||
* null tuner instance.
|
||||
*
|
||||
* @return started and initialized tuner
|
||||
*/
|
||||
public Tuner getTuner()
|
||||
{
|
||||
return mTuner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this discovered tuner is started and has a fully constructed tuner instance
|
||||
*/
|
||||
public boolean hasTuner()
|
||||
{
|
||||
return getTuner() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tuner configuration for this tuner
|
||||
*/
|
||||
public TunerConfiguration getTunerConfiguration()
|
||||
{
|
||||
return mTunerConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tuner configuration for this tuner.
|
||||
*/
|
||||
public void setTunerConfiguration(TunerConfiguration tunerConfiguration)
|
||||
{
|
||||
mTunerConfiguration = tunerConfiguration;
|
||||
|
||||
if(hasTuner())
|
||||
{
|
||||
try
|
||||
{
|
||||
getTuner().getTunerController().apply(mTunerConfiguration);
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error applying tuner configuration [" + mTunerConfiguration.getClass() +
|
||||
"] to discovered tuner [" + getId() + "}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this discovered tuner has a tuner configuration
|
||||
*/
|
||||
public boolean hasTunerConfiguration()
|
||||
{
|
||||
return mTunerConfiguration != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a tuner status change listener to monitor this discovered tuner for changes in status.
|
||||
*/
|
||||
public void addTunerStatusListener(IDiscoveredTunerStatusListener listener)
|
||||
{
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a tuner status change listener from monitoring this discovered tuner for changes in status.
|
||||
*/
|
||||
public void removeTunerStatusListener(IDiscoveredTunerStatusListener listener)
|
||||
{
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the tuner status change to all registered listeners.
|
||||
* @param tuner that was changed
|
||||
* @param previous tuner status
|
||||
* @param current tuner status
|
||||
*/
|
||||
private void broadcast(DiscoveredTuner tuner, TunerStatus previous, TunerStatus current)
|
||||
{
|
||||
for(IDiscoveredTunerStatusListener listener: mListeners)
|
||||
{
|
||||
listener.tunerStatusUpdated(tuner, previous, current);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this tuner to an error state and applies the error message
|
||||
* @param errorMessage to set
|
||||
*/
|
||||
@Override
|
||||
public void setErrorMessage(String errorMessage)
|
||||
{
|
||||
mErrorMessage = errorMessage;
|
||||
setTunerStatus(TunerStatus.ERROR);
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this tuner has an error message.
|
||||
*/
|
||||
public boolean hasErrorMessage()
|
||||
{
|
||||
return mErrorMessage != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional error message.
|
||||
* @return error message if there is one, or null if there is not.
|
||||
*/
|
||||
public String getErrorMessage()
|
||||
{
|
||||
return mErrorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully instantiate and start this discovered tuner to make it usable within the application. Implementations
|
||||
* should attempt to instantiate the tuner and assign it to mTuner variable. If there is an error, invoke the
|
||||
* setErrorMessage() to signal the tuner is unusable.
|
||||
*/
|
||||
public abstract void start();
|
||||
|
||||
/**
|
||||
* Attempts to restart a tuner that's currently in an error state
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
if(getTunerStatus() == TunerStatus.ERROR)
|
||||
{
|
||||
mErrorMessage = null;
|
||||
|
||||
if(isEnabled())
|
||||
{
|
||||
setTunerStatus(TunerStatus.ENABLED);
|
||||
}
|
||||
else
|
||||
{
|
||||
setTunerStatus(TunerStatus.DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop this discovered tuner, notify registered listeners/consumers and release any resources that it is using.
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
if(hasTuner())
|
||||
{
|
||||
mLog.info("Stopping Tuner: " + getId());
|
||||
getTuner().stop();
|
||||
mTuner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.manager;
|
||||
|
||||
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.TunerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A USB tuner that is discovered during the USB device traversal process that has a VID and PID that match a known
|
||||
* USB tuner type that is currently supported by the software.
|
||||
*/
|
||||
public class DiscoveredUSBTuner extends DiscoveredTuner
|
||||
{
|
||||
private Logger mLog = LoggerFactory.getLogger(DiscoveredUSBTuner.class);
|
||||
private TunerClass mTunerClass;
|
||||
private int mBus;
|
||||
private int mPort;
|
||||
private ChannelizerType mChannelizerType;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param bus (USB) number
|
||||
* @param port (USB) number
|
||||
* @param channelizerType to use with the tuner
|
||||
*/
|
||||
public DiscoveredUSBTuner(TunerClass tunerClass, int bus, int port, ChannelizerType channelizerType)
|
||||
{
|
||||
mTunerClass = tunerClass;
|
||||
mBus = bus;
|
||||
mPort = port;
|
||||
mChannelizerType = channelizerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this USB tuner is plugged into the specified bus and port.
|
||||
* @param bus number
|
||||
* @param port number
|
||||
* @return true if there is a match
|
||||
*/
|
||||
public boolean isAt(int bus, int port)
|
||||
{
|
||||
return mBus == bus && mPort == port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tuner class as determined from the USB VID and PID values
|
||||
*/
|
||||
public TunerClass getTunerClass()
|
||||
{
|
||||
return mTunerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* USB bus number
|
||||
*/
|
||||
public int getBus()
|
||||
{
|
||||
return mBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* USB port number
|
||||
*/
|
||||
public int getPort()
|
||||
{
|
||||
return mPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getTunerClass());
|
||||
sb.append(" USB Bus:").append(getBus());
|
||||
sb.append(" Port:").append(getPort());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
if(isAvailable() && !hasTuner())
|
||||
{
|
||||
try
|
||||
{
|
||||
mTuner = TunerFactory.getUsbTuner(getTunerClass(), getPort(), getBus(), this, mChannelizerType);
|
||||
mTuner.start();
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
|
||||
//Set error message to flag this tuner with error status and invoke stop()
|
||||
setErrorMessage(se.getMessage());
|
||||
|
||||
mLog.error("Unable to start tuner [" + getTunerClass() + "] - error: " + getErrorMessage());
|
||||
|
||||
try
|
||||
{
|
||||
stop();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//No-op on attempted cleanup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "USB Tuner - " + getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.manager;
|
||||
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* Monitors measured frequency error PPM values from the tuner and (when enabled) applies the current
|
||||
* error measurement to the tuner controller to adjust the tuner's PPM and align the frequency value.
|
||||
*/
|
||||
public class FrequencyErrorCorrectionManager
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(FrequencyErrorCorrectionManager.class);
|
||||
|
||||
private static final double FREQUENCY_CORRECTION_ERROR_THRESHOLD = 0.4;
|
||||
private static final long AUTO_CORRECTION_OBSERVATION_PERIOD_MILLISECONDS = 30 * 1000; //30 seconds
|
||||
private long mObservationPeriodStart;
|
||||
private double mPPMRequired;
|
||||
private boolean mEnabled = true;
|
||||
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
|
||||
private TunerController mTunerController;
|
||||
|
||||
/**
|
||||
* Constructs a frequency error correction manager for the specified tuner controller.
|
||||
*/
|
||||
public FrequencyErrorCorrectionManager(TunerController tunerController)
|
||||
{
|
||||
mTunerController = tunerController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables automatic frequency PPM correction.
|
||||
* @param enabled to true to turn on automatic PPM adjustments.
|
||||
*/
|
||||
public void setEnabled(boolean enabled)
|
||||
{
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if Auto-PPM is enabled
|
||||
*/
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets monitoring
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
mObservationPeriodStart = 0;
|
||||
mPPMRequired = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a PPM adjustment value to the tuner's current PPM setting, when auto-correction is enabled.
|
||||
*/
|
||||
private void applyCorrection()
|
||||
{
|
||||
if(mEnabled && mTunerController.hasMeasuredFrequencyError())
|
||||
{
|
||||
double frequencyCorrection = mTunerController.getFrequencyCorrection();
|
||||
frequencyCorrection -= mTunerController.getPPMFrequencyError();
|
||||
|
||||
try
|
||||
{
|
||||
mLog.info("Auto-Correcting Tuner PPM to [" + mDecimalFormat.format(frequencyCorrection) + "]");
|
||||
|
||||
mTunerController.setFrequencyCorrection(frequencyCorrection);
|
||||
reset();
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error while applying automatic frequency error PPM correction value: " + frequencyCorrection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the monitor with a new PPM frequency error measurement from the tuner.
|
||||
*/
|
||||
public void updatePPM(double ppm)
|
||||
{
|
||||
if(ppm > FREQUENCY_CORRECTION_ERROR_THRESHOLD)
|
||||
{
|
||||
if(mObservationPeriodStart == 0)
|
||||
{
|
||||
mObservationPeriodStart = System.currentTimeMillis();
|
||||
mPPMRequired = ppm;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mPPMRequired > 0)
|
||||
{
|
||||
mPPMRequired = ppm;
|
||||
|
||||
if(mObservationPeriodStart + AUTO_CORRECTION_OBSERVATION_PERIOD_MILLISECONDS < System.currentTimeMillis())
|
||||
{
|
||||
applyCorrection();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Opposite polarity ppm value ... restart the timer
|
||||
mObservationPeriodStart = System.currentTimeMillis();
|
||||
mPPMRequired = ppm;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ppm < -FREQUENCY_CORRECTION_ERROR_THRESHOLD)
|
||||
{
|
||||
if(mObservationPeriodStart == 0)
|
||||
{
|
||||
mObservationPeriodStart = System.currentTimeMillis();
|
||||
mPPMRequired = ppm;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mPPMRequired < 0)
|
||||
{
|
||||
mPPMRequired = ppm;
|
||||
|
||||
if(mObservationPeriodStart + AUTO_CORRECTION_OBSERVATION_PERIOD_MILLISECONDS < System.currentTimeMillis())
|
||||
{
|
||||
applyCorrection();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Opposite polarity ppm value ... restart the timer
|
||||
mObservationPeriodStart = System.currentTimeMillis();
|
||||
mPPMRequired = ppm;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
@@ -45,12 +46,12 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
|
||||
|
||||
private final static int DELAY_BUFFER_DURATION_MILLISECONDS = 2000;
|
||||
|
||||
// private List<CICTunerChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
|
||||
private List<HalfBandTunerChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
|
||||
private SortedSet<TunerChannel> mTunerChannels = new TreeSet<>();
|
||||
private TunerController mTunerController;
|
||||
private ChannelSourceEventProcessor mChannelSourceEventProcessor = new ChannelSourceEventProcessor();
|
||||
private NativeSampleDelayBuffer mSampleDelayBuffer;
|
||||
private boolean mRunning = true;
|
||||
|
||||
public HeterodyneChannelSourceManager(TunerController tunerController)
|
||||
{
|
||||
@@ -58,6 +59,26 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
|
||||
mTunerController.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAllChannels()
|
||||
{
|
||||
mRunning = false;
|
||||
|
||||
List<TunerChannelSource> toStop = new ArrayList<>(mChannelSources);
|
||||
|
||||
for(TunerChannelSource tunerChannelSource: toStop)
|
||||
{
|
||||
try
|
||||
{
|
||||
tunerChannelSource.process(SourceEvent.tunerShutdown(tunerChannelSource));
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error stopping tuner channel source for tuner shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<TunerChannel> getTunerChannels()
|
||||
{
|
||||
@@ -73,14 +94,16 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
|
||||
@Override
|
||||
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
||||
{
|
||||
if(!mRunning)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if(CenterFrequencyCalculator.canTune(tunerChannel, mTunerController, mTunerChannels))
|
||||
{
|
||||
try
|
||||
{
|
||||
//Attempt to create the channel source first, in case we get a filter design exception
|
||||
// CICTunerChannelSource tunerChannelSource = new CICTunerChannelSource(mChannelSourceEventProcessor,
|
||||
// tunerChannel, mTunerController.getSampleRate(), channelSpecification);
|
||||
|
||||
HalfBandTunerChannelSource tunerChannelSource = new HalfBandTunerChannelSource(mChannelSourceEventProcessor,
|
||||
tunerChannel, mTunerController.getSampleRate(), channelSpecification);
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.manager;
|
||||
|
||||
/**
|
||||
* Discovered tuner status change listener interface
|
||||
*/
|
||||
public interface IDiscoveredTunerStatusListener
|
||||
{
|
||||
/**
|
||||
* Notification that a discovered tuner's status has changed.
|
||||
* @param discoveredTuner that has a status change.
|
||||
* @param previous tuner status
|
||||
* @param current tuner status
|
||||
*/
|
||||
void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current);
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.manager;
|
||||
@@ -33,6 +30,7 @@ import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
@@ -48,12 +46,33 @@ public class PassThroughSourceManager extends ChannelSourceManager
|
||||
private TunerController mTunerController;
|
||||
private SortedSet<TunerChannel> mTunerChannels = new TreeSet<>();
|
||||
private List<TunerChannelSource> mTunerChannelSources = new CopyOnWriteArrayList<>();
|
||||
private boolean mRunning = true;
|
||||
|
||||
public PassThroughSourceManager(TunerController tunerController)
|
||||
{
|
||||
mTunerController = tunerController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAllChannels()
|
||||
{
|
||||
mRunning = false;
|
||||
|
||||
List<TunerChannelSource> toStop = new ArrayList<>(mTunerChannelSources);
|
||||
|
||||
for(TunerChannelSource tunerChannelSource: toStop)
|
||||
{
|
||||
try
|
||||
{
|
||||
tunerChannelSource.process(SourceEvent.tunerShutdown(tunerChannelSource));
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error stopping tuner channel source for tuner shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<TunerChannel> getTunerChannels()
|
||||
{
|
||||
@@ -69,6 +88,11 @@ public class PassThroughSourceManager extends ChannelSourceManager
|
||||
@Override
|
||||
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
||||
{
|
||||
if(!mRunning)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(),
|
||||
mTunerController, tunerChannel);
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.SortedSet;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* be provided and then adjusting the center frequency and provisioning a DDC Polyphase Tuner channel source.
|
||||
*/
|
||||
public class PolyphaseChannelSourceManager extends ChannelSourceManager
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSourceManager.class);
|
||||
@@ -38,10 +43,7 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
|
||||
private TunerController mTunerController;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* be provided and then adjusting the center frequency and provisioning a DDC Polyphase Tuner channel source.
|
||||
*
|
||||
* Constructs an instance
|
||||
* @param tunerController with a center tuned frequency that will be managed by this instance
|
||||
*/
|
||||
public PolyphaseChannelSourceManager(TunerController tunerController)
|
||||
@@ -54,6 +56,12 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
|
||||
mTunerController.addListener(mPolyphaseChannelManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAllChannels()
|
||||
{
|
||||
mPolyphaseChannelManager.stopAllChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the channel min/max frequencies are within the tunable frequency range of the tuner controller
|
||||
*
|
||||
|
||||
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.manager;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||
import io.github.dsheirer.source.Source;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.config.SourceConfigTuner;
|
||||
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
|
||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||
import io.github.dsheirer.source.mixer.MixerManager;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
import io.github.dsheirer.source.tuner.channel.MultiFrequencyTunerChannelSource;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationManager;
|
||||
import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.usb4java.Context;
|
||||
import org.usb4java.Device;
|
||||
import org.usb4java.DeviceDescriptor;
|
||||
import org.usb4java.DeviceList;
|
||||
import org.usb4java.HotplugCallback;
|
||||
import org.usb4java.HotplugCallbackHandle;
|
||||
import org.usb4java.LibUsb;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Tuner manager provides access to tuners using USB, recording, sound-card and system-daemon accessible devices. This
|
||||
* manager also supports hot-plug detection and black-listing of discovered tuners so that they can be used with other
|
||||
* software applications.
|
||||
*/
|
||||
public class TunerManager implements IDiscoveredTunerStatusListener
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(TunerManager.class);
|
||||
private static final int MAXIMUM_USB_2_DATA_RATE = 480000000;
|
||||
|
||||
private UserPreferences mUserPreferences;
|
||||
private DiscoveredTunerModel mDiscoveredTunerModel = new DiscoveredTunerModel();
|
||||
private TunerConfigurationManager mTunerConfigurationManager;
|
||||
private HotplugEventSupport mHotplugEventSupport = new HotplugEventSupport();
|
||||
private Context mLibUsbApplicationContext = new Context();
|
||||
private boolean mLibUsbInitialized = false;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param userPreferences for preferences
|
||||
*/
|
||||
public TunerManager(UserPreferences userPreferences)
|
||||
{
|
||||
mUserPreferences = userPreferences;
|
||||
mTunerConfigurationManager = new TunerConfigurationManager(userPreferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovered tuner model
|
||||
*/
|
||||
public DiscoveredTunerModel getDiscoveredTunerModel()
|
||||
{
|
||||
return mDiscoveredTunerModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the tuner manager and loads all available tuners
|
||||
*/
|
||||
public void start()
|
||||
{
|
||||
mLog.info("LibUSB API Version: " + LibUsb.getApiVersion());
|
||||
mLog.info("LibUSB Version: " + LibUsb.getVersion());
|
||||
|
||||
int status = LibUsb.init(mLibUsbApplicationContext);
|
||||
|
||||
if(status == LibUsb.SUCCESS)
|
||||
{
|
||||
mLibUsbInitialized = true;
|
||||
// LibUsb.setOption(mLibUsbApplicationContext, LibUsb.OPTION_LOG_LEVEL, LibUsb.LOG_LEVEL_DEBUG);
|
||||
mHotplugEventSupport.start();
|
||||
|
||||
discoverUSBTuners();
|
||||
}
|
||||
|
||||
discoverSdrPlayTuners();
|
||||
discoverSoundCardTuners();
|
||||
discoverRecordingTuners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops or shuts down the tuner manager and releases all devices
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
//Stop all tuners
|
||||
mDiscoveredTunerModel.releaseDiscoveredTuners();
|
||||
|
||||
//Shutdown LibUsb
|
||||
if(mLibUsbInitialized)
|
||||
{
|
||||
mHotplugEventSupport.stop();
|
||||
LibUsb.exit(mLibUsbApplicationContext);
|
||||
mLibUsbInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests to save the current state of the tuner configurations
|
||||
*/
|
||||
public void saveConfigurations()
|
||||
{
|
||||
mTunerConfigurationManager.saveConfigurations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover or rediscovers USB tuners
|
||||
*/
|
||||
private void discoverUSBTuners()
|
||||
{
|
||||
if(mLibUsbInitialized)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeviceList deviceList = new DeviceList();
|
||||
int deviceCount = LibUsb.getDeviceList(mLibUsbApplicationContext, deviceList);
|
||||
|
||||
if(deviceCount >= 0)
|
||||
{
|
||||
for(Device device: deviceList)
|
||||
{
|
||||
addUsbTuner(device);
|
||||
}
|
||||
}
|
||||
|
||||
LibUsb.freeDeviceList(deviceList, true);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("LibUsb - error during USB device discovery", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the USB device is a supported tuner and add if it has not already been added/discovered.
|
||||
* @param device to inspect and optionally add as a discovered tuner
|
||||
*/
|
||||
private void addUsbTuner(Device device)
|
||||
{
|
||||
int bus = LibUsb.getBusNumber(device);
|
||||
int port = LibUsb.getPortNumber(device);
|
||||
|
||||
DeviceDescriptor deviceDescriptor = new DeviceDescriptor();
|
||||
int status = LibUsb.getDeviceDescriptor(device, deviceDescriptor);
|
||||
|
||||
if(status == LibUsb.SUCCESS)
|
||||
{
|
||||
TunerClass tunerClass = TunerClass.lookup(deviceDescriptor.idVendor(), deviceDescriptor.idProduct());
|
||||
|
||||
if(tunerClass.isSupportedUsbTuner() && !mDiscoveredTunerModel.hasUsbTuner(bus, port))
|
||||
{
|
||||
mLog.info("Adding USB Bus [" + bus + "] Port [" + port + "] ID [" +
|
||||
String.format("%04X", deviceDescriptor.idVendor()) + ":" +
|
||||
String.format("%04X", deviceDescriptor.idProduct()) + "] Tuner Class [" + tunerClass + "]");
|
||||
ChannelizerType channelizerType = mUserPreferences.getTunerPreference().getChannelizerType();
|
||||
DiscoveredUSBTuner discoveredUSBTuner = new DiscoveredUSBTuner(tunerClass, bus, port, channelizerType);
|
||||
discoveredUSBTuner.addTunerStatusListener(this);
|
||||
|
||||
//Set the tuner to disabled if the user has previously blacklisted the tuner
|
||||
if(mTunerConfigurationManager.isDisabled(discoveredUSBTuner))
|
||||
{
|
||||
discoveredUSBTuner.setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Attempt to start the discovered tuner so we can determine the tuner type
|
||||
discoveredUSBTuner.start();
|
||||
|
||||
if(discoveredUSBTuner.hasTuner())
|
||||
{
|
||||
TunerType tunerType = discoveredUSBTuner.getTuner().getTunerType();
|
||||
|
||||
TunerConfiguration tunerConfiguration = mTunerConfigurationManager
|
||||
.getTunerConfiguration(tunerType, discoveredUSBTuner.getId());
|
||||
|
||||
if(tunerConfiguration != null)
|
||||
{
|
||||
mLog.info("Applying tuner configuration to " + discoveredUSBTuner.getId());
|
||||
discoveredUSBTuner.setTunerConfiguration(tunerConfiguration);
|
||||
mTunerConfigurationManager.saveConfigurations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDiscoveredTunerModel.addDiscoveredTuner(discoveredUSBTuner);
|
||||
mLog.info("Tuner Added: " + discoveredUSBTuner);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("LibUsb - unable to get device descriptor for device on bus [" + bus +
|
||||
"] port [" + port + "] - status [" + status + "] - " + LibUsb.errorName(status));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops and removes the USB tuner if it is currently discovered.
|
||||
* @param bus usb for the tuner
|
||||
* @param port usb for the tuner
|
||||
*/
|
||||
private void removeUsbTuner(int bus, int port)
|
||||
{
|
||||
mDiscoveredTunerModel.removeUsbTuner(bus, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover sound card tuners like the Funcube Dongle (Pro)
|
||||
*/
|
||||
private void discoverSoundCardTuners()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover SDRPlay RSP tuners
|
||||
*/
|
||||
private void discoverSdrPlayTuners()
|
||||
{
|
||||
//placeholder ...
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover recording based tuners
|
||||
*/
|
||||
private void discoverRecordingTuners()
|
||||
{
|
||||
//placeholder
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles tuner status change events. Events are sent to the tuner configuration manager so that it can save
|
||||
* configuration updates and events are also monitored to detect when a user changes the tuner state of a tuner
|
||||
* so that we can auto-start the tuner and apply a tuner configuration.
|
||||
*
|
||||
* @param discoveredTuner that has a status change.
|
||||
* @param previous tuner status
|
||||
* @param current tuner status
|
||||
*/
|
||||
@Override
|
||||
public void tunerStatusUpdated(DiscoveredTuner discoveredTuner, TunerStatus previous, TunerStatus current)
|
||||
{
|
||||
mTunerConfigurationManager.tunerStatusUpdated(discoveredTuner, previous, current);
|
||||
|
||||
if(previous != TunerStatus.ENABLED && current == TunerStatus.ENABLED)
|
||||
{
|
||||
discoveredTuner.start();
|
||||
|
||||
if(discoveredTuner.hasTuner())
|
||||
{
|
||||
TunerType tunerType = discoveredTuner.getTuner().getTunerType();
|
||||
|
||||
TunerConfiguration tunerConfiguration = mTunerConfigurationManager
|
||||
.getTunerConfiguration(tunerType, discoveredTuner.getId());
|
||||
|
||||
if(tunerConfiguration != null)
|
||||
{
|
||||
discoveredTuner.setTunerConfiguration(tunerConfiguration);
|
||||
mTunerConfigurationManager.saveConfigurations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a tuner that matches the name argument
|
||||
*
|
||||
* @param preferredTunerName of the tuner
|
||||
* @return named tuner or null
|
||||
*/
|
||||
public DiscoveredTuner getDiscoveredTuner(String preferredTunerName)
|
||||
{
|
||||
if(preferredTunerName != null)
|
||||
{
|
||||
for(DiscoveredTuner discoveredTuner : getDiscoveredTunerModel().getAvailableTuners())
|
||||
{
|
||||
if(discoveredTuner.isAvailable() &&
|
||||
discoveredTuner.getTuner().getPreferredName().equalsIgnoreCase(preferredTunerName))
|
||||
{
|
||||
return discoveredTuner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorted list of preferred tuner names
|
||||
*/
|
||||
public List<String> getPreferredTunerNames()
|
||||
{
|
||||
List<String> preferredNames = new ArrayList<>();
|
||||
List<DiscoveredTuner> availableTuners = getAvailableTuners();
|
||||
|
||||
for(DiscoveredTuner discoveredTuner: availableTuners)
|
||||
{
|
||||
String preferredName = discoveredTuner.getTuner().getPreferredName();
|
||||
|
||||
if(!preferredNames.contains(preferredName))
|
||||
{
|
||||
preferredNames.add(preferredName);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(preferredNames);
|
||||
|
||||
return preferredNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tuners currently available for use
|
||||
*/
|
||||
public List<DiscoveredTuner> getAvailableTuners()
|
||||
{
|
||||
return mDiscoveredTunerModel.getAvailableTuners();
|
||||
}
|
||||
|
||||
public Source getSource(SourceConfiguration config, ChannelSpecification channelSpecification) throws SourceException
|
||||
{
|
||||
Source retVal = null;
|
||||
|
||||
switch(config.getSourceType())
|
||||
{
|
||||
case MIXER:
|
||||
retVal = MixerManager.getSource(config);
|
||||
break;
|
||||
case TUNER:
|
||||
if(config instanceof SourceConfigTuner)
|
||||
{
|
||||
SourceConfigTuner sourceConfigTuner = (SourceConfigTuner)config;
|
||||
TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth());
|
||||
String preferredTuner = sourceConfigTuner.getPreferredTuner();
|
||||
retVal = getSource(tunerChannel, channelSpecification, preferredTuner);
|
||||
}
|
||||
break;
|
||||
case TUNER_MULTIPLE_FREQUENCIES:
|
||||
if(config instanceof SourceConfigTunerMultipleFrequency)
|
||||
{
|
||||
SourceConfigTunerMultipleFrequency sourceConfigTuner = (SourceConfigTunerMultipleFrequency)config;
|
||||
TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth());
|
||||
String preferredTuner = sourceConfigTuner.getPreferredTuner();
|
||||
|
||||
Source source = getSource(tunerChannel, channelSpecification, preferredTuner);
|
||||
|
||||
if(source instanceof TunerChannelSource)
|
||||
{
|
||||
retVal = new MultiFrequencyTunerChannelSource(this, (TunerChannelSource)source,
|
||||
sourceConfigTuner.getFrequencies(), channelSpecification, sourceConfigTuner.getPreferredTuner());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates current available tuners to get a tuner channel source for the specified frequency and bandwidth
|
||||
*
|
||||
* Returns null if no tuner can source the channel
|
||||
*/
|
||||
public Source getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification, String preferredTuner)
|
||||
{
|
||||
TunerChannelSource source = null;
|
||||
|
||||
if(tunerChannel != null && channelSpecification != null)
|
||||
{
|
||||
DiscoveredTuner discoveredTuner;
|
||||
|
||||
if(preferredTuner != null)
|
||||
{
|
||||
discoveredTuner = getDiscoveredTuner(preferredTuner);
|
||||
|
||||
if(discoveredTuner != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
source = discoveredTuner.getTuner().getChannelSourceManager().getSource(tunerChannel, channelSpecification);
|
||||
|
||||
if(source != null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Fall through to logger below
|
||||
}
|
||||
}
|
||||
|
||||
mLog.info("Unable to source channel [" + tunerChannel.getFrequency() + "] from preferred tuner [" +
|
||||
preferredTuner + "] - searching for another tuner");
|
||||
}
|
||||
|
||||
Iterator<DiscoveredTuner> it = mDiscoveredTunerModel.getAvailableTuners().iterator();
|
||||
|
||||
while(it.hasNext() && source == null)
|
||||
{
|
||||
discoveredTuner = it.next();
|
||||
|
||||
if(discoveredTuner.hasTuner())
|
||||
{
|
||||
try
|
||||
{
|
||||
source = discoveredTuner.getTuner().getChannelSourceManager().getSource(tunerChannel, channelSpecification);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error obtaining channel from tuner [" + discoveredTuner.getTuner().getPreferredName() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* USB hotplug event listener to register, unregister and detect when USB devices are added or removed.
|
||||
*
|
||||
* Note: hotplug is not supported on all platforms.
|
||||
*/
|
||||
public class HotplugEventSupport implements HotplugCallback
|
||||
{
|
||||
private static final int HOTPLUG_CONTINUE_EVENT_SUPPORT = 0;
|
||||
private HotplugCallbackHandle mHotplugCallbackHandle;
|
||||
private ScheduledFuture<?> mEventProcessorFuture;
|
||||
|
||||
/**
|
||||
* LibUsb hotplug event notification
|
||||
* @param context used for hotplug event registration
|
||||
* @param device that arrived or left
|
||||
* @param event value, arrived event or left event
|
||||
* @param userData not used
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int processEvent(Context context, Device device, int event, Object userData)
|
||||
{
|
||||
int bus = LibUsb.getBusNumber(device);
|
||||
int port = LibUsb.getPortNumber(device);
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case LibUsb.HOTPLUG_EVENT_DEVICE_ARRIVED:
|
||||
addUsbTuner(device);
|
||||
break;
|
||||
case LibUsb.HOTPLUG_EVENT_DEVICE_LEFT:
|
||||
removeUsbTuner(bus, port);
|
||||
break;
|
||||
}
|
||||
|
||||
return HOTPLUG_CONTINUE_EVENT_SUPPORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers for hotplug event notifications, if the capability is supported on this platform and starts an
|
||||
* event processing timer to process hotplug events.
|
||||
*/
|
||||
public void start()
|
||||
{
|
||||
if(LibUsb.hasCapability(LibUsb.CAP_HAS_HOTPLUG))
|
||||
{
|
||||
mHotplugCallbackHandle = new HotplugCallbackHandle();
|
||||
int events = LibUsb.HOTPLUG_EVENT_DEVICE_ARRIVED | LibUsb.HOTPLUG_EVENT_DEVICE_LEFT;
|
||||
int flags = LibUsb.HOTPLUG_ENUMERATE;
|
||||
int vendorId = LibUsb.HOTPLUG_MATCH_ANY;
|
||||
int productId = LibUsb.HOTPLUG_MATCH_ANY;
|
||||
int deviceClass = LibUsb.HOTPLUG_MATCH_ANY;
|
||||
|
||||
int status = LibUsb.hotplugRegisterCallback(mLibUsbApplicationContext, events, flags, vendorId,
|
||||
productId, deviceClass, this, "sdrtrunk hotplug support", mHotplugCallbackHandle);
|
||||
|
||||
if(status == LibUsb.SUCCESS)
|
||||
{
|
||||
Runnable eventHandler = () -> LibUsb.handleEvents(mLibUsbApplicationContext);
|
||||
mEventProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(eventHandler,
|
||||
0, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
mHotplugCallbackHandle = null;
|
||||
mLog.info("LibUsb - unable to register device hotplug listener - " + LibUsb.errorName(status));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info("LibUsb Hotplug event notification Is Not Supported on this platform.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters from hotplug event notifications and stops the event processing timer
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
if(mHotplugCallbackHandle != null)
|
||||
{
|
||||
LibUsb.hotplugDeregisterCallback(mLibUsbApplicationContext, mHotplugCallbackHandle);
|
||||
mHotplugCallbackHandle = null;
|
||||
}
|
||||
|
||||
if(mEventProcessorFuture != null)
|
||||
{
|
||||
mEventProcessorFuture.cancel(true);
|
||||
mEventProcessorFuture = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
mLog.info("Starting ...");
|
||||
UserPreferences userPreferences = new UserPreferences();
|
||||
TunerManager tunerManager = new TunerManager(userPreferences);
|
||||
tunerManager.start();
|
||||
|
||||
while(true);
|
||||
|
||||
// mLog.info("Finished!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.manager;
|
||||
|
||||
/**
|
||||
* Current status of a discovered tuner.
|
||||
*/
|
||||
public enum TunerStatus
|
||||
{
|
||||
/**
|
||||
* A tuner that is enabled and usable, but not currently in-use.
|
||||
*/
|
||||
ENABLED("Enabled"),
|
||||
|
||||
/**
|
||||
* A tuner that has been disabled or black-listed for use
|
||||
*/
|
||||
DISABLED("Disabled"),
|
||||
|
||||
/**
|
||||
* A tuner that has an error state and is unusable
|
||||
*/
|
||||
ERROR("Error");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param label
|
||||
*/
|
||||
TunerStatus(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this tuner is available and can be started
|
||||
*/
|
||||
public boolean isAvailable()
|
||||
{
|
||||
return this == ENABLED;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user