Compare commits

..

14 Commits

Author SHA1 Message Date
denny
03e641525c Updating version to 0.2.0 (final). 2016-03-29 20:11:41 -04:00
denny
f2b189ee2b Adds support to TunerManager for the RAD1O (HackRF compatible) tuner. 2016-03-21 18:39:59 -04:00
denny
3f0581dad1 Adds support for Munich hackerspace Rad1o tuner - a HackRF compatible
tuner.
2016-03-21 18:33:56 -04:00
denny
daf2b58cf2 Updates build to Version 0.2.0 Beta 8 2016-02-27 06:21:51 -05:00
denny
cf2c0ca07d Resolves significant memory usage issue - updates Airspy and HackRF with
the recent changes applied to RTL2832 Tuner controller for processing of
libusb transfer buffers.
2016-02-27 06:19:35 -05:00
denny
eead22ffad Issue #59 - more changes to improve handling of transfer buffers between
tuner start/stop iterations.
2016-02-26 14:52:31 -05:00
denny
1550c62782 Adds null checking to channel state panel. 2016-02-26 14:37:12 -05:00
denny
5f2d8c8fe6 Issue #59 - reorganizes management of the libusb transfers queue to
better handle processing and shutdowns.

-Reuses transfers between tuner start/stop invocations

-Moves libusb transfer status direct byte buffer allocation out of
buffer processing thread and reuses it, saving a lot of memory
allocations over time.
2016-02-26 14:35:34 -05:00
denny
22d3871d0b Restructures transfer buffer handling to better handle shutdown and
canceling of transfers across multiple threads.
2016-02-26 13:28:59 -05:00
denny
455e45269c Issue #58 - DFT processor queue - removed logging that indicates the
queue is full and samples are being purged.
2016-02-26 10:08:52 -05:00
denny
ca8406bdc0 Resolves #58 - DFT processor sample queue may fill up over time causing
the spectral display to be slow to reflect changes to tuner settings.

Changed the max size of the sample queue based on the highest rate tuner
sample rate 10 MHz (airspy) and the lowest available spectral update
frame rate (12).  Sized the buffer queue based on the maximum number of
buffers that should be available per DFT processing iteration.  If the
queue backs up, new buffers will be discarded until the DFT processor
consumes the queue and catches up.
2016-02-26 06:35:03 -05:00
denny
399d164bd0 Resolves #57 - default tuner configuration per tuner type was being
shared across multiple tuners of the same type, unless the user created
a new tuner configuration for each tuner. 

Updated code to create a default tuner configuration based on the tuner
type, but name the configuration as 'Default-UNIQUETUNERID', so that any
changes to that uniquely identified tuner will persist across sessions.

Note: this does not avoid the situation where multiple tuners of the
same type are all using the same default serial number (e.g. 0000001).
In this case, they will all still share the same configuraiton.  User
must change the serial number of the dongle in order for it to be
uniquely identifiable, so that we can correctly lookup the configuration
for the specific tuner.
2016-02-26 06:07:21 -05:00
denny
6f2a7e89a9 Resolves issue with RTL2832 Tuner cancelling transfer buffers that
haven't yet been initialized.  This occurs when there is a channel
allocated from the tuner that is the first/only listener and then is
immediately released.  Some of the buffers are in motion and the others
simply haven't yet been initialized.
2016-02-22 20:49:20 -05:00
denny
b821588e54 Removes debug message from trafficchannelmanager 2016-02-20 17:20:15 -05:00
11 changed files with 391 additions and 348 deletions

View File

@@ -7,8 +7,8 @@
<property name="major" value="0"/>
<property name="minor" value="2"/>
<property name="patch" value="0"/>
<property name="release" value="beta.7"/>
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}-${release}"/>
<property name="release" value=""/>
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
<!-- Build properties -->
<property name="repo" location=".."/>

View File

@@ -202,7 +202,7 @@ public class ChannelStatePanel extends JPanel
switch( changedAttribute )
{
case CHANNEL_STATE:
if( state.getState() != null )
if( state != null && state.getState() != null )
{
mStateLabel.setText( state.getState().getDisplayValue() );
}

View File

@@ -91,7 +91,6 @@ public class TrafficChannelManager extends Module
@Override
public void dispose()
{
mLog.debug( "dispose() on traffic channel manager" );
for( Channel trafficChannel: mTrafficChannelPool )
{
trafficChannel.setEnabled( false );

View File

@@ -557,11 +557,6 @@ public class SettingsManager
ArrayList<TunerConfiguration> configs = getSettings()
.getTunerConfigurations( type );
if( configs.isEmpty() )
{
configs.add( addNewTunerConfiguration( type, "Default" ) );
}
return configs;
}

View File

@@ -36,6 +36,7 @@ public enum TunerClass
GIGABYTE_GTU7300( TunerType.FITIPOWER_FC0012, "1B80", "D393", "Gigabyte", "GT-U7300" ),
GTEK_T803( TunerType.FITIPOWER_FC0012, "1F4D", "B803", "GTek", "T803" ),
HACKRF_ONE( TunerType.HACKRF, "1D50", "6089", "Great Scott Gadgets", "HackRF One" ),
RAD1O( TunerType.HACKRF, "1D50", "CC15", "Munich hackerspace", "Rad1o" ),
LIFEVIEW_LV5T_DELUXE( TunerType.FITIPOWER_FC0012, "1F4D", "C803", "Liveview", "LV5T Deluxe" ),
MYGICA_TD312( TunerType.FITIPOWER_FC0012, "1F4D", "D286", "MyGica", "TD312" ),
PEAK_102569AGPK( TunerType.FITIPOWER_FC0012, "1B80", "D395", "Peak", "102569AGPK" ),
@@ -185,6 +186,10 @@ public enum TunerClass
{
retVal = AIRSPY;
}
else if( productID == 52245 ) //CC15
{
retVal = HACKRF_ONE;
}
break;
case 8013: //1F4D
if( productID == 47107 ) //B803

View File

@@ -218,6 +218,7 @@ public class TunerManager
case FUNCUBE_DONGLE_PRO_PLUS:
return initFuncubeProPlusTuner( device, descriptor );
case HACKRF_ONE:
case RAD1O:
return initHackRFTuner( device, descriptor );
case COMPRO_VIDEOMATE_U620F:
case COMPRO_VIDEOMATE_U650F:
@@ -544,16 +545,14 @@ public class TunerManager
}
}
}
else
{
config = configs.get( 0 );
}
//If we're still null at this point, create a default config
if( config == null )
{
String name = "Default-" + tuner.getUniqueID();
config = mResourceManager.getSettingsManager()
.addNewTunerConfiguration( tuner.getTunerType(), "Default" );
.addNewTunerConfiguration( tuner.getTunerType(), name );
}
//Persist the config as the selected tuner configuration. The method

View File

