#652 USB Tuner Error Handling Enhancements (#653)

-Separates USB timeout event processing to a dedicated thread
-Updates USB transfer buffer processor to better detect errors and auto-restart on error (max 3 times)
-Updates Tuner to add an error state and SourceManager to not select tuners with an error state
-Updates ChannelProcessingManager to auto-shutdown channels sourced from tuners set with error state
-Improves logging of USB related errors.
This commit is contained in:
Denny Sheirer
2020-02-09 05:40:04 -05:00
committed by GitHub
parent 2e942113c6
commit 2d71f5173a
22 changed files with 799 additions and 357 deletions

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -47,6 +47,7 @@ import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.source.Source;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.SourceManager;
import org.slf4j.Logger;
@@ -262,6 +263,30 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
processingChain.addDecodeEventListener(listener);
}
//Add a listener to detect source error state that indicates the channel should be shutdown
processingChain.addSourceEventListener(sourceEvent ->
{
if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_ERROR_STATE && sourceEvent.getSource() != null)
{
Channel toShutdown = null;
for(Map.Entry<Channel,ProcessingChain> entry: mProcessingChains.entrySet())
{
if(entry.getValue().hasSource(sourceEvent.getSource()))
{
toShutdown = entry.getKey();
break;
}
}
if(toShutdown != null)
{
mLog.info("Channel source error detected - stopping channel [" + toShutdown.getName() + "]");
stopProcessing(toShutdown, true);
}
}
});
//Register this manager to receive channel events from traffic channel manager modules within
//the processing chain
processingChain.addChannelEventListener(this);

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -126,6 +126,18 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
this(tunerController, tunerController.getFrequency(), tunerController.getSampleRate());
}
/**
* Signals to all provisioned tuner channel sources that the source complex buffer provider has an error and can
* no longer provide channels, so that the tuner channel source can notify the consumer of the error state.
*/
public void setErrorMessage(String errorMessage)
{
for(TunerChannelSource tunerChannelSource: mChannelSources)
{
tunerChannelSource.setError(errorMessage);
}
}
/**
* Current channel sample rate which is (2 * channel bandwidth).
*/

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -474,7 +474,6 @@ public class SDRTrunk implements Listener<TunerEvent>
mAudioPacketManager.stop();
mLog.info("Stopping spectral display ...");
mSpectralPanel.clearTuner();
mLog.info("Releasing tuners ...");
mSourceManager.shutdown();
mLog.info("Shutdown complete.");
mApplicationLog.stop();

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -115,14 +115,12 @@ public class ProcessingChain implements Listener<ChannelEvent>
private Broadcaster<SourceEvent> mSourceEventBroadcaster = new Broadcaster<>();
private Broadcaster<IMessage> mMessageBroadcaster = new Broadcaster<>();
private Broadcaster<SquelchStateEvent> mSquelchStateEventBroadcaster = new Broadcaster<>();
private AtomicBoolean mRunning = new AtomicBoolean();
protected Source mSource;
private List<Module> mModules = new ArrayList<>();
private DecodeEventModel mDecodeEventModel;
private AbstractChannelState mChannelState;
private MessageActivityModel mMessageActivityModel;
protected Source mSource;
/**
* Creates a processing chain for managing a set of modules
@@ -208,6 +206,14 @@ public class ProcessingChain implements Listener<ChannelEvent>
return mSource != null;
}
/**
* Indicates if this chain's source is the same as the source argument
*/
public boolean hasSource(Source source)
{
return mSource != null && mSource.equals(source);
}
/**
* Applies a sample source to this processing chain. Processing won't
* start until the start() method is invoked.
@@ -800,6 +806,14 @@ public class ProcessingChain implements Listener<ChannelEvent>
}
}
/**
* Adds a listener to receive source events from this processing chain
*/
public void addSourceEventListener(Listener<SourceEvent> listener)
{
mSourceEventBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving decoded messages from all decoders.
*/

View File

@@ -1,21 +1,23 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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.
* * ******************************************************************************
* * 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 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;
@@ -39,6 +41,7 @@ public class SourceEvent
NOTIFICATION_RECORDING_FILE_LOADED,
NOTIFICATION_SAMPLE_RATE_CHANGE,
NOTIFICATION_STOP_SAMPLE_STREAM,
NOTIFICATION_ERROR_STATE,
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
REQUEST_FREQUENCY_CHANGE,
@@ -69,6 +72,14 @@ public class SourceEvent
mEventDescription = eventDescription;
}
/**
* Private constructor. Use the static constructor methods to create an event.
*/
private SourceEvent(Event event, Source source, String eventDescription)
{
this(event, source, null, eventDescription);
}
/**
* Private constructor. Use the static constructor methods to create an event.
*/
@@ -167,6 +178,15 @@ public class SourceEvent
return mSource != null;
}
/**
* Creates a new error state for the specified source and error description
*/
public static SourceEvent errorState(Source source, String errorDescription)
{
return new SourceEvent(Event.NOTIFICATION_ERROR_STATE, source, errorDescription);
}
/**
* Creates a new locked state change event
*/

View File

