mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Resolves issue with channel frequency correction events not being broadcast within the processing chain and rendering in the spectral display.
This commit is contained in:
@@ -20,11 +20,13 @@ package source;
|
||||
import module.Module;
|
||||
import sample.SampleType;
|
||||
import sample.real.IOverflowListener;
|
||||
import source.tuner.frequency.IFrequencyChangeListener;
|
||||
import source.tuner.frequency.IFrequencyChangeProvider;
|
||||
|
||||
/**
|
||||
* Abstract class to define the minimum functionality of a sample data provider.
|
||||
*/
|
||||
public abstract class Source extends Module
|
||||
public abstract class Source extends Module implements IFrequencyChangeListener, IFrequencyChangeProvider
|
||||
{
|
||||
protected SampleType mSampleType;
|
||||
protected IOverflowListener mOverflowListener;
|
||||
|
||||
@@ -36,11 +36,11 @@ import sample.real.RealBuffer;
|
||||
import source.RealSource;
|
||||
import source.SourceException;
|
||||
import dsp.gain.AutomaticGainControl;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
|
||||
public class RealMixerSource extends RealSource
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( RealMixerSource.class );
|
||||
private final static Logger mLog = LoggerFactory.getLogger( RealMixerSource.class );
|
||||
|
||||
private long mFrequency = 0;
|
||||
private int mBufferSize = 16384;
|
||||
@@ -48,13 +48,11 @@ public class RealMixerSource extends RealSource
|
||||
private BufferReader mBufferReader = new BufferReader();
|
||||
private TargetDataLine mTargetDataLine;
|
||||
private AudioFormat mAudioFormat;
|
||||
private String mDisplayName;
|
||||
private int mBytesPerFrame = 0;
|
||||
private ISampleAdapter mSampleAdapter;
|
||||
private AutomaticGainControl mAGC = new AutomaticGainControl();
|
||||
|
||||
CopyOnWriteArrayList<Listener<RealBuffer>> mSampleListeners =
|
||||
new CopyOnWriteArrayList<Listener<RealBuffer>>();
|
||||
CopyOnWriteArrayList<Listener<RealBuffer>> mSampleListeners = new CopyOnWriteArrayList<Listener<RealBuffer>>();
|
||||
|
||||
/**
|
||||
* Real Mixer Source - constructs a reader on the mixer/sound card target
|
||||
@@ -80,8 +78,27 @@ public class RealMixerSource extends RealSource
|
||||
mAudioFormat = format;
|
||||
mSampleAdapter = sampleAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFrequencyChangeListener()
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||
{
|
||||
//Not implemented
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
@@ -133,23 +150,15 @@ public class RealMixerSource extends RealSource
|
||||
* buffer, otherwise send him a copy of the buffer */
|
||||
if( it.hasNext() )
|
||||
{
|
||||
// next.receive( mAGC.process( samples.copyOf() ) );
|
||||
next.receive( samples.copyOf() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// next.receive( mAGC.process( samples ) );
|
||||
next.receive( samples );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mDisplayName;
|
||||
}
|
||||
|
||||
public int getSampleRate()
|
||||
{
|
||||
if( mTargetDataLine != null )
|
||||
|
||||
@@ -34,7 +34,9 @@ import source.ComplexSource;
|
||||
import source.SourceException;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
||||
import source.tuner.frequency.IFrequencyChangeListener;
|
||||
import source.tuner.frequency.IFrequencyChangeProcessor;
|
||||
import source.tuner.frequency.IFrequencyChangeProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -65,6 +67,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
||||
private ComplexPrimeCICDecimate mDecimationFilter;
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
private IFrequencyChangeProcessor mFrequencyChangeProcessor;
|
||||
private DownstreamProcessor mDownstreamFrequencyEventProcessor = new DownstreamProcessor();
|
||||
private ScheduledFuture<?> mTaskHandle;
|
||||
|
||||
private long mTunerFrequency = 0;
|
||||
@@ -75,7 +78,6 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
||||
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private boolean mExpended = false;
|
||||
private int mCount;
|
||||
|
||||
/**
|
||||
* Provides a Digital Drop Channel (DDC) to decimate the IQ output from a
|
||||
@@ -109,7 +111,8 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
||||
mMixer = new Oscillator(frequencyOffset, mTuner.getTunerController().getSampleRate());
|
||||
|
||||
/* Fire a sample rate change event to setup the decimation chain */
|
||||
frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getTunerController().getSampleRate()));
|
||||
frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE,
|
||||
mTuner.getTunerController().getSampleRate()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +134,10 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
||||
|
||||
if(mRunning.compareAndSet(false, true))
|
||||
{
|
||||
//Broadcast current frequency and sample rate settings so all downstream components are aware
|
||||
mDownstreamFrequencyEventProcessor.broadcastCurrentFrequency();
|
||||
mDownstreamFrequencyEventProcessor.broadcastCurrentSampleRate();
|
||||
|
||||
//Schedule the decimation task to run every 9 ms (111 iterations/second), an odd periodicity relative
|
||||
//to the inbound periodicity of 20 ms, to attempt to avoid thread queue contention
|
||||
mTaskHandle = executor.scheduleAtFixedRate(mDecimationProcessor, 0, 9, TimeUnit.MILLISECONDS);
|
||||
@@ -184,6 +191,20 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the frequency correction value and broadcasts the change to the registered downstream listener.
|
||||
* @param correction current frequency correction value.
|
||||
*/
|
||||
private void setFrequencyCorrection(int correction)
|
||||
{
|
||||
mChannelFrequencyCorrection = correction;
|
||||
|
||||
updateMixerFrequencyOffset();
|
||||
|
||||
mDownstreamFrequencyEventProcessor.broadcast(
|
||||
new FrequencyChangeEvent( Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, mChannelFrequencyCorrection));
|
||||
}
|
||||
|
||||
public Tuner getTuner()
|
||||
{
|
||||
return mTuner;
|
||||
@@ -242,50 +263,49 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
mTunerFrequency = event.getValue().longValue();
|
||||
updateMixerFrequencyOffset();
|
||||
break;
|
||||
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
mChannelFrequencyCorrection = event.getValue().intValue();
|
||||
|
||||
updateMixerFrequencyOffset();
|
||||
|
||||
if(mFrequencyChangeProcessor != null)
|
||||
{
|
||||
mFrequencyChangeProcessor.frequencyChanged(new FrequencyChangeEvent(
|
||||
Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, mChannelFrequencyCorrection));
|
||||
}
|
||||
//Reset frequency correction so that downstream components can recalculate the value
|
||||
setFrequencyCorrection(0);
|
||||
break;
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
int sampleRate = event.getValue().intValue();
|
||||
|
||||
if(mTunerSampleRate != sampleRate)
|
||||
{
|
||||
mMixer.setSampleRate(sampleRate);
|
||||
|
||||
/* Get new decimation filter */
|
||||
mDecimationFilter = FilterFactory.getDecimationFilter(sampleRate, CHANNEL_RATE, 1,
|
||||
CHANNEL_PASS_FREQUENCY, 60, WindowType.HAMMING);
|
||||
|
||||
/* re-add the original output listener */
|
||||
mDecimationFilter.setListener(mListener);
|
||||
|
||||
mTunerSampleRate = sampleRate;
|
||||
}
|
||||
setSampleRate(sampleRate);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sample rate to the requested value and notifies any downstream components of the change
|
||||
* @param sampleRate to set
|
||||
*/
|
||||
private void setSampleRate(int sampleRate)
|
||||
{
|
||||
if(mTunerSampleRate != sampleRate)
|
||||
{
|
||||
mMixer.setSampleRate(sampleRate);
|
||||
|
||||
/* Get new decimation filter */
|
||||
mDecimationFilter = FilterFactory.getDecimationFilter(sampleRate, CHANNEL_RATE, 1,
|
||||
CHANNEL_PASS_FREQUENCY, 60, WindowType.HAMMING);
|
||||
|
||||
/* re-add the original output listener */
|
||||
mDecimationFilter.setListener(mListener);
|
||||
|
||||
mTunerSampleRate = sampleRate;
|
||||
|
||||
mDownstreamFrequencyEventProcessor.broadcastCurrentSampleRate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the local mixer frequency offset from the tuned frequency,
|
||||
* channel's requested frequency, and channel frequency correction.
|
||||
*/
|
||||
private void updateMixerFrequencyOffset()
|
||||
{
|
||||
long offset = mTunerFrequency -
|
||||
mTunerChannel.getFrequency() -
|
||||
mChannelFrequencyCorrection;
|
||||
|
||||
long offset = mTunerFrequency - mTunerChannel.getFrequency() - mChannelFrequencyCorrection;
|
||||
mMixer.setFrequency(offset);
|
||||
}
|
||||
|
||||
@@ -299,6 +319,140 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
|
||||
return mTunerChannel.getFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements IFrequencyChangeProvider to enable this source to broadcast frequency change events to downstream
|
||||
* listeners.
|
||||
*
|
||||
* @param listener to receive downstream events
|
||||
*/
|
||||
@Override
|
||||
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||
{
|
||||
mDownstreamFrequencyEventProcessor.setFrequencyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements IFrequencyChangeProvider to remove the frequency change listener from receiving down-stream frequency
|
||||
* change events.
|
||||
*/
|
||||
@Override
|
||||
public void removeFrequencyChangeListener()
|
||||
{
|
||||
mDownstreamFrequencyEventProcessor.removeFrequencyChangeListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements IFrequencyChangeListener to receive frequency change events containing requests from downstream
|
||||
* listeners to change frequency values.
|
||||
* @return listener
|
||||
*/
|
||||
@Override
|
||||
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||
{
|
||||
return mDownstreamFrequencyEventProcessor.getFrequencyChangeListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Managers frequency change requests and notifications from/to any downstream component. Downstream
|
||||
* components are those that receive samples from this tuner channel source. These downstream components will be
|
||||
* notified of any frequency or sample rate change events and will also be able to request frequency correction
|
||||
* updates.
|
||||
*/
|
||||
public class DownstreamProcessor implements IFrequencyChangeListener, IFrequencyChangeProvider,
|
||||
Listener<FrequencyChangeEvent>
|
||||
{
|
||||
//Listener to receive downstream events
|
||||
private Listener<FrequencyChangeEvent> mListener;
|
||||
|
||||
/**
|
||||
* Broadcasts the frequency change event to the downstream frequency change listener
|
||||
* @param event to broadcast
|
||||
*/
|
||||
public void broadcast(FrequencyChangeEvent event)
|
||||
{
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the current frequency of this tuner channel source to the downstream listener
|
||||
*/
|
||||
public void broadcastCurrentFrequency()
|
||||
{
|
||||
try
|
||||
{
|
||||
long frequency = getFrequency();
|
||||
broadcast(new FrequencyChangeEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, frequency));
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error obtaining frequency from tuner to broadcast downstream");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the current decimated sample rate of this tuner channel source
|
||||
*/
|
||||
public void broadcastCurrentSampleRate()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Note: downstream sample rate is currently a fixed value -- it will change in the future
|
||||
broadcast(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, getSampleRate()));
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error obtaining sample rate from tuner to broadcast downstream");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the downstream listener to receive frequency change events from this tuner channel source
|
||||
* @param listener to receive events
|
||||
*/
|
||||
@Override
|
||||
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the downstream listener from receiving frequency change events.
|
||||
*/
|
||||
@Override
|
||||
public void removeFrequencyChangeListener()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for receiving frequency change events from downstream components
|
||||
*/
|
||||
@Override
|
||||
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes frequency change events from downstream components.
|
||||
* @param event to process
|
||||
*/
|
||||
@Override
|
||||
public void receive(FrequencyChangeEvent event)
|
||||
{
|
||||
switch(event.getEvent())
|
||||
{
|
||||
//Frequency correction requests are the only change requests supported from downstream components
|
||||
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
setFrequencyCorrection(event.getValue().intValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimates an inbound buffer of I/Q samples from the source down to the
|
||||
* standard 48000 channel sample rate
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
/*******************************************************************************
|
||||
* 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 source.tuner.frequency;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
/**
|
||||
* Provider of Frequency Change Events
|
||||
*/
|
||||
public interface IFrequencyChangeProvider
|
||||
{
|
||||
public void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener );
|
||||
public void removeFrequencyChangeListener();
|
||||
void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener );
|
||||
void removeFrequencyChangeListener();
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014,2015 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/>
|
||||
* 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 source.wave;
|
||||
|
||||
@@ -36,6 +37,7 @@ import sample.complex.ComplexBuffer;
|
||||
import source.ComplexSource;
|
||||
import source.IControllableFileSource;
|
||||
import source.IFrameLocationListener;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
|
||||
public class ComplexWaveSource extends ComplexSource implements IControllableFileSource
|
||||
{
|
||||
@@ -54,8 +56,27 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
{
|
||||
mFile = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFrequencyChangeListener()
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||
{
|
||||
//Not implemented
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
stop();
|
||||
|
||||
@@ -36,6 +36,7 @@ import sample.real.RealBuffer;
|
||||
import source.IControllableFileSource;
|
||||
import source.IFrameLocationListener;
|
||||
import source.RealSource;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
|
||||
public class RealWaveSource extends RealSource
|
||||
implements IControllableFileSource, AutoCloseable
|
||||
@@ -55,8 +56,27 @@ public class RealWaveSource extends RealSource
|
||||
{
|
||||
mFile = file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFrequencyChangeListener()
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||
{
|
||||
//Not implemented
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user