#701 P25 Phase 2 Tone Metadata and Channel State (#702)

* Resolves #701 Updates P25P2 Tone metadata handling and P25P2 channel state management.
This commit is contained in:
Denny Sheirer
2020-03-28 09:51:11 +00:00
committed by GitHub
parent 41df207bb9
commit 158c8b4b8a
25 changed files with 493 additions and 611 deletions

View File

@@ -105,7 +105,7 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
//Set the proxy as a listener so that echo'd updates are broadcast externally
mutableIdentifierCollection.setIdentifierUpdateListener(mIdentifierUpdateNotificationProxy);
StateMachine stateMachine = new StateMachine(timeslot);
StateMachine stateMachine = new StateMachine(timeslot, State.MULTI_CHANNEL_ACTIVE_STATES);
mStateMachineMap.put(timeslot, stateMachine);
stateMachine.addListener(this);
@@ -133,7 +133,7 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
public void stateChanged(State state, int timeslot)
{
//Broadcast current channel state so that channel rotation monitor can track
if(state.isActiveState())
if(State.MULTI_CHANNEL_ACTIVE_STATES.contains(state))
{
broadcast(DecoderStateEvent.activeState(timeslot));
}
@@ -148,9 +148,6 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
switch(state)
{
case IDLE:
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
break;
case RESET:
reset(timeslot);
mStateMachineMap.get(timeslot).setState(State.IDLE);
@@ -173,19 +170,49 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
*/
private void checkTeardown()
{
boolean teardown = true;
boolean teardown = false;
boolean active = false;
for(StateMachine stateMachine: mStateMachineMap.values())
{
if(stateMachine.getState().isActiveState())
State state = stateMachine.getState();
//If we have an active state in either timeslot, don't teardown. IDLE is a special state that is active
//but doesn't prevent a teardown when the other timeslot is in TEARDOWN
if(State.MULTI_CHANNEL_ACTIVE_STATES.contains(state) && state != State.IDLE)
{
teardown = false;
active = true;
}
else if(state == State.TEARDOWN)
{
teardown = true;
}
}
if(teardown)
if(teardown && !active)
{
broadcast(new ChannelEvent(mChannel, ChannelEvent.Event.REQUEST_DISABLE));
if(mChannel.isTrafficChannel())
{
broadcast(new ChannelEvent(mChannel, ChannelEvent.Event.REQUEST_DISABLE));
}
else
{
for(StateMachine stateMachine: mStateMachineMap.values())
{
stateMachine.setState(State.RESET);
}
}
}
//If one timeslot is teardown but the other is still active, reset the teardown timeslot to IDLE
else if(teardown && active)
{
for(StateMachine stateMachine: mStateMachineMap.values())
{
if(stateMachine.getState() == State.TEARDOWN)
{
stateMachine.setState(State.RESET);
}
}
}
}
@@ -340,11 +367,7 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
for(int timeslot = 0; timeslot < mTimeslotCount; timeslot++)
{
mIdentifierCollectionMap.get(timeslot).broadcastIdentifiers();
if(mChannel.getChannelType() == ChannelType.TRAFFIC)
{
mStateMachineMap.get(timeslot).setState(State.ACTIVE);
}
mStateMachineMap.get(timeslot).setState(State.RESET);
}
}
@@ -476,7 +499,7 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
//in an active (ie sync locked) state.
for(int timeslot = 0; timeslot < mTimeslotCount; timeslot++)
{
if(mStateMachineMap.get(timeslot).getState().isActiveState())
if(State.MULTI_CHANNEL_ACTIVE_STATES.contains(mStateMachineMap.get(timeslot).getState()))
{
broadcast(SourceEvent.frequencyErrorMeasurementSyncLocked(sourceEvent.getValue().longValue(),
mChannel.getChannelType().name()));
@@ -511,7 +534,7 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
case CONTINUATION:
case DECODE:
case START:
if(State.CALL_STATES.contains(event.getState()))
if(State.MULTI_CHANNEL_ACTIVE_STATES.contains(event.getState()))
{
mStateMachineMap.get(event.getTimeslot()).setState(event.getState());
}
@@ -519,11 +542,11 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS
case END:
if(mChannel.isTrafficChannel())
{
mStateMachineMap.get(event.getTimeslot()).setState(State.TEARDOWN);
mStateMachineMap.get(event.getTimeslot()).setState(event.getState());
}
else
{
mStateMachineMap.get(event.getTimeslot()).setState(State.FADE);
mStateMachineMap.get(event.getTimeslot()).setState(event.getState());
}
break;
case RESET:

View File

@@ -94,7 +94,7 @@ public class SingleChannelState extends AbstractChannelState implements IDecoder
private DecoderStateEventReceiver mDecoderStateEventReceiver = new DecoderStateEventReceiver();
private SourceEventListener mInternalSourceEventListener;
private ChannelMetadata mChannelMetadata;
private StateMachine mStateMachine = new StateMachine(0);
private StateMachine mStateMachine = new StateMachine(0, State.SINGLE_CHANNEL_ACTIVE_STATES);
private StateMonitoringSquelchController mSquelchController = new StateMonitoringSquelchController(0);
public SingleChannelState(Channel channel, AliasModel aliasModel)
@@ -123,7 +123,7 @@ public class SingleChannelState extends AbstractChannelState implements IDecoder
public void stateChanged(State state, int timeslot)
{
//Broadcast current channel state so that channel rotation monitor can track
if(state.isActiveState())
if(State.SINGLE_CHANNEL_ACTIVE_STATES.contains(state))
{
broadcast(DecoderStateEvent.activeState(timeslot));
}
@@ -377,7 +377,7 @@ public class SingleChannelState extends AbstractChannelState implements IDecoder
case NOTIFICATION_MEASURED_FREQUENCY_ERROR:
//Rebroadcast frequency error measurements to external listener if we're currently
//in an active (ie sync locked) state.
if(mStateMachine.getState().isActiveState())
if(State.SINGLE_CHANNEL_ACTIVE_STATES.contains(mStateMachine.getState()))
{
broadcast(SourceEvent.frequencyErrorMeasurementSyncLocked(sourceEvent.getValue().longValue(),
mChannel.getChannelType().name()));
@@ -411,7 +411,7 @@ public class SingleChannelState extends AbstractChannelState implements IDecoder
case CONTINUATION:
case DECODE:
case START:
if(State.CALL_STATES.contains(event.getState()))
if(State.SINGLE_CHANNEL_ACTIVE_STATES.contains(event.getState()))
{
mStateMachine.setState(event.getState());
}

View File

@@ -43,6 +43,7 @@ public enum State
state == DATA ||
state == ENCRYPTED ||
state == FADE ||
state == IDLE ||
state == TEARDOWN ||
state == RESET;
}
@@ -60,6 +61,7 @@ public enum State
state == DATA ||
state == ENCRYPTED ||
state == FADE ||
state == IDLE ||
state == TEARDOWN ||
state == RESET;
}
@@ -121,7 +123,8 @@ public enum State
}
},
/**
* Indicates the channel is idle and not actively decoding any messages
* Indicates the channel is idle and not actively decoding any messages or in a multiple timeslot decoder this
* state reflects active decoding that indicates the timeslot is idle.
*/
IDLE("IDLE")
{
@@ -159,8 +162,8 @@ public enum State
private String mDisplayValue;
public static final EnumSet<State> CALL_STATES = EnumSet.of(ACTIVE, CALL, CONTROL, DATA, ENCRYPTED);
public static final EnumSet<State> IDLE_STATES = EnumSet.of(IDLE, FADE);
public static final EnumSet<State> SINGLE_CHANNEL_ACTIVE_STATES = EnumSet.of(ACTIVE, CALL, CONTROL, DATA, ENCRYPTED);
public static final EnumSet<State> MULTI_CHANNEL_ACTIVE_STATES = EnumSet.of(ACTIVE, CALL, CONTROL, DATA, ENCRYPTED, IDLE);
private State(String displayValue)
{
@@ -169,17 +172,6 @@ public enum State
public abstract boolean canChangeTo(State state);
/**
* Indicates that this state is an active (ie decoding) state and is one of the states
* enumerated in the CALL_STATES enumeration set.
*
* @return true if this state is an active state
*/
public boolean isActiveState()
{
return CALL_STATES.contains(this);
}
public String getDisplayValue()
{
return mDisplayValue;

View File

@@ -30,8 +30,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* State machine for tracking a channel state.
*/
public class StateMachine
{
private final static Logger mLog = LoggerFactory.getLogger(StateMachine.class);
@@ -42,13 +46,21 @@ public class StateMachine
protected long mEndTimeout;
protected long mEndTimeoutBuffer = 0;
private int mTimeslot;
private EnumSet<State> mActiveStates;
private Channel.ChannelType mChannelType = Channel.ChannelType.STANDARD;
private List<IStateMachineListener> mStateMachineListeners = new ArrayList<>();
private Listener<IdentifierUpdateNotification> mIdentifierUpdateListener;
public StateMachine(int timeslot)
/**
* Constructs an instance
*
* @param timeslot for this state machine
* @param activeStates set of states considered active for updating the fade timeout
*/
public StateMachine(int timeslot, EnumSet<State> activeStates)
{
mTimeslot = timeslot;
mActiveStates = activeStates;
}
public void addListener(IStateMachineListener listener)
@@ -73,7 +85,7 @@ public class StateMachine
public void checkState()
{
if(mState.isActiveState() && mFadeTimeout <= System.currentTimeMillis())
if(mActiveStates.contains(mState) && mFadeTimeout <= System.currentTimeMillis())
{
setState(State.FADE);
}
@@ -92,18 +104,22 @@ public class StateMachine
{
if(state == mState)
{
if(State.CALL_STATES.contains(state))
if(mActiveStates.contains(state))
{
updateFadeTimeout();
}
}
else if(mState.canChangeTo(state))
{
if(mActiveStates.contains(state))
{
updateFadeTimeout();
}
switch(state)
{
case ACTIVE:
mState = state;
updateFadeTimeout();
broadcast(ChannelStateIdentifier.ACTIVE);
break;
case CONTROL:
@@ -111,23 +127,19 @@ public class StateMachine
if(mChannelType == Channel.ChannelType.STANDARD)
{
mState = state;
updateFadeTimeout();
broadcast(ChannelStateIdentifier.CONTROL);
}
break;
case DATA:
mState = state;
updateFadeTimeout();
broadcast(ChannelStateIdentifier.DATA);
break;
case ENCRYPTED:
mState = state;
updateFadeTimeout();
broadcast(ChannelStateIdentifier.ENCRYPTED);
break;
case CALL:
mState = state;
updateFadeTimeout();
broadcast(ChannelStateIdentifier.CALL);
break;
case FADE: