mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 09:37:36 -05:00
Merge pull request #178 from DSheirer/165-overflowable-buffer-issue-causes-channels-decoding-stop
165 overflowable buffer issue causes channels decoding stop
This commit is contained in:
1
.idea/dictionaries/denny.xml
generated
1
.idea/dictionaries/denny.xml
generated
@@ -7,6 +7,7 @@
|
|||||||
<w>fleetsync</w>
|
<w>fleetsync</w>
|
||||||
<w>icecast</w>
|
<w>icecast</w>
|
||||||
<w>ident</w>
|
<w>ident</w>
|
||||||
|
<w>overflowable</w>
|
||||||
<w>polyphase</w>
|
<w>polyphase</w>
|
||||||
<w>remez</w>
|
<w>remez</w>
|
||||||
<w>samplerate</w>
|
<w>samplerate</w>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
0.3.0-beta1 20170129
|
0.3.0-beta1 20170129
|
||||||
|
- REQUIRES: Java 8 (or newer) for compatibility and eventual changeover to JavaFX
|
||||||
- FEATURE: audio streaming - adds capability to stream to multiple remote broadcast
|
- FEATURE: audio streaming - adds capability to stream to multiple remote broadcast
|
||||||
servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each
|
servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each
|
||||||
stream can be independently configured with a delay. Audio calls are directed
|
stream can be independently configured with a delay. Audio calls are directed
|
||||||
to one or more streams by attaching one or more stream identifiers to each alias.
|
to one or more streams by attaching one or more stream identifiers to each alias.
|
||||||
- Requires Java 8 (or newer) for compatibility and eventual changeover to JavaFX
|
|
||||||
- Extensive GUI updates for tuner, channel and alias management. Includes new
|
- Extensive GUI updates for tuner, channel and alias management. Includes new
|
||||||
'Now Playing' channel view and 'Detail' view.
|
'Now Playing' channel view and 'Detail' view.
|
||||||
- Updated alias list management of alias id values and wildcards. Weighting is now
|
- Updated alias list management of alias id values and wildcards. Weighting is now
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
- Implemented bounded queues for both tuner and individual channel IQ sample
|
- Implemented bounded queues for both tuner and individual channel IQ sample
|
||||||
buffers. Bounded queues have ability to enter/exit overflow state when queue
|
buffers. Bounded queues have ability to enter/exit overflow state when queue
|
||||||
size reaches overflow threshold and reset when queue size falls below the reset
|
size reaches overflow threshold and reset when queue size falls below the reset
|
||||||
threshold.
|
threshold. Channel state will show OVERFLOW in red/yellow.
|
||||||
- Updated airspy controller for airspy-mini specific sample rate handling.
|
- Updated airspy controller for airspy-mini specific sample rate handling.
|
||||||
- New developer remez filter designer tool and internal usage
|
- New developer remez filter designer tool and internal usage
|
||||||
|
|
||||||
|
|||||||
@@ -168,11 +168,14 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
for(AliasAction action: alias.getAction())
|
if(!(alias instanceof PatchGroupAlias))
|
||||||
{
|
{
|
||||||
if(!aliasActions.contains(action))
|
for(AliasAction action: alias.getAction())
|
||||||
{
|
{
|
||||||
aliasActions.add(action);
|
if(!aliasActions.contains(action))
|
||||||
|
{
|
||||||
|
aliasActions.add(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,7 +188,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
{
|
{
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasActions())
|
if(!(alias instanceof PatchGroupAlias) && alias.hasActions())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -201,7 +204,9 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasCallPriority() && alias.getCallPriority() < highestPriority)
|
if(!(alias instanceof PatchGroupAlias) &&
|
||||||
|
alias.hasCallPriority() &&
|
||||||
|
alias.getCallPriority() < highestPriority)
|
||||||
{
|
{
|
||||||
highestPriority = alias.getCallPriority();
|
highestPriority = alias.getCallPriority();
|
||||||
}
|
}
|
||||||
@@ -220,7 +225,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasCallPriority())
|
if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -239,7 +244,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.isRecordable())
|
if(!(alias instanceof PatchGroupAlias) && alias.isRecordable())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -258,7 +263,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.isStreamable())
|
if(!(alias instanceof PatchGroupAlias) && alias.isStreamable())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -279,11 +284,14 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels())
|
if(!(alias instanceof PatchGroupAlias))
|
||||||
{
|
{
|
||||||
if(!broadcastChannels.contains(broadcastChannel))
|
for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels())
|
||||||
{
|
{
|
||||||
broadcastChannels.add(broadcastChannel);
|
if(!broadcastChannels.contains(broadcastChannel))
|
||||||
|
{
|
||||||
|
broadcastChannels.add(broadcastChannel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,7 +309,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasBroadcastChannel(channel))
|
if(!(alias instanceof PatchGroupAlias) && alias.hasBroadcastChannel(channel))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,10 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
|||||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
|
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
|
||||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
|
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
|
||||||
break;
|
break;
|
||||||
|
case OVERFLOW:
|
||||||
|
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "overflow", Color.RED);
|
||||||
|
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "overflow", Color.YELLOW);
|
||||||
|
break;
|
||||||
case RESET:
|
case RESET:
|
||||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
|
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
|
||||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);
|
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class ChannelMetadataViewer extends JPanel
|
|||||||
mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager);
|
mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager);
|
||||||
mCallEventPanel = new CallEventPanel(iconManager);
|
mCallEventPanel = new CallEventPanel(iconManager);
|
||||||
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
|
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
|
||||||
mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager);
|
// mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager);
|
||||||
mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager);
|
mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import module.decode.event.ICallEventProvider;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
|
import sample.real.IOverflowListener;
|
||||||
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@@ -42,7 +43,7 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
|
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
|
||||||
IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener
|
IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener, IOverflowListener
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
|
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
|
|
||||||
private boolean mSquelchLocked = false;
|
private boolean mSquelchLocked = false;
|
||||||
private boolean mSelected = false;
|
private boolean mSelected = false;
|
||||||
|
private boolean mSourceOverflow = false;
|
||||||
|
|
||||||
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
|
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
|
||||||
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
|
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
|
||||||
@@ -123,6 +125,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
|
|
||||||
mState = State.IDLE;
|
mState = State.IDLE;
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
||||||
|
mSourceOverflow = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -350,15 +353,26 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
|
||||||
}
|
mSourceOverflow ? State.OVERFLOW : mState));
|
||||||
else
|
|
||||||
{
|
|
||||||
// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is invoked if the source buffer provider goes into overflow state. Since this is an external state,
|
||||||
|
* we use the mSourceOverflow variable to override the internal state reported to external listeners.
|
||||||
|
* @param overflow true to indicate an overflow state
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void sourceOverflow(boolean overflow)
|
||||||
|
{
|
||||||
|
mSourceOverflow = overflow;
|
||||||
|
|
||||||
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
|
||||||
|
mSourceOverflow ? State.OVERFLOW : mState));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the state and processes related actions
|
* Sets the state and processes related actions
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -51,6 +51,14 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
|
|||||||
return mAliasList != null;
|
return mAliasList != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides subclass reference to the call event broadcaster
|
||||||
|
*/
|
||||||
|
protected Broadcaster<CallEvent> getCallEventBroadcaster()
|
||||||
|
{
|
||||||
|
return mCallEventBroadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the decoder state to prepare for processing a different sample
|
* Reset the decoder state to prepare for processing a different sample
|
||||||
* source
|
* source
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* SDR Trunk
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
@@ -23,98 +23,94 @@ import java.util.EnumSet;
|
|||||||
* Details the set of states for a channel and the allowable transition states
|
* Details the set of states for a channel and the allowable transition states
|
||||||
*/
|
*/
|
||||||
public enum State
|
public enum State
|
||||||
{
|
{
|
||||||
IDLE( "IDLE" )
|
IDLE("IDLE")
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean canChangeTo( State state )
|
|
||||||
{
|
{
|
||||||
return state != TEARDOWN &&
|
@Override
|
||||||
state != RESET;
|
public boolean canChangeTo(State state)
|
||||||
}
|
{
|
||||||
},
|
return state != TEARDOWN && state != RESET && state != OVERFLOW;
|
||||||
CALL( "CALL" )
|
}
|
||||||
{
|
},
|
||||||
@Override
|
CALL("CALL")
|
||||||
public boolean canChangeTo( State state )
|
|
||||||
{
|
{
|
||||||
return state == CONTROL ||
|
@Override
|
||||||
state == DATA ||
|
public boolean canChangeTo(State state)
|
||||||
state == ENCRYPTED ||
|
{
|
||||||
state == FADE ||
|
return state == CONTROL || state == DATA || state == ENCRYPTED || state == FADE || state == TEARDOWN;
|
||||||
state == TEARDOWN;
|
}
|
||||||
}
|
},
|
||||||
},
|
DATA("DATA")
|
||||||
DATA( "DATA" )
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean canChangeTo( State state )
|
|
||||||
{
|
{
|
||||||
return state == CALL ||
|
@Override
|
||||||
state == CONTROL ||
|
public boolean canChangeTo(State state)
|
||||||
state == ENCRYPTED ||
|
{
|
||||||
state == FADE ||
|
return state == CALL || state == CONTROL || state == ENCRYPTED || state == FADE || state == TEARDOWN;
|
||||||
state == TEARDOWN;
|
}
|
||||||
}
|
},
|
||||||
},
|
ENCRYPTED("ENCRYPTED")
|
||||||
ENCRYPTED( "ENCRYPTED" )
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return state == FADE || state == TEARDOWN;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CONTROL("CONTROL")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return state == IDLE || state == FADE;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FADE("FADE")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return state != FADE && state != RESET && state != OVERFLOW;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OVERFLOW("OVERFLOW")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RESET("RESET")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return state == IDLE;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TEARDOWN("TEARDOWN")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return state == RESET;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private String mDisplayValue;
|
||||||
|
|
||||||
|
public static final EnumSet<State> CALL_STATES = EnumSet.of(CALL, CONTROL, DATA, ENCRYPTED);
|
||||||
|
public static final EnumSet<State> IDLE_STATES = EnumSet.of(IDLE, FADE);
|
||||||
|
|
||||||
|
private State(String displayValue)
|
||||||
{
|
{
|
||||||
@Override
|
mDisplayValue = displayValue;
|
||||||
public boolean canChangeTo( State state )
|
}
|
||||||
{
|
|
||||||
return state == FADE || state == TEARDOWN;
|
public abstract boolean canChangeTo(State state);
|
||||||
}
|
|
||||||
},
|
public String getDisplayValue()
|
||||||
CONTROL( "CONTROL" )
|
|
||||||
{
|
{
|
||||||
@Override
|
return mDisplayValue;
|
||||||
public boolean canChangeTo( State state )
|
}
|
||||||
{
|
|
||||||
return state == IDLE ||
|
|
||||||
state == FADE;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FADE( "FADE" )
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean canChangeTo( State state )
|
|
||||||
{
|
|
||||||
return state != FADE &&
|
|
||||||
state != RESET;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RESET( "RESET" )
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean canChangeTo(State state)
|
|
||||||
{
|
|
||||||
return state == IDLE;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
TEARDOWN( "TEARDOWN" )
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean canChangeTo(State state)
|
|
||||||
{
|
|
||||||
return state == RESET;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private String mDisplayValue;
|
|
||||||
|
|
||||||
public static final EnumSet<State> CALL_STATES = EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED );
|
|
||||||
public static final EnumSet<State> IDLE_STATES = EnumSet.of( IDLE, FADE );
|
|
||||||
|
|
||||||
private State( String displayValue )
|
|
||||||
{
|
|
||||||
mDisplayValue = displayValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract boolean canChangeTo( State state );
|
|
||||||
|
|
||||||
public String getDisplayValue()
|
|
||||||
{
|
|
||||||
return mDisplayValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
package controller;
|
package controller;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import util.ThreadPool;
|
||||||
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@@ -8,24 +12,23 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
*/
|
*/
|
||||||
public class NamingThreadFactory implements ThreadFactory
|
public class NamingThreadFactory implements ThreadFactory
|
||||||
{
|
{
|
||||||
static final AtomicInteger poolNumber = new AtomicInteger(1);
|
private final static Logger mLog = LoggerFactory.getLogger(ThreadPool.class);
|
||||||
|
|
||||||
|
private static final AtomicInteger mPoolNumber = new AtomicInteger(1);
|
||||||
|
|
||||||
final ThreadGroup mThreadGroup;
|
private final ThreadGroup mThreadGroup;
|
||||||
|
|
||||||
final AtomicInteger mThreadNumber = new AtomicInteger(1);
|
private final AtomicInteger mThreadNumber = new AtomicInteger(1);
|
||||||
|
|
||||||
final String mNamePrefix;
|
private final String mNamePrefix;
|
||||||
|
|
||||||
public NamingThreadFactory( String prefix )
|
public NamingThreadFactory( String prefix )
|
||||||
{
|
{
|
||||||
SecurityManager s = System.getSecurityManager();
|
SecurityManager s = System.getSecurityManager();
|
||||||
|
|
||||||
mThreadGroup = (s != null)? s.getThreadGroup() :
|
mThreadGroup = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
|
||||||
Thread.currentThread().getThreadGroup();
|
|
||||||
|
|
||||||
mNamePrefix = prefix + " pool-" +
|
mNamePrefix = prefix + " pool-" + mPoolNumber.getAndIncrement() + "-thread-";
|
||||||
poolNumber.getAndIncrement() +
|
|
||||||
"-thread-";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thread newThread( Runnable runnable )
|
public Thread newThread( Runnable runnable )
|
||||||
@@ -42,7 +45,16 @@ public class NamingThreadFactory implements ThreadFactory
|
|||||||
{
|
{
|
||||||
thread.setPriority( Thread.NORM_PRIORITY );
|
thread.setPriority( Thread.NORM_PRIORITY );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread t, Throwable e)
|
||||||
|
{
|
||||||
|
mLog.error("Error while executing runnable in scheduled thread pool [" + t.getName() + "]", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,6 +538,9 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
module.reset();
|
module.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Setup the channel state to monitor source overflow conditions
|
||||||
|
mSource.setOverflowListener(mChannelState);
|
||||||
|
|
||||||
/* Register with the source to receive sample data. Setup a
|
/* Register with the source to receive sample data. Setup a
|
||||||
* timer task to process the buffer queues 50 times a second
|
* timer task to process the buffer queues 50 times a second
|
||||||
* (every 20 ms) */
|
* (every 20 ms) */
|
||||||
@@ -606,6 +609,7 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
if(mSource != null)
|
if(mSource != null)
|
||||||
{
|
{
|
||||||
mSource.stop();
|
mSource.stop();
|
||||||
|
mSource.setOverflowListener(null);
|
||||||
|
|
||||||
switch(mSource.getSampleType())
|
switch(mSource.getSampleType())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ package module.decode.p25;
|
|||||||
|
|
||||||
import alias.Alias;
|
import alias.Alias;
|
||||||
import alias.AliasList;
|
import alias.AliasList;
|
||||||
import alias.PatchGroupAlias;
|
|
||||||
import alias.id.AliasIDType;
|
import alias.id.AliasIDType;
|
||||||
import alias.id.talkgroup.TalkgroupID;
|
|
||||||
import channel.metadata.AliasedStringAttributeMonitor;
|
import channel.metadata.AliasedStringAttributeMonitor;
|
||||||
import channel.metadata.Attribute;
|
import channel.metadata.Attribute;
|
||||||
import channel.metadata.AttributeChangeRequest;
|
import channel.metadata.AttributeChangeRequest;
|
||||||
@@ -163,7 +161,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
private Map<Integer,IdentifierUpdate> mBands = new HashMap<>();
|
private Map<Integer,IdentifierUpdate> mBands = new HashMap<>();
|
||||||
private Map<String,Long> mRegistrations = new HashMap<>();
|
private Map<String,Long> mRegistrations = new HashMap<>();
|
||||||
private Map<String,IAdjacentSite> mNeighborMap = new HashMap<>();
|
private Map<String,IAdjacentSite> mNeighborMap = new HashMap<>();
|
||||||
private Map<String,List<String>> mPatchGroupMap = new HashMap<>();
|
|
||||||
|
|
||||||
private String mLastCommandEventID;
|
private String mLastCommandEventID;
|
||||||
private String mLastPageEventID;
|
private String mLastPageEventID;
|
||||||
@@ -188,6 +185,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
private P25CallEvent mCurrentCallEvent;
|
private P25CallEvent mCurrentCallEvent;
|
||||||
private List<String> mCallDetectTalkgroups = new ArrayList<>();
|
private List<String> mCallDetectTalkgroups = new ArrayList<>();
|
||||||
private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>();
|
private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>();
|
||||||
|
private PatchGroupManager mPatchGroupManager;
|
||||||
|
|
||||||
public P25DecoderState(AliasList aliasList,
|
public P25DecoderState(AliasList aliasList,
|
||||||
ChannelType channelType,
|
ChannelType channelType,
|
||||||
@@ -199,6 +197,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
mModulation = modulation;
|
mModulation = modulation;
|
||||||
mIgnoreDataCalls = ignoreDataCalls;
|
mIgnoreDataCalls = ignoreDataCalls;
|
||||||
|
|
||||||
|
mPatchGroupManager = new PatchGroupManager(aliasList, getCallEventBroadcaster());
|
||||||
mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2,
|
mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2,
|
||||||
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
|
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
|
||||||
mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM,
|
mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM,
|
||||||
@@ -2719,6 +2718,9 @@ public class P25DecoderState extends DecoderState
|
|||||||
switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode())
|
switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode())
|
||||||
{
|
{
|
||||||
case PATCH_GROUP_CHANNEL_GRANT:
|
case PATCH_GROUP_CHANNEL_GRANT:
|
||||||
|
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
|
||||||
|
mPatchGroupManager.cleanupPatchGroups();
|
||||||
|
|
||||||
PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk;
|
PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk;
|
||||||
|
|
||||||
channel = pgvcg.getChannel();
|
channel = pgvcg.getChannel();
|
||||||
@@ -2754,6 +2756,9 @@ public class P25DecoderState extends DecoderState
|
|||||||
mChannelCallMap.get(channel)));
|
mChannelCallMap.get(channel)));
|
||||||
break;
|
break;
|
||||||
case PATCH_GROUP_CHANNEL_GRANT_UPDATE:
|
case PATCH_GROUP_CHANNEL_GRANT_UPDATE:
|
||||||
|
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
|
||||||
|
mPatchGroupManager.cleanupPatchGroups();
|
||||||
|
|
||||||
PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk;
|
PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk;
|
||||||
|
|
||||||
channel = gvcgu.getChannel1();
|
channel = gvcgu.getChannel1();
|
||||||
@@ -2784,35 +2789,11 @@ public class P25DecoderState extends DecoderState
|
|||||||
break;
|
break;
|
||||||
case PATCH_GROUP_ADD:
|
case PATCH_GROUP_ADD:
|
||||||
PatchGroupAdd pga = (PatchGroupAdd)tsbk;
|
PatchGroupAdd pga = (PatchGroupAdd)tsbk;
|
||||||
|
mPatchGroupManager.updatePatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups());
|
||||||
if(addPatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups()))
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
sb.append("PATCH GROUP:").append(pga.getPatchGroupAddress());
|
|
||||||
sb.append(" ").append(pga.getPatchedTalkgroups());
|
|
||||||
|
|
||||||
broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_ADD)
|
|
||||||
.aliasList(getAliasList())
|
|
||||||
.details(sb.toString())
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case PATCH_GROUP_DELETE:
|
case PATCH_GROUP_DELETE:
|
||||||
PatchGroupDelete pgd = (PatchGroupDelete)tsbk;
|
PatchGroupDelete pgd = (PatchGroupDelete)tsbk;
|
||||||
|
mPatchGroupManager.removePatchGroup(pgd.getPatchGroupAddress());
|
||||||
if(removePatchGroup(pgd.getPatchGroupAddress(), pgd.getPatchedTalkgroups()))
|
|
||||||
{
|
|
||||||
StringBuilder sbpgd = new StringBuilder();
|
|
||||||
|
|
||||||
sbpgd.append("PATCH GROUP:").append(pgd.getPatchGroupAddress());
|
|
||||||
sbpgd.append(" ").append(pgd.getPatchedTalkgroups());
|
|
||||||
|
|
||||||
broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_DELETE)
|
|
||||||
.aliasList(getAliasList())
|
|
||||||
.details(sbpgd.toString())
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CCH_PLANNED_SHUTDOWN:
|
case CCH_PLANNED_SHUTDOWN:
|
||||||
if(!mControlChannelShutdownLogged)
|
if(!mControlChannelShutdownLogged)
|
||||||
@@ -2827,174 +2808,14 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new patch group or updates an existing patch group with the patched talkgroup entries.
|
|
||||||
*
|
|
||||||
* @param patchGroupID for the patch group
|
|
||||||
* @param talkgroupsToAdd to include in the patch group
|
|
||||||
* @return true if the patch group or the patch group map was updated
|
|
||||||
*/
|
|
||||||
private boolean addPatchGroup(String patchGroupID, List<String> talkgroupsToAdd)
|
|
||||||
{
|
|
||||||
boolean updated = false;
|
|
||||||
|
|
||||||
if(patchGroupID != null && !talkgroupsToAdd.isEmpty())
|
|
||||||
{
|
|
||||||
List<String> patchedTalkgroups = mPatchGroupMap.get(patchGroupID);
|
|
||||||
|
|
||||||
if(patchedTalkgroups == null)
|
|
||||||
{
|
|
||||||
patchedTalkgroups = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
for(String talkgroupToAdd : talkgroupsToAdd)
|
|
||||||
{
|
|
||||||
//Exclude the patch group ID if it is included in the patched talkgroup list so that we don't have
|
|
||||||
//an infinite loop situation
|
|
||||||
if(talkgroupToAdd.equals(patchGroupID))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!patchedTalkgroups.contains(talkgroupToAdd))
|
|
||||||
{
|
|
||||||
patchedTalkgroups.add(talkgroupToAdd);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(updated)
|
|
||||||
{
|
|
||||||
mPatchGroupMap.put(patchGroupID, patchedTalkgroups);
|
|
||||||
updatePatchAlias(patchGroupID, patchedTalkgroups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the patched talkgroups from the patch group and removes the patch group from the patch group map if
|
|
||||||
* there are no more patched talkgroups in the patch group.
|
|
||||||
*
|
|
||||||
* @param patchGroupID for the patch group
|
|
||||||
* @param talkgroupsToRemove to remove from the patch group
|
|
||||||
* @return true if the patch group or the patch group map was updated
|
|
||||||
*/
|
|
||||||
private boolean removePatchGroup(String patchGroupID, List<String> talkgroupsToRemove)
|
|
||||||
{
|
|
||||||
boolean updated = false;
|
|
||||||
|
|
||||||
if(patchGroupID != null && !talkgroupsToRemove.isEmpty())
|
|
||||||
{
|
|
||||||
List<String> patchedTalkgroups = mPatchGroupMap.get(patchGroupID);
|
|
||||||
|
|
||||||
if(patchedTalkgroups != null)
|
|
||||||
{
|
|
||||||
for(String talkgroupToRemove : talkgroupsToRemove)
|
|
||||||
{
|
|
||||||
if(patchedTalkgroups.contains(talkgroupToRemove))
|
|
||||||
{
|
|
||||||
patchedTalkgroups.remove(talkgroupToRemove);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(patchedTalkgroups.isEmpty())
|
|
||||||
{
|
|
||||||
mPatchGroupMap.remove(patchGroupID);
|
|
||||||
removePatchGroupAlias(patchGroupID);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
updatePatchAlias(patchGroupID, patchedTalkgroups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the
|
|
||||||
* patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and
|
|
||||||
* replaced with the aliases corresponding to the patched talkgroup aliases.
|
|
||||||
*
|
|
||||||
* @param patchGroupID for the patch group
|
|
||||||
* @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups
|
|
||||||
*/
|
|
||||||
private void updatePatchAlias(String patchGroupID, List<String> patchedTalkgroups)
|
|
||||||
{
|
|
||||||
PatchGroupAlias patchGroupAlias = null;
|
|
||||||
|
|
||||||
//Check for an existing alias for the patch talkgroup - do not include wildcard aliases
|
|
||||||
Alias existingAlias = getAliasList().getTalkgroupAlias(patchGroupID, false);
|
|
||||||
|
|
||||||
if(existingAlias instanceof PatchGroupAlias)
|
|
||||||
{
|
|
||||||
patchGroupAlias = (PatchGroupAlias)existingAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
patchGroupAlias = new PatchGroupAlias();
|
|
||||||
|
|
||||||
patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID));
|
|
||||||
|
|
||||||
if(existingAlias != null)
|
|
||||||
{
|
|
||||||
getAliasList().removeAlias(existingAlias);
|
|
||||||
|
|
||||||
patchGroupAlias.setPatchGroupAlias(existingAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAliasList().addAlias(patchGroupAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(patchGroupAlias != null)
|
|
||||||
{
|
|
||||||
patchGroupAlias.setPatchedTalkgroupIDs(patchedTalkgroups);
|
|
||||||
|
|
||||||
patchGroupAlias.clearPatchedAliases();
|
|
||||||
|
|
||||||
for(String patchedTalkgroup : patchedTalkgroups)
|
|
||||||
{
|
|
||||||
Alias patchedAlias = getAliasList().getTalkgroupAlias(patchedTalkgroup);
|
|
||||||
|
|
||||||
if(patchedAlias != null)
|
|
||||||
{
|
|
||||||
patchGroupAlias.addPatchedAlias(patchedAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the temporary patch group alias from the alias list
|
|
||||||
*/
|
|
||||||
private void removePatchGroupAlias(String patchGroupID)
|
|
||||||
{
|
|
||||||
Alias alias = getAliasList().getTalkgroupAlias(patchGroupID);
|
|
||||||
|
|
||||||
if(alias instanceof PatchGroupAlias)
|
|
||||||
{
|
|
||||||
PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias;
|
|
||||||
|
|
||||||
getAliasList().removeAlias(patchGroupAlias);
|
|
||||||
|
|
||||||
//Replace our temporary patch group alias with the original alias for the patch group ID
|
|
||||||
if(patchGroupAlias.hasPatchGroupAlias())
|
|
||||||
{
|
|
||||||
getAliasList().addAlias(patchGroupAlias.getPatchGroupAlias());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a traffic channel allocation message
|
* Process a traffic channel allocation message
|
||||||
*/
|
*/
|
||||||
private void processTSBKChannelGrant(TSBKMessage message)
|
private void processTSBKChannelGrant(TSBKMessage message)
|
||||||
{
|
{
|
||||||
|
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
|
||||||
|
mPatchGroupManager.cleanupPatchGroups();
|
||||||
|
|
||||||
String channel = null;
|
String channel = null;
|
||||||
String from = null;
|
String from = null;
|
||||||
String to = null;
|
String to = null;
|
||||||
|
|||||||
102
src/module/decode/p25/PatchGroup.java
Normal file
102
src/module/decode/p25/PatchGroup.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package module.decode.p25;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PatchGroup
|
||||||
|
{
|
||||||
|
private long mExpireThreshold = 5000; //5 seconds
|
||||||
|
private long mLastUpdated;
|
||||||
|
private String mPatchGroupID;
|
||||||
|
private List<String> mPatchedGroups = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a patch group with the specified patch group ID
|
||||||
|
* @param patchGroupID that identifies this patch group
|
||||||
|
*/
|
||||||
|
public PatchGroup(String patchGroupID)
|
||||||
|
{
|
||||||
|
mPatchGroupID = patchGroupID;
|
||||||
|
mLastUpdated = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch group ID for this patch group
|
||||||
|
*/
|
||||||
|
public String getPatchGroupID()
|
||||||
|
{
|
||||||
|
return mPatchGroupID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the patched group to this patch group. Duplicate patched groups are ignored.
|
||||||
|
*
|
||||||
|
* Note: the patch group ID will not be added as a patched group, to avoid circular dependencies.
|
||||||
|
*
|
||||||
|
* @return true if the patched group was added to this patch group
|
||||||
|
*/
|
||||||
|
public boolean addPatchedGroup(String patchedGroup)
|
||||||
|
{
|
||||||
|
if(patchedGroup != null && !patchedGroup.equals(mPatchGroupID) && !mPatchedGroups.contains(patchedGroup))
|
||||||
|
{
|
||||||
|
mPatchedGroups.add(patchedGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patched talkgroups that are part of this patch group.
|
||||||
|
*/
|
||||||
|
public List<String> getPatchedGroups()
|
||||||
|
{
|
||||||
|
return mPatchedGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the timestamp for this patch group.
|
||||||
|
*/
|
||||||
|
public void updateTimestamp()
|
||||||
|
{
|
||||||
|
mLastUpdated = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this patch group is expired, meaning that it hasn't been updated in the past (expire
|
||||||
|
* threshold) seconds.
|
||||||
|
*/
|
||||||
|
public boolean isExpired()
|
||||||
|
{
|
||||||
|
return System.currentTimeMillis() - mLastUpdated > mExpireThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the patch group expiration threshold (default = 5000 ms). A patch group is considered expired if
|
||||||
|
* no new updates have been received within the past (expire threshold) milliseconds
|
||||||
|
*
|
||||||
|
* @param expireThreshold in milliseconds
|
||||||
|
*/
|
||||||
|
public void setExpireThreshold(long expireThreshold)
|
||||||
|
{
|
||||||
|
mExpireThreshold = expireThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
245
src/module/decode/p25/PatchGroupManager.java
Normal file
245
src/module/decode/p25/PatchGroupManager.java
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package module.decode.p25;
|
||||||
|
|
||||||
|
import alias.Alias;
|
||||||
|
import alias.AliasList;
|
||||||
|
import alias.PatchGroupAlias;
|
||||||
|
import alias.id.talkgroup.TalkgroupID;
|
||||||
|
import module.decode.event.CallEvent;
|
||||||
|
import sample.Broadcaster;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PatchGroupManager
|
||||||
|
{
|
||||||
|
private AliasList mAliasList;
|
||||||
|
private List<PatchGroup> mPatchGroups = new ArrayList<>();
|
||||||
|
private Broadcaster<CallEvent> mCallEventBroadcaster;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages active P25 patch groups and auto-expires patch groups if not updated every 5 seconds.
|
||||||
|
*
|
||||||
|
* @param aliasList to keep updated with patch group changes
|
||||||
|
* @param broadcaster for rebroadcasting patch group related call events
|
||||||
|
*/
|
||||||
|
public PatchGroupManager(AliasList aliasList, Broadcaster<CallEvent> broadcaster)
|
||||||
|
{
|
||||||
|
mAliasList = aliasList;
|
||||||
|
mCallEventBroadcaster = broadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the patch group.
|
||||||
|
*
|
||||||
|
* @param patchGroupID of the patch group to remove
|
||||||
|
*/
|
||||||
|
public void removePatchGroup(String patchGroupID)
|
||||||
|
{
|
||||||
|
removePatchGroup(patchGroupID, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the patch group.
|
||||||
|
*
|
||||||
|
* @param patchGroupID of the patch group to remove
|
||||||
|
* @param expired true if the patch group is being removed because it expired, false if we receive a
|
||||||
|
* message commanding commanding a patch group delete.
|
||||||
|
*/
|
||||||
|
private void removePatchGroup(String patchGroupID, boolean expired)
|
||||||
|
{
|
||||||
|
PatchGroup patchGroup = getPatchGroup(patchGroupID);
|
||||||
|
|
||||||
|
if(patchGroup != null)
|
||||||
|
{
|
||||||
|
//Cleanup the alias list
|
||||||
|
if(mAliasList != null)
|
||||||
|
{
|
||||||
|
Alias alias = mAliasList.getTalkgroupAlias(patchGroupID);
|
||||||
|
|
||||||
|
if(alias instanceof PatchGroupAlias)
|
||||||
|
{
|
||||||
|
PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias;
|
||||||
|
|
||||||
|
mAliasList.removeAlias(patchGroupAlias);
|
||||||
|
|
||||||
|
//Replace our temporary patch group alias with the original alias for the patch group ID
|
||||||
|
if(patchGroupAlias.hasPatchGroupAlias())
|
||||||
|
{
|
||||||
|
mAliasList.addAlias(patchGroupAlias.getPatchGroupAlias());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPatchGroups.remove(patchGroup);
|
||||||
|
|
||||||
|
if(mCallEventBroadcaster != null)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append((expired ? "EXPIRED " : "DELETED "));
|
||||||
|
sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID());
|
||||||
|
sb.append(" ").append(patchGroup.getPatchedGroups());
|
||||||
|
|
||||||
|
|
||||||
|
mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_DELETE)
|
||||||
|
.to(patchGroup.getPatchGroupID())
|
||||||
|
.details(sb.toString())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the
|
||||||
|
* patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and
|
||||||
|
* replaced with the aliases corresponding to the patched talkgroup aliases.
|
||||||
|
*
|
||||||
|
* @param patchGroupID for the patch group
|
||||||
|
* @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups
|
||||||
|
*/
|
||||||
|
public void updatePatchGroup(String patchGroupID, List<String> patchedTalkgroups)
|
||||||
|
{
|
||||||
|
boolean updated = false;
|
||||||
|
|
||||||
|
PatchGroup patchGroup = getPatchGroup(patchGroupID);
|
||||||
|
|
||||||
|
if(patchGroup == null)
|
||||||
|
{
|
||||||
|
//Remove any expired patch groups before we add a new one
|
||||||
|
cleanupPatchGroups();
|
||||||
|
patchGroup = new PatchGroup(patchGroupID);
|
||||||
|
mPatchGroups.add(patchGroup);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
patchGroup.updateTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String patchedGroup : patchedTalkgroups)
|
||||||
|
{
|
||||||
|
if(patchGroup.addPatchedGroup(patchedGroup))
|
||||||
|
{
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we make any changes to the patch group, update the patch group alias
|
||||||
|
if(updated)
|
||||||
|
{
|
||||||
|
PatchGroupAlias patchGroupAlias = null;
|
||||||
|
|
||||||
|
//Check for an existing alias for the patch talkgroup - do not include wildcard aliases
|
||||||
|
if(mAliasList != null)
|
||||||
|
{
|
||||||
|
Alias existingAlias = mAliasList.getTalkgroupAlias(patchGroupID, false);
|
||||||
|
|
||||||
|
if(existingAlias instanceof PatchGroupAlias)
|
||||||
|
{
|
||||||
|
patchGroupAlias = (PatchGroupAlias)existingAlias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
patchGroupAlias = new PatchGroupAlias();
|
||||||
|
|
||||||
|
patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID));
|
||||||
|
|
||||||
|
if(existingAlias != null)
|
||||||
|
{
|
||||||
|
mAliasList.removeAlias(existingAlias);
|
||||||
|
|
||||||
|
patchGroupAlias.setPatchGroupAlias(existingAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAliasList.addAlias(patchGroupAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(patchGroupAlias != null)
|
||||||
|
{
|
||||||
|
patchGroupAlias.setPatchedTalkgroupIDs(patchGroup.getPatchedGroups());
|
||||||
|
|
||||||
|
patchGroupAlias.clearPatchedAliases();
|
||||||
|
|
||||||
|
for(String patchedTalkgroup : patchGroup.getPatchedGroups())
|
||||||
|
{
|
||||||
|
Alias patchedAlias = mAliasList.getTalkgroupAlias(patchedTalkgroup);
|
||||||
|
|
||||||
|
if(patchedAlias != null)
|
||||||
|
{
|
||||||
|
patchGroupAlias.addPatchedAlias(patchedAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mCallEventBroadcaster != null)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID());
|
||||||
|
sb.append(" ").append(patchGroup.getPatchedGroups());
|
||||||
|
|
||||||
|
mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_ADD)
|
||||||
|
.aliasList(mAliasList)
|
||||||
|
.details(sb.toString())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the patch group matching the patch group ID or null
|
||||||
|
*/
|
||||||
|
private PatchGroup getPatchGroup(String patchGroupID)
|
||||||
|
{
|
||||||
|
for(PatchGroup patchGroup : mPatchGroups)
|
||||||
|
{
|
||||||
|
if(patchGroup.getPatchGroupID().equals(patchGroupID))
|
||||||
|
{
|
||||||
|
return patchGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the last updated timestamp on all patch groups and removes any patch groups that have not been
|
||||||
|
* updated in the past 5 seconds
|
||||||
|
*/
|
||||||
|
public void cleanupPatchGroups()
|
||||||
|
{
|
||||||
|
Iterator<PatchGroup> it = mPatchGroups.iterator();
|
||||||
|
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
PatchGroup patchGroup = it.next();
|
||||||
|
|
||||||
|
if(patchGroup.isExpired())
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
|
||||||
|
removePatchGroup(patchGroup.getPatchGroupID(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,20 +18,28 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package sample;
|
package sample;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sample.real.IOverflowListener;
|
||||||
|
import source.tuner.TunerChannelSource;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.LinkedTransferQueue;
|
import java.util.concurrent.LinkedTransferQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class OverflowableTransferQueue<E>
|
public class OverflowableTransferQueue<E>
|
||||||
{
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(OverflowableTransferQueue.class);
|
||||||
|
|
||||||
public enum State {NORMAL, OVERFLOW};
|
public enum State {NORMAL, OVERFLOW};
|
||||||
private Listener<State> mStateListener;
|
private IOverflowListener mOverflowListener;
|
||||||
|
|
||||||
private LinkedTransferQueue<E> mQueue = new LinkedTransferQueue<E>();
|
private LinkedTransferQueue<E> mQueue = new LinkedTransferQueue<E>();
|
||||||
private AtomicInteger mCounter = new AtomicInteger();
|
private AtomicInteger mCounter = new AtomicInteger();
|
||||||
|
private AtomicBoolean mOverflow = new AtomicBoolean();
|
||||||
private int mMaximumSize;
|
private int mMaximumSize;
|
||||||
private int mResetThreshold;
|
private int mResetThreshold;
|
||||||
private boolean mOverflow = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for
|
* Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for
|
||||||
@@ -53,7 +61,7 @@ public class OverflowableTransferQueue<E>
|
|||||||
*/
|
*/
|
||||||
public void offer(E e)
|
public void offer(E e)
|
||||||
{
|
{
|
||||||
if(!mOverflow)
|
if(!mOverflow.get())
|
||||||
{
|
{
|
||||||
mQueue.offer(e);
|
mQueue.offer(e);
|
||||||
|
|
||||||
@@ -73,46 +81,36 @@ public class OverflowableTransferQueue<E>
|
|||||||
{
|
{
|
||||||
int drainCount = mQueue.drainTo(collection, maxElements);
|
int drainCount = mQueue.drainTo(collection, maxElements);
|
||||||
|
|
||||||
if(drainCount > 0)
|
int size = mCounter.addAndGet(-drainCount);
|
||||||
{
|
|
||||||
int size = mCounter.addAndGet(-drainCount);
|
|
||||||
|
|
||||||
if(mOverflow && size <= mResetThreshold)
|
if(mOverflow.get() && size <= mResetThreshold)
|
||||||
{
|
{
|
||||||
setOverflow(false);
|
setOverflow(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return drainCount;
|
return drainCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a listener to receive state change events
|
* Sets a listener to receive overflow state change events
|
||||||
*/
|
*/
|
||||||
public void setStateListener(Listener<State> listener)
|
public void setOverflowListener(IOverflowListener listener)
|
||||||
{
|
{
|
||||||
mStateListener = listener;
|
mOverflowListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current state of the queue, normal or overflow
|
* Toggles the overflow state and broadcast state change to listener
|
||||||
*/
|
|
||||||
public State getState()
|
|
||||||
{
|
|
||||||
return mOverflow ? State.OVERFLOW : State.NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the overflow state
|
|
||||||
*/
|
*/
|
||||||
private void setOverflow(boolean overflow)
|
private void setOverflow(boolean overflow)
|
||||||
{
|
{
|
||||||
mOverflow = overflow;
|
if(mOverflow.compareAndSet(!overflow, overflow))
|
||||||
|
|
||||||
if(mStateListener != null)
|
|
||||||
{
|
{
|
||||||
mStateListener.receive(getState());
|
if(mOverflowListener != null)
|
||||||
|
{
|
||||||
|
mOverflowListener.sourceOverflow(overflow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +123,11 @@ public class OverflowableTransferQueue<E>
|
|||||||
{
|
{
|
||||||
mQueue.clear();
|
mQueue.clear();
|
||||||
mCounter.set(0);
|
mCounter.set(0);
|
||||||
mOverflow = false;
|
mOverflow.set(false);
|
||||||
|
if(mOverflowListener != null)
|
||||||
|
{
|
||||||
|
mOverflowListener.sourceOverflow(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/sample/real/IOverflowListener.java
Normal file
28
src/sample/real/IOverflowListener.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package sample.real;
|
||||||
|
|
||||||
|
public interface IOverflowListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Indicates that the source is in overflow state (true) or out of overflow state (false)
|
||||||
|
* @param overflow true to indicate an overflow state
|
||||||
|
*/
|
||||||
|
void sourceOverflow(boolean overflow);
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ package source;
|
|||||||
|
|
||||||
import module.Module;
|
import module.Module;
|
||||||
import sample.SampleType;
|
import sample.SampleType;
|
||||||
|
import sample.real.IOverflowListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class to define the minimum functionality of a sample data provider.
|
* Abstract class to define the minimum functionality of a sample data provider.
|
||||||
@@ -26,6 +27,7 @@ import sample.SampleType;
|
|||||||
public abstract class Source extends Module
|
public abstract class Source extends Module
|
||||||
{
|
{
|
||||||
protected SampleType mSampleType;
|
protected SampleType mSampleType;
|
||||||
|
protected IOverflowListener mOverflowListener;
|
||||||
|
|
||||||
public Source( SampleType sampleType )
|
public Source( SampleType sampleType )
|
||||||
{
|
{
|
||||||
@@ -47,4 +49,25 @@ public abstract class Source extends Module
|
|||||||
{
|
{
|
||||||
mSampleType = sampleType;
|
mSampleType = sampleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the listener to receive overflow state changes. Use null argument to clear the listener
|
||||||
|
*/
|
||||||
|
public void setOverflowListener(IOverflowListener listener)
|
||||||
|
{
|
||||||
|
mOverflowListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts an overflow state
|
||||||
|
*
|
||||||
|
* @param overflow true if overlow, false if normal
|
||||||
|
*/
|
||||||
|
protected void broadcastOverflowState(boolean overflow)
|
||||||
|
{
|
||||||
|
if(mOverflowListener != null)
|
||||||
|
{
|
||||||
|
mOverflowListener.sourceOverflow(overflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import sample.Listener;
|
|||||||
import sample.OverflowableTransferQueue;
|
import sample.OverflowableTransferQueue;
|
||||||
import sample.complex.Complex;
|
import sample.complex.Complex;
|
||||||
import sample.complex.ComplexBuffer;
|
import sample.complex.ComplexBuffer;
|
||||||
|
import sample.real.IOverflowListener;
|
||||||
import source.ComplexSource;
|
import source.ComplexSource;
|
||||||
import source.SourceException;
|
import source.SourceException;
|
||||||
import source.tuner.frequency.FrequencyChangeEvent;
|
import source.tuner.frequency.FrequencyChangeEvent;
|
||||||
@@ -51,7 +52,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
|||||||
private static final int BUFFER_MAX_CAPACITY = 300;
|
private static final int BUFFER_MAX_CAPACITY = 300;
|
||||||
|
|
||||||
//Threshold for resetting buffer overflow condition
|
//Threshold for resetting buffer overflow condition
|
||||||
private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 200;
|
private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 100;
|
||||||
|
|
||||||
private static int CHANNEL_RATE = 48000;
|
private static int CHANNEL_RATE = 48000;
|
||||||
private static int CHANNEL_PASS_FREQUENCY = 12000;
|
private static int CHANNEL_PASS_FREQUENCY = 12000;
|
||||||
@@ -101,21 +102,6 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
|||||||
mTunerFrequency = mTuner.getTunerController().getFrequency();
|
mTunerFrequency = mTuner.getTunerController().getFrequency();
|
||||||
|
|
||||||
mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD);
|
mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD);
|
||||||
mBuffer.setStateListener(new Listener<OverflowableTransferQueue.State>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void receive(OverflowableTransferQueue.State state)
|
|
||||||
{
|
|
||||||
if(state == OverflowableTransferQueue.State.NORMAL)
|
|
||||||
{
|
|
||||||
mLog.debug("Channel [" + mTunerFrequency + "] buffer overflow - temporary pause until processing catches up");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.debug("Channel [" + mTunerFrequency + "] buffer overflow cleared - resuming normal processing");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Setup the frequency translator to the current source frequency */
|
/* Setup the frequency translator to the current source frequency */
|
||||||
long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
|
long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
|
||||||
@@ -126,6 +112,14 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
|||||||
frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getTunerController().getSampleRate()));
|
frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getTunerController().getSampleRate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the default source overflow listener management to delegate responsibility to the overflow buffer
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setOverflowListener(IOverflowListener listener)
|
||||||
|
{
|
||||||
|
mBuffer.setOverflowListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public void start(ScheduledExecutorService executor)
|
public void start(ScheduledExecutorService executor)
|
||||||
{
|
{
|
||||||
@@ -175,7 +169,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mLog.warn("Attempt to stop() and already stopped tuner channel source was ignored");
|
mLog.warn("Attempt to stop() an already stopped tuner channel source was ignored");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,6 +373,10 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
|||||||
mLog.error("Error encountered during decimation process", e);
|
mLog.error("Error encountered during decimation process", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(Throwable throwable)
|
||||||
|
{
|
||||||
|
mLog.error("Code error encountered during decimation process - channel thread will probably die", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check to see if we've been shutdown */
|
/* Check to see if we've been shutdown */
|
||||||
if(!mProcessing)
|
if(!mProcessing)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import sample.Listener;
|
|||||||
import sample.OverflowableTransferQueue;
|
import sample.OverflowableTransferQueue;
|
||||||
import sample.adapter.ISampleAdapter;
|
import sample.adapter.ISampleAdapter;
|
||||||
import sample.complex.ComplexBuffer;
|
import sample.complex.ComplexBuffer;
|
||||||
|
import sample.real.IOverflowListener;
|
||||||
import source.tuner.TunerManager;
|
import source.tuner.TunerManager;
|
||||||
import util.ThreadPool;
|
import util.ThreadPool;
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ public class USBTransferProcessor implements TransferCallback
|
|||||||
private static final int FILLED_BUFFER_MAX_CAPACITY = 300;
|
private static final int FILLED_BUFFER_MAX_CAPACITY = 300;
|
||||||
|
|
||||||
//Threshold for resetting buffer overflow condition
|
//Threshold for resetting buffer overflow condition
|
||||||
private static final int FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD = 200;
|
private static final int FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD = 100;
|
||||||
|
|
||||||
private String mDeviceName;
|
private String mDeviceName;
|
||||||
|
|
||||||
@@ -101,12 +102,12 @@ public class USBTransferProcessor implements TransferCallback
|
|||||||
mBufferSize = bufferSize;
|
mBufferSize = bufferSize;
|
||||||
|
|
||||||
mFilledBuffers = new OverflowableTransferQueue<>(FILLED_BUFFER_MAX_CAPACITY, FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD);
|
mFilledBuffers = new OverflowableTransferQueue<>(FILLED_BUFFER_MAX_CAPACITY, FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD);
|
||||||
mFilledBuffers.setStateListener(new Listener<OverflowableTransferQueue.State>()
|
mFilledBuffers.setOverflowListener(new IOverflowListener()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void receive(OverflowableTransferQueue.State state)
|
public void sourceOverflow(boolean overflow)
|
||||||
{
|
{
|
||||||
if(state == OverflowableTransferQueue.State.NORMAL)
|
if(overflow)
|
||||||
{
|
{
|
||||||
mLog.debug(mDeviceName + " - buffer overflow - temporary pause until processing catches up");
|
mLog.debug(mDeviceName + " - buffer overflow - temporary pause until processing catches up");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user