- Descriptor for Plaformio
- New API with simpled functions
- Fixes on rare bugs
pull/21/head v1.4
Enrique Condes 2018-02-11 04:01:14 +08:00
rodzic de7c8e447c
commit d8c22a897e
6 zmienionych plików z 227 dodań i 91 usunięć

Wyświetl plik

@ -11,6 +11,10 @@ Tested on Arduino 1.6.11
### Installation on Arduino
Use the Arduino Library Manager to install and keep it updated. Just look for arduinoFFT. Only for Arduino 1.5+
### Manual installation on Arduino
To install this library, just place this entire folder as a subfolder in your Arduino installation
When installed, this library should look like:
@ -37,23 +41,28 @@ select arduinoFTT. This will add a corresponding line to the top of your sketch
* Document windowing functions advantages and disadvantages.
* Optimize usage and arguments.
* Add new windowing functions.
* Spectrum table?
<del>* Spectrum table? </del>
### API
* **arduinoFFT**(void);
* **arduinoFFT**(double *vReal, double *vImag, uint16_t samples, double samplingFrequency);
Constructor
* **~arduinoFFT**(void);
Destructor
* **ComplexToMagnitude**(double *vReal, double *vImag, uint16_t samples);
* **ComplexToMagnitude**();
* **Compute**(double *vReal, double *vImag, uint16_t samples, uint8_t dir);
Calculates the power value according to **Exponent** and calcuates the Fast Fourier Transform.
* **Compute**(double *vReal, double *vImag, uint16_t samples, uint8_t power, uint8_t dir);
* **Compute**(uint8_t dir);
Calcuates the Fast Fourier Transform.
* **MajorPeak**(double *vD, uint16_t samples, double samplingFrequency);
* **MajorPeak**();
Looks for and returns the frequency of the biggest spike in the analyzed signal.
* **Revision**(void);
Returns the library revision.
* **Windowing**(double *vData, uint16_t samples, uint8_t windowType, uint8_t dir);
* **Windowing**(uint8_t windowType, uint8_t dir);
Performs a windowing function on the values array. The possible windowing options are:
* FFT_WIN_TYP_RECTANGLE
* FFT_WIN_TYP_HAMMING

Wyświetl plik

@ -1,3 +1,9 @@
02/10/18 v1.4
Transition version. Minor optimization to functions. New API. Deprecation of old functions.
12/06/18 v1.3
Add support for mbed development boards.
09/04/17 v1.2.3
Finally solves the issue of Arduino IDE not correctly detecting and highlighting the keywords.

26
library.json 100644
Wyświetl plik

@ -0,0 +1,26 @@
{
"name": "arduinoFFT",
"keywords": "FFT, Fourier, FDT, frequency",
"description": "A library for implementing floating point Fast Fourier Transform calculations.",
"repository":
{
"type": "git",
"url": "https://github.com/kosme/arduinoFFT.git"
},
"authors":
[
{
"name": "Enrique Condes",
"email": "enrique@shapeoko.com",
"maintainer": true
},
{
"name": "Didier Longueville",
"url": "http://www.arduinoos.com/",
"email": "contact@arduinoos.com"
}
],
"version": "1.4",
"frameworks": ["arduino","mbed","espidf"],
"platforms": "*"
}

Wyświetl plik

@ -1,6 +1,6 @@
name=arduinoFFT
version=1.3
author=kosme <enrique@shapeoko.com>
version=1.4
author=Enrique Condes <enrique@shapeoko.com>
maintainer=Enrique Condes <enrique@shapeoko.com>
sentence=A library for implementing floating point Fast Fourier Transform calculations on Arduino.
paragraph=With this library you can calculate the frequency of a sampled signal.

Wyświetl plik

