mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
Resolves issue with correct display and scaling of decibel values in
both the spectral and waterfall displays. Resolves issue with gain loss in HilbertTransform.
This commit is contained in:
@@ -389,7 +389,6 @@ public class SpectralDisplayPanel extends JPanel
|
||||
//Register the dft processor to receive samples from the tuner
|
||||
mTuner.addListener( (Listener<ComplexBuffer>)mDFTProcessor );
|
||||
|
||||
mDFTConverter.setSampleSize( mTuner.getSampleSize() );
|
||||
mSpectrumPanel.setSampleSize( mTuner.getSampleSize() );
|
||||
|
||||
//Fire frequency and sample rate change events so that everyone
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.awt.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
@@ -111,8 +112,8 @@ public class SpectrumPanel extends JPanel
|
||||
public void setSampleSize( double sampleSize )
|
||||
{
|
||||
assert( 2.0 <= sampleSize && sampleSize <= 32.0 );
|
||||
|
||||
mDBScale = -(float)( 20.0 * Math.log10( Math.pow( 2.0, sampleSize ) ) );
|
||||
|
||||
mDBScale = (float)( 20.0 * Math.log10( 1 / Math.pow( 2.0, sampleSize - 1 ) ) );
|
||||
}
|
||||
|
||||
public void setAveraging( int size )
|
||||
|
||||
@@ -33,6 +33,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
@@ -48,7 +51,10 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
Pausable,
|
||||
SettingChangeListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( WaterfallPanel.class );
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static DecimalFormat CURSOR_FORMAT = new DecimalFormat( "0.00000" );
|
||||
private static final String SPECTRUM_CURSOR = "spectrum_cursor";
|
||||
private static final String PAUSED = "PAUSED";
|
||||
@@ -202,9 +208,9 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
public void receive( float[] update )
|
||||
{
|
||||
//If our FFT size changes, reset our pixel map and image source
|
||||
if( mFFTSize != update.length - 1 )
|
||||
if( mFFTSize != update.length )
|
||||
{
|
||||
mFFTSize = update.length - 1;
|
||||
mFFTSize = update.length;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
@@ -20,13 +20,50 @@ package spectrum.converter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* 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 )
|
||||
*
|
||||
* Convert each scaled DFT bin magnitude to decibels using this formula:
|
||||
*
|
||||
* 20 * log10( scaledBinValue )
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( ComplexDecibelConverter.class );
|
||||
|
||||
private float mDynamicRangeReference = (float)Math.pow( 2.0, 16.0 );
|
||||
|
||||
/**
|
||||
* Converts the output of the JTransforms FloatFFT_1D.complexForward()
|
||||
* calculation into the power spectrum in decibels, normalized to the
|
||||
@@ -36,44 +73,35 @@ public class ComplexDecibelConverter extends DFTResultsConverter
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the bit depth to establish the maximum dynamic range. All
|
||||
* FFT bin values will be scaled according to this value.
|
||||
*/
|
||||
@Override
|
||||
public void setSampleSize( double size )
|
||||
{
|
||||
assert( 2.0 <= size && size <= 32.0 );
|
||||
|
||||
mDynamicRangeReference = (float)Math.pow( 2.0d, size );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( float[] results )
|
||||
{
|
||||
float[] processed = new float[ results.length / 2 ];
|
||||
|
||||
int half = processed.length / 2;
|
||||
int halfResults = results.length / 2;
|
||||
|
||||
float dftScalor = 1.0f / (float)( halfResults ^ 2 );
|
||||
|
||||
float[] processed = new float[ halfResults ];
|
||||
|
||||
int middle = processed.length / 2;
|
||||
|
||||
for( int x = 0; x < results.length; x += 2 )
|
||||
{
|
||||
//Calculate the magnitude squared value from each bin's real and
|
||||
//imaginary value, scale it to the available dynamic range and
|
||||
//convert to dB.
|
||||
float normalizedMagnitude = 10.0f * (float)Math.log10(
|
||||
//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)Math.log10(
|
||||
( ( results[ x ] * results[ x ] ) +
|
||||
( results[ x + 1 ] * results[ x + 1 ] ) ) /
|
||||
mDynamicRangeReference );
|
||||
( results[ x + 1 ] * results[ x + 1 ] ) ) * dftScalor );
|
||||
|
||||
// We have to swap the upper and lower halves of the JTransforms
|
||||
// DFT results for correct display
|
||||
if( x / 2 >= half )
|
||||
if( x / 2 >= middle )
|
||||
{
|
||||
processed[ x / 2 - half ] = normalizedMagnitude;
|
||||
processed[ x / 2 - middle ] = decibels;
|
||||
}
|
||||
else
|
||||
{
|
||||
processed[ x / 2 + half ] = normalizedMagnitude;
|
||||
processed[ x / 2 + middle ] = decibels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,6 @@ public abstract class DFTResultsConverter
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the sample size in bits to establish the maximum dynamic range.
|
||||
* All FFT bin values will be scaled according to this value.
|
||||
*/
|
||||
public abstract void setSampleSize( double size );
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListeners.clear();
|
||||
|
||||
@@ -7,33 +7,22 @@ public class RealDecibelConverter extends DFTResultsConverter
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger( RealDecibelConverter.class );
|
||||
|
||||
private float mDynamicRangeReference;
|
||||
|
||||
public RealDecibelConverter()
|
||||
{
|
||||
setSampleSize( 24.0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the bit depth to establish the maximum dynamic range. All
|
||||
* FFT bin values will be scaled according to this value.
|
||||
*/
|
||||
@Override
|
||||
public void setSampleSize( double size )
|
||||
{
|
||||
assert( 2.0 <= size && size <= 32.0 );
|
||||
|
||||
mDynamicRangeReference = (float)Math.pow( 2.0d, size );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the output of the JTransforms FloatFFT_1D.realForward()
|
||||
* calculation into a normalized power spectrum in decibels, per description
|
||||
* in Lyons, Understanding Digital Signal Processing, 3e, page 141.
|
||||
*
|
||||
* Note: this is only calculating the lower half of the spectrum
|
||||
*/
|
||||
@Override
|
||||
public void receive( float[] results )
|
||||
{
|
||||
float dftScalor = 1.0f / (float)( results.length ^ 2 );
|
||||
|
||||
float[] processed = new float[ results.length / 4 ];
|
||||
|
||||
int index = 0;
|
||||
@@ -42,12 +31,9 @@ public class RealDecibelConverter extends DFTResultsConverter
|
||||
{
|
||||
index = x * 2;
|
||||
|
||||
float normalizedMagnitude = 10.0f * (float)Math.log10(
|
||||
processed[ x ] = 20.0f * (float)Math.log10(
|
||||
( ( results[ index ] * results[ index ] ) +
|
||||
( results[ index + 1 ] * results[ index + 1 ] ) ) /
|
||||
mDynamicRangeReference );
|
||||
|
||||
processed[ x ] = normalizedMagnitude;
|
||||
( results[ index + 1 ] * results[ index + 1 ] ) ) * dftScalor );
|
||||
}
|
||||
|
||||
dispatch( processed );
|
||||
|
||||
Reference in New Issue
Block a user