From bdd00039b26b3e9cd9c1fbfd0369a0496bbff4d6 Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Sat, 5 Oct 2024 16:35:01 +0100 Subject: [PATCH] Events for modular UI and other listeners --- lib/charts/BarChart.cpp | 14 ++++++++ lib/charts/StackedChart.cpp | 13 +++++++- lib/charts/WaterfallChart.cpp | 10 ++++++ lib/charts/charts.h | 11 +++++-- lib/events/events.h | 45 ++++++++++++++++++++++++++ lib/scan/scan.cpp | 45 ++++++++++++++++++++++++-- lib/scan/scan.h | 19 ++++++++--- src/main.cpp | 61 ++++++++++++++--------------------- test/test_rssi.cpp | 7 ++-- 9 files changed, 175 insertions(+), 50 deletions(-) create mode 100644 lib/events/events.h diff --git a/lib/charts/BarChart.cpp b/lib/charts/BarChart.cpp index 36c17d7..f92c63a 100644 --- a/lib/charts/BarChart.cpp +++ b/lib/charts/BarChart.cpp @@ -102,6 +102,20 @@ int BarChart::y2pos(float y) return height - height * (y - min_y) / (max_y - min_y); } +void BarChart::onEvent(Event &e) +{ + if (e.type != DETECTED) + { + return; + } + + int u = updatePoint(e.emitter.current_frequency, e.detected.rssi); + if (e.emitter.animated) + { + drawOne(u); + } +} + void DecoratedBarChart::reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { Chart::reset(x, y, w, h); diff --git a/lib/charts/StackedChart.cpp b/lib/charts/StackedChart.cpp index 1c879dc..2c6ca73 100644 --- a/lib/charts/StackedChart.cpp +++ b/lib/charts/StackedChart.cpp @@ -12,7 +12,8 @@ size_t StackedChart::addChart(Chart *c) cc[charts_sz] = c; free(charts); - c->reset(pos_x + c->pos_x, pos_y + c->pos_y, trim_w(c->pos_x, c->width, width), c->height); + c->reset(pos_x + c->pos_x, pos_y + c->pos_y, trim_w(c->pos_x, c->width, width), + c->height); charts = cc; return charts_sz++; } @@ -82,3 +83,13 @@ void StackedChart::draw() for (int i = 0; i < charts_sz; i++) charts[i]->draw(); } + +void StackedChart::onEvent(Event &e) +{ + if (e.type != SCAN_TASK_COMPLETE) + { + return; + } + + draw(); +} diff --git a/lib/charts/WaterfallChart.cpp b/lib/charts/WaterfallChart.cpp index af83090..9828794 100644 --- a/lib/charts/WaterfallChart.cpp +++ b/lib/charts/WaterfallChart.cpp @@ -55,3 +55,13 @@ int WaterfallChart::x2pos(float x) return width * (x - min_x) / (max_x - min_x); } + +void WaterfallChart::onEvent(Event &e) +{ + if (e.type != DETECTED) + { + return; + } + + updatePoint(e.time_ms, e.detected.freq, e.detected.rssi); +} diff --git a/lib/charts/charts.h b/lib/charts/charts.h index d6d11b4..d159cc0 100644 --- a/lib/charts/charts.h +++ b/lib/charts/charts.h @@ -10,6 +10,7 @@ typedef OLEDDisplay Display_t; #endif #include +#include #include #include @@ -58,7 +59,7 @@ struct ProgressChart : Chart virtual void drawOne(int x) = 0; }; -struct BarChart : ProgressChart +struct BarChart : ProgressChart, Listener { float min_x, max_x, min_y, max_y; float level_y; @@ -83,6 +84,7 @@ struct BarChart : ProgressChart int updatePoint(float x, float y) override; void drawOne(int x) override; void draw() override; + void onEvent(Event &) override; int x2pos(float x); int y2pos(float y); @@ -109,7 +111,7 @@ struct DecoratedBarChart : Chart void draw() override; }; -struct StackedChart : Chart +struct StackedChart : Chart, Listener { Chart **charts; size_t charts_sz; @@ -134,9 +136,11 @@ struct StackedChart : Chart void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; void draw() override; + + void onEvent(Event &e) override; }; -struct WaterfallChart : Chart +struct WaterfallChart : Chart, Listener { float min_x, max_x; float level_y, threshold; @@ -156,6 +160,7 @@ struct WaterfallChart : Chart void reset(uint16_t x, uint16_t y, uint16_t w, uint16_t h) override; void draw() override; + void onEvent(Event &e) override; int x2pos(float x); }; diff --git a/lib/events/events.h b/lib/events/events.h new file mode 100644 index 0000000..539277c --- /dev/null +++ b/lib/events/events.h @@ -0,0 +1,45 @@ +#ifndef LORASA_EVENTS_H +#define LORASA_EVENTS_H + +struct Event; +enum EventType +{ + DETECTED = 0, + SCAN_TASK_COMPLETE, + _MAX_EVENT_TYPE // unused as event type +}; +struct Listener; + +#include +#include + +struct Event +{ + EventType type; + uint64_t epoch; + uint64_t time_ms; + + Scan &emitter; + + union + { + struct + { + float rssi; + float freq; + bool trigger; + bool detected; + size_t detected_at; + } detected; + }; + + Event(Scan &emitter, EventType type, uint64_t time_ms) + : emitter(emitter), type(type), epoch(emitter.epoch), time_ms(time_ms) {}; +}; + +struct Listener +{ + virtual void onEvent(Event &event) = 0; +}; + +#endif diff --git a/lib/scan/scan.cpp b/lib/scan/scan.cpp index 5f584b4..028cf42 100644 --- a/lib/scan/scan.cpp +++ b/lib/scan/scan.cpp @@ -66,8 +66,8 @@ uint16_t Scan::rssiMethod(size_t samples, uint16_t *result, size_t res_size) return max_signal; } -size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, - int samples) +Event Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples) { size_t max_rssi_x = result_size; @@ -122,7 +122,46 @@ size_t Scan::detect(uint16_t *result, bool *filtered_result, size_t result_size, } } - return max_rssi_x; + Event event(*this, EventType::DETECTED, 0); + event.epoch = epoch; + event.detected.detected = max_rssi_x < result_size; + event.detected.freq = current_frequency; + event.detected.rssi = + event.detected.detected ? -(float)result[max_rssi_x] : LO_RSSI_THRESHOLD; + event.detected.detected_at = max_rssi_x; + event.detected.trigger = + event.detected.detected && event.detected.rssi >= trigger_level; + detection_count++; + + return event; +} + +size_t Scan::addEventListener(EventType t, Listener &l) +{ + size_t c = listener_count[(size_t)t]; + Listener **new_list = new Listener *[c + 1]; + new_list[c] = &l; + listener_count[(size_t)t] = c + 1; + + if (c > 0) + { + Listener **old_list = eventListeners[(size_t)t]; + memcpy(new_list, old_list, c * sizeof(Listener *)); + delete[] old_list; + } + + eventListeners[(size_t)t] = new_list; + return c; +} + +void Scan::fireEvent(Event &event) +{ + Listener **list = eventListeners[(size_t)event.type]; + size_t c = listener_count[(size_t)event.type]; + for (int i = 0; i < c; i++) + { + list[i]->onEvent(event); + } } #endif diff --git a/lib/scan/scan.h b/lib/scan/scan.h index b471e7e..68b8cb5 100644 --- a/lib/scan/scan.h +++ b/lib/scan/scan.h @@ -1,4 +1,5 @@ #include +#include #include #ifndef LORASA_CORE_H @@ -41,11 +42,18 @@ struct Scan bool sound_on; bool led_flag; uint64_t detection_count; + bool animated; + float trigger_level; + + Listener **eventListeners[(size_t)EventType::_MAX_EVENT_TYPE]; + size_t listener_count[(size_t)EventType::_MAX_EVENT_TYPE]; Scan() : epoch(0), current_frequency(0), fr_begin(0), fr_end(0), - drone_detection_level(0), sound_on(false), led_flag(false), - detection_count(0) {}; + drone_detection_level(0), sound_on(false), led_flag(false), detection_count(0), + animated(false), trigger_level(0), listener_count{ + 0, + } {}; virtual float getRSSI() = 0; @@ -57,8 +65,11 @@ struct Scan // those values that represent a detection event. // It returns index that represents strongest signal at which a detection event // occurred. - static size_t detect(uint16_t *result, bool *filtered_result, size_t result_size, - int samples); + Event detect(uint16_t *result, bool *filtered_result, size_t result_size, + int samples); + + size_t addEventListener(EventType t, Listener &l); + void fireEvent(Event &e); }; // Remove reading without neighbors diff --git a/src/main.cpp b/src/main.cpp index 1a6590a..c6e6514 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,7 @@ #define RADIOLIB_GODMODE (1) #include +#include #include #ifndef LILYGO @@ -197,7 +198,7 @@ bool first_run, new_pixel, detected_x = false; // drone detection flag bool detected = false; uint64_t drone_detection_level = DEFAULT_DRONE_DETECTION_LEVEL; -uint64_t show_db_after = 80; +#define TRIGGER_LEVEL -80.0 uint64_t drone_detected_frequency_start = 0; uint64_t drone_detected_frequency_end = 0; bool single_page_scan = false; @@ -675,12 +676,14 @@ void setup(void) xTaskCreate(logToSerialTask, "LOG_DATA_JSON", 2048, NULL, 1, NULL); #endif + r.trigger_level = TRIGGER_LEVEL; stacked.reset(0, 0, display.width(), display.height()); + r.addEventListener(SCAN_TASK_COMPLETE, stacked); bar = new DecoratedBarChart(display, 0, 0, display.width(), 0, FREQ_BEGIN, FREQ_END, - LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, - -(float)show_db_after); + LO_RSSI_THRESHOLD, HI_RSSI_THRESHOLD, r.trigger_level); + r.addEventListener(DETECTED, bar->bar); size_t b = stacked.addChart(bar); Chart *statusBar = new StatusBar(display, 0, 0, display.width(), r); @@ -695,10 +698,12 @@ void setup(void) waterChart = new WaterfallChart(display, 0, WATERFALL_START, display.width(), 0, FREQ_BEGIN, - FREQ_END, -(float)show_db_after, WATERFALL_SENSITIVITY, model); + FREQ_END, r.trigger_level, WATERFALL_SENSITIVITY, model); size_t c = stacked.addChart(waterChart); stacked.setHeight(c, stacked.height - WATERFALL_START - statusBar->height); + + r.addEventListener(DETECTED, *waterChart); #endif size_t d = stacked.addChart(statusBar); @@ -1053,6 +1058,7 @@ void loop(void) LOG("METHOD RSSI"); uint16_t max_rssi = r.rssiMethod(SAMPLES_RSSI, result, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE); + if (max_x_rssi[display_x] > max_rssi) { max_x_rssi[display_x] = max_rssi; @@ -1077,42 +1083,24 @@ void loop(void) display.setColor(WHITE); } #endif - size_t detected_at = r.detect( - result, filtered_result, RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, samples); + Event event = r.detect(result, filtered_result, + RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, samples); + event.time_ms = millis(); + size_t detected_at = event.detected.detected_at; if (max_rssi_x > detected_at) { // MAx bin Value not RSSI max_rssi_x = detected_at; } - detected = detected_at < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; + detected = event.detected.detected; detected_y[display_x] = false; - float rr; - if (detected) - { - rr = -(float)result[detected_at]; - } - else - { - rr = LO_RSSI_THRESHOLD; - } - + float rr = event.detected.rssi; r.drone_detection_level = drone_detection_level; -#if (WATERFALL_ENABLED == true) - waterChart->updatePoint(millis(), r.current_frequency, rr); -#endif - - int updated = bar->bar.updatePoint(r.current_frequency, rr); - - if (first_run || ANIMATED_RELOAD) - { - bar->bar.drawOne(updated); - } - - if (detected_at <= drone_detection_level) + if (event.detected.trigger) { // check if we should alarm about a drone presence if (detected_y[display_x] == false) // detection threshold match @@ -1152,6 +1140,8 @@ void loop(void) } } + r.fireEvent(event); + #ifdef JOYSTICK_ENABLED // Draw joystick cursor and Frequency RSSI value if (display_x == cursor_x_position) @@ -1166,16 +1156,10 @@ void loop(void) #ifdef PRINT_PROFILE_TIME scan_time += (millis() - scan_start_time); #endif - // count detected - if (detected) - { - r.detection_count++; - } - #ifdef PRINT_DEBUG Serial.println("....\n"); #endif - if (first_run || ANIMATED_RELOAD) + if (r.animated) { display.display(); } @@ -1230,7 +1214,10 @@ void loop(void) w = WATERFALL_START; } - stacked.draw(); + { + Event event(r, SCAN_TASK_COMPLETE, millis()); + r.fireEvent(event); + } // Render display data here #ifdef UPTIME_CLOCK diff --git a/test/test_rssi.cpp b/test/test_rssi.cpp index 3d82f29..a38d166 100644 --- a/test/test_rssi.cpp +++ b/test/test_rssi.cpp @@ -48,14 +48,17 @@ void test_detect() uint16_t samples[test_sz] = {20, 50, 55, 60, 0, 70, 75, 80, 0, 90, 0, 100, 110}; bool result[test_sz]; - size_t r = Scan::detect(samples, result, test_sz, 1); + TestScan test_scan({}, 0); + Event e = test_scan.detect(samples, result, test_sz, 1); + size_t r = e.detected.detected_at; bool expect[test_sz] = {1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1}; TEST_ASSERT_EQUAL_INT16(0, r); TEST_ASSERT_EQUAL_INT8_ARRAY(expect, result, test_sz); - r = Scan::detect(samples, result, test_sz, 2); + Event e2 = test_scan.detect(samples, result, test_sz, 2); + r = e2.detected.detected_at; bool expect2[test_sz] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0};