61 Rotating Control Channel Support - P25 Decoder (#475)

* Initial work on add support for rotating control channels.

* Creates multiple frequency editor control.

* Updates channel source editor to allow user to select multi-frequency
configuration and add multiple frequencies.

* Adds full support for rotating control channels to the P25 channelizer.
Users define multiple frequencies in the channel source configuration.
The decoder monitors the state of the channel for a CONTROL state.  If
the state does not stay in the CONTROL state for a user-specified delay
value, then the decoder rotates to the next frequency in the list.  This
behavior continues until the decoder state once again changes to CONTROL.

This is NOT a 'scanner' feature.
This commit is contained in:
Denny Sheirer
2019-01-06 06:59:04 -05:00
committed by GitHub
parent 3a39ae76ba
commit 5481fb7d4d
44 changed files with 2502 additions and 634 deletions

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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
@@ -20,7 +20,7 @@
package io.github.dsheirer.source.tuner;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.tuner.ChannelizerType;
import io.github.dsheirer.preference.source.ChannelizerType;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.ISourceEventProcessor;

View File

@@ -1,25 +1,28 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* ******************************************************************************
* 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.
* 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.
* 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/>
*
******************************************************************************/
* 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;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.Source;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.config.SourceConfigTuner;
import io.github.dsheirer.source.tuner.TunerEvent.Event;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
@@ -335,62 +338,60 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
}
/**
* Iterates current tuners to get a tuner channel source for the frequency
* specified in the channel config's source config object.
* Iterates current tuners to get a tuner channel source for the specified frequency and bandwidth
*
* Returns null if no tuner can source the channel
*/
public Source getSource(SourceConfigTuner config, ChannelSpecification channelSpecification)
public Source getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification, String preferredTuner)
{
TunerChannelSource retVal = null;
TunerChannelSource source = null;
TunerChannel tunerChannel = config.getTunerChannel();
tunerChannel.setBandwidth(channelSpecification.getBandwidth());
Iterator<Tuner> it = mTuners.iterator();
Tuner tuner;
if(config.hasPreferredTuner())
if(tunerChannel != null && channelSpecification != null)
{
tuner = getTuner(config.getPreferredTuner());
Iterator<Tuner> it = mTuners.iterator();
if(tuner != null)
Tuner tuner;
if(preferredTuner != null)
{
tuner = getTuner(preferredTuner);
if(tuner != null)
{
try
{
source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
if(source != null)
{
return source;
}
}
catch(Exception e)
{
//Fall through to logger below
}
}
mLog.info("Unable to source channel [" + tunerChannel.getFrequency() + "] from preferred tuner [" +
preferredTuner + "] - searching for another tuner");
}
while(it.hasNext() && source == null)
{
tuner = it.next();
try
{
retVal = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
if(retVal != null)
{
return retVal;
}
source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
}
catch(Exception e)
{
//Fall through to logger below
mLog.error("Error obtaining channel from tuner [" + tuner.getName() + "]", e);
}
}
mLog.info("Unable to source channel [" + config.getFrequency() + "] from preferred tuner [" +
config.getPreferredTuner() + "] - searching for another tuner");
}
while(it.hasNext() && retVal == null)
{
tuner = it.next();
try
{
retVal = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
}
catch(Exception e)
{
mLog.error("error obtaining channel from tuner [" + tuner.getName() + "]", e);
}
}
return retVal;
return source;
}
}

View File

