From cb3774ad8140252c6ccc961477f994922823ef82 Mon Sep 17 00:00:00 2001 From: Totoo Date: Thu, 17 Oct 2024 10:22:01 +0200 Subject: [PATCH] Increase sensitivity (#2309) Increase sensitivity of Weather and SubghzD apps. --- .../application/apps/ui_weatherstation.cpp | 41 ++++ firmware/baseband/fprotos/w-bresser_3ch.hpp | 197 ++++++++++++++++++ firmware/baseband/fprotos/w-vauno_en8822.hpp | 88 ++++++++ firmware/baseband/fprotos/weatherprotos.hpp | 7 +- firmware/baseband/fprotos/weathertypes.hpp | 3 + firmware/baseband/proc_subghzd.cpp | 73 +++++-- firmware/baseband/proc_subghzd.hpp | 24 ++- firmware/baseband/proc_weather.cpp | 71 +++++-- firmware/baseband/proc_weather.hpp | 20 +- 9 files changed, 486 insertions(+), 38 deletions(-) create mode 100644 firmware/baseband/fprotos/w-bresser_3ch.hpp create mode 100644 firmware/baseband/fprotos/w-vauno_en8822.hpp diff --git a/firmware/application/apps/ui_weatherstation.cpp b/firmware/application/apps/ui_weatherstation.cpp index bf63094c..21fd5911 100644 --- a/firmware/application/apps/ui_weatherstation.cpp +++ b/firmware/application/apps/ui_weatherstation.cpp @@ -213,6 +213,11 @@ const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) { return "EmosE601x"; case FPW_SolightTE44: return "SolightTE44"; + case FPW_Bresser3CH: + case FPW_Bresser3CH_V1: + return "Bresser3CH"; + case FPW_Vauno_EN8822: + return "Vauno EN8822"; case FPW_Invalid: default: return "Unknown"; @@ -532,6 +537,42 @@ WeatherRecentEntry WeatherView::process_data(const WeatherDataMessage* data) { i16 |= 0xf000; } ret.temp = (float)i16 / 10.0; + break; + case FPW_Bresser3CH: + ret.id = (data->decode_data >> 28) & 0xff; + ret.channel = ((data->decode_data >> 27) & 0x01) | (((data->decode_data >> 26) & 0x01) << 1); + // ret.btn = ((data->decode_data >> 25) & 0x1); + ret.battery_low = ((data->decode_data >> 24) & 0x1); + i16 = (data->decode_data >> 12) & 0x0fff; + /* Handle signed data */ + if (i16 & 0x0800) { + i16 |= 0xf000; + } + ret.temp = (float)i16 / 10.0; + ret.humidity = data->decode_data & 0xff; + break; + case FPW_Bresser3CH_V1: + ret.id = (data->decode_data >> 32) & 0xff; + ret.battery_low = ((data->decode_data >> 31) & 0x1); + // ret.btn = (data->decode_data >> 30) & 0x1; + ret.channel = (data->decode_data >> 28) & 0x3; + ret.temp = (data->decode_data >> 16) & 0xfff; + ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)(ret.temp - 900) / 10.0); + ret.humidity = (data->decode_data >> 8) & 0xff; + break; + + case FPW_Vauno_EN8822: + ret.id = (data->decode_data >> 34) & 0xff; + ret.battery_low = (data->decode_data >> 33) & 0x01; + ret.channel = ((data->decode_data >> 30) & 0x03); + i16 = (data->decode_data >> 18) & 0x0fff; + /* Handle signed data */ + if (i16 & 0x0800) { + i16 |= 0xf000; + } + ret.temp = (float)i16 / 10.0; + ret.humidity = (data->decode_data >> 11) & 0x7f; + break; case FPW_Invalid: default: diff --git a/firmware/baseband/fprotos/w-bresser_3ch.hpp b/firmware/baseband/fprotos/w-bresser_3ch.hpp new file mode 100644 index 00000000..dcc5950c --- /dev/null +++ b/firmware/baseband/fprotos/w-bresser_3ch.hpp @@ -0,0 +1,197 @@ + +#ifndef __FPROTO_Bresser3ch_H__ +#define __FPROTO_Bresser3ch_H__ + +#include "weatherbase.hpp" + +#define BRESSER_V0_DATA 36 +#define BRESSER_V0_DATA_AND_TAIL 52 +#define BRESSER_V1_DATA 40 + +typedef enum { + Bresser3chDecoderStepReset = 0, + Bresser3chDecoderStepV0SaveDuration, + Bresser3chDecoderStepV0CheckDuration, + Bresser3chDecoderStepV0TailCheckDuration, + Bresser3chDecoderStepV1PreambleDn, + Bresser3chDecoderStepV1PreambleUp, + Bresser3chDecoderStepV1SaveDuration, + Bresser3chDecoderStepV1CheckDuration, +} Bresser3chDecoderStepV1; + +class FProtoWeatheBresser3CH : public FProtoWeatherBase { + public: + FProtoWeatheBresser3CH() { + sensorType = FPW_Bresser3CH; + } + + void feed(bool level, uint32_t duration) { + switch (parser_step) { + case Bresser3chDecoderStepReset: + if (level && DURATION_DIFF(duration, te_short * 3) < te_delta) { + te_last = duration; + parser_step = Bresser3chDecoderStepV1PreambleDn; + decode_data = 0; + decode_count_bit = 0; + } else if ((!level) && duration >= te_long) { + parser_step = Bresser3chDecoderStepV0SaveDuration; + decode_data = 0; + decode_count_bit = 0; + } + break; + + case Bresser3chDecoderStepV0SaveDuration: + if (level) { + te_last = duration; + if (decode_count_bit < BRESSER_V0_DATA) { + parser_step = Bresser3chDecoderStepV0CheckDuration; + } else { + parser_step = Bresser3chDecoderStepV0TailCheckDuration; + } + } else { + parser_step = Bresser3chDecoderStepReset; + } + break; + + case Bresser3chDecoderStepV0CheckDuration: + if (!level) { + if (DURATION_DIFF(te_last, te_short) < te_delta) { + if (DURATION_DIFF(duration, te_short * 2) < te_delta) { + subghz_protocol_blocks_add_bit(0); + parser_step = Bresser3chDecoderStepV0SaveDuration; + } else if ( + DURATION_DIFF(duration, te_short * 4) < te_delta) { + subghz_protocol_blocks_add_bit(1); + parser_step = Bresser3chDecoderStepV0SaveDuration; + } else + parser_step = Bresser3chDecoderStepReset; + } else + parser_step = Bresser3chDecoderStepReset; + } else + parser_step = Bresser3chDecoderStepReset; + break; + + case Bresser3chDecoderStepV0TailCheckDuration: + if (!level) { + if (duration >= te_long) { + if (decode_count_bit == BRESSER_V0_DATA_AND_TAIL && + ws_protocol_bresser_3ch_check_v0()) { + decode_count_bit = BRESSER_V0_DATA; + sensorType = FPW_Bresser3CH; + // ws_protocol_bresser_3ch_extract_data_v0(); + if (callback) { + callback(this); + } + } + decode_data = 0; + decode_count_bit = 0; + parser_step = Bresser3chDecoderStepReset; + } else if ( + decode_count_bit < BRESSER_V0_DATA_AND_TAIL && + DURATION_DIFF(te_last, te_short) < te_delta && + DURATION_DIFF(duration, te_short * 2) < te_delta) { + decode_count_bit++; + parser_step = Bresser3chDecoderStepV0SaveDuration; + } else + parser_step = Bresser3chDecoderStepReset; + } else + parser_step = Bresser3chDecoderStepReset; + break; + + case Bresser3chDecoderStepV1PreambleDn: + if ((!level) && DURATION_DIFF(duration, te_short_v1 * 3) < te_delta) { + if (DURATION_DIFF(te_last, te_short_v1 * 12) < te_delta * 2) { + // End of sync after 4*750 (12*250) high values, start reading the message + parser_step = Bresser3chDecoderStepV1SaveDuration; + } else { + parser_step = Bresser3chDecoderStepV1PreambleUp; + } + } else { + parser_step = Bresser3chDecoderStepReset; + } + break; + + case Bresser3chDecoderStepV1PreambleUp: + if (level && DURATION_DIFF(duration, te_short_v1 * 3) < te_delta) { + te_last = te_last + duration; + parser_step = Bresser3chDecoderStepV1PreambleDn; + } else { + parser_step = Bresser3chDecoderStepReset; + } + break; + + case Bresser3chDecoderStepV1SaveDuration: + if (decode_count_bit == BRESSER_V1_DATA) { + if (ws_protocol_bresser_3ch_check_v1()) { + // ws_protocol_bresser_3ch_extract_data_v1(&instance->generic); + sensorType = FPW_Bresser3CH_V1; + if (callback) { + callback(this); + } + } + decode_data = 0; + decode_count_bit = 0; + parser_step = Bresser3chDecoderStepReset; + } else if (level) { + te_last = duration; + parser_step = Bresser3chDecoderStepV1CheckDuration; + } else { + parser_step = Bresser3chDecoderStepReset; + } + break; + + case Bresser3chDecoderStepV1CheckDuration: + if (!level) { + if (DURATION_DIFF(te_last, te_short_v1) < te_delta && DURATION_DIFF(duration, te_long_v1) < te_delta) { + subghz_protocol_blocks_add_bit(0); + parser_step = Bresser3chDecoderStepV1SaveDuration; + } else if ( + DURATION_DIFF(te_last, te_long_v1) < te_delta && DURATION_DIFF(duration, te_short_v1) < te_delta) { + subghz_protocol_blocks_add_bit(1); + parser_step = Bresser3chDecoderStepV1SaveDuration; + } else + parser_step = Bresser3chDecoderStepReset; + } else + parser_step = Bresser3chDecoderStepReset; + break; + } + } + + protected: + uint32_t te_short = 475; + uint32_t te_long = 3900; + uint32_t te_delta = 150; + + uint32_t te_short_v1 = 250; + uint32_t te_long_v1 = 500; + + bool ws_protocol_bresser_3ch_check_v0() { + if (!decode_data) return false; + // No CRC, so better sanity checks here + if (((decode_data >> 8) & 0x0f) != 0x0f) return false; // separator not 0xf + if (((decode_data >> 28) & 0xff) == 0xff) return false; // ID only ones? + if (((decode_data >> 28) & 0xff) == 0x00) return false; // ID only zeroes? + if (((decode_data >> 25) & 0x0f) == 0x0f) return false; // flags only ones? + if (((decode_data >> 25) & 0x0f) == 0x00) return false; // flags only zeroes? + if (((decode_data >> 12) & 0x0fff) == 0x0fff) + return false; // temperature maxed out? + if ((decode_data & 0xff) < 20) + return false; // humidity percentage less than 20? + if ((decode_data & 0xff) > 95) + return false; // humidity percentage more than 95? + return true; + } + bool ws_protocol_bresser_3ch_check_v1() { + if (!decode_data) return false; + + uint8_t sum = (((decode_data >> 32) & 0xff) + + ((decode_data >> 24) & 0xff) + + ((decode_data >> 16) & 0xff) + + ((decode_data >> 8) & 0xff)) & + 0xff; + + return (decode_data & 0xff) == sum; + } +}; + +#endif diff --git a/firmware/baseband/fprotos/w-vauno_en8822.hpp b/firmware/baseband/fprotos/w-vauno_en8822.hpp new file mode 100644 index 00000000..73dbda70 --- /dev/null +++ b/firmware/baseband/fprotos/w-vauno_en8822.hpp @@ -0,0 +1,88 @@ + +#ifndef __FPROTO_VAUNO_EN8822_H__ +#define __FPROTO_VAUNO_EN8822_H__ + +#include "weatherbase.hpp" + +typedef enum { + VaunoEN8822CDecoderStepReset = 0, + VaunoEN8822CDecoderStepSaveDuration, + VaunoEN8822CDecoderStepCheckDuration, +} VaunoEN8822CDecoderStep; + +class FProtoWeatherVaunoEN8822 : public FProtoWeatherBase { + public: + FProtoWeatherVaunoEN8822() { + sensorType = FPW_Vauno_EN8822; + } + + void feed(bool level, uint32_t duration) override { + switch (parser_step) { + case VaunoEN8822CDecoderStepReset: + if ((!level) && DURATION_DIFF(duration, te_long * 4) < te_delta) { + parser_step = VaunoEN8822CDecoderStepSaveDuration; + decode_data = 0; + decode_count_bit = 0; + } + break; + + case VaunoEN8822CDecoderStepSaveDuration: + if (level) { + te_last = duration; + parser_step = VaunoEN8822CDecoderStepCheckDuration; + } else { + parser_step = VaunoEN8822CDecoderStepReset; + } + break; + + case VaunoEN8822CDecoderStepCheckDuration: + if (!level) { + if (DURATION_DIFF(te_last, te_short) < te_delta) { + if (DURATION_DIFF(duration, te_long * 2) < te_delta) { + subghz_protocol_blocks_add_bit(1); + parser_step = VaunoEN8822CDecoderStepSaveDuration; + } else if (DURATION_DIFF(duration, te_long) < te_delta) { + subghz_protocol_blocks_add_bit(0); + parser_step = VaunoEN8822CDecoderStepSaveDuration; + } else if (DURATION_DIFF(duration, te_long * 4) < te_delta) { + parser_step = VaunoEN8822CDecoderStepReset; + if (decode_count_bit == min_count_bit_for_found && ws_protocol_vauno_en8822c_check()) { + // ws_protocol_vauno_en8822c_extract_data(&instance->generic); + + if (callback) { + callback(this); + } + } else if (decode_count_bit == 1) { + parser_step = VaunoEN8822CDecoderStepSaveDuration; + } + decode_data = 0; + decode_count_bit = 0; + } else + parser_step = VaunoEN8822CDecoderStepReset; + } else + parser_step = VaunoEN8822CDecoderStepReset; + } else + parser_step = VaunoEN8822CDecoderStepReset; + break; + } + } + + protected: + // timing values + uint32_t te_short = 500; + uint32_t te_long = 1940; + uint32_t te_delta = 150; + uint32_t min_count_bit_for_found = 42; + + bool ws_protocol_vauno_en8822c_check() { + if (!decode_data) return false; + // The sum of all nibbles should match the last 6 bits + uint8_t sum = 0; + for (uint8_t i = 6; i <= 38; i += 4) { + sum += ((decode_data >> i) & 0x0f); + } + return sum != 0 && (sum & 0x3f) == (decode_data & 0x3f); + } +}; + +#endif diff --git a/firmware/baseband/fprotos/weatherprotos.hpp b/firmware/baseband/fprotos/weatherprotos.hpp index a4b82a13..efcb4220 100644 --- a/firmware/baseband/fprotos/weatherprotos.hpp +++ b/firmware/baseband/fprotos/weatherprotos.hpp @@ -29,6 +29,8 @@ So include here the .hpp, and add a new element to the protos vector in the cons #include "w-acurite5in1.hpp" #include "w-emose601x.hpp" #include "w-solight_te44.hpp" +#include "w-bresser_3ch.hpp" +#include "w-vauno_en8822.hpp" #include #include @@ -66,10 +68,13 @@ class WeatherProtos : public FProtoListGeneral { protos[FPW_Acurite5in1] = new FProtoWeatherAcurite5in1(); protos[FPW_EmosE601x] = new FProtoWeatherEmosE601x(); protos[FPW_SolightTE44] = new FProtoWeatherSolightTE44(); + protos[FPW_Bresser3CH] = new FProtoWeatheBresser3CH(); + protos[FPW_Bresser3CH_V1] = nullptr; // done by FProtoWeatheBresser3CH + protos[FPW_Vauno_EN8822] = new FProtoWeatherVaunoEN8822(); // set callback for them for (uint8_t i = 0; i < FPW_COUNT; ++i) { - protos[i]->setCallback(callbackTarget); + if (protos[i] != NULL) protos[i]->setCallback(callbackTarget); } } diff --git a/firmware/baseband/fprotos/weathertypes.hpp b/firmware/baseband/fprotos/weathertypes.hpp index ce10d672..fd6d043b 100644 --- a/firmware/baseband/fprotos/weathertypes.hpp +++ b/firmware/baseband/fprotos/weathertypes.hpp @@ -39,6 +39,9 @@ enum FPROTO_WEATHER_SENSOR { FPW_Acurite5in1 = 21, FPW_EmosE601x = 22, FPW_SolightTE44 = 23, + FPW_Bresser3CH = 24, + FPW_Bresser3CH_V1 = 25, + FPW_Vauno_EN8822 = 26, FPW_COUNT // this must be the last }; diff --git a/firmware/baseband/proc_subghzd.cpp b/firmware/baseband/proc_subghzd.cpp index 4743d872..6dfa14ba 100644 --- a/firmware/baseband/proc_subghzd.cpp +++ b/firmware/baseband/proc_subghzd.cpp @@ -38,32 +38,79 @@ void SubGhzDProcessor::execute(const buffer_c8_t& buffer) { feed_channel_stats(decim_1_out); for (size_t i = 0; i < decim_1_out.count; i++) { + threshold = (low_estimate + high_estimate) / 2; + int32_t const hysteresis = threshold / 8; // +-12% int16_t re = decim_1_out.p[i].real(); int16_t im = decim_1_out.p[i].imag(); uint32_t mag = ((uint32_t)re * (uint32_t)re) + ((uint32_t)im * (uint32_t)im); - mag = (mag >> 12); // Decim samples are calculated with saturated gain . (we could also reduce that sat. param at configure time) + mag = (mag >> 10); + int32_t const ook_low_delta = mag - low_estimate; + bool meashl = currentHiLow; + if (sig_state == STATE_IDLE) { + if (mag > (threshold + hysteresis)) { // just become high + meashl = true; + sig_state = STATE_PULSE; + numg = 0; + } else { + meashl = false; // still low + low_estimate += ook_low_delta / OOK_EST_LOW_RATIO; + low_estimate += ((ook_low_delta > 0) ? 1 : -1); // Hack to compensate for lack of fixed-point scaling + // Calculate default OOK high level estimate + high_estimate = 1.35 * low_estimate; // Default is a ratio of low level + high_estimate = std::max(high_estimate, min_high_level); + high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL); + } + + } else if (sig_state == STATE_PULSE) { + ++numg; + if (numg > 100) numg = 100; + if (mag < (threshold - hysteresis)) { + // check if really a bad value + if (numg < 3) { + // susp + sig_state = STATE_GAP; + } else { + numg = 0; + sig_state = STATE_GAP_START; + } + meashl = false; // low + } else { + high_estimate += mag / OOK_EST_HIGH_RATIO - high_estimate / OOK_EST_HIGH_RATIO; + high_estimate = std::max(high_estimate, min_high_level); + high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL); + meashl = true; // still high + } + } else if (sig_state == STATE_GAP_START) { + ++numg; + if (mag > (threshold + hysteresis)) { // New pulse? + sig_state = STATE_PULSE; + meashl = true; + } else if (numg >= 3) { + sig_state = STATE_GAP; + meashl = false; // gap + } + } else if (sig_state == STATE_GAP) { + ++numg; + if (mag > (threshold + hysteresis)) { // New pulse? + numg = 0; + sig_state = STATE_PULSE; + meashl = true; + } else { + meashl = false; + } + } - bool meashl = (mag > threshold); - tm += mag; if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal { currentDuration += nsPerDecSamp; } else { // called on change, so send the last duration and dir. + if (currentDuration >= 30'000'000) sig_state = STATE_IDLE; if (protoList) protoList->feed(currentHiLow, currentDuration / 1000); currentDuration = nsPerDecSamp; currentHiLow = meashl; } } - - cnt += decim_1_out.count; // TODO , check if it is necessary that xdecim factor. - if (cnt > 90'000) { - threshold = (tm / cnt) / 2; - cnt = 0; - tm = 0; - if (threshold < 50) threshold = 50; - if (threshold > 1700) threshold = 1700; - } } void SubGhzDProcessor::on_message(const Message* const message) { @@ -75,8 +122,6 @@ void SubGhzDProcessor::configure(const SubGhzFPRxConfigureMessage& message) { // constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; //unused // constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor; //unused - modulation = message.modulation; // TODO: add support for FM (currently AM only) - baseband_fs = message.sampling_rate; baseband_thread.set_sampling_rate(baseband_fs); nsPerDecSamp = 1'000'000'000 / baseband_fs * 8; // Scaled it due to less array buffer sampes due to /8 decimation. 250 nseg (4Mhz) * 8 diff --git a/firmware/baseband/proc_subghzd.hpp b/firmware/baseband/proc_subghzd.hpp index b064c540..0650b3d3 100644 --- a/firmware/baseband/proc_subghzd.hpp +++ b/firmware/baseband/proc_subghzd.hpp @@ -32,7 +32,14 @@ #include "message.hpp" #include "dsp_decimate.hpp" +#pragma GCC push_options +#pragma GCC optimize("Os") #include "fprotos/subghzdprotos.hpp" +#pragma GCC pop_options + +#define OOK_EST_HIGH_RATIO 3 // Constant for slowness of OOK high level estimator +#define OOK_EST_LOW_RATIO 5 // Constant for slowness of OOK low level (noise) estimator (very slow) +#define OOK_MAX_HIGH_LEVEL 450000 class SubGhzDProcessor : public BasebandProcessor { public: @@ -40,9 +47,18 @@ class SubGhzDProcessor : public BasebandProcessor { void on_message(const Message* const message) override; private: + enum { + STATE_IDLE = 0, + STATE_PULSE = 1, + STATE_GAP_START = 2, + STATE_GAP = 3, + } sig_state = STATE_IDLE; + uint32_t low_estimate = 100; + uint32_t high_estimate = 12000; + uint32_t min_high_level = 10; + uint8_t numg = 0; // count of matched signals to filter spikes size_t baseband_fs = 0; // will be set later by configure message uint32_t nsPerDecSamp = 0; - uint8_t modulation = 0; /* Array Buffer aux. used in decim0 and decim1 IQ c16 signed data ; (decim0 defines the max length of the array) */ std::array dst{}; // decim0 /4 , 2048/4 = 512 complex I,Q @@ -55,14 +71,10 @@ class SubGhzDProcessor : public BasebandProcessor { dsp::decimate::FIRC16xR16x16Decim2 decim_1{}; uint32_t currentDuration = 0; - uint32_t threshold = 0x0630; // will overwrite after the first iteration + uint32_t threshold = 0x0630; bool currentHiLow = false; bool configured{false}; - // for threshold - uint32_t cnt = 0; - uint32_t tm = 0; - FProtoListGeneral* protoList = new SubGhzDProtos(); // holds all the protocols we can parse void configure(const SubGhzFPRxConfigureMessage& message); diff --git a/firmware/baseband/proc_weather.cpp b/firmware/baseband/proc_weather.cpp index 6939af8e..ae738c67 100644 --- a/firmware/baseband/proc_weather.cpp +++ b/firmware/baseband/proc_weather.cpp @@ -39,32 +39,79 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) { feed_channel_stats(decim_1_out); for (size_t i = 0; i < decim_1_out.count; i++) { + threshold = (low_estimate + high_estimate) / 2; + int32_t const hysteresis = threshold / 8; // +-12% int16_t re = decim_1_out.p[i].real(); int16_t im = decim_1_out.p[i].imag(); uint32_t mag = ((uint32_t)re * (uint32_t)re) + ((uint32_t)im * (uint32_t)im); - mag = (mag >> 12); // Decim samples are calculated with saturated gain . (we could also reduce that sat. param at configure time) + mag = (mag >> 10); + int32_t const ook_low_delta = mag - low_estimate; + bool meashl = currentHiLow; + if (sig_state == STATE_IDLE) { + if (mag > (threshold + hysteresis)) { // just become high + meashl = true; + sig_state = STATE_PULSE; + numg = 0; + } else { + meashl = false; // still low + low_estimate += ook_low_delta / OOK_EST_LOW_RATIO; + low_estimate += ((ook_low_delta > 0) ? 1 : -1); // Hack to compensate for lack of fixed-point scaling + // Calculate default OOK high level estimate + high_estimate = 1.35 * low_estimate; // Default is a ratio of low level + high_estimate = std::max(high_estimate, min_high_level); + high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL); + } + + } else if (sig_state == STATE_PULSE) { + ++numg; + if (numg > 100) numg = 100; + if (mag < (threshold - hysteresis)) { + // check if really a bad value + if (numg < 3) { + // susp + sig_state = STATE_GAP; + } else { + numg = 0; + sig_state = STATE_GAP_START; + } + meashl = false; // low + } else { + high_estimate += mag / OOK_EST_HIGH_RATIO - high_estimate / OOK_EST_HIGH_RATIO; + high_estimate = std::max(high_estimate, min_high_level); + high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL); + meashl = true; // still high + } + } else if (sig_state == STATE_GAP_START) { + ++numg; + if (mag > (threshold + hysteresis)) { // New pulse? + sig_state = STATE_PULSE; + meashl = true; + } else if (numg >= 3) { + sig_state = STATE_GAP; + meashl = false; // gap + } + } else if (sig_state == STATE_GAP) { + ++numg; + if (mag > (threshold + hysteresis)) { // New pulse? + numg = 0; + sig_state = STATE_PULSE; + meashl = true; + } else { + meashl = false; + } + } - bool meashl = (mag > threshold); - tm += mag; if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal { currentDuration += nsPerDecSamp; } else { // called on change, so send the last duration and dir. + if (currentDuration >= 30'000'000) sig_state = STATE_IDLE; if (protoList) protoList->feed(currentHiLow, currentDuration / 1000); currentDuration = nsPerDecSamp; currentHiLow = meashl; } } - - cnt += decim_1_out.count; // TODO , check if it is necessary that xdecim factor. - if (cnt > 90'000) { - threshold = (tm / cnt) / 2; - cnt = 0; - tm = 0; - if (threshold < 50) threshold = 50; - if (threshold > 1700) threshold = 1700; - } } void WeatherProcessor::on_message(const Message* const message) { diff --git a/firmware/baseband/proc_weather.hpp b/firmware/baseband/proc_weather.hpp index 4945db34..ec272b58 100644 --- a/firmware/baseband/proc_weather.hpp +++ b/firmware/baseband/proc_weather.hpp @@ -26,6 +26,7 @@ #ifndef __PROC_WEATHER_H__ #define __PROC_WEATHER_H__ +#include #include "baseband_processor.hpp" #include "baseband_thread.hpp" #include "rssi_thread.hpp" @@ -34,15 +35,28 @@ #include "fprotos/weatherprotos.hpp" +#define OOK_EST_HIGH_RATIO 3 // Constant for slowness of OOK high level estimator +#define OOK_EST_LOW_RATIO 5 // Constant for slowness of OOK low level (noise) estimator (very slow) +#define OOK_MAX_HIGH_LEVEL 450000 + class WeatherProcessor : public BasebandProcessor { public: void execute(const buffer_c8_t& buffer) override; void on_message(const Message* const message) override; private: + enum { + STATE_IDLE = 0, + STATE_PULSE = 1, + STATE_GAP_START = 2, + STATE_GAP = 3, + } sig_state = STATE_IDLE; + uint32_t low_estimate = 100; + uint32_t high_estimate = 12000; + uint32_t min_high_level = 10; size_t baseband_fs = 0; // will be set later by configure message. uint32_t nsPerDecSamp = 0; - + uint8_t numg = 0; // count of matched signals to filter spikes /* Array Buffer aux. used in decim0 and decim1 IQ c16 signed data ; (decim0 defines the max length of the array) */ std::array dst{}; // decim0 /4 , 2048/4 = 512 complex I,Q const buffer_c16_t dst_buffer{ @@ -58,10 +72,6 @@ class WeatherProcessor : public BasebandProcessor { bool currentHiLow = false; bool configured{false}; - // for threshold - uint32_t cnt = 0; - uint32_t tm = 0; - FProtoListGeneral* protoList = new WeatherProtos(); // holds all the protocols we can parse void configure(const SubGhzFPRxConfigureMessage& message); void on_beep_message(const AudioBeepMessage& message);