From a07c02ed404f9e68b596abc7359ea56b8c518840 Mon Sep 17 00:00:00 2001 From: Maximilian Stiefel Date: Sun, 27 Aug 2017 15:53:29 +0200 Subject: [PATCH] Tested FFT library. --- software/app/signal_process.cpp | 31 +++- software/app/signal_process.h | 3 +- software/include/hardware.h | 9 +- software/include/signal_processing.h | 16 +++ software/libs/arduinoFFT/arduinoFFT.cpp | 184 ++++++++++++++++++++++++ software/libs/arduinoFFT/arduinoFFT.h | 76 ++++++++++ software/libs/arduinoFFT/defs.h | 90 ++++++++++++ software/libs/arduinoFFT/types.h | 65 +++++++++ 8 files changed, 469 insertions(+), 5 deletions(-) create mode 100644 software/include/signal_processing.h create mode 100644 software/libs/arduinoFFT/arduinoFFT.cpp create mode 100644 software/libs/arduinoFFT/arduinoFFT.h create mode 100644 software/libs/arduinoFFT/defs.h create mode 100644 software/libs/arduinoFFT/types.h diff --git a/software/app/signal_process.cpp b/software/app/signal_process.cpp index f817681..d1e83b2 100644 --- a/software/app/signal_process.cpp +++ b/software/app/signal_process.cpp @@ -9,6 +9,9 @@ #include #include #include +#include +#include + //TODO remove this namespace rijnfel { @@ -33,8 +36,34 @@ void cSignalProcess::ReceiveCallback(void* i_data, cDataReceiver* i_provider) { } void cSignalProcess::process(uint32_t* io_array, size_t size) { + //Vars + arduinoFFT FFT; + double vReal[SAMPLES]; + double vImag[SAMPLES]; + + //Action Serial.printf("Size:%d\n", size); - //Do whatever here really + for(int i = 0; i < SAMPLES; i++) + { + vReal[i] = (double)(io_array[i]); + } + Serial.printf("Casted.\n"); + FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); + Serial.printf("Windowed.\n"); + FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); + Serial.printf("Computed.\n"); + print(vReal); +} + +void cSignalProcess::print(double* vData) +{ + for(int i = 0; i < SAMPLES; i++) + { + if(i == SAMPLES-1) + Serial.printf("%f\n", vData[i]); + else + Serial.printf("%f, ", vData[i]); + } } } /* namespace rijnfel */ diff --git a/software/app/signal_process.h b/software/app/signal_process.h index 3ce3fbd..f811fa3 100644 --- a/software/app/signal_process.h +++ b/software/app/signal_process.h @@ -8,6 +8,7 @@ #ifndef APP_SIGNAL_PROCESS_H_ #define APP_SIGNAL_PROCESS_H_ #include +#include namespace rijnfel { class cSignalProcess: public cDataSink { @@ -22,8 +23,8 @@ protected: uint32_t * m_currentBuffer; uint32_t m_currentIndex; uint32_t m_currentMaxIndex; + void print(double* vData); }; - } /* namespace rijnfel */ #endif /* APP_SIGNAL_PROCESS_H_ */ diff --git a/software/include/hardware.h b/software/include/hardware.h index d49db20..9a323ca 100644 --- a/software/include/hardware.h +++ b/software/include/hardware.h @@ -11,6 +11,8 @@ #ifndef APP_HARDWARE_H_ #define APP_HARDWARE_H_ +#include + #define REV_1 //-------------------------------------I2C ADRESSES--------------------------------------------------------------------------- @@ -22,9 +24,10 @@ static const uint16_t CURR_MAX_UAMP = 10000; // 10 mA max. static const uint16_t VREF_DAC = 3300; // in mV static const uint16_t RESOLUTION_DAC = 1024; -static const int HUB_PERIOD = 800; -static const int ADC_TIMEBASE = 800; -static const int ADC_PERIOD = 800; +static const int HUB_PERIOD = 500; +static const int ADC_PERIOD = 500; // f_samp = 2 kHz +static const int ADC_TIMEBASE = ADC_PERIOD * SAMPLES * 4; + #define LED_PIN 2 // GPIO2 diff --git a/software/include/signal_processing.h b/software/include/signal_processing.h new file mode 100644 index 0000000..b04a9e0 --- /dev/null +++ b/software/include/signal_processing.h @@ -0,0 +1,16 @@ +//---------------------------------------------------------------------------------------------------------------------------- +// Project: Uppsense +// Name: signal_processing.h +// Author: Maximilian Stiefel +// Date: 27.08.2017 +// +// Description: +// +//---------------------------------------------------------------------------------------------------------------------------- + +#ifndef APP_SIGNAL_PROCESSING_H_ +#define APP_SIGNAL_PROCESSING_H_ + +const uint16_t SAMPLES = 128; + +#endif /* APP_SIGNAL_PROCESING_H_ */ diff --git a/software/libs/arduinoFFT/arduinoFFT.cpp b/software/libs/arduinoFFT/arduinoFFT.cpp new file mode 100644 index 0000000..8e054c6 --- /dev/null +++ b/software/libs/arduinoFFT/arduinoFFT.cpp @@ -0,0 +1,184 @@ +/* + + FFT libray + Copyright (C) 2010 Didier Longueville + Copyright (C) 2014 Enrique Condes + + This program 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. + + This program 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 this program. If not, see . + +*/ + +#include "arduinoFFT.h" + +arduinoFFT::arduinoFFT(void) +{ +/* Constructor */ +} + +arduinoFFT::~arduinoFFT(void) +{ +/* Destructor */ +} + +uint8_t arduinoFFT::Revision(void) +{ + return(FFT_LIB_REV); +} + +void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t dir) +{ + Compute(vReal, vImag, samples, Exponent(samples), dir); +} + +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 */ + 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]); + } + uint16_t k = (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 < 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 < samples; i += l2) { + uint16_t i1 = i + l1; + double t1 = u1 * vReal[i1] - u2 * vImag[i1]; + double t2 = u1 * vImag[i1] + u2 * vReal[i1]; + vReal[i1] = vReal[i] - t1; + vImag[i1] = vImag[i] - t2; + vReal[i] += t1; + 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 < samples; i++) { + vReal[i] /= samples; + vImag[i] /= samples; + } + } +} + +void arduinoFFT::ComplexToMagnitude(double *vReal, double *vImag, uint16_t samples) +{ +/* vM is half the size of vReal and vImag */ + for (uint16_t i = 0; i < samples; i++) { + vReal[i] = sqrt(sq(vReal[i]) + sq(vImag[i])); + } +} + +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 */ + 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 */ + 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) { + vData[i] *= weighingFactor; + vData[samples - (i + 1)] *= weighingFactor; + } + else { + vData[i] /= weighingFactor; + vData[samples - (i + 1)] /= weighingFactor; + } + } +} + +double arduinoFFT::MajorPeak(double *vD, uint16_t samples, double samplingFrequency) +{ + double maxY = 0; + uint16_t IndexOfMaxY = 0; + 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]; + IndexOfMaxY = i; + } + } + } + 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 */ + return(interpolatedX); +} + +/* Private functions */ + +void arduinoFFT::Swap(double *x, double *y) +{ + double temp = *x; + *x = *y; + *y = temp; +} + +uint8_t arduinoFFT::Exponent(uint16_t value) +{ + /* Calculates the base 2 logarithm of a value */ + uint8_t result = 0; + while (((value >> result) & 1) != 1) result++; + return(result); +} diff --git a/software/libs/arduinoFFT/arduinoFFT.h b/software/libs/arduinoFFT/arduinoFFT.h new file mode 100644 index 0000000..59d9e75 --- /dev/null +++ b/software/libs/arduinoFFT/arduinoFFT.h @@ -0,0 +1,76 @@ +/* + + FFT libray + Copyright (C) 2010 Didier Longueville + Copyright (C) 2014 Enrique Condes + + This program 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. + + This program 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 this program. If not, see . + +*/ + +#ifndef arduinoFFT_h /* Prevent loading library twice */ +#define arduinoFFT_h +#ifdef ARDUINO + #if ARDUINO >= 100 + #include "Arduino.h" + #else + #include "WProgram.h" /* This is where the standard Arduino code lies */ + #endif +#else + #include + #include + #include + #include + #include "defs.h" + #include "types.h" +#endif + +#define FFT_LIB_REV 0x02c +/* Custom constants */ +#define FFT_FORWARD 0x01 +#define FFT_REVERSE 0x00 +/* Windowing type */ +#define FFT_WIN_TYP_RECTANGLE 0x00 /* rectangle (Box car) */ +#define FFT_WIN_TYP_HAMMING 0x01 /* hamming */ +#define FFT_WIN_TYP_HANN 0x02 /* hann */ +#define FFT_WIN_TYP_TRIANGLE 0x03 /* triangle (Bartlett) */ +#define FFT_WIN_TYP_BLACKMAN 0x04 /* blackmann */ +#define FFT_WIN_TYP_FLT_TOP 0x05 /* flat top */ +#define FFT_WIN_TYP_WELCH 0x06 /* welch */ +/*Mathematial constants*/ +#define twoPi 6.28318531 +#define fourPi 12.56637061 + +class arduinoFFT { +public: + /* Constructor */ + arduinoFFT(void); + /* Destructor */ + ~arduinoFFT(void); + /* Functions */ + 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); + 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); + +private: + /* Functions */ + void Swap(double *x, double *y); + +}; + +#endif diff --git a/software/libs/arduinoFFT/defs.h b/software/libs/arduinoFFT/defs.h new file mode 100644 index 0000000..2422b24 --- /dev/null +++ b/software/libs/arduinoFFT/defs.h @@ -0,0 +1,90 @@ +/*! \file avrlibdefs.h \brief AVRlib global defines and macros. */ +//***************************************************************************** +// +// File Name : 'avrlibdefs.h' +// Title : AVRlib global defines and macros include file +// Author : Pascal Stang +// Created : 7/12/2001 +// Revised : 9/30/2002 +// Version : 1.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This include file is designed to contain items useful to all +// code files and projects, regardless of specific implementation. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef AVRLIBDEFS_H +#define AVRLIBDEFS_H + +//#define F_CPU 4000000 +#define MEM_TYPE 1 + +// Code compatibility to new AVR-libc +// outb(), inb(), inw(), outw(), BV(), sbi(), cbi(), sei(), cli() +#ifndef outb + #define outb(addr, data) addr = (data) +#endif +#ifndef inb + #define inb(addr) (addr) +#endif +#ifndef outw + #define outw(addr, data) addr = (data) +#endif +#ifndef inw + #define inw(addr) (addr) +#endif +#ifndef BV + #define BV(bit) (1<<(bit)) +#endif +//#ifndef cbi +// #define cbi(reg,bit) reg &= ~(BV(bit)) +//#endif +//#ifndef sbi +// #define sbi(reg,bit) reg |= (BV(bit)) +//#endif +#ifndef cli + #define cli() __asm__ __volatile__ ("cli" ::) +#endif +#ifndef sei + #define sei() __asm__ __volatile__ ("sei" ::) +#endif + +// support for individual port pin naming in the mega128 +// see port128.h for details +#ifdef __AVR_ATmega128__ +// not currently necessary due to inclusion +// of these defines in newest AVR-GCC +// do a quick test to see if include is needed +#ifndef PD0 + //#include "port128.h" +#endif +#endif + +// use this for packed structures +// (this is seldom necessary on an 8-bit architecture like AVR, +// but can assist in code portability to AVR) +#define GNUC_PACKED __attribute__((packed)) + +// port address helpers +#define DDR(x) ((x)-1) // address of data direction register of port x +#define PIN(x) ((x)-2) // address of input register of port x + +// MIN/MAX/ABS macros +#define MIN(a,b) ((ab)?(a):(b)) +#define ABS(x) ((x>0)?(x):(-x)) + +// constants +#define PI 3.14159265359 + +//Math +#define sq(x) ((x)*(x)) +#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) + +#endif diff --git a/software/libs/arduinoFFT/types.h b/software/libs/arduinoFFT/types.h new file mode 100644 index 0000000..c84bf33 --- /dev/null +++ b/software/libs/arduinoFFT/types.h @@ -0,0 +1,65 @@ +//useful things to include in code + +#ifndef TYPES_H +#define TYPES_H + +#ifndef WIN32 + // true/false defines + #define FALSE 0 + #define TRUE -1 +#endif + +// datatype definitions macros +typedef unsigned char u08; +typedef signed char s08; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned long u32; +typedef signed long s32; +typedef unsigned long long u64; +typedef signed long long s64; + +/* use inttypes.h instead +// C99 standard integer type definitions +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned long uint32_t; +typedef signed long int32_t; +typedef unsigned long uint64_t; +typedef signed long int64_t; +*/ +// maximum value that can be held +// by unsigned data types (8,16,32bits) +#define MAX_U08 255 +#define MAX_U16 65535 +#define MAX_U32 4294967295 + +// maximum values that can be held +// by signed data types (8,16,32bits) +#define MIN_S08 -128 +#define MAX_S08 127 +#define MIN_S16 -32768 +#define MAX_S16 32767 +#define MIN_S32 -2147483648 +#define MAX_S32 2147483647 + +#ifndef WIN32 + // more type redefinitions + typedef unsigned char BOOL; + typedef unsigned char BYTE; + typedef unsigned int WORD; + typedef unsigned long DWORD; + + typedef unsigned char UCHAR; + typedef unsigned int UINT; + typedef unsigned short USHORT; + typedef unsigned long ULONG; + + typedef char CHAR; + typedef int INT; + typedef long LONG; +#endif + +#endif