mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#2212 Introduce carrier offset measurement processor to monitor a 12.5 kHz channel and provide estimated carrier offset measurements that allow the tuner's frequency error monitor to correct for the average error reported from each channel. Uses a 15 dB threshold where the signal must be at least 15 dB above the noise floor before it will calculate and broadcast the estimate. Stores the updated PPM in the tuner configuration after each update to ensure the value persists across sessions. When selected in the tuner panel, updates the displayed PPM value after each PPM autocorrection. (#2218)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -59,7 +59,7 @@ public class FrequencyOverlayPanel extends JPanel implements ISourceEventProcess
|
||||
private int mBandwidth = 0;
|
||||
private int mChannelBandwidth = 0;
|
||||
|
||||
private long mPllTrackingFrequency = 0;
|
||||
private long mEstimatedCarrierOffsetFrequency = 0;
|
||||
private Point mCursorLocation = new Point(0, 0);
|
||||
private boolean mCursorVisible = false;
|
||||
|
||||
@@ -103,12 +103,12 @@ public class FrequencyOverlayPanel extends JPanel implements ISourceEventProcess
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tracking frequency of the Phase Locked Loop (PLL)
|
||||
* @param pllTrackingFrequency current setting. Set to 0 to reset.
|
||||
* Sets the estimated carrier offset of the channel's signal
|
||||
* @param estimatedCarrierOffsetFrequency current setting. Set to 0 to reset.
|
||||
*/
|
||||
public void setPllTrackingFrequency(long pllTrackingFrequency)
|
||||
public void setEstimatedCarrierOffsetFrequency(long estimatedCarrierOffsetFrequency)
|
||||
{
|
||||
mPllTrackingFrequency = pllTrackingFrequency;
|
||||
mEstimatedCarrierOffsetFrequency = estimatedCarrierOffsetFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +212,7 @@ public class FrequencyOverlayPanel extends JPanel implements ISourceEventProcess
|
||||
graphics.setRenderingHints(RENDERING_HINTS);
|
||||
drawFrequencies(graphics);
|
||||
drawCursor(graphics);
|
||||
drawPLL(graphics);
|
||||
drawEstimatedCarrierOffset(graphics);
|
||||
drawChannelBandwidth(graphics);
|
||||
}
|
||||
|
||||
@@ -326,16 +326,16 @@ public class FrequencyOverlayPanel extends JPanel implements ISourceEventProcess
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the Phase Locked Loop tracking frequency, offset from channel center
|
||||
* Draws the estimated carrier offset measured by the CarrierOffsetProcessor
|
||||
*/
|
||||
private void drawPLL(Graphics2D graphics)
|
||||
private void drawEstimatedCarrierOffset(Graphics2D graphics)
|
||||
{
|
||||
if(mPllTrackingFrequency == 0)
|
||||
if(mEstimatedCarrierOffsetFrequency == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long frequency = mFrequency - mPllTrackingFrequency;
|
||||
long frequency = mFrequency + mEstimatedCarrierOffsetFrequency;
|
||||
double xAxis = getAxisFromFrequency(frequency);
|
||||
|
||||
double height = getSize().getHeight() - mSpectrumInset;
|
||||
@@ -349,7 +349,7 @@ public class FrequencyOverlayPanel extends JPanel implements ISourceEventProcess
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the Phase Locked Loop tracking frequency, offset from channel center
|
||||
* Draws the channel bandwidth indicators
|
||||
*/
|
||||
private void drawChannelBandwidth(Graphics2D graphics)
|
||||
{
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 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/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.spectrum.converter;
|
||||
|
||||
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Converts complex DFT output to scaled dB values with a maximum amplitude of
|
||||
* 0 dB and all values scaled to the minimum dB value which is:
|
||||
* Converts complex DFT output to scaled dB values with a maximum amplitude of 0 dB and all values scaled to the
|
||||
* minimum dB value which is:
|
||||
*
|
||||
* Max: 0dB
|
||||
* Min: 20 * log10( 1.0 / ( 2.0 ^ ( bit depth - 1) ) = scaling factor
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* The magnitude of each DFT output bin must be scaled by the number of DFT
|
||||
* points to normalize the output to a range of ( 0 to 1.0 ). DFT bin magnitude
|
||||
* can be calculated as:
|
||||
* The magnitude of each DFT output bin must be scaled by the number of DFT points to normalize the output to a range
|
||||
* of ( 0 to 1.0 ). DFT bin magnitude can be calculated as:
|
||||
*
|
||||
* 1) sqrt( bin[ x ] * bin[ x ] + bin[ x + 1 ] * bin[ x + 1] ) / DFTSize or,
|
||||
* 2) ( bin[ x ] * bin[ x ] + bin[ x + 1 ] * bin[ x + 1] ) / ( DFTSize * DFTSize )
|
||||
@@ -42,70 +42,72 @@ import org.apache.commons.math3.util.FastMath;
|
||||
*
|
||||
* Max Value: 20 * log10( 1.0 ) = 0 dB
|
||||
*
|
||||
* When plotting, these dB bin values should be scaled according to the maximum
|
||||
* dynamic range supported by the source. Maximum dynamic range for bit depth
|
||||
* is calculated as:
|
||||
* When plotting, these dB bin values should be scaled according to the maximum dynamic range supported by the source.
|
||||
* Maximum dynamic range for bit depth is calculated as:
|
||||
*
|
||||
* 20 * log10( 1 / ( 2 ^ ( bit depth - 1 ) )
|
||||
*
|
||||
* A source that provides 12-bit samples can produce values in the range of
|
||||
* ( 0 to 4096 ) or -2048 to 2047. The smallest observable value would then
|
||||
* be 1 / 2048.
|
||||
* A source that provides 12-bit samples can produce values in the range of ( 0 to 4096 ) or -2048 to 2047. The
|
||||
* smallest observable value would then be 1 / 2048.
|
||||
*
|
||||
* So, the smallest decibel value of a source producing 12 bit samples is:
|
||||
*
|
||||
* 20 * log10( 1 / 2048 ) = -66.23 dB
|
||||
*
|
||||
* and the dynamic range is: (-66.23 to 0.0 dB)
|
||||
*
|
||||
*/
|
||||
public class ComplexDecibelConverter extends DFTResultsConverter
|
||||
{
|
||||
/**
|
||||
* Converts the output of the JTransforms FloatFFT_1D.complexForward()
|
||||
* calculation into the power spectrum in decibels, normalized to the
|
||||
* sample bit depth.
|
||||
* Converts the output of the JTransforms FloatFFT_1D.complexForward() calculation into the power spectrum in
|
||||
* decibels, normalized to the sample bit depth.
|
||||
*/
|
||||
public ComplexDecibelConverter()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( float[] results )
|
||||
{
|
||||
|
||||
public static float[] convert(float[] results)
|
||||
{
|
||||
int halfResults = results.length / 2;
|
||||
|
||||
// float dftBinSizeScalor = 1.0f / (float)Math.pow( halfResults, 2.0 );
|
||||
float dftBinSizeScalor = 1.0f / (float)halfResults;
|
||||
|
||||
float[] processed = new float[ halfResults ];
|
||||
|
||||
float dftBinSizeScalor = 1.0f / (float) halfResults;
|
||||
float[] processed = new float[halfResults];
|
||||
int middle = processed.length / 2;
|
||||
|
||||
for( int x = 0; x < results.length; x += 2 )
|
||||
{
|
||||
//Calculate the magnitude squared (power) value from each bin's real
|
||||
//and imaginary value and scale it to the DFT bin size squared.
|
||||
//Convert the scaled value to decibels.
|
||||
float decibels = 10.0f * (float) FastMath.log10(
|
||||
( ( results[ x ] * results[ x ] ) +
|
||||
( results[ x + 1 ] * results[ x + 1 ] ) ) * dftBinSizeScalor );
|
||||
|
||||
// We have to swap the upper and lower halves of the JTransforms
|
||||
// DFT results for correct display
|
||||
|
||||
int index = x / 2;
|
||||
|
||||
if( index >= middle )
|
||||
float temp, decibels;
|
||||
for(int x = 0; x < results.length; x += 2)
|
||||
{
|
||||
//Calculate the magnitude squared (power) value from each bin's real and imaginary value and scale it to the
|
||||
// DFT bin size squared. Convert the scaled value to decibels.
|
||||
temp = ((results[x] * results[x]) + (results[x + 1] * results[x + 1]));
|
||||
|
||||
if(temp == 0)
|
||||
{
|
||||
processed[ index - middle ] = decibels;
|
||||
decibels = -196.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
processed[ index + middle ] = decibels;
|
||||
decibels = 10.0f * (float)FastMath.log10(temp * dftBinSizeScalor);
|
||||
}
|
||||
|
||||
// We have to swap the upper and lower halves of the JTransforms DFT results for correct display
|
||||
int index = x / 2;
|
||||
|
||||
if(index >= middle)
|
||||
{
|
||||
processed[index - middle] = decibels;
|
||||
}
|
||||
else
|
||||
{
|
||||
processed[index + middle] = decibels;
|
||||
}
|
||||
}
|
||||
|
||||
dispatch( processed );
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(float[] results)
|
||||
{
|
||||
dispatch(convert(results));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,32 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.spectrum.converter;
|
||||
|
||||
import io.github.dsheirer.spectrum.DFTResultsListener;
|
||||
import io.github.dsheirer.spectrum.DFTResultsProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public abstract class DFTResultsConverter
|
||||
implements DFTResultsListener, DFTResultsProvider
|
||||
public abstract class DFTResultsConverter implements DFTResultsListener, DFTResultsProvider
|
||||
{
|
||||
private CopyOnWriteArrayList<DFTResultsListener> mListeners =
|
||||
new CopyOnWriteArrayList<DFTResultsListener>();
|
||||
private final List<DFTResultsListener> mListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* DFT Results Converter - for converting the output of the JTransforms
|
||||
@@ -25,22 +42,22 @@ public abstract class DFTResultsConverter
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener( DFTResultsListener listener )
|
||||
public void addListener(DFTResultsListener listener)
|
||||
{
|
||||
mListeners.add( listener );
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener( DFTResultsListener listener )
|
||||
public void removeListener(DFTResultsListener listener)
|
||||
{
|
||||
mListeners.remove( listener );
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
protected void dispatch( float[] results )
|
||||
protected void dispatch(float[] results)
|
||||
{
|
||||
for( DFTResultsListener listener: mListeners )
|
||||
for(DFTResultsListener listener: mListeners)
|
||||
{
|
||||
listener.receive( results );
|
||||
listener.receive(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user