mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
Adds airspy USB tuner support and updates the spectral display
component. -Airspy supported with 10 and 2.5 MSps sample rates. Additional decimation may be added later. -Spectral display updated to include smoothing and filtering options. Note: the decimated output of a tuner channel from the airspy is 48,076.9 samples per second, versus the normal 48 kHz decimated rate. This is 1.0015 times the normal rate and doesn't appear to impact the decoders or audio processing. Therefore, no fractional resampling has been added.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@ import dsp.filter.Window.WindowType;
|
||||
import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
|
||||
import dsp.filter.halfband.complex.HalfBandFilter_CB_CB;
|
||||
|
||||
public class ComplexPrimeCICDecimate
|
||||
public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
|
||||
{
|
||||
public static int[] PRIMES = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
|
||||
59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,
|
||||
@@ -163,6 +163,7 @@ public class ComplexPrimeCICDecimate
|
||||
/**
|
||||
* Primary input method for receiving sample arrays composed as I,Q,I,Q, etc.
|
||||
*/
|
||||
@Override
|
||||
public void receive( ComplexBuffer buffer )
|
||||
{
|
||||
if( mFirstDecimatingStage != null )
|
||||
@@ -343,9 +344,8 @@ public class ComplexPrimeCICDecimate
|
||||
|
||||
|
||||
/**
|
||||
* Output adapter - applies gain correction, applies cleanup filter, casts
|
||||
* double-valued samples into float-valued complex sample and sends the
|
||||
* result to the registered listener.
|
||||
* Output adapter - applies gain correction and cleanup filter and broadcast
|
||||
* to registered listener.
|
||||
*/
|
||||
public class Output implements ComplexSampleListener
|
||||
{
|
||||
|
||||
@@ -17,18 +17,16 @@
|
||||
******************************************************************************/
|
||||
package dsp.filter.dc;
|
||||
|
||||
public abstract class DCRemovalFilter
|
||||
public class DCRemovalFilter
|
||||
{
|
||||
private float mAverage;
|
||||
private float mRatio;
|
||||
protected float mAverage;
|
||||
protected float mRatio;
|
||||
|
||||
public DCRemovalFilter( float ratio )
|
||||
{
|
||||
mRatio = ratio;
|
||||
}
|
||||
|
||||
public abstract void dispose();
|
||||
|
||||
public void reset()
|
||||
{
|
||||
mAverage = 0.0f;
|
||||
@@ -36,8 +34,8 @@ public abstract class DCRemovalFilter
|
||||
|
||||
public float filter( float sample )
|
||||
{
|
||||
mAverage += mRatio * ( sample - mAverage );
|
||||
|
||||
return sample - mAverage;
|
||||
float filtered = sample - mAverage;
|
||||
mAverage += mRatio * filtered;
|
||||
return filtered;
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,18 @@
|
||||
package dsp.filter.dc;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.real.RealBuffer;
|
||||
import sample.complex.ComplexBuffer;
|
||||
|
||||
public class AveragingDCRemovalFilter_RB extends DCRemovalFilter_RB
|
||||
implements Listener<RealBuffer>
|
||||
public class DCRemovalFilter_CB implements Listener<ComplexBuffer>
|
||||
{
|
||||
private float mAverage;
|
||||
private float mRatio;
|
||||
|
||||
public AveragingDCRemovalFilter_RB( float ratio )
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
private DCRemovalFilter mIFilter;
|
||||
private DCRemovalFilter mQFilter;
|
||||
|
||||
public DCRemovalFilter_CB( float ratio )
|
||||
{
|
||||
mRatio = ratio;
|
||||
mIFilter = new DCRemovalFilter( ratio );
|
||||
mQFilter = new DCRemovalFilter( ratio );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
@@ -36,31 +37,35 @@ public class AveragingDCRemovalFilter_RB extends DCRemovalFilter_RB
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
public float[] filter( float[] samples )
|
||||
{
|
||||
mAverage = 0.0f;
|
||||
}
|
||||
for( int x = 0; x < samples.length; x += 2 )
|
||||
{
|
||||
samples[ x ] = mIFilter.filter( samples[ x ] );
|
||||
samples[ x + 1 ] = mQFilter.filter( samples[ x + 1 ] );
|
||||
}
|
||||
|
||||
public float filter( float sample )
|
||||
{
|
||||
mAverage += mRatio * ( sample - mAverage );
|
||||
return samples;
|
||||
}
|
||||
|
||||
return sample - mAverage;
|
||||
public ComplexBuffer filter( ComplexBuffer buffer )
|
||||
{
|
||||
filter( buffer.getSamples() );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void receive( RealBuffer buffer )
|
||||
public void receive( ComplexBuffer buffer )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
|
||||
for( int x = 0; x < samples.length; x++ )
|
||||
{
|
||||
samples[ x ] = filter( samples[ x ] );
|
||||
}
|
||||
|
||||
mListener.receive( buffer );
|
||||
mListener.receive( filter( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,14 @@ package dsp.filter.dc;
|
||||
import sample.Listener;
|
||||
import sample.real.RealBuffer;
|
||||
|
||||
public abstract class DCRemovalFilter_RB implements Listener<RealBuffer>
|
||||
public class DCRemovalFilter_RB extends DCRemovalFilter
|
||||
implements Listener<RealBuffer>
|
||||
{
|
||||
protected Listener<RealBuffer> mListener;
|
||||
|
||||
public DCRemovalFilter_RB()
|
||||
public DCRemovalFilter_RB( float ratio )
|
||||
{
|
||||
|
||||
super( ratio );
|
||||
}
|
||||
|
||||
public void setListener( Listener<RealBuffer> listener )
|
||||
@@ -27,5 +28,34 @@ public abstract class DCRemovalFilter_RB implements Listener<RealBuffer>
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public abstract void reset();
|
||||
public float[] filter( float[] samples )
|
||||
{
|
||||
for( int x = 0; x < samples.length; x++ )
|
||||
{
|
||||
samples[ x ] = filter( samples[ x ] );
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
public RealBuffer filter( RealBuffer buffer )
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
|
||||
for( int x = 0; x < samples.length; x++ )
|
||||
{
|
||||
samples[ x ] = filter( samples[ x ] );
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( RealBuffer buffer )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( filter( buffer ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ public class DCRemovalFilter_R_R extends DCRemovalFilter implements RealSampleLi
|
||||
super( ratio );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package dsp.filter.dc;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.real.RealBuffer;
|
||||
|
||||
public class IIRSinglePoleDCRemovalFilter_RB extends DCRemovalFilter_RB
|
||||
public class IIRSinglePoleDCRemovalFilter_RB implements Listener<RealBuffer>
|
||||
{
|
||||
private float mAlpha;
|
||||
private float mPreviousInput = 0.0f;
|
||||
private float mPreviousOutput = 0.0f;
|
||||
private Listener<RealBuffer> mListener;
|
||||
|
||||
/**
|
||||
* IIR single-pole DC removal filter, as described by J M de Freitas in
|
||||
@@ -24,6 +26,14 @@ public class IIRSinglePoleDCRemovalFilter_RB extends DCRemovalFilter_RB
|
||||
|
||||
@Override
|
||||
public void receive( RealBuffer buffer )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( filter( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
public RealBuffer filter( RealBuffer buffer )
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
|
||||
@@ -31,11 +41,8 @@ public class IIRSinglePoleDCRemovalFilter_RB extends DCRemovalFilter_RB
|
||||
{
|
||||
samples[ x ] = filter( samples[ x ] );
|
||||
}
|
||||
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( buffer );
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public float filter( float sample )
|
||||
@@ -49,10 +56,14 @@ public class IIRSinglePoleDCRemovalFilter_RB extends DCRemovalFilter_RB
|
||||
return currentOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
mPreviousInput = 0.0f;
|
||||
mPreviousOutput = 0.0f;
|
||||
}
|
||||
|
||||
public void setListener( Listener<RealBuffer> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,21 @@ public class HalfBandFilter_RB_RB extends HalfBandFilter implements Listener<Rea
|
||||
private Listener<RealBuffer> mListener;
|
||||
|
||||
/**
|
||||
* Halfband filter for real buffers with optional decimation by two.
|
||||
*
|
||||
* @param coefficients
|
||||
* @param gain
|
||||
* @param decimate
|
||||
* If this filter is used in non-decimating mode, the filter will overwrite
|
||||
* the inbound sample buffer with the filtered samples. This will cause
|
||||
* issues if the buffers are being used across several processes are sharing
|
||||
* the buffer, since this filter will modify the contents of the object, so
|
||||
* consider making a copy of the buffer prior to feeding the buffer to this
|
||||
* filter.
|
||||
*
|
||||
* If used in decimating mode, a new (half-length) buffer will be created
|
||||
* for the filtered samples.
|
||||
*
|
||||
* @param coefficients - filter kernel
|
||||
* @param gain - gain to apply to the outputs
|
||||
* @param decimate - true for decimate by 2, or false for no decimation
|
||||
*/
|
||||
public HalfBandFilter_RB_RB( float[] coefficients, float gain, boolean decimate )
|
||||
{
|
||||
@@ -28,12 +39,18 @@ public class HalfBandFilter_RB_RB extends HalfBandFilter implements Listener<Rea
|
||||
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void receive( RealBuffer buffer )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( filter( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
public RealBuffer filter( RealBuffer buffer )
|
||||
{
|
||||
if( mDecimate )
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
@@ -72,7 +89,7 @@ public class HalfBandFilter_RB_RB extends HalfBandFilter implements Listener<Rea
|
||||
mDecimateFlag = !mDecimateFlag;
|
||||
}
|
||||
|
||||
mListener.receive( new RealBuffer( decimated ) );
|
||||
return new RealBuffer( decimated );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -84,9 +101,8 @@ public class HalfBandFilter_RB_RB extends HalfBandFilter implements Listener<Rea
|
||||
samples[ x ] = filter( samples[ x ] );
|
||||
}
|
||||
|
||||
mListener.receive( buffer );
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<RealBuffer> listener )
|
||||
|
||||
281
src/dsp/filter/hilbert/HilbertTransform.java
Normal file
281
src/dsp/filter/hilbert/HilbertTransform.java
Normal file
@@ -0,0 +1,281 @@
|
||||
package dsp.filter.hilbert;
|
||||
|
||||
/*******************************************************************************
|
||||
* 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/>
|
||||
******************************************************************************/
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.real.RealBuffer;
|
||||
import dsp.filter.Filters;
|
||||
|
||||
public class HilbertTransform implements Listener<RealBuffer>
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger( HilbertTransform.class );
|
||||
|
||||
private float[] mHilbertFilter;
|
||||
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
|
||||
private float[] mBuffer;
|
||||
private int mBufferSize;
|
||||
private int mBufferPointer;
|
||||
|
||||
private int[][] mIndexMap;
|
||||
private int mMapHeight;
|
||||
private int mCenterTapIndex;
|
||||
private float mCenterCoefficent;
|
||||
|
||||
private boolean mInvertFlag = false;
|
||||
|
||||
/**
|
||||
* Hilbert transform filter used for converting real-valued samples into
|
||||
* complex valued samples using frequency translation (FS/4) and a half-band
|
||||
* filter.
|
||||
*
|
||||
* This filter uses a circular sample buffer and a pre-calculated
|
||||
* index map to correctly map the filter coefficients to the circular delay
|
||||
* buffer contents as each new sample is added to the buffer and processed.
|
||||
*
|
||||
* Half-band filter coefficients used in this filter must be of length N
|
||||
* where (N + 1) is a multiple of 4. This filter uses a pre-defined half
|
||||
* band filter with 47 coefficients.
|
||||
*
|
||||
* This transform process is described in Understanding Digital Signal
|
||||
* Processing, Lyons, 3e, 2011, sections 13.1.2 and 13.1.3 (p 674-678) and
|
||||
* implemented as described in Section 13.37.1 and 13.37.2 (p 802-804)
|
||||
*/
|
||||
public HilbertTransform()
|
||||
{
|
||||
convertHalfBandToHilbert( Filters.HALF_BAND_FILTER_47T.getCoefficients() );
|
||||
|
||||
mBufferSize = mHilbertFilter.length + 1;
|
||||
mBuffer = new float[ mBufferSize ];
|
||||
|
||||
mCenterCoefficent = mHilbertFilter[ mHilbertFilter.length / 2 ];
|
||||
|
||||
generateIndexMap( mHilbertFilter.length );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( RealBuffer buffer )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( filter( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to receive filtered complex buffers.
|
||||
*/
|
||||
public void setListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies Hilbert transform to the real sample buffer and returns a
|
||||
* complex sample buffer
|
||||
*/
|
||||
public ComplexBuffer filter( RealBuffer buffer )
|
||||
{
|
||||
return new ComplexBuffer( filter( buffer.getSamples() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the continuous real-sample stream by treating the stream as a
|
||||
* set of interleaved values and applying two hilbert filters against each
|
||||
* interleaved set (I & Q). Frequency translation has already been
|
||||
* pre-applied to the hilbert filter coefficients.
|
||||
*
|
||||
* Filtering of the inphase component is a simple multiply by the center
|
||||
* coefficient (0.5) since all of the other coefficients are zero-valued.
|
||||
*
|
||||
* Filtering of the quadrature component uses a folded FIR structure and
|
||||
* expects the hilbert coefficients to be symmetric with all negative valued
|
||||
* coefficients below the center coefficient and all positive-valued coefficients
|
||||
* above the center coefficient. So, convolution only applies the filter
|
||||
* coefficient against the difference of the two samples.
|
||||
*
|
||||
* ( coefficient * value1 ) + (-coefficient * value2 ) = coefficient * ( value1 - value2 )
|
||||
*
|
||||
* Performs frequency translation by FS/2 on the final filtered values by
|
||||
* applying a sequence of 1,-1 (sign change) to each I/Q output sample.
|
||||
*/
|
||||
public float[] filter( float[] samples )
|
||||
{
|
||||
float accumulator;
|
||||
|
||||
for( int y = 0; y < samples.length; y +=2 )
|
||||
{
|
||||
insert( samples[ y ] );
|
||||
insert( samples[ y + 1 ] );
|
||||
|
||||
accumulator = 0.0f;
|
||||
|
||||
int index = mBufferPointer / 2;
|
||||
|
||||
for( int x = 0; x < mHilbertFilter.length / 2; x += 2 )
|
||||
{
|
||||
accumulator += mHilbertFilter[ x ] *
|
||||
( mBuffer[ mIndexMap[ index ][ x + 1] ] -
|
||||
mBuffer[ mIndexMap[ index ][ x ] ] );
|
||||
}
|
||||
|
||||
//Perform FS/2 frequency translation on the final filtered values
|
||||
if( mInvertFlag )
|
||||
{
|
||||
//inphase
|
||||
samples[ y ] = -( mCenterCoefficent *
|
||||
mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ] );
|
||||
|
||||
//quadrature
|
||||
samples[ y + 1 ] = -accumulator;
|
||||
}
|
||||
else
|
||||
{
|
||||
//inphase
|
||||
samples[ y ] = mCenterCoefficent *
|
||||
mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ];
|
||||
|
||||
//quadrature
|
||||
samples[ y + 1 ] = accumulator;
|
||||
}
|
||||
|
||||
mInvertFlag = !mInvertFlag;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the sample into the circular buffer, overwriting the oldest value
|
||||
*/
|
||||
private void insert( float sample )
|
||||
{
|
||||
mBuffer[ mBufferPointer++ ] = sample;
|
||||
|
||||
mBufferPointer = mBufferPointer % mBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index map to support efficient lookup of sample indexes from
|
||||
* the circular buffer.
|
||||
*
|
||||
* The first index of the map corresponds to the current buffer pointer
|
||||
* setting.
|
||||
*
|
||||
* The second index is structured in sample pairs to align with
|
||||
* the hilbert filter coefficients. For example, the first iteration of
|
||||
* the filter would use filter coefficient 0 and the index map values 0 and
|
||||
* 1 would be pointing to samples 46 and 0.
|
||||
*
|
||||
* The final element of each index set is the index of the center sample.
|
||||
*/
|
||||
private void generateIndexMap( int size )
|
||||
{
|
||||
mMapHeight = size / 2 + 1;
|
||||
int mapWidth = mMapHeight + 1;
|
||||
|
||||
mIndexMap = new int[ mMapHeight ][ mapWidth ];
|
||||
|
||||
//Setup the first row with buffer pointer index 0 as a starting point
|
||||
for( int x = 0; x < mapWidth - 1; x += 2 )
|
||||
{
|
||||
mIndexMap[ 0 ][ x ] = size - 1 - x;
|
||||
mIndexMap[ 0 ][ x + 1 ] = x;
|
||||
}
|
||||
|
||||
//Place the center index at the end of the array
|
||||
mCenterTapIndex = mapWidth - 1;
|
||||
|
||||
mIndexMap[ 0 ][ mCenterTapIndex ] = size / 2;
|
||||
|
||||
//For each subsequent row, increment the previous row's value by 2,
|
||||
//wrapping as needed, to keep the values between 0 and size - 1
|
||||
for( int x = 1; x < mMapHeight; x++ )
|
||||
{
|
||||
for( int y = 0; y < mapWidth; y++ )
|
||||
{
|
||||
mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 2;
|
||||
|
||||
if( mIndexMap[ x ][ y ] >= size )
|
||||
{
|
||||
mIndexMap[ x ][ y ] -= size + 1;
|
||||
|
||||
//Special handling for center tap wrap around
|
||||
if( y == mCenterTapIndex && mIndexMap[ x ][ y ] < 0 )
|
||||
{
|
||||
mIndexMap[ x ][ y ] = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the index map contents
|
||||
*/
|
||||
public void logIndexMap()
|
||||
{
|
||||
for( int[] indexSet: mIndexMap )
|
||||
{
|
||||
mLog.debug( "Row:" + Arrays.toString( indexSet ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the half-band filter coefficients for use as hilbert transform
|
||||
* filter coefficients. Sets all even-numbered coefficients left of center
|
||||
* to negative and all even-numbered coefficients right of center to positive
|
||||
* which applies a FS/4 frequency translation to the coefficients.
|
||||
*
|
||||
* Note: even though the coefficients above the center coefficient are being
|
||||
* defined, they are unused during the convolution process since we're using
|
||||
* a folded FIR structure to exploit the symmetric nature of the half-band
|
||||
* filter coefficients. The polarity difference between the upper and lower
|
||||
* halves is accounted for in the filter() method.
|
||||
*/
|
||||
private void convertHalfBandToHilbert( float[] coefficients )
|
||||
{
|
||||
mHilbertFilter = new float[ coefficients.length ];
|
||||
|
||||
int middle = coefficients.length / 2;
|
||||
|
||||
for( int x = 0; x < coefficients.length; x++ )
|
||||
{
|
||||
if( x < middle )
|
||||
{
|
||||
mHilbertFilter[ x ] = -Math.abs( coefficients[ x ] );
|
||||
}
|
||||
else if( x > middle )
|
||||
{
|
||||
mHilbertFilter[ x ] = Math.abs( coefficients[ x ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
mHilbertFilter[ x ] = coefficients[ x ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
281
src/dsp/filter/hilbert/InterpolatingHilbertTransform.java
Normal file
281
src/dsp/filter/hilbert/InterpolatingHilbertTransform.java
Normal file
@@ -0,0 +1,281 @@
|
||||
package dsp.filter.hilbert;
|
||||
|
||||
/*******************************************************************************
|
||||
* 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/>
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* Half-band filter coefficients retrieved October 2015 from:
|
||||
* https://github.com/airspy/host/libairspy/src/filters.h
|
||||
*
|
||||
* Copyright (C) 2014, Youssef Touil <youssef@airspy.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.real.RealBuffer;
|
||||
|
||||
public class InterpolatingHilbertTransform implements Listener<RealBuffer>
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger( InterpolatingHilbertTransform.class );
|
||||
|
||||
private static final float[] HALF_BAND_FILTER =
|
||||
{
|
||||
-0.000998606272947510f, 0.0f, 0.001695637278417295f, 0.0f,
|
||||
-0.003054430179754289f, 0.0f, 0.005055504379767936f, 0.0f,
|
||||
-0.007901319195893647f, 0.0f, 0.011873357051047719f, 0.0f,
|
||||
-0.017411159379930066f, 0.0f, 0.025304817427568772f, 0.0f,
|
||||
-0.037225225204559217f, 0.0f, 0.057533286997004301f, 0.0f,
|
||||
-0.102327462004259350f, 0.0f, 0.317034472508947400f, 0.5f,
|
||||
0.317034472508947400f, 0.0f, -0.102327462004259350f, 0.0f,
|
||||
0.057533286997004301f, 0.0f, -0.037225225204559217f, 0.0f,
|
||||
0.025304817427568772f, 0.0f, -0.017411159379930066f, 0.0f,
|
||||
0.011873357051047719f, 0.0f, -0.007901319195893647f, 0.0f,
|
||||
0.005055504379767936f, 0.0f, -0.003054430179754289f, 0.0f,
|
||||
0.001695637278417295f, 0.0f, -0.000998606272947510f
|
||||
};
|
||||
|
||||
private static final float GAIN = 2.0f;
|
||||
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
|
||||
private float[] mBuffer;
|
||||
private int mBufferSize;
|
||||
private int mBufferPointer;
|
||||
private int mGroupDelayedInphaseTapPointIndex;
|
||||
|
||||
private float[] mCoefficients;
|
||||
private int[][] mIndexMap;
|
||||
|
||||
|
||||
/**
|
||||
* Hilbert transform filter used for converting real-valued samples into
|
||||
* complex valued samples using frequency translation (FS/4) and a half-band
|
||||
* filter.
|
||||
*
|
||||
* This filter uses a circular sample buffer and a pre-calculated
|
||||
* index map to correctly map the filter coefficients to the circular delay
|
||||
* buffer contents as each new sample is added to the buffer and processed.
|
||||
*
|
||||
* Half-band filter coefficients used in this filter must be of length N
|
||||
* where (N + 1) is a multiple of 4. This filter uses a pre-defined half
|
||||
* band filter with 47 coefficients.
|
||||
*
|
||||
* This transform process is described in Understanding Digital Signal
|
||||
* Processing, Lyons, 3e, 2011, sections 13.1.2 and 13.1.3 (p 674-678) and
|
||||
* implemented as described in Section 13.37.1 and 13.37.2 (p 802-804)
|
||||
*/
|
||||
public InterpolatingHilbertTransform()
|
||||
{
|
||||
mBufferSize = HALF_BAND_FILTER.length;
|
||||
mBuffer = new float[ mBufferSize ];
|
||||
|
||||
createHilbertCoefficients();
|
||||
|
||||
generateIndexMap( mBufferSize );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( RealBuffer buffer )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( filter( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to receive filtered complex buffers.
|
||||
*/
|
||||
public void setListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies Hilbert transform to the real sample buffer and returns a
|
||||
* complex sample buffer
|
||||
*/
|
||||
public ComplexBuffer filter( RealBuffer buffer )
|
||||
{
|
||||
return new ComplexBuffer( filter( buffer.getSamples() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a hilbert transform to an array of real samples and returns an
|
||||
* array twice as long containing I,Q,I,Q ... complex samples
|
||||
*/
|
||||
public float[] filter( float[] samples )
|
||||
{
|
||||
float[] filtered = new float[ samples.length * 2 ];
|
||||
|
||||
float accumulator;
|
||||
|
||||
for( int y = 0; y < samples.length; y++ )
|
||||
{
|
||||
insert( samples[ y ] );
|
||||
|
||||
accumulator = 0.0f;
|
||||
|
||||
for( int x = 0; x < mCoefficients.length; x++ )
|
||||
{
|
||||
accumulator += mCoefficients[ x ] *
|
||||
mBuffer[ mIndexMap[ mBufferPointer ][ x ] ] ;
|
||||
}
|
||||
|
||||
int index = y * 2;
|
||||
|
||||
/* We use the buffered sample at the half group delay point as the
|
||||
* Inphase value and the accumulator as the quadrature value */
|
||||
filtered[ index ] = mBuffer[ mIndexMap[ mBufferPointer ]
|
||||
[ mGroupDelayedInphaseTapPointIndex ] ];
|
||||
|
||||
filtered[ index + 1 ] = accumulator;
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the sample into the circular buffer, overwriting the oldest value
|
||||
*/
|
||||
private void insert( float sample )
|
||||
{
|
||||
mBuffer[ mBufferPointer ] = sample;
|
||||
|
||||
mBufferPointer++;
|
||||
|
||||
mBufferPointer = mBufferPointer % mBufferSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an N x (N + 1 / 2) index map enabling quick access to the
|
||||
* circular buffer samples to support convolution.
|
||||
*
|
||||
* As the buffer pointer shifts right, as each sample is added, we use the
|
||||
* index map pointed to by the buffer pointer, so that the filter coefficients
|
||||
* are pointing to the correct circular buffer samples.
|
||||
*
|
||||
* We add an extra index to each index map with a value that points to the
|
||||
* current index of the middle sample in the buffer, so that we can retrieve
|
||||
* that value as the half-group delayed sample that forms the inphase
|
||||
* component.
|
||||
*/
|
||||
private void generateIndexMap( int size )
|
||||
{
|
||||
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
||||
|
||||
int center = ( size - 1 ) / 2;
|
||||
|
||||
//Set center tap index for coefficients array
|
||||
mGroupDelayedInphaseTapPointIndex = center + 1;
|
||||
|
||||
mIndexMap = new int[ size ][ mapWidth ];
|
||||
|
||||
//Setup the first row with buffer pointer index 0 as a starting point
|
||||
for( int x = 0; x < mapWidth - 1; x++ )
|
||||
{
|
||||
mIndexMap[ 0 ][ x ] = size - 1 - ( x * 2 );
|
||||
}
|
||||
|
||||
//Place the group delayed in-phase sample tap point in last element of
|
||||
//the first row
|
||||
mIndexMap[ 0 ][ mGroupDelayedInphaseTapPointIndex ] = center;
|
||||
|
||||
//For each subsequent row, increment the previous row's value by 1,
|
||||
//subtracting size as needed, to keep the values between 0 and size - 1
|
||||
for( int x = 1; x < size; x++ )
|
||||
{
|
||||
for( int y = 0; y < mapWidth; y++ )
|
||||
{
|
||||
mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1;
|
||||
|
||||
if( mIndexMap[ x ][ y ] >= size )
|
||||
{
|
||||
mIndexMap[ x ][ y ] -= size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void logIndexMap()
|
||||
{
|
||||
for( int[] indexSet: mIndexMap )
|
||||
{
|
||||
mLog.debug( "Row:" + Arrays.toString( indexSet ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the half-band filter coefficients into a packed set of hilbert
|
||||
* coefficients by removing all of the odd index, zero-valued coefficients
|
||||
* and the center 0.5 coefficient. Then, all coefficients left of the
|
||||
* original center are made negative and all coefficients right of the
|
||||
* original center are made positive. These coefficient sign modifications
|
||||
* effectively incorporate a frequency translation by FS/4 into the hilbert
|
||||
* coefficients. A gain of 2.0 is applied to the coefficients to offset the
|
||||
* gain of 0.5 induced by the filter.
|
||||
*/
|
||||
private void createHilbertCoefficients()
|
||||
{
|
||||
int half = ( HALF_BAND_FILTER.length + 1 ) / 2;
|
||||
int middle = half / 2;
|
||||
|
||||
mCoefficients = new float[ half ];
|
||||
|
||||
/* Pack all of the even numbered HB coefficients into our coefficient
|
||||
* array and pre-apply a gain of two to offset the loss in the filter */
|
||||
for( int x = 0; x < half; x++ )
|
||||
{
|
||||
mCoefficients[ x ] = HALF_BAND_FILTER[ x * 2 ] * GAIN;
|
||||
|
||||
/* Make all of the left of center coefficients negative */
|
||||
if( x < middle && mCoefficients[ x ] > 0.0f )
|
||||
{
|
||||
mCoefficients[ x ] = -mCoefficients[ x ];
|
||||
}
|
||||
/* Make all of the right of center coefficients positive */
|
||||
else if( x >= middle && mCoefficients[ x ] < 0.0f )
|
||||
{
|
||||
mCoefficients[ x ] = -mCoefficients[ x ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/dsp/filter/smoothing/GaussianSmoothingFilter.java
Normal file
39
src/dsp/filter/smoothing/GaussianSmoothingFilter.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package dsp.filter.smoothing;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class GaussianSmoothingFilter extends SmoothingFilter
|
||||
{
|
||||
private static Map<Integer,float[]> mMap = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
mMap.put( 3, new float[] { 0.1842153217f,0.6315693566f,0.1842153217f } );
|
||||
mMap.put( 5, new float[] { 0.0646709835f,0.2446878423f,0.3812823483f,0.2446878423f,0.0646709835f } );
|
||||
mMap.put( 7, new float[] { 0.0355966134f,0.1103628062f,0.2176061504f,0.27286886f,0.2176061504f,0.1103628062f,0.0355966134f } );
|
||||
mMap.put( 9, new float[] { 0.0237625774f,0.061955345f,0.1228439993f,0.185233293f,0.2124095706f,0.185233293f,0.1228439993f,0.061955345f,0.0237625774f } );
|
||||
mMap.put( 11, new float[] { 0.0175873706f,0.040124091f,0.0762094006f,0.120506627f,0.1586398759f,0.1738652697f,0.1586398759f,0.120506627f,0.0762094006f,0.040124091f,0.0175873706f } );
|
||||
mMap.put( 13, new float[] { 0.0138646514f,0.0285342951f,0.0515030495f,0.0815279159f,0.1131845993f,0.137808478f,0.1471540217f,0.137808478f,0.1131845993f,0.0815279159f,0.0515030495f,0.0285342951f,0.0138646514f } );
|
||||
mMap.put( 15, new float[] { 0.011400054f,0.0216349783f,0.0372047506f,0.0579739252f,0.0818576722f,0.1047317717f,0.1214199044f,0.1275538873f,0.1214199044f,0.1047317717f,0.0818576722f,0.0579739252f,0.0372047506f,0.0216349783f,0.011400054f } );
|
||||
mMap.put( 17, new float[] { 0.0096581767f,0.0171732764f,0.0282802625f,0.0431306476f,0.0609201251f,0.07969074f,0.0965444243f,0.1083225312f,0.1125596326f,0.1083225312f,0.0965444243f,0.07969074f,0.0609201251f,0.0431306476f,0.0282802625f,0.0171732764f,0.0096581767f } );
|
||||
mMap.put( 19, new float[] { 0.0083665107f,0.0141035977f,0.0223580953f,0.0333318211f,0.0467307368f,0.0616120364f,0.0763919939f,0.0890736966f,0.097672084f,0.1007188551f,0.097672084f,0.0890736966f,0.0763919939f,0.0616120364f,0.0467307368f,0.0333318211f,0.0223580953f,0.0141035977f,0.0083665107f } );
|
||||
mMap.put( 21, new float[] { 0.0073728906f,0.0118884085f,0.0182292596f,0.0265811467f,0.0368585098f,0.0486027908f,0.0609458331f,0.0726751947f,0.0824114898f,0.0888686744f,0.091131604f,0.0888686744f,0.0824114898f,0.0726751947f,0.0609458331f,0.0486027908f,0.0368585098f,0.0265811467f,0.0182292596f,0.0118884085f,0.0073728906f } );
|
||||
mMap.put( 23, new float[] { 0.0065861356f,0.010228416f,0.0152327635f,0.0217541246f,0.0297918261f,0.0391241787f,0.0492703835f,0.0595003129f,0.0689041144f,0.0765180086f,0.0814844543f,0.0832105633f,0.0814844543f,0.0765180086f,0.0689041144f,0.0595003129f,0.0492703835f,0.0391241787f,0.0297918261f,0.0217541246f,0.0152327635f,0.010228416f,0.0065861356f } );
|
||||
mMap.put( 25, new float[] { 0.0059484873f,0.0089460248f,0.0129850288f,0.0181905149f,0.0245944051f,0.0320934871f,0.0404191102f,0.0491298959f,0.0576360481f,0.0652577058f,0.0713113508f,0.07520986f,0.076556162f,0.07520986f,0.0713113508f,0.0652577058f,0.0576360481f,0.0491298959f,0.0404191102f,0.0320934871f,0.0245944051f,0.0181905149f,0.0129850288f,0.0089460248f,0.0059484873f } );
|
||||
mMap.put( 27, new float[] { 0.0054216802f,0.007930248f,0.0112519401f,0.0154865908f,0.0206762609f,0.0267778655f,0.0336409056f,0.0409965368f,0.0484634619f,0.0555737199f,0.0618176187f,0.0667026128f,0.0698170011f,0.0708871156f,0.0698170011f,0.0667026128f,0.0618176187f,0.0555737199f,0.0484634619f,0.0409965368f,0.0336409056f,0.0267778655f,0.0206762609f,0.0154865908f,0.0112519401f,0.007930248f,0.0054216802f } );
|
||||
mMap.put( 29, new float[] { 0.0049794093f,0.0071086836f,0.0098843442f,0.013386093f,0.0176566017f,0.022683377f,0.0283828243f,0.0345900198f,0.0410575774f,0.0474660547f,0.0534466215f,0.0586144488f,0.0626089499f,0.0651351564f,0.0659996766f,0.0651351564f,0.0626089499f,0.0586144488f,0.0534466215f,0.0474660547f,0.0410575774f,0.0345900198f,0.0283828243f,0.022683377f,0.0176566017f,0.013386093f,0.0098843442f,0.0071086836f,0.0049794093f } );
|
||||
mMap.put( 31, new float[] { 0.0046030224f,0.0064323659f,0.0087836656f,0.0117208269f,0.0152833344f,0.0194740097f,0.0242476721f,0.0295027244f,0.0350777394f,0.0407547727f,0.0462703471f,0.0513339215f,0.0556523529f,0.0589576307f,0.0610342929f,0.0617426429f,0.0610342929f,0.0589576307f,0.0556523529f,0.0513339215f,0.0462703471f,0.0407547727f,0.0350777394f,0.0295027244f,0.0242476721f,0.0194740097f,0.0152833344f,0.0117208269f,0.0087836656f,0.0064323659f,0.0046030224f } );
|
||||
}
|
||||
|
||||
public GaussianSmoothingFilter()
|
||||
{
|
||||
super( mMap, 3 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmoothingType getSmoothingType()
|
||||
{
|
||||
return SmoothingType.GAUSSIAN;
|
||||
}
|
||||
}
|
||||
31
src/dsp/filter/smoothing/NoSmoothingFilter.java
Normal file
31
src/dsp/filter/smoothing/NoSmoothingFilter.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package dsp.filter.smoothing;
|
||||
|
||||
|
||||
public class NoSmoothingFilter extends SmoothingFilter
|
||||
{
|
||||
public NoSmoothingFilter()
|
||||
{
|
||||
super( null, 3 );
|
||||
}
|
||||
|
||||
public float[] filter( float[] data )
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
public static float[] getCoefficients( int points )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getPointSize()
|
||||
{
|
||||
return SMOOTHING_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmoothingType getSmoothingType()
|
||||
{
|
||||
return SmoothingType.NONE;
|
||||
}
|
||||
}
|
||||
49
src/dsp/filter/smoothing/RectangularSmoothingFilter.java
Normal file
49
src/dsp/filter/smoothing/RectangularSmoothingFilter.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package dsp.filter.smoothing;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RectangularSmoothingFilter extends SmoothingFilter
|
||||
{
|
||||
private static Map<Integer,float[]> mMap = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
mMap.put( 3, getCoefficients( 3 ) );
|
||||
mMap.put( 5, getCoefficients( 5 ) );
|
||||
mMap.put( 7, getCoefficients( 7 ) );
|
||||
mMap.put( 9, getCoefficients( 9 ) );
|
||||
mMap.put( 11, getCoefficients( 11 ) );
|
||||
mMap.put( 13, getCoefficients( 13 ) );
|
||||
mMap.put( 15, getCoefficients( 15 ) );
|
||||
mMap.put( 17, getCoefficients( 17 ) );
|
||||
mMap.put( 19, getCoefficients( 19 ) );
|
||||
mMap.put( 21, getCoefficients( 21 ) );
|
||||
mMap.put( 23, getCoefficients( 23 ) );
|
||||
mMap.put( 25, getCoefficients( 25 ) );
|
||||
mMap.put( 27, getCoefficients( 27 ) );
|
||||
mMap.put( 29, getCoefficients( 29 ) );
|
||||
mMap.put( 31, getCoefficients( 31 ) );
|
||||
}
|
||||
|
||||
public RectangularSmoothingFilter()
|
||||
{
|
||||
super( mMap, 3 );
|
||||
}
|
||||
|
||||
public static float[] getCoefficients( int points )
|
||||
{
|
||||
float[] coefficients = new float[ points ];
|
||||
|
||||
Arrays.fill( coefficients, 1.0f / (float)points );
|
||||
|
||||
return coefficients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmoothingType getSmoothingType()
|
||||
{
|
||||
return SmoothingType.RECTANGLE;
|
||||
}
|
||||
}
|
||||
97
src/dsp/filter/smoothing/SmoothingFilter.java
Normal file
97
src/dsp/filter/smoothing/SmoothingFilter.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package dsp.filter.smoothing;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class SmoothingFilter
|
||||
{
|
||||
public static final int SMOOTHING_MINIMUM = 3;
|
||||
public static final int SMOOTHING_MAXIMUM = 29;
|
||||
public static final int SMOOTHING_DEFAULT = 9;
|
||||
|
||||
public enum SmoothingType
|
||||
{
|
||||
NONE,
|
||||
RECTANGLE,
|
||||
TRIANGLE,
|
||||
GAUSSIAN;
|
||||
}
|
||||
|
||||
private Map<Integer,float[]> mCoefficientMap;
|
||||
private float[] mCoefficients;
|
||||
private float[] mNewCoefficients;
|
||||
private boolean mNewCoefficientsAvailable;
|
||||
|
||||
public SmoothingFilter( Map<Integer,float[]> coefficients, int index )
|
||||
{
|
||||
mCoefficientMap = coefficients;
|
||||
|
||||
if( mCoefficientMap != null )
|
||||
{
|
||||
mCoefficients = mCoefficientMap.get( index );
|
||||
}
|
||||
}
|
||||
|
||||
public abstract SmoothingType getSmoothingType();
|
||||
|
||||
public Set<Integer> getPointSizeList()
|
||||
{
|
||||
return mCoefficientMap.keySet();
|
||||
}
|
||||
|
||||
public int getPointSize()
|
||||
{
|
||||
return mCoefficients.length;
|
||||
}
|
||||
|
||||
public void setPointSize( int pointSize )
|
||||
{
|
||||
if( mCoefficientMap != null )
|
||||
{
|
||||
if( mCoefficientMap.containsKey( pointSize ) )
|
||||
{
|
||||
mNewCoefficients = mCoefficientMap.get( pointSize );
|
||||
mNewCoefficientsAvailable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException( "Point size [" + pointSize +
|
||||
"] is not valid in the current coefficient map" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float[] filter( float[] data )
|
||||
{
|
||||
if( mNewCoefficientsAvailable )
|
||||
{
|
||||
mCoefficients = mNewCoefficients;
|
||||
mNewCoefficientsAvailable = false;
|
||||
}
|
||||
|
||||
int middle = mCoefficients.length / 2;
|
||||
|
||||
float[] filtered = new float[ data.length ];
|
||||
|
||||
int toCopy = middle;
|
||||
|
||||
System.arraycopy( data, 0, filtered, 0, toCopy );
|
||||
System.arraycopy( data, data.length - toCopy, filtered, filtered.length - toCopy, toCopy );
|
||||
|
||||
float accumulator;
|
||||
|
||||
for( int x = 0; x < data.length - mCoefficients.length + 1; x++ )
|
||||
{
|
||||
accumulator = 0.0f;
|
||||
|
||||
for( int y = 0; y < mCoefficients.length; y++ )
|
||||
{
|
||||
accumulator += data[ x + y ] * mCoefficients[ y ];
|
||||
}
|
||||
|
||||
filtered[ x + middle ] = accumulator;
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
}
|
||||
38
src/dsp/filter/smoothing/TriangularSmoothingFilter.java
Normal file
38
src/dsp/filter/smoothing/TriangularSmoothingFilter.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package dsp.filter.smoothing;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TriangularSmoothingFilter extends SmoothingFilter
|
||||
{
|
||||
private static Map<Integer,float[]> mMap = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
mMap.put( 3, new float[]{ 0.25f,0.5f,0.25f } );
|
||||
mMap.put( 5, new float[]{ 0.1111111111f,0.2222222222f,0.3333333333f,0.2222222222f,0.1111111111f } );
|
||||
mMap.put( 7, new float[]{ 0.0625f,0.125f,0.1875f,0.25f,0.1875f,0.125f,0.0625f } );
|
||||
mMap.put( 9, new float[]{ 0.04f,0.08f,0.12f,0.16f,0.2f,0.16f,0.12f,0.08f,0.04f } );
|
||||
mMap.put( 11, new float[]{ 0.0277777778f,0.0555555556f,0.0833333333f,0.1111111111f,0.1388888889f,0.1666666667f,0.1388888889f,0.1111111111f,0.0833333333f,0.0555555556f,0.0277777778f } );
|
||||
mMap.put( 13, new float[]{ 0.0204081633f,0.0408163265f,0.0612244898f,0.0816326531f,0.1020408163f,0.1224489796f,0.1428571429f,0.1224489796f,0.1020408163f,0.0816326531f,0.0612244898f,0.0408163265f,0.0204081633f } );
|
||||
mMap.put( 15, new float[]{ 0.015625f,0.03125f,0.046875f,0.0625f,0.078125f,0.09375f,0.109375f,0.125f,0.109375f,0.09375f,0.078125f,0.0625f,0.046875f,0.03125f,0.015625f } );
|
||||
mMap.put( 17, new float[]{ 0.012345679f,0.024691358f,0.037037037f,0.049382716f,0.0617283951f,0.0740740741f,0.0864197531f,0.0987654321f,0.1111111111f,0.0987654321f,0.0864197531f,0.0740740741f,0.0617283951f,0.049382716f,0.037037037f,0.024691358f,0.012345679f } );
|
||||
mMap.put( 19, new float[]{ 0.01f,0.02f,0.03f,0.04f,0.05f,0.06f,0.07f,0.08f,0.09f,0.1f,0.09f,0.08f,0.07f,0.06f,0.05f,0.04f,0.03f,0.02f,0.01f } );
|
||||
mMap.put( 21, new float[]{ 0.0082644628f,0.0165289256f,0.0247933884f,0.0330578512f,0.041322314f,0.0495867769f,0.0578512397f,0.0661157025f,0.0743801653f,0.0826446281f,0.0909090909f,0.0826446281f,0.0743801653f,0.0661157025f,0.0578512397f,0.0495867769f,0.041322314f,0.0330578512f,0.0247933884f,0.0165289256f,0.0082644628f } );
|
||||
mMap.put( 23, new float[]{ 0.0069444444f,0.0138888889f,0.0208333333f,0.0277777778f,0.0347222222f,0.0416666667f,0.0486111111f,0.0555555556f,0.0625f,0.0694444444f,0.0763888889f,0.0833333333f,0.0763888889f,0.0694444444f,0.0625f,0.0555555556f,0.0486111111f,0.0416666667f,0.0347222222f,0.0277777778f,0.0208333333f,0.0138888889f,0.0069444444f } );
|
||||
mMap.put( 25, new float[]{ 0.0059171598f,0.0118343195f,0.0177514793f,0.0236686391f,0.0295857988f,0.0355029586f,0.0414201183f,0.0473372781f,0.0532544379f,0.0591715976f,0.0650887574f,0.0710059172f,0.0769230769f,0.0710059172f,0.0650887574f,0.0591715976f,0.0532544379f,0.0473372781f,0.0414201183f,0.0355029586f,0.0295857988f,0.0236686391f,0.0177514793f,0.0118343195f,0.0059171598f } );
|
||||
mMap.put( 27, new float[]{ 0.0051020408f,0.0102040816f,0.0153061224f,0.0204081633f,0.0255102041f,0.0306122449f,0.0357142857f,0.0408163265f,0.0459183673f,0.0510204082f,0.056122449f,0.0612244898f,0.0663265306f,0.0714285714f,0.0663265306f,0.0612244898f,0.056122449f,0.0510204082f,0.0459183673f,0.0408163265f,0.0357142857f,0.0306122449f,0.0255102041f,0.0204081633f,0.0153061224f,0.0102040816f,0.0051020408f } );
|
||||
mMap.put( 29, new float[]{ 0.0044444444f,0.0088888889f,0.0133333333f,0.0177777778f,0.0222222222f,0.0266666667f,0.0311111111f,0.0355555556f,0.04f,0.0444444444f,0.0488888889f,0.0533333333f,0.0577777778f,0.0622222222f,0.0666666667f,0.0622222222f,0.0577777778f,0.0533333333f,0.0488888889f,0.0444444444f,0.04f,0.0355555556f,0.0311111111f,0.0266666667f,0.0222222222f,0.0177777778f,0.0133333333f,0.0088888889f,0.0044444444f } );
|
||||
}
|
||||
|
||||
public TriangularSmoothingFilter()
|
||||
{
|
||||
super( mMap, 3 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmoothingType getSmoothingType()
|
||||
{
|
||||
return SmoothingType.TRIANGLE;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user