From 00d5610213bb71f1109d22d43239ebc990900acd Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sat, 23 Nov 2019 10:19:23 -0500 Subject: [PATCH] Resolves issue with the multi-frequency tuner source and P25/MPT1327 (#603) Resolves issue with the multi-frequency tuner source and P25/MPT1327 (#603) rotating channel frequency configurations failing to maintain lock on the channel. Converted the ChannelRotationMonitor from using identifier updates to now using DecoderStateEvents so that the ChannelState can supply a steady stream of channel state events to the monitor. --- .../channel/state/DecoderStateEvent.java | 72 +++++++++++++++++++ .../channel/state/MultiChannelState.java | 10 +++ .../channel/state/SingleChannelState.java | 10 +++ .../tuner/channel/ChannelRotationMonitor.java | 71 +++++++++--------- 4 files changed, 125 insertions(+), 38 deletions(-) diff --git a/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java b/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java index 7fb3d512..e724ce99 100644 --- a/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java +++ b/src/main/java/io/github/dsheirer/channel/state/DecoderStateEvent.java @@ -24,6 +24,14 @@ package io.github.dsheirer.channel.state; public class DecoderStateEvent { + private static final DecoderStateEvent ACTIVE_STATE_TIMESLOT_0 = + new DecoderStateEvent(null, Event.NOTIFICATION_CHANNEL_ACTIVE_STATE, State.ACTIVE, 0); + private static final DecoderStateEvent ACTIVE_STATE_TIMESLOT_1 = + new DecoderStateEvent(null, Event.NOTIFICATION_CHANNEL_ACTIVE_STATE, State.ACTIVE, 1); + private static final DecoderStateEvent INACTIVE_STATE_TIMESLOT_0 = + new DecoderStateEvent(null, Event.NOTIFICATION_CHANNEL_INACTIVE_STATE, State.ACTIVE, 0); + private static final DecoderStateEvent INACTIVE_STATE_TIMESLOT_1 = + new DecoderStateEvent(null, Event.NOTIFICATION_CHANNEL_INACTIVE_STATE, State.ACTIVE, 1); private Object mSource; private Event mEvent; private State mState; @@ -54,6 +62,68 @@ public class DecoderStateEvent this(source, event, state, 0, frequency); } + /** + * Creates a channel state active event for the specified timeslot + * @param timeslot that the state applies to + * @return new event + */ + public static DecoderStateEvent activeState(int timeslot) + { + if(timeslot == 0) + { + return ACTIVE_STATE_TIMESLOT_0; + } + else if(timeslot == 1) + { + return ACTIVE_STATE_TIMESLOT_1; + } + else + { + throw new IllegalArgumentException("Only timeslots 0 and 1 are currently supported"); + } + } + + /** + * Creates a channel state active event for the specified timeslot. + * @param state currently + * @return new event + */ + public static DecoderStateEvent activeState() + { + return activeState(0); + } + + /** + * Creates a channel state inactive event for the specified timeslot + * @param timeslot that the state applies to + * @return new event + */ + public static DecoderStateEvent inactiveState(int timeslot) + { + if(timeslot == 0) + { + return INACTIVE_STATE_TIMESLOT_0; + } + else if(timeslot == 1) + { + return INACTIVE_STATE_TIMESLOT_1; + } + else + { + throw new IllegalArgumentException("Only timeslots 0 and 1 are currently supported"); + } + } + + /** + * Creates a channel state active event for the specified timeslot. + * @param state currently + * @return new event + */ + public static DecoderStateEvent inactiveState() + { + return inactiveState(0); + } + public String toString() { StringBuilder sb = new StringBuilder(); @@ -99,6 +169,8 @@ public class DecoderStateEvent CONTINUATION, DECODE, END, + NOTIFICATION_CHANNEL_ACTIVE_STATE, + NOTIFICATION_CHANNEL_INACTIVE_STATE, RESET, SOURCE_FREQUENCY, START; diff --git a/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java b/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java index 3ea1f2dd..a2bbf690 100644 --- a/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java +++ b/src/main/java/io/github/dsheirer/channel/state/MultiChannelState.java @@ -132,6 +132,16 @@ public class MultiChannelState extends AbstractChannelState implements IDecoderS @Override public void stateChanged(State state, int timeslot) { + //Broadcast current channel state so that channel rotation monitor can track + if(state.isActiveState()) + { + broadcast(DecoderStateEvent.activeState(timeslot)); + } + else + { + broadcast(DecoderStateEvent.inactiveState(timeslot)); + } + ChannelStateIdentifier stateIdentifier = ChannelStateIdentifier.create(state); mIdentifierCollectionMap.get(timeslot).update(stateIdentifier); mChannelMetadataMap.get(timeslot).receive(new IdentifierUpdateNotification(stateIdentifier, IdentifierUpdateNotification.Operation.ADD, timeslot)); diff --git a/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java b/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java index cff05d75..d1391f5e 100644 --- a/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java +++ b/src/main/java/io/github/dsheirer/channel/state/SingleChannelState.java @@ -122,6 +122,16 @@ public class SingleChannelState extends AbstractChannelState implements IDecoder @Override public void stateChanged(State state, int timeslot) { + //Broadcast current channel state so that channel rotation monitor can track + if(state.isActiveState()) + { + broadcast(DecoderStateEvent.activeState(timeslot)); + } + else + { + broadcast(DecoderStateEvent.inactiveState(timeslot)); + } + ChannelStateIdentifier stateIdentifier = ChannelStateIdentifier.create(state); mIdentifierCollection.update(stateIdentifier); mChannelMetadata.receive(new IdentifierUpdateNotification(stateIdentifier, IdentifierUpdateNotification.Operation.ADD, timeslot)); diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelRotationMonitor.java b/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelRotationMonitor.java index 90b715b0..799ae272 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelRotationMonitor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelRotationMonitor.java @@ -1,32 +1,32 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * * ****************************************************************************** + * * Copyright (C) 2014-2019 Dennis Sheirer + * * + * * This program is free software: you can redistribute it and/or modify + * * it under the terms of the GNU General Public License as published by + * * the Free Software Foundation, either version 3 of the License, or + * * (at your option) any later version. + * * + * * This program is distributed in the hope that it will be useful, + * * but WITHOUT ANY WARRANTY; without even the implied warranty of + * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * * GNU General Public License for more details. + * * + * * You should have received a copy of the GNU General Public License + * * along with this program. If not, see + * * ***************************************************************************** * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * ***************************************************************************** */ package io.github.dsheirer.source.tuner.channel; import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.channel.state.DecoderStateEvent; +import io.github.dsheirer.channel.state.IDecoderStateEventListener; import io.github.dsheirer.channel.state.State; import io.github.dsheirer.eventbus.MyEventBus; -import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.identifier.IdentifierUpdateListener; -import io.github.dsheirer.identifier.IdentifierUpdateNotification; -import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier; import io.github.dsheirer.module.Module; import io.github.dsheirer.preference.PreferenceType; import io.github.dsheirer.preference.UserPreferences; @@ -43,10 +43,11 @@ import java.util.concurrent.TimeUnit; /** * Monitors channel state to detect when a channel is not in an identified active state and issues a request to rotate - * the next channel frequency in the list. + * the next channel frequency in the list. This class depends on the ChannelState to provide a continuous + * stream of channel active/inactive messaging in the form of DecoderStateEvents. */ -public class ChannelRotationMonitor extends Module implements ISourceEventProvider, IdentifierUpdateListener, - Listener +public class ChannelRotationMonitor extends Module implements ISourceEventProvider, IDecoderStateEventListener, + Listener { private final static Logger mLog = LoggerFactory.getLogger(ChannelRotationMonitor.class); @@ -93,33 +94,27 @@ public class ChannelRotationMonitor extends Module implements ISourceEventProvid } @Override - public Listener getIdentifierUpdateListener() + public Listener getDecoderStateListener() { return this; } - /** - * Primary method for receiving channel state identifier notifications, enabling this module to track the - * current channel state being provided by the Channel State module. - */ @Override - public void receive(IdentifierUpdateNotification identifierUpdateNotification) + public void receive(DecoderStateEvent decoderStateEvent) { - Identifier identifier = identifierUpdateNotification.getIdentifier(); - - if(identifier instanceof ChannelStateIdentifier) + if(decoderStateEvent.getEvent() == DecoderStateEvent.Event.NOTIFICATION_CHANNEL_ACTIVE_STATE) { - State currentState = ((ChannelStateIdentifier)identifier).getValue(); - - if(mActiveStates.contains(currentState)) + mActive = true; + } + else if(decoderStateEvent.getEvent() == DecoderStateEvent.Event.NOTIFICATION_CHANNEL_INACTIVE_STATE) + { + if(mActive) { - mActive = true; - } - else if(mActive) - { - mActive = false; + //If we're toggling from active to inactive, update the timestamp, mInactiveTimestamp = System.currentTimeMillis(); } + + mActive = false; } }