@@ -262,7 +262,30 @@ public class AirspyTunerController extends TunerController
}
}
@Override
public static String getTransferStatus( int status )
{
switch( status )
{
case 0:
return "TRANSFER COMPLETED (0)";
case 1:
return "TRANSFER ERROR (1)";
case 2:
return "TRANSFER TIMED OUT (2)";
case 3:
return "TRANSFER CANCELLED (3)";
case 4:
return "TRANSFER STALL (4)";
case 5:
return "TRANSFER NO DEVICE (5)";
case 6:
return "TRANSFER OVERFLOW (6)";
default:
return "UNKNOWN TRANSFER STATUS (" + status + ")";
}
}
@Override
public void apply( TunerConfiguration config ) throws SourceException
{
if( config instanceof AirspyTunerConfiguration )
@@ -1192,18 +1215,21 @@ public class AirspyTunerController extends TunerController
*/
public void addListener( Listener<ComplexBuffer> listener )
{
mComplexBufferBroadcaster.addListener( listener );
synchronized( mComplexBufferBroadcaster )
{
mComplexBufferBroadcaster.addListener( listener );
if( mBufferProcessor == null || !mBufferProcessor.isRunning() )
{
mBufferProcessor = new BufferProcessor();
if( mBufferProcessor == null || !mBufferProcessor.isRunning() )
{
mBufferProcessor = new BufferProcessor();
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "Airspy Buffer Processor" );
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "Airspy Buffer Processor" );
thread.start();
}
thread.start();
}
}
}
/**
@@ -1212,12 +1238,15 @@ public class AirspyTunerController extends TunerController
*/
public void removeListener( Listener<ComplexBuffer> listener )
{
mComplexBufferBroadcaster.removeListener( listener );
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
synchronized( mComplexBufferBroadcaster )
{
mComplexBufferBroadcaster.removeListener( listener );
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
}
}
/**
@@ -1227,8 +1256,11 @@ public class AirspyTunerController extends TunerController
public class BufferProcessor implements Runnable, TransferCallback
{
private ScheduledFuture<?> mSampleDispatcherTask;
private CopyOnWriteArrayList<Transfer> mTransfers;
private LinkedTransferQueue<Transfer> mAvailableTransfers;
private LinkedTransferQueue<Transfer> mTransfersInProgress = new LinkedTransferQueue<>();
private AtomicBoolean mRunning = new AtomicBoolean();
private ByteBuffer mLibUsbHandlerStatus;
private boolean mCancel = false;
@Override
public void run()
@@ -1237,33 +1269,64 @@ public class AirspyTunerController extends TunerController
{
prepareTransfers();
for( Transfer transfer: mTransfers )
{
int result = LibUsb.submitTransfer( transfer );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error submitting transfer [" +
LibUsb.errorName( result ) + "]" );
break;
}
}
mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(),
20, TimeUnit.MILLISECONDS );
mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 );
List<Transfer> transfers = new ArrayList<>();
mCancel = false;
while( mRunning.get() )
{
ByteBuffer completed = ByteBuffer.allocateDirect( 4 );
mAvailableTransfers.drainTo( transfers );
for( Transfer transfer: transfers )
{
int result = LibUsb.submitTransfer( transfer );
if( result == LibUsb.SUCCESS )
{
mTransfersInProgress.add( transfer );
}
else
{
mLog.error( "error submitting transfer [" +
LibUsb.errorName( result ) + "]" );
}
}
int result = LibUsb.handleEventsTimeoutCompleted(
null, USB_TIMEOUT_MS, completed.asIntBuffer() );
null, USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling events for libusb" );
}
transfers.clear();
mLibUsbHandlerStatus.rewind();
}
if( mCancel )
{
for( Transfer transfer: mTransfersInProgress )
{
LibUsb.cancelTransfer( transfer );
}
int result = LibUsb.handleEventsTimeoutCompleted( null,
USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling events for libusb" );
}
mLibUsbHandlerStatus.rewind();
}
}
}
@@ -1273,10 +1336,10 @@ public class AirspyTunerController extends TunerController
*/
public void stop()
{
mCancel = true;
if( mRunning.compareAndSet( true, false ) )
{
cancel();
if( mSampleDispatcherTask != null )
{
mSampleDispatcherTask.cancel( true );
@@ -1296,66 +1359,39 @@ public class AirspyTunerController extends TunerController
@Override
public void processTransfer( Transfer transfer )
{
if( transfer.status() == LibUsb.TRANSFER_COMPLETED )
{
ByteBuffer buffer = transfer.buffer();
byte[] data = new byte[ transfer.actualLength() ];
buffer.get( data );
buffer.rewind();
mFilledBuffers.add( data );
if( !isRunning() )
{
LibUsb.cancelTransfer( transfer );
}
}
mTransfersInProgress.remove( transfer );
switch( transfer.status() )
{
case LibUsb.TRANSFER_COMPLETED:
/* resubmit the transfer */
int result = LibUsb.submitTransfer( transfer );
if( result != LibUsb.SUCCESS )
case LibUsb.TRANSFER_STALL:
if( transfer.actualLength() > 0 )
{
mLog.error( "couldn't resubmit buffer transfer to tuner" );
LibUsb.freeTransfer( transfer );
mTransfers.remove( transfer );
ByteBuffer buffer = transfer.buffer();
byte[] data = new byte[ transfer.actualLength() ];
buffer.get( data );
buffer.rewind();
if( isRunning() )
{
mFilledBuffers.add( data );
}
}
break;
case LibUsb.TRANSFER_CANCELLED:
/* free the transfer and remove it */
LibUsb.freeTransfer( transfer );
mTransfers.remove( transfer );
break;
default:
/* unexpected error */
mLog.error( "transfer error" );
mLog.error( "transfer error [" +
getTransferStatus( transfer.status() ) +
"] transferred actual: " + transfer.actualLength() );
}
}
private void cancel()
{
for( Transfer transfer: mTransfers )
{
LibUsb.cancelTransfer( transfer );
}
ByteBuffer completed = ByteBuffer.allocateDirect( 4 );
int result = LibUsb.handleEventsTimeoutCompleted(
null, USB_TIMEOUT_MS, completed.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling usb events during cancel [" +
LibUsb.errorName( result ) + "]" );
}
}
mAvailableTransfers.add( transfer );
}
/**
* Prepares (allocates) a set of transfer buffers for use in
@@ -1363,30 +1399,28 @@ public class AirspyTunerController extends TunerController
*/
private void prepareTransfers() throws LibUsbException
{
mTransfers = new CopyOnWriteArrayList<Transfer>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
if( mAvailableTransfers == null )
{
Transfer transfer = LibUsb.allocTransfer();
mAvailableTransfers = new LinkedTransferQueue<>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
{
Transfer transfer = LibUsb.allocTransfer();
if( transfer == null )
{
throw new LibUsbException( "couldn't allocate transfer",
LibUsb.ERROR_NO_MEM );
}
final ByteBuffer buffer =
ByteBuffer.allocateDirect( mBufferSize );
LibUsb.fillBulkTransfer( transfer,
mDeviceHandle,
USB_ENDPOINT,
buffer,
BufferProcessor.this,
"Buffer #" + x,
USB_TIMEOUT_MS );
mTransfers.add( transfer );
if( transfer == null )
{
throw new LibUsbException( "couldn't allocate transfer",
LibUsb.ERROR_NO_MEM );
}
final ByteBuffer buffer =
ByteBuffer.allocateDirect( mBufferSize );
LibUsb.fillBulkTransfer( transfer, mDeviceHandle, USB_ENDPOINT,
buffer, BufferProcessor.this, "Buffer", USB_TIMEOUT_MS );
mAvailableTransfers.add( transfer );
}
}
}
}

View File

@@ -56,6 +56,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
@@ -91,7 +92,7 @@ public class HackRFTunerController extends TunerController
private final static Logger mLog =
LoggerFactory.getLogger( HackRFTunerController.class );
public final static long TIMEOUT_US = 1000000l; //uSeconds
public final static long USB_TIMEOUT_MS = 1000000l; //uSeconds
public static final byte USB_ENDPOINT = (byte)0x81;
public static final byte USB_INTERFACE = (byte)0x0;
@@ -394,7 +395,7 @@ public class HackRFTunerController extends TunerController
(short)value,
(short)index,
buffer,
TIMEOUT_US );
USB_TIMEOUT_MS );
if( transferred < 0 )
{
@@ -467,7 +468,7 @@ public class HackRFTunerController extends TunerController
(short)value,
(short)index,
buffer,
TIMEOUT_US );
USB_TIMEOUT_MS );
if( transferred < 0 )
{
@@ -886,18 +887,21 @@ public class HackRFTunerController extends TunerController
*/
public void addListener( Listener<ComplexBuffer> listener )
{
mComplexBufferBroadcaster.addListener( listener );
synchronized( mComplexBufferBroadcaster )
{
mComplexBufferBroadcaster.addListener( listener );
if( mBufferProcessor == null || !mBufferProcessor.isRunning() )
{
mBufferProcessor = new BufferProcessor();
if( mBufferProcessor == null || !mBufferProcessor.isRunning() )
{
mBufferProcessor = new BufferProcessor();
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "HackRF Sample Processor" );
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "HackRF Sample Processor" );
thread.start();
}
thread.start();
}
}
}
/**
@@ -906,12 +910,15 @@ public class HackRFTunerController extends TunerController
*/
public void removeListener( Listener<ComplexBuffer> listener )
{
mComplexBufferBroadcaster.removeListener( listener );
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
synchronized( mComplexBufferBroadcaster )
{
mComplexBufferBroadcaster.removeListener( listener );
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
}
}
/**
@@ -921,45 +928,77 @@ public class HackRFTunerController extends TunerController
public class BufferProcessor implements Runnable, TransferCallback
{
private ScheduledFuture<?> mSampleDispatcherTask;
private CopyOnWriteArrayList<Transfer> mTransfers;
private LinkedTransferQueue<Transfer> mAvailableTransfers;
private LinkedTransferQueue<Transfer> mTransfersInProgress = new LinkedTransferQueue<>();
private AtomicBoolean mRunning = new AtomicBoolean();
private ByteBuffer mLibUsbHandlerStatus;
private boolean mCancel = false;
@Override
public void run()
{
if( mRunning.compareAndSet( false, true ) )
{
mLog.info( "starting HackRF sample fetch thread" );
prepareTransfers();
for( Transfer transfer: mTransfers )
{
int result = LibUsb.submitTransfer( transfer );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error submitting transfer [" +
LibUsb.errorName( result ) + "]" );
break;
}
}
mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(),
20, TimeUnit.MILLISECONDS );
mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 );
List<Transfer> transfers = new ArrayList<>();
mCancel = false;
while( mRunning.get() )
{
ByteBuffer completed = ByteBuffer.allocateDirect( 4 );
mAvailableTransfers.drainTo( transfers );
for( Transfer transfer: transfers )
{
int result = LibUsb.submitTransfer( transfer );
if( result == LibUsb.SUCCESS )
{
mTransfersInProgress.add( transfer );
}
else
{
mLog.error( "error submitting transfer [" +
LibUsb.errorName( result ) + "]" );
}
}
int result = LibUsb.handleEventsTimeoutCompleted(
null, TIMEOUT_US, completed.asIntBuffer() );
null, USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling events for libusb" );
}
transfers.clear();
mLibUsbHandlerStatus.rewind();
}
if( mCancel )
{
for( Transfer transfer: mTransfersInProgress )
{
LibUsb.cancelTransfer( transfer );
}
int result = LibUsb.handleEventsTimeoutCompleted(
null, USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error cancelling transfers in progress" );
}
mLibUsbHandlerStatus.rewind();
}
}
}
@@ -969,12 +1008,10 @@ public class HackRFTunerController extends TunerController
*/
public void stop()
{
mCancel = true;
if( mRunning.compareAndSet( true, false ) )
{
mLog.info( "stopping HackRF sample fetch thread" );
cancel();
if( mSampleDispatcherTask != null )
{
mSampleDispatcherTask.cancel( true );
@@ -994,41 +1031,29 @@ public class HackRFTunerController extends TunerController
@Override
public void processTransfer( Transfer transfer )
{
if( transfer.status() == LibUsb.TRANSFER_COMPLETED )
{
ByteBuffer buffer = transfer.buffer();
byte[] data = new byte[ transfer.actualLength() ];
buffer.get( data );
buffer.rewind();
mFilledBuffers.add( data );
if( !isRunning() )
{
LibUsb.cancelTransfer( transfer );
}
}
mTransfersInProgress.remove( transfer );
switch( transfer.status() )
{
case LibUsb.TRANSFER_COMPLETED:
/* resubmit the transfer */
int result = LibUsb.submitTransfer( transfer );
if( result != LibUsb.SUCCESS )
case LibUsb.TRANSFER_STALL:
if( transfer.actualLength() > 0 )
{
mLog.error( "couldn't resubmit buffer transfer to tuner" );
LibUsb.freeTransfer( transfer );
mTransfers.remove( transfer );
ByteBuffer buffer = transfer.buffer();
byte[] data = new byte[ transfer.actualLength() ];
buffer.get( data );
buffer.rewind();
if( isRunning() )
{
mFilledBuffers.add( data );
}
}
break;
case LibUsb.TRANSFER_CANCELLED:
/* free the transfer and remove it */
LibUsb.freeTransfer( transfer );
mTransfers.remove( transfer );
break;
default:
/* unexpected error */
@@ -1036,26 +1061,9 @@ public class HackRFTunerController extends TunerController
getTransferStatus( transfer.status() ) +
"] transferred actual: " + transfer.actualLength() );
}
}
private void cancel()
{
for( Transfer transfer: mTransfers )
{
LibUsb.cancelTransfer( transfer );
}
ByteBuffer completed = ByteBuffer.allocateDirect( 4 );
int result = LibUsb.handleEventsTimeoutCompleted(
null, TIMEOUT_US, completed.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling usb events during cancel [" +
LibUsb.errorName( result ) + "]" );
}
}
mAvailableTransfers.add( transfer );
}
/**
* Prepares (allocates) a set of transfer buffers for use in
@@ -1063,30 +1071,28 @@ public class HackRFTunerController extends TunerController
*/
private void prepareTransfers() throws LibUsbException
{
mTransfers = new CopyOnWriteArrayList<Transfer>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
if( mAvailableTransfers == null )
{
Transfer transfer = LibUsb.allocTransfer();
mAvailableTransfers = new LinkedTransferQueue<>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
{
Transfer transfer = LibUsb.allocTransfer();
if( transfer == null )
{
throw new LibUsbException( "couldn't allocate transfer",
LibUsb.ERROR_NO_MEM );
}
final ByteBuffer buffer =
ByteBuffer.allocateDirect( mBufferSize );
LibUsb.fillBulkTransfer( transfer,
mDeviceHandle,
USB_ENDPOINT,
buffer,
BufferProcessor.this,
"Buffer #" + x,
TIMEOUT_US );
mTransfers.add( transfer );
if( transfer == null )
{
throw new LibUsbException( "couldn't allocate transfer",
LibUsb.ERROR_NO_MEM );
}
final ByteBuffer buffer =
ByteBuffer.allocateDirect( mBufferSize );
LibUsb.fillBulkTransfer( transfer, mDeviceHandle,USB_ENDPOINT,
buffer, BufferProcessor.this, "Buffer", USB_TIMEOUT_MS );
mAvailableTransfers.add( transfer );
}
}
}
}

