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.
340 lines
9.4 KiB
C++
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;
|
|
}
|