From ba053457bd8a165929d44d9b6a38edfa3cc0aeaa Mon Sep 17 00:00:00 2001 From: "dennis.sheirer" Date: Fri, 19 Dec 2014 23:59:38 +0000 Subject: [PATCH] Out of memory fixes and updates to the P25 decoder. Memory Fixes Decreased the decimation process repeat timing so that it runs smaller work chunks more often Decreased scheduled thread pool to 2 hot threads Wrapped all scheduled thread pool runnables in try/catch blocks so that an exception doesn't kill the sample buffer processing thread and cause the buffer to grow unbounded. P25 updates Moved TSBK crc checks into the message framer so that message corrections don't occur after the message is constructed. Added more functionality to the channel state for handling registrations and call events. --- src/controller/ThreadPoolManager.java | 2 +- src/controller/channel/ProcessingChain.java | 64 ++-- src/decode/DecodeComponentEditor.java | 5 +- src/decode/p25/P25ChannelState.java | 334 +++++++++++++----- src/decode/p25/P25Decoder.java | 62 +++- .../message/tsbk/GroupMultiChannelGrant.java | 80 ++++- src/decode/p25/message/tsbk/TSBKMessage.java | 11 +- .../voice/GroupVoiceChannelGrantUpdate.java | 2 +- .../TelephoneInterconnectAnswerRequest.java | 13 +- src/dsp/fsk/C4FMSymbolFilter.java | 73 +++- src/dsp/fsk/P25MessageFramer.java | 83 +++-- src/gui/SDRTrunk.java | 27 ++ .../tap/stream/AdditiveFloatTap.java | 40 +++ src/instrument/tap/stream/GainFloatTap.java | 40 +++ src/source/tuner/TunerChannelSource.java | 75 ++-- .../tuner/hackrf/HackRFTunerController.java | 21 +- .../tuner/rtl/RTL2832TunerController.java | 21 +- src/spectrum/DFTProcessor.java | 19 +- 18 files changed, 758 insertions(+), 214 deletions(-) create mode 100644 src/instrument/tap/stream/AdditiveFloatTap.java create mode 100644 src/instrument/tap/stream/GainFloatTap.java diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index 158a5b19..ba8234bb 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit; public class ThreadPoolManager { - private int THREAD_POOL_SIZE = 10; + private int THREAD_POOL_SIZE = 2; private int MAX_TASK_COUNT = 24; private HashMap,ThreadType> mTasks = diff --git a/src/controller/channel/ProcessingChain.java b/src/controller/channel/ProcessingChain.java index 2a7c7b93..fc583d90 100644 --- a/src/controller/channel/ProcessingChain.java +++ b/src/controller/channel/ProcessingChain.java @@ -682,26 +682,33 @@ public class ProcessingChain implements Listener @Override public void run() { - List sampleBuffers = new ArrayList(); - - mRealQueue.drainTo( sampleBuffers, 4 ); - - for( RealBuffer sampleBuffer: sampleBuffers ) + try { - if( mDecoder != null ) + List sampleBuffers = new ArrayList(); + + mRealQueue.drainTo( sampleBuffers, 4 ); + + for( RealBuffer sampleBuffer: sampleBuffers ) { - float[] samples = sampleBuffer.getSamples(); - - for( float sample: samples ) + if( mDecoder != null ) { - mDecoder.getRealReceiver().receive( sample ); + float[] samples = sampleBuffer.getSamples(); + + for( float sample: samples ) + { + mDecoder.getRealReceiver().receive( sample ); + } } + + sampleBuffer.dispose(); } - sampleBuffer.dispose(); + sampleBuffers.clear(); + } + catch( Exception e ) + { + mLog.error( "error during processing chain real processor run", e ); } - - sampleBuffers.clear(); } } @@ -713,25 +720,32 @@ public class ProcessingChain implements Listener @Override public void run() { - List> sampleSets = - new ArrayList>(); - - mComplexQueue.drainTo( sampleSets, 16 ); - - for( List samples: sampleSets ) + try { - for( ComplexSample sample: samples ) + List> sampleSets = + new ArrayList>(); + + mComplexQueue.drainTo( sampleSets, 16 ); + + for( List samples: sampleSets ) { - if( sample != null && mDecoder != null ) + for( ComplexSample sample: samples ) { - mDecoder.getComplexReceiver().receive( sample ); + if( sample != null && mDecoder != null ) + { + mDecoder.getComplexReceiver().receive( sample ); + } } + + samples.clear(); } - samples.clear(); + sampleSets.clear(); + } + catch( Exception e ) + { + mLog.error( "error during processing chain complex processor run", e ); } - - sampleSets.clear(); } } diff --git a/src/decode/DecodeComponentEditor.java b/src/decode/DecodeComponentEditor.java index ece532ab..ee9e754f 100644 --- a/src/decode/DecodeComponentEditor.java +++ b/src/decode/DecodeComponentEditor.java @@ -124,7 +124,10 @@ public class DecodeComponentEditor extends AbstractChannelEditor @Override public void save() { - mEditor.save(); + if( mEditor != null ) + { + mEditor.save(); + } //The component that calls save will invoke the change broadcast mChannelNode.getChannel() diff --git a/src/decode/p25/P25ChannelState.java b/src/decode/p25/P25ChannelState.java index b3942971..3883e61b 100644 --- a/src/decode/p25/P25ChannelState.java +++ b/src/decode/p25/P25ChannelState.java @@ -17,6 +17,10 @@ ******************************************************************************/ package decode.p25; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + import message.Message; import alias.Alias; import alias.AliasList; @@ -35,11 +39,19 @@ import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; +import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; +import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; +import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; +import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; +import decode.p25.reference.Opcode; import decode.p25.reference.Vendor; public class P25ChannelState extends ChannelState { + private HashMap mRegistrations = new HashMap(); + private String mLastDeRegistration; + private P25ActivitySummary mActivitySummary; private String mNAC; private String mSystem; @@ -154,99 +166,261 @@ public class P25ChannelState extends ChannelState switch( tsbk.getOpcode() ) { case GROUP_VOICE_CHANNEL_GRANT: - GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)tsbk; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcg.getChannelIdentifier() + "-" + gvcg.getChannel() ) - .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcg.getDownlinkFrequency() ) - .from( gvcg.getSourceAddress() ) - .to( gvcg.getGroupAddress() ) - .build() ); - break; case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - GroupVoiceChannelGrantUpdate gvcgu = - (GroupVoiceChannelGrantUpdate)tsbk; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgu.getChannelID1() + "-" + gvcgu.getChannelNumber1() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency() ) - .to( gvcgu.getGroupAddress1() ) - .build() ); - if( gvcgu.getChannelNumber2() != 0 ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgu.getChannelID2() + "-" + gvcgu.getChannelNumber2() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency() ) - .to( gvcgu.getGroupAddress2() ) - .build() ); - } - break; case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - GroupVoiceChannelGrantUpdateExplicit gvcgue = - (GroupVoiceChannelGrantUpdateExplicit)tsbk; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgue.getTransmitChannelID() + "-" + - gvcgue.getTransmitChannelNumber() ) - .details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgue.getDownlinkFrequency() ) - .to( gvcgue.getGroupAddress() ) - .build() ); + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + processCall( tsbk ); break; + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: case UNIT_TO_UNIT_ANSWER_REQUEST: - UnitToUnitAnswerRequest utuar = - (UnitToUnitAnswerRequest)tsbk; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) ) - .from( utuar.getSourceAddress() ) - .to( utuar.getTargetAddress() ) - .build() ); + processPage( tsbk ); break; case LOCATION_REGISTRATION_RESPONSE: - LocationRegistrationResponse lrr = - (LocationRegistrationResponse)tsbk; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "REG:" + lrr.getResponse().name() + - " SITE: " + lrr.getRFSSID() + "-" + - lrr.getSiteID() ) - .from( lrr.getTargetAddress() ) - .to( lrr.getGroupAddress() ) - .build() ); - break; case UNIT_DEREGISTRATION_ACKNOWLEDGE: - UnitDeregistrationAcknowledge udr = - (UnitDeregistrationAcknowledge)tsbk; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.DEREGISTER ) - .aliasList( mAliasList ) - .from( udr.getSourceID() ) - .build() ); + processRegistration( tsbk ); + default: break; } } } } + + /** + * Process a call event message + */ + private void processCall( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case GROUP_VOICE_CHANNEL_GRANT: + GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( gvcg.getChannelIdentifier() + "-" + gvcg.getChannel() ) + .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcg.getDownlinkFrequency() ) + .from( gvcg.getSourceAddress() ) + .to( gvcg.getGroupAddress() ) + .build() ); + break; + case GROUP_VOICE_CHANNEL_GRANT_UPDATE: + GroupVoiceChannelGrantUpdate gvcgu = + (GroupVoiceChannelGrantUpdate)message; + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( gvcgu.getChannelID1() + "-" + gvcgu.getChannelNumber1() ) + .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency1() ) + .to( gvcgu.getGroupAddress1() ) + .build() ); + if( gvcgu.hasChannelNumber2() ) + { + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( gvcgu.getChannelID2() + "-" + gvcgu.getChannelNumber2() ) + .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency2() ) + .to( gvcgu.getGroupAddress2() ) + .build() ); + } + break; + case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + GroupVoiceChannelGrantUpdateExplicit gvcgue = + (GroupVoiceChannelGrantUpdateExplicit)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( gvcgue.getTransmitChannelID() + "-" + + gvcgue.getTransmitChannelNumber() ) + .details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgue.getDownlinkFrequency() ) + .to( gvcgue.getGroupAddress() ) + .build() ); + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + TelephoneInterconnectVoiceChannelGrant tivcg = + (TelephoneInterconnectVoiceChannelGrant)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( tivcg.getChannelIdentifier() + "-" + + tivcg.getChannel() ) + .details( ( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( tivcg.getDownlinkFrequency() ) + .from( tivcg.getSourceAddress() ) + .to( tivcg.getTargetAddress() ) + .build() ); + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = + (TelephoneInterconnectVoiceChannelGrantUpdate)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( tivcgu.getChannelIdentifier() + "-" + + tivcgu.getChannel() ) + .details( ( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( tivcgu.getDownlinkFrequency() ) + .from( tivcgu.getSourceAddress() ) + .to( tivcgu.getTargetAddress() ) + .build() ); + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + UnitToUnitVoiceChannelGrant uuvcg = + (UnitToUnitVoiceChannelGrant)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( uuvcg.getChannelIdentifier() + "-" + + uuvcg.getChannel() ) + .details( ( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcg.getDownlinkFrequency() ) + .from( uuvcg.getSourceAddress() ) + .to( uuvcg.getTargetAddress() ) + .build() ); + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + UnitToUnitVoiceChannelGrantUpdate uuvcgu = + (UnitToUnitVoiceChannelGrantUpdate)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( uuvcgu.getChannelIdentifier() + "-" + + uuvcgu.getChannel() ) + .details( ( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcgu.getDownlinkFrequency() ) + .from( uuvcgu.getSourceAddress() ) + .to( uuvcgu.getTargetAddress() ) + .build() ); + break; + default: + break; + } + } + + /** + * Process a unit paging event message + */ + private void processPage( TSBKMessage message ) + { + if( message.getOpcode() == Opcode.UNIT_TO_UNIT_ANSWER_REQUEST ) + { + UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( mAliasList ) + .details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) ) + .from( utuar.getSourceAddress() ) + .to( utuar.getTargetAddress() ) + .build() ); + } + } + + /** + * Process a unit registration or deregistration message + */ + private void processRegistration( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case LOCATION_REGISTRATION_RESPONSE: + LocationRegistrationResponse lrr = + (LocationRegistrationResponse)message; + + String addressPlusGroup = lrr.getTargetAddress() + + lrr.getGroupAddress(); + + boolean updateEvent = false; + + if( mRegistrations.containsKey( addressPlusGroup ) ) + { + long timestamp = mRegistrations.get( addressPlusGroup ); + + updateEvent = ( System.currentTimeMillis() - + timestamp ) > 300000; //5 minutes + } + else + { + updateEvent = true; + } + + mRegistrations.put( addressPlusGroup, System.currentTimeMillis() ); + + if( updateEvent ) + { + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( mAliasList ) + .details( "RESPONSE:" + lrr.getResponse().name() + + " SITE: " + lrr.getRFSSID() + "-" + + lrr.getSiteID() ) + .from( lrr.getTargetAddress() ) + .to( lrr.getGroupAddress() ) + .build() ); + } + + /* Reset the deregistration variable */ + mLastDeRegistration = null; + break; + case UNIT_DEREGISTRATION_ACKNOWLEDGE: + UnitDeregistrationAcknowledge udr = + (UnitDeregistrationAcknowledge)message; + + if( mLastDeRegistration != null && + mLastDeRegistration.contentEquals( udr.getSourceID() ) ) + { + break; //Suppress duplicate event + } + + mLastDeRegistration = udr.getSourceID(); + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.DEREGISTER ) + .aliasList( mAliasList ) + .from( udr.getSourceID() ) + .build() ); + + List keysToRemove = new ArrayList(); + + /* Remove this radio from the registrations set */ + for( String key: mRegistrations.keySet() ) + { + if( key.startsWith( udr.getSourceID() ) ) + { + keysToRemove.add( key ); + } + } + + for( String key: keysToRemove ) + { + mRegistrations.remove( key ); + } + + break; + default: + break; + } + } @Override public void setSquelchState( SquelchState state ) diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java index 457a3fa1..be1fb72d 100644 --- a/src/decode/p25/P25Decoder.java +++ b/src/decode/p25/P25Decoder.java @@ -19,6 +19,7 @@ package decode.p25; import instrument.Instrumentable; import instrument.tap.Tap; +import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; @@ -31,6 +32,7 @@ import source.Source.SampleType; import alias.AliasList; import decode.Decoder; import decode.DecoderType; +import dsp.agc.RealAutomaticGainControl; import dsp.filter.ComplexFIRFilter; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; @@ -43,10 +45,17 @@ public class P25Decoder extends Decoder implements Instrumentable { private final static Logger mLog = LoggerFactory.getLogger( P25Decoder.class ); + /* Instrumentation Taps */ + private static final String INSTRUMENT_INPUT_TO_AGC = "Tap Point: Input to AGC"; + private static final String INSTRUMENT_AGC_TO_SYMBOL_FILTER = "Tap Point: AGC to Symbol Filter"; + private static final String INSTRUMENT_SYMBOL_FILTER_TO_SLICER = "Tap Point: Symbol Filter to Slicer"; + private List mAvailableTaps; + private ComplexFIRFilter mBasebandFilter = new ComplexFIRFilter( FilterFactory.getLowPass( 48000, 5000, 31, WindowType.HAMMING ), 1.0 ); private FMDiscriminator mDemodulator = new FMDiscriminator( 1 ); + private RealAutomaticGainControl mAGC = new RealAutomaticGainControl(); private C4FMSymbolFilter mSymbolFilter = new C4FMSymbolFilter(); private C4FMSlicer mSlicer = new C4FMSlicer(); private P25MessageFramer mNormalFramer; @@ -80,7 +89,8 @@ public class P25Decoder extends Decoder implements Instrumentable mDemodulator.setListener( getRealReceiver() ); } - addRealSampleListener( mSymbolFilter ); + addRealSampleListener( mAGC ); + mAGC.setListener( mSymbolFilter ); mSymbolFilter.setListener( mSlicer ); @@ -108,17 +118,65 @@ public class P25Decoder extends Decoder implements Instrumentable @Override public List getTaps() { - return new ArrayList(); + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT_TO_AGC, 0, 1.0f ) ); + mAvailableTaps.add( new FloatTap( INSTRUMENT_AGC_TO_SYMBOL_FILTER, 0, 1.0f ) ); + mAvailableTaps.add( new FloatTap( INSTRUMENT_SYMBOL_FILTER_TO_SLICER, 0, 0.1f ) ); + + mAvailableTaps.addAll( mSymbolFilter.getTaps() ); + } + + return mAvailableTaps; } @Override public void addTap( Tap tap ) { + mSymbolFilter.addTap( tap ); + + switch( tap.getName() ) + { + case INSTRUMENT_INPUT_TO_AGC: + FloatTap inputAGC = (FloatTap)tap; + removeRealListener( mAGC ); + addRealSampleListener( inputAGC ); + inputAGC.setListener( mAGC ); + break; + case INSTRUMENT_AGC_TO_SYMBOL_FILTER: + FloatTap agcSymbol = (FloatTap)tap; + mAGC.setListener( agcSymbol ); + agcSymbol.setListener( mSymbolFilter ); + break; + case INSTRUMENT_SYMBOL_FILTER_TO_SLICER: + FloatTap symbolSlicer = (FloatTap)tap; + mSymbolFilter.setListener( symbolSlicer ); + symbolSlicer.setListener( mSlicer ); + break; + } } @Override public void removeTap( Tap tap ) { + mSymbolFilter.removeTap( tap ); + + switch( tap.getName() ) + { + case INSTRUMENT_INPUT_TO_AGC: + FloatTap inputAGC = (FloatTap)tap; + removeRealListener( inputAGC ); + addRealSampleListener( mAGC ); + break; + case INSTRUMENT_AGC_TO_SYMBOL_FILTER: + mAGC.setListener( mSymbolFilter ); + break; + case INSTRUMENT_SYMBOL_FILTER_TO_SLICER: + mSymbolFilter.setListener( mSlicer ); + break; + } } @Override diff --git a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java b/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java index 7703ab08..b8084b04 100644 --- a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java +++ b/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java @@ -1,11 +1,13 @@ package decode.p25.message.tsbk; -import alias.Alias; import alias.AliasList; import bits.BitSetBuffer; +import decode.p25.message.tsbk.osp.control.IdentifierUpdate; +import decode.p25.message.tsbk.osp.control.IdentifierUpdateReceiver; import decode.p25.reference.DataUnitID; public abstract class GroupMultiChannelGrant extends ChannelGrant + implements IdentifierUpdateReceiver { public static final int[] CHANNEL_ID_1 = { 80,81,82,83 }; public static final int[] CHANNEL_NUMBER_1 = { 84,85,86,87, @@ -17,6 +19,9 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant 112,113,114,115,116,117,118,119 }; public static final int[] GROUP_ADDRESS_2 = { 120,121,122,123,124,125,126, 127,128,129,130,131,132,133,134,135 }; + + private IdentifierUpdate mIdentifierUpdate1; + private IdentifierUpdate mIdentifierUpdate2; public GroupMultiChannelGrant( BitSetBuffer message, DataUnitID duid, @@ -37,11 +42,14 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant sb.append( " GRP1:" ); sb.append( getGroupAddress1() ); - sb.append( " CHAN2:" ); - sb.append( getChannelID2() + "/" + getChannelNumber2() ); - - sb.append( " GRP2:" ); - sb.append( getGroupAddress2() ); + if( hasChannelNumber2() ) + { + sb.append( " CHAN2:" ); + sb.append( getChannelID2() + "/" + getChannelNumber2() ); + + sb.append( " GRP2:" ); + sb.append( getGroupAddress2() ); + } return sb.toString(); } @@ -76,6 +84,11 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant return mMessage.getHex( GROUP_ADDRESS_2, 4 ); } + public boolean hasChannelNumber2() + { + return mMessage.getInt( GROUP_ADDRESS_2 ) > 0; + } + @Override public String getFromID() { @@ -87,4 +100,59 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant { return getGroupAddress2(); } + + @Override + public void setIdentifierMessage( int identifier, IdentifierUpdate message ) + { + if( identifier == getChannelID1() ) + { + mIdentifierUpdate1 = message; + } + + if( hasChannelNumber2() && identifier == getChannelID2() ) + { + mIdentifierUpdate2 = message; + } + } + + @Override + public int[] getIdentifiers() + { + int[] identifiers; + + if( hasChannelNumber2() ) + { + identifiers = new int[ 2 ]; + identifiers[ 0 ] = getChannelID1(); + identifiers[ 1 ] = getChannelID2(); + } + else + { + identifiers = new int[ 1 ]; + identifiers[ 0 ] = getChannelID1(); + } + + return identifiers; + } + + public long getDownlinkFrequency1() + { + return calculateDownlink( mIdentifierUpdate1, getChannel() ); + } + + public long getUplinkFrequency1() + { + return calculateUplink( mIdentifierUpdate1, getChannel() ); + } + + public long getDownlinkFrequency2() + { + return calculateDownlink( mIdentifierUpdate2, getChannel() ); + } + + public long getUplinkFrequency2() + { + return calculateUplink( mIdentifierUpdate2, getChannel() ); + } + } diff --git a/src/decode/p25/message/tsbk/TSBKMessage.java b/src/decode/p25/message/tsbk/TSBKMessage.java index 3f326c4a..18f67861 100644 --- a/src/decode/p25/message/tsbk/TSBKMessage.java +++ b/src/decode/p25/message/tsbk/TSBKMessage.java @@ -36,15 +36,10 @@ public class TSBKMessage extends P25Message public TSBKMessage( BitSetBuffer message, DataUnitID duid, AliasList aliasList ) { super( message, duid, aliasList ); - - checkCRC(); - } - - protected void checkCRC() - { - mCRC = new CRC[ 1 ]; - mCRC[ 0 ] = CRCP25.correctCCITT80( mMessage, MESSAGE_START, CRC_START ); + /* Since CRC is checked before construction, mark it as passed */ + mCRC = new CRC[ 1 ]; + mCRC[ 0 ] = CRC.PASSED; } public boolean isLastBlock() diff --git a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java b/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java index 0d9f4999..7a7c3b45 100644 --- a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java +++ b/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java @@ -18,6 +18,6 @@ public class GroupVoiceChannelGrantUpdate extends GroupMultiChannelGrant @Override public String getEventType() { - return Opcode.GROUP_VOICE_CHANNEL_GRANT.getDescription(); + return Opcode.GROUP_VOICE_CHANNEL_GRANT_UPDATE.getDescription(); } } diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java b/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java index 248a47cd..bc532999 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java +++ b/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java @@ -3,7 +3,6 @@ package decode.p25.message.tsbk.osp.voice; import java.util.ArrayList; import java.util.List; -import alias.Alias; import alias.AliasList; import bits.BitSetBuffer; import decode.p25.message.tsbk.TSBKMessage; @@ -34,6 +33,12 @@ public class TelephoneInterconnectAnswerRequest extends TSBKMessage super( message, duid, aliasList ); } + @Override + public String getEventType() + { + return Opcode.TELEPHONE_INTERCONNECT_ANSWER_REQUEST.getDescription(); + } + public String getMessage() { StringBuilder sb = new StringBuilder(); @@ -72,12 +77,6 @@ public class TelephoneInterconnectAnswerRequest extends TSBKMessage return mMessage.getHex( TARGET_ADDRESS, 6 ); } - @Override - public String getEventType() - { - return Opcode.TELEPHONE_INTERCONNECT_ANSWER_REQUEST.getDescription(); - } - @Override public String getToID() { diff --git a/src/dsp/fsk/C4FMSymbolFilter.java b/src/dsp/fsk/C4FMSymbolFilter.java index adbf3c5d..ec9af0b4 100644 --- a/src/dsp/fsk/C4FMSymbolFilter.java +++ b/src/dsp/fsk/C4FMSymbolFilter.java @@ -1,5 +1,13 @@ package dsp.fsk; +import instrument.Instrumentable; +import instrument.tap.Tap; +import instrument.tap.stream.AdditiveFloatTap; +import instrument.tap.stream.FloatTap; + +import java.util.ArrayList; +import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -7,7 +15,8 @@ import sample.real.RealSampleListener; import sample.real.RealSampleProvider; public class C4FMSymbolFilter implements RealSampleListener, - RealSampleProvider + RealSampleProvider, + Instrumentable { private final static Logger mLog = LoggerFactory.getLogger( C4FMSymbolFilter.class ); @@ -145,6 +154,11 @@ public class C4FMSymbolFilter implements RealSampleListener, { 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 128/128 }; + /* Instrumentation Taps */ + private static final String INSTRUMENT_SYMBOL_SPREAD = "Tap Point: Symbol Spread (Goal=2.0)"; + private FloatTap mSymbolSpreadTap; + private List mAvailableTaps; + private static final int NUMBER_FILTER_TAPS = 8; private static final int NUMBER_FILTER_STEPS = 128; @@ -181,6 +195,8 @@ public class C4FMSymbolFilter implements RealSampleListener, private RealSampleListener mListener; + private float mGain = 15.4f; + /** * C4FM Symbol Filter * @@ -190,10 +206,15 @@ public class C4FMSymbolFilter implements RealSampleListener, * is less than 2.0, then increase gain. If the value is over 2.0, then * decrease gain. * - * During testing, the incoming samples were within the range of -0.5 <> 0.5 - * and applying a gain of 11.0 to those samples achieved the best symbol - * spread, centered about 2.0 with +/- 0.5% deviations on a strong signal. + * When preceded by the RealAutomaticGainFilter class, an optimal gain + * setting for this filter is 15.4 and that yields a symbol spread that + * is centered on 2.0, ranging 1.92 to 2.08. */ + public C4FMSymbolFilter( float gain ) + { + mGain = gain; + } + public C4FMSymbolFilter() { } @@ -203,8 +224,7 @@ public class C4FMSymbolFilter implements RealSampleListener, { mSymbolClock += mSymbolTime; -// mHistory[ mHistoryLast++ ] = sample; - mHistory[ mHistoryLast++ ] = sample * 10.5f; + mHistory[ mHistoryLast++ ] = sample * mGain; mHistoryLast %= NUMBER_FILTER_TAPS; if( mSymbolClock > 1.0f ) @@ -298,6 +318,11 @@ public class C4FMSymbolFilter implements RealSampleListener, ( mCoarseFrequencyCorrection > 0 ? "-" : "+" ) + mCoarseFrequencyCorrection + "]" ); } + + if( mSymbolSpreadTap != null ) + { + mSymbolSpreadTap.receive( mSymbolSpread ); + } /* dispatch the interpolated value to the listener */ if( mListener != null ) @@ -318,4 +343,40 @@ public class C4FMSymbolFilter implements RealSampleListener, { mListener = null; } + + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + /* + * Since the target is 2.0, we subtract 2.0 from the value so that + * a good value is plotted right on the zero center line + */ + mAvailableTaps.add( + new AdditiveFloatTap( INSTRUMENT_SYMBOL_SPREAD, 0, 0.1f, -2.0f ) ); + } + + return mAvailableTaps; + } + + @Override + public void addTap( Tap tap ) + { + if( tap.getName().contentEquals( INSTRUMENT_SYMBOL_SPREAD ) ) + { + mSymbolSpreadTap = (FloatTap)tap; + } + } + + @Override + public void removeTap( Tap tap ) + { + if( tap.getName().contentEquals( INSTRUMENT_SYMBOL_SPREAD ) ) + { + mSymbolSpreadTap = null; + } + } } diff --git a/src/dsp/fsk/P25MessageFramer.java b/src/dsp/fsk/P25MessageFramer.java index b76b6124..3629a894 100644 --- a/src/dsp/fsk/P25MessageFramer.java +++ b/src/dsp/fsk/P25MessageFramer.java @@ -33,6 +33,7 @@ public class P25MessageFramer implements Listener new ArrayList(); public static final int TSBK_BEGIN = 64; + public static final int TSBK_CRC_START = 144; public static final int TSBK_END = 260; public static final int TSBK_DECODED_END = 160; @@ -378,23 +379,33 @@ public class P25MessageFramer implements Listener * unsuccessful decode due to excessive errors */ if( mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END ) ) { - BitSetBuffer tsbkBuffer1 = mMessage.copy(); - tsbkBuffer1.setSize( TSBK_DECODED_END ); - - TSBKMessage tsbkMessage1 = TSBKMessageFactory.getMessage( - tsbkBuffer1, DataUnitID.TSBK1, mAliasList ); + CRC tsbkCRC = CRCP25.correctCCITT80( mMessage, + TSBK_BEGIN, TSBK_CRC_START ); - if( tsbkMessage1.isLastBlock() ) + if( tsbkCRC != CRC.FAILED_CRC ) { - mComplete = true; + BitSetBuffer tsbkBuffer1 = mMessage.copy(); + tsbkBuffer1.setSize( TSBK_DECODED_END ); + + TSBKMessage tsbkMessage1 = TSBKMessageFactory.getMessage( + tsbkBuffer1, DataUnitID.TSBK1, mAliasList ); + + if( tsbkMessage1.isLastBlock() ) + { + mComplete = true; + } + else + { + setDUID( DataUnitID.TSBK2 ); + mMessage.setPointer( TSBK_BEGIN ); + } + + dispatch( tsbkMessage1 ); } else { - setDUID( DataUnitID.TSBK2 ); - mMessage.setPointer( TSBK_BEGIN ); + mComplete = true; } - - dispatch( tsbkMessage1 ); } else { @@ -409,23 +420,33 @@ public class P25MessageFramer implements Listener * unsuccessful decode due to excessive errors */ if( mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END ) ) { - BitSetBuffer tsbkBuffer2 = mMessage.copy(); - tsbkBuffer2.setSize( TSBK_DECODED_END ); + CRC tsbkCRC = CRCP25.correctCCITT80( mMessage, + TSBK_BEGIN, TSBK_CRC_START ); - TSBKMessage tsbkMessage2 = TSBKMessageFactory.getMessage( - tsbkBuffer2, DataUnitID.TSBK2, mAliasList ); - - if( tsbkMessage2.isLastBlock() ) + if( tsbkCRC != CRC.FAILED_CRC ) { - mComplete = true; + BitSetBuffer tsbkBuffer2 = mMessage.copy(); + tsbkBuffer2.setSize( TSBK_DECODED_END ); + + TSBKMessage tsbkMessage2 = TSBKMessageFactory.getMessage( + tsbkBuffer2, DataUnitID.TSBK2, mAliasList ); + + if( tsbkMessage2.isLastBlock() ) + { + mComplete = true; + } + else + { + setDUID( DataUnitID.TSBK3 ); + mMessage.setPointer( TSBK_BEGIN ); + } + + dispatch( tsbkMessage2 ); } else { - setDUID( DataUnitID.TSBK3 ); - mMessage.setPointer( TSBK_BEGIN ); + mComplete = true; } - - dispatch( tsbkMessage2 ); } else { @@ -440,13 +461,19 @@ public class P25MessageFramer implements Listener * unsuccessful decode due to excessive errors */ if( mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END ) ) { - BitSetBuffer tsbkBuffer3 = mMessage.copy(); - tsbkBuffer3.setSize( TSBK_DECODED_END ); - - TSBKMessage tsbkMessage3 = TSBKMessageFactory.getMessage( - tsbkBuffer3, DataUnitID.TSBK3, mAliasList ); + CRC tsbkCRC = CRCP25.correctCCITT80( mMessage, + TSBK_BEGIN, TSBK_CRC_START ); + + if( tsbkCRC != CRC.FAILED_CRC ) + { + BitSetBuffer tsbkBuffer3 = mMessage.copy(); + tsbkBuffer3.setSize( TSBK_DECODED_END ); + + TSBKMessage tsbkMessage3 = TSBKMessageFactory.getMessage( + tsbkBuffer3, DataUnitID.TSBK3, mAliasList ); - dispatch( tsbkMessage3 ); + dispatch( tsbkMessage3 ); + } } mComplete = true; diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 7a5b1cb7..22498f7d 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -18,12 +18,14 @@ package gui; import java.awt.AWTException; +import java.awt.Desktop; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Robot; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; +import java.io.File; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -34,6 +36,7 @@ import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JSeparator; import net.miginfocom.swing.MigLayout; @@ -188,6 +191,30 @@ public class SDRTrunk JMenu fileMenu = new JMenu( "File" ); menuBar.add( fileMenu ); + + JMenuItem logFilesMenu = new JMenuItem( "Logs & Recordings" ); + logFilesMenu.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + Desktop.getDesktop().open( getHomePath().toFile() ); + } + catch( Exception e ) + { + mLog.error( "Couldn't open file explorer" ); + + JOptionPane.showMessageDialog( mMainGui, + "Can't launch file explorer - files are located at: " + + getHomePath().toString(), + "Can't launch file explorer", + JOptionPane.ERROR_MESSAGE ); + } + } + } ); + fileMenu.add( logFilesMenu ); JMenuItem settingsMenu = new JMenuItem( "Icon Manager" ); settingsMenu.addActionListener( new ActionListener() diff --git a/src/instrument/tap/stream/AdditiveFloatTap.java b/src/instrument/tap/stream/AdditiveFloatTap.java new file mode 100644 index 00000000..31ff2d23 --- /dev/null +++ b/src/instrument/tap/stream/AdditiveFloatTap.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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. + * + * 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 instrument.tap.stream; + + +public class AdditiveFloatTap extends FloatTap +{ + private float mValue; + + public AdditiveFloatTap( String name, + int delay, + float sampleRateRatio, + float addValue ) + { + super( name, delay, sampleRateRatio ); + + mValue = addValue; + } + + @Override + public void receive( float sample ) + { + super.receive( sample + mValue ); + } +} diff --git a/src/instrument/tap/stream/GainFloatTap.java b/src/instrument/tap/stream/GainFloatTap.java new file mode 100644 index 00000000..3a064a00 --- /dev/null +++ b/src/instrument/tap/stream/GainFloatTap.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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. + * + * 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 instrument.tap.stream; + + +public class GainFloatTap extends FloatTap +{ + private float mGain; + + public GainFloatTap( String name, + int delay, + float sampleRateRatio, + float gain ) + { + super( name, delay, sampleRateRatio ); + + mGain = gain; + } + + @Override + public void receive( float sample ) + { + super.receive( sample * mGain ); + } +} diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index bf6a96da..8ff69422 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -24,6 +24,9 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.ComplexSample; @@ -34,6 +37,7 @@ import source.tuner.FrequencyChangeEvent.Attribute; import util.Oscillator; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; +import decode.p25.P25Decoder; import dsp.filter.ComplexFilter; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; @@ -42,6 +46,9 @@ public class TunerChannelSource extends ComplexSource implements FrequencyChangeListener, Listener { + private final static Logger mLog = + LoggerFactory.getLogger( TunerChannelSource.class ); + private static int sCHANNEL_RATE = 48000; private static int sCHANNEL_PASS_FREQUENCY = 12500; @@ -87,9 +94,9 @@ public class TunerChannelSource extends ComplexSource frequencyChanged( new FrequencyChangeEvent( Attribute.SAMPLE_RATE, mTuner.getSampleRate() ) ); - /* Schedule the decimation task to run 20 times a second */ + /* Schedule the decimation task to run 50 times a second */ mTaskHandle = mThreadPoolManager.schedule( ThreadType.DECIMATION, - new DecimationProcessor(), 50, TimeUnit.MILLISECONDS ); + new DecimationProcessor(), 20, TimeUnit.MILLISECONDS ); /* Finally, register to receive samples from the tuner */ mTuner.addListener( (Listener)this ); @@ -243,41 +250,51 @@ public class TunerChannelSource extends ComplexSource @Override public void run() { - List sampleBuffers = - new ArrayList(); - - if( mBuffer != null ) + /* General exception handler so that any errors won't kill the + * decimation thread and cause the input buffers to fill up and + * run the program out of memory */ + try { - /* Limit to 10, so that after a garbage collect we don't - * induce wild swings as it tries to take tons of samples - * and process them, causing delays and buffer refill */ - mBuffer.drainTo( sampleBuffers, 10 ); - - for( ComplexBuffer sampleArray: sampleBuffers ) + List sampleBuffers = + new ArrayList(); + + if( mBuffer != null ) { - float[] samples = sampleArray.getSamples(); - - for( int x = 0; x < samples.length; x += 2 ) + /* Limit to 4, so that after a garbage collect we don't + * induce wild swings as it tries to take tons of samples + * and process them, causing delays and buffer refill */ + mBuffer.drainTo( sampleBuffers, 4 ); + + for( ComplexBuffer sampleArray: sampleBuffers ) { - Float left = samples[ x ]; - Float right = samples[ x + 1 ]; + float[] samples = sampleArray.getSamples(); - if( left != null && right != null ) + for( int x = 0; x < samples.length; x += 2 ) { - /* perform frequency translation */ - ComplexSample translated = ComplexSample.multiply( - mSineWaveGenerator.nextComplex(), left, right ); - - /* decimate the sample rate */ - if( mDecimationFilters[ 0 ] != null ) - { - mDecimationFilters[ 0 ].receive( translated ); - } + Float left = samples[ x ]; + Float right = samples[ x + 1 ]; + + if( left != null && right != null ) + { + /* perform frequency translation */ + ComplexSample translated = ComplexSample.multiply( + mSineWaveGenerator.nextComplex(), left, right ); + + /* decimate the sample rate */ + if( mDecimationFilters[ 0 ] != null ) + { + mDecimationFilters[ 0 ].receive( translated ); + } + } } } + + sampleBuffers.clear(); } - - sampleBuffers.clear(); + } + catch( Exception e ) + { + mLog.error( "encountered an error during decimation process", e ); } } } diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java index aadb63ed..66461a43 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -1115,15 +1115,22 @@ public class HackRFTunerController extends TunerController @Override public void run() { - ArrayList buffers = new ArrayList(); - - mFilledBuffers.drainTo( buffers ); - - for( byte[] buffer: buffers ) + try { - float[] samples = mSampleAdapter.convert( buffer ); + ArrayList buffers = new ArrayList(); - broadcast( new ComplexBuffer( samples ) ); + mFilledBuffers.drainTo( buffers ); + + for( byte[] buffer: buffers ) + { + float[] samples = mSampleAdapter.convert( buffer ); + + broadcast( new ComplexBuffer( samples ) ); + } + } + catch( Exception e ) + { + mLog.error( "error duruing HackRF buffer dispatching", e ); } } } diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index 8995043a..91db8979 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -1611,15 +1611,22 @@ public abstract class RTL2832TunerController extends TunerController @Override public void run() { - ArrayList buffers = new ArrayList(); - - mFilledBuffers.drainTo( buffers ); - - for( byte[] buffer: buffers ) + try { - float[] samples = mSampleAdapter.convert( buffer ); + ArrayList buffers = new ArrayList(); - broadcast( new ComplexBuffer( samples ) ); + mFilledBuffers.drainTo( buffers ); + + for( byte[] buffer: buffers ) + { + float[] samples = mSampleAdapter.convert( buffer ); + + broadcast( new ComplexBuffer( samples ) ); + } + } + catch( Exception e ) + { + mLog.error( "error duing rtl2832 buffer dispatcher run", e ); } } } diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index 98a9bd55..d445dc57 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -400,14 +400,21 @@ public class DFTProcessor implements Listener, @Override public void run() { - /* Only run if we're not currently running */ - if( mRunning.compareAndSet( false, true ) ) + try { - checkFFTSize(); + /* Only run if we're not currently running */ + if( mRunning.compareAndSet( false, true ) ) + { + checkFFTSize(); - calculate(); - - mRunning.set( false ); + calculate(); + + mRunning.set( false ); + } + } + catch( Exception e ) + { + mLog.error( "error during dft processor calculation task", e ); } } }