ch.slf.FFTRealOneSided.java Source code

Java tutorial

Introduction

Here is the source code for ch.slf.FFTRealOneSided.java

Source

/**
* Global Sensor Networks (GSN) Source Code
* Copyright (c) 2006-2014, Ecole Polytechnique Federale de Lausanne (EPFL)
* 
* This file is part of GSN.
* 
* GSN 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.
* 
* GSN 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 GSN.  If not, see <http://www.gnu.org/licenses/>.
* 
* File: src/ch/slf/FFTRealOneSided.java
*
* @author Timotee Maret
*
*/

package ch.slf;

import gsn.beans.DataField;
import gsn.beans.DataTypes;
import gsn.beans.StreamElement;

import java.io.Serializable;

import org.apache.commons.math.MathException;
import org.apache.commons.math.complex.Complex;
import org.apache.commons.math.transform.FastFourierTransformer;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

/**
 * <p>
 * This Virtual Sensor computes the FFT over an array of data.
 * The input array must contain a power of 2 elements. If any other number of elements is used, the array will be zero-padded to the next power of 2 elements.
 * </p>
 * <p>
 * The output format is a 1 sided real FFT normalised using 1/sqrt(N). 
 * This algorithm uses the Cooley-Tukey FFT method. 
 * The algorithm returns N/2 +1 points where the first point is the DC component.
 * </p>
 * <p>
 * This Virtual Sensor uses the free Apache Math Library for computation.
 * Have a look to <a href="http://commons.apache.org/math/apidocs/index.html">Apache Math Library</a>
 * for its documentation.
 * </p>
 */
public class FFTRealOneSided extends WindowAwareVS {

    private static final String DF = "DF";

    private static final String VALUES = "VALS";

    private static final DataField[] outputStructure = new DataField[] { new DataField(DF, DataTypes.DOUBLE_NAME),
            new DataField(VALUES, "BINARY:text/plain") };

    private static transient Logger logger = LoggerFactory.getLogger(FFTRealOneSided.class);

    private static FastFourierTransformer fft;

    private int fft_size;

    public boolean init() {
        try {
            fft_size = getPredicateValueAsIntWithException("window-size");
            fft = new FastFourierTransformer();
            if (!FastFourierTransformer.isPowerOf2(fft_size)) {
                logger.error("The window size >" + fft_size + "< is not a power of 2.");
                return false;
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * @param values A two dimensional array that contains in first dimension (values[])
     *        the differents steps, and in the second dimension (value[step][]) the different
     *        measures for this step.
     * @param timestamps An array that contains the timestamps in milli seconds at every
     *        step.
     * @return
     */
    public void process(double[] values, long[] timestampsInMSec) {

        if (logger.isDebugEnabled()) {
            logger.debug("INPUT FFT DATA");
            for (int i = 0; i < values.length; i++) {
                logger.debug(values[i] + "\n");
            }
        }

        int windowSize = timestampsInMSec.length;
        logger.debug("Window Size: " + windowSize);

        long deltaTimeStampInSec = (timestampsInMSec[timestampsInMSec.length - 1] - timestampsInMSec[0]) / 1000;
        logger.debug("Delta Time Stamp in s: " + deltaTimeStampInSec);

        double sampling_rate = ((double) windowSize / (double) deltaTimeStampInSec);
        logger.debug("Sampling Rate: " + sampling_rate);

        double df = 1 / (double) deltaTimeStampInSec;
        logger.debug("df: " + df);

        int nbOfPointsToReturn = (windowSize / 2) + 1;
        logger.debug("Number of points to return: " + nbOfPointsToReturn);

        long middleTimeStamp = timestampsInMSec[0] + ((deltaTimeStampInSec / 2) * 1000);

        Complex[] fftResult = null;
        try {

            fftResult = fft.transform2(values);

            double[] realPartFftResult = new double[nbOfPointsToReturn];
            for (int i = 0; i < realPartFftResult.length; i++) {
                realPartFftResult[i] = fftResult[i].getReal();
            }

            //
            Serializable[] dataOut = new Serializable[2];
            // df
            dataOut[0] = df;
            // data
            StringBuilder sb = new StringBuilder();
            sb.append(realPartFftResult[0]);
            for (int i = 1; i < realPartFftResult.length; i++) {
                sb.append("," + realPartFftResult[i]);
            }
            dataOut[1] = sb.toString().getBytes();

            //
            StreamElement se = new StreamElement(outputStructure, dataOut, middleTimeStamp);
            logger.debug("FFT StreamElement produced: " + se);
            dataProduced(se);

        } catch (IllegalArgumentException e) {
            logger.error("Unable to compute the FFT: " + e.getMessage());
        }

        catch (Exception e) {
            logger.error("Unable to compute the FFT: " + e.getMessage());
        }
    }
}