From 1a9ac19d60d6fb2f35ff5279d1c26effbab6fde0 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Sat, 23 Jul 2022 22:50:24 +0200 Subject: [PATCH] Basic implementation of sampling+FFT+dBV/Hz calculation --- .gitignore | 11 +++ Makefile | 5 ++ data/etc/.keep | 0 gen_lut.py | 68 +++++++++++++++ platformio.ini | 34 ++++++++ src/Config.cpp | 44 ++++++++++ src/Config.h | 36 ++++++++ src/fft.c | 155 +++++++++++++++++++++++++++++++++ src/fft.h | 16 ++++ src/fft_config.h | 12 +++ src/lut.c | 51 +++++++++++ src/lut.h | 9 ++ src/main.cpp | 222 +++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 663 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 data/etc/.keep create mode 100755 gen_lut.py create mode 100644 platformio.ini create mode 100644 src/Config.cpp create mode 100644 src/Config.h create mode 100644 src/fft.c create mode 100644 src/fft.h create mode 100644 src/fft_config.h create mode 100644 src/lut.c create mode 100644 src/lut.h create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49f4b87 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1f8e025 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +.pio/: src + pio run + +compile_commands.json: src + pio run -t compiledb diff --git a/data/etc/.keep b/data/etc/.keep new file mode 100644 index 0000000..e69de29 diff --git a/gen_lut.py b/gen_lut.py new file mode 100755 index 0000000..5095b8a --- /dev/null +++ b/gen_lut.py @@ -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) diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..dab1e13 --- /dev/null +++ b/platformio.ini @@ -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 diff --git a/src/Config.cpp b/src/Config.cpp new file mode 100644 index 0000000..88bf4e9 --- /dev/null +++ b/src/Config.cpp @@ -0,0 +1,44 @@ +#include + +#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(); +} \ No newline at end of file diff --git a/src/Config.h b/src/Config.h new file mode 100644 index 0000000..78f5f09 --- /dev/null +++ b/src/Config.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +class Config +{ + public: + struct WLAN + { + std::string ssid; + std::string password; + }; + + typedef std::vector 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; +}; \ No newline at end of file diff --git a/src/fft.c b/src/fft.c new file mode 100644 index 0000000..9b7f6de --- /dev/null +++ b/src/fft.c @@ -0,0 +1,155 @@ +#include + +#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; +} diff --git a/src/fft.h b/src/fft.h new file mode 100644 index 0000000..6013eb4 --- /dev/null +++ b/src/fft.h @@ -0,0 +1,16 @@ +#ifndef FFT_H +#define FFT_H + +#include + +#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 diff --git a/src/fft_config.h b/src/fft_config.h new file mode 100644 index 0000000..25accf6 --- /dev/null +++ b/src/fft_config.h @@ -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 diff --git a/src/lut.c b/src/lut.c new file mode 100644 index 0000000..e0eccb5 --- /dev/null +++ b/src/lut.c @@ -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]; +} diff --git a/src/lut.h b/src/lut.h new file mode 100644 index 0000000..ae591e8 --- /dev/null +++ b/src/lut.h @@ -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 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6ffcb15 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,222 @@ +#include + +#include +#include + +#include + +#include + +#include + +#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; + }*/ +}