@@ -0,0 +1,32 @@
/*
*
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
*
*/
package io.github.dsheirer.source.tuner;
public interface ITunerErrorListener
{
/**
* Sets an error message for the tuner listener
* @param errorMessage to set
*/
void setErrorMessage(String errorMessage);
}

View File

@@ -1,21 +1,23 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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.
* * ******************************************************************************
* * 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 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;
@@ -36,7 +38,7 @@ import org.slf4j.LoggerFactory;
* Tuner provides an interface to a software or hardware tuner controller that provides I/Q sample data coupled with a
* channel source manager to provide access to Digital Drop Channel (DDC) resources.
*/
public abstract class Tuner implements ISourceEventProcessor
public abstract class Tuner implements ISourceEventProcessor, ITunerErrorListener
{
private final static Logger mLog = LoggerFactory.getLogger(Tuner.class);
@@ -45,6 +47,7 @@ public abstract class Tuner implements ISourceEventProcessor
private TunerController mTunerController;
private TunerFrequencyErrorMonitor mTunerFrequencyErrorMonitor;
private String mName;
private String mErrorMessage;
public Tuner(String name, TunerController tunerController)
{
@@ -52,7 +55,7 @@ public abstract class Tuner implements ISourceEventProcessor
mTunerController = tunerController;
//Register to receive frequency and sample rate change notifications
mTunerController.addListener(this::process);
mTunerController.setTunerErrorListener(this);
mTunerFrequencyErrorMonitor = new TunerFrequencyErrorMonitor(this);
mTunerFrequencyErrorMonitor.start();
}
@@ -94,6 +97,34 @@ public abstract class Tuner implements ISourceEventProcessor
mChannelSourceManager.addSourceEventListener(this::process);
}
/**
* Sets this tuner to an error state with the errorMessage description.
* @param errorMessage describing the error state
*/
public void setErrorMessage(String errorMessage)
{
mLog.info("[" + getName() + "] tuner is now disabled for error [" + errorMessage + "]");
mErrorMessage = errorMessage;
broadcast(new TunerEvent(this, Event.ERROR_STATE));
getChannelSourceManager().setErrorMessage(errorMessage);
}
/**
* Optional error message that describes an error state.
*/
public String getErrorMessage()
{
return mErrorMessage;
}
/**
* Indicates if this tuner has an error.
*/
public boolean hasError()
{
return mErrorMessage != null;
}
@Override
public void process(SourceEvent event)
{
@@ -148,7 +179,7 @@ public abstract class Tuner implements ISourceEventProcessor
*/
public String toString()
{
return mName;
return mName + (hasError() ? mErrorMessage : "");
}
/**

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory;
import java.util.SortedSet;
public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener,
IReusableComplexBufferProvider, Listener<ReusableComplexBuffer>
IReusableComplexBufferProvider, Listener<ReusableComplexBuffer>, ITunerErrorListener
{
private final static Logger mLog = LoggerFactory.getLogger(TunerController.class);
protected ReusableBufferBroadcaster mReusableBufferBroadcaster = new ReusableBufferBroadcaster();
@@ -52,6 +52,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
private Listener<SourceEvent> mSourceEventListener;
private int mMeasuredFrequencyError;
private ComplexBufferWaveRecorder mRecorder;
private ITunerErrorListener mTunerErrorListener;
/**
* Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels
@@ -77,6 +78,24 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
*/
public abstract void dispose();
@Override
public void setErrorMessage(String errorMessage)
{
if(mTunerErrorListener != null)
{
mTunerErrorListener.setErrorMessage(errorMessage);
}
}
/**
* Sets the listener for tuner error messages that originate outside of the tuner. This is normally the enclosing
* tuner so that it can receive error signals from the USB processor(s)..
*/
public void setTunerErrorListener(ITunerErrorListener tunerErrorListener)
{
mTunerErrorListener = tunerErrorListener;
}
/**
* Number of samples contained in each complex buffer provided by this tuner.
*

View File

@@ -1,3 +1,25 @@
/*
*
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
*
*/
package io.github.dsheirer.source.tuner;
public class TunerEvent
@@ -24,6 +46,7 @@ public class TunerEvent
public enum Event
{
CHANNEL_COUNT,
ERROR_STATE,
FREQUENCY_UPDATED,
FREQUENCY_ERROR_UPDATED,
LOCK_STATE_CHANGE,

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -58,9 +58,7 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
public static final int FREQUENCY_ERROR = 5;
public static final int MEASURED_FREQUENCY_ERROR = 6;
public static final int SPECTRAL_DISPLAY_NEW = 7;
private static final int MAXIMUM_RECORDING_TUNERS_ALLOWED = 1;
private static final String MHZ = " MHz";
private static final String[] COLUMNS = {"Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Current PPM",
"Measured Error", "Display"};
@@ -113,7 +111,7 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
{
for(Tuner tuner : mTuners)
{
if(tuner.getName().equalsIgnoreCase(name))
if(!tuner.hasError() && tuner.getName().equalsIgnoreCase(name))
{
return tuner;
}
@@ -279,6 +277,14 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
break;
case REQUEST_MAIN_SPECTRAL_DISPLAY:
fireTableCellUpdated(index, MEASURED_FREQUENCY_ERROR);
break;
case ERROR_STATE:
fireTableCellUpdated(index, CHANNEL_COUNT);
fireTableCellUpdated(index, FREQUENCY);
fireTableCellUpdated(index, FREQUENCY_ERROR);
fireTableCellUpdated(index, MEASURED_FREQUENCY_ERROR);
fireTableCellUpdated(index, SAMPLE_RATE);
break;
default:
break;
}
@@ -314,10 +320,17 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
case TUNER_ID:
return tuner.getUniqueID();
case SAMPLE_RATE:
if(tuner.hasError())
{
return tuner.getErrorMessage();
}
double sampleRate = tuner.getTunerController().getSampleRate();
return mSampleRateFormat.format(sampleRate / 1E6D) + MHZ;
case FREQUENCY:
if(tuner.hasError())
{
return tuner.getErrorMessage();
}
try
{
long frequency = tuner.getTunerController().getFrequency();
@@ -329,12 +342,24 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
return 0;
}
case CHANNEL_COUNT:
if(tuner.hasError())
{
return tuner.getErrorMessage();
}
int channelCount = tuner.getChannelSourceManager().getTunerChannelCount();
return channelCount + " (" + (tuner.getTunerController().isLocked() ? "LOCKED)" : "UNLOCKED)");
case FREQUENCY_ERROR:
if(tuner.hasError())
{
return tuner.getErrorMessage();
}
double ppm = tuner.getTunerController().getFrequencyCorrection();
return mFrequencyErrorPPMFormat.format(ppm);
case MEASURED_FREQUENCY_ERROR:
if(tuner.hasError())
{
return tuner.getErrorMessage();
}
if(tuner.getTunerController().hasMeasuredFrequencyError())
{
StringBuilder sb = new StringBuilder();
@@ -347,7 +372,14 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
return "";
case SPECTRAL_DISPLAY_NEW:
return "New";
if(tuner.hasError())
{
return null;
}
else
{
return "New";
}
default:
break;
}
@@ -373,8 +405,6 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
if(tunerChannel != null && channelSpecification != null)
{
Iterator<Tuner> it = mTuners.iterator();
Tuner tuner;
if(preferredTuner != null)
@@ -402,17 +432,22 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
preferredTuner + "] - searching for another tuner");
}
Iterator<Tuner> it = mTuners.iterator();
while(it.hasNext() && source == null)
{
tuner = it.next();
try
if(!tuner.hasError())
{
source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
}
catch(Exception e)
{
mLog.error("Error obtaining channel from tuner [" + tuner.getName() + "]", e);
try
{
source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
}
catch(Exception e)
{
mLog.error("Error obtaining channel from tuner [" + tuner.getName() + "]", e);
}
}
}
}

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -191,8 +191,15 @@ public class TunerViewPanel extends JPanel
}
});
TableCellRenderer renderer = new LinkCellRenderer();
mTunerTable.getColumnModel().getColumn(TunerModel.SPECTRAL_DISPLAY_NEW).setCellRenderer(renderer);
TableCellRenderer linkCellRenderer = new LinkCellRenderer();
mTunerTable.getColumnModel().getColumn(TunerModel.SPECTRAL_DISPLAY_NEW).setCellRenderer(linkCellRenderer);
TableCellRenderer errorCellRenderer = new ErrorCellRenderer();
mTunerTable.getColumnModel().getColumn(TunerModel.SAMPLE_RATE).setCellRenderer(errorCellRenderer);
mTunerTable.getColumnModel().getColumn(TunerModel.FREQUENCY).setCellRenderer(errorCellRenderer);
mTunerTable.getColumnModel().getColumn(TunerModel.CHANNEL_COUNT).setCellRenderer(errorCellRenderer);
mTunerTable.getColumnModel().getColumn(TunerModel.FREQUENCY_ERROR).setCellRenderer(errorCellRenderer);
mTunerTable.getColumnModel().getColumn(TunerModel.MEASURED_FREQUENCY_ERROR).setCellRenderer(errorCellRenderer);
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTunerTable, TABLE_PREFERENCE_KEY);
JScrollPane tunerTableScroller = new JScrollPane(mTunerTable);
@@ -238,6 +245,32 @@ public class TunerViewPanel extends JPanel
add(mSplitPane);
}
public class ErrorCellRenderer extends DefaultTableCellRenderer
{
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Tuner tuner = mTunerModel.getTuner(mTunerTable.convertRowIndexToModel(row));
if(isSelected)
{
component.setBackground(table.getSelectionBackground());
}
else if(tuner.hasError())
{
component.setBackground(Color.RED);
}
else
{
component.setBackground(table.getBackground());
}
return component;
}
}
public class LinkCellRenderer extends DefaultTableCellRenderer
{
private static final long serialVersionUID = 1L;

View File

@@ -1,3 +1,25 @@
/*
*
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
*
*/
package io.github.dsheirer.source.tuner.airspy;
import io.github.dsheirer.source.SourceException;
@@ -20,47 +42,6 @@ import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* SDR Trunk
* Copyright (C) 2015-2018 Dennis Sheirer
*
* -----------------------------------------------------------------------------
* Ported from libairspy at:
* https://github.com/airspy/host/tree/master/libairspy
*
* Copyright (c) 2013, Michael Ossmann <mike@ossmann.com>
* Copyright (c) 2012, Jared Boone <jared@sharebrained.com>
* Copyright (c) 2014, Youssef Touil <youssef@airspy.com>
* Copyright (c) 2014, Benjamin Vernoux <bvernoux@airspy.com>
* Copyright (c) 2015, Ian Gilmour <ian@sdrsharp.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of AirSpy nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
public class AirspyTunerController extends USBTunerController
{
public static final Gain LINEARITY_GAIN_DEFAULT = Gain.LINEARITY_14;
@@ -184,7 +165,7 @@ public class AirspyTunerController extends USBTunerController
String deviceName = "Airspy " + getDeviceInfo().getSerialNumber();
mUSBTransferProcessor = new USBTransferProcessor(deviceName, mDeviceHandle, mSampleAdapter,
USB_TRANSFER_BUFFER_SIZE);
USB_TRANSFER_BUFFER_SIZE, this);
}
@Override

