This commit is contained in:
2025-05-13 00:32:28 +03:00
parent 7323332eac
commit 8e250df54c
123 changed files with 14142 additions and 0 deletions
@@ -0,0 +1,80 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class Drawing
{
//transform parameters to draw the chart;
public static double a, b, x0, y0;
public static LineRenderer linR;
public static void drawChart(Transform tf, double[] X_inputValues, double[]Y_inputValues, string type, float? customYScale = null)
{
linR =tf.GetComponent<LineRenderer>();
//transform parameters to draw the heart rate
a = tf.localScale.x;
b = tf.localScale.y;
x0 = tf.position.x;
y0 = tf.position.y;
//do not resize array
if (type=="time")
{
}
//resize array
else if(type=="frequency")
{
X_inputValues=ArrayResize(X_inputValues, (int)X_inputValues.Length/2);
Y_inputValues=ArrayResize(Y_inputValues, (int)Y_inputValues.Length/2);
}
linR.positionCount = X_inputValues.Length;
double max_X = FastFourierTransform.MaxD(X_inputValues);
double min_X = FastFourierTransform.MinD(X_inputValues);
double max_Y = FastFourierTransform.MaxD(Y_inputValues);
double min_Y = FastFourierTransform.MinD(Y_inputValues);
double dY = max_Y - min_Y;
if (customYScale.HasValue) {
dY = customYScale.Value;
}
//drawing factors
double factorA_X = (x0 + a / 2 - (x0 - a / 2)) / (max_X - min_X + 0.01f);
double factorB_X = factorA_X * (-min_X) + (x0 - a / 2);
double factorA_Y = (y0 + b / 2 - (y0 - b / 2)) / (dY + 0.01f);
double factorB_Y = factorA_Y * (-min_Y) + (y0 - b / 2);
//draw using the lineRender
for (int ii = 0; ii < linR.positionCount; ii++)
{
// Debug.Log(ii);
double xt = X_inputValues[ii] * factorA_X + factorB_X;
double yt = Y_inputValues[ii] * factorA_Y + factorB_Y;
linR.SetPosition(ii, new UnityEngine.Vector3((float)xt, (float)yt, tf.position.z));
}
}
//gets an array and resizes it to a fixed value
public static double[] ArrayResize(double[] a, int Size)
{
double[] temp = new double[Size];
for (int c = 1; c < Mathf.Min(Size, a.Length); c++)
{
temp[c] = a[c];
}
return temp;
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 48b69030bca8c794da64aa2cbef70bd3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 152492
packageName: FFT Fast Fourrier Transform
packageVersion: 1.0
assetPath: Assets/FFT fast fourrier transform/Scripts/Drawing.cs
uploadId: 325757
@@ -0,0 +1,174 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Numerics;
public class Example01_time_to_frequency : MonoBehaviour
{
[Space]
[Header("[!] set fixed time to 0.01f [!]")]
[Header("TIME SIGNAL TO FREQUENCY FFT")]
[Space]
//samplingfrequency
public int samplingFrequency;
//input-output values for the FFT
double[] X_inputValues;
double[] Y_inputValues;
double[] Y_output;
//list with the input values
public List<double> Y_values;
//these are the frequencies, amplitude and phase of the sinus added to the signal
[Space(10)]
[Header("F,A and P must be same lenght")]
[Tooltip("Add different values of amplitude/frequency to change the time response")]
[Space]
public double[] freq;
public double[] Amp;
public double[] phase;
//this is the size of the window of data
public int windowSize = 1024;
//this is used to obtain the frequency with a peak
[Space(10)]
[Header("MOST IMPORTANT FREQUENCY")]
[Tooltip("This corresponds to the peak on the FFT")]
[Space]
public double maxf_obtained;
//counts the steps taken
long count = 0;
//the chart transforms for the time and frequency
[Space(10)]
[Header("CHARTS FOR DRAWING")]
[Space]
public Transform tfTime;
public Transform tfFreq;
void Start()
{
X_inputValues = new double[windowSize];
Y_inputValues = new double[windowSize];
samplingFrequency = (int)(1 / Time.fixedDeltaTime);
for (int ii = 0; ii < windowSize; ii++)
{
Y_values.Add(0);
}
StartCoroutine(time_to_frequency());
}
/// <summary>
/// This corrutine obtains these steps in order:
/// 1) generates a time response using SIN() signals
/// 2) obtains a window of the signal
/// 3) applies the FFT
/// </summary>
IEnumerator time_to_frequency()
{
while (true)
{
// add point to the time chart
double signal = 0;
for (int jj = 0; jj < Amp.Length; jj++)
{
signal+= (double)( Amp[jj] * Mathf.Sin(2 * Mathf.PI * (float)freq[jj] * (float)count *1/(float)samplingFrequency + (float)phase[jj]) );
}
count++;
Y_values.Add(signal);
if (Y_values.Count >= windowSize)
{
//ONE IS THE INPUT VALUES
//this is the window size
for (int ii = 0; ii < windowSize; ii++)
{
X_inputValues[ii] = ii;
X_inputValues[ii] = ii;
Y_inputValues[ii] = (Y_values[ii + Y_values.Count - 1 - windowSize]);
}
//perform complex opterations and set up the arrays
Complex[] inputSignal_Time = new Complex[windowSize];
Complex[] outputSignal_Freq = new Complex[windowSize];
inputSignal_Time = FastFourierTransform.doubleToComplex(Y_inputValues);
//result is the iutput values once DFT has been applied
outputSignal_Freq = FastFourierTransform.FFT(inputSignal_Time,false);
Y_output = new double[windowSize];
//get module of complex number
for (int ii = 0; ii < windowSize; ii++)
{
//Debug.Log(ii);
Y_output[ii] = (double)Complex.Abs(outputSignal_Freq[ii]);
}
// find peak VALUES on the FFT
double MaxPeak = -1000;
int peakIndex = 0;
for (int i = 1; i < Y_output.Length/2 - 1; i++)
{
if (Y_output[i]>MaxPeak)
{
MaxPeak = Y_output[i];
peakIndex = i;
}
}
//store results
maxf_obtained = (double)peakIndex / (double)windowSize/2* (double)samplingFrequency;
}
yield return new WaitForFixedUpdate();
}
}
void FixedUpdate()
{
//draw both charts: time-domain and frequency-domain
Drawing.drawChart(tfTime,X_inputValues, Y_inputValues, "time");
Drawing.drawChart(tfFreq, X_inputValues, Y_output, "frequency");
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f7e2890ad1d354344943df8cd47704e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 152492
packageName: FFT Fast Fourrier Transform
packageVersion: 1.0
assetPath: Assets/FFT fast fourrier transform/Scripts/Example01_time_to_frequency.cs
uploadId: 325757
@@ -0,0 +1,208 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Numerics;
public class Example02_filtering : MonoBehaviour
{
public enum filterMode { low_pass, high_pass };
[Space]
[Header("[!] set fixed time to 0.01f [!]")]
[Header("FILTERING")]
[Space]
//samplingfrequency
public int samplingFrequency;
//input-output values for the FFT
double[] X_inputValues;
double[] Y_inputValues;
double[] Y_output;
double[] Y_inputValues_filtered;
double[] Y_output_filtered;
//list with the input values
public List<double> Y_values;
//these are the frequencies, amplitude and phase of the sinus added to the signal
[Space(10)]
[Header("F,A and P must be same lenght")]
[Tooltip("Add different values of amplitude/frequency to change the time response")]
[Space]
public double[] freq;
public double[] Amp;
public double[] phase;
//this is the size of the window of data
public int windowSize = 1024;
//counts the steps taken
long count = 0;
//
//the chart transforms for the time and frequency
[Space(10)]
[Header("CHARTS FOR DRAWING")]
[Space]
public Transform tfTime;
public Transform tfFreq;
public Transform tfTime_filt;
public Transform tfFreq_filt;
[Space(10)]
[Header("Select filtering type")]
[Space]
public filterMode filtermode;
//this is the filter parameter
public double alpha = 0.2f;
void Start()
{
X_inputValues = new double[windowSize];
Y_inputValues = new double[windowSize];
samplingFrequency = (int)(1 / Time.fixedDeltaTime);
for (int ii = 0; ii < windowSize; ii++)
{
Y_values.Add(0);
}
StartCoroutine(filterSignal());
}
/// <summary>
/// This corrutine obtains these steps in order:
/// 1) generates a time response using SIN() signals
/// 2) obtains a window of the signal
/// 3) applies the FFT
/// 4) filters the signal
/// 5) applies FFT of filtered signal
/// </summary>
IEnumerator filterSignal()
{
while (true)
{
// add point to the time chart
double signal = 0;
for (int jj = 0; jj < Amp.Length; jj++)
{
signal += (double)(Amp[jj] * Mathf.Sin(2 * Mathf.PI * (float)freq[jj] * (float)count * 1 / (float)samplingFrequency + (float)phase[jj]));
}
count++;
Y_values.Add(signal);
if (Y_values.Count >= windowSize)
{
//ONE IS THE INPUT VALUES
//this is the window size
for (int ii = 0; ii < windowSize; ii++)
{
X_inputValues[ii] = ii;
X_inputValues[ii] = ii;
Y_inputValues[ii] = (Y_values[ii + Y_values.Count - 1 - windowSize]);
}
//perform complex opterations and set up the arrays
Complex[] inputSignal_Time = new Complex[windowSize];
Complex[] outputSignal_Freq = new Complex[windowSize];
inputSignal_Time = FastFourierTransform.doubleToComplex(Y_inputValues);
//result is the iutput values once FFT has been applied
outputSignal_Freq = FastFourierTransform.FFT(inputSignal_Time, false);
Y_output = new double[windowSize];
//get module of complex number
for (int ii = 0; ii < windowSize; ii++)
{
//Debug.Log(ii);
Y_output[ii] = (double)Complex.Abs(outputSignal_Freq[ii]);
}
//filter process
if (filtermode==filterMode.low_pass)
{
Y_inputValues_filtered = Filters.low_pass_band(Y_inputValues, alpha);
}
else if (filtermode == filterMode.high_pass)
{
Y_inputValues_filtered = Filters.high_pass_band(Y_inputValues, alpha);
}
//recalculate FFT of filtered
//perform complex opterations and set up the arrays
inputSignal_Time = new Complex[windowSize];
outputSignal_Freq = new Complex[windowSize];
inputSignal_Time = FastFourierTransform.doubleToComplex(Y_inputValues_filtered);
//result is the iutput values once FFT has been applied
outputSignal_Freq = FastFourierTransform.FFT(inputSignal_Time, false);
Y_output_filtered = new double[windowSize];
//get module of complex number
for (int ii = 0; ii < windowSize; ii++)
{
//Debug.Log(ii);
Y_output_filtered[ii] = (double)Complex.Abs(outputSignal_Freq[ii]);
}
}
yield return new WaitForFixedUpdate();
}
}
void FixedUpdate()
{
//draw both charts: time-domain and frequency-domain
Drawing.drawChart(tfTime, X_inputValues, Y_inputValues, "time");
Drawing.drawChart(tfFreq, X_inputValues, Y_output, "frequency");
//draw both charts: time-domain and frequency-domain
Drawing.drawChart(tfTime_filt, X_inputValues, Y_inputValues_filtered, "time");
Drawing.drawChart(tfFreq_filt, X_inputValues, Y_output_filtered, "frequency");
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 867c2404c3444e14da2df1dd6800be23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 152492
packageName: FFT Fast Fourrier Transform
packageVersion: 1.0
assetPath: Assets/FFT fast fourrier transform/Scripts/Example02_filtering.cs
uploadId: 325757
@@ -0,0 +1,139 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Numerics;
public class Example03_soundTreatment : MonoBehaviour
{
[Space]
[Header("[!] set fixed time to 0.01f [!]")]
[Header("sound treatment")]
[Space]
//samplingfrequency
public int samplingFrequency;
//input-output values for the FFT
double [] X_inputValues;
double [] Y_inputValues;
double[] Y_output;
//list with the input values
public List<double> Y_values;
//these are the frequencies, amplitude and phase of the sinus added to the signal
[Space(10)]
[Header("This is the noise to analyse")]
[Space]
public AudioClip audioC;
public AudioSource audioS;
//counts the steps taken
long count = 0;
//the chart transforms for the time and frequency
[Space(10)]
[Header("CHARTS FOR DRAWING")]
[Space]
public Transform tfTime;
public Transform tfFreq;
//window size
public long windowSize;
void Start()
{
windowSize = audioS.clip.samples * audioS.clip.channels;
audioS.clip = audioC;
audioS.Play();
//initialize
X_inputValues = new double[windowSize];
Y_inputValues = new double[windowSize];
Y_output = new double[windowSize];
StartCoroutine(treatmentOfSound());
}
/// <summary>
/// This corrutine obtains these steps in order:
/// 1) gets the data from the audiosource
/// 2) displays the signal in time domain
/// 3) displays the signal in FFT
/// </summary>
IEnumerator treatmentOfSound()
{
//ONE IS THE INPUT VALUES
float[] samples = new float[windowSize];
audioS.clip.GetData(samples, 0);
//this is the window size
for (int ii = 0; ii < windowSize; ii++)
{
X_inputValues[ii] = ii;
Y_inputValues[ii] =(double)samples[ii];
}
//perform complex opterations and set up the arrays
Complex[] inputSignal_Time = new Complex[windowSize];
Complex[] outputSignal_Freq = new Complex[windowSize];
inputSignal_Time = FastFourierTransform.doubleToComplex(Y_inputValues);
//result is the iutput values once FFT has been applied
outputSignal_Freq = FastFourierTransform.FFT(inputSignal_Time, false);
Y_output = new double[windowSize];
//get module of complex number
for (int ii = 0; ii < windowSize; ii++)
{
//Debug.Log(ii);
Y_output[ii] = (double)Complex.Abs(outputSignal_Freq[ii]);
}
yield return null;
}
void FixedUpdate()
{
//draw both charts: time-domain and frequency-domain
Drawing.drawChart(tfTime, X_inputValues, Y_inputValues, "time");
Drawing.drawChart(tfFreq, X_inputValues, Y_output, "frequency");
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 48de1f8e70e299f469ac55f6789258fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 152492
packageName: FFT Fast Fourrier Transform
packageVersion: 1.0
assetPath: Assets/FFT fast fourrier transform/Scripts/Example03_soundTreatment.cs
uploadId: 325757
@@ -0,0 +1,137 @@
using System;
using UnityEngine;
using System.Numerics;
public class FastFourierTransform : MonoBehaviour
{
/// <summary>
/// A fast Fourier transform (FFT) is an algorithm that computes the discrete Fourier transform (DFT) of a sequence.
/// It converts a signal from its original domain (often time or space) to a representation in the frequency domain
/// </summary>
///
//the FFT returns a complex array of numbers given a input array of complex numbers.
public static Complex[] FFT(Complex[] input, bool invert)
{
//in case there is only one element
if (input.Length == 1)
{
return new Complex[] { input[0] };
}
//for more elements we need to otain the lenght of the input data stream
int length = input.Length;
//half will be the half of the lenght
int half = length / 2;
//this is the result of the FFT
Complex[] result = new Complex[length];
// factor that goes in the
double factorEXP = -2.0 * Math.PI / length;
//in case we want to invert the factor
if (invert)
{
factorEXP = -factorEXP;
}
//
// Cooley–Tukey algorithm. This is a divide and conquer algorithm that recursively breaks down a DFT of any composite size N = N1N2 into many smaller DFTs of sizes N1 and N2,
// it is divided into even and odd components
//
//even
Complex[] evens = new Complex[half];
for (int i = 0; i < half; i++)
{
evens[i] = input[2 * i];
}
//FFT recursive call
Complex[] evenResult = FFT(evens, invert);
//odd
Complex[] odds = evens;
for (int i = 0; i < half; i++)
{
odds[i] = input[2 * i + 1];
}
// FFT recursive call
Complex[] oddResult = FFT(odds, invert);
// final algorithm
// N/2-1 N/2-1
// FFT_k= SUM X_2n ·e^(-2*pi*(2n)*k)/(N/2) + SUM X_2n+1 ·e^(-2*pi*(2n+1)*k)/(N/2)
// 0 0
//
// = Even_k + O_k·e^(-2*pi**k)/(N)
for (int k = 0; k < half; k++)
{
double factor_K = factorEXP * k;
// odd part & this is the second part that is added module 1 argument factor_k
Complex oddComponent = oddResult[k] * new Complex(1*Math.Cos(factor_K), 1*Math.Sin(factor_K));
//first part of the chart
result[k] = evenResult[k] + oddComponent;
//second part of the chart
result[k + half] = evenResult[k] - oddComponent;
}
//reutrn the values (complex). To show FFT we need to display module or "abs" of the complex number
return result;
}
public static Complex[] doubleToComplex(double[] inp)
{
Complex[] outp = new Complex[inp.Length];
//convert to complex number
for (int ii = 0; ii < inp.Length; ii++)
{
outp[ii] = new Complex(inp[ii], 0);
}
return outp;
}
/// <summary>
/// maximum and minimum funtions for double arrays
/// </summary>
public static double MaxD(double[] inp)
{
double outp = -1e10;
for (int ii = 0; ii < inp.Length; ii++)
{
if (inp[ii] > outp)
{
outp = inp[ii];
}
}
return outp;
}
public static double MinD(double[] inp)
{
double outp = 1e10;
for (int ii = 0; ii < inp.Length; ii++)
{
if (inp[ii] < outp)
{
outp = inp[ii];
}
}
return outp;
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 77ef741c346e2a844aeebd52cc140f1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 152492
packageName: FFT Fast Fourrier Transform
packageVersion: 1.0
assetPath: Assets/FFT fast fourrier transform/Scripts/FastFourierTransform.cs
uploadId: 325757
@@ -0,0 +1,38 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class Filters
{
//high pass filter
public static double[] high_pass_band(double[] inp, double alpha)
{
double[] outp = new double[inp.Length];
outp[0] = alpha * inp[0];
for (int jj = 1; jj < inp.Length; jj++)
{
outp[jj] = alpha * (outp[jj - 1]) + alpha * (inp[jj] - inp[jj - 1]);
}
return outp;
}
// low pass filter
public static double[] low_pass_band(double[] inp, double alpha)
{
double[] outp = new double[inp.Length];
outp[0] = alpha * inp[0];
for (int jj = 1; jj < inp.Length; jj++)
{
outp[jj] = alpha * (inp[jj]) + (1-alpha) * (outp[jj - 1]);
}
return outp;
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1d6135e9a9ceeda4ca239d8c421ede11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 152492
packageName: FFT Fast Fourrier Transform
packageVersion: 1.0
assetPath: Assets/FFT fast fourrier transform/Scripts/Filters.cs
uploadId: 325757