firmware-base/vendor/sming/Sming/samples/Basic_Tasks/include/AnalogueReader.h
2026-01-28 16:42:43 +01:00

167 lines
4.2 KiB
C++

#pragma once
#include <Services/Profiling/MinMaxTimes.h>
#include <Platform/Timers.h>
#include <Task.h>
#include <array>
#include <numeric>
#include <arduinoFFT.h>
/**
*
* @tparam sampleCount Must be a power of 2. Buckets are: 0.1 0.2 0.5 1K 2K 4K
* @tparam samplingFrequency In Hz, must be 10000 or less due to ADC conversion time
* Determines maximum frequency that can be analysed by the FFT.
* @tparam reportIntervalMs
*/
template <unsigned sampleCount = 512, unsigned samplingFrequency = 10000, unsigned reportIntervalMs = 3000>
class AnalogueReader : public Task
{
public:
AnalogueReader() : loopTimes("Loop Times")
{
printTimer.reset<reportIntervalMs>();
}
void loop() override;
void onNotify(Notify code) override;
private:
void processSamples();
// Timing information in clock ticks
ElapseTimer sampleTimer;
uint32_t sampleIntervalTicks{0}; ///< Ticks between samples
uint32_t groupStartTicks{0};
uint32_t nextSampleTicks{0};
unsigned sampleIndex{0};
unsigned missedSamples{0};
bool restartSampler{true};
std::array<double, sampleCount> vReal, vImag;
// Stats.
OneShotFastMs printTimer; ///< How often to print results
Profiling::MilliTimes loopTimes; ///< Times how long between calls
};
#define ANALOGUE_READER(returnType) \
template <unsigned sampleCount, unsigned samplingFrequency, unsigned reportIntervalMs> \
returnType AnalogueReader<sampleCount, samplingFrequency, reportIntervalMs>
ANALOGUE_READER(void)::processSamples()
{
arduinoFFT fft(vReal.data(), vImag.data(), sampleCount, samplingFrequency);
fft.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
fft.Compute(FFT_FORWARD);
fft.ComplexToMagnitude();
auto average = [this](unsigned first, unsigned last) {
double total{0};
total = std::accumulate(vReal.begin() + first, vReal.begin() + last, total);
unsigned count = last - first + 1;
return int(round(total / count));
};
// for(unsigned i = 0; i < SAMPLE_COUNT; ++i) {
// int value = vReal[i];
// m_printf("[%u] = %d, ", i, value);
// }
//
// return;
/*
* Don't use sample 0 and only first SAMPLE_COUNT/2 are usable.
* Each array element represents a frequency and its value the amplitude.
*
* 0 - 5 0 195
* 6 - 12 234
* 13 - 32 508
* 33 - 62 1290
* 63 - 105 2461
* 106 - 120 4141 4688
*/
int bands[6] = {average(0, 5), average(6, 12), average(13, 32),
average(33, 62), average(63, 105), average(106, 120)};
for(unsigned i = 0; i < ARRAY_SIZE(bands); ++i) {
if(i > 0) {
Serial.print(", ");
}
Serial.print(bands[i]);
}
Serial << " - " << missedSamples << " missed" << endl;
}
ANALOGUE_READER(void)::loop()
{
loopTimes.update();
auto now = sampleTimer.ticks();
if(restartSampler) {
// Advance group start time
auto groupLength = sampleIntervalTicks * sampleCount;
while(int(now - groupStartTicks) > 0) {
groupStartTicks += groupLength;
}
vReal.fill(0);
vImag.fill(0);
nextSampleTicks = groupStartTicks;
sampleIndex = 0;
missedSamples = 0;
restartSampler = false;
// Serial << "groupStartTicks = " << groupStartTicks,
// " << now = " << now << ", diff = " << int(now - groupStartTicks) << endl;
} else if(int(nextSampleTicks - now) <= 0) {
unsigned index = (now - groupStartTicks) / sampleIntervalTicks;
missedSamples += min(index, sampleCount) - sampleIndex;
if(index < sampleCount) {
vReal[index] = analogRead(A0);
sampleIndex = index + 1;
}
if((index + 1) >= sampleCount) {
processSamples();
restartSampler = true;
} else {
do {
nextSampleTicks += sampleIntervalTicks;
} while(int(now - nextSampleTicks) > 0);
}
}
if(printTimer.expired()) {
Serial.println(loopTimes);
loopTimes.clear();
printTimer.start();
// sleep(1500);
} else {
loopTimes.start();
}
}
ANALOGUE_READER(void)::onNotify(Notify code)
{
switch(code) {
case Notify::Waking:
case Notify::Resuming:
loopTimes.start();
loopTimes.clear();
sampleIntervalTicks = sampleTimer.timeToTicks(1000000 / samplingFrequency);
groupStartTicks = sampleTimer.ticks();
restartSampler = true;
Serial << _F("sampleIntervalTicks = ") << sampleIntervalTicks << endl;
break;
default:;
}
}
#undef ANALOGUE_READER