View File

@@ -27,7 +27,7 @@ import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.List;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -55,8 +55,6 @@ import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.TunerController;
import source.tuner.TunerType;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import buffer.FloatAveragingBuffer;
import controller.ResourceManager;
import controller.ThreadPoolManager;
@@ -73,7 +71,7 @@ public abstract class RTL2832TunerController extends TunerController
public final static int TWO_TO_22_POWER = 4194304;
public final static byte USB_INTERFACE = (byte)0x0;
public final static byte BULK_ENDPOINT_IN = (byte)0x81;
public final static byte USB_BULK_ENDPOINT = (byte)0x81;
public final static byte CONTROL_ENDPOINT_IN =
(byte)( LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR );
public final static byte CONTROL_ENDPOINT_OUT =
@@ -1342,21 +1340,24 @@ public abstract class RTL2832TunerController extends TunerController
*/
public void addListener( Listener<ComplexBuffer> listener )
{
mComplexBufferBroadcaster.addListener( listener );
synchronized( mComplexBufferBroadcaster )
{
mComplexBufferBroadcaster.addListener( listener );
if( mBufferProcessor == null )
{
mBufferProcessor = new BufferProcessor( mThreadPoolManager );
}
if( !mBufferProcessor.isRunning() )
{
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "RTL2832 Sample Processor" );
if( mBufferProcessor == null )
{
mBufferProcessor = new BufferProcessor( mThreadPoolManager );
}
if( !mBufferProcessor.isRunning() )
{
Thread thread = new Thread( mBufferProcessor );
thread.setDaemon( true );
thread.setName( "RTL2832 Sample Processor" );
thread.start();
}
thread.start();
}
}
}
/**
@@ -1365,12 +1366,15 @@ public abstract class RTL2832TunerController extends TunerController
*/
public void removeListener( Listener<ComplexBuffer> listener )
{
mComplexBufferBroadcaster.removeListener( listener );
synchronized( mComplexBufferBroadcaster )
{
mComplexBufferBroadcaster.removeListener( listener );
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
if( !mComplexBufferBroadcaster.hasListeners() )
{
mBufferProcessor.stop();
}
}
}
/**
@@ -1380,9 +1384,12 @@ public abstract class RTL2832TunerController extends TunerController
public class BufferProcessor implements Runnable, TransferCallback
{
private ScheduledFuture<?> mSampleDispatcherTask;
private CopyOnWriteArrayList<Transfer> mTransfers;
private LinkedTransferQueue<Transfer> mAvailableTransfers;
private LinkedTransferQueue<Transfer> mTransfersInProgress = new LinkedTransferQueue<>();
private AtomicBoolean mRunning = new AtomicBoolean();
private ThreadPoolManager mThreadPoolManager;
private ByteBuffer mLibUsbHandlerStatus;
private boolean mCancel = false;
public BufferProcessor( ThreadPoolManager manager )
{
@@ -1409,18 +1416,6 @@ public abstract class RTL2832TunerController extends TunerController
prepareTransfers();
for( Transfer transfer: mTransfers )
{
int result = LibUsb.submitTransfer( transfer );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error submitting transfer [" +
LibUsb.errorName( result ) + "]" );
break;
}
}
try
{
mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate(
@@ -1431,20 +1426,66 @@ public abstract class RTL2832TunerController extends TunerController
{
mLog.error( "NPE! = tpm null: " + ( mThreadPoolManager == null ), npe );
}
while( mRunning.get() )
mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 );
List<Transfer> transfers = new ArrayList<>();
mCancel = false;
while( mRunning.get() )
{
ByteBuffer completed = ByteBuffer.allocateDirect( 4 );
mAvailableTransfers.drainTo( transfers );
for( Transfer transfer: transfers )
{
int result = LibUsb.submitTransfer( transfer );
if( result == LibUsb.SUCCESS )
{
mTransfersInProgress.add( transfer );
}
else
{
mLog.error( "Error submitting transfer [" +
LibUsb.errorName( result ) + "]" );
}
}
int result = LibUsb.handleEventsTimeoutCompleted(
null, TIMEOUT_US, completed.asIntBuffer() );
null, TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling events for libusb" );
}
transfers.clear();
mLibUsbHandlerStatus.rewind();
}
if( mCancel )
{
for( Transfer transfer: mTransfersInProgress )
{
LibUsb.cancelTransfer( transfer );
}
int result = LibUsb.handleEventsTimeoutCompleted(
null, TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling events for libusb" );
}
mLibUsbHandlerStatus.rewind();
}
}
else
{
mLog.error( "Couldn't start buffer processor - already running!" );
}
}
@@ -1453,10 +1494,10 @@ public abstract class RTL2832TunerController extends TunerController
*/
public void stop()
{
mCancel = true;
if( mRunning.compareAndSet( true, false ) )
{
cancel();
if( mSampleDispatcherTask != null )
{
mThreadPoolManager.cancel( mSampleDispatcherTask );
@@ -1477,43 +1518,29 @@ public abstract class RTL2832TunerController extends TunerController
@Override
public void processTransfer( Transfer transfer )
{
if( transfer.status() == LibUsb.TRANSFER_COMPLETED )
{
ByteBuffer buffer = transfer.buffer();
byte[] data = new byte[ transfer.actualLength() ];
buffer.get( data );
buffer.rewind();
mFilledBuffers.add( data );
mSampleCounter.addAndGet( transfer.actualLength() );
if( !isRunning() )
{
LibUsb.cancelTransfer( transfer );
}
}
mTransfersInProgress.remove( transfer );
switch( transfer.status() )
{
case LibUsb.TRANSFER_COMPLETED:
/* resubmit the transfer */
int result = LibUsb.submitTransfer( transfer );
if( result != LibUsb.SUCCESS )
case LibUsb.TRANSFER_STALL:
if( transfer.actualLength() > 0 )
{
mLog.error( "couldn't resubmit buffer transfer to tuner" );
LibUsb.freeTransfer( transfer );
mTransfers.remove( transfer );
ByteBuffer buffer = transfer.buffer();
byte[] data = new byte[ transfer.actualLength() ];
buffer.get( data );
buffer.rewind();
if( isRunning() )
{
mFilledBuffers.add( data );
}
}
break;
case LibUsb.TRANSFER_CANCELLED:
/* free the transfer and remove it */
LibUsb.freeTransfer( transfer );
mTransfers.remove( transfer );
break;
default:
/* unexpected error */
@@ -1521,26 +1548,9 @@ public abstract class RTL2832TunerController extends TunerController
getTransferStatus( transfer.status() ) +
"] transferred actual: " + transfer.actualLength() );
}
}
private void cancel()
{
for( Transfer transfer: mTransfers )
{
LibUsb.cancelTransfer( transfer );
}
ByteBuffer completed = ByteBuffer.allocateDirect( 4 );
int result = LibUsb.handleEventsTimeoutCompleted(
null, TIMEOUT_US, completed.asIntBuffer() );
if( result != LibUsb.SUCCESS )
{
mLog.error( "error handling usb events during cancel [" +
LibUsb.errorName( result ) + "]" );
}
}
mAvailableTransfers.add( transfer );
}
/**
* Prepares (allocates) a set of transfer buffers for use in
@@ -1548,30 +1558,28 @@ public abstract class RTL2832TunerController extends TunerController
*/
private void prepareTransfers() throws LibUsbException
{
mTransfers = new CopyOnWriteArrayList<Transfer>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
if( mAvailableTransfers == null )
{
Transfer transfer = LibUsb.allocTransfer();
mAvailableTransfers = new LinkedTransferQueue<>();
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
{
Transfer transfer = LibUsb.allocTransfer();
if( transfer == null )
{
throw new LibUsbException( "couldn't allocate transfer",
LibUsb.ERROR_NO_MEM );
}
final ByteBuffer buffer =
ByteBuffer.allocateDirect( mBufferSize );
LibUsb.fillBulkTransfer( transfer,
mDeviceHandle,
BULK_ENDPOINT_IN,
buffer,
BufferProcessor.this,
"Buffer #" + x,
TIMEOUT_US );
mTransfers.add( transfer );
if( transfer == null )
{
throw new LibUsbException( "couldn't allocate transfer",
LibUsb.ERROR_NO_MEM );
}
final ByteBuffer buffer =
ByteBuffer.allocateDirect( mBufferSize );
LibUsb.fillBulkTransfer( transfer, mDeviceHandle, USB_BULK_ENDPOINT,
buffer, BufferProcessor.this, "Buffer", TIMEOUT_US );
mAvailableTransfers.add( transfer );
}
}
}
}

