#1397 Adds channel fft frequency overlay, cursor readout, pll tracking indicator, and channel bandwidth lines. (#1406)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2023-01-13 04:28:00 -05:00
committed by GitHub
parent 8ce8aaddf9
commit f97b2150b9
27 changed files with 1406 additions and 529 deletions

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -29,6 +29,7 @@ public class SourceEvent
public enum Event
{
NOTIFICATION_CHANNEL_COUNT_CHANGE,
NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION,
NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
NOTIFICATION_CHANNEL_POWER,
NOTIFICATION_CHANNEL_SAMPLE_RATE_CHANGE,
@@ -40,6 +41,7 @@ public class SourceEvent
NOTIFICATION_FREQUENCY_ROTATION_FAILURE,
NOTIFICATION_MEASURED_FREQUENCY_ERROR,
NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED,
NOTIFICATION_PLL_FREQUENCY,
NOTIFICATION_RECORDING_FILE_LOADED,
NOTIFICATION_SAMPLE_RATE_CHANGE,
NOTIFICATION_SQUELCH_THRESHOLD,
@@ -47,7 +49,6 @@ public class SourceEvent
NOTIFICATION_TUNER_SHUTDOWN,
NOTIFICATION_ERROR_STATE,
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
REQUEST_FREQUENCY_CHANGE,
REQUEST_FREQUENCY_ROTATION,
REQUEST_FREQUENCY_SELECTION,
@@ -242,13 +243,13 @@ public class SourceEvent
}
/**
* Creates a new channel frequency correction change event
* Creates a new PLL frequency error measurement notification event.
*
* @param frequencyCorrection in hertz
* @param frequencyError in hertz
*/
public static SourceEvent channelFrequencyCorrectionChange(long frequencyCorrection)
public static SourceEvent pllFrequencyMeasurement(long frequencyError)
{
return new SourceEvent(Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, frequencyCorrection);
return new SourceEvent(Event.NOTIFICATION_PLL_FREQUENCY, frequencyError);
}
/**
@@ -332,16 +333,6 @@ public class SourceEvent
return new SourceEvent(Event.REQUEST_FREQUENCY_CHANGE, frequency);
}
/**
* Creates a new channel frequency correction change request event
*
* @param frequency requested
*/
public static SourceEvent channelFrequencyCorrectionRequest(long frequency)
{
return new SourceEvent(Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, frequency);
}
/**
* Creates a new start sample stream request event. This method uses the current system time in
* milliseconds as the requested sample start time.

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -50,9 +50,7 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
private IRealDecimationFilter mIDecimationFilter;
private IRealDecimationFilter mQDecimationFilter;
private double mChannelSampleRate;
private long mChannelFrequencyCorrection = 0;
private long mTunerFrequency;
private Listener<ComplexSamples> mListener;
private StreamProcessorWithHeartbeat<ComplexSamples> mStreamHeartbeatProcessor;
/**
@@ -131,38 +129,25 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
updateMixerFrequencyOffset();
}
/**
* Current frequency correction being applied to the channel sample stream
*
* @return correction in hertz
*/
public long getFrequencyCorrection()
{
return mChannelFrequencyCorrection;
}
/**
* Changes the frequency correction value and broadcasts the change to the registered downstream listener.
*
* @param correction current frequency correction value.
*/
public void setFrequencyCorrection(long correction)
{
mChannelFrequencyCorrection = correction;
updateMixerFrequencyOffset();
broadcastConsumerSourceEvent(SourceEvent.frequencyCorrectionChange(mChannelFrequencyCorrection));
}
/**
* Calculates the local mixer frequency offset from the tuned frequency,
* channel's requested frequency, and channel frequency correction.
*/
private void updateMixerFrequencyOffset()
{
long offset = mTunerFrequency - getTunerChannel().getFrequency() - mChannelFrequencyCorrection;
long offset = mTunerFrequency - getTunerChannel().getFrequency();
mFrequencyCorrectionMixer.setFrequency(offset);
}
/**
* Mixer frequency applying offset to the samples.
* @return mixer frequency
*/
public long getMixerFrequency()
{
return (long)mFrequencyCorrectionMixer.getFrequency();
}
/**
* Sets the sample rate of the incoming sample stream from the producer
*
@@ -174,28 +159,6 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
//Not implemented. Sample rate changes are not permitted once sample stream starts
}
/**
* Sets the frequency correction for the outbound sample stream to the consumer
*
* @param correction in hertz
*/
@Override
protected void setChannelFrequencyCorrection(long correction)
{
mChannelFrequencyCorrection = correction;
broadcastConsumerSourceEvent(SourceEvent.channelFrequencyCorrectionChange(correction));
updateMixerFrequencyOffset();
}
/**
* Frequency correction value currently being applied to the outbound sample stream
*/
@Override
public long getChannelFrequencyCorrection()
{
return mChannelFrequencyCorrection;
}
/**
* Sets the listener to receive the complex buffer sample output from this channel
*

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -29,13 +29,12 @@ import io.github.dsheirer.source.heartbeat.Heartbeat;
import io.github.dsheirer.source.tuner.channel.rotation.FrequencyLockChangeRequest;
import io.github.dsheirer.source.tuner.manager.TunerManager;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Multiple-frequency tuner channel source. Provides a wrapper around a tuner channel source and listens for external
@@ -273,26 +272,6 @@ public class MultiFrequencyTunerChannelSource extends TunerChannelSource
}
}
@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
public void setListener(Listener<ComplexSamples> complexSamplesListener)
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -27,11 +27,10 @@ import io.github.dsheirer.source.ISourceEventListener;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.util.Dispatcher;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
/**
* Pass-through channel source that simply passes complex sample buffers from the tuner controller
* directly to the registered listener
@@ -90,18 +89,6 @@ public class PassThroughChannelSource extends TunerChannelSource implements ISou
mLog.debug("Request to set sample rate: " + sampleRate);
}
@Override
protected void setChannelFrequencyCorrection(long correction)
{
mLog.debug("Request to set frequency correction: " + correction);
}
@Override
public long getChannelFrequencyCorrection()
{
return 0;
}
@Override
public void setListener(Listener<ComplexSamples> listener)
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -78,17 +78,6 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
*/
protected abstract void setSampleRate(double sampleRate);
/**
* Sets the frequency correction for the outbound sample stream to the consumer
* @param correction in hertz
*/
protected abstract void setChannelFrequencyCorrection(long correction);
/**
* Frequency correction value currently being applied to the outbound sample stream
*/
public abstract long getChannelFrequencyCorrection();
/**
* Sets the listener to receive the complex buffer sample output from this channel
* @param complexSamplesListener to receive complex buffers
@@ -152,11 +141,11 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
{
switch(sourceEvent.getEvent())
{
case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
case NOTIFICATION_FREQUENCY_CHANGE:
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED:
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED:
case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
case NOTIFICATION_PLL_FREQUENCY:
case NOTIFICATION_STOP_SAMPLE_STREAM:
//no-op
break;
@@ -172,10 +161,6 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
sourceEvent.setSource(this);
broadcastProducerSourceEvent(sourceEvent);
break;
//Request events from the consumer
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
setChannelFrequencyCorrection(sourceEvent.getValue().longValue());
break;
case NOTIFICATION_FREQUENCY_ROTATION_FAILURE:
case NOTIFICATION_FREQUENCY_ROTATION_SUCCESS:
case NOTIFICATION_CHANNEL_POWER:

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -169,7 +169,7 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
* @return optimal center tuned frequency for the set of currently sourced tuner channels.
* @throws IllegalArgumentException if a center frequency cannot be determined for the set of tuner channels
*/
private long getCenterFrequency(SortedSet<TunerChannel> channels, long currentCenterFrequency)
protected long getCenterFrequency(SortedSet<TunerChannel> channels, long currentCenterFrequency)
throws IllegalArgumentException
{
if(channels.isEmpty())

View File

@@ -0,0 +1,50 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.manager;
import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import java.util.SortedSet;
public class TestPolyphaseChannelSourceManager extends PolyphaseChannelSourceManager
{
private boolean mLockCenterFrequency = true;
/**
* Constructs an instance
*
* @param tunerController with a center tuned frequency that will be managed by this instance
*/
public TestPolyphaseChannelSourceManager(TunerController tunerController)
{
super(tunerController);
}
@Override
protected long getCenterFrequency(SortedSet<TunerChannel> channels, long currentCenterFrequency) throws IllegalArgumentException
{
if(mLockCenterFrequency)
{
return currentCenterFrequency;
}
return super.getCenterFrequency(channels, currentCenterFrequency);
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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,6 +20,7 @@ package io.github.dsheirer.source.tuner.test;
import io.github.dsheirer.buffer.FloatNativeBuffer;
import io.github.dsheirer.buffer.INativeBuffer;
import io.github.dsheirer.dsp.oscillator.AWGNOscillator;
import io.github.dsheirer.dsp.oscillator.IComplexOscillator;
import io.github.dsheirer.dsp.oscillator.OscillatorFactory;
import io.github.dsheirer.sample.Broadcaster;
@@ -59,7 +60,9 @@ public class SampleGenerator
throw new IllegalArgumentException("Sweep update rate cannot be greater than sample rate");
}
mComplexOscillator = OscillatorFactory.getComplexOscillator(frequency, sampleRate);
// mComplexOscillator = OscillatorFactory.getComplexOscillator(frequency, sampleRate);
IComplexOscillator oscillator = OscillatorFactory.getComplexOscillator(frequency, sampleRate);
mComplexOscillator = new AWGNOscillator(oscillator, 0.003f);
mInterval = 1000 / (sampleRate / mSamplesPerInterval);
mSweepUpdateInterval = sweepUpdateRate;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -22,6 +22,7 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.TunerClass;
import io.github.dsheirer.source.tuner.TunerType;
import io.github.dsheirer.source.tuner.manager.TestPolyphaseChannelSourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +39,7 @@ public class TestTuner extends Tuner
public TestTuner(ITunerErrorListener tunerErrorListener)
{
super(new TestTunerController(), tunerErrorListener);
setChannelSourceManager(new TestPolyphaseChannelSourceManager(getTunerController()));
}
/**

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -33,7 +33,7 @@ public class TestTunerController extends TunerController
private final static Logger mLog = LoggerFactory.getLogger(TestTunerController.class);
public static final long MINIMUM_FREQUENCY = 1l;
public static final long MAXIMUM_FREQUENCY = 1000000000l;
public static final long MAXIMUM_FREQUENCY = 1_000_000_000l;
public static final int SAMPLE_RATE = 2_400_000;
public static final int DC_NOISE_BANDWIDTH = 0;
public static final double USABLE_BANDWIDTH_PERCENTAGE = 1.00;
@@ -42,7 +42,7 @@ public class TestTunerController extends TunerController
public static final long SAMPLE_GENERATION_INTERVAL = 1000 / SPECTRAL_FRAME_RATE;
private SampleGenerator mSampleGenerator;
private long mFrequency = 100000000l;
private long mFrequency = 460662500l;
/**
* Tuner controller testing implementation.