package org.signalml.math.fft; /** * This class is capable of applying window functions ({@link WindowType}) * to the given signal. * * @author Marcin Szumski * @author Piotr Szachewicz */ public class WindowFunction { /** * the type of the window function */ protected WindowType windowType = WindowType.RECTANGULAR; /** * the parameter of the window function */ protected double windowParameter = 0; /** * the calculated weights of the window function; * the signal is multiplied by these weights to obtain windowed data */ protected double[] windowWeights = null; /** * the calculated sum of squares of {@link #windowWeights weights} */ private double windowWeightsSqueredSum = 0; /** * Sets the {@link WindowType type} of the window, the parameter and if any * of these values is different then formerly invalidates calculated weights. * @param type the type of the window * @param parameter the parameter of the window - it is used only for * {@link WindowType#GAUSSIAN GAUSSIAN} and {@link WindowType#KAISER * KAISER} window */ public WindowFunction(WindowType type, double parameter) { this.windowType = type; this.windowParameter = parameter; } public WindowFunction(WindowType type) { this.windowType = type; } /** * Calculates the square of the double value. * @param i the value to be squared * @return the square of the double value */ private double square(double i) { return i * i; } /** * Calculates the weights for the window of a specified length using the * {@link #windowType type} of the window and the {@link #windowParameter * parameter}. Calculated weights are put in {@link #windowWeights}. * <p> * If the weights already exist, no action is taken. * <p> * Apart from that calculates the sum of squared weights and stores the * result in {@link #windowWeightsSqueredSum}. * @param length the length of the window (number of samples) */ protected void calculateWeights(int length) { if (windowWeights != null && windowWeights.length == length) { return; } windowWeights = new double[length]; double n = length; switch (windowType) { case BARTLETT: for (int i = 0; i < length; ++i) { windowWeights[i] = 1.0D - Math.abs((2.0D * i - n + 1.0D) / (n - 1.0D)); } break; case GAUSSIAN: for (int i = 0; i < length; ++i) { windowWeights[i] = Math.exp((-0.5D) * square(windowParameter * ((2.0D * i - n + 1.0D) / (n - 1.0D)))); } break; case HAMMING: for (int i = 0; i < length; ++i) { windowWeights[i] = 0.54D - 0.46D * Math.cos(2.0D * i * Math.PI / (n - 1.0D)); } break; case HANN: for (int i = 0; i < length; ++i) { windowWeights[i] = 0.5D * (1.0D - Math.cos(2.0D * i * Math.PI / (n - 1.0D))); } break; case KAISER: //TODO kaiser window case RECTANGULAR: for (int i = 0; i < length; ++i) { windowWeights[i] = 1.0D; } break; case WELCH: for (int i = 0; i < length; ++i) { windowWeights[i] = 1.0D - square((2.0D * i - n + 1.0D) / (n - 1.0D)); } break; } windowWeightsSqueredSum = 0; for (int i = 0; i < windowWeights.length; ++i) windowWeightsSqueredSum += square(windowWeights[i]); } /** * Returns the sum of squares of window weights. * @return */ public double getWindowWeightsSqueredSum() { return windowWeightsSqueredSum; } /** * Applies the window to the real data and returns the array with * windowed data. * <p> * The array with windowed data is twice as large as the original data * because the (real) values are stored only in cells with even indexes * (odd indexes are reserved for imaginary values). * @param data the data to be windowed * @return the array with windowed data */ public double[] applyWindow(double[] data) { calculateWeights(data.length); double[] windowedData = new double[data.length]; for (int i = 0; i < data.length; ++i) { windowedData[i] = windowWeights[i] * data[i]; } return windowedData; } }