View File

@@ -1,7 +1,7 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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
@@ -65,6 +65,15 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
return mTunerChannel.getFrequency();
}
/**
* Signals that this tuner channel source has an error state so that any channel processing can be shutdown.
* @param errorMessage describing the error
*/
public void setError(String errorMessage)
{
broadcastConsumerSourceEvent(SourceEvent.errorState(this, errorMessage));
}
/**
* Sets the center frequency for the sample streaming being sent from the producer.
* @param frequency in hertz

View File

@@ -1,56 +1,24 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2017 Dennis Sheirer
/*
*
* Ported from libhackrf at:
* https://github.com/mossmann/hackrf/tree/master/host/libhackrf
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* Copyright (c) 2012, Jared Boone <jared@sharebrained.com>
* Copyright (c) 2013, Benjamin Vernoux <titanmkd@gmail.com>
* copyright (c) 2013, Michael Ossmann <mike@ossmann.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Great Scott Gadgets nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* 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.hackrf;
import io.github.dsheirer.source.SourceException;
@@ -160,7 +128,8 @@ public class HackRFTunerController extends USBTunerController
name = "HackRF - Unidentified Serial";
}
mUSBTransferProcessor = new USBTransferProcessor(name, mDeviceHandle, mNativeBufferConverter, USB_TRANSFER_BUFFER_SIZE);
mUSBTransferProcessor = new USBTransferProcessor(name, mDeviceHandle, mNativeBufferConverter,
USB_TRANSFER_BUFFER_SIZE, this);
}
@Override

View File

@@ -1,18 +1,24 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 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.
* * ******************************************************************************
* * 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 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.sample.Broadcaster;
@@ -56,6 +62,13 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor
*/
public abstract TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification);
/**
* Signals that the complex buffer provider has an error and can no long provider buffers. The subclass should
* implement procedures to gracefully shutdown any tuner channel sources that have been provisioned.
* @param errorMessage describing the error state
*/
public abstract void setErrorMessage(String errorMessage);
/**
* Adds a listener to receive source events
*/

