Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter blocks using the FFT algo: #9

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.project
85 changes: 85 additions & 0 deletions blocks/BandPassComplexNumber.mon
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//*****************************************************************************
// Title: ComplexType
//
// Copyright (c) 2015-2017 Software AG, Darmstadt, Germany and/or its licensors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//*****************************************************************************

package com.industry.analytics;

/**
* This event provides functionality to support complex number
* (numbers with real and imaginary components) arithmetic.
* This is currently used by the Fast Fourier Transformation
* calculations.
*/
event ComplexType {
decimal real;
decimal imaginary;

static action init( decimal r, decimal i ) returns ComplexType {
ComplexType ret := new ComplexType;
ret.real := r;
ret.imaginary := i;
return ret;
}

static action initFromSequence( sequence<decimal> input ) returns sequence<ComplexType> {
sequence<ComplexType> cOutput := [];

decimal currIn;
for currIn in input {
cOutput.append( ComplexType.init( currIn, 0.0d ) );
}
return cOutput;
}

action cexp() returns ComplexType {
decimal ex := self.real.exp();
return ComplexType( ex * self.imaginary.cos(), ex * self.imaginary.sin() );
}

action add( ComplexType b ) returns ComplexType {
return ComplexType( self.real + b.real, self.imaginary + b.imaginary );
}

action subtract( ComplexType b ) returns ComplexType {
return ComplexType( self.real - b.real, self.imaginary - b.imaginary );
}

action multiply( ComplexType b ) returns ComplexType {
return ComplexType( self.real * b.real - self.imaginary * b.imaginary,
self.real * b.imaginary + self.imaginary * b.real);
}

action abs() returns decimal {
return ( ( self.real * self.real ) + ( self.imaginary * self.imaginary ) ).sqrt();
}

action power() returns decimal {
return ( ( self.real * self.real ) + ( self.imaginary * self.imaginary ) );
}

action _toSimpleString() returns string {
return "("+real.toString()+","+imaginary.toString()+")";
}
action _toString() returns string {
if( imaginary >= 0.0d ) then {
return real.toString()+" +"+imaginary.toString()+"i";
} else {
return real.toString()+" "+imaginary.toString()+"i";
}
}
}
36 changes: 36 additions & 0 deletions blocks/BandPassEventsWindowContents.mon
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* $Copyright (c) 2019 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors.$
* This file is licensed under the Apache 2.0 license - see https://www.apache.org/licenses/LICENSE-2.0
*
*/
/* ***DISCLAIMER***
*
* This is only a sample block and there is no support for this block. This block only supports English. There may be incompatible changes in the future releases without prior notice.
* To use this block, we recommend that you copy it and change the package name. Software AG accepts no responsibility for bug fixes, maintenance or adding new features to this block.
*/

package apamax.analyticsbuilder.samples;

/**
* Window contents.
*
* This is a single value in the <tt>sequence&lt;WindowContents&gt;</tt> property <tt>timeWindow</tt> provided by the <tt>TimeWindow</tt> block.
*/
event WindowContents {
/** Value.
*
* While typically a float, this can be any type that is valid for an Analytics Builder wire.
*/
any value;
/** Time of this data point.
*
* The timestamp of this data point, in standard Apama form (i.e. seconds since the Unix epoch)
*/
float timestamp;
/** Property name for <tt>timeWindow</tt>.
*
* For convenience, the property on the Value on which that <tt>timeWindow</tt> is stored. The value of this property should be
* <tt>sequence&lt;WindowContents&gt;</tt>
*/
constant string WINDOW_PROPERTY_NAME := "timeWindow"; // value will be sequence<WindowContents>
}
227 changes: 227 additions & 0 deletions blocks/BandPassFFT.mon
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@

package com.industry.analytics;

event AmplitudeFrequency {
integer frequency;
decimal amplitude;
}

