/** * Global Sensor Networks (GSN) Source Code * Copyright (c) 2006-2016, 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 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 ch.epfl.gsn.beans.DataField; import ch.epfl.gsn.beans.DataTypes; import ch.epfl.gsn.beans.StreamElement; 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()); } } }