From 2d71f5173a0725356126a816fe323c9ed8307a1d Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sun, 9 Feb 2020 05:40:04 -0500 Subject: [PATCH] #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. --- .../channel/ChannelProcessingManager.java | 27 +- .../channelizer/PolyphaseChannelManager.java | 14 +- .../java/io/github/dsheirer/gui/SDRTrunk.java | 3 +- .../dsheirer/module/ProcessingChain.java | 22 +- .../github/dsheirer/source/SourceEvent.java | 48 ++- .../source/tuner/ITunerErrorListener.java | 32 ++ .../github/dsheirer/source/tuner/Tuner.java | 65 +++- .../source/tuner/TunerController.java | 23 +- .../dsheirer/source/tuner/TunerEvent.java | 23 ++ .../dsheirer/source/tuner/TunerModel.java | 63 +++- .../dsheirer/source/tuner/TunerViewPanel.java | 39 ++- .../tuner/airspy/AirspyTunerController.java | 65 ++-- .../tuner/channel/TunerChannelSource.java | 11 +- .../tuner/hackrf/HackRFTunerController.java | 71 ++-- .../tuner/manager/ChannelSourceManager.java | 37 +- .../HeterodyneChannelSourceManager.java | 39 ++- .../manager/PassThroughSourceManager.java | 17 +- .../PolyphaseChannelSourceManager.java | 40 ++- .../tuner/rtl/RTL2832TunerController.java | 46 +-- .../tuner/rtl/e4k/E4KTunerController.java | 34 +- .../source/tuner/usb/USBMasterProcessor.java | 112 ++++-- .../tuner/usb/USBTransferProcessor.java | 325 ++++++++++++------ 22 files changed, 799 insertions(+), 357 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/source/tuner/ITunerErrorListener.java diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java index 8e738d4d..0d45ee7e 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java @@ -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 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 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); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java index 33ee5d3f..92da1704 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java @@ -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). */ diff --git a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java index 2f48dedd..37c9a8a0 100644 --- a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java +++ b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java @@ -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 mAudioPacketManager.stop(); mLog.info("Stopping spectral display ..."); mSpectralPanel.clearTuner(); - mLog.info("Releasing tuners ..."); mSourceManager.shutdown(); mLog.info("Shutdown complete."); mApplicationLog.stop(); diff --git a/src/main/java/io/github/dsheirer/module/ProcessingChain.java b/src/main/java/io/github/dsheirer/module/ProcessingChain.java index 445291ca..ca757551 100644 --- a/src/main/java/io/github/dsheirer/module/ProcessingChain.java +++ b/src/main/java/io/github/dsheirer/module/ProcessingChain.java @@ -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 private Broadcaster mSourceEventBroadcaster = new Broadcaster<>(); private Broadcaster mMessageBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateEventBroadcaster = new Broadcaster<>(); - private AtomicBoolean mRunning = new AtomicBoolean(); - - protected Source mSource; private List 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 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 } } + /** + * Adds a listener to receive source events from this processing chain + */ + public void addSourceEventListener(Listener listener) + { + mSourceEventBroadcaster.addListener(listener); + } + /** * Removes the listener from receiving decoded messages from all decoders. */ diff --git a/src/main/java/io/github/dsheirer/source/SourceEvent.java b/src/main/java/io/github/dsheirer/source/SourceEvent.java index cb200e9d..8ea0169e 100644 --- a/src/main/java/io/github/dsheirer/source/SourceEvent.java +++ b/src/main/java/io/github/dsheirer/source/SourceEvent.java @@ -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 + * * ***************************************************************************** * - * 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 - * ***************************************************************************** */ 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 */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/ITunerErrorListener.java b/src/main/java/io/github/dsheirer/source/tuner/ITunerErrorListener.java new file mode 100644 index 00000000..4332783e --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/ITunerErrorListener.java @@ -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 + * * ***************************************************************************** + * + * + */ + +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); +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java index 745234ab..56b44997 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java @@ -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 + * * ***************************************************************************** * - * 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 - * ***************************************************************************** */ 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 : ""); } /** diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java index cdc0713a..d07d5939 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java @@ -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 + IReusableComplexBufferProvider, Listener, 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 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. * diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java b/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java index c7e9ca8b..d4a4afe0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java @@ -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 + * * ***************************************************************************** + * + * + */ + 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, diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java index e970a651..c159071e 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java @@ -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 it = mTuners.iterator(); - Tuner tuner; if(preferredTuner != null) @@ -402,17 +432,22 @@ public class TunerModel extends AbstractTableModel implements Listener 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); + } } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java index afdbc980..eecd0800 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java @@ -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; diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java index 37f599c6..4c493655 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java @@ -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 + * * ***************************************************************************** + * + * + */ + 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 - * Copyright (c) 2012, Jared Boone - * Copyright (c) 2014, Youssef Touil - * Copyright (c) 2014, Benjamin Vernoux - * Copyright (c) 2015, Ian Gilmour - * - * 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 diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java index ce22073f..b6e55d2a 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java @@ -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 diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java index e635b874..2696f234 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java @@ -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 + * * ***************************************************************************** * - * Copyright (c) 2012, Jared Boone - * Copyright (c) 2013, Benjamin Vernoux - * copyright (c) 2013, Michael Ossmann * - * 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 - ******************************************************************************/ + */ 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 diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java index 4d127180..cdef6b0d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java @@ -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 + * * ***************************************************************************** * - * 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 - * - ******************************************************************************/ + */ 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 */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java index 180bfc7c..e41983d8 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java @@ -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 + * * ***************************************************************************** * - * 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 - * - ******************************************************************************/ + */ 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 */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java index 1b4ebe1b..b01496a8 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java @@ -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 mTunerChannels = new TreeSet<>(); + private List 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 { @Override diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java index 4da3e143..680f01a3 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java @@ -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 + * * ***************************************************************************** * - * 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 - * - ******************************************************************************/ + */ 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 channels) { return channels.last().getMaxFrequency() - channels.first().getMinFrequency(); diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java index 8c062a1c..94211f51 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java @@ -1,29 +1,30 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* * - * Java version based on librtlsdr - * Copyright (C) 2012-2013 by Steve Markgraf - * Copyright (C) 2012 by Dimitri Stolnikov + * * ****************************************************************************** + * * 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 + * * ***************************************************************************** * - * 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 - ******************************************************************************/ + */ 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 diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java index 698d73b7..5a56fe94 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java @@ -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 + * * ***************************************************************************** * - * 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 - ******************************************************************************/ + */ 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 diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java index 91b3cf9a..bcab6755 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/usb/USBMasterProcessor.java @@ -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 + * * ***************************************************************************** * - * 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 - * - ******************************************************************************/ + */ 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 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); } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java index d48e4cac..d5571c50 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java @@ -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 + * * ***************************************************************************** * - * 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 - * ***************************************************************************** */ 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 mAvailableTransfers = new LinkedTransferQueue<>(); private LinkedTransferQueue mInProgressTransfers = new LinkedTransferQueue<>(); private LinkedTransferQueue mCompletedTransfers = new LinkedTransferQueue<>(); + private List mTransfersToDispose = new ArrayList<>(); private List 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 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); } } }