Basic implementation of sampling+FFT+dBV/Hz calculation

This commit is contained in:
Thomas Kolb 2022-07-23 22:50:24 +02:00
commit 1a9ac19d60
13 changed files with 663 additions and 0 deletions

11
.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
.cache/
.pio/
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
data/etc/*
!data/etc/.*
compile_commands.json

5
Makefile Normal file
View file

@ -0,0 +1,5 @@
.pio/: src
pio run
compile_commands.json: src
pio run -t compiledb

0
data/etc/.keep Normal file
View file

68
gen_lut.py Executable file
View file

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import sys
from math import *
preamble = """// This file was auto-generated using gen_lut.py
#include "lut.h"
"""
postamble = """
value_type lookup_sin(int layer, int element) {
return sin_lut[layer][element];
}
value_type lookup_cos(int layer, int element) {
return cos_lut[layer][element];
}
"""
if len(sys.argv) < 2:
print("Argument required: FFT_EXPONENT")
exit(1)
fft_exponent = int(sys.argv[1])
with open("src/lut.c", "w") as ofile:
ofile.write(preamble)
# generate the sin() lookup table
for layer in range(0, fft_exponent):
num_elements = (1 << layer)
ofile.write("value_type sin_lut%i[%i] = {" % (layer, num_elements))
ofile.write("0")
for element in range(1, num_elements):
ofile.write(", %.10f" % sin(-pi * element / num_elements));
ofile.write("};\n\n")
ofile.write("value_type *sin_lut[%i] = {sin_lut0" % fft_exponent);
for i in range(1, fft_exponent):
ofile.write(", sin_lut" + str(i));
ofile.write("};\n");
# generate the cos() lookup table
for layer in range(0, fft_exponent):
num_elements = (1 << layer)
ofile.write("value_type cos_lut%i[%i] = {" % (layer, num_elements))
ofile.write("1")
for element in range(1, num_elements):
ofile.write(", %.10f" % cos(-pi * element / num_elements));
ofile.write("};\n\n")
ofile.write("value_type *cos_lut[%i] = {cos_lut0" % fft_exponent);
for i in range(1, fft_exponent):
ofile.write(", cos_lut" + str(i));
ofile.write("};\n");
ofile.write(postamble)

34
platformio.ini Normal file
View file

@ -0,0 +1,34 @@
;PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nodemcu-32s]
platform = espressif32
board = nodemcu-32s
framework = arduino
monitor_speed = 115200
lib_deps =
AutoAnalogAudio
build_flags = -std=c++11
#[env:esp32-evb]
#platform = espressif32
#board = esp32-evb
#framework = arduino
#
#monitor_speed = 115200
#
#lib_deps =
# AutoAnalogAudio
#
#build_flags = -std=c++11
#board_build.filesystem = littlefs

44
src/Config.cpp Normal file
View file

@ -0,0 +1,44 @@
#include <SPIFFS.h>
#include "Config.h"
Config::Config()
{}
void Config::load(void)
{
// load WLANs
File wlanFile = SPIFFS.open("/etc/wlan", "r");
while(wlanFile.available()) {
String ssid = wlanFile.readStringUntil('\n');
if(!wlanFile.available()) {
Serial.println("/etc/wlan terminated early. Last entry ignored.");
break;
}
String passwd = wlanFile.readStringUntil('\n');
m_wlans.emplace_back(WLAN{ssid.c_str(), passwd.c_str()});
}
wlanFile.close();
// load Challenge-Response data
File authFile = SPIFFS.open("/etc/auth", "r");
if(authFile.available()) {
String passwd = authFile.readStringUntil('\n');
m_crPassword = passwd.c_str();
if(!authFile.available()) {
m_crSalt = "";
} else {
String salt = authFile.readStringUntil('\n');
m_crSalt = salt.c_str();
}
}
authFile.close();
}

36
src/Config.h Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include <string>
class Config
{
public:
struct WLAN
{
std::string ssid;
std::string password;
};
typedef std::vector<WLAN> WLANList;
static Config &instance()
{
static Config theConfig;
return theConfig;
}
void load(void);
const WLANList& getWLANList(void) { return m_wlans; }
const std::string& getCRPassword(void) { return m_crPassword; }
const std::string& getCRSalt(void) { return m_crSalt; }
private:
Config();
WLANList m_wlans;
std::string m_crPassword;
std::string m_crSalt;
};

155
src/fft.c Normal file
View file

@ -0,0 +1,155 @@
#include <math.h>
#include "fft_config.h"
#include "lut.h"
#include "fft.h"
value_type hanning_buffer[BLOCK_LEN];
int lookup_table[BLOCK_LEN];
void init_fft(void) {
int i = 0;
int ri, b;
for(i = 0; i < BLOCK_LEN; i++) {
hanning_buffer[i] = 0.5 * (1 - cos(2 * M_PI * i / BLOCK_LEN));
}
for(i = 0; i < BLOCK_LEN; i++) {
ri = 0;
for(b = 0; b < FFT_EXPONENT; b++)
ri |= ((i >> b) & 1) << (FFT_EXPONENT - b - 1);
lookup_table[i] = ri;
}
}
void complex_to_absolute(value_type *re, value_type *im, value_type *result) {
int i;
for(i = 0; i < DATALEN; i++)
{
result[i] = sqrt( re[i]*re[i] + im[i]*im[i] );
}
}
void apply_hanning(value_type *dftinput) {
int i;
for(i = 0; i < BLOCK_LEN; i++)
dftinput[i] *= hanning_buffer[i];
}
void fft_transform(value_type *samples, value_type *resultRe, value_type *resultIm) {
int i;
int layer, part, element;
int num_parts, num_elements;
int left, right;
value_type x_left_re, x_left_im, x_right_re, x_right_im;
value_type sinval, cosval;
// re-arrange the input array according to the lookup table
// and store it into the real output array (as the input is obviously real).
// zero the imaginary output
for(i = 0; i < BLOCK_LEN; i++)
{
resultRe[lookup_table[i]] = samples[i];
resultIm[i] = 0;
}
// walk layers
for(layer = 0; layer < FFT_EXPONENT; layer++)
{
// number of parts in current layer
num_parts = 1 << (FFT_EXPONENT - layer - 1);
// walk parts of layer
for(part = 0; part < num_parts; part++)
{
// number of elements in current part
num_elements = (1 << layer);
// walk elements in part
for(element = 0; element < num_elements; element++)
{
// calculate index of element in left and right half of part
left = (1 << (layer + 1)) * part + element;
right = left + (1 << layer);
// buffer the elements for the calculation
x_left_re = resultRe[left];
x_left_im = resultIm[left];
x_right_re = resultRe[right];
x_right_im = resultIm[right];
// use lookup table to get sinus and cosinus values for param
//param = -M_PI * element / (1 << layer);
sinval = lookup_sin(layer, element);
cosval = lookup_cos(layer, element);
// combine the values according to a butterfly diagram
resultRe[left] = x_right_re + x_left_re * cosval - x_left_im * sinval;
resultIm[left] = x_right_im + x_left_im * cosval + x_left_re * sinval;
resultRe[right] = x_right_re - x_left_re * cosval + x_left_im * sinval;
resultIm[right] = x_right_im - x_left_im * cosval - x_left_re * sinval;
}
}
}
}
uint32_t find_loudest_frequency(value_type *absFFT) {
int maxPos = 0;
value_type maxVal = 0;
int i;
for(i = 0; i < BLOCK_LEN; i++) {
if(absFFT[i] > maxVal) {
maxPos = i;
maxVal = absFFT[i];
}
}
return (value_type)maxPos * SAMPLE_RATE / BLOCK_LEN;
}
value_type get_energy_in_band(value_type *fft, uint32_t minFreq, uint32_t maxFreq) {
int firstBlock = minFreq * BLOCK_LEN / SAMPLE_RATE;
int lastBlock = maxFreq * BLOCK_LEN / SAMPLE_RATE;
int i;
value_type energy = 0;
for(i = firstBlock; i < lastBlock; i++) {
energy += fft[i];
}
return energy;
}
value_type get_energy_density_in_band(value_type *fft, uint32_t minFreq, uint32_t maxFreq) {
int firstBlock = minFreq * BLOCK_LEN / SAMPLE_RATE;
int lastBlock = maxFreq * BLOCK_LEN / SAMPLE_RATE;
int i;
value_type energy = 0;
for(i = firstBlock; i < lastBlock; i++) {
energy += fft[i];
}
if(firstBlock != lastBlock) {
energy /= (lastBlock - firstBlock);
}
energy *= (float)BLOCK_LEN / (float)SAMPLE_RATE; // normalze to Hz
return energy;
}

16
src/fft.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef FFT_H
#define FFT_H
#include <stdint.h>
#include "fft_config.h"
void init_fft(void);
void complex_to_absolute(value_type *re, value_type *im, value_type *result);
void apply_hanning(value_type *dftinput);
void fft_transform(value_type *samples, value_type *resultRe, value_type *resultIm);
uint32_t find_loudest_frequency(value_type *absFFT);
value_type get_energy_in_band(value_type *fft, uint32_t minFreq, uint32_t maxFreq);
value_type get_energy_density_in_band(value_type *fft, uint32_t minFreq, uint32_t maxFreq);
#endif // FFT_H

12
src/fft_config.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef CONFIG_H
#define CONFIG_H
// FFT transformation parameters
#define FFT_EXPONENT 9 // ATTENTION: when you change this, run gen_lut.py with this value as argument
#define BLOCK_LEN (1 << FFT_EXPONENT) // 2^FFT_EXPONENT
#define SAMPLE_RATE 48000
#define DATALEN (BLOCK_LEN / 2)
typedef float value_type;
#endif // CONFIG_H

51
src/lut.c Normal file
View file

@ -0,0 +1,51 @@
// This file was auto-generated using gen_lut.py
#include "lut.h"
value_type sin_lut0[1] = {0};
value_type sin_lut1[2] = {0, -1.0000000000};
value_type sin_lut2[4] = {0, -0.7071067812, -1.0000000000, -0.7071067812};
value_type sin_lut3[8] = {0, -0.3826834324, -0.7071067812, -0.9238795325, -1.0000000000, -0.9238795325, -0.7071067812, -0.3826834324};
value_type sin_lut4[16] = {0, -0.1950903220, -0.3826834324, -0.5555702330, -0.7071067812, -0.8314696123, -0.9238795325, -0.9807852804, -1.0000000000, -0.9807852804, -0.9238795325, -0.8314696123, -0.7071067812, -0.5555702330, -0.3826834324, -0.1950903220};
value_type sin_lut5[32] = {0, -0.0980171403, -0.1950903220, -0.2902846773, -0.3826834324, -0.4713967368, -0.5555702330, -0.6343932842, -0.7071067812, -0.7730104534, -0.8314696123, -0.8819212643, -0.9238795325, -0.9569403357, -0.9807852804, -0.9951847267, -1.0000000000, -0.9951847267, -0.9807852804, -0.9569403357, -0.9238795325, -0.8819212643, -0.8314696123, -0.7730104534, -0.7071067812, -0.6343932842, -0.5555702330, -0.4713967368, -0.3826834324, -0.2902846773, -0.1950903220, -0.0980171403};
value_type sin_lut6[64] = {0, -0.0490676743, -0.0980171403, -0.1467304745, -0.1950903220, -0.2429801799, -0.2902846773, -0.3368898534, -0.3826834324, -0.4275550934, -0.4713967368, -0.5141027442, -0.5555702330, -0.5956993045, -0.6343932842, -0.6715589548, -0.7071067812, -0.7409511254, -0.7730104534, -0.8032075315, -0.8314696123, -0.8577286100, -0.8819212643, -0.9039892931, -0.9238795325, -0.9415440652, -0.9569403357, -0.9700312532, -0.9807852804, -0.9891765100, -0.9951847267, -0.9987954562, -1.0000000000, -0.9987954562, -0.9951847267, -0.9891765100, -0.9807852804, -0.9700312532, -0.9569403357, -0.9415440652, -0.9238795325, -0.9039892931, -0.8819212643, -0.8577286100, -0.8314696123, -0.8032075315, -0.7730104534, -0.7409511254, -0.7071067812, -0.6715589548, -0.6343932842, -0.5956993045, -0.5555702330, -0.5141027442, -0.4713967368, -0.4275550934, -0.3826834324, -0.3368898534, -0.2902846773, -0.2429801799, -0.1950903220, -0.1467304745, -0.0980171403, -0.0490676743};
value_type sin_lut7[128] = {0, -0.0245412285, -0.0490676743, -0.0735645636, -0.0980171403, -0.1224106752, -0.1467304745, -0.1709618888, -0.1950903220, -0.2191012402, -0.2429801799, -0.2667127575, -0.2902846773, -0.3136817404, -0.3368898534, -0.3598950365, -0.3826834324, -0.4052413140, -0.4275550934, -0.4496113297, -0.4713967368, -0.4928981922, -0.5141027442, -0.5349976199, -0.5555702330, -0.5758081914, -0.5956993045, -0.6152315906, -0.6343932842, -0.6531728430, -0.6715589548, -0.6895405447, -0.7071067812, -0.7242470830, -0.7409511254, -0.7572088465, -0.7730104534, -0.7883464276, -0.8032075315, -0.8175848132, -0.8314696123, -0.8448535652, -0.8577286100, -0.8700869911, -0.8819212643, -0.8932243012, -0.9039892931, -0.9142097557, -0.9238795325, -0.9329927988, -0.9415440652, -0.9495281806, -0.9569403357, -0.9637760658, -0.9700312532, -0.9757021300, -0.9807852804, -0.9852776424, -0.9891765100, -0.9924795346, -0.9951847267, -0.9972904567, -0.9987954562, -0.9996988187, -1.0000000000, -0.9996988187, -0.9987954562, -0.9972904567, -0.9951847267, -0.9924795346, -0.9891765100, -0.9852776424, -0.9807852804, -0.9757021300, -0.9700312532, -0.9637760658, -0.9569403357, -0.9495281806, -0.9415440652, -0.9329927988, -0.9238795325, -0.9142097557, -0.9039892931, -0.8932243012, -0.8819212643, -0.8700869911, -0.8577286100, -0.8448535652, -0.8314696123, -0.8175848132, -0.8032075315, -0.7883464276, -0.7730104534, -0.7572088465, -0.7409511254, -0.7242470830, -0.7071067812, -0.6895405447, -0.6715589548, -0.6531728430, -0.6343932842, -0.6152315906, -0.5956993045, -0.5758081914, -0.5555702330, -0.5349976199, -0.5141027442, -0.4928981922, -0.4713967368, -0.4496113297, -0.4275550934, -0.4052413140, -0.3826834324, -0.3598950365, -0.3368898534, -0.3136817404, -0.2902846773, -0.2667127575, -0.2429801799, -0.2191012402, -0.1950903220, -0.1709618888, -0.1467304745, -0.1224106752, -0.0980171403, -0.0735645636, -0.0490676743, -0.0245412285};
value_type sin_lut8[256] = {0, -0.0122715383, -0.0245412285, -0.0368072229, -0.0490676743, -0.0613207363, -0.0735645636, -0.0857973123, -0.0980171403, -0.1102222073, -0.1224106752, -0.1345807085, -0.1467304745, -0.1588581433, -0.1709618888, -0.1830398880, -0.1950903220, -0.2071113762, -0.2191012402, -0.2310581083, -0.2429801799, -0.2548656596, -0.2667127575, -0.2785196894, -0.2902846773, -0.3020059493, -0.3136817404, -0.3253102922, -0.3368898534, -0.3484186802, -0.3598950365, -0.3713171940, -0.3826834324, -0.3939920401, -0.4052413140, -0.4164295601, -0.4275550934, -0.4386162385, -0.4496113297, -0.4605387110, -0.4713967368, -0.4821837721, -0.4928981922, -0.5035383837, -0.5141027442, -0.5245896827, -0.5349976199, -0.5453249884, -0.5555702330, -0.5657318108, -0.5758081914, -0.5857978575, -0.5956993045, -0.6055110414, -0.6152315906, -0.6248594881, -0.6343932842, -0.6438315429, -0.6531728430, -0.6624157776, -0.6715589548, -0.6806009978, -0.6895405447, -0.6983762494, -0.7071067812, -0.7157308253, -0.7242470830, -0.7326542717, -0.7409511254, -0.7491363945, -0.7572088465, -0.7651672656, -0.7730104534, -0.7807372286, -0.7883464276, -0.7958369046, -0.8032075315, -0.8104571983, -0.8175848132, -0.8245893028, -0.8314696123, -0.8382247056, -0.8448535652, -0.8513551931, -0.8577286100, -0.8639728561, -0.8700869911, -0.8760700942, -0.8819212643, -0.8876396204, -0.8932243012, -0.8986744657, -0.9039892931, -0.9091679831, -0.9142097557, -0.9191138517, -0.9238795325, -0.9285060805, -0.9329927988, -0.9373390119, -0.9415440652, -0.9456073254, -0.9495281806, -0.9533060404, -0.9569403357, -0.9604305194, -0.9637760658, -0.9669764710, -0.9700312532, -0.9729399522, -0.9757021300, -0.9783173707, -0.9807852804, -0.9831054874, -0.9852776424, -0.9873014182, -0.9891765100, -0.9909026354, -0.9924795346, -0.9939069700, -0.9951847267, -0.9963126122, -0.9972904567, -0.9981181129, -0.9987954562, -0.9993223846, -0.9996988187, -0.9999247018, -1.0000000000, -0.9999247018, -0.9996988187, -0.9993223846, -0.9987954562, -0.9981181129, -0.9972904567, -0.9963126122, -0.9951847267, -0.9939069700, -0.9924795346, -0.9909026354, -0.9891765100, -0.9873014182, -0.9852776424, -0.9831054874, -0.9807852804, -0.9783173707, -0.9757021300, -0.9729399522, -0.9700312532, -0.9669764710, -0.9637760658, -0.9604305194, -0.9569403357, -0.9533060404, -0.9495281806, -0.9456073254, -0.9415440652, -0.9373390119, -0.9329927988, -0.9285060805, -0.9238795325, -0.9191138517, -0.9142097557, -0.9091679831, -0.9039892931, -0.8986744657, -0.8932243012, -0.8876396204, -0.8819212643, -0.8760700942, -0.8700869911, -0.8639728561, -0.8577286100, -0.8513551931, -0.8448535652, -0.8382247056, -0.8314696123, -0.8245893028, -0.8175848132, -0.8104571983, -0.8032075315, -0.7958369046, -0.7883464276, -0.7807372286, -0.7730104534, -0.7651672656, -0.7572088465, -0.7491363945, -0.7409511254, -0.7326542717, -0.7242470830, -0.7157308253, -0.7071067812, -0.6983762494, -0.6895405447, -0.6806009978, -0.6715589548, -0.6624157776, -0.6531728430, -0.6438315429, -0.6343932842, -0.6248594881, -0.6152315906, -0.6055110414, -0.5956993045, -0.5857978575, -0.5758081914, -0.5657318108, -0.5555702330, -0.5453249884, -0.5349976199, -0.5245896827, -0.5141027442, -0.5035383837, -0.4928981922, -0.4821837721, -0.4713967368, -0.4605387110, -0.4496113297, -0.4386162385, -0.4275550934, -0.4164295601, -0.4052413140, -0.3939920401, -0.3826834324, -0.3713171940, -0.3598950365, -0.3484186802, -0.3368898534, -0.3253102922, -0.3136817404, -0.3020059493, -0.2902846773, -0.2785196894, -0.2667127575, -0.2548656596, -0.2429801799, -0.2310581083, -0.2191012402, -0.2071113762, -0.1950903220, -0.1830398880, -0.1709618888, -0.1588581433, -0.1467304745, -0.1345807085, -0.1224106752, -0.1102222073, -0.0980171403, -0.0857973123, -0.0735645636, -0.0613207363, -0.0490676743, -0.0368072229, -0.0245412285, -0.0122715383};
value_type *sin_lut[9] = {sin_lut0, sin_lut1, sin_lut2, sin_lut3, sin_lut4, sin_lut5, sin_lut6, sin_lut7, sin_lut8};
value_type cos_lut0[1] = {1};
value_type cos_lut1[2] = {1, 0.0000000000};
value_type cos_lut2[4] = {1, 0.7071067812, 0.0000000000, -0.7071067812};
value_type cos_lut3[8] = {1, 0.9238795325, 0.7071067812, 0.3826834324, 0.0000000000, -0.3826834324, -0.7071067812, -0.9238795325};
value_type cos_lut4[16] = {1, 0.9807852804, 0.9238795325, 0.8314696123, 0.7071067812, 0.5555702330, 0.3826834324, 0.1950903220, 0.0000000000, -0.1950903220, -0.3826834324, -0.5555702330, -0.7071067812, -0.8314696123, -0.9238795325, -0.9807852804};
value_type cos_lut5[32] = {1, 0.9951847267, 0.9807852804, 0.9569403357, 0.9238795325, 0.8819212643, 0.8314696123, 0.7730104534, 0.7071067812, 0.6343932842, 0.5555702330, 0.4713967368, 0.3826834324, 0.2902846773, 0.1950903220, 0.0980171403, 0.0000000000, -0.0980171403, -0.1950903220, -0.2902846773, -0.3826834324, -0.4713967368, -0.5555702330, -0.6343932842, -0.7071067812, -0.7730104534, -0.8314696123, -0.8819212643, -0.9238795325, -0.9569403357, -0.9807852804, -0.9951847267};
value_type cos_lut6[64] = {1, 0.9987954562, 0.9951847267, 0.9891765100, 0.9807852804, 0.9700312532, 0.9569403357, 0.9415440652, 0.9238795325, 0.9039892931, 0.8819212643, 0.8577286100, 0.8314696123, 0.8032075315, 0.7730104534, 0.7409511254, 0.7071067812, 0.6715589548, 0.6343932842, 0.5956993045, 0.5555702330, 0.5141027442, 0.4713967368, 0.4275550934, 0.3826834324, 0.3368898534, 0.2902846773, 0.2429801799, 0.1950903220, 0.1467304745, 0.0980171403, 0.0490676743, 0.0000000000, -0.0490676743, -0.0980171403, -0.1467304745, -0.1950903220, -0.2429801799, -0.2902846773, -0.3368898534, -0.3826834324, -0.4275550934, -0.4713967368, -0.5141027442, -0.5555702330, -0.5956993045, -0.6343932842, -0.6715589548, -0.7071067812, -0.7409511254, -0.7730104534, -0.8032075315, -0.8314696123, -0.8577286100, -0.8819212643, -0.9039892931, -0.9238795325, -0.9415440652, -0.9569403357, -0.9700312532, -0.9807852804, -0.9891765100, -0.9951847267, -0.9987954562};
value_type cos_lut7[128] = {1, 0.9996988187, 0.9987954562, 0.9972904567, 0.9951847267, 0.9924795346, 0.9891765100, 0.9852776424, 0.9807852804, 0.9757021300, 0.9700312532, 0.9637760658, 0.9569403357, 0.9495281806, 0.9415440652, 0.9329927988, 0.9238795325, 0.9142097557, 0.9039892931, 0.8932243012, 0.8819212643, 0.8700869911, 0.8577286100, 0.8448535652, 0.8314696123, 0.8175848132, 0.8032075315, 0.7883464276, 0.7730104534, 0.7572088465, 0.7409511254, 0.7242470830, 0.7071067812, 0.6895405447, 0.6715589548, 0.6531728430, 0.6343932842, 0.6152315906, 0.5956993045, 0.5758081914, 0.5555702330, 0.5349976199, 0.5141027442, 0.4928981922, 0.4713967368, 0.4496113297, 0.4275550934, 0.4052413140, 0.3826834324, 0.3598950365, 0.3368898534, 0.3136817404, 0.2902846773, 0.2667127575, 0.2429801799, 0.2191012402, 0.1950903220, 0.1709618888, 0.1467304745, 0.1224106752, 0.0980171403, 0.0735645636, 0.0490676743, 0.0245412285, 0.0000000000, -0.0245412285, -0.0490676743, -0.0735645636, -0.0980171403, -0.1224106752, -0.1467304745, -0.1709618888, -0.1950903220, -0.2191012402, -0.2429801799, -0.2667127575, -0.2902846773, -0.3136817404, -0.3368898534, -0.3598950365, -0.3826834324, -0.4052413140, -0.4275550934, -0.4496113297, -0.4713967368, -0.4928981922, -0.5141027442, -0.5349976199, -0.5555702330, -0.5758081914, -0.5956993045, -0.6152315906, -0.6343932842, -0.6531728430, -0.6715589548, -0.6895405447, -0.7071067812, -0.7242470830, -0.7409511254, -0.7572088465, -0.7730104534, -0.7883464276, -0.8032075315, -0.8175848132, -0.8314696123, -0.8448535652, -0.8577286100, -0.8700869911, -0.8819212643, -0.8932243012, -0.9039892931, -0.9142097557, -0.9238795325, -0.9329927988, -0.9415440652, -0.9495281806, -0.9569403357, -0.9637760658, -0.9700312532, -0.9757021300, -0.9807852804, -0.9852776424, -0.9891765100, -0.9924795346, -0.9951847267, -0.9972904567, -0.9987954562, -0.9996988187};
value_type cos_lut8[256] = {1, 0.9999247018, 0.9996988187, 0.9993223846, 0.9987954562, 0.9981181129, 0.9972904567, 0.9963126122, 0.9951847267, 0.9939069700, 0.9924795346, 0.9909026354, 0.9891765100, 0.9873014182, 0.9852776424, 0.9831054874, 0.9807852804, 0.9783173707, 0.9757021300, 0.9729399522, 0.9700312532, 0.9669764710, 0.9637760658, 0.9604305194, 0.9569403357, 0.9533060404, 0.9495281806, 0.9456073254, 0.9415440652, 0.9373390119, 0.9329927988, 0.9285060805, 0.9238795325, 0.9191138517, 0.9142097557, 0.9091679831, 0.9039892931, 0.8986744657, 0.8932243012, 0.8876396204, 0.8819212643, 0.8760700942, 0.8700869911, 0.8639728561, 0.8577286100, 0.8513551931, 0.8448535652, 0.8382247056, 0.8314696123, 0.8245893028, 0.8175848132, 0.8104571983, 0.8032075315, 0.7958369046, 0.7883464276, 0.7807372286, 0.7730104534, 0.7651672656, 0.7572088465, 0.7491363945, 0.7409511254, 0.7326542717, 0.7242470830, 0.7157308253, 0.7071067812, 0.6983762494, 0.6895405447, 0.6806009978, 0.6715589548, 0.6624157776, 0.6531728430, 0.6438315429, 0.6343932842, 0.6248594881, 0.6152315906, 0.6055110414, 0.5956993045, 0.5857978575, 0.5758081914, 0.5657318108, 0.5555702330, 0.5453249884, 0.5349976199, 0.5245896827, 0.5141027442, 0.5035383837, 0.4928981922, 0.4821837721, 0.4713967368, 0.4605387110, 0.4496113297, 0.4386162385, 0.4275550934, 0.4164295601, 0.4052413140, 0.3939920401, 0.3826834324, 0.3713171940, 0.3598950365, 0.3484186802, 0.3368898534, 0.3253102922, 0.3136817404, 0.3020059493, 0.2902846773, 0.2785196894, 0.2667127575, 0.2548656596, 0.2429801799, 0.2310581083, 0.2191012402, 0.2071113762, 0.1950903220, 0.1830398880, 0.1709618888, 0.1588581433, 0.1467304745, 0.1345807085, 0.1224106752, 0.1102222073, 0.0980171403, 0.0857973123, 0.0735645636, 0.0613207363, 0.0490676743, 0.0368072229, 0.0245412285, 0.0122715383, 0.0000000000, -0.0122715383, -0.0245412285, -0.0368072229, -0.0490676743, -0.0613207363, -0.0735645636, -0.0857973123, -0.0980171403, -0.1102222073, -0.1224106752, -0.1345807085, -0.1467304745, -0.1588581433, -0.1709618888, -0.1830398880, -0.1950903220, -0.2071113762, -0.2191012402, -0.2310581083, -0.2429801799, -0.2548656596, -0.2667127575, -0.2785196894, -0.2902846773, -0.3020059493, -0.3136817404, -0.3253102922, -0.3368898534, -0.3484186802, -0.3598950365, -0.3713171940, -0.3826834324, -0.3939920401, -0.4052413140, -0.4164295601, -0.4275550934, -0.4386162385, -0.4496113297, -0.4605387110, -0.4713967368, -0.4821837721, -0.4928981922, -0.5035383837, -0.5141027442, -0.5245896827, -0.5349976199, -0.5453249884, -0.5555702330, -0.5657318108, -0.5758081914, -0.5857978575, -0.5956993045, -0.6055110414, -0.6152315906, -0.6248594881, -0.6343932842, -0.6438315429, -0.6531728430, -0.6624157776, -0.6715589548, -0.6806009978, -0.6895405447, -0.6983762494, -0.7071067812, -0.7157308253, -0.7242470830, -0.7326542717, -0.7409511254, -0.7491363945, -0.7572088465, -0.7651672656, -0.7730104534, -0.7807372286, -0.7883464276, -0.7958369046, -0.8032075315, -0.8104571983, -0.8175848132, -0.8245893028, -0.8314696123, -0.8382247056, -0.8448535652, -0.8513551931, -0.8577286100, -0.8639728561, -0.8700869911, -0.8760700942, -0.8819212643, -0.8876396204, -0.8932243012, -0.8986744657, -0.9039892931, -0.9091679831, -0.9142097557, -0.9191138517, -0.9238795325, -0.9285060805, -0.9329927988, -0.9373390119, -0.9415440652, -0.9456073254, -0.9495281806, -0.9533060404, -0.9569403357, -0.9604305194, -0.9637760658, -0.9669764710, -0.9700312532, -0.9729399522, -0.9757021300, -0.9783173707, -0.9807852804, -0.9831054874, -0.9852776424, -0.9873014182, -0.9891765100, -0.9909026354, -0.9924795346, -0.9939069700, -0.9951847267, -0.9963126122, -0.9972904567, -0.9981181129, -0.9987954562, -0.9993223846, -0.9996988187, -0.9999247018};
value_type *cos_lut[9] = {cos_lut0, cos_lut1, cos_lut2, cos_lut3, cos_lut4, cos_lut5, cos_lut6, cos_lut7, cos_lut8};
value_type lookup_sin(int layer, int element) {
return sin_lut[layer][element];
}
value_type lookup_cos(int layer, int element) {
return cos_lut[layer][element];
}

9
src/lut.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef LUT_H
#define LUT_H
#include "fft_config.h"
value_type lookup_sin(int layer, int element);
value_type lookup_cos(int layer, int element);
#endif // LUT_H

222
src/main.cpp Normal file
View file

@ -0,0 +1,222 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <AutoAnalogAudio.h>
#include <ArduinoOTA.h>
#include <SPIFFS.h>
#include "Config.h"
#include "fft_config.h"
//#include "httpserver.h"
extern "C" {
#include "fft.h"
}
#define ENDLESS_LOOP() while(true) { delay(100); }
#define OUTPUT_INTERVAL 1000 // milliseconds
#define SAMPLES_PER_BLOCK 512
static WiFiMulti wiFiMulti;
static bool wiFiConnectedToStation;
static AutoAnalog recorder;
// too large for the stack
static value_type timedomain[SAMPLES_PER_BLOCK];
static value_type fft_re[BLOCK_LEN], fft_im[BLOCK_LEN], fft_abs[BLOCK_LEN];
void wifi_setup(void)
{
// Connect the WiFi network (or start an AP if that doesn't work)
for (auto &net : Config::instance().getWLANList())
{
Serial.print(F("Adding network "));
Serial.println(net.ssid.c_str());
wiFiMulti.addAP(net.ssid.c_str(), net.password.c_str());
}
Serial.println(F("Trying to connect for 5 Minutes..."));
WiFi.setHostname("loudness");
WiFi.mode(WIFI_STA);
//WiFi.setSleepMode(WIFI_MODEM_SLEEP);
wiFiConnectedToStation = true; // assume the best
bool led_on = true;
for(size_t tries = 0; tries < 3000; tries++)
{
if(wiFiMulti.run() == WL_CONNECTED) {
Serial.println(F(""));
Serial.println(F("WiFi connected"));
Serial.println(F("IP address: "));
Serial.println(WiFi.localIP());
break;
}
led_on = !led_on;
digitalWrite(LED_BUILTIN, led_on);
delay(100);
Serial.print(F("."));
}
if(WiFi.status() != WL_CONNECTED) {
Serial.println(F("Connection failed, setting up access point..."));
wiFiConnectedToStation = false;
IPAddress apIP(192, 168, 42, 1);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("loudness", "dB(A)");
WiFi.enableAP(true);
digitalWrite(LED_BUILTIN, false); // LED ON (active low)
} else {
digitalWrite(LED_BUILTIN, true); // LED OFF (active low)
}
}
void setup()
{
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
Serial.println(F("Hello World!"));
Serial.println(F("Initializing LittleFS…"));
if(!SPIFFS.begin()) {
Serial.println(F("LittleFS setup failed!"));
ENDLESS_LOOP();
}
Config::instance().load();
Serial.println(F("Initializing WiFi…"));
wifi_setup();
//Serial.println(F("Initializing HTTP Server…"));
//httpserver_setup();
//Serial.println(F("Testing display…"));
//epaper_test();
Serial.println(F("Initialization routine complete."));
init_fft();
// start sampling
recorder.enableAdcChannel(6);
recorder.begin(true, false);
recorder.autoAdjust = 0; //Disable auto adjust of timers
recorder.adcBitsPerSample = 12;
recorder.setSampleRate(48000, false);
recorder.getADC(SAMPLES_PER_BLOCK);
}
void loop() {
static uint32_t last_output_time = 0;
static uint32_t nsamples = 0;
static value_type total_energy_0_to_200_hz = 0.0f;
static value_type total_energy_200_to_3500_hz = 0.0f;
static value_type total_energy_3500_to_8000_hz = 0.0f;
static value_type total_energy_8000_to_20000_hz = 0.0f;
wiFiMulti.run(); // maintain the WiFi connection
recorder.getADC(SAMPLES_PER_BLOCK);
// convert to Volt (float)
for(size_t i = 0; i < SAMPLES_PER_BLOCK; i++) {
timedomain[i] = (value_type)recorder.adcBuffer16[i] / 4096.0f * 3.30f;
}
// calculate average value
value_type avg = 0;
for(size_t i = 0; i < SAMPLES_PER_BLOCK; i++) {
avg += timedomain[i];
}
avg /= SAMPLES_PER_BLOCK;
// and remove it from the data
for(size_t i = 0; i < SAMPLES_PER_BLOCK; i++) {
timedomain[i] -= avg;
}
apply_hanning(timedomain);
fft_transform(timedomain, fft_re, fft_im);
complex_to_absolute(fft_re, fft_im, fft_abs);
value_type energy_0_to_200_hz = get_energy_density_in_band(fft_abs, 0, 200);
value_type energy_200_to_3500_hz = get_energy_density_in_band(fft_abs, 200, 3500);
value_type energy_3500_to_8000_hz = get_energy_density_in_band(fft_abs, 3500, 8000);
value_type energy_8000_to_20000_hz = get_energy_density_in_band(fft_abs, 8000, 20000);
total_energy_0_to_200_hz += energy_0_to_200_hz;
total_energy_200_to_3500_hz += energy_200_to_3500_hz;
total_energy_3500_to_8000_hz += energy_3500_to_8000_hz;
total_energy_8000_to_20000_hz += energy_8000_to_20000_hz;
nsamples++;
uint32_t now = millis();
if(now - last_output_time > OUTPUT_INTERVAL) {
total_energy_0_to_200_hz /= nsamples;
total_energy_200_to_3500_hz /= nsamples;
total_energy_3500_to_8000_hz /= nsamples;
total_energy_8000_to_20000_hz /= nsamples;
// calculate dBV
value_type dBV_per_Hz_0_to_200_hz = 20*log10(total_energy_0_to_200_hz);
value_type dBV_per_Hz_200_to_3500_hz = 20*log10(total_energy_200_to_3500_hz);
value_type dBV_per_Hz_3500_to_8000_hz = 20*log10(total_energy_3500_to_8000_hz);
value_type dBV_per_Hz_8000_to_20000_hz = 20*log10(total_energy_8000_to_20000_hz);
Serial.print(" 0 - 200 Hz [dBV/Hz]: ");
Serial.println(dBV_per_Hz_0_to_200_hz);
Serial.print(" 200 - 3500 Hz [dBV/Hz]: ");
Serial.println(dBV_per_Hz_200_to_3500_hz);
Serial.print(" 3500 - 8000 Hz [dBV/Hz]: ");
Serial.println(dBV_per_Hz_3500_to_8000_hz);
Serial.print(" 8000 - 20000 Hz [dBV/Hz]: ");
Serial.println(dBV_per_Hz_8000_to_20000_hz);
Serial.println();
// TODO: send the data to graphite
total_energy_0_to_200_hz = 0.0f;
total_energy_200_to_3500_hz = 0.0f;
total_energy_3500_to_8000_hz = 0.0f;
total_energy_8000_to_20000_hz = 0.0f;
nsamples = 0;
last_output_time = now;
}
//Serial.println(analogRead(A6));
/*unsigned long now = millis();
if((now - displayLastUpdateTime) >= DISPLAY_UPDATE_INTERVAL) {
Serial.println(F("Sending HTTP request…"));
displayLastUpdateTime = now;
}*/
}