@ -22,13 +22,22 @@
#include "arduinoFFT.h"
arduinoFFT::arduinoFFT(void)
{
/* Constructor */
{ // Constructor
#warning("This method is deprecated and will be removed on future revisions.")
}
arduinoFFT::arduinoFFT(double *vReal, double *vImag, uint16_t samples, double samplingFrequency)
{// Constructor
this->_vReal = vReal;
this->_vImag = vImag;
this->_samples = samples;
this->_samplingFrequency = samplingFrequency;
this->_power = Exponent(samples);
}
arduinoFFT::~arduinoFFT(void)
{
/* Destructor */
// Destructor
}
uint8_t arduinoFFT::Revision(void)
@ -38,18 +47,75 @@ uint8_t arduinoFFT::Revision(void)
void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t dir)
{
#warning("This method is deprecated and will be removed on future revisions.")
Compute(vReal, vImag, samples, Exponent(samples), dir);
}
void arduinoFFT::Compute(uint8_t dir)
{// Computes in-place complex-to-complex FFT /
// Reverse bits /
uint16_t j = 0;
for (uint16_t i = 0; i < (this->_samples - 1); i++) {
if (i < j) {
Swap(&this->_vReal[i], &this->_vReal[j]);
if(dir==FFT_REVERSE)
Swap(&this->_vImag[i], &this->_vImag[j]);
}
uint16_t k = (this->_samples >> 1);
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
// Compute the FFT /
double c1 = -1.0;
double c2 = 0.0;
uint16_t l2 = 1;
for (uint8_t l = 0; (l < this->_power); l++) {
uint16_t l1 = l2;
l2 <<= 1;
double u1 = 1.0;
double u2 = 0.0;
for (j = 0; j < l1; j++) {
for (uint16_t i = j; i < this->_samples; i += l2) {
uint16_t i1 = i + l1;
double t1 = u1 * this->_vReal[i1] - u2 * this->_vImag[i1];
double t2 = u1 * this->_vImag[i1] + u2 * this->_vReal[i1];
this->_vReal[i1] = this->_vReal[i] - t1;
this->_vImag[i1] = this->_vImag[i] - t2;
this->_vReal[i] += t1;
this->_vImag[i] += t2;
}
double z = ((u1 * c1) - (u2 * c2));
u2 = ((u1 * c2) + (u2 * c1));
u1 = z;
}
c2 = sqrt((1.0 - c1) / 2.0);
if (dir == FFT_FORWARD) {
c2 = -c2;
}
c1 = sqrt((1.0 + c1) / 2.0);
}
// Scaling for reverse transform /
if (dir != FFT_FORWARD) {
for (uint16_t i = 0; i < this->_samples; i++) {
this->_vReal[i] /= this->_samples;
this->_vImag[i] /= this->_samples;
}
}
}
void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t power, uint8_t dir)
{
/* Computes in-place complex-to-complex FFT */
/* Reverse bits */
{ // Computes in-place complex-to-complex FFT
// Reverse bits
#warning("This method is deprecated and will be removed on future revisions.")
uint16_t j = 0;
for (uint16_t i = 0; i < (samples - 1); i++) {
if (i < j) {
Swap(&vReal[i], &vReal[j]);
Swap(&vImag[i], &vImag[j]);
if(dir==FFT_REVERSE)
Swap(&vImag[i], &vImag[j]);
}
uint16_t k = (samples >> 1);
while (k <= j) {
@ -58,7 +124,7 @@ void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t
}
j += k;
}
/* Compute the FFT */
// Compute the FFT
double c1 = -1.0;
double c2 = 0.0;
uint16_t l2 = 1;
@ -87,7 +153,7 @@ void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t
}
c1 = sqrt((1.0 + c1) / 2.0);
}
/* Scaling for reverse transform */
// Scaling for reverse transform
if (dir != FFT_FORWARD) {
for (uint16_t i = 0; i < samples; i++) {
vReal[i] /= samples;
@ -96,44 +162,95 @@ void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t
}
}
void arduinoFFT::ComplexToMagnitude()
{ // vM is half the size of vReal and vImag
for (uint16_t i = 0; i < this->_samples; i++) {
this->_vReal[i] = sqrt(sq(this->_vReal[i]) + sq(this->_vImag[i]));
}
}
void arduinoFFT::ComplexToMagnitude(double *vReal, double *vImag, uint16_t samples)
{
/* vM is half the size of vReal and vImag */
{ // vM is half the size of vReal and vImag
#warning("This method is deprecated and will be removed on future revisions.")
for (uint16_t i = 0; i < samples; i++) {
vReal[i] = sqrt(sq(vReal[i]) + sq(vImag[i]));
}
}
void arduinoFFT::Windowing(uint8_t windowType, uint8_t dir)
{// Weighing factors are computed once before multiple use of FFT
// The weighing function is symetric; half the weighs are recorded
double samplesMinusOne = (double(this->_samples) - 1.0);
for (uint16_t i = 0; i < (this->_samples >> 1); i++) {
double indexMinusOne = double(i);
double ratio = (indexMinusOne / samplesMinusOne);
double weighingFactor = 1.0;
// Compute and record weighting factor
switch (windowType) {
case FFT_WIN_TYP_RECTANGLE: // rectangle (box car)
weighingFactor = 1.0;
break;
case FFT_WIN_TYP_HAMMING: // hamming
weighingFactor = 0.54 - (0.46 * cos(twoPi * ratio));
break;
case FFT_WIN_TYP_HANN: // hann
weighingFactor = 0.54 * (1.0 - cos(twoPi * ratio));
break;
case FFT_WIN_TYP_TRIANGLE: // triangle (Bartlett)
weighingFactor = 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
break;
case FFT_WIN_TYP_BLACKMAN: // blackmann
weighingFactor = 0.42323 - (0.49755 * (cos(twoPi * ratio))) + (0.07922 * (cos(fourPi * ratio)));
break;
case FFT_WIN_TYP_FLT_TOP: // flat top
weighingFactor = 0.2810639 - (0.5208972 * cos(twoPi * ratio)) + (0.1980399 * cos(fourPi * ratio));
break;
case FFT_WIN_TYP_WELCH: // welch
weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / (samplesMinusOne / 2.0));
break;
}
if (dir == FFT_FORWARD) {
this->_vReal[i] *= weighingFactor;
this->_vReal[this->_samples - (i + 1)] *= weighingFactor;
}
else {
this->_vReal[i] /= weighingFactor;
this->_vReal[this->_samples - (i + 1)] /= weighingFactor;
}
}
}
void arduinoFFT::Windowing(double *vData, uint16_t samples, uint8_t windowType, uint8_t dir)
{
/* Weighing factors are computed once before multiple use of FFT */
/* The weighing function is symetric; half the weighs are recorded */
{// Weighing factors are computed once before multiple use of FFT
// The weighing function is symetric; half the weighs are recorded
#warning("This method is deprecated and will be removed on future revisions.")
double samplesMinusOne = (double(samples) - 1.0);
for (uint16_t i = 0; i < (samples >> 1); i++) {
double indexMinusOne = double(i);
double ratio = (indexMinusOne / samplesMinusOne);
double weighingFactor = 1.0;
/* Compute and record weighting factor */
// Compute and record weighting factor
switch (windowType) {
case FFT_WIN_TYP_RECTANGLE: /* rectangle (box car) */
case FFT_WIN_TYP_RECTANGLE: // rectangle (box car)
weighingFactor = 1.0;
break;
case FFT_WIN_TYP_HAMMING: /* hamming */
case FFT_WIN_TYP_HAMMING: // hamming
weighingFactor = 0.54 - (0.46 * cos(twoPi * ratio));
break;
case FFT_WIN_TYP_HANN: /* hann */
case FFT_WIN_TYP_HANN: // hann
weighingFactor = 0.54 * (1.0 - cos(twoPi * ratio));
break;
case FFT_WIN_TYP_TRIANGLE: /* triangle (Bartlett) */
case FFT_WIN_TYP_TRIANGLE: // triangle (Bartlett)
weighingFactor = 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
break;
case FFT_WIN_TYP_BLACKMAN: /* blackmann */
case FFT_WIN_TYP_BLACKMAN: // blackmann
weighingFactor = 0.42323 - (0.49755 * (cos(twoPi * ratio))) + (0.07922 * (cos(fourPi * ratio)));
break;
case FFT_WIN_TYP_FLT_TOP: /* flat top */
case FFT_WIN_TYP_FLT_TOP: // flat top
weighingFactor = 0.2810639 - (0.5208972 * cos(twoPi * ratio)) + (0.1980399 * cos(fourPi * ratio));
break;
case FFT_WIN_TYP_WELCH: /* welch */
case FFT_WIN_TYP_WELCH: // welch
weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / (samplesMinusOne / 2.0));
break;
}
@ -148,31 +265,36 @@ void arduinoFFT::Windowing(double *vData, uint16_t samples, uint8_t windowType,
}
}
void arduinoFFT::PrintVector(double *vData, uint16_t samples, double samplingFrequency)
double arduinoFFT::MajorPeak()
{
PrintArray(vData,samples, samplingFrequency, SCL_INDEX);
}
void arduinoFFT::PrintSignal(double *vData, uint16_t samples, double samplingFrequency)
{
PrintArray(vData,samples, samplingFrequency, SCL_TIME);
}
void arduinoFFT::PrintSpectrum(double *vData, uint16_t samples, double samplingFrequency)
{
PrintArray(vData,samples, samplingFrequency, SCL_FREQUENCY);
}
void arduinoFFT::PlotSpectrum(double *vData, uint16_t samples, double samplingFrequency)
{
PrintArray(vData,samples, samplingFrequency, SCL_PLOT);
double maxY = 0;
uint16_t IndexOfMaxY = 0;
//If sampling_frequency = 2 * max_frequency in signal,
//value would be stored at position samples/2
for (uint16_t i = 1; i < ((this->_samples >> 1) + 1); i++) {
if ((this->_vReal[i-1] < this->_vReal[i]) && (this->_vReal[i] > this->_vReal[i+1])) {
if (this->_vReal[i] > maxY) {
maxY = this->_vReal[i];
IndexOfMaxY = i;
}
}
}
double delta = 0.5 * ((this->_vReal[IndexOfMaxY-1] - this->_vReal[IndexOfMaxY+1]) / (this->_vReal[IndexOfMaxY-1] - (2.0 * this->_vReal[IndexOfMaxY]) + this->_vReal[IndexOfMaxY+1]));
double interpolatedX = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples-1);
if(IndexOfMaxY==(this->_samples >> 1)) //To improve calculation on edge values
interpolatedX = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples);
// retuned value: interpolated frequency peak apex
return(interpolatedX);
}
double arduinoFFT::MajorPeak(double *vD, uint16_t samples, double samplingFrequency)
{
#warning("This method is deprecated and will be removed on future revisions.")
double maxY = 0;
uint16_t IndexOfMaxY = 0;
for (uint16_t i = 1; i < ((samples >> 1) - 1); i++) {
//If sampling_frequency = 2 * max_frequency in signal,
//value would be stored at position samples/2
for (uint16_t i = 1; i < ((samples >> 1) + 1); i++) {
if ((vD[i-1] < vD[i]) && (vD[i] > vD[i+1])) {
if (vD[i] > maxY) {
maxY = vD[i];
@ -182,19 +304,22 @@ double arduinoFFT::MajorPeak(double *vD, uint16_t samples, double samplingFreque
}
double delta = 0.5 * ((vD[IndexOfMaxY-1] - vD[IndexOfMaxY+1]) / (vD[IndexOfMaxY-1] - (2.0 * vD[IndexOfMaxY]) + vD[IndexOfMaxY+1]));
double interpolatedX = ((IndexOfMaxY + delta) * samplingFrequency) / (samples-1);
/* retuned value: interpolated frequency peak apex */
if(IndexOfMaxY==(samples >> 1)) //To improve calculation on edge values
interpolatedX = ((IndexOfMaxY + delta) * samplingFrequency) / (samples);
// returned value: interpolated frequency peak apex
return(interpolatedX);
}
uint8_t arduinoFFT::Exponent(uint16_t value)
{
/* Calculates the base 2 logarithm of a value */
#warning("This method will not be accessible on future revisions.")
// Calculates the base 2 logarithm of a value
uint8_t result = 0;
while (((value >> result) & 1) != 1) result++;
return(result);
}
/* Private functions */
// Private functions
void arduinoFFT::Swap(double *x, double *y)
{
@ -202,35 +327,3 @@ void arduinoFFT::Swap(double *x, double *y)
*x = *y;
*y = temp;
}
void arduinoFFT::PrintArray(double *vData, uint16_t samples, double samplingFrequency, uint8_t scaleType)
{
uint16_t bufferSize = samples;
if((scaleType == SCL_FREQUENCY)||(scaleType == SCL_PLOT))
bufferSize = bufferSize>>1;
for (uint16_t i = 0; i < bufferSize; i++)
{
double abscissa;
switch (scaleType)
{
case SCL_INDEX:
abscissa = (i * 1.0);
break;
case SCL_TIME:
abscissa = ((i * 1.0) / samplingFrequency);
break;
case SCL_FREQUENCY:
case SCL_PLOT:
abscissa = ((i * 1.0 * samplingFrequency) / samples);
break;
}
if(scaleType!=SCL_PLOT){
Serial.print(abscissa, 6);
if(scaleType==SCL_FREQUENCY)
Serial.print(" Hz");
Serial.print(" ");
}
Serial.println(vData[i], 4);
}
Serial.println();
}

Wyświetl plik

@ -42,10 +42,7 @@
/* Custom constants */
#define FFT_FORWARD 0x01
#define FFT_REVERSE 0x00
#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
#define SCL_PLOT 0x03
/* Windowing type */
#define FFT_WIN_TYP_RECTANGLE 0x00 /* rectangle (Box car) */
#define FFT_WIN_TYP_HAMMING 0x01 /* hamming */
@ -62,26 +59,31 @@ class arduinoFFT {
public:
/* Constructor */
arduinoFFT(void);
arduinoFFT(double *vReal, double *vImag, uint16_t samples, double samplingFrequency);
/* Destructor */
~arduinoFFT(void);
/* Functions */
uint8_t Revision(void);
uint8_t Exponent(uint16_t value);
void ComplexToMagnitude(double *vReal, double *vImag, uint16_t samples);
void Compute(double *vReal, double *vImag, uint16_t samples, uint8_t dir);
void Compute(double *vReal, double *vImag, uint16_t samples, uint8_t power, uint8_t dir);
void PrintVector(double *vData, uint16_t samples, double samplingFrequency);
void PrintSignal(double *vData, uint16_t samples, double samplingFrequency);
void PrintSpectrum(double *vData, uint16_t samples, double samplingFrequency);
void PlotSpectrum(double *vData, uint16_t samples, double samplingFrequency);
double MajorPeak(double *vD, uint16_t samples, double samplingFrequency);
uint8_t Revision(void);
void Windowing(double *vData, uint16_t samples, uint8_t windowType, uint8_t dir);
uint8_t Exponent(uint16_t value);
void ComplexToMagnitude();
void Compute(uint8_t dir);
double MajorPeak();
void Windowing(uint8_t windowType, uint8_t dir);
private:
/* Variables */
uint16_t _samples;
double _samplingFrequency;
double *_vReal;
double *_vImag;
uint8_t _power;
/* Functions */
void Swap(double *x, double *y);
void PrintArray(double *vData, uint16_t samples, double samplingFrequency, uint8_t scaleType);
};
#endif