View File

@@ -1,18 +1,24 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 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.
* * ******************************************************************************
* * 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 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.dsp.filter.design.FilterDesignException;
@@ -103,6 +109,15 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
return null;
}
@Override
public void setErrorMessage(String errorMessage)
{
for(TunerChannelSource tunerChannelSource: mChannelSources)
{
tunerChannelSource.setError(errorMessage);
}
}
/**
* Calculates a new center frequency and updates the tuner center frequency
*/

View File

@@ -33,17 +33,21 @@ import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* Pass through source manager for relatively small bandwidth complex streams that don't warrant the overhead of
* a polyphase or heterodyne channelizer.
*/
public class PassThroughSourceManager extends ChannelSourceManager
{
private final static Logger mLog = LoggerFactory.getLogger(PassThroughSourceManager.class);
private TunerController mTunerController;
private SortedSet<TunerChannel> mTunerChannels = new TreeSet<>();
private List<TunerChannelSource> mTunerChannelSources = new CopyOnWriteArrayList<>();
public PassThroughSourceManager(TunerController tunerController)
{
@@ -69,6 +73,7 @@ public class PassThroughSourceManager extends ChannelSourceManager
mTunerController, tunerChannel);
mTunerChannels.add(tunerChannel);
mTunerChannelSources.add(channelSource);
return channelSource;
}
@@ -91,12 +96,22 @@ public class PassThroughSourceManager extends ChannelSourceManager
PassThroughChannelSource source = (PassThroughChannelSource)event.getSource();
mTunerController.removeBufferListener(source);
mTunerChannels.remove(source.getTunerChannel());
mTunerChannelSources.remove(source);
broadcast(SourceEvent.channelCountChange(mTunerChannels.size()));
}
break;
}
}
@Override
public void setErrorMessage(String errorMessage)
{
for(TunerChannelSource tunerChannelSource: mTunerChannelSources)
{
tunerChannelSource.setError(errorMessage);
}
}
public class SourceEventProxy implements Listener<SourceEvent>
{
@Override

View File

@@ -1,18 +1,24 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 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.
* * ******************************************************************************
* * 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 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.dsp.filter.channelizer.PolyphaseChannelManager;
@@ -259,6 +265,16 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
throw new IllegalArgumentException("Can't calculate valid center frequency for the channel set");
}
@Override
public void setErrorMessage(String errorMessage)
{
if(mPolyphaseChannelManager != null)
{
mPolyphaseChannelManager.setErrorMessage(errorMessage);
}
}
private long getChannelSetBandwidth(SortedSet<TunerChannel> channels)
{
return channels.last().getMaxFrequency() - channels.first().getMinFrequency();

View File

@@ -1,29 +1,30 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
*
* Java version based on librtlsdr
* Copyright (C) 2012-2013 by Steve Markgraf <steve@steve-m.de>
* Copyright (C) 2012 by Dimitri Stolnikov <horiz0n@gmx.net>
* * ******************************************************************************
* * 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.rtl;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.ITunerErrorListener;
import io.github.dsheirer.source.tuner.TunerManager;
import io.github.dsheirer.source.tuner.TunerType;
import io.github.dsheirer.source.tuner.usb.USBTransferProcessor;
@@ -160,7 +161,7 @@ public abstract class RTL2832TunerController extends USBTunerController
String deviceName = getTunerType().getLabel() + " " + getUniqueID();
mUSBTransferProcessor = new RTL2832USBTransferProcessor(deviceName, mDeviceHandle, mNativeBufferConverter,
USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE);
USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE, this);
}
@Override
@@ -1563,10 +1564,13 @@ public abstract class RTL2832TunerController extends USBTunerController
* @param deviceHandle to the USB bulk transfer device
* @param nativeBufferConverter specific to the tuner's byte buffer format for converting to floating point I/Q samples
* @param bufferSize in bytes. Should be a multiple of two: 65536, 131072 or 262144.
* @param tunerErrorListener to receive error state message
*/
public RTL2832USBTransferProcessor(String deviceName, DeviceHandle deviceHandle, NativeBufferConverter nativeBufferConverter, int bufferSize)
public RTL2832USBTransferProcessor(String deviceName, DeviceHandle deviceHandle,
NativeBufferConverter nativeBufferConverter, int bufferSize,
ITunerErrorListener tunerErrorListener)
{
super(deviceName, deviceHandle, nativeBufferConverter, bufferSize);
super(deviceName, deviceHandle, nativeBufferConverter, bufferSize, tunerErrorListener);
}
@Override

