WIP - adds windowed-sinc (nyquist) filter designer and polyphase channelizer filter designer to FilterFactory. Resolves a few issues with Window designers. ChannelizerViewer is now able to show 800 channels with 12.5 kHz channelization of a 10 MHz bandwidth input signal.

This commit is contained in:
Denny
2017-03-26 10:23:38 -04:00
parent 0e1da8fb3b
commit 6d9e129087
5 changed files with 267 additions and 219 deletions

View File

@@ -30,9 +30,13 @@ import org.slf4j.LoggerFactory;
import java.util.Set;
import java.util.TreeSet;
/**
* Utility methods for designing various types of filters.
*/
public class FilterFactory
{
private final static Logger mLog = LoggerFactory.getLogger(FilterFactory.class);
private static final double PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE = -6.020599842071533; //decibel(0.5)
/**
* Generates coefficients for a unity-gain, windowed low-pass filter
@@ -246,10 +250,7 @@ public class FilterFactory
* @param windowType - window to apply against the generated coefficients
* @return
*/
public static float[] getHighPass(int sampleRate,
long cutoff,
int filterLength,
WindowType windowType)
public static float[] getHighPass(int sampleRate, long cutoff, int filterLength, WindowType windowType)
{
//Convert the high frequency cutoff to its low frequency cutoff
//equivalent, so that when we generate the low pass filter, prior to
@@ -259,18 +260,11 @@ public class FilterFactory
return invert(getSinc(sampleRate, convertedCutoff, filterLength, windowType));
}
public static float[] getHighPass(int sampleRate,
long stopFrequency,
long passFrequency,
int attenuation,
WindowType windowType,
boolean forceOddLength)
public static float[] getHighPass(int sampleRate, long stopFrequency, long passFrequency, int attenuation,
WindowType windowType, boolean forceOddLength)
{
/* reverse the stop and pass frequency to get the low pass variant */
int tapCount = getTapCount(sampleRate,
stopFrequency,
passFrequency,
attenuation);
int tapCount = getTapCount(sampleRate, stopFrequency, passFrequency, attenuation);
if(forceOddLength)
{
@@ -314,11 +308,10 @@ public class FilterFactory
}
/**
* Determines the number of fir filter taps required to produce the
* specified frequency response with passband ripple near .1dB.
* Determines the number of fir filter taps required to produce the specified frequency response with passband
* ripple near 0.1dB.
*
* Implements the algorithm from Understanding Digital Signal Processing, 3e
* , Lyons, section 5.10.5.
* Implements the algorithm from Understanding Digital Signal Processing, 3e, Lyons, section 5.10.5.
*
* @param sampleRate in hertz
* @param pass pass frequency in hertz
@@ -326,19 +319,15 @@ public class FilterFactory
* @param attenuation in dB
* @return
*/
public static int getTapCount(int sampleRate,
long pass,
long stop,
int attenuation)
public static int getTapCount(int sampleRate, long pass, long stop, int attenuation)
{
double frequency = ((double)stop - (double)pass) / (double)sampleRate;
return (int)(Math.round((double)attenuation / (22.0d * frequency)));
}
public static ComplexPrimeCICDecimate getDecimationFilter(int sampleRate,
int decimatedRate, int order, int passFrequency, int attenuation,
WindowType windowType)
public static ComplexPrimeCICDecimate getDecimationFilter(int sampleRate, int decimatedRate, int order,
int passFrequency, int attenuation, WindowType windowType)
{
int decimationRate = (int)(sampleRate / decimatedRate);
@@ -355,10 +344,7 @@ public class FilterFactory
* polyphase filter decimation chain
* @throws - AssertionException if sample rate is not a multiple of 48 kHz
*/
public static int[] getPolyphaseDecimationRates(int sampleRate,
int decimatedRate,
long passFrequency,
long stopFrequency)
public static int[] getPolyphaseDecimationRates(int sampleRate, int decimatedRate, long passFrequency, long stopFrequency)
{
int[] rates;
@@ -386,11 +372,6 @@ public class FilterFactory
int stage1 = findClosest(optimalStage1, factors);
// mLog.info( "Decimation rate [" + decimation +
// "] stage1 optimal [" + optimalStage1 +
// "] stage1 actual [" + stage1 +
// "]");
if(stage1 == decimation || stage1 == 1)
{
rates = new int[1];
@@ -419,8 +400,7 @@ public class FilterFactory
}
/**
* Finds the factor that is closest to the desired factor, from an ordered
* list of factors.
* Finds the factor that is closest to the desired factor, from an ordered list of factors.
*/
private static int findClosest(int desiredFactor, Set<Integer> factors)
{
@@ -442,9 +422,8 @@ public class FilterFactory
}
/**
* Determines the factors that make up an integer. Uses a brute force
* method to iterate all integers from 1 to value, determining which factors
* are evenly divisible into the value.
* Determines the factors that make up an integer. Uses a brute force method to iterate all integers from 1 to
* value, determining which factors are evenly divisible into the value.
*
* @param value - integer decimation value
* @return - ordered set of factors for value
@@ -468,14 +447,10 @@ public class FilterFactory
}
/**
* Determines the optimal decimation rate for stage 1 of a two-stage
* poly-phase decimation filter chain, to produce a final sample rate of
* 48 kHz using a pass bandwidth of 25 kHz.
* Determines the optimal decimation rate for stage 1 of a two-stage poly-phase decimation filter chain, to produce
* a final sample rate of 48 kHz using a pass bandwidth of 25 kHz. Use for total decimation rates of 20 or higher.
*
* Use for total decimation rates of 20 or higher.
*
* Implements the algorithm described in Lyons, Understanding Digital Signal
* Processing, 3e, section 10.2.1, page 511.
* Implements the algorithm described in Lyons, Understanding Digital Signal Processing, 3e, section 10.2.1, page 511.
*
* @param sampleRate
* @param decimation
@@ -497,11 +472,8 @@ public class FilterFactory
}
/**
* Determines the F ratio as described in Lyons, Understanding Digital
* Signal Processing, 3e, section 10.2.1, page 511
*
* Used in conjunction with the optimal stage one decimation rate method
* above.
* Determines the F ratio as described in Lyons, Understanding Digital Signal Processing, 3e, section 10.2.1, page 511
* Used in conjunction with the optimal stage one decimation rate method above.
*/
private static double getBandwidthRatio(long passFrequency, long stopFrequency)
{
@@ -586,21 +558,17 @@ public class FilterFactory
}
/**
* Creates root raised cosine filter coefficients with a tap count equal
* to the symbols x samplesPerSymbol + 1.
* Creates root raised cosine filter coefficients with a tap count equal to the symbols x samplesPerSymbol + 1.
*
* Symbol count should be an even integer.
*
* Ported to java from gnuradio/filter/firdes.cc
*
* For 40db attenuation, calculate the number of symbols based on the
* following formula:
*
* Symbols = -44 * alpha + 33
* For 40db attenuation, calculate the number of symbols based on the following formula:
* Symbols = -44 * alpha + 33
*
* Polyphase Channelizer notes:
* -Set samples Per Symbol at 2 (or more) * number of channels
* -Set symbolCount to sufficient size to produce requireded attenuation
* -Set symbolCount to sufficient size to produce required attenuation
* -Set symbol rate in hertz
* -Set alpha = (desired channel bandwidth / (symbol rate * samples per symbol) - 1.0
* -Channel bandwidth must be larger than symbol rate * samples per symbol, in order for there to be a positive alpha
@@ -677,10 +645,10 @@ public class FilterFactory
}
/**
* Creates a filter from the filter specification
* Creates a filter from the filter specification using the remez exchange design algorithm
*
* @param specification
* @return
* @return filter coefficients
* @throws FilterDesignException if the filter cannot be designed
*/
public static float[] getTaps(FIRFilterSpecification specification) throws FilterDesignException
@@ -716,78 +684,195 @@ public class FilterFactory
return decibel(real, imag);
}
/**
* Calculates the decibel magnitude of the real and imaginary complex sample
* @param real (x-axis) value
* @param imag (y-axis) value
* @return magnitude in decibels
*/
public static double decibel(double real, double imag)
{
return (float)(10.0 * Math.log10(Math.pow(real, 2.0) + Math.pow(imag, 2.0)));
}
public static float[] getChannelizer(int channelBandwidth, int channels, int tapsPerChannel, WindowType windowType)
/**
* Creates a windowed-sync (Nyquist) M/2 prototype FIR filter for use with a Polyphase Channelizer/Synthesizer.
* The filter is designed for x2 oversampling of each channel and the filter cutoff frequency is incrementally
* adjusted to achieve a -6.02 dB attenuation at the channel band edge to enable perfect reconstruction of adjacent
* channels.
*
* The odd length sync filter that meets the design objectives will be increased in length by pre-padding a zero
* coefficient to produce a filter with a length that is an integral of the channel count.
*
* Note: the tapsPerChannel value will be iteratively increased (up to 10x) if a filter cannot be designed to meet
* the channel band edge attenuation objective.
*
* Note: a channelizer filter designed using a Blackman-Harris-7 window and 18-21 taps per channel creates a filter
* with attenuation in range of -60 to -80 dB at the edge of the 2x sampled channel.
*
* @param channelBandwidth - full bandwidth of the channel in hertz
* @param channels - number of channels
* @param tapsPerChannel - desired filter tap count per channel (may be increased to meet objective)
* @param windowType - to use when designing the windowed-sync filter
* @param logResults - to log a debug summary of the filter characteristics
*
* @return filter coefficients
*
* @throws FilterDesignException if a filter cannot be designed with a band edge attenuation of -6.02 dB
*/
public static float[] getChannelizer(int channelBandwidth, int channels, int tapsPerChannel, WindowType windowType,
boolean logResults) throws FilterDesignException
{
int currentTapsPerChannel = tapsPerChannel;
int sampleRate = channelBandwidth * channels * 2;
int filterLength = channels * tapsPerChannel;
int filterLength = (channels * currentTapsPerChannel) - 1;
double objective = -6.0;
int cutoff = channelBandwidth;
int increment = (int)((double)channelBandwidth * .1);
double cutoffFrequency = ((double)channelBandwidth / (double)sampleRate);
double bandEdge = cutoffFrequency;
double increment = cutoffFrequency * 0.1;
float[] taps = null;
double response = 0.0;
//Get an initial filter and band edge frequency response using the channel bandwidth as a cutoff
float[] taps = FilterFactory.getSinc(cutoffFrequency, filterLength, windowType);
double response = FilterFactory.evaluate(taps, bandEdge);
while(increment > 1)
//Set cutoff adjustment threshold - we'll test cutoff frequencies to around 1 hertz resolution
double incrementThreshold = 1.0 / (double)sampleRate;
//Iteratively evaluate filters to find the filter with the highest cutoff frequency that meets the objective
while(increment > incrementThreshold)
{
taps = FilterFactory.getLowPass(sampleRate, cutoff, filterLength, windowType);
double responseToCompare = FilterFactory.evaluate(taps, (double)channelBandwidth / (double)sampleRate );
mLog.debug("Comparing: " + responseToCompare + " Previous:" + response + " Cutoff:" + cutoff + " Increment:" + increment);
double a = Math.abs(objective - responseToCompare);
double b = Math.abs(objective - response);
if(a == b)
//If the current cutoff meets the objective, test if a higher cutoff also meets the objective
if(matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE) &&
(cutoffFrequency + increment <= bandEdge))
{
cutoff -= increment;
float[] higherCutoffTaps = FilterFactory.getSinc(cutoffFrequency + increment, filterLength, windowType);
double higherCutoffResponse = FilterFactory.evaluate(higherCutoffTaps, bandEdge);
if(matchesObjective(higherCutoffResponse, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
{
cutoffFrequency += increment;
taps = higherCutoffTaps;
response = higherCutoffResponse;
}
else
{
increment /= 2.0;
}
}
else if(a < b)
//If the current cutoff meets the objective, decrease the increment to test for smaller resolution cutoffs
else if(matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
{
response = responseToCompare;
cutoff -= increment;
increment /= 2.0;
}
//Decrease the cutoff frequency - we can't meet the objective
else
{
//reset the cutoff
if(cutoff < channelBandwidth)
cutoffFrequency -= increment;
if(cutoffFrequency <= 0)
{
cutoff += increment;
if(logResults)
{
mLog.debug("Warning: cannot design channelizer filter with tap count [" + currentTapsPerChannel +
"] increasing tap count and starting over");
}
currentTapsPerChannel++;
//After we've attempted increasing the taps per channel 10x, give up
if(currentTapsPerChannel > (tapsPerChannel + 10))
{
throw new FilterDesignException("Couldn't design filter with taps per channel count in the " +
"range of " + tapsPerChannel + " - " + (tapsPerChannel + 10));
}
filterLength = channels * currentTapsPerChannel - 1;
cutoffFrequency = ((double)channelBandwidth / (double)sampleRate);
increment = cutoffFrequency * 0.1;
}
increment /= 2;
if(increment == 1)
{
taps = FilterFactory.getLowPass(sampleRate, cutoff, filterLength, windowType);
}
taps = FilterFactory.getSinc(cutoffFrequency, filterLength, windowType);
response = FilterFactory.evaluate(taps, bandEdge);
}
}
if(response < objective)
//This will probably never happen, but ...
if(!matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
{
mLog.debug("Checking ...");
float[] tapsToCompare = FilterFactory.getLowPass(sampleRate, cutoff + 1, filterLength, windowType);
double responseToCompare = FilterFactory.evaluate(tapsToCompare, (double)channelBandwidth / (double)sampleRate );
if(Math.abs(objective - responseToCompare) < Math.abs(objective - response))
{
mLog.debug("Using Cutoff:" + cutoff + 1);
taps = tapsToCompare;
}
else
{
mLog.debug("Staying at cutoff: " + cutoff + " And Response at 12500 of:" + response);
}
throw new FilterDesignException("Cannot design filter to specifications");
}
if(logResults)
{
mLog.debug("Polyphase Channelizer Filter Design Summary");
mLog.debug("-------------------------------------------");
mLog.debug("Channels: " + channels);
mLog.debug("Taps Per Channel: " + tapsPerChannel);
mLog.debug("Filter Length: " + (taps.length + 1));
mLog.debug("Channel Bandwidth: " + channelBandwidth);
mLog.debug("Cutoff Frequency: " + (sampleRate * cutoffFrequency));
mLog.debug("Attenuation at 1.00 OBJECTIVE: " + PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE);
mLog.debug("Attenuation at 1.00 Channels: " + evaluate(taps, bandEdge * 1.00));
mLog.debug("Attenuation at 1.25 Channels: " + evaluate(taps, bandEdge * 1.25));
mLog.debug("Attenuation at 1.50 Channels: " + evaluate(taps, bandEdge * 1.50));
mLog.debug("Attenuation at 1.75 Channels: " + evaluate(taps, bandEdge * 1.75));
mLog.debug("Attenuation at 2.00 Channels: " + evaluate(taps, bandEdge * 2.00));
}
return taps;
//This is an odd length filter - increase the length by 1 by pre-padding a zero coefficient
float[] extendedTaps = new float[taps.length + 1];
System.arraycopy(taps, 0, extendedTaps, 1, taps.length);
return extendedTaps;
}
/**
* Creates a windowed-sync (Nyquist) filter.
*
* @param cutoff frequency (0.0 <> 0.5)
* @param length of the filter - must be odd-length
* @param windowType to use in designing the filter
* @return filter coefficients.
* @throws FilterDesignException if the requested length is not odd
*/
public static float[] getSinc(double cutoff, int length, WindowType windowType) throws FilterDesignException
{
if(length % 2 == 0)
{
throw new FilterDesignException("Sinc filters must be odd-length");
}
float[] coefficients = new float[length];
int half = length / 2;
double[] window = Window.getWindow(windowType, length);
double scalor = 2.0 * cutoff;
double piScalor = Math.PI * scalor;
coefficients[half] = (float)(1.0 * scalor);
for(int x = 1; x <= half; x++)
{
double a = piScalor * x;
double coefficient = scalor * Math.sin(a) / a;
coefficient *= window[half + x];
coefficients[half + x] = (float)coefficient;
coefficients[half - x] = (float)coefficient;
}
return coefficients;
}
/**
* Compares two doubles for equals and avoids any rounding error that are present.
* @param a value to compare
* @param objective value to compare
* @return true if they are equivalent within the margin of error
*/
private static boolean matchesObjective(double a, double objective)
{
return Math.abs(a - objective) < 0.0000000001;
}
}

View File

@@ -138,8 +138,8 @@ public class Window
for(int x = 0; x < length; x++)
{
coefficients[x] = a0 -
(a1 * Math.cos((TWO_PI * (double)x) / denominator) +
(a2 * Math.cos((FOUR_PI * (double)x) / denominator)));
(a1 * Math.cos((TWO_PI * (double)x) / denominator)) +
(a2 * Math.cos((FOUR_PI * (double)x) / denominator));
}
return coefficients;
@@ -273,21 +273,13 @@ public class Window
{
double[] coefficients = new double[length];
if(length % 2 == 0) //Even length
double a0 = 0.54;
double a1 = 0.46;
double denominator = length;
for(int x = 0; x < length; x++)
{
for(int x = 0; x < length; x++)
{
coefficients[x] = .54D - (.46D *
Math.cos((Math.PI * (2.0D * (double)x + 1.0d)) / (double)(length - 1)));
}
}
else //Odd length
{
for(int x = 0; x < length; x++)
{
coefficients[x] = .54D - (.46D *
Math.cos((2.0D * Math.PI * (double)x) / (double)(length - 1)));
}
coefficients[x] = a0 - (a1 * Math.cos(TWO_PI * (double)x / denominator));
}
return coefficients;
@@ -303,23 +295,14 @@ public class Window
{
double[] coefficients = new double[length];
if(length % 2 == 0) //Even length
{
for(int x = 0; x < length; x++)
{
coefficients[x] = .5D - (.5D *
Math.cos((Math.PI * (2.0D * (double)x + 1)) / (double)(length - 1)));
}
}
else //Odd length
{
for(int x = 0; x < length; x++)
{
coefficients[x] = .5D - (.5D *
Math.cos((2.0D * Math.PI * (double)x) / (double)(length - 1)));
}
}
double a0 = 0.5;
double a1 = 0.5;
double denominator = length;
for(int x = 0; x < length; x++)
{
coefficients[x] = a0 - (a1 * Math.cos(TWO_PI * (double)x / denominator));
}
return coefficients;
}

View File

@@ -18,13 +18,10 @@
******************************************************************************/
package dsp.filter.channelizer;
import dsp.filter.*;
import dsp.filter.FilterFactory;
import dsp.filter.Window;
import dsp.filter.design.FilterDesignException;
import dsp.filter.fir.FIRFilterSpecification;
import dsp.filter.fir.remez.RemezFIRFilterDesigner;
import dsp.mixer.LowPhaseNoiseOscillator;
import dsp.mixer.Oscillator;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,13 +58,22 @@ public class ChannelizerViewer extends JFrame
private ChannelArrayPanel mChannelPanel;
private LowPhaseNoiseOscillator mOscillator;
private int mChannelCount;
private int mChannelsPerRow;
private int mSampleRate;
private int mToneFrequency;
private DFTSize mDFTSize = DFTSize.FFT04096;
private DFTSize mMainPanelDFTSize = DFTSize.FFT32768;
private DFTSize mChannelPanelDFTSize = DFTSize.FFT00512;
public ChannelizerViewer(int channelCount)
/**
* GUI Test utility for researching polyphase channelizers.
*
* @param channelCount
* @param channelsPerRow
*/
public ChannelizerViewer(int channelCount, int channelsPerRow)
{
mChannelCount = channelCount;
mChannelsPerRow = channelsPerRow;
mSampleRate = mChannelCount * CHANNEL_BANDWIDTH;
mToneFrequency = (int)(CHANNEL_BANDWIDTH * 1); //Set to channel 1 as default
@@ -130,7 +136,7 @@ public class ChannelizerViewer extends JFrame
{
mPrimarySpectrumPanel = new ChannelPanel(mSettingsManager, mSampleRate);
mPrimarySpectrumPanel.setPreferredSize(new Dimension(1000, 200));
mPrimarySpectrumPanel.setDFTSize(DFTSize.FFT32768);
mPrimarySpectrumPanel.setDFTSize(mMainPanelDFTSize);
}
return mPrimarySpectrumPanel;
@@ -177,24 +183,27 @@ public class ChannelizerViewer extends JFrame
{
if(mChannelPanel == null)
{
mChannelPanel = new ChannelArrayPanel(getFilter());
float[] taps = null;
try
{
taps = getFilter();
}
catch(FilterDesignException fde)
{
mLog.debug("Couldn't design filter", fde);
}
mChannelPanel = new ChannelArrayPanel(taps);
}
return mChannelPanel;
}
private float[] getFilter()
private float[] getFilter() throws FilterDesignException
{
float[] taps = FilterFactory.getChannelizer(CHANNEL_BANDWIDTH, mChannelCount, 16, Window.WindowType.BLACKMAN_HARRIS_7);
StringBuilder sb = new StringBuilder();
sb.append("\nPolyphase Channelizer\n");
sb.append("Sample Rate:" + mSampleRate + " Channels:" + mChannelCount + " Channel Rate:" + CHANNEL_BANDWIDTH + "\n");
sb.append("Tap Count:" + taps.length + "\n");
sb.append("Channel:" + mToneFrequency);
mLog.debug(sb.toString());
return taps;
return FilterFactory.getChannelizer(CHANNEL_BANDWIDTH, mChannelCount, 19,
Window.WindowType.BLACKMAN_HARRIS_7, true);
}
public class ChannelArrayPanel extends JPanel implements Listener<ComplexBuffer>
@@ -221,15 +230,12 @@ public class ChannelizerViewer extends JFrame
{
setLayout(new MigLayout("insets 0 0 0 0", "fill", "fill"));
int channelsPerRow = 20;
for(int x = 0; x < mChannelCount; x++)
{
ChannelPanel channelPanel = new ChannelPanel(mSettingsManager, CHANNEL_BANDWIDTH * 2);
// channelPanel.setPreferredSize(new Dimension(250,100));
channelPanel.setDFTSize(DFTSize.FFT00512);
channelPanel.setDFTSize(mChannelPanelDFTSize);
if(x % channelsPerRow == channelsPerRow - 1)
if(x % mChannelsPerRow == mChannelsPerRow - 1)
{
add(channelPanel, "grow,push,wrap 2px");
}
@@ -279,27 +285,6 @@ public class ChannelizerViewer extends JFrame
}
}
public class SampleGenerator
{
public SampleGenerator()
{
mLog.debug("Starting ...");
PolyphaseChannelizer channelizer = new PolyphaseChannelizer(getFilter(), mChannelCount);
Oscillator oscillator = new Oscillator(mToneFrequency, mSampleRate);
for(int x = 0; x < 2000; x++)
{
channelizer.filter(oscillator.getComplex().inphase(), oscillator.getComplex().quadrature());
oscillator.rotate();
}
mLog.debug("Finished!");
}
}
public class SampleGenerationTask implements Runnable
{
@Override
@@ -311,9 +296,10 @@ public class ChannelizerViewer extends JFrame
public static void main(String[] args)
{
int channelCount = 10;
int channelCount = 100;
int channelsPerRow = 25;
final ChannelizerViewer frame = new ChannelizerViewer(channelCount);
final ChannelizerViewer frame = new ChannelizerViewer(channelCount, channelsPerRow);
EventQueue.invokeLater(new Runnable()
{

View File

@@ -22,7 +22,7 @@ public class FilterView extends BorderPane
private CurveFittedAreaChart mDFTChart;
private NumberAxis mDFTXAxis;
private NumberAxis mDFTYAxis;
private static final int FFT_SIZE = 4096;
private static final int FFT_SIZE = 2048;
private FloatFFT_1D mFFT = new FloatFFT_1D(FFT_SIZE);
public FilterView(float[] taps)
@@ -156,7 +156,8 @@ public class FilterView extends BorderPane
for(int x = 0; x < decibels.length; x++)
{
index = x * 2;
decibels[x] = 10.0f * (float)Math.log10((dft[index] * dft[index]) + (dft[index + 1] * dft[index + 1]));
double magnitude = Math.sqrt(Math.pow(dft[index], 2.0) + Math.pow(dft[index + 1], 2.0));
decibels[x] = (float)(20.0 * Math.log10(magnitude));
}
return decibels;

View File

@@ -5,6 +5,7 @@ import dsp.filter.Window;
import dsp.filter.fir.FIRFilterSpecification;
import dsp.filter.fir.remez.PolyphaseChannelizerFilterFactory;
import dsp.filter.fir.remez.RemezFIRFilterDesigner;
import dsp.filter.fir.remez.RemezFIRFilterDesigner2;
import dsp.filter.fir.remez.RemezFIRFilterDesignerWithLagrange;
import filter.Filter;
import javafx.application.Application;
@@ -55,15 +56,17 @@ public class FilterViewer extends Application
*/
private float[] getFilter()
{
float[] taps = null;
// FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
// .sampleRate(10000)
// .sampleRate(1000)
// .gridDensity(16)
// .passBandAmplitude(1.0)
// .passBandCutoff(4000)
// .passBandCutoff(100)
// .passBandRipple(0.01)
// .stopBandAmplitude(0.0)
// .stopBandRipple(0.001)
// .stopBandStart(5000)
// .stopBandRipple(0.0001)
// .stopBandStart(150)
// .build();
// FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
@@ -86,35 +89,25 @@ public class FilterViewer extends Application
// .passRipple( 0.008)
// .build();
float[] taps = null;
// try
// {
// RemezFIRFilterDesignerWithLagrange remez = new RemezFIRFilterDesignerWithLagrange(specification);
// taps = remez.getImpulseResponse();
// }
// catch(Exception e)
// {
// mLog.error("Error designing filter", e);
// }
// int tapCount = FilterFactory.getTapCount(10000000, 12500, 18750, 80);
// taps = FilterFactory.getLowPass( 10000000, 12500, 5600, Window.WindowType.BLACKMAN_HARRIS_7 );
// int channels = 10;
// int channelRate = 12500;
// int cutoff = 8000;
// int sampleRate = channelRate * channels;
// int filterLength = channels * 7;
//
// taps = FilterFactory.getLowPass(sampleRate, cutoff, filterLength, Window.WindowType.BLACKMAN_HARRIS_7);
//
// double response = FilterFactory.evaluate(taps, (double)channelRate / (double)sampleRate );
//
// mLog.debug("Taps:" + taps.length + " With response at [" + channelRate + "]: " + response);
int channelBandwidth = 12500;
int channels = 4;
int tapsPerChannel = 20;
taps = FilterFactory.getChannelizer(channelBandwidth, channels, tapsPerChannel, Window.WindowType.BLACKMAN_HARRIS_4);
double sampleRate = channelBandwidth * channels * 2;
for(int x = 0; x < 25000; x += 100)
// taps = FilterFactory.getSinc(0.1, 51, Window.WindowType.BLACKMAN_HARRIS_7);
try
{
double toEval = (double)x / sampleRate;
mLog.debug("Response at [" + x + "] is:" + FilterFactory.evaluate(taps, toEval));
taps = FilterFactory.getChannelizer(12500, 50, 14,
Window.WindowType.BLACKMAN_HARRIS_7, true);
}
catch(Exception e)
{
mLog.error("Couldn't design filter", e);
}
return taps;