You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CMSIS-DSP/Applications/SineDetectorApp/SineDetection/SineDetection.ino

340 lines
9.4 KiB
C++

/*
* Copyright (c) 2020 Arm Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* This example reads audio data from the on-board PDM microphones
* and try to detect a 1kHz sine signal using a SVM predictor.
*
* Circuit:
* - Arduino Nano 33 BLE board
*/
#include <PDM.h>
/*
The CMSIS-DSP coming with the Arduino Nano 33 BLE board is not yet the
latest release and thus is not yet including the SVM predictor.
So, it is duplicated in this app (svmDef.cpp and svmDef.h)
*/
#include "arm_math.h"
#include "svmDef.hpp"
/*
Undefine this line if you want to use BLE rather than the serial console
for the detection information.
*/
//#define BLEOUTPUT
#if defined(BLEOUTPUT)
#include <ArduinoBLE.h>
// The UUID are coming from Arduino examples.
BLEService svmDetectionService("19B10010-E8F2-537E-4F6C-D104768A1214");
BLEBoolCharacteristic svmDetectionStatus("19B10012-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify);
#endif
/*
Use to enable / disable the interrupts
*/
#include <hal/nrf_pdm.h>
/* Header generated by a training script (not included in this app) */
#include "svm.hpp"
//#define DUMP
/*
Dimension of the vector.
The training data has used segment of 256 samples.
*/
#define BUFSIZE vectorDimensions
/*
Hanning window.
*/
const float32_t hanning[BUFSIZE]={0.0f, 0.000151774f, 0.000607004f, 0.00136541f, 0.00242654f, 0.00378975f,
0.0054542f, 0.00741888f, 0.00968261f, 0.012244f, 0.0151015f, 0.0182534f,
0.0216978f, 0.0254325f, 0.0294554f, 0.0337639f, 0.0383554f, 0.0432273f,
0.0483764f, 0.0537997f, 0.0594939f, 0.0654555f, 0.071681f, 0.0781664f,
0.084908f, 0.0919015f, 0.0991429f, 0.106628f, 0.114351f, 0.122309f,
0.130496f, 0.138907f, 0.147537f, 0.156382f, 0.165435f, 0.174691f, 0.184144f,
0.19379f, 0.203621f, 0.213632f, 0.223818f, 0.23417f, 0.244684f, 0.255354f,
0.266171f, 0.277131f, 0.288226f, 0.299449f, 0.310794f, 0.322255f, 0.333823f,
0.345492f, 0.357254f, 0.369104f, 0.381032f, 0.393033f, 0.405099f, 0.417223f,
0.429397f, 0.441614f, 0.453866f, 0.466146f, 0.478447f, 0.490761f, 0.50308f,
0.515398f, 0.527706f, 0.539997f, 0.552264f, 0.5645f, 0.576696f, 0.588845f,
0.600941f, 0.612976f, 0.624941f, 0.636831f, 0.648638f, 0.660355f, 0.671974f,
0.683489f, 0.694893f, 0.706178f, 0.717338f, 0.728366f, 0.739256f, 0.75f,
0.760592f, 0.771027f, 0.781296f, 0.791395f, 0.801317f, 0.811056f, 0.820607f,
0.829962f, 0.839118f, 0.848067f, 0.856805f, 0.865327f, 0.873626f, 0.881699f,
0.88954f, 0.897145f, 0.904508f, 0.911626f, 0.918495f, 0.925109f, 0.931464f,
0.937558f, 0.943387f, 0.948946f, 0.954233f, 0.959243f, 0.963976f, 0.968426f,
0.972592f, 0.976471f, 0.980061f, 0.983359f, 0.986364f, 0.989074f, 0.991487f,
0.993601f, 0.995416f, 0.99693f, 0.998142f, 0.999052f, 0.999659f, 0.999962f,
0.999962f, 0.999659f, 0.999052f, 0.998142f, 0.99693f, 0.995416f, 0.993601f,
0.991487f, 0.989074f, 0.986364f, 0.983359f, 0.980061f, 0.976471f, 0.972592f,
0.968426f, 0.963976f, 0.959243f, 0.954233f, 0.948946f, 0.943387f, 0.937558f,
0.931464f, 0.925109f, 0.918495f, 0.911626f, 0.904508f, 0.897145f, 0.88954f,
0.881699f, 0.873626f, 0.865327f, 0.856805f, 0.848067f, 0.839118f, 0.829962f,
0.820607f, 0.811056f, 0.801317f, 0.791395f, 0.781296f, 0.771027f, 0.760592f,
0.75f, 0.739256f, 0.728366f, 0.717338f, 0.706178f, 0.694893f, 0.683489f,
0.671974f, 0.660355f, 0.648638f, 0.636831f, 0.624941f, 0.612976f, 0.600941f,
0.588845f, 0.576696f, 0.5645f, 0.552264f, 0.539997f, 0.527706f, 0.515398f,
0.50308f, 0.490761f, 0.478447f, 0.466146f, 0.453866f, 0.441614f, 0.429397f,
0.417223f, 0.405099f, 0.393033f, 0.381032f, 0.369104f, 0.357254f, 0.345492f,
0.333823f, 0.322255f, 0.310794f, 0.299449f, 0.288226f, 0.277131f, 0.266171f,
0.255354f, 0.244684f, 0.23417f, 0.223818f, 0.213632f, 0.203621f, 0.19379f,
0.184144f, 0.174691f, 0.165435f, 0.156382f, 0.147537f, 0.138907f, 0.130496f,
0.122309f, 0.114351f, 0.106628f, 0.0991429f, 0.0919015f, 0.084908f,
0.0781664f, 0.071681f, 0.0654555f, 0.0594939f, 0.0537997f, 0.0483764f,
0.0432273f, 0.0383554f, 0.0337639f, 0.0294554f, 0.0254325f, 0.0216978f,
0.0182534f, 0.0151015f, 0.012244f, 0.00968261f, 0.00741888f, 0.0054542f,
0.00378975f, 0.00242654f, 0.00136541f, 0.000607004f, 0.000151774f, 0.0f};
/*
Sample buffer for samples coming from PDM
*/
short sampleBuffer[512];
/*
svm buffer : The PDM samples converted to float,
rescaled and multiplied by the Hanning window.
*/
float svmBuffer[BUFSIZE];
/*
Number of PDM samples copied to SVM buffer.
*/
int svmSamplesConverted=0;
/*
Number of samples read from PDM
*/
volatile int samplesRead=0;
/*
PDM buffer ID.
It is used for debugging. Each time a new buffer of smples is
received, this number is incremented.
*/
volatile int bufferNb=0;
/*
Detection ID : Each time a new sine is detected, this number is incremented.
It is to display in the console and help deugging.
*/
int nbDetect=0;
// Class 0 is signal present
// Class 1 is signal missing
int32_t classes[2]={0,1};
/*
Configuration of the SVM data structure with parameters generated
from the training script.
*/
arm_svm_polynomial_instance_f32 svm = {
nbSupportVectors,
vectorDimensions,
intercept,
dualCoefs,
supportVectors,
classes,
degree,
coef0,
gamma
};
void setup() {
Serial.begin(115200);
while (!Serial);
PDM.setBufferSize(1024);
// configure the data receive callback
PDM.onReceive(onPDMdata);
// optionally set the gain, defaults to 20
// PDM.setGain(30);
// initialize PDM with:
// - one channel (mono mode)
// - a 16 kHz sample rate
if (!PDM.begin(1, 16000)) {
Serial.println("Failed to start PDM!");
while (1);
}
#if defined(BLEOUTPUT)
if (!BLE.begin())
{
Serial.println("starting BLE failed!");
while (1);
}
BLE.setLocalName("Sound Detection");
BLE.setAdvertisedService(svmDetectionService);
svmDetectionService.addCharacteristic(svmDetectionStatus);
BLE.addService(svmDetectionService);
svmDetectionStatus.writeValue(false);
BLE.advertise();
#endif
}
void loop() {
#if defined(BLEOUTPUT)
BLE.poll();
#endif
// If there are enough samples to apply the SVM prediction
if (samplesRead >0)
{
int i=0;
// We copy the received PDM samples to the SVM buffer.
// We don't want the sampleBuffer buffer to be modified
// while this copy is taking place.
// So PDM interrupts are disablsd.
NVIC_DisableIRQ(PDM_IRQn);
while((svmSamplesConverted < BUFSIZE) && (samplesRead > 0))
{
svmBuffer[svmSamplesConverted] = (float)sampleBuffer[i];
svmSamplesConverted++;
i++;
samplesRead--;
}
samplesRead = 0;
NVIC_EnableIRQ(PDM_IRQn);
}
// If the SVM buffer is full, we preprocess the sample
// and apply the SVM classifier.
if (svmSamplesConverted == BUFSIZE)
{
float32_t avgEnergy;
svmSamplesConverted = 0;
float32_t result=0;
// Convert samples to float and normalize them
// since SVM algorithm is not scale invariant.
// Clip to avoid outlier sample which would be too big.
// Apply the Hanning window.
arm_rms_f32(svmBuffer,BUFSIZE,&avgEnergy);
for (int i = 0; i < BUFSIZE; i++) {
svmBuffer[i] = svmBuffer[i] / avgEnergy;
// Analysis of the scaled tests patterns have shown
// that most values are between -2 and 2.
// So to avoid outliers, we clip between [-2,2].
// We have not checked if it is making a difference
// to the final quality of the prediction so this
// clipping is perhaps not needed.
if (svmBuffer[i] < -2)
{
svmBuffer[i] = -2;
}
if (svmBuffer[i] > 2)
{
svmBuffer[i] = 2;
}
}
// We multiply with the Hanning window.
arm_mult_f32(svmBuffer,(float32_t*)hanning,svmBuffer,BUFSIZE);
// We try to classify the result.
arm_svm_polynomial_predict_f32(&svm, svmBuffer,&result);
// If negative then a signal was detected.
if (result < 0)
{
nbDetect = nbDetect + 1;
#if defined(BLEOUTPUT)
if (!svmDetectionStatus.value())
{
svmDetectionStatus.writeValue(true);
}
#else
Serial.print(" d:");
Serial.print(nbDetect);
Serial.print(" b:");
Serial.print(bufferNb);
Serial.print(" ");
Serial.println("DETECTED");
#endif
}
else
{
#if defined(BLEOUTPUT)
if (svmDetectionStatus.value())
{
svmDetectionStatus.writeValue(false);
}
#endif
}
}
}
/*
Interrupt handler.
Received PDM data is copied into the buffer sampleBuffer.
*/
void onPDMdata() {
int bytesAvailable = PDM.available();
PDM.read(sampleBuffer , bytesAvailable);
samplesRead = bytesAvailable / 2;
bufferNb = bufferNb + 1;
}