View File

@@ -1,20 +1,24 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 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.
* * ******************************************************************************
* * 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 distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
*/
package io.github.dsheirer.source.tuner.rtl.e4k;
import io.github.dsheirer.source.SourceException;
@@ -253,7 +257,7 @@ public class E4KTunerController extends RTL2832TunerController
String deviceName = getTunerType().getLabel() + " " + getUniqueID();
mUSBTransferProcessor = new RTL2832USBTransferProcessor(deviceName, mDeviceHandle, mNativeBufferConverter,
USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE);
USB_TRANSFER_BUFFER_SIZE_HIGH_SAMPLE_RATE, this);
}
@Override

View File

@@ -1,30 +1,32 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 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.
* * ******************************************************************************
* * 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 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.usb;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.usb4java.LibUsb;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -38,10 +40,9 @@ public class USBMasterProcessor
private static final long USB_TIMEOUT_MS = 2000l; //milliseconds
private List<USBTransferProcessor> mRegisteredProcessors = new CopyOnWriteArrayList<>();
private ByteBuffer mStatusBuffer = ByteBuffer.allocateDirect(4);
private AtomicBoolean mRunning = new AtomicBoolean();
private Processor mProcessor = new Processor();
private ScheduledFuture mProcessorFuture;
private Thread mProcessorThread;
/**
* Registers the transfer processor so that LibUSB timeout processing will auto-start.
@@ -80,8 +81,25 @@ public class USBMasterProcessor
{
if(mRunning.compareAndSet(false, true))
{
//Set periodicity to an odd multiple to avoid contention with transfer buffer receivers
mProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mProcessor, 0L, 9L, TimeUnit.MILLISECONDS);
mLog.warn("Starting USB master processor thread");
if(mProcessorThread == null)
{
mProcessor.reset();
mProcessorThread = new Thread(mProcessor);
mProcessorThread.setName("USB Event Processor");
mProcessorThread.setDaemon(true);
try
{
mProcessorThread.setPriority(Thread.MAX_PRIORITY);
}
catch(IllegalArgumentException iae)
{
//Do nothing ... couldn't set the priority
}
mProcessorThread.start();
}
}
}
@@ -92,10 +110,22 @@ public class USBMasterProcessor
{
if(mRunning.compareAndSet(true, false))
{
if(mProcessorFuture != null)
mLog.warn("Stopping USB master processor thread");
if(mProcessorThread != null)
{
mProcessorFuture.cancel(true);
mProcessorFuture = null;
mProcessor.stop();
mProcessorThread.interrupt();
try
{
mProcessorThread.join(1000);
}
catch(InterruptedException ie)
{
//No action
}
mProcessorThread = null;
}
}
}
@@ -114,23 +144,37 @@ public class USBMasterProcessor
*/
class Processor implements Runnable
{
private boolean mRunning = true;
/**
* Sets stop flag to signal to stop processing
*/
public void stop()
{
mRunning = false;
}
/**
* Resets this processor so that it can be reused.
*/
public void reset()
{
mRunning = true;
}
@Override
public void run()
{
try
while(mRunning)
{
int result = LibUsb.handleEventsTimeoutCompleted(null, USB_TIMEOUT_MS, mStatusBuffer.asIntBuffer());
if(result != LibUsb.SUCCESS)
try
{
mLog.error("Error processing timeout events for LibUSB - error code:" + result);
LibUsb.handleEvents(null);
}
catch(Throwable throwable)
{
mLog.error("Error while processing LibUSB timeout events", throwable);
}
mStatusBuffer.rewind();
}
catch(Throwable throwable)
{
mLog.error("Error while processing LibUSB timeout events", throwable);
}
}
}

