Getting Started¶
Install¶
Arduino Library Manager¶
In the Arduino IDE, open Tools → Manage Libraries…, search for SignalCore, and click Install.
PlatformIO¶
Add the dependency to platformio.ini:
Manual¶
Clone or download the repository into the Arduino libraries directory:
The libraries folder is found within the Arduino sketchbook folder, whose
location can be found within the Settings dialog. Click Arduino IDE →
Settings and look for the Sketchbook location text box. The libraries
folder is not necessarily auto created — if it does not yet exist, simply
create the folder and copy SignalCore into it.
Restart the Arduino IDE so it picks up the new library.
Confirm the install by opening the SignalGeneratorBasic example (File → Examples → SignalCore → SignalGeneratorBasic) and uploading it to a board.
Hello SignalCore¶
The smallest useful sketch generates a tone in software and prints it one sample per line, so the Arduino Serial Plotter draws it directly. No analog hardware is needed, which makes this a good first check that the library compiles and runs on your board.
#include <SignalCore.h>
using namespace signalcore;
#define SAMPLE_RATE 10000 // virtual samples per second
SignalGenerator sig(SAMPLE_RATE);
void setup() {
Serial.begin(115200);
sig.setCarrier(SignalShape::Sine, 200.0); // 200 Hz tone
}
void loop() {
Serial.println((float)sig.nextSample(0), 4); // 0 = full-resolution float
delayMicroseconds(1000000UL / SAMPLE_RATE); // pace for the plotter
}
Upload it, then open Tools → Serial Plotter at 115200 baud. A clean 200 Hz
sine wave should scroll across the window. This is the bundled
SignalGeneratorBasic example in its
simplest form.
A first FFT¶
The next step generates a tone, runs an FFT over it, and reports the peak bin.
The reported frequency should track the carrier to within one bin, where a bin
is SAMPLE_RATE / FFT_LENGTH wide.
The transform length is set once, as a single number. That same number sizes the sample buffer too, so there is only one value to change when you want a different length:
#include <SignalCore.h>
using namespace signalcore;
#define SAMPLE_RATE 10000
#define FFT_LENGTH 1024 // power of two, 32 up to 4096
FFT<FFT_LENGTH> fft; // the transform, sized at compile time
SignalGenerator sig(SAMPLE_RATE);
float samples[FFT_LENGTH];
void setup() {
Serial.begin(115200);
sig.setCarrier(SignalShape::Sine, 1000.0); // 1 kHz tone
fft.setWindowType(WindowType::BlackmanHarris);
}
void loop() {
for (int i = 0; i < FFT_LENGTH; i++) {
samples[i] = (float)sig.nextSample(0);
}
removeDC(samples, FFT_LENGTH); // strip DC before transforming
fft.calculate(samples); // run the transform
// peakBin() searches result[0 .. N/2] and skips the DC bin by default, so a
// constant offset cannot win the search. peakFrequency() converts it to Hz.
int peak = fft.peakBin();
float hz = fft.peakFrequency(SAMPLE_RATE);
Serial.print("Peak bin: "); Serial.print(peak);
Serial.print(" Hz: "); Serial.println(hz);
delay(250);
}
A few things worth knowing:
- The size is one number.
FFT<FFT_LENGTH>takes a plain integer, and the sameFFT_LENGTHsizes thesamplesbuffer, so the two cannot drift apart. You can also write the length directly, asFFT<1024>. A named form,FFT<FFTSize::N1024>, still works if you prefer it. - The size is fixed at compile time and must be a power of two between 32 and
- A value that is not a power of two, such as
FFT<500>, fails to compile with a message saying why, rather than misbehaving at run time. - Reading the spectrum.
peakBin()andpeakFrequency()hand back the dominant tone directly. To read the spectrum itself, treat the FFT object as afloat*array, where only the firstN/2 + 1entries hold real magnitude bins. - Board limits. The AVR build cap is 256 points, sized for larger AVR parts
such as the Mega 2560. On an ATmega328 board (UNO R3, Nano — 2 KB of SRAM)
the practical ceiling is
FFT<128>:FFT<256>compiles but its ~2.5 KB of buffers do not fit. See Configuration & Platforms.
Tip
On a Teensy 4.x the FFT runs on CMSIS-DSP automatically, with no change to this sketch. On every other board the portable C++ implementation produces the same output, bin for bin, so the same code ports unchanged.
Created by VoidLoop · Founded by Gregory Kovacs · Written by Zachariah Magee