/**
* This event provides functionality to calculate
* Fast-Fourier Transformations using the Cooley-Tukey algorithm.
* Fourier analysis converts data from a time-domain to a representation
* in the frequency domain (and vice versa).
*/
event FFT {

/** This action calculates the inverse FFT */
static action ifft( sequence<ComplexType> amplitudes ) returns sequence<ComplexType> {
integer N := amplitudes.size();
decimal iN := 1.0d / N.toDecimal();

// Conjugate if imaginary part is not 0
integer i := 0;
ComplexType currVal;
for currVal in amplitudes {
amplitudes[i].imaginary := -currVal.imaginary;
i := i + 1;
}

// Apply fourier transform
amplitudes := cfft( amplitudes );

while i < N {
// Conjugate again
amplitudes[i].imaginary := -amplitudes[i].imaginary;
// Scale
amplitudes[i].real := amplitudes[i].real * iN;
amplitudes[i].imaginary := amplitudes[i].imaginary * iN;

i := i + 1;
}
return amplitudes;
}

/** Calculate the FFT based on simple numbers */
static action fft( sequence<decimal> buffer ) returns sequence<ComplexType> {

// Create the set of complex numbers from the buffer
return cfft( ComplexType.initFromSequence( buffer ) );
}

/** Calculate the FFT based on complex numbers */
static action cfft( sequence<ComplexType> buffer ) returns sequence<ComplexType> {

integer N := buffer.size();
if( N <= 1 ) then {
return buffer;
}

integer hN := N / 2;

sequence<ComplexType> even := [];
sequence<ComplexType> odd := [];

// Set the size of each sequence to be half of the total
even.setSize( hN );
odd.setSize( hN );

// Divide data
integer i := 0;
while i < hN {
even[i] := buffer[i*2];
odd[i] := buffer[i*2+1];

// Increment the index
i := i + 1;
}

// Analyze
even := cfft( even );
odd := cfft( odd );

// Calculate this upfront for performance
decimal a := -2.0d * decimal.PI;

// Combine results
integer k := 0;
while k < hN {
decimal p := (k / N).toDecimal();
decimal term := a * k.toDecimal() / N.toDecimal();
ComplexType t := ComplexType.init(0.0d, term ).cexp().multiply( odd[k] );

buffer[k] := even[ k ].add( t );
odd[k] := buffer[ k ];
buffer[k + hN] := even[ k ].subtract( t );
even[k] := buffer[ k + hN ];

// Increment the index
k := k + 1;
}
return buffer;
}

/** Get the set of amplitude values from a previously calculated set of FFT results */
static action getAmplitudes( sequence<ComplexType> fftResult ) returns sequence<decimal> {
sequence<decimal> magnitude := [];
integer fftSize := fftResult.size();
decimal fftSizeAsDecimal := fftSize.toDecimal();

// Iterate over the FFT values
integer i := 0;
while i < (fftSize / 2) {
// Add to the magnitude sequence for the power spectrum
magnitude.append( 2.0d * (fftResult[i].abs() / fftSizeAsDecimal ) );

// Increment the index
i := i + 1;
}
return magnitude;
}

/** Get the set of frequency values from a previously
* calculated set of FFT results */
static action getFrequencies( integer sampleSize, sequence<ComplexType> fftResult ) returns sequence<integer> {
sequence<integer> frequencies := [];
integer fftSize := fftResult.size();

// Iterate over the FFT values
integer i := 0;
while i < (fftSize / 2) {
// Add to the frequencies sequence
frequencies.append( (i*sampleSize) / fftSize );

// Increment the index
i := i + 1;
}
return frequencies;
}

/** Get the set of amplitude and frequency pairs from
* a previously calculated set of FFT results */
static action getAmplitudesAndFrequencies( integer sampleSize, sequence<ComplexType> fftResult ) returns sequence<AmplitudeFrequency> {
// Calculate the magnitude/amplitude values
sequence<AmplitudeFrequency> ret := [];
integer fftSize := fftResult.size();
decimal fftSizeAsDecimal := fftSize.toDecimal();

// Iterate over the FFT values
integer i := 0;
while i < (fftSize / 2) {
AmplitudeFrequency af := new AmplitudeFrequency;

// Calculate the frequency
af.frequency := (i*sampleSize) / fftSize;

// Calculate the amplitude
af.amplitude := 2.0d * ( fftResult[i].abs() / fftSizeAsDecimal );

// Append the tuple to the sequence
ret.append( af );

// Increment the index
i := i + 1;
}
return ret;
}

/** Get the defined number of highest amplitude and frequency pairs from
* a previously calculated set of FFT results */
static action getTopNAmplitudesAndFrequencies( integer sampleSize, sequence<ComplexType> fftResult,
integer numToRet ) returns sequence<AmplitudeFrequency> {
// Calculate the magnitude/amplitude values
sequence<AmplitudeFrequency> ret := [];
sequence<decimal> amplitudes := getAmplitudes( fftResult );

// Sort in descending order of amplitudes
sequence<decimal> sortedAmps := amplitudes.clone();
sortedAmps.sort();
sortedAmps.reverse();
sortedAmps.setSize( numToRet ); // Truncate the sequence by the number we want to return

integer fftSize := fftResult.size();

// Iterate over the values to get the associated frequencies
decimal currAmplitude;
for currAmplitude in sortedAmps {

integer i := amplitudes.indexOf( currAmplitude );
if( i != -1 ) then {
// Calculate the frequency
AmplitudeFrequency af := new AmplitudeFrequency;
af.frequency := (i*sampleSize) / fftSize;
af.amplitude := currAmplitude;

// Append the tuple to the sequence
ret.append( af );
}
}

return ret;
}

/** Get the frequency with the highest amplitude from
* a previously calculated set of FFT results */
static action getLargestFrequency( integer sampleSize, sequence<ComplexType> fftResult ) returns integer {
// Get the amplitudes
sequence<decimal> magnitudes := getAmplitudes( fftResult );

// Find largest peak in power spectrum
decimal max_magnitude := -decimal.INFINITY;
integer max_index := -1;
integer i := 0;

decimal currVal;
for currVal in magnitudes {
if( currVal > max_magnitude ) then {
max_magnitude := currVal;
max_index := i;
}
// Increment the index
i := i + 1;
}

// Convert index of largest peak to frequency
return (max_index * sampleSize / fftResult.size());
}
}
Loading