From 979f0f97b961cb718655754ab8cd80b90f6115d2 Mon Sep 17 00:00:00 2001 From: watertrainer Date: Tue, 5 Apr 2022 22:22:30 +0000 Subject: [PATCH 1/7] replace magic number 200 with const DATA_SIZE we can replace dataIndex in ProcessHeartRate, because it was only being called if dataIndex is 200 anyway (see line 61-67) and the Trough function expects its second argument to be the size and not an index. --- src/components/heartrate/Ppg.cpp | 12 ++++++------ src/components/heartrate/Ppg.h | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index a5d836966a..0c36eb3c68 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -52,13 +52,13 @@ int8_t Ppg::Preprocess(float spl) { auto spl_int = static_cast(spl); - if (dataIndex < 200) + if (dataIndex < DATA_SIZE) data[dataIndex++] = spl_int; return spl_int; } float Ppg::HeartRate() { - if (dataIndex < 200) + if (dataIndex < DATA_SIZE) return 0; NRF_LOG_INFO("PREPROCESS, offset = %d", offset); @@ -67,22 +67,22 @@ float Ppg::HeartRate() { return hr; } float Ppg::ProcessHeartRate() { - auto t0 = Trough(data.data(), dataIndex, 7, 48); + auto t0 = Trough(data.data(), DATA_SIZE, 7, 48); if (t0 < 0) return 0; float t1 = t0 * 2; - t1 = Trough(data.data(), dataIndex, t1 - 5, t1 + 5); + t1 = Trough(data.data(), DATA_SIZE, t1 - 5, t1 + 5); if (t1 < 0) return 0; float t2 = static_cast(t1 * 3) / 2; - t2 = Trough(data.data(), dataIndex, t2 - 5, t2 + 5); + t2 = Trough(data.data(), DATA_SIZE, t2 - 5, t2 + 5); if (t2 < 0) return 0; float t3 = static_cast(t2 * 4) / 3; - t3 = Trough(data.data(), dataIndex, t3 - 4, t3 + 4); + t3 = Trough(data.data(), DATA_SIZE, t3 - 4, t3 + 4); if (t3 < 0) return static_cast(60 * 24 * 3) / static_cast(t2); diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index 7000c8710b..9095c75a43 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -18,7 +18,8 @@ namespace Pinetime { void Reset(); private: - std::array data; + static const uint8_t DATA_SIZE = 200; + std::array data; size_t dataIndex = 0; float offset; Biquad hpf; From 254fb846ce1bf577fda40c0d13697d9f7c0de9ce Mon Sep 17 00:00:00 2001 From: watertrainer Date: Thu, 14 Apr 2022 23:04:45 +0000 Subject: [PATCH 2/7] ring buffer impl for processing of hr sensor data --- src/components/heartrate/Ppg.cpp | 57 +++++++++++++++++--------------- src/components/heartrate/Ppg.h | 6 +++- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index 0c36eb3c68..47c0d38fc9 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -10,21 +10,25 @@ using namespace Pinetime::Controllers; /** Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/ppg.py */ -namespace { - int Compare(int8_t* d1, int8_t* d2, size_t count) { +Ppg::Ppg() + : hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694}, + agc {20, 0.971, 2}, + lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} { +} + + int Ppg::Compare(int8_t* d1,int shift, size_t count) { int e = 0; for (size_t i = 0; i < count; i++) { - auto d = d1[i] - d2[i]; + auto d = d1[getRingIndex(i+shift)] - d1[getRingIndex(i)]; e += d * d; } return e; } - int CompareShift(int8_t* d, int shift, size_t count) { - return Compare(d + shift, d, count - shift); + int Ppg::CompareShift(int8_t* d, int shift, size_t count) { + return Compare(d ,shift, count - shift); } - - int Trough(int8_t* d, size_t size, uint8_t mn, uint8_t mx) { + int Ppg::Trough(int8_t* d, size_t size, uint8_t mn, uint8_t mx) { auto z2 = CompareShift(d, mn - 2, size); auto z1 = CompareShift(d, mn - 1, size); for (int i = mn; i < mx + 1; i++) { @@ -36,13 +40,6 @@ namespace { } return -1; } -} - -Ppg::Ppg() - : hpf {0.87033078, -1.74066156, 0.87033078, -1.72377617, 0.75754694}, - agc {20, 0.971, 2}, - lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} { -} int8_t Ppg::Preprocess(float spl) { spl -= offset; @@ -52,43 +49,49 @@ int8_t Ppg::Preprocess(float spl) { auto spl_int = static_cast(spl); - if (dataIndex < DATA_SIZE) - data[dataIndex++] = spl_int; + data[dataIndex] = spl_int; + dataIndex = (dataIndex+1)%Ppg::DATA_SIZE; return spl_int; } float Ppg::HeartRate() { - if (dataIndex < DATA_SIZE) + if(data[DATA_SIZE-1] == 0){ return 0; + } NRF_LOG_INFO("PREPROCESS, offset = %d", offset); auto hr = ProcessHeartRate(); - dataIndex = 0; return hr; } float Ppg::ProcessHeartRate() { - auto t0 = Trough(data.data(), DATA_SIZE, 7, 48); + int t0 = Trough(data.data(), DATA_SIZE, 7, 48); if (t0 < 0) return 0; - float t1 = t0 * 2; - t1 = Trough(data.data(), DATA_SIZE, t1 - 5, t1 + 5); + int t1 = t0 * 2; + t1 = Trough(data.data(), DATA_SIZE, (t1 - 5), (t1 + 5)); if (t1 < 0) return 0; - float t2 = static_cast(t1 * 3) / 2; - t2 = Trough(data.data(), DATA_SIZE, t2 - 5, t2 + 5); + int t2 = (t1 * 3) / 2; + t2 = Trough(data.data(), DATA_SIZE,(t2 - 5),(t2 + 5)); if (t2 < 0) return 0; - float t3 = static_cast(t2 * 4) / 3; - t3 = Trough(data.data(), DATA_SIZE, t3 - 4, t3 + 4); + int t3 = (t2 * 4) / 3; + t3 = Trough(data.data(), DATA_SIZE,(t3 - 4), (t3 + 4)); if (t3 < 0) - return static_cast(60 * 24 * 3) / static_cast(t2); + return (60 * 24 * 3) / (t2); - return static_cast(60 * 24 * 4) / static_cast(t3); + return (60 * 24 * 4) / (t3); } +//Gets the Index in the Ring Buffer which corresponds to the index, if it was a 0-based Array +//DataIndex is where there was last data written to, so if the arrray was full once, it marks the end of the array +//The next Index is the start then. +int Ppg::getRingIndex(int8_t index){ + return (index+dataIndex)%Ppg::DATA_SIZE; +} void Ppg::SetOffset(uint16_t offset) { this->offset = offset; dataIndex = 0; diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index 9095c75a43..bf4249c288 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -10,15 +10,19 @@ namespace Pinetime { namespace Controllers { class Ppg { public: + static const uint8_t DATA_SIZE = 200; Ppg(); int8_t Preprocess(float spl); float HeartRate(); void SetOffset(uint16_t i); void Reset(); + int Compare(int8_t* d1, int shift, size_t count); + int CompareShift(int8_t* d, int shift, size_t count); + int Trough(int8_t* d, size_t size, uint8_t mn, uint8_t mx); + int getRingIndex(int8_t index); private: - static const uint8_t DATA_SIZE = 200; std::array data; size_t dataIndex = 0; float offset; From c59f9b560834575dfde3fe4222b3f33b77d3e85b Mon Sep 17 00:00:00 2001 From: watertrainer Date: Fri, 15 Apr 2022 13:00:28 +0000 Subject: [PATCH 3/7] variable to change the freq of heart rate update --- src/components/heartrate/Ppg.cpp | 6 ++++-- src/components/heartrate/Ppg.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index 47c0d38fc9..a6a8938293 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -55,7 +55,7 @@ int8_t Ppg::Preprocess(float spl) { } float Ppg::HeartRate() { - if(data[DATA_SIZE-1] == 0){ + if(data[DATA_SIZE-1] == 0 || dataIndex% Ppg::UPDATE_HEARTRATE_AFTER != 0){ return 0; } @@ -94,9 +94,11 @@ int Ppg::getRingIndex(int8_t index){ } void Ppg::SetOffset(uint16_t offset) { this->offset = offset; - dataIndex = 0; + this->Reset(); } void Ppg::Reset() { dataIndex = 0; + //invalidates the current array + data[DATA_SIZE-1] = 0; } diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index bf4249c288..4e0da19110 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -23,6 +23,7 @@ namespace Pinetime { int getRingIndex(int8_t index); private: + static const uint8_t UPDATE_HEARTRATE_AFTER = 200; std::array data; size_t dataIndex = 0; float offset; From ef84a228f4be4405d5a47c39eae7a8c947783f92 Mon Sep 17 00:00:00 2001 From: watertrainer Date: Fri, 15 Apr 2022 15:45:30 +0000 Subject: [PATCH 4/7] some clang-tidy cleanups/autoformat changed data Type of heartrate to int, it is only used as an int anyway --- src/components/heartrate/Ppg.cpp | 83 +++++++++++++++++--------------- src/components/heartrate/Ppg.h | 14 +++--- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index a6a8938293..8bc3ab9fd8 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -5,8 +5,8 @@ */ #include "components/heartrate/Ppg.h" -#include #include +#include using namespace Pinetime::Controllers; /** Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/ppg.py */ @@ -16,30 +16,30 @@ Ppg::Ppg() lpf {0.11595249, 0.23190498, 0.11595249, -0.72168143, 0.18549138} { } - int Ppg::Compare(int8_t* d1,int shift, size_t count) { - int e = 0; - for (size_t i = 0; i < count; i++) { - auto d = d1[getRingIndex(i+shift)] - d1[getRingIndex(i)]; - e += d * d; - } - return e; +int Ppg::Compare(int8_t* d1, int shift, size_t count) { + int e = 0; + for (size_t i = 0; i < count; i++) { + auto d = d1[getRingIndex(i + shift)] - d1[getRingIndex(i)]; + e += d * d; } + return e; +} - int Ppg::CompareShift(int8_t* d, int shift, size_t count) { - return Compare(d ,shift, count - shift); - } - int Ppg::Trough(int8_t* d, size_t size, uint8_t mn, uint8_t mx) { - auto z2 = CompareShift(d, mn - 2, size); - auto z1 = CompareShift(d, mn - 1, size); - for (int i = mn; i < mx + 1; i++) { - auto z = CompareShift(d, i, size); - if (z2 > z1 && z1 < z) - return i; - z2 = z1; - z1 = z; - } - return -1; +int Ppg::CompareShift(int8_t* d, int shift, size_t count) { + return Compare(d, shift, count - shift); +} +int Ppg::Trough(int8_t* d, size_t size, uint8_t mn, uint8_t mx) { + auto z2 = CompareShift(d, mn - 2, size); + auto z1 = CompareShift(d, mn - 1, size); + for (int i = mn; i < mx + 1; i++) { + auto z = CompareShift(d, i, size); + if (z2 > z1 && z1 < z) + return i; + z2 = z1; + z1 = z; } + return -1; +} int8_t Ppg::Preprocess(float spl) { spl -= offset; @@ -50,47 +50,50 @@ int8_t Ppg::Preprocess(float spl) { auto spl_int = static_cast(spl); data[dataIndex] = spl_int; - dataIndex = (dataIndex+1)%Ppg::DATA_SIZE; + dataIndex = (dataIndex + 1) % Ppg::DATA_SIZE; return spl_int; } -float Ppg::HeartRate() { - if(data[DATA_SIZE-1] == 0 || dataIndex% Ppg::UPDATE_HEARTRATE_AFTER != 0){ +int Ppg::HeartRate() { + if (data[DATA_SIZE - 1] == 0 || dataIndex % Ppg::UPDATE_HEARTRATE_AFTER != 0) { return 0; } NRF_LOG_INFO("PREPROCESS, offset = %d", offset); - auto hr = ProcessHeartRate(); + int hr = ProcessHeartRate(); return hr; } -float Ppg::ProcessHeartRate() { +int Ppg::ProcessHeartRate() { int t0 = Trough(data.data(), DATA_SIZE, 7, 48); - if (t0 < 0) + if (t0 < 0) { return 0; + } int t1 = t0 * 2; t1 = Trough(data.data(), DATA_SIZE, (t1 - 5), (t1 + 5)); - if (t1 < 0) + if (t1 < 0) { return 0; + } int t2 = (t1 * 3) / 2; - t2 = Trough(data.data(), DATA_SIZE,(t2 - 5),(t2 + 5)); - if (t2 < 0) + t2 = Trough(data.data(), DATA_SIZE, (t2 - 5), (t2 + 5)); + if (t2 < 0) { return 0; + } int t3 = (t2 * 4) / 3; - t3 = Trough(data.data(), DATA_SIZE,(t3 - 4), (t3 + 4)); - if (t3 < 0) + t3 = Trough(data.data(), DATA_SIZE, (t3 - 4), (t3 + 4)); + if (t3 < 0) { return (60 * 24 * 3) / (t2); + } return (60 * 24 * 4) / (t3); } -//Gets the Index in the Ring Buffer which corresponds to the index, if it was a 0-based Array -//DataIndex is where there was last data written to, so if the arrray was full once, it marks the end of the array -//The next Index is the start then. -int Ppg::getRingIndex(int8_t index){ - return (index+dataIndex)%Ppg::DATA_SIZE; +// Gets the Index in the Ring Buffer which corresponds to the index, if it was a 0-based Array +// dataIndex points to the next index to write to, so it is the start of the buffer and the last element is at dataIndex-1 +int Ppg::getRingIndex(int8_t index) { + return (index + dataIndex) % Ppg::DATA_SIZE; } void Ppg::SetOffset(uint16_t offset) { this->offset = offset; @@ -99,6 +102,6 @@ void Ppg::SetOffset(uint16_t offset) { void Ppg::Reset() { dataIndex = 0; - //invalidates the current array - data[DATA_SIZE-1] = 0; + // invalidates the current array + data[DATA_SIZE - 1] = 0; } diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index 4e0da19110..d0e6e13483 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -13,17 +13,13 @@ namespace Pinetime { static const uint8_t DATA_SIZE = 200; Ppg(); int8_t Preprocess(float spl); - float HeartRate(); + int HeartRate(); void SetOffset(uint16_t i); void Reset(); - int Compare(int8_t* d1, int shift, size_t count); - int CompareShift(int8_t* d, int shift, size_t count); - int Trough(int8_t* d, size_t size, uint8_t mn, uint8_t mx); - int getRingIndex(int8_t index); private: - static const uint8_t UPDATE_HEARTRATE_AFTER = 200; + static const uint8_t UPDATE_HEARTRATE_AFTER = 200; std::array data; size_t dataIndex = 0; float offset; @@ -31,7 +27,11 @@ namespace Pinetime { Ptagc agc; Biquad lpf; - float ProcessHeartRate(); + int getRingIndex(int8_t index); + int Compare(int8_t* d1, int shift, size_t count); + int CompareShift(int8_t* d, int shift, size_t count); + int Trough(int8_t* d, size_t size, uint8_t mn, uint8_t mx); + int ProcessHeartRate(); }; } } From c6d76739ff18585eac6d41720b82ea4d24bf5b27 Mon Sep 17 00:00:00 2001 From: watertrainer Date: Mon, 25 Apr 2022 17:14:28 +0000 Subject: [PATCH 5/7] replaced DATA_SIZE variable with data.size() --- src/components/heartrate/Ppg.cpp | 16 ++++++++-------- src/components/heartrate/Ppg.h | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index 8bc3ab9fd8..fa1a52f7c3 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -50,12 +50,12 @@ int8_t Ppg::Preprocess(float spl) { auto spl_int = static_cast(spl); data[dataIndex] = spl_int; - dataIndex = (dataIndex + 1) % Ppg::DATA_SIZE; + dataIndex = (dataIndex + 1) % Ppg::data.size(); return spl_int; } int Ppg::HeartRate() { - if (data[DATA_SIZE - 1] == 0 || dataIndex % Ppg::UPDATE_HEARTRATE_AFTER != 0) { + if (data[data.size() - 1] == 0 || dataIndex % Ppg::UPDATE_HEARTRATE_AFTER != 0) { return 0; } @@ -64,25 +64,25 @@ int Ppg::HeartRate() { return hr; } int Ppg::ProcessHeartRate() { - int t0 = Trough(data.data(), DATA_SIZE, 7, 48); + int t0 = Trough(data.data(), data.size(), 7, 48); if (t0 < 0) { return 0; } int t1 = t0 * 2; - t1 = Trough(data.data(), DATA_SIZE, (t1 - 5), (t1 + 5)); + t1 = Trough(data.data(), data.size(), (t1 - 5), (t1 + 5)); if (t1 < 0) { return 0; } int t2 = (t1 * 3) / 2; - t2 = Trough(data.data(), DATA_SIZE, (t2 - 5), (t2 + 5)); + t2 = Trough(data.data(), data.size(), (t2 - 5), (t2 + 5)); if (t2 < 0) { return 0; } int t3 = (t2 * 4) / 3; - t3 = Trough(data.data(), DATA_SIZE, (t3 - 4), (t3 + 4)); + t3 = Trough(data.data(), data.size(), (t3 - 4), (t3 + 4)); if (t3 < 0) { return (60 * 24 * 3) / (t2); } @@ -93,7 +93,7 @@ int Ppg::ProcessHeartRate() { // Gets the Index in the Ring Buffer which corresponds to the index, if it was a 0-based Array // dataIndex points to the next index to write to, so it is the start of the buffer and the last element is at dataIndex-1 int Ppg::getRingIndex(int8_t index) { - return (index + dataIndex) % Ppg::DATA_SIZE; + return (index + dataIndex) % data.size(); } void Ppg::SetOffset(uint16_t offset) { this->offset = offset; @@ -103,5 +103,5 @@ void Ppg::SetOffset(uint16_t offset) { void Ppg::Reset() { dataIndex = 0; // invalidates the current array - data[DATA_SIZE - 1] = 0; + data[data.size() - 1] = 0; } diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index d0e6e13483..6f73237be2 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -10,7 +10,6 @@ namespace Pinetime { namespace Controllers { class Ppg { public: - static const uint8_t DATA_SIZE = 200; Ppg(); int8_t Preprocess(float spl); int HeartRate(); @@ -20,7 +19,7 @@ namespace Pinetime { private: static const uint8_t UPDATE_HEARTRATE_AFTER = 200; - std::array data; + std::array data; size_t dataIndex = 0; float offset; Biquad hpf; From faf2faabea986b1efebb204beab3a4f439008a56 Mon Sep 17 00:00:00 2001 From: watertrainer Date: Mon, 25 Apr 2022 17:19:40 +0000 Subject: [PATCH 6/7] removed unnecessary parenthesis --- src/components/heartrate/Ppg.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index fa1a52f7c3..1dc06985f1 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -70,24 +70,24 @@ int Ppg::ProcessHeartRate() { } int t1 = t0 * 2; - t1 = Trough(data.data(), data.size(), (t1 - 5), (t1 + 5)); + t1 = Trough(data.data(), data.size(), t1 - 5, t1 + 5); if (t1 < 0) { return 0; } int t2 = (t1 * 3) / 2; - t2 = Trough(data.data(), data.size(), (t2 - 5), (t2 + 5)); + t2 = Trough(data.data(), data.size(), t2 - 5, t2 + 5); if (t2 < 0) { return 0; } int t3 = (t2 * 4) / 3; - t3 = Trough(data.data(), data.size(), (t3 - 4), (t3 + 4)); + t3 = Trough(data.data(), data.size(), t3 - 4, t3 + 4); if (t3 < 0) { - return (60 * 24 * 3) / (t2); + return (60 * 24 * 3) / t2; } - return (60 * 24 * 4) / (t3); + return (60 * 24 * 4) / t3; } // Gets the Index in the Ring Buffer which corresponds to the index, if it was a 0-based Array From 6fb413a85fc1fb9293d22ca667677dd0381dbda0 Mon Sep 17 00:00:00 2001 From: watertrainer Date: Thu, 28 Apr 2022 16:59:36 +0000 Subject: [PATCH 7/7] introduced boolean, if data was filled up at least once --- src/components/heartrate/Ppg.cpp | 8 +++++--- src/components/heartrate/Ppg.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index 1dc06985f1..20849f21a7 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -51,11 +51,14 @@ int8_t Ppg::Preprocess(float spl) { data[dataIndex] = spl_int; dataIndex = (dataIndex + 1) % Ppg::data.size(); + if (dataIndex == 0) { + dataReady = true; + } return spl_int; } int Ppg::HeartRate() { - if (data[data.size() - 1] == 0 || dataIndex % Ppg::UPDATE_HEARTRATE_AFTER != 0) { + if (!dataReady || dataIndex % Ppg::UPDATE_HEARTRATE_AFTER != 0) { return 0; } @@ -102,6 +105,5 @@ void Ppg::SetOffset(uint16_t offset) { void Ppg::Reset() { dataIndex = 0; - // invalidates the current array - data[data.size() - 1] = 0; + dataReady = false; } diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index 6f73237be2..cb7ba5e623 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -21,6 +21,7 @@ namespace Pinetime { static const uint8_t UPDATE_HEARTRATE_AFTER = 200; std::array data; size_t dataIndex = 0; + bool dataReady = false; float offset; Biquad hpf; Ptagc agc;