View File

@@ -1,26 +1,29 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.
* * ******************************************************************************
* * 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 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.usb;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.source.tuner.ITunerErrorListener;
import io.github.dsheirer.source.tuner.TunerManager;
import io.github.dsheirer.source.tuner.usb.converter.NativeBufferConverter;
import io.github.dsheirer.util.ThreadPool;
@@ -53,6 +56,7 @@ public class USBTransferProcessor implements TransferCallback
private LinkedTransferQueue<Transfer> mAvailableTransfers = new LinkedTransferQueue<>();
private LinkedTransferQueue<Transfer> mInProgressTransfers = new LinkedTransferQueue<>();
private LinkedTransferQueue<Transfer> mCompletedTransfers = new LinkedTransferQueue<>();
private List<Transfer> mTransfersToDispose = new ArrayList<>();
private List<Transfer> mTransfersToSubmit = new ArrayList<>();
//Tuner format-specific byte buffer to IQ float sample converter
@@ -66,14 +70,13 @@ public class USBTransferProcessor implements TransferCallback
//Handle to the USB bulk transfer device
private DeviceHandle mUsbBulkTransferDeviceHandle;
private AtomicBoolean mRunning = new AtomicBoolean();
private ByteBuffer mLibUsbHandlerStatus = ByteBuffer.allocateDirect(4);
private AtomicBoolean mRestarting = new AtomicBoolean();
private CompletedTransferProcessor mCompletedTransferProcessor = new CompletedTransferProcessor();
private ScheduledFuture mBufferDispatcherFuture;
private ScheduledFuture mRestartFuture;
private String mDeviceName;
private int mTransferErrorLoggingCount = 0;
private int mRestartCount = 0;
private ITunerErrorListener mITunerErrorListener;
/**
* Manages stream of USB transfer buffers and converts buffers to complex buffer samples for distribution to
@@ -85,12 +88,14 @@ public class USBTransferProcessor implements TransferCallback
* @param bufferSize in bytes. Should be a multiple of two: 65536, 131072 or 262144.
*/
public USBTransferProcessor(String deviceName, DeviceHandle usbBulkTransferDeviceHandle,
NativeBufferConverter nativeBufferConverter, int bufferSize)
NativeBufferConverter nativeBufferConverter, int bufferSize,
ITunerErrorListener tunerErrorListener)
{
mDeviceName = deviceName;
mUsbBulkTransferDeviceHandle = usbBulkTransferDeviceHandle;
mNativeBufferConverter = nativeBufferConverter;
mBufferSize = bufferSize;
mITunerErrorListener = tunerErrorListener;
}
/**
@@ -121,23 +126,31 @@ public class USBTransferProcessor implements TransferCallback
/**
* Start USB transfer buffer processing. Subsequent calls to this method after started will be ignored.
*
* @return true if start successful with no errors or is already running. returns false if errors on startup.
*/
private void start()
private boolean start()
{
boolean success = false;
if(mRunning.compareAndSet(false, true))
{
prepareDeviceStart();
prepareTransfers();
submitTransfers();
//Start transferred buffer dispatcher
mBufferDispatcherFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mCompletedTransferProcessor,
0, 6, TimeUnit.MILLISECONDS);
if(submitTransfers())
{
success = true;
//Start transferred buffer dispatcher
mBufferDispatcherFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mCompletedTransferProcessor,
0, 6, TimeUnit.MILLISECONDS);
//Register with LibUSB processor so that it auto-starts LibUSB processing
TunerManager.LIBUSB_TRANSFER_PROCESSOR.registerTransferProcessor(this);
//Register with LibUSB processor so that it auto-starts LibUSB processing
TunerManager.LIBUSB_TRANSFER_PROCESSOR.registerTransferProcessor(this);
}
}
return success;
}
/**
@@ -147,7 +160,11 @@ public class USBTransferProcessor implements TransferCallback
{
if(mRunning.compareAndSet(true, false))
{
mBufferDispatcherFuture.cancel(true);
if(mBufferDispatcherFuture != null)
{
mBufferDispatcherFuture.cancel(true);
mBufferDispatcherFuture = null;
}
//Cancel all buffers that are currently in progress
for(Transfer transfer : mInProgressTransfers)
@@ -155,22 +172,37 @@ public class USBTransferProcessor implements TransferCallback
LibUsb.cancelTransfer(transfer);
}
//Await completion of all in-progress transfers
int waitCycleCount = 0;
while(!mInProgressTransfers.isEmpty() && waitCycleCount < 10)
{
waitCycleCount++;
try
{
Thread.sleep(10);
}
catch(InterruptedException ie)
{
//No - action
}
}
//Clear all completed buffers
Transfer completedTransfer = mCompletedTransfers.poll();
while(completedTransfer != null)
{
completedTransfer.buffer().rewind();
mAvailableTransfers.add(completedTransfer);
completedTransfer = mCompletedTransfers.poll();
}
//Unregister from LibUSB processor so that it auto-stops LibUSB processing
//Unregister from LibUSB processor so that it auto-stops LibUSB event timeout processing
TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(this);
mLibUsbHandlerStatus.rewind();
executeDeviceStop();
disposeTransfers();
}
}
@@ -180,28 +212,44 @@ public class USBTransferProcessor implements TransferCallback
*/
private void restart()
{
mLog.debug("Stopping for a restart ....");
stop();
mLog.debug("Attempting to clear halt condition ...");
//Attempt to clear any halt condition
LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT);
mLog.warn("USB tuner [" + mDeviceName + "] stopped due to a buffer transfer error. Restarting in 10 milliseconds");
if(mRestartFuture == null)
if(mRestarting.compareAndSet(false, true))
{
Runnable runnable = new Runnable()
stop();
if(mRestartCount >= 3)
{
@Override
public void run()
mLog.warn("[" + mDeviceName + "] - maximum restart attempts [" + mRestartCount +
"] exceeded for tuner - stopping");
if(mITunerErrorListener != null)
{
start();
mRestartFuture = null;
mITunerErrorListener.setErrorMessage("USB ERROR");
}
return;
}
mRestartCount++;
//Attempt to clear any halt condition
LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT);
mLog.info("[" + mDeviceName + "] - restart attempt [" + mRestartCount + "]");
Runnable runnable = () ->
{
mRestarting.set(false);
boolean success = start();
if(!success)
{
mLog.warn("[" + mDeviceName + "] - restart attempt [" + mRestartCount + "] failed - retrying");
restart();
}
};
mRestartFuture = ThreadPool.SCHEDULED.schedule(runnable, 10, TimeUnit.MILLISECONDS);
ThreadPool.SCHEDULED.schedule(runnable, 20, TimeUnit.MILLISECONDS);
}
}
@@ -210,56 +258,85 @@ public class USBTransferProcessor implements TransferCallback
* to another thread, there may be times when sufficient transfer buffers are not available to keep a level number
* of transfer buffers in progress. Therefore, we track the transfer buffers to submit deficit number and attempt
* to play catch-up each time this method is invoked.
*
* @return boolean true if there were no errors submitting transfer buffers. A false value indicates that there
* were errors and that the device likely needs to be reset.
*/
private void submitTransfers()
private boolean submitTransfers()
{
if(mRunning.get())
{
mAvailableTransfers.drainTo(mTransfersToSubmit);
for(Transfer transfer: mTransfersToSubmit)
if(!mTransfersToSubmit.isEmpty())
{
int result = LibUsb.submitTransfer(transfer);
Transfer transfer = mTransfersToSubmit.remove(0);
if(result == LibUsb.SUCCESS)
while(transfer != null)
{
mInProgressTransfers.add(transfer);
}
else if(result == LibUsb.ERROR_PIPE)
{
mLog.warn("USB pipe error - attempting to clear halt on USB device [" + mDeviceName + "]");
int status = LibUsb.submitTransfer(transfer);
int resetResult = LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT);
if(resetResult == LibUsb.SUCCESS)
switch(status)
{
int resubmitResult = LibUsb.submitTransfer(transfer);
if(resubmitResult == LibUsb.SUCCESS)
{
case LibUsb.SUCCESS:
mInProgressTransfers.add(transfer);
}
else
{
mAvailableTransfers.add(transfer);
mLog.error(mDeviceName + " - error resubmitting transfer after endpoint clear halt");
}
if(!mTransfersToSubmit.isEmpty())
{
transfer = mTransfersToSubmit.remove(0);
}
else
{
transfer = null;
}
break;
case LibUsb.ERROR_PIPE:
mLog.warn("[" + mDeviceName + "] - USB Pipe Error while submitting transfer buffers");
LibUsb.clearHalt(mUsbBulkTransferDeviceHandle, USB_BULK_TRANSFER_ENDPOINT);
mTransfersToDispose.add(transfer);
transfer = null;
break;
default:
mLog.error("[" + mDeviceName + "] - " + getErrorStatus(status) + " while submitting transfer buffers");
mTransfersToDispose.add(transfer);
transfer = null;
break;
}
else
{
mAvailableTransfers.add(transfer);
mLog.error(mDeviceName + " - unable to clear device endpoint halt");
}
}
else
{
mAvailableTransfers.add(transfer);
mLog.error(mDeviceName + "- error submitting transfer [" + LibUsb.errorName(result) + "]");
}
}
mTransfersToSubmit.clear();
//If we aborted submitting transfers or if we had an error while submitting, move the remaining transfers
// back to the available queue and restart the device
if(!mTransfersToSubmit.isEmpty())
{
mAvailableTransfers.addAll(mTransfersToSubmit);
mTransfersToSubmit.clear();;
}
}
return mTransfersToDispose.isEmpty();
}
/**
* Frees all allocated transfers in preparation for shutdown
*/
private void disposeTransfers()
{
mAvailableTransfers.drainTo(mTransfersToDispose);
if(mTransfersToDispose.size() != TRANSFER_BUFFER_POOL_SIZE)
{
mLog.warn("[" + mDeviceName + "] - potential memory leak [" +
mTransfersToDispose.size() + " of " + TRANSFER_BUFFER_POOL_SIZE +
"] USB transfer buffers will be disposed during shutdown");
}
for(Transfer transfer: mTransfersToDispose)
{
LibUsb.freeTransfer(transfer);
}
mTransfersToDispose.clear();
}
/**
@@ -283,8 +360,17 @@ public class USBTransferProcessor implements TransferCallback
*/
public void setListener(Listener<ReusableComplexBuffer> listener)
{
mComplexBufferListener = listener;
start();
if(mComplexBufferListener == null || !mComplexBufferListener.equals(listener))
{
mComplexBufferListener = listener;
boolean success = start();
if(!success)
{
restart();
}
}
}
/**
@@ -359,21 +445,19 @@ public class USBTransferProcessor implements TransferCallback
{
transfer.buffer().rewind();
mAvailableTransfers.add(transfer);
}
mTransferErrorLoggingCount++;
mTransferErrorLoggingCount++;
if(mTransferErrorLoggingCount <= 5)
{
//TODO: this error situation should be raised/highlighted to the user for action in the gui ...
mLog.warn("USB buffer transfer error detected. You should try using a different USB port for the usb-based tuner");
//Handle integer overflow
if(mTransferErrorLoggingCount < 0)
if(mTransferErrorLoggingCount <= 5)
{
mTransferErrorLoggingCount = 1;
//Handle integer overflow
if(mTransferErrorLoggingCount < 0)
{
mTransferErrorLoggingCount = 1;
}
}
}
break;
case LibUsb.TRANSFER_CANCELLED:
transfer.buffer().rewind();
@@ -381,7 +465,7 @@ public class USBTransferProcessor implements TransferCallback
break;
default:
//Unexpected transfer error - need to reset the bulk transfer interface
mLog.error(mDeviceName + " - transfer error [" + getTransferStatus(transfer.status()) +
mLog.error("[" + mDeviceName + "] - transfer error [" + getTransferStatus(transfer.status()) +
"] transferred actual: " + transfer.actualLength());
transfer.buffer().rewind();
mAvailableTransfers.add(transfer);
@@ -390,6 +474,44 @@ public class USBTransferProcessor implements TransferCallback
}
}
/**
* Converts the error status code to a textual description
*/
public static String getErrorStatus(int status)
{
switch(status)
{
case LibUsb.SUCCESS:
return "SUCCESS";
case LibUsb.ERROR_IO:
return "USB I/O ERROR";
case LibUsb.ERROR_INVALID_PARAM:
return "USB INVALID PARAMETERS ERROR";
case LibUsb.ERROR_ACCESS:
return "USB ACCESS ERROR";
case LibUsb.ERROR_NO_DEVICE:
return "USB NO DEVICE ERROR";
case LibUsb.ERROR_NOT_FOUND:
return "USB NOT FOUND ERROR";
case LibUsb.ERROR_BUSY:
return "USB BUSY ERROR";
case LibUsb.ERROR_TIMEOUT:
return "USB TIMEOUT ERROR";
case LibUsb.ERROR_OVERFLOW:
return "USB OVERFLOW ERROR";
case LibUsb.ERROR_PIPE:
return "USB PIPE ERROR";
case LibUsb.ERROR_INTERRUPTED:
return "USB INTERRUPTED ERROR";
case LibUsb.ERROR_NO_MEM:
return "USB NO MEMORY ERROR";
case LibUsb.ERROR_NOT_SUPPORTED:
return "USB NOT SUPPORTED ERROR";
default:
return "USB UNKNOWN [" + status + "] ERROR";
}
}
/**
* Converts the USB transfer status number into a descriptive label
*/
@@ -447,15 +569,22 @@ public class USBTransferProcessor implements TransferCallback
transfer.buffer().rewind();
mAvailableTransfers.add(transfer);
submitTransfers();
boolean success = submitTransfers();
//Fetch the next transfer to process
transfer = mCompletedTransfers.poll();
if(success)
{
transfer = mCompletedTransfers.poll();
}
else
{
ThreadPool.SCHEDULED.submit(() -> restart());
transfer = null;
}
}
}
catch(Throwable throwable)
{
mLog.error("Error while processing USB transfer buffers", throwable);
mLog.error("[" + mDeviceName + "] - error while processing USB transfer buffers", throwable);
}
}
}