mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
* #6 DMR decoder feature - work in progress * #6 DMR Decoder. * #6 DMR Decoder. Code review updates. Co-authored-by: Zhenyu Mao <maozhenyu@foxmail.com> Co-authored-by: Denny <denny@denny-desktop>
This commit is contained in:
@@ -85,11 +85,6 @@ public abstract class Source extends Module implements ISourceEventListener, ISo
|
||||
*/
|
||||
public abstract long getFrequency();
|
||||
|
||||
/**
|
||||
* Process any cleanup actions to prepare for garbage collection of this source
|
||||
*/
|
||||
public abstract void dispose();
|
||||
|
||||
/**
|
||||
* Registers the listener to receive overflow state changes. Use null argument to clear the listener
|
||||
*/
|
||||
|
||||
@@ -21,10 +21,15 @@
|
||||
*/
|
||||
package io.github.dsheirer.source;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class SourceEvent
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(SourceEvent.class);
|
||||
|
||||
public enum Event
|
||||
{
|
||||
NOTIFICATION_CHANNEL_COUNT_CHANGE,
|
||||
@@ -46,6 +51,7 @@ public class SourceEvent
|
||||
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
|
||||
REQUEST_FREQUENCY_CHANGE,
|
||||
REQUEST_FREQUENCY_ROTATION,
|
||||
REQUEST_FREQUENCY_SELECTION,
|
||||
REQUEST_SOURCE_DISPOSE,
|
||||
REQUEST_START_SAMPLE_STREAM,
|
||||
REQUEST_STOP_SAMPLE_STREAM;
|
||||
@@ -388,6 +394,15 @@ public class SourceEvent
|
||||
return new SourceEvent(Event.REQUEST_FREQUENCY_ROTATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request to cycle to a specific frequency. This is normally used for decoders to request the
|
||||
* next frequency in a list when a multiple-frequency source configuration is defined.
|
||||
*/
|
||||
public static SourceEvent frequencySelectionRequest()
|
||||
{
|
||||
return new SourceEvent(Event.REQUEST_FREQUENCY_SELECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a notification that the request to rotate the source frequency to the next frequency in the list
|
||||
* was successful.
|
||||
|
||||
@@ -95,7 +95,7 @@ public class SourceManager
|
||||
if(config instanceof SourceConfigTunerMultipleFrequency)
|
||||
{
|
||||
SourceConfigTunerMultipleFrequency sourceConfigTuner = (SourceConfigTunerMultipleFrequency)config;
|
||||
TunerChannel tunerChannel = sourceConfigTuner.getFirstTunerChannel(channelSpecification.getBandwidth());
|
||||
TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth());
|
||||
String preferredTuner = sourceConfigTuner.getPreferredTuner();
|
||||
|
||||
Source source = mTunerModel.getSource(tunerChannel, channelSpecification, preferredTuner);
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.source.SourceType;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.rotation.ChannelRotationMonitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -37,6 +38,8 @@ public class SourceConfigTunerMultipleFrequency extends SourceConfiguration
|
||||
{
|
||||
private List<Long> mFrequencies = new ArrayList<>();
|
||||
private String mPreferredTuner;
|
||||
private Long mPreferredFrequency;
|
||||
private int mFrequencyRotationDelay = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MINIMUM;
|
||||
|
||||
public SourceConfigTunerMultipleFrequency()
|
||||
{
|
||||
@@ -69,6 +72,37 @@ public class SourceConfigTunerMultipleFrequency extends SourceConfiguration
|
||||
return mFrequencies.size() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preferred frequency to start first for this configuration
|
||||
*/
|
||||
@JsonIgnore
|
||||
public long getPreferredFrequency()
|
||||
{
|
||||
if(mPreferredFrequency != null)
|
||||
{
|
||||
return mPreferredFrequency;
|
||||
}
|
||||
else if(mFrequencies.size() > 0)
|
||||
{
|
||||
return mFrequencies.get(0);
|
||||
}
|
||||
|
||||
return 0l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets which is the preferred frequency to use for this config when it's started
|
||||
* @param frequency to use first
|
||||
*/
|
||||
public void setPreferredFrequency(long frequency)
|
||||
{
|
||||
//Only set the frequency if it is one of the frequencies in the list
|
||||
if(mFrequencies.contains(frequency))
|
||||
{
|
||||
mPreferredFrequency = frequency;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a frequency to the list
|
||||
*/
|
||||
@@ -113,15 +147,43 @@ public class SourceConfigTunerMultipleFrequency extends SourceConfiguration
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public TunerChannel getFirstTunerChannel(int bandwidth)
|
||||
public TunerChannel getTunerChannel(int bandwidth)
|
||||
{
|
||||
if(getFrequencies().size() > 0)
|
||||
return new TunerChannel(getPreferredFrequency(), bandwidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel rotation delay. This setting is used when multiple channel frequencies are defined in the source
|
||||
* config and controls how long the decoder will remaining on each frequency until the channel is identified as
|
||||
* active or the channel is identified as inactive and a change frequency request is issued.
|
||||
*
|
||||
* Note: this value is ignored when the source config contains a single frequency.
|
||||
*
|
||||
* @return channel rotation delay in milliseconds.
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "frequency_rotation_delay")
|
||||
public int getFrequencyRotationDelay()
|
||||
{
|
||||
return mFrequencyRotationDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel rotation delay.
|
||||
* @param frequencyRotationDelay in milliseconds.
|
||||
*/
|
||||
public void setFrequencyRotationDelay(int frequencyRotationDelay)
|
||||
{
|
||||
if(frequencyRotationDelay < ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MINIMUM)
|
||||
{
|
||||
return new TunerChannel(getFrequencies().get(0), bandwidth);
|
||||
mFrequencyRotationDelay = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MINIMUM;
|
||||
}
|
||||
else if(frequencyRotationDelay > ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MAXIMUM)
|
||||
{
|
||||
mFrequencyRotationDelay = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MAXIMUM;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new TunerChannel(0l, bandwidth);
|
||||
mFrequencyRotationDelay = frequencyRotationDelay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,11 +140,4 @@ public class RealMixerSource extends RealSource
|
||||
{
|
||||
mFrequency = frequency;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mMixerReader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
package io.github.dsheirer.source.tuner.channel;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.source.Source;
|
||||
@@ -27,10 +28,12 @@ import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.heartbeat.Heartbeat;
|
||||
import io.github.dsheirer.source.tuner.TunerModel;
|
||||
import io.github.dsheirer.source.tuner.channel.rotation.FrequencyLockChangeRequest;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -47,6 +50,7 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||
private TunerModel mTunerModel;
|
||||
private TunerChannelSource mTunerChannelSource;
|
||||
private List<Long> mFrequencies;
|
||||
private List<Long> mLockedFrequencies = new ArrayList<>();
|
||||
private int mFrequencyListPointer = 0;
|
||||
private ChannelSpecification mChannelSpecification;
|
||||
private Listener<ReusableComplexBuffer> mReusableComplexBufferListener;
|
||||
@@ -70,29 +74,34 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles this source to use the next frequency in the list
|
||||
* Cycles this source to use the next frequency in the list. If no other frequencies are available,
|
||||
* because of frequency locking, ignore the rotate request.
|
||||
*/
|
||||
private void nextFrequency()
|
||||
private void rotate()
|
||||
{
|
||||
if(mChangingChannels.compareAndSet(false, true))
|
||||
{
|
||||
//Only change if we have more than one frequency specified
|
||||
if(mFrequencies.size() > 0)
|
||||
{
|
||||
if(mTunerChannelSource != null)
|
||||
{
|
||||
//Shutdown the existing tuner channel source
|
||||
mTunerChannelSource.stop();
|
||||
mTunerChannelSource.removeListener(mReusableComplexBufferListener);
|
||||
mTunerChannelSource.removeSourceEventListener();
|
||||
mTunerChannelSource.removeHeartbeatListener(mHeartbeatListener);
|
||||
mTunerChannelSource.dispose();
|
||||
mTunerChannelSource = null;
|
||||
}
|
||||
long frequency = getNextFrequency();
|
||||
|
||||
//Request the next tuner channel source
|
||||
getNextSource();
|
||||
if(frequency == 0)
|
||||
{
|
||||
mChangingChannels.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mTunerChannelSource != null)
|
||||
{
|
||||
//Shutdown the existing tuner channel source
|
||||
mTunerChannelSource.stop();
|
||||
mTunerChannelSource.removeListener(mReusableComplexBufferListener);
|
||||
mTunerChannelSource.removeSourceEventListener();
|
||||
mTunerChannelSource.removeHeartbeatListener(mHeartbeatListener);
|
||||
mTunerChannelSource.dispose();
|
||||
mTunerChannelSource = null;
|
||||
}
|
||||
|
||||
//Request the next tuner channel source
|
||||
getNextSource(getTunerChannel(frequency));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,11 +112,10 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||
* Note: if this channel source is shutdown by the external consumer, the mStarted flag will be set to false and
|
||||
* the persistent attempts will stop.
|
||||
*/
|
||||
private void getNextSource()
|
||||
private void getNextSource(TunerChannel nextChannel)
|
||||
{
|
||||
if(mStarted)
|
||||
{
|
||||
TunerChannel nextChannel = getNextChannel();
|
||||
Source source = mTunerModel.getSource(nextChannel, mChannelSpecification, mPreferredTuner);
|
||||
|
||||
if(source instanceof TunerChannelSource)
|
||||
@@ -117,7 +125,9 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||
mTunerChannelSource.setListener(mReusableComplexBufferListener);
|
||||
mTunerChannelSource.addHeartbeatListener(mHeartbeatListener);
|
||||
mTunerChannelSource.start();
|
||||
mTunerChannel = nextChannel;
|
||||
mChangingChannels.set(false);
|
||||
|
||||
getSourceEventListener().receive(SourceEvent.frequencyRotationSuccessNotification(this, nextChannel.getFrequency()));
|
||||
}
|
||||
|
||||
@@ -126,7 +136,7 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||
if(mTunerChannelSource == null)
|
||||
{
|
||||
getSourceEventListener().receive(SourceEvent.frequencyRotationFailureNotification(this, nextChannel.getFrequency()));
|
||||
ThreadPool.SCHEDULED.schedule(() -> getNextSource(), 1, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(() -> getNextSource(getTunerChannel(getNextFrequency())), 500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,23 +161,70 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||
{
|
||||
mTunerChannelSource.stop();
|
||||
mTunerChannelSource.removeSourceEventListener();
|
||||
mTunerChannelSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes requests to lock or unlock frequencies for this source that are received over the processing chain
|
||||
* event bus. A locked frequency will not be used in the frequency rotation list until it is unlocked.
|
||||
*
|
||||
* @param request to lock or unlock a frequency
|
||||
*/
|
||||
@Subscribe
|
||||
public void process(FrequencyLockChangeRequest request)
|
||||
{
|
||||
if(request.isLockRequest() && !mLockedFrequencies.contains(request.getFrequency()))
|
||||
{
|
||||
mLockedFrequencies.add(request.getFrequency());
|
||||
}
|
||||
else if(request.isUnlockRequest() && mLockedFrequencies.contains(request.getFrequency()))
|
||||
{
|
||||
mLockedFrequencies.remove(request.getFrequency());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the next frequency in the list to use, while respecting the locked frequencies list.
|
||||
*
|
||||
* @return next frequency or 0 if there currently are no other frequencies than the frequency currently in use.
|
||||
*/
|
||||
private long getNextFrequency()
|
||||
{
|
||||
if(mFrequencies.size() <= 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int attempts = 0;
|
||||
|
||||
mFrequencyListPointer = ++mFrequencyListPointer % mFrequencies.size();
|
||||
|
||||
long frequency = mFrequencies.get(mFrequencyListPointer);
|
||||
|
||||
while((mLockedFrequencies.contains(frequency) || frequency == getFrequency()) && attempts < mFrequencies.size())
|
||||
{
|
||||
//If we loop through the entire list back to the current frequency, return 0;
|
||||
if(frequency == getFrequency())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
mFrequencyListPointer = ++mFrequencyListPointer % mFrequencies.size();
|
||||
|
||||
frequency = mFrequencies.get(mFrequencyListPointer);
|
||||
|
||||
attempts++;
|
||||
}
|
||||
|
||||
return frequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the next tuner channel from the frequency list.
|
||||
*/
|
||||
private TunerChannel getNextChannel()
|
||||
private TunerChannel getTunerChannel(long frequency)
|
||||
{
|
||||
mFrequencyListPointer++;
|
||||
|
||||
if(mFrequencyListPointer >= mFrequencies.size())
|
||||
{
|
||||
mFrequencyListPointer = 0;
|
||||
}
|
||||
|
||||
long frequency = mFrequencies.get(mFrequencyListPointer);
|
||||
|
||||
return new TunerChannel(frequency, mChannelSpecification.getBandwidth());
|
||||
}
|
||||
|
||||
@@ -291,7 +348,7 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||
{
|
||||
if(sourceEvent.getEvent() == SourceEvent.Event.REQUEST_FREQUENCY_ROTATION)
|
||||
{
|
||||
nextFrequency();
|
||||
rotate();
|
||||
}
|
||||
else if(mTunerChannelSource != null)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
|
||||
private final static Logger mLog = LoggerFactory.getLogger(TunerChannelSource.class);
|
||||
private static final long BUFFER_PROCESSOR_RUN_INTERVAL_MILLISECONDS = 50;
|
||||
private SourceEventListenerToProcessorAdapter mConsumerSourceEventListenerAdapter;
|
||||
private TunerChannel mTunerChannel;
|
||||
protected TunerChannel mTunerChannel;
|
||||
private Listener<SourceEvent> mProducerSourceEventListener;
|
||||
private Listener<SourceEvent> mConsumerSourceEventListener;
|
||||
private ScheduledIntervalProcessor mScheduledIntervalProcessor = new ScheduledIntervalProcessor();
|
||||
@@ -117,12 +117,6 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
|
||||
return mTunerChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts this tuner channel source producing sample stream.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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 io.github.dsheirer.source.tuner.channel.rotation;
|
||||
|
||||
import io.github.dsheirer.channel.state.State;
|
||||
import io.github.dsheirer.module.ModuleEventBusMessage;
|
||||
|
||||
/**
|
||||
* Request to add a channel state to the list of active channel states monitored by the channel rotation monitor.
|
||||
*/
|
||||
public class AddChannelRotationActiveStateRequest extends ModuleEventBusMessage
|
||||
{
|
||||
private State mState;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param state to add
|
||||
*/
|
||||
public AddChannelRotationActiveStateRequest(State state)
|
||||
{
|
||||
mState = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* State to add to the list of active states
|
||||
* @return state
|
||||
*/
|
||||
public State getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,28 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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 <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
* 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 io.github.dsheirer.source.tuner.channel;
|
||||
package io.github.dsheirer.source.tuner.channel.rotation;
|
||||
|
||||
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.module.Module;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
@@ -43,36 +39,38 @@ 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. This class depends on the ChannelState to provide a continuous
|
||||
* stream of channel active/inactive messaging in the form of DecoderStateEvents.
|
||||
* to the next channel frequency in the list. This class depends on the ChannelState providing a continuous
|
||||
* stream of channel state notification events in the form of DecoderStateEvents.
|
||||
*/
|
||||
public class ChannelRotationMonitor extends Module implements ISourceEventProvider, IDecoderStateEventListener,
|
||||
Listener<DecoderStateEvent>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelRotationMonitor.class);
|
||||
public static final int CHANNEL_ROTATION_DELAY_MINIMUM = 200;
|
||||
public static final int CHANNEL_ROTATION_DELAY_MAXIMUM = 2000;
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelRotationMonitor.class);
|
||||
private UserPreferences mUserPreferences;
|
||||
private Collection<State> mActiveStates;
|
||||
private ScheduledFuture<?> mScheduledFuture;
|
||||
private Listener<SourceEvent> mSourceEventListener;
|
||||
private long mRotationDelay;
|
||||
private boolean mActive;
|
||||
private long mInactiveTimestamp = System.currentTimeMillis();
|
||||
private long mLastActiveTimestamp = System.currentTimeMillis();
|
||||
private boolean mEnabled = true;
|
||||
|
||||
/**
|
||||
* Constructs a channel rotation monitor.
|
||||
*
|
||||
* Constructs a channel rotation monitor that uses the specified rotation delay.
|
||||
* @param activeStates to monitor
|
||||
* @param userPreferences to receive updates for rotation delay value from user preferences
|
||||
* @param rotationDelay specifies how long to remain on each frequency before rotating (in milliseconds).
|
||||
*/
|
||||
public ChannelRotationMonitor(Collection<State> activeStates, UserPreferences userPreferences)
|
||||
public ChannelRotationMonitor(Collection<State> activeStates, long rotationDelay)
|
||||
{
|
||||
mActiveStates = activeStates;
|
||||
mUserPreferences = userPreferences;
|
||||
mRotationDelay = mUserPreferences.getChannelMultiFrequencyPreference().getRotationDelay();
|
||||
mRotationDelay = rotationDelay;
|
||||
|
||||
//Register to receive user preference updates
|
||||
MyEventBus.getEventBus().register(this);
|
||||
if(mRotationDelay == 0)
|
||||
{
|
||||
mRotationDelay = 200;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,21 +98,40 @@ public class ChannelRotationMonitor extends Module implements ISourceEventProvid
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(DecoderStateEvent decoderStateEvent)
|
||||
public void receive(DecoderStateEvent event)
|
||||
{
|
||||
if(decoderStateEvent.getEvent() == DecoderStateEvent.Event.NOTIFICATION_CHANNEL_ACTIVE_STATE)
|
||||
switch(event.getEvent())
|
||||
{
|
||||
mActive = true;
|
||||
case NOTIFICATION_CHANNEL_STATE:
|
||||
if(mActiveStates.contains(event.getState()))
|
||||
{
|
||||
mLastActiveTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if(decoderStateEvent.getEvent() == DecoderStateEvent.Event.NOTIFICATION_CHANNEL_INACTIVE_STATE)
|
||||
{
|
||||
if(mActive)
|
||||
{
|
||||
//If we're toggling from active to inactive, update the timestamp,
|
||||
mInactiveTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
mActive = false;
|
||||
/**
|
||||
* Processes a request to disable this monitor instance.
|
||||
* @param request to disable
|
||||
*/
|
||||
@Subscribe
|
||||
public void disable(DisableChannelRotationMonitorRequest request)
|
||||
{
|
||||
mEnabled = false;
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a request to add an active state to the list of monitored active states.
|
||||
* @param request to add
|
||||
*/
|
||||
@Subscribe
|
||||
public void addActiveState(AddChannelRotationActiveStateRequest request)
|
||||
{
|
||||
if(!mActiveStates.contains(request.getState()))
|
||||
{
|
||||
mActiveStates.add(request.getState());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,11 +141,11 @@ public class ChannelRotationMonitor extends Module implements ISourceEventProvid
|
||||
*/
|
||||
private void checkState()
|
||||
{
|
||||
if(mSourceEventListener != null && !mActive &&
|
||||
((mInactiveTimestamp + mRotationDelay) < System.currentTimeMillis()))
|
||||
if(mEnabled && mSourceEventListener != null &&
|
||||
((mLastActiveTimestamp + mRotationDelay) < System.currentTimeMillis()))
|
||||
{
|
||||
mSourceEventListener.receive(SourceEvent.frequencyRotationRequest());
|
||||
mInactiveTimestamp = System.currentTimeMillis();
|
||||
mLastActiveTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +165,6 @@ public class ChannelRotationMonitor extends Module implements ISourceEventProvid
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +172,19 @@ public class ChannelRotationMonitor extends Module implements ISourceEventProvid
|
||||
{
|
||||
if(mScheduledFuture == null)
|
||||
{
|
||||
mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> checkState(), 1, 1, TimeUnit.SECONDS);
|
||||
Runnable runnable = () -> {
|
||||
try
|
||||
{
|
||||
checkState();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.warn("Error while checking state", t);
|
||||
}
|
||||
};
|
||||
|
||||
mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(runnable, mRotationDelay,
|
||||
mRotationDelay / 2, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,12 +194,8 @@ public class ChannelRotationMonitor extends Module implements ISourceEventProvid
|
||||
if(mScheduledFuture != null)
|
||||
{
|
||||
mScheduledFuture.cancel(true);
|
||||
mScheduledFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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 io.github.dsheirer.source.tuner.channel.rotation;
|
||||
|
||||
import io.github.dsheirer.module.ModuleEventBusMessage;
|
||||
|
||||
/**
|
||||
* Request to disable the channel rotation monitor.
|
||||
*/
|
||||
public class DisableChannelRotationMonitorRequest extends ModuleEventBusMessage
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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 io.github.dsheirer.source.tuner.channel.rotation;
|
||||
|
||||
import io.github.dsheirer.module.ModuleEventBusMessage;
|
||||
|
||||
/**
|
||||
* Request to lock or unlock a frequency from use.
|
||||
*/
|
||||
public class FrequencyLockChangeRequest extends ModuleEventBusMessage
|
||||
{
|
||||
private long mFrequency;
|
||||
private boolean mLock;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param frequency for the request
|
||||
* @param lock true if this is a lock or false if this is an unlock
|
||||
*/
|
||||
public FrequencyLockChangeRequest(long frequency, boolean lock)
|
||||
{
|
||||
mFrequency = frequency;
|
||||
mLock = lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frequency for this request
|
||||
*/
|
||||
public long getFrequency()
|
||||
{
|
||||
return mFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this request is to lock the frequency
|
||||
*/
|
||||
public boolean isLockRequest()
|
||||
{
|
||||
return mLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this request is to unlock the frequency
|
||||
*/
|
||||
public boolean isUnlockRequest()
|
||||
{
|
||||
return !mLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FREQUENCY ").append(isLockRequest() ? "LOCK" : "UNLOCK").append(" REQUEST: ").append(getFrequency());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create a lock request
|
||||
* @param frequency to lock
|
||||
* @return request
|
||||
*/
|
||||
public static FrequencyLockChangeRequest lock(long frequency)
|
||||
{
|
||||
return new FrequencyLockChangeRequest(frequency, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create an unlock request
|
||||
* @param frequency to unlock
|
||||
* @return request
|
||||
*/
|
||||
public static FrequencyLockChangeRequest unlock(long frequency)
|
||||
{
|
||||
return new FrequencyLockChangeRequest(frequency, false);
|
||||
}
|
||||
}
|
||||
@@ -297,12 +297,6 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile()
|
||||
{
|
||||
|
||||
@@ -256,12 +256,6 @@ public class RealWaveSource extends RealSource implements IControllableFileSourc
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user