diff --git a/.project b/.project index 7accc076..f65c4170 100644 --- a/.project +++ b/.project @@ -5,11 +5,6 @@ - - org.eclipse.xtext.ui.shared.xtextBuilder - - - org.eclipse.jdt.core.javabuilder @@ -18,6 +13,5 @@ org.eclipse.jdt.core.javanature - org.eclipse.xtext.ui.shared.xtextNature diff --git a/images/Curve-fitted-background.png b/images/Curve-fitted-background.png deleted file mode 100644 index 20858058..00000000 Binary files a/images/Curve-fitted-background.png and /dev/null differ diff --git a/images/Curve-fitted-chart-background.png b/images/Curve-fitted-chart-background.png deleted file mode 100644 index 5172edca..00000000 Binary files a/images/Curve-fitted-chart-background.png and /dev/null differ diff --git a/images/Curve-fitted-graph-gridlines.png b/images/Curve-fitted-graph-gridlines.png deleted file mode 100644 index 648c4f9a..00000000 Binary files a/images/Curve-fitted-graph-gridlines.png and /dev/null differ diff --git a/src/dsp/filter/FilterFactory.java b/src/dsp/filter/FilterFactory.java index f73a0283..8f6b0065 100644 --- a/src/dsp/filter/FilterFactory.java +++ b/src/dsp/filter/FilterFactory.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014-2016,2015 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * Root Raised Cosine filter designer: * Copyright 2002,2007,2008,2012,2013 Free Software Foundation, Inc. @@ -35,46 +35,9 @@ import dsp.filter.cic.ComplexPrimeCICDecimate; public class FilterFactory { - private final static Logger mLog = LoggerFactory.getLogger( FilterFactory.class ); + private final static Logger mLog = + LoggerFactory.getLogger( FilterFactory.class ); - public static final double TWO_PI = Math.pow( Math.PI, 2.0 ); - - public static float[] getNewLowPass( int sampleRate, long passFrequency, long stopFrequency, - int attenuation, WindowType window ) - { - assert( passFrequency <= sampleRate / 2 ); - - int length = getTapCount( sampleRate, passFrequency, stopFrequency, attenuation ); - - if( length % 2 == 0 ) - { - length++; - } - - float[] coefficients = new float[ length ]; - - int half = length / 2; - - int K = (int)( length * ( 2.0d * (double)passFrequency / (double) sampleRate )); - - double scale = 1.0 / (double)length; - - coefficients[ half ] = (float)( K / (double)length ); - - for( int k = 1; k <= half; k++ ) - { - float coefficient = (float)( scale * ( Math.sin( Math.PI * k * K / length ) / - Math.sin( Math.PI * k / length ) ) ); - - coefficients[ k + half ] = coefficient; - coefficients[ half - k ] = coefficient; - } - - Window.apply( WindowType.HANNING, coefficients ); - - return coefficients; - } - /** * Generates coefficients for a unity-gain, windowed low-pass filter * @param sampleRate - hertz @@ -115,7 +78,6 @@ public class FilterFactory } //Apply the window against the coefficients - coefficients = Window.apply( window, coefficients ); //Normalize to unity (1) gain diff --git a/src/dsp/filter/GoertzelFilter.java b/src/dsp/filter/GoertzelFilter.java index c448beb2..a421f912 100644 --- a/src/dsp/filter/GoertzelFilter.java +++ b/src/dsp/filter/GoertzelFilter.java @@ -32,7 +32,7 @@ public class GoertzelFilter private int mSampleRate; private long mTargetFrequency; private int mBlockSize; - private float[] mWindowCoefficients; + private double[] mWindowCoefficients; private double mCoefficient; diff --git a/src/dsp/filter/Window.java b/src/dsp/filter/Window.java index 5247b16b..72e846b4 100644 --- a/src/dsp/filter/Window.java +++ b/src/dsp/filter/Window.java @@ -19,9 +19,6 @@ package dsp.filter; import java.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Window creator * @@ -36,9 +33,7 @@ import org.slf4j.LoggerFactory; */ public class Window { - private final static Logger mLog = LoggerFactory.getLogger( Window.class ); - - public static float[] getWindow( WindowType type, int length ) + public static double[] getWindow( WindowType type, int length ) { switch( type ) { @@ -56,18 +51,18 @@ public class Window } } - public static float[] getRectangularWindow( int length ) + public static double[] getRectangularWindow( int length ) { - float[] coefficients = new float[ length ]; + double[] coefficients = new double[ length ]; - Arrays.fill( coefficients, 1.0f ); + Arrays.fill( coefficients, 1.0d ); return coefficients; } - public static float[] getCosineWindow( int length ) + public static double[] getCosineWindow( int length ) { - float[] coefficients = new float[ length ]; + double[] coefficients = new double[ length ]; if( length % 2 == 0 ) //Even length @@ -76,7 +71,7 @@ public class Window for( int x = -half; x < length / 2 + 1; x++ ) { - coefficients[ x + half ] = (float)Math.cos( + coefficients[ x + half ] = Math.cos( ( (double)x * Math.PI ) / ( (double)length + 1.0d ) ); } } @@ -86,7 +81,7 @@ public class Window for( int x = -half; x < half + 1; x++ ) { - coefficients[ x + half ] = (float)Math.cos( + coefficients[ x + half ] = Math.cos( ( (double)x * Math.PI ) / ( (double)length + 1.0d ) ); } } @@ -94,62 +89,62 @@ public class Window return coefficients; } - public static float[] getBlackmanWindow( int length ) + public static double[] getBlackmanWindow( int length ) { - float[] coefficients = new float[ length ]; + double[] coefficients = new double[ length ]; for( int x = 0; x < length; x++ ) { - coefficients[ x ] = (float)( .426591D - + coefficients[ x ] = .426591D - ( .496561D * Math.cos( ( Math.PI * 2.0D * (double)x ) / (double)( length - 1 ) ) + - ( .076848D * Math.cos( ( Math.PI * 4.0D * (double)x ) / (double)( length - 1 ) ) ) ) ); + ( .076848D * Math.cos( ( Math.PI * 4.0D * (double)x ) / (double)( length - 1 ) ) ) ); } return coefficients; } - public static float[] getHammingWindow( int length ) + public static double[] getHammingWindow( int length ) { - float[] coefficients = new float[ length ]; + double[] coefficients = new double[ length ]; if( length % 2 == 0 ) //Even length { for( int x = 0; x < length; x++ ) { - coefficients[ x ] = (float)( .54D - ( .46D * - Math.cos( ( Math.PI * ( 2.0D * (double)x + 1.0d ) ) / (double)( length - 1 ) ) ) ); + 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 ] = (float)( .54D - ( .46D * - Math.cos( ( 2.0D * Math.PI * (double)x ) / (double)( length - 1 ) ) ) ); + coefficients[ x ] = .54D - ( .46D * + Math.cos( ( 2.0D * Math.PI * (double)x ) / (double)( length - 1 ) ) ); } } return coefficients; } - public static float[] getHanningWindow( int length ) + public static double[] getHanningWindow( int length ) { - float[] coefficients = new float[ length ]; + double[] coefficients = new double[ length ]; if( length % 2 == 0 ) //Even length { for( int x = 0; x < length; x++ ) { - coefficients[ x ] = (float)( .5D - ( .5D * - Math.cos( ( Math.PI * ( 2.0D * (double)x + 1 ) ) / (double)( length - 1 ) ) ) ); + 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 ] = (float)( .5D - ( .5D * - Math.cos( ( 2.0D * Math.PI * (double)x ) / (double)( length - 1 ) ) ) ); + coefficients[ x ] = .5D - ( .5D * + Math.cos( ( 2.0D * Math.PI * (double)x ) / (double)( length - 1 ) ) ); } } @@ -160,21 +155,41 @@ public class Window /** * Apply the window against an array of float-type samples */ - public static float[] apply( float[] windowCoefficients, float[] samples ) + public static float[] apply( double[] coefficients, float[] samples ) { - for( int x = 0; x < windowCoefficients.length; x++ ) + for( int x = 0; x < coefficients.length; x++ ) { - samples[ x ] = samples[ x ] * windowCoefficients[ x ]; + samples[ x ] = (float)( samples[ x ] * coefficients[ x ] ); + } + + return samples; + } + + public static float[] apply( WindowType type, float[] samples ) + { + double[] coefficients = getWindow( type, samples.length ); + + return apply( coefficients, samples ); + } + + /** + * Apply the window against an array of float-type samples + */ + public static double[] apply( double[] coefficients, double[] samples ) + { + for( int x = 0; x < coefficients.length; x++ ) + { + samples[ x ] = samples[ x ] * coefficients[ x ]; } return samples; } - public static float[] apply( WindowType type, float[] samples ) + public static double[] apply( WindowType type, double[] samples ) { - float[] windowCoefficients = getWindow( type, samples.length ); + double[] coefficients = getWindow( type, samples.length ); - return apply( windowCoefficients, samples ); + return apply( coefficients, samples ); } @@ -190,7 +205,7 @@ public class Window /** * Utility to log the double arrays */ - public static String arrayToString( float[] array, boolean breaks ) + public static String arrayToString( double[] array, boolean breaks ) { StringBuilder sb = new StringBuilder(); for( int x = 0; x < array.length; x++ ) diff --git a/src/filter/Remez.java b/src/filter/Remez.java deleted file mode 100644 index bc8b8b32..00000000 --- a/src/filter/Remez.java +++ /dev/null @@ -1,574 +0,0 @@ - -/************************************************************************** - * Parks-McClellan algorithm for FIR filter design (C version) - *------------------------------------------------- - * Copyright (C) 1995 Jake Janovetz (janovetz@coewl.cen.uiuc.edu) - * - * 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 2 of the License, or - * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - *************************************************************************/ -package filter; - -public class Remez -{ - - - /* Converted to Java by Iain A Robin, June 1998 */ - - /* changes by Hanns Holger Rutz, Jan 2010: - * needed to make method 'remez' public - */ - - - public static final int BANDPASS = 1; - public static final int DIFFERENTIATOR = 2; - public static final int HILBERT = 3; - - public static final int NEGATIVE = 0; - public static final int POSITIVE = 1; - - public static final int GRIDDENSITY = 16; - public static final int MAXITERATIONS = 40; - - double pi = Math.PI; - double pi2 = 2.0*pi; - - int numtaps; - - public Remez() - { - } - - /******************* - * createDenseGrid - *================= - * Creates the dense grid of frequencies from the specified bands. - * Also creates the Desired Frequency Response function (d[]) and - * the Weight function (w[]) on that dense grid - * - * - * INPUT: - * ------ - * int r - 1/2 the number of filter coefficients - * int numtaps - Number of taps in the resulting filter - * int numband - Number of bands in user specification - * double[] bands - User-specified band edges [2*numband] - * double[] des - Desired response per band [numband] - * double[] weight - Weight per band [numband] - * int symmetry - Symmetry of filter - used for grid check - * - * OUTPUT: - * ------- - * int gridSize - Number of elements in the dense frequency grid - * double[] grid - Frequencies (0 to 0.5) on the dense grid [gridSize] - * double[] d - Desired response on the dense grid [gridSize] - * double[] w - Weight function on the dense grid [gridSize] - *******************/ - - void createDenseGrid(int r, int numtaps, int numband, double bands[], - double des[], double weight[], int gridSize, - double grid[], double d[], double w[], - int symmetry) { - double lowf, highf; - double delf = 0.5/(GRIDDENSITY*r); - - // For differentiator, hilbert, - // symmetry is odd and grid[0] = max(delf, band[0]) - if ((symmetry == NEGATIVE) && (delf > bands[0])) - bands[0] = delf; - - int j=0; - int k; - for (int band=0; band < numband; band++) { - grid[j] = bands[2*band]; - lowf = bands[2*band]; - highf = bands[2*band + 1]; - k = (int)Math.round((highf - lowf)/delf); - for (int i=0; i (0.5 - delf)) && ((numtaps % 2) != 0)) { - grid[gridSize-1] = 0.5-delf; - } - } - - /******************** - * initialGuess - *============== - * Places Extremal Frequencies evenly throughout the dense grid. - * - * - * INPUT: - * ------ - * int r - 1/2 the number of filter coefficients - * int gridSize - Number of elements in the dense frequency grid - * - * OUTPUT: - * ------- - * int ext[] - Extremal indexes to dense frequency grid [r+1] - ********************/ - - void initialGuess(int r, int ext[], int gridSize) { - for (int i=0; i<=r; i++) ext[i] = i * (gridSize-1) / r; - } - - - /*********************** - * calcParms - *=========== - * - * - * INPUT: - * ------ - * int r - 1/2 the number of filter coefficients - * int[] ext - Extremal indexes to dense frequency grid [r+1] - * double[] grid - Frequencies (0 to 0.5) on the dense grid [gridSize] - * double[] d - Desired response on the dense grid [gridSize] - * double[] w - Weight function on the dense grid [gridSize] - * - * OUTPUT: - * ------- - * double[] ad - 'b' in Oppenheim & Schafer [r+1] - * double[] x - [r+1] - * double[] y - 'C' in Oppenheim & Schafer [r+1] - ***********************/ - - void calcParms(int r, int ext[], double grid[], double d[], double w[], - double ad[], double x[], double y[]) { - double sign, xi, delta, denom, numer; - - // Find x[] - for (int i=0; i<=r; i++) x[i] = Math.cos(pi2 * grid[ext[i]]); - - // Calculate ad[] - Oppenheim & Schafer eq 7.132 - int ld = (r-1)/15 + 1; // Skips around to avoid round errors - for (int i=0; i<=r; i++) { - denom = 1.0; - xi = x[i]; - for (int j=0; j0.0) && (e[0]>e[1])) || ((e[0]<0.0) && (e[0]=e[i-1]) && (e[i]>e[i+1]) && (e[i]>0.0)) || - ((e[i]<=e[i-1]) && (e[i]0.0) && (e[j]>e[j-1])) || ((e[j]<0.0) && (e[j] 0) { - up = e[foundExt[0]] > 0.0; - // up = true --> first one is a maximum - // up = false --> first one is a minimum - l=0; - alt = true; - for (j=1; j 0.0)) - up = true; // switch to a maxima - else { - alt = false; - break; // Ooops, found two non-alternating - } // extrema. Delete smallest of them - } // if the loop finishes, all extrema are alternating - - // If there's only one extremal and all are alternating, - // delete the smallest of the first/last extremals. - if (alt && (extra == 1)) { - if (Math.abs(e[foundExt[k-1]]) < Math.abs(e[foundExt[0]])) - l = foundExt[k-1]; // Delete last extremal - else - l = foundExt[0]; // Delete first extremal - } - - for (j=l; j max) max = current; - } - if (((max-min)/max) < 0.0001) return true; - return false; - } - - /******************** - * remez - *======= - * Calculates the optimal (in the Chebyshev/minimax sense) - * FIR filter impulse response given a set of band edges, - * the desired reponse on those bands, and the weight given to - * the error in those bands. - * - * INPUT: - * ------ - * int numtaps - Number of filter coefficients - * int numband - Number of bands in filter specification - * double[] bands - User-specified band edges [2 * numband] - * double[] des - User-specified band responses [numband] - * double[] weight - User-specified error weights [numband] - * int type - Type of filter - * - * OUTPUT: - * ------- - * double[] h - Impulse response of final filter [numtaps] - ********************/ - - public double[] remez(int n, - double bands[], double des[], double weight[], int type) { - - double c; - numtaps = n; - int symmetry = (type == BANDPASS)? POSITIVE : NEGATIVE; - int r = numtaps/2; // number of extrema - if ((numtaps%2 != 0) && (symmetry == POSITIVE)) r++; - - // Predict dense grid size in advance for array sizes - int gridSize = 0; - int numband = des.length; - for (int i=0; i 0.0001) w[i] /= grid[i]; - } - } - - // For odd or Negative symmetry filters, alter the - // d[] and w[] according to Parks McClellan - if (symmetry == POSITIVE) { - if (numtaps % 2 == 0) { - for (int i=0; i -{ - - public CurveFittedAreaChart( NumberAxis xAxis, NumberAxis yAxis ) - { - super( xAxis, yAxis ); - } - - @Override - protected void layoutPlotChildren() - { - super.layoutPlotChildren(); - for ( int seriesIndex = 0; seriesIndex < getDataSize(); seriesIndex++ ) - { - final XYChart.Series series = getData() - .get( seriesIndex ); - final Path seriesLine = (Path) ( (Group) series.getNode() ) - .getChildren().get( 1 ); - final Path fillPath = (Path) ( (Group) series.getNode() ) - .getChildren().get( 0 ); - smooth( seriesLine.getElements(), fillPath.getElements() ); - } - } - - private int getDataSize() - { - final ObservableList> data = getData(); - return ( data != null ) ? data.size() : 0; - } - - private static void smooth( ObservableList strokeElements, - ObservableList fillElements ) - { - // as we do not have direct access to the data, first recreate the list - // of all the data points we have - final Point2D[] dataPoints = new Point2D[ strokeElements.size() ]; - for ( int i = 0; i < strokeElements.size(); i++ ) - { - final PathElement element = strokeElements.get( i ); - if ( element instanceof MoveTo ) - { - final MoveTo move = (MoveTo) element; - dataPoints[ i ] = new Point2D( move.getX(), move.getY() ); - } else if ( element instanceof LineTo ) - { - final LineTo line = (LineTo) element; - final double x = line.getX(), y = line.getY(); - dataPoints[ i ] = new Point2D( x, y ); - } - } - // next we need to know the zero Y value - final double zeroY = ( (MoveTo) fillElements.get( 0 ) ).getY(); - // now clear and rebuild elements - strokeElements.clear(); - fillElements.clear(); - Pair result = calcCurveControlPoints( - dataPoints ); - Point2D[] firstControlPoints = result.getKey(); - Point2D[] secondControlPoints = result.getValue(); - // start both paths - strokeElements.add( - new MoveTo( dataPoints[ 0 ].getX(), dataPoints[ 0 ].getY() ) ); - fillElements.add( new MoveTo( dataPoints[ 0 ].getX(), zeroY ) ); - fillElements.add( - new LineTo( dataPoints[ 0 ].getX(), dataPoints[ 0 ].getY() ) ); - // add curves - for ( int i = 1; i < dataPoints.length; i++ ) - { - final int ci = i - 1; - strokeElements - .add( new CubicCurveTo( firstControlPoints[ ci ].getX(), - firstControlPoints[ ci ].getY(), - secondControlPoints[ ci ].getX(), - secondControlPoints[ ci ].getY(), - dataPoints[ i ].getX(), dataPoints[ i ].getY() ) ); - fillElements.add( new CubicCurveTo( firstControlPoints[ ci ].getX(), - firstControlPoints[ ci ].getY(), - secondControlPoints[ ci ].getX(), - secondControlPoints[ ci ].getY(), dataPoints[ i ].getX(), - dataPoints[ i ].getY() ) ); - } - // end the paths - fillElements.add( new LineTo( - dataPoints[ dataPoints.length - 1 ].getX(), zeroY ) ); - fillElements.add( new ClosePath() ); - } - - /** - * Calculate open-ended Bezier Spline Control Points. - */ - public static Pair calcCurveControlPoints( - Point2D[] dataPoints ) - { - Point2D[] firstControlPoints; - Point2D[] secondControlPoints; - int n = dataPoints.length - 1; - if ( n == 1 ) - { // Special case: Bezier curve should be a straight line. - firstControlPoints = new Point2D[ 1 ]; - // 3P1 = 2P0 + P3 - firstControlPoints[ 0 ] = new Point2D( - ( 2 * dataPoints[ 0 ].getX() + dataPoints[ 1 ].getX() ) / 3, - ( 2 * dataPoints[ 0 ].getY() + dataPoints[ 1 ].getY() ) - / 3 ); - - secondControlPoints = new Point2D[ 1 ]; - // P2 = 2P1 – P0 - secondControlPoints[ 0 ] = new Point2D( - 2 * firstControlPoints[ 0 ].getX() - dataPoints[ 0 ].getX(), - 2 * firstControlPoints[ 0 ].getY() - - dataPoints[ 0 ].getY() ); - return new Pair( firstControlPoints, - secondControlPoints ); - } - - // Calculate first Bezier control points - // Right hand side vector - double[] rhs = new double[ n ]; - - // Set right hand side X values - for ( int i = 1; i < n - 1; ++i ) - { - rhs[ i ] = 4 * dataPoints[ i ].getX() - + 2 * dataPoints[ i + 1 ].getX(); - } - rhs[ 0 ] = dataPoints[ 0 ].getX() + 2 * dataPoints[ 1 ].getX(); - rhs[ n - 1 ] = ( 8 * dataPoints[ n - 1 ].getX() - + dataPoints[ n ].getX() ) / 2.0; - // Get first control points X-values - double[] x = GetFirstControlPoints( rhs ); - - // Set right hand side Y values - for ( int i = 1; i < n - 1; ++i ) - { - rhs[ i ] = 4 * dataPoints[ i ].getY() - + 2 * dataPoints[ i + 1 ].getY(); - } - rhs[ 0 ] = dataPoints[ 0 ].getY() + 2 * dataPoints[ 1 ].getY(); - rhs[ n - 1 ] = ( 8 * dataPoints[ n - 1 ].getY() - + dataPoints[ n ].getY() ) / 2.0; - // Get first control points Y-values - double[] y = GetFirstControlPoints( rhs ); - - // Fill output arrays. - firstControlPoints = new Point2D[ n ]; - secondControlPoints = new Point2D[ n ]; - for ( int i = 0; i < n; ++i ) - { - // First control point - firstControlPoints[ i ] = new Point2D( x[ i ], y[ i ] ); - // Second control point - if ( i < n - 1 ) - { - secondControlPoints[ i ] = new Point2D( - 2 * dataPoints[ i + 1 ].getX() - x[ i + 1 ], - 2 * dataPoints[ i + 1 ].getY() - y[ i + 1 ] ); - } else - { - secondControlPoints[ i ] = new Point2D( - ( dataPoints[ n ].getX() + x[ n - 1 ] ) / 2, - ( dataPoints[ n ].getY() + y[ n - 1 ] ) / 2 ); - } - } - return new Pair( firstControlPoints, - secondControlPoints ); - } - - /** - * Solves a tridiagonal system for one of coordinates (x or y) of first - * Bezier control points. - */ - private static double[] GetFirstControlPoints( double[] rhs ) - { - int n = rhs.length; - double[] x = new double[ n ]; // Solution vector. - double[] tmp = new double[ n ]; // Temp workspace. - double b = 2.0; - x[ 0 ] = rhs[ 0 ] / b; - for ( int i = 1; i < n; i++ ) - {// Decomposition and forward substitution. - tmp[ i ] = 1 / b; - b = ( i < n - 1 ? 4.0 : 3.5 ) - tmp[ i ]; - x[ i ] = ( rhs[ i ] - x[ i - 1 ] ) / b; - } - for ( int i = 1; i < n; i++ ) - { - x[ n - i - 1 ] -= tmp[ n - i ] * x[ n - i ]; // Backsubstitution. - } - return x; - } -} \ No newline at end of file diff --git a/src/filter/viewer/FilterView.css b/src/filter/viewer/FilterView.css deleted file mode 100644 index f5ef3421..00000000 --- a/src/filter/viewer/FilterView.css +++ /dev/null @@ -1,39 +0,0 @@ -/* ....Show License.... */ -.chart { - -fx-background-image: url("../../../images/Curve-fitted-background.png"); - -fx-padding: 15 25 15 15; -} -.chart-plot-background { - -fx-background-color: red; - -fx-background-image: url("../../../images/Curve-fitted-chart-background.png"), - url("../../../images/Curve-fitted-graph-gridlines.png"); - -fx-background-size: cover, auto; - -fx-background-repeat: no-repeat, repeat; - -fx-background-position: 0% 0%, 0% 100%; - -fx-border-color: black black transparent transparent; -} -.chart-area-symbol { - -fx-background-color: white; -} -.chart-series-area-line { - -fx-stroke: white; - -fx-stroke-width: 2px; -} -.chart-series-area-fill { - -fx-fill: linear-gradient(to right, white, rgba(255,255,255,0)); - -fx-blend-mode: OVERLAY; -} -.axis { - -fx-tick-mark-visible: false; - -fx-minor-tick-visible: false; - -fx-tick-length: 3; - -fx-minor-tick-length: 0; - -fx-border-color: transparent; - -fx-tick-label-fill: white; -} -.axis:bottom { - -fx-border-color: black transparent transparent transparent; -} -.axis:left { - -fx-border-color: transparent black transparent transparent; -} \ No newline at end of file diff --git a/src/filter/viewer/FilterView.java b/src/filter/viewer/FilterView.java deleted file mode 100644 index 883acf3c..00000000 --- a/src/filter/viewer/FilterView.java +++ /dev/null @@ -1,168 +0,0 @@ -package filter.viewer; - -import org.jtransforms.fft.FloatFFT_1D; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import dsp.filter.FilterFactory; -import dsp.filter.Window.WindowType; -import javafx.collections.ObservableList; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.XYChart; -import javafx.scene.chart.XYChart.Series; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; - -public class FilterView extends BorderPane -{ - private final static Logger mLog = LoggerFactory.getLogger( FilterView.class ); - - private CurveFittedAreaChart mImpulseChart; - private NumberAxis mImpulseXAxis; - private NumberAxis mImpulseYAxis; - private CurveFittedAreaChart mDFTChart; - private NumberAxis mDFTXAxis; - private NumberAxis mDFTYAxis; - private static final int FFT_SIZE = 4096; - private FloatFFT_1D mFFT = new FloatFFT_1D( FFT_SIZE ); - - public FilterView() - { - init(); - - float[] taps = FilterFactory.getNewLowPass( 8000, 1500, 2500, 50, WindowType.HANNING ); - - setImpulseTaps( taps ); - - float[] dft = new float[ FFT_SIZE ]; - System.arraycopy( taps, 0, dft, 0, taps.length ); - - mFFT.realForward( dft ); - - setDFTTaps( convertDFTToDecibel( dft, taps.length ) ); - } - - private void logTaps( float[] taps ) - { - StringBuilder sb = new StringBuilder(); - sb.append( "\n" ); - - for( float tap: taps ) - { - sb.append( "Tap: " + tap + "\n" ); - } - - mLog.debug( sb.toString() ); - } - - private void init() - { - HBox hbox = new HBox(); - - mImpulseXAxis = new NumberAxis( 0.0, 0.5, 1.0 ); - mImpulseYAxis = new NumberAxis( -0.2, 1.2, .1 ); - - mImpulseChart = new CurveFittedAreaChart( mImpulseXAxis, mImpulseYAxis ); - mImpulseChart.setLegendVisible( false ); - mImpulseChart.setHorizontalGridLinesVisible( false ); - mImpulseChart.setVerticalGridLinesVisible(false); - mImpulseChart.setAlternativeColumnFillVisible(false); - mImpulseChart.setAlternativeRowFillVisible(false); - - String css = FilterView.class.getResource( "FilterView.css" ).toExternalForm(); - mImpulseChart.getStylesheets().add( css ); - - hbox.getChildren().add( mImpulseChart ); - HBox.setHgrow( mImpulseChart, Priority.ALWAYS ); - - mDFTXAxis = new NumberAxis( 0.0, FFT_SIZE / 2, FFT_SIZE / 8 ); - mDFTYAxis = new NumberAxis( -200.0, 10.0, 10.0 ); - - mDFTChart = new CurveFittedAreaChart( mDFTXAxis, mDFTYAxis ); - mDFTChart.setLegendVisible( false ); - mDFTChart.setHorizontalGridLinesVisible( false ); - mDFTChart.setVerticalGridLinesVisible(false); - mDFTChart.setAlternativeColumnFillVisible(false); - mDFTChart.setAlternativeRowFillVisible(false); - mDFTChart.getStylesheets().add( css ); - - hbox.getChildren().add( mDFTChart ); - HBox.setHgrow( mDFTChart, Priority.ALWAYS ); - - setCenter( hbox ); - } - - public void setImpulseTaps( float[] taps ) - { - double smallest = 0.0; - double largest = 0.0; - - final XYChart.Series series = new XYChart.Series<>(); - - for( int x = 0; x < taps.length; x++ ) - { - if( taps[ x ] < smallest ) - { - smallest = taps[ x ]; - } - - if( taps[ x ] > largest ) - { - largest = taps[ x ]; - } - - series.getData().add( new XYChart.Data( x, taps[ x ] ) ); - } - - mImpulseXAxis.setLowerBound( 0.0 ); - mImpulseXAxis.setUpperBound( taps.length - 1 ); - mImpulseXAxis.setTickUnit( 1.0 ); - - double padding = Math.abs( largest - smallest ) * .1; - - mImpulseYAxis.setLowerBound( smallest - padding ); - mImpulseYAxis.setUpperBound( largest + padding ); - mImpulseYAxis.setTickUnit( padding ); - - ObservableList> list = mImpulseChart.getData(); - list.add( series ); - } - - public void setDFTTaps( float[] taps ) - { - double smallest = 0.0; - - final XYChart.Series series = new XYChart.Series<>(); - - for( int x = 0; x < taps.length; x++ ) - { - if( taps[ x ] < smallest ) - { - smallest = taps[ x ]; - } - - series.getData().add( new XYChart.Data( x, taps[ x ] ) ); - } - - ObservableList> list = mDFTChart.getData(); - list.add( series ); - } - - public float[] convertDFTToDecibel( float[] dft, int tapLength ) - { - float[] decibels = new float[ dft.length / 2 ]; - - int index = 0; - - for( int x = 0; x < decibels.length; x ++ ) - { - index = x * 2; - - decibels[ x ] = 20.0f * (float)Math.log10( ( ( dft[ index ] * dft[ index ] ) + - ( dft[ index + 1 ] * dft[ index + 1 ] ) ) ); - } - - return decibels; - } -} diff --git a/src/filter/viewer/FilterViewer.java b/src/filter/viewer/FilterViewer.java deleted file mode 100644 index 31035233..00000000 --- a/src/filter/viewer/FilterViewer.java +++ /dev/null @@ -1,27 +0,0 @@ -package filter.viewer; - -import javafx.application.Application; -import javafx.scene.Scene; -import javafx.stage.Stage; - -public class FilterViewer extends Application -{ - public FilterViewer() - { - } - - @Override - public void start( Stage primaryStage ) throws Exception - { - Scene scene = new Scene( new FilterView() ); - - primaryStage.setTitle( "Filter Viewer" ); - primaryStage.setScene( scene ); - primaryStage.show(); - } - - public static void main( String[] args ) - { - launch( args ); - } -} diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index d092a459..a90c432d 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -72,7 +72,7 @@ public class DFTProcessor implements Listener, private DFTSize mDFTSize = DFTSize.FFT04096; private DFTSize mNewDFTSize = DFTSize.FFT04096; - private float[] mWindow; + private double[] mWindow; /* The Cosine and Hanning windows seem to offer the best spectral display * with minimal bin leakage/smearing */