mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 17:47:32 -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:
@@ -9,7 +9,7 @@
|
|||||||
an alias identifier having the lowest priority.
|
an alias identifier having the lowest priority.
|
||||||
- Audio recording by call with new 'Non-Recordable' alias action
|
- Audio recording by call with new 'Non-Recordable' alias action
|
||||||
- (planned)Added Ettus UHD support for B100 with WBX tuner
|
- (planned)Added Ettus UHD support for B100 with WBX tuner
|
||||||
- (planned)Added Airspy tuner support
|
- Added Airspy tuner support
|
||||||
|
|
||||||
0.1.5 20150612 Added initial P25 Decoder support with integrated audio decoding
|
0.1.5 20150612 Added initial P25 Decoder support with integrated audio decoding
|
||||||
using the JMBE library
|
using the JMBE library
|
||||||
|
|||||||
@@ -146,8 +146,7 @@ public class HilbertTransform implements Listener<RealBuffer>
|
|||||||
if( mInvertFlag )
|
if( mInvertFlag )
|
||||||
{
|
{
|
||||||
//inphase
|
//inphase
|
||||||
samples[ y ] = -( mCenterCoefficent *
|
samples[ y ] = -( mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ] );
|
||||||
mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ] );
|
|
||||||
|
|
||||||
//quadrature
|
//quadrature
|
||||||
samples[ y + 1 ] = -accumulator;
|
samples[ y + 1 ] = -accumulator;
|
||||||
@@ -155,8 +154,7 @@ public class HilbertTransform implements Listener<RealBuffer>
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//inphase
|
//inphase
|
||||||
samples[ y ] = mCenterCoefficent *
|
samples[ y ] = mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ];
|
||||||
mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ];
|
|
||||||
|
|
||||||
//quadrature
|
//quadrature
|
||||||
samples[ y + 1 ] = accumulator;
|
samples[ y + 1 ] = accumulator;
|
||||||
@@ -255,6 +253,9 @@ public class HilbertTransform implements Listener<RealBuffer>
|
|||||||
* a folded FIR structure to exploit the symmetric nature of the half-band
|
* a folded FIR structure to exploit the symmetric nature of the half-band
|
||||||
* filter coefficients. The polarity difference between the upper and lower
|
* filter coefficients. The polarity difference between the upper and lower
|
||||||
* halves is accounted for in the filter() method.
|
* halves is accounted for in the filter() method.
|
||||||
|
*
|
||||||
|
* Note: we apply a 2.0 gain to the coefficients to compensate for the loss
|
||||||
|
* of splitting the signal via two filters.
|
||||||
*/
|
*/
|
||||||
private void convertHalfBandToHilbert( float[] coefficients )
|
private void convertHalfBandToHilbert( float[] coefficients )
|
||||||
{
|
{
|
||||||
@@ -266,15 +267,15 @@ public class HilbertTransform implements Listener<RealBuffer>
|
|||||||
{
|
{
|
||||||
if( x < middle )
|
if( x < middle )
|
||||||
{
|
{
|
||||||
mHilbertFilter[ x ] = -Math.abs( coefficients[ x ] );
|
mHilbertFilter[ x ] = 2.0f * -Math.abs( coefficients[ x ] );
|
||||||
}
|
}
|
||||||
else if( x > middle )
|
else if( x > middle )
|
||||||
{
|
{
|
||||||
mHilbertFilter[ x ] = Math.abs( coefficients[ x ] );
|
mHilbertFilter[ x ] = 2.0f * Math.abs( coefficients[ x ] );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mHilbertFilter[ x ] = coefficients[ x ];
|
mHilbertFilter[ x ] = 2.0f * coefficients[ x ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,21 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.mixer;
|
package dsp.mixer;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.jtransforms.fft.FloatFFT_1D;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import dsp.filter.Filters;
|
||||||
|
import dsp.filter.hilbert.HilbertTransform;
|
||||||
import sample.complex.Complex;
|
import sample.complex.Complex;
|
||||||
|
|
||||||
public class Oscillator
|
public class Oscillator
|
||||||
{
|
{
|
||||||
|
private final static Logger mLog =
|
||||||
|
LoggerFactory.getLogger( Oscillator.class );
|
||||||
|
|
||||||
private double mFrequency;
|
private double mFrequency;
|
||||||
private double mSampleRate;
|
private double mSampleRate;
|
||||||
|
|
||||||
@@ -105,4 +116,109 @@ public class Oscillator
|
|||||||
{
|
{
|
||||||
return mCurrentAngle.real();
|
return mCurrentAngle.real();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
Oscillator o = new Oscillator( 1536, 4096 );
|
||||||
|
|
||||||
|
o.rotate();
|
||||||
|
|
||||||
|
float[] samples = new float[ 8192 ];
|
||||||
|
|
||||||
|
for( int x = 0; x < 8192; x++ )
|
||||||
|
{
|
||||||
|
samples[ x ] = o.inphase();
|
||||||
|
|
||||||
|
o.rotate();
|
||||||
|
}
|
||||||
|
|
||||||
|
logLargest( "Real: ", samples, false );
|
||||||
|
|
||||||
|
HilbertTransform ht = new HilbertTransform();
|
||||||
|
|
||||||
|
float[] transformed = ht.filter( samples );
|
||||||
|
|
||||||
|
logLargest( "Hilbert: ", transformed, true );
|
||||||
|
|
||||||
|
FloatFFT_1D fft = new FloatFFT_1D( 4096 );
|
||||||
|
|
||||||
|
fft.complexForward( transformed );
|
||||||
|
|
||||||
|
logLargest( "DFT:", transformed, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logLargest( String label, float[] samples, boolean complex )
|
||||||
|
{
|
||||||
|
float largestI = 0.0f;
|
||||||
|
int indexLargestI = 0;
|
||||||
|
float largestQ = 0.0f;
|
||||||
|
int indexLargestQ = 0;
|
||||||
|
|
||||||
|
float smallestI = 0.0f;
|
||||||
|
int indexsmallestI = 0;
|
||||||
|
float smallestQ = 0.0f;
|
||||||
|
int indexsmallestQ = 0;
|
||||||
|
|
||||||
|
for( int x = 0; x < samples.length; x++ )
|
||||||
|
{
|
||||||
|
if( complex )
|
||||||
|
{
|
||||||
|
if( x % 2 == 0 )
|
||||||
|
{
|
||||||
|
if( samples[ x ] > largestI )
|
||||||
|
{
|
||||||
|
largestI = samples[ x ];
|
||||||
|
indexLargestI = x;
|
||||||
|
}
|
||||||
|
if( samples[ x ] < smallestI )
|
||||||
|
{
|
||||||
|
smallestI = samples[ x ];
|
||||||
|
indexsmallestI = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( samples[ x ] > largestQ )
|
||||||
|
{
|
||||||
|
largestQ = samples[ x ];
|
||||||
|
indexLargestQ = x;
|
||||||
|
}
|
||||||
|
if( samples[ x ] < smallestQ )
|
||||||
|
{
|
||||||
|
smallestQ = samples[ x ];
|
||||||
|
indexsmallestQ = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( samples[ x ] > largestI )
|
||||||
|
{
|
||||||
|
largestI = samples[ x ];
|
||||||
|
indexLargestI = x;
|
||||||
|
}
|
||||||
|
if( samples[ x ] < smallestI )
|
||||||
|
{
|
||||||
|
smallestI = samples[ x ];
|
||||||
|
indexsmallestI = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( complex )
|
||||||
|
{
|
||||||
|
mLog.debug( label +
|
||||||
|
" largestI (" + indexLargestI + "): " + largestI +
|
||||||
|
" smallestI (" + indexsmallestI + "): " + smallestI +
|
||||||
|
" largestQ (" + indexLargestQ + "): " + largestQ +
|
||||||
|
" smallestQ (" + indexsmallestQ + "): " + smallestQ );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLog.debug( label +
|
||||||
|
" largest (" + indexLargestI + "): " + largestI +
|
||||||
|
" smallest (" + indexsmallestI + "): " + smallestI );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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/>
|
||||||
|
******************************************************************************/
|
||||||
package source.tuner.airspy;
|
package source.tuner.airspy;
|
||||||
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
@@ -20,23 +37,6 @@ import source.tuner.airspy.AirspyTunerController.BoardID;
|
|||||||
import controller.ResourceManager;
|
import controller.ResourceManager;
|
||||||
import controller.ThreadPoolManager;
|
import controller.ThreadPoolManager;
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* 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/>
|
|
||||||
******************************************************************************/
|
|
||||||
public class AirspyTuner extends Tuner
|
public class AirspyTuner extends Tuner
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( AirspyTuner.class );
|
private final static Logger mLog = LoggerFactory.getLogger( AirspyTuner.class );
|
||||||
|
|||||||
@@ -389,7 +389,6 @@ public class SpectralDisplayPanel extends JPanel
|
|||||||
//Register the dft processor to receive samples from the tuner
|
//Register the dft processor to receive samples from the tuner
|
||||||
mTuner.addListener( (Listener<ComplexBuffer>)mDFTProcessor );
|
mTuner.addListener( (Listener<ComplexBuffer>)mDFTProcessor );
|
||||||
|
|
||||||
mDFTConverter.setSampleSize( mTuner.getSampleSize() );
|
|
||||||
mSpectrumPanel.setSampleSize( mTuner.getSampleSize() );
|
mSpectrumPanel.setSampleSize( mTuner.getSampleSize() );
|
||||||
|
|
||||||
//Fire frequency and sample rate change events so that everyone
|
//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.RenderingHints;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.GeneralPath;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
@@ -111,8 +112,8 @@ public class SpectrumPanel extends JPanel
|
|||||||
public void setSampleSize( double sampleSize )
|
public void setSampleSize( double sampleSize )
|
||||||
{
|
{
|
||||||
assert( 2.0 <= sampleSize && sampleSize <= 32.0 );
|
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 )
|
public void setAveraging( int size )
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import settings.ColorSetting;
|
import settings.ColorSetting;
|
||||||
import settings.ColorSetting.ColorSettingName;
|
import settings.ColorSetting.ColorSettingName;
|
||||||
import settings.Setting;
|
import settings.Setting;
|
||||||
@@ -48,7 +51,10 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
|||||||
Pausable,
|
Pausable,
|
||||||
SettingChangeListener
|
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 DecimalFormat CURSOR_FORMAT = new DecimalFormat( "0.00000" );
|
||||||
private static final String SPECTRUM_CURSOR = "spectrum_cursor";
|
private static final String SPECTRUM_CURSOR = "spectrum_cursor";
|
||||||
private static final String PAUSED = "PAUSED";
|
private static final String PAUSED = "PAUSED";
|
||||||
@@ -202,9 +208,9 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
|||||||
public void receive( float[] update )
|
public void receive( float[] update )
|
||||||
{
|
{
|
||||||
//If our FFT size changes, reset our pixel map and image source
|
//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();
|
reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,50 @@ package spectrum.converter;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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
|
public class ComplexDecibelConverter extends DFTResultsConverter
|
||||||
{
|
{
|
||||||
private final static Logger mLog =
|
private final static Logger mLog =
|
||||||
LoggerFactory.getLogger( ComplexDecibelConverter.class );
|
LoggerFactory.getLogger( ComplexDecibelConverter.class );
|
||||||
|
|
||||||
private float mDynamicRangeReference = (float)Math.pow( 2.0, 16.0 );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the output of the JTransforms FloatFFT_1D.complexForward()
|
* Converts the output of the JTransforms FloatFFT_1D.complexForward()
|
||||||
* calculation into the power spectrum in decibels, normalized to the
|
* 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
|
@Override
|
||||||
public void receive( float[] results )
|
public void receive( float[] results )
|
||||||
{
|
{
|
||||||
float[] processed = new float[ results.length / 2 ];
|
int halfResults = results.length / 2;
|
||||||
|
|
||||||
int half = processed.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 )
|
for( int x = 0; x < results.length; x += 2 )
|
||||||
{
|
{
|
||||||
//Calculate the magnitude squared value from each bin's real and
|
//Calculate the magnitude squared (power) value from each bin's real
|
||||||
//imaginary value, scale it to the available dynamic range and
|
//and imaginary value and scale it to the DFT bin size squared.
|
||||||
//convert to dB.
|
//Convert the scaled value to decibels.
|
||||||
float normalizedMagnitude = 10.0f * (float)Math.log10(
|
float decibels = 10.0f * (float)Math.log10(
|
||||||
( ( results[ x ] * results[ x ] ) +
|
( ( results[ x ] * results[ x ] ) +
|
||||||
( results[ x + 1 ] * results[ x + 1 ] ) ) /
|
( results[ x + 1 ] * results[ x + 1 ] ) ) * dftScalor );
|
||||||
mDynamicRangeReference );
|
|
||||||
|
|
||||||
// We have to swap the upper and lower halves of the JTransforms
|
// We have to swap the upper and lower halves of the JTransforms
|
||||||
// DFT results for correct display
|
// DFT results for correct display
|
||||||
if( x / 2 >= half )
|
if( x / 2 >= middle )
|
||||||
{
|
{
|
||||||
processed[ x / 2 - half ] = normalizedMagnitude;
|
processed[ x / 2 - middle ] = decibels;
|
||||||
}
|
}
|
||||||
else
|
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()
|
public void dispose()
|
||||||
{
|
{
|
||||||
mListeners.clear();
|
mListeners.clear();
|
||||||
|
|||||||
@@ -7,33 +7,22 @@ public class RealDecibelConverter extends DFTResultsConverter
|
|||||||
{
|
{
|
||||||
private static final Logger mLog = LoggerFactory.getLogger( RealDecibelConverter.class );
|
private static final Logger mLog = LoggerFactory.getLogger( RealDecibelConverter.class );
|
||||||
|
|
||||||
private float mDynamicRangeReference;
|
|
||||||
|
|
||||||
public RealDecibelConverter()
|
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()
|
* Converts the output of the JTransforms FloatFFT_1D.realForward()
|
||||||
* calculation into a normalized power spectrum in decibels, per description
|
* calculation into a normalized power spectrum in decibels, per description
|
||||||
* in Lyons, Understanding Digital Signal Processing, 3e, page 141.
|
* in Lyons, Understanding Digital Signal Processing, 3e, page 141.
|
||||||
|
*
|
||||||
|
* Note: this is only calculating the lower half of the spectrum
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receive( float[] results )
|
public void receive( float[] results )
|
||||||
{
|
{
|
||||||
|
float dftScalor = 1.0f / (float)( results.length ^ 2 );
|
||||||
|
|
||||||
float[] processed = new float[ results.length / 4 ];
|
float[] processed = new float[ results.length / 4 ];
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
@@ -42,12 +31,9 @@ public class RealDecibelConverter extends DFTResultsConverter
|
|||||||
{
|
{
|
||||||
index = x * 2;
|
index = x * 2;
|
||||||
|
|
||||||
float normalizedMagnitude = 10.0f * (float)Math.log10(
|
processed[ x ] = 20.0f * (float)Math.log10(
|
||||||
( ( results[ index ] * results[ index ] ) +
|
( ( results[ index ] * results[ index ] ) +
|
||||||
( results[ index + 1 ] * results[ index + 1 ] ) ) /
|
( results[ index + 1 ] * results[ index + 1 ] ) ) * dftScalor );
|
||||||
mDynamicRangeReference );
|
|
||||||
|
|
||||||
processed[ x ] = normalizedMagnitude;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch( processed );
|
dispatch( processed );
|
||||||
|
|||||||
Reference in New Issue
Block a user