@@ -0,0 +1,184 @@
/*
* ******************************************************************************
* 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.
*
* 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;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.gui.control.MultipleFrequencyEditor;
import io.github.dsheirer.gui.editor.Editor;
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
import io.github.dsheirer.source.config.SourceConfiguration;
import net.miginfocom.swing.MigLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TunerSourceMultipleFrequencyEditor extends Editor<Channel>
{
private static final long serialVersionUID = 1L;
private static final String NO_PREFERRED_TUNER = "(none)";
private static final String UNAVAILABLE_TUNER = " (unavailable)";
private MultipleFrequencyEditor mFrequencyEditor = new MultipleFrequencyEditor(0.0, 6000.0);
private JComboBox<String> mTunerNameComboBox;
private TunerModel mTunerModel;
private List<String> mCurrentTunerNames;
public TunerSourceMultipleFrequencyEditor(TunerModel tunerModel)
{
mTunerModel = tunerModel;
init();
}
private void init()
{
loadTunerNames();
setLayout(new MigLayout("insets 0 0 0 0",
"[grow,fill][][right][grow,fill,left]", "[][grow,fill]"));
mFrequencyEditor.getFrequencyModel().addTableModelListener(new TableModelListener()
{
@Override
public void tableChanged(TableModelEvent e)
{
setModified(true);
}
});
// mFrequencyEditor.setSize(new Dimension(100, 30));
add(mFrequencyEditor, "span 2 2");
mTunerNameComboBox = new JComboBox<>(mCurrentTunerNames.toArray(new String[mCurrentTunerNames.size()]));
mTunerNameComboBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
setModified(true);
}
});
add(new JLabel("Preferred Tuner:"));
add(mTunerNameComboBox);
}
private void loadTunerNames()
{
mCurrentTunerNames = new ArrayList<>();
for(Tuner tuner: mTunerModel.getTuners())
{
mCurrentTunerNames.add(tuner.getName());
}
Collections.sort(mCurrentTunerNames);
mCurrentTunerNames.add(0, NO_PREFERRED_TUNER);
}
private void updateTunerNameCombo(String preferredTuner)
{
if(preferredTuner != null && !mCurrentTunerNames.contains(preferredTuner))
{
List<String> updatedTunerNameList = new ArrayList<>(mCurrentTunerNames);
String unavailableTuner = preferredTuner + UNAVAILABLE_TUNER;
updatedTunerNameList.add(unavailableTuner);
String[] tunerNameArray = updatedTunerNameList.toArray(new String[updatedTunerNameList.size()]);
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
mTunerNameComboBox.setSelectedItem(unavailableTuner);
}
else
{
String[] tunerNameArray = mCurrentTunerNames.toArray(new String[mCurrentTunerNames.size()]);
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
if(preferredTuner == null)
{
mTunerNameComboBox.setSelectedItem(NO_PREFERRED_TUNER);
}
else
{
mTunerNameComboBox.setSelectedItem(preferredTuner);
}
}
}
public void save()
{
if(hasItem() && isModified())
{
SourceConfigTunerMultipleFrequency config = new SourceConfigTunerMultipleFrequency();
String preferredTuner = (String)mTunerNameComboBox.getSelectedItem();
if(preferredTuner != null)
{
if(preferredTuner.equalsIgnoreCase(NO_PREFERRED_TUNER))
{
config.setPreferredTuner(null);
}
else if(preferredTuner.contains(UNAVAILABLE_TUNER))
{
//don't do anything - the config already contains this unavailable tuner name
}
else
{
config.setPreferredTuner(preferredTuner);
}
}
config.setFrequencies(mFrequencyEditor.getFrequencyModel().getFrequencies());
getItem().setSourceConfiguration(config);
}
setModified(false);
}
@Override
public void setItem(Channel item)
{
super.setItem(item);
mFrequencyEditor.getFrequencyModel().clear();
if(hasItem())
{
SourceConfiguration config = getItem().getSourceConfiguration();
if(config instanceof SourceConfigTunerMultipleFrequency)
{
SourceConfigTunerMultipleFrequency tunerConfig = (SourceConfigTunerMultipleFrequency) config;
updateTunerNameCombo(tunerConfig.getPreferredTuner());
mFrequencyEditor.getFrequencyModel().setFrequencies(tunerConfig.getFrequencies());
setModified(false);
}
}
else
{
setModified(false);
}
}
}

View File

@@ -0,0 +1,182 @@
/*
* ******************************************************************************
* 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.
*
* 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;
import com.google.common.eventbus.Subscribe;
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;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.ISourceEventProvider;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.concurrent.ScheduledFuture;
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.
*/
public class ChannelRotationMonitor extends Module implements ISourceEventProvider, IdentifierUpdateListener,
Listener<IdentifierUpdateNotification>
{
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();
/**
* Constructs a channel rotation monitor.
*
* @param activeStates to monitor
* @param userPreferences to receive updates for rotation delay value from user preferences
*/
public ChannelRotationMonitor(Collection<State> activeStates, UserPreferences userPreferences)
{
mActiveStates = activeStates;
mUserPreferences = userPreferences;
mRotationDelay = mUserPreferences.getChannelMultiFrequencyPreference().getRotationDelay();
//Register to receive user preference updates
MyEventBus.getEventBus().register(this);
}
/**
* Registers the external listener to receive frequency rotation requests from this module
*/
@Override
public void setSourceEventListener(Listener<SourceEvent> listener)
{
mSourceEventListener = listener;
}
/**
* Unregisters the external listener from receiving frequency rotation requests.
*/
@Override
public void removeSourceEventListener()
{
mSourceEventListener = null;
}
@Override
public Listener<IdentifierUpdateNotification> getIdentifierUpdateListener()
{
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)
{
Identifier identifier = identifierUpdateNotification.getIdentifier();
if(identifier instanceof ChannelStateIdentifier)
{
State currentState = ((ChannelStateIdentifier)identifier).getValue();
if(mActiveStates.contains(currentState))
{
mActive = true;
}
else if(mActive)
{
mActive = false;
mInactiveTimestamp = System.currentTimeMillis();
}
}
}
/**
* Checks the current active state and when inactive for longer than the specified delay, issues a
* channel frequency rotation request
*/
private void checkState()
{
if(mSourceEventListener != null && !mActive &&
((mInactiveTimestamp + mRotationDelay) < System.currentTimeMillis()))
{
mSourceEventListener.receive(SourceEvent.frequencyRotationRequest());
mInactiveTimestamp = System.currentTimeMillis();
}
}
/**
* Guava event bus method to receive notifications when user preferences are updated. This allows us to
* dynamically adjust rotation delay if/when the user changes the rotation delay value.
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.MULTI_FREQUENCY)
{
mRotationDelay = mUserPreferences.getChannelMultiFrequencyPreference().getRotationDelay();
}
}
@Override
public void reset()
{
}
@Override
public void start()
{
if(mScheduledFuture == null)
{
mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> checkState(), 1, 1, TimeUnit.SECONDS);
}
}
@Override
public void stop()
{
if(mScheduledFuture != null)
{
mScheduledFuture.cancel(true);
}
}
@Override
public void dispose()
{
}
}

View File

@@ -0,0 +1,315 @@
/*
* ******************************************************************************
* 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.
*
* 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;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.source.Source;
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.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Multiple-frequency tuner channel source. Provides a wrapper around a tuner channel source and listens for external
* source events requests to change frequency. Maintains an ordered list of frequencies and automatically tears down
* an existing tuner channel source and obtains a new one with the next frequency from the list, on request.
*/
public class MultiFrequencyTunerChannelSource extends TunerChannelSource
{
private final static Logger mLog = LoggerFactory.getLogger(MultiFrequencyTunerChannelSource.class);
private TunerModel mTunerModel;
private TunerChannelSource mTunerChannelSource;
private List<Long> mFrequencies;
private int mFrequencyListPointer = 0;
private ChannelSpecification mChannelSpecification;
private Listener<ReusableComplexBuffer> mReusableComplexBufferListener;
private Listener<Heartbeat> mHeartbeatListener;
private String mPreferredTuner;
private AtomicBoolean mChangingChannels = new AtomicBoolean();
private boolean mStarted;
private ConsumerSourceEventAdapter mConsumerSourceEventAdapter = new ConsumerSourceEventAdapter();
public MultiFrequencyTunerChannelSource(TunerModel tunerModel, TunerChannelSource tunerChannelSource,
List<Long> frequencies, ChannelSpecification channelSpecification,
String preferredTuner)
{
super(null, tunerChannelSource.getTunerChannel());
mTunerModel = tunerModel;
mTunerChannelSource = tunerChannelSource;
mTunerChannelSource.setSourceEventListener(mConsumerSourceEventAdapter);
mFrequencies = frequencies;
mChannelSpecification = channelSpecification;
mPreferredTuner = preferredTuner;
}
/**
* Cycles this source to use the next frequency in the list
*/
private void nextFrequency()
{
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;
}
//Request the next tuner channel source
getNextSource();
}
}
}
/**
* Persistently attempts to get the next tuner channel source using the next frequency in the list. This method
* should only be invoked by the nextFrequency() method that protects access via the mChangingChannels flag.
*
* 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()
{
if(mStarted)
{
TunerChannel nextChannel = getNextChannel();
Source source = mTunerModel.getSource(nextChannel, mChannelSpecification, mPreferredTuner);
if(source instanceof TunerChannelSource)
{
mTunerChannelSource = (TunerChannelSource)source;
mTunerChannelSource.setSourceEventListener(mConsumerSourceEventAdapter);
mTunerChannelSource.setListener(mReusableComplexBufferListener);
mTunerChannelSource.addHeartbeatListener(mHeartbeatListener);
mTunerChannelSource.start();
mChangingChannels.set(false);
getSourceEventListener().receive(SourceEvent.frequencyRotationSuccessNotification(this, nextChannel.getFrequency()));
}
//If we don't get a channel source because a tuner is not available or none of the available tuners can
//support the frequency, then persistently attempt to get a source by iterating the frequency list
if(mTunerChannelSource == null)
{
getSourceEventListener().receive(SourceEvent.frequencyRotationFailureNotification(this, nextChannel.getFrequency()));
ThreadPool.SCHEDULED.schedule(() -> getNextSource(), 1, TimeUnit.SECONDS);
}
}
}
@Override
public void start()
{
//The initial source should not be null
if(mTunerChannelSource != null)
{
mTunerChannelSource.start();
mStarted = true;
}
}
@Override
public void stop()
{
mStarted = false;
if(mTunerChannelSource != null)
{
mTunerChannelSource.stop();
mTunerChannelSource.removeSourceEventListener();
}
}
/**
* Provides the next tuner channel from the frequency list.
*/
private TunerChannel getNextChannel()
{
mFrequencyListPointer++;
if(mFrequencyListPointer >= mFrequencies.size())
{
mFrequencyListPointer = 0;
}
long frequency = mFrequencies.get(mFrequencyListPointer);
return new TunerChannel(frequency, mChannelSpecification.getBandwidth());
}
/**
* Adds the heartbeat listener to receive heartbeats from the enclosed tuner channel source.
*/
@Override
public void addHeartbeatListener(Listener<Heartbeat> listener)
{
mHeartbeatListener = listener;
if(mTunerChannelSource != null)
{
mTunerChannelSource.addHeartbeatListener(listener);
}
}
/**
* Removes the heartbeat listener from receiving heartbeats from the enclosed tuner channel source
*/
@Override
public void removeHeartbeatListener(Listener<Heartbeat> listener)
{
mHeartbeatListener = null;
if(mTunerChannelSource != null)
{
mTunerChannelSource.removeHeartbeatListener(listener);
}
}
@Override
public void setFrequency(long frequency)
{
if(mTunerChannelSource != null)
{
mTunerChannelSource.setFrequency(frequency);
}
}
@Override
protected void setSampleRate(double sampleRate)
{
if(mTunerChannelSource != null)
{
mTunerChannelSource.setSampleRate(sampleRate);
}
}
@Override
protected void setChannelFrequencyCorrection(long correction)
{
if(mTunerChannelSource != null)
{
mTunerChannelSource.setChannelFrequencyCorrection(correction);
}
}
@Override
public long getChannelFrequencyCorrection()
{
if(mTunerChannelSource != null)
{
return mTunerChannelSource.getChannelFrequencyCorrection();
}
return 0;
}
@Override
protected void processSamples()
{
if(mTunerChannelSource != null)
{
mTunerChannelSource.processSamples();
}
}
@Override
public void setListener(Listener<ReusableComplexBuffer> complexBufferListener)
{
mReusableComplexBufferListener = complexBufferListener;
if(mTunerChannelSource != null)
{
mTunerChannelSource.setListener(complexBufferListener);
}
}
@Override
public void removeListener(Listener<ReusableComplexBuffer> listener)
{
if(mTunerChannelSource != null)
{
mTunerChannelSource.removeListener(listener);
}
mReusableComplexBufferListener = null;
}
@Override
public double getSampleRate()
{
if(mTunerChannelSource != null)
{
return mTunerChannelSource.getSampleRate();
}
return 0;
}
/**
* Overrides the parent method so that we can intercept external requests to change to the next frequency in the
* frequency list.
*
* @param sourceEvent to process
* @throws SourceException
*/
@Override
public void process(SourceEvent sourceEvent) throws SourceException
{
if(sourceEvent.getEvent() == SourceEvent.Event.REQUEST_FREQUENCY_ROTATION)
{
nextFrequency();
}
else if(mTunerChannelSource != null)
{
mTunerChannelSource.process(sourceEvent);
}
}
/**
* Listener for source events produced by the enclosed tuner channel source. This adapter receives events from
* the wrapped tuner channel source and rebroadcasts them as consumer source events from this enclosing multi-frequency
* tuner channel source and to the registered external consumer source event listener.
*/
public class ConsumerSourceEventAdapter implements Listener<SourceEvent>
{
@Override
public void receive(SourceEvent sourceEvent)
{
broadcastConsumerSourceEvent(sourceEvent);
}
}
}

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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
@@ -14,8 +15,8 @@
*
* 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;
import io.github.dsheirer.sample.Listener;
@@ -117,7 +118,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
public void start()
{
//Broadcast current frequency and sample rate so consumer can configure correctly
broadcastConsumerSourceEvent(SourceEvent.frequencyChange(getFrequency()));
broadcastConsumerSourceEvent(SourceEvent.frequencyChange(this, getFrequency(), "Startup"));
broadcastProducerSourceEvent(SourceEvent.startSampleStreamRequest(this));
mScheduledIntervalProcessor.start();
}
@@ -186,6 +187,10 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
setChannelFrequencyCorrection(sourceEvent.getValue().longValue());
break;
case NOTIFICATION_FREQUENCY_ROTATION_FAILURE:
case NOTIFICATION_FREQUENCY_ROTATION_SUCCESS:
//Ignore
break;
default:
mLog.error("Ignoring unrecognized source event: " + sourceEvent.getEvent() + " from [" +
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "") + "]");

View File

@@ -1,18 +1,22 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* ******************************************************************************
* 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.
* 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.
* 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/>
*
******************************************************************************/
* 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.frequency;
import io.github.dsheirer.source.ISourceEventProcessor;
@@ -267,7 +271,7 @@ public class FrequencyController
*/
protected void broadcastFrequencyChange() throws SourceException
{
broadcast(SourceEvent.frequencyChange(mFrequency));
broadcast(SourceEvent.frequencyChange(null, mFrequency));
}
/**