View File

@@ -34,7 +34,7 @@ public class R820TTunerConfiguration extends TunerConfiguration
private R820TLNAGain mLNAGain = R820TLNAGain.GAIN_222;
private R820TVGAGain mVGAGain = R820TVGAGain.GAIN_210;
private double mFrequencyCorrection = 0.0d;
private SampleRate mSampleRate = SampleRate.RATE_1_920MHZ;
private SampleRate mSampleRate = SampleRate.RATE_2_400MHZ;
/**
* Default constructor for JAXB

View File

@@ -20,6 +20,7 @@ package spectrum;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -56,8 +57,12 @@ public class DFTProcessor implements Listener<ComplexBuffer>,
private CopyOnWriteArrayList<DFTResultsConverter> mListeners =
new CopyOnWriteArrayList<DFTResultsConverter>();
private ArrayBlockingQueue<ComplexBuffer> mQueue =
new ArrayBlockingQueue<ComplexBuffer>( 200 );
//Fixed size sample queue. We set the size at 12 based on the highest
//sample rate tuner (10 MHz) producing 153 frames per second and the lowest
//DFT processing frame rate of 12 frames per second. This means that the
//maximum size needed is 153 / 12 = 11 frames per DFT processing iteration
//so we set it at 12.
private BlockingQueue<ComplexBuffer> mQueue = new ArrayBlockingQueue<>( 12 );
private ScheduledExecutorService mScheduler = Executors
.newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) );
@@ -246,15 +251,7 @@ public class DFTProcessor implements Listener<ComplexBuffer>,
@Override
public void receive( ComplexBuffer sampleBuffer )
{
if( !mQueue.offer( sampleBuffer ) )
{
mLog.error( "DFTProcessor - [" + mSampleType.toString()
+ "] queue is full, purging queue, "
+ "samples[" + sampleBuffer + "]" );
mQueue.clear();
mQueue.offer( sampleBuffer );
}
mQueue.offer( sampleBuffer );
}
private void getNextBuffer()