From 15ffa8fa1a36ac42624c2cf24be071a62161dedc Mon Sep 17 00:00:00 2001 From: francovaro Date: Fri, 3 May 2024 10:41:42 +0200 Subject: [PATCH] SDK release v1.49.21 --- EdgeImpulse.EI-SDK.pdsc | 10 +- EdgeImpulse.pidx | 4 +- .../classifier/ei_model_types.h | 4 +- .../edge-impulse-sdk/classifier/ei_run_dsp.h | 23 ++++- edgeimpulse/edge-impulse-sdk/dsp/ei_flatten.h | 4 +- edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp | 96 +++++++++++++++++++ edgeimpulse/edge-impulse-sdk/dsp/numpy.hpp | 61 ++++++------ .../edge-impulse-sdk/dsp/spectral/signal.hpp | 15 ++- 8 files changed, 173 insertions(+), 44 deletions(-) create mode 100644 edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp diff --git a/EdgeImpulse.EI-SDK.pdsc b/EdgeImpulse.EI-SDK.pdsc index a9ef04e..24dc4a9 100644 --- a/EdgeImpulse.EI-SDK.pdsc +++ b/EdgeImpulse.EI-SDK.pdsc @@ -5,13 +5,16 @@ EI-SDK LICENSE-apache-2.0.txt Edge Impulse SDK - https://github.com/edgeimpulse/edge-impulse-sdk-pack/releases/download/v1.49.18/ + https://github.com/edgeimpulse/edge-impulse-sdk-pack/releases/download/v1.49.21/ hello@edgeimpulse.com https://github.com/edgeimpulse/edge-impulse-sdk-pack.git - + EI-SDK + + EI-SDK + EI-SDK @@ -125,7 +128,7 @@ - + Edge Impulse SDK @@ -615,6 +618,7 @@ + diff --git a/EdgeImpulse.pidx b/EdgeImpulse.pidx index d2bf066..74f0f02 100644 --- a/EdgeImpulse.pidx +++ b/EdgeImpulse.pidx @@ -2,8 +2,8 @@ EdgeImpulse https://raw.githubusercontent.com/edgeimpulse/edge-impulse-sdk-pack/main/ - 2024-04-30 16:25:39 + 2024-05-03 10:40:48 - + diff --git a/edgeimpulse/edge-impulse-sdk/classifier/ei_model_types.h b/edgeimpulse/edge-impulse-sdk/classifier/ei_model_types.h index 5ed5608..c85e8d6 100644 --- a/edgeimpulse/edge-impulse-sdk/classifier/ei_model_types.h +++ b/edgeimpulse/edge-impulse-sdk/classifier/ei_model_types.h @@ -107,7 +107,7 @@ typedef struct { uint8_t *axes; size_t axes_size; int version; // future proof, can easily add to this struct now - DspHandle* (*factory)(void* config); // nullptr means no state + DspHandle* (*factory)(void* config, float sampling_freq); // nullptr means no state // v1 ends here } ei_model_dsp_t; @@ -264,7 +264,7 @@ typedef DspHandle* _dsp_handle_ptr_t; DspHandle* get_dsp_handle(size_t ix) { if (dsp_handles[ix] == nullptr) { - dsp_handles[ix] = impulse->dsp_blocks[ix].factory(impulse->dsp_blocks[ix].config); + dsp_handles[ix] = impulse->dsp_blocks[ix].factory(impulse->dsp_blocks[ix].config, impulse->frequency); } return dsp_handles[ix]; } diff --git a/edgeimpulse/edge-impulse-sdk/classifier/ei_run_dsp.h b/edgeimpulse/edge-impulse-sdk/classifier/ei_run_dsp.h index 4ad67ac..e46612c 100644 --- a/edgeimpulse/edge-impulse-sdk/classifier/ei_run_dsp.h +++ b/edgeimpulse/edge-impulse-sdk/classifier/ei_run_dsp.h @@ -25,6 +25,10 @@ #include "edge-impulse-sdk/dsp/ei_flatten.h" #include "model-parameters/model_metadata.h" +#if EI_CLASSIFIER_HR_ENABLED +#include "edge-impulse-sdk/dsp/ei_hr.hpp" +#endif + #if defined(__cplusplus) && EI_C_LINKAGE == 1 extern "C" { extern void ei_printf(const char *format, ...); @@ -48,6 +52,23 @@ static float *ei_dsp_cont_current_frame = nullptr; static size_t ei_dsp_cont_current_frame_size = 0; static int ei_dsp_cont_current_frame_ix = 0; +__attribute__((unused)) int extract_hr_features( + signal_t *signal, + matrix_t *output_matrix, + void *config_ptr, + const float frequency) +{ +#if EI_CLASSIFIER_HR_ENABLED + auto handle = hr_class::create(config_ptr, frequency); + auto ret = handle->extract(signal, output_matrix, config_ptr, frequency); + delete handle; + return ret; +#else + ei_printf("ERR: Please contact EI sales to enable heart rate processing in deployment"); + return EIDSP_NOT_SUPPORTED; +#endif +} + __attribute__((unused)) int extract_spectral_analysis_features( signal_t *signal, matrix_t *output_matrix, @@ -137,7 +158,7 @@ __attribute__((unused)) int extract_raw_features(signal_t *signal, matrix_t *out } __attribute__((unused)) int extract_flatten_features(signal_t *signal, matrix_t *output_matrix, void *config_ptr, const float frequency) { - auto handle = flatten_class::create(config_ptr); + auto handle = flatten_class::create(config_ptr, frequency); auto ret = handle->extract(signal, output_matrix, config_ptr, frequency); delete handle; return ret; diff --git a/edgeimpulse/edge-impulse-sdk/dsp/ei_flatten.h b/edgeimpulse/edge-impulse-sdk/dsp/ei_flatten.h index 8802543..d7586b2 100644 --- a/edgeimpulse/edge-impulse-sdk/dsp/ei_flatten.h +++ b/edgeimpulse/edge-impulse-sdk/dsp/ei_flatten.h @@ -154,7 +154,7 @@ class flatten_class : public DspHandle { return EIDSP_OK; } - static DspHandle* create(void* config); + static DspHandle* create(void* config, float _sampling_frequency); void* operator new(size_t size) { // Custom memory allocation logic here @@ -190,7 +190,7 @@ class flatten_class : public DspHandle { } }; -DspHandle* flatten_class::create(void* config_in) { // NOLINT def in header is OK at EI +DspHandle* flatten_class::create(void* config_in, float _sampling_frequency) { // NOLINT def in header is OK at EI auto config = reinterpret_cast(config_in); return new flatten_class(config->moving_avg_num_windows, config->axes); }; diff --git a/edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp b/edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp new file mode 100644 index 0000000..6570f9b --- /dev/null +++ b/edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp @@ -0,0 +1,96 @@ +/* Edge Impulse inferencing library + * Copyright (c) 2022 EdgeImpulse Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef HR_PPG_HPP +#define HR_PPG_HPP + +#include "edge-impulse-sdk/dsp/numpy.hpp" +#include "edge-impulse-sdk/dsp/ei_dsp_handle.h" +#include "edge-impulse-enterprise/hr/hr_ppg.hpp" + +class hr_class : public DspHandle { +public: + int print() override { + ei_printf("Last HR: %f\n", ppg._res.hr); + return ei::EIDSP_OK; + } + + int extract(ei::signal_t *signal, ei::matrix_t *output_matrix, void *config_ptr, const float frequency) override { + using namespace ei; + + // Don't need just yet + // ei_dsp_config_hr_t config = *((ei_dsp_config_hr_t*)config_ptr); + + + // TODO fix for axes / accel + size_t samples_per_inc = ppg.win_inc_samples; + // TODO go in a loop for the full window size, once I can actually test this vs studio + if(signal->total_length != samples_per_inc) { + return EIDSP_BUFFER_SIZE_MISMATCH; + } + + // TODO ask for smaller increments and bp them into place + // Copy into the end of the buffer + matrix_t temp(ppg.axes, samples_per_inc); + signal->get_data(0, samples_per_inc, temp.buffer); + + + output_matrix->buffer[0] = ppg.stream(&temp); + + output_matrix->rows = 1; + output_matrix->cols = 1; + return EIDSP_OK; + } + + // TODO: actually read in config: axes too! + hr_class(float frequency) : ppg(frequency, 1, 8*50, 2*50, true) { + } + + // Boilerplate below here + static DspHandle* create(void* config, float frequency); + + void* operator new(size_t size) { + // Custom memory allocation logic here + return ei_malloc(size); + } + + void operator delete(void* ptr) { + // Custom memory deallocation logic here + ei_free(ptr); + } + // end boilerplate +private: + ei::hr_ppg ppg; +}; + +DspHandle* hr_class::create(void* config_in, float frequency) { // NOLINT def in header is OK at EI + // Don't need just yet + // auto config = reinterpret_cast(config_in); + // TODO: actually read in config + return new hr_class(frequency); +}; + +/* +NOTE, contact EI sales for license and source to use EI heart rate and heart rate variance functions in deployment +*/ + +#endif \ No newline at end of file diff --git a/edgeimpulse/edge-impulse-sdk/dsp/numpy.hpp b/edgeimpulse/edge-impulse-sdk/dsp/numpy.hpp index 7870ff7..28b8f22 100644 --- a/edgeimpulse/edge-impulse-sdk/dsp/numpy.hpp +++ b/edgeimpulse/edge-impulse-sdk/dsp/numpy.hpp @@ -335,37 +335,40 @@ class numpy { } static void transpose_in_place(matrix_t *matrix) { - size_t size = matrix->cols * matrix->rows - 1; - float temp; // temp for swap - size_t next; // next item to swap - size_t cycleBegin; // index of start of cycle - size_t i; // location in matrix - size_t all_done_mark = 1; - ei_vector done(size+1,false); - - i = 1; // Note that matrix[0] and last element of matrix won't move - while (1) - { - cycleBegin = i; - temp = matrix->buffer[i]; - do + // Don't bother if either dim is one, just need to swap the dimension sizes + if( matrix->rows != 1 && matrix->cols != 1) { + size_t size = matrix->cols * matrix->rows - 1; + float temp; // temp for swap + size_t next; // next item to swap + size_t cycleBegin; // index of start of cycle + size_t i; // location in matrix + size_t all_done_mark = 1; + ei_vector done(size+1,false); + + i = 1; // Note that matrix[0] and last element of matrix won't move + while (1) { - size_t col = i % matrix->cols; - size_t row = i / matrix->cols; - // swap row and col to make new idx, b/c we want to know where in the transposed matrix - next = col*matrix->rows + row; - float temp2 = matrix->buffer[next]; - matrix->buffer[next] = temp; - temp = temp2; - done[next] = true; - i = next; - } - while (i != cycleBegin); + cycleBegin = i; + temp = matrix->buffer[i]; + do + { + size_t col = i % matrix->cols; + size_t row = i / matrix->cols; + // swap row and col to make new idx, b/c we want to know where in the transposed matrix + next = col*matrix->rows + row; + float temp2 = matrix->buffer[next]; + matrix->buffer[next] = temp; + temp = temp2; + done[next] = true; + i = next; + } + while (i != cycleBegin); - // start next cycle by find next not done - for (i = all_done_mark; done[i]; i++) { - all_done_mark++; // move the high water mark so we don't look again - if(i>=size) { goto LOOP_END; } + // start next cycle by find next not done + for (i = all_done_mark; done[i]; i++) { + all_done_mark++; // move the high water mark so we don't look again + if(i>=size) { goto LOOP_END; } + } } } LOOP_END: diff --git a/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp b/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp index 7452f76..37fb0e9 100644 --- a/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp +++ b/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp @@ -95,7 +95,12 @@ class signal { zi_vec(zi_, zi_ + (num_sections_ * 2)), num_sections(num_sections_) { - zi = zi_vec.data(); + } + + void update(const float *coeff_, const float *zi_) + { + coeff = coeff_; + zi_vec.assign(zi_, zi_ + (num_sections * 2)); } /** @@ -109,7 +114,7 @@ class signal { { assert(num_sections > 0); - iir2(input, output, size, coeff, coeff + 3, zi); + iir2(input, output, size, coeff, coeff + 3, zi_vec.data()); for (size_t sect = 1; sect < num_sections; sect++) { iir2( @@ -118,15 +123,15 @@ class signal { size, coeff + sect * 6, coeff + sect * 6 + 3, - zi + sect * 2); + zi_vec.data() + sect * 2); } } void init(float x0) { for (size_t sect = 0; sect < num_sections; sect++) { - zi[sect * 2] *= x0; - zi[sect * 2 + 1] *= x0; + zi_vec.data()[sect * 2] *= x0; + zi_vec.data()[sect * 2 + 1] *= x0; } } };