From 305fe283a6cc346ac58291d926caabd4c207a012 Mon Sep 17 00:00:00 2001 From: "ionsurdu@github.com" Date: Tue, 6 Aug 2024 21:37:11 +0300 Subject: [PATCH] Fix formatting + spellcheck --- README.md | 215 ++++++------ include/ui.h | 21 +- src/main.cpp | 925 +++++++++++++++++++++++++-------------------------- src/ui.cpp | 364 ++++++++++---------- 4 files changed, 754 insertions(+), 771 deletions(-) diff --git a/README.md b/README.md index a52c95e..767c204 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Lora SA(Spectrum Analyzer) -RF Spectrum Analyzer using Lora Radio + +## RF Spectrum Analyzer using Lora Radio LORA hardware @@ -10,46 +11,48 @@ The output is in the form of scan lines; each line has 33 power bins. The first power bin corresponds to -11 dBm, the second to -15 dBm, and so on. The higher number of samples in a bin corresponds to more power received at that level. -``` + +```text N in Bin / dBm -1 -11 -2 -15 -3 -19 -4 -23 -5 -27 -6 -31 -7 -35 -8 -39 -9 -43 -10 -47 -11 -51 -12 -55 -13 -59 -14 -63 -15 -67 -16 -71 -17 -75 -18 -79 -19 -83 -20 -87 -21 -91 -22 -95 -23 -99 -24 -103 -25 -107 -26 -111 -27 -115 -28 -119 -29 -123 -30 -127 -31 -131 -32 -135 -33 -139 +1 -11 +2 -15 +3 -19 +4 -23 +5 -27 +6 -31 +7 -35 +8 -39 +9 -43 +10 -47 +11 -51 +12 -55 +13 -59 +14 -63 +15 -67 +16 -71 +17 -75 +18 -79 +19 -83 +20 -87 +21 -91 +22 -95 +23 -99 +24 -103 +25 -107 +26 -111 +27 -115 +28 -119 +29 -123 +30 -127 +31 -131 +32 -135 +33 -139 ``` Example: -``` -step-13 Frequancy:816.25 + +```text +step-13 Frequency:816.25 Power Bins: 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0400, 0000,0000,0000,0000,0000,0000,0006,001B,000E,0005,0006,0002,0000 ``` @@ -57,47 +60,58 @@ The spectrum analyzer performs power measurements in the configured bandwidth. The X-axis represents frequency in MHz, and the Y-axis displays actually received power. In the example above, the frequency span goes from 850 MHz to 950 MHz (that is a 100MHz range), and -the visual amplitude goes from -11 dBm to -110(-139) according to the datasheet(High sensitivity: down to -148dBm) dBm. +the visual amplitude goes from -11 dBm to -110(-139) according to the datasheet (High sensitivity: down to -148dBm) dBm. To show the results in a plot, run the Python script RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py -# Features -## Multiple Ranges Scan +## Features + +### Multiple Ranges Scan + Disabled By Default -``` + +```c // Feature to scan diapazones. Other frequency settings will be ignored. int SCAN_DIAPAZONES[] = {}; //int SCAN_DIAPAZONES[] = {850890, 920950}; ``` -To Enable Add/ uncomment an array of the frequencies -``` + +To Enable Add/ uncomment an array of the frequencies + +```c int SCAN_DIAPAZONES[] = {850890, 920950}; ``` -where 850890 stands for 950-890Mhz range -920950 - 920-890Mhz -Other settings will be ignored if **Multiple Ranges Scan** is enabled. -## Waterfall -Waterfall showed only on One Page Scan -to disable - uncomment this line -``` +where 850890 stands for 950-890Mhz range +920950 - 920-890Mhz +Other settings will be ignored if **Multiple Ranges Scan** is enabled. + +### Waterfall + +Waterfall showed only on One Page Scan +to disable - uncomment this line + +```c #define WATERFALL_ENABLED true ``` -Waterfall shows the last **N** = SCREAN_HEIGHT (64) - WATERFALL_START(37) - 8 (part of the STATUS_TEXT_TOP) = **19** signal detection that excited set signal level -## RSSI Method of scan -By default, we are using the spectralScan method of the RadioLib Library: https://jgromes.github.io/RadioLib/class_s_x126x.html#a8a3ad4e12df862ab18b326d9dba26d66 -This method works only with Sx1262 modules. -We implemented a scan using the **getRSSI** method, which has more flexibility and supports sx1276 and other modules. -Using this method, we also receive the signal's **dB** values, not just the O-33 number. -To enable this method, set the value of the **RSSI_METHOD** to true. +Waterfall shows the last **N** = SCREEN_HEIGHT (64) - WATERFALL_START(37) - 8 (part of the STATUS_TEXT_TOP) = **19** signal detection that excited set signal level + +### RSSI Method of scan + +By default, we are using the spectralScan method of the RadioLib Library: +This method works only with Sx1262 modules. +We implemented a scan using the **getRSSI** method, which has more flexibility and supports sx1276 and other modules. +Using this method, we also receive the signal's **dB** values, not just the O-33 number. +To enable this method, set the value of the **RSSI_METHOD** to true. + +### Multi Screen Scan -## Multi Screen Scan Single screen scan for now has **RANGE / 128** resolution. -Multi-page scan can be adjusted to how many MHz per page you wanna scan +Multi-page scan can be adjusted to how many MHz per page you wanna scan -``` +```c // frequency range in MHz to scan #define FREQ_BEGIN 850 // TODO: if % RANGE_PER_PAGE != 0 @@ -108,72 +122,81 @@ int SCAN_DIAPAZONES[] = {}; // int SCAN_DIAPAZONES[] = {850890, 920950}; // MHZ per page -//To put everething into one page set RANGE_PER_PAGE = FREQ_END - 800 +//To put everything into one page set RANGE_PER_PAGE = FREQ_END - 800 unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN //To Enable Multi Screen scan // unsigned int RANGE_PER_PAGE = 50; // Default Range on Menu Button Switch #define DEFAULT_RANGE_PER_PAGE = 50; - ``` + To enable Multi-page by default set **RANGE_PER_PAGE** less than **FREQ_END - FREQ_BEGIN**; -Switch to multi-page during regular One Screen application run. Restart the ESP32 on screen after the logo press the P button. +Switch to multi-page during regular One Screen application run. Restart the ESP32 on screen after the logo press the P button. -## Mute Autio Notifications -Restart ESP32, and on the logo display, press the P button. +### Mute Audio Notifications -## Pause Execution -Press P for more than 2 seconds. Execution will pause, and the scan's current Mhz position will be shown on the display. -If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button changes the notification level +Restart ESP32, and on the logo display, press the P button. + +### Pause Execution + +Press P for more than 2 seconds. Execution will pause, and the scan's current Mhz position will be shown on the display. +If less, ESP32 will turn off. Fast pressing(less than 0.5 second) P button changes the notification level + +## VSCode Platform.IO development env installation -# VSCode Platform.IO development env installation 1. Install VSCode -2. install Platfor.IO extension +2. install Platform.IO extension ![image](https://github.com/user-attachments/assets/00547068-6153-4c78-9f12-54981b013b06) 3. Connect ESP32 to USB. Install USB drivers for Windows 4. Clone this Git Repo or download zip of the sources ![image](https://github.com/user-attachments/assets/971b6592-3b71-414c-971c-2ecd20f0f0b7) - ``` - git clone https://github.com/Genaker/LoraSA.git - ``` -NOTE: in you case name will be Just LoraSA. I have LoraSA2 because I already have LoraSA folder -6. Open the Project with the VS code Platform.IO + + ```bash + git clone https://github.com/Genaker/LoraSA.git + ``` + + NOTE: in you case name will be Just LoraSA. I have LoraSA2 because I already have LoraSA folder + +5. Open the Project with the VS code Platform.IO ![image](https://github.com/user-attachments/assets/5066836b-32ac-4a24-ac03-b5f739a6a658) ![image](https://github.com/user-attachments/assets/da14488d-7a4a-410e-b754-59598578016d) -7. Select Proper Environment +6. Select Proper Environment ![image](https://github.com/user-attachments/assets/a9c6557b-a387-4457-b59b-b3d7242d2826) -8. Select ESP32 USB Device to program +7. Select ESP32 USB Device to program ![image](https://github.com/user-attachments/assets/af76c4b1-7122-45e1-b26b-08b59e03ca3b) Note: It is theoretically possible to program via WiFi and BTH. -9. Programm your ESP32 +8. Program your ESP32 ![image](https://github.com/user-attachments/assets/9e67afd8-0522-4a96-82dc-8e1cdb32add5) -10. Wait until you are done with the compilation and upload. - Usually takes 1 minute. The first run is slower. It needs to compile all libraries. +9. Wait until you are done with the compilation and upload. + Usually takes 1 minute. The first run is slower. It needs to compile all libraries. ![image](https://github.com/user-attachments/assets/6796eb5d-6e3f-45bc-b88c-251499f1ad47) You will have the UCOG SA logo and spectrum analyzing scanning screen when done. ![image](https://github.com/user-attachments/assets/f86ab32a-cab1-461e-ade9-7021136a0af7) - -# Hardware + +## Hardware + Heltec ESP32 Lora V3: -https://www.amazon.com/Heltec-Development-863-870MHz-ESP32-S3FN8-902-928MHz/dp/B0D1H1FN9Y/ -https://heltec.org/project/wifi-lora-32-v3/ -https://www.aliexpress.us/item/3256807037422978.html + + + -Battery with Wire JT connector : -https://www.amazon.com/EEMB-2000mAh-Battery-Rechargeable-Connector/dp/B08214DJLJ +Battery with Wire JT connector : + + +## 3D printed case -# 3D printed case ![image](https://github.com/user-attachments/assets/52ae4e90-5f25-4d72-888e-7586bef9df69) -https://www.printables.com/model/118750-heltec-lora-32-case-for-meshtastic -https://www.thingiverse.com/thing:3125854 -https://thangs.com/designer/Snake0017/3d-model/Heltec%20LoRa%2032%20Desktop%20%26%20Vehicle%20Enclosure-40844 -or buy : -https://www.amazon.com/DIYmalls-ESP32-OLED-WiFi-Type-C/dp/B0BR3MQ9BG + + + +or buy : + -https://www.thingiverse.com/thing:6522462 + + +## Heltec ESP32 Lora v3 Pin Map -# Heltec ESP32 Lora v3 Pin Map ![image](https://github.com/user-attachments/assets/a1e00b51-5566-4ff5-98fe-67eaeb5bc81f) -We are using pin 41 as a Buzzer trigger. Connect buzzer + leg with pin 41 and - leg with the ground (GND). You can change the buzzer pin in the code. +We are using pin 41 as a Buzzer trigger. Connect buzzer + leg with pin 41 and - leg with the ground (GND). You can change the buzzer pin in the code. diff --git a/include/ui.h b/include/ui.h index 776bc35..5fb9ca7 100644 --- a/include/ui.h +++ b/include/ui.h @@ -8,38 +8,35 @@ // #include -// (optional) major and minor tickmarks at x MHz +// (optional) major and minor tick-marks at x MHz #define MAJOR_TICKS 10 #define MINOR_TICKS 5 #define ONE_MILLISEC 1 // Prints debug information and the scan measurement bins from the SX1262 in hex -//#define PRINT_DEBUG +// #define PRINT_DEBUG // Change spectrum plot values at once or by line #define ANIMATED_RELOAD true #define MAJOR_TICK_LENGTH 2 #define MINOR_TICK_LENGTH 1 -// WEIGHT of the x-asix line +// WEIGHT of the x-axis line #define X_AXIS_WEIGHT 1 #define STATUS_TEXT_TOP (64 - 10) - // The number of the spectrum screen lines = width of screen // Resolution of the scan is limited by 128-pixel screen #define STEPS 128 +#define SCREEN_HEIGHT 64 // ???? not used -#define SCREAN_HEIGHT 64 - -// publish functions -extern void UI_Init(SSD1306Wire*); -extern void UI_displayDecorate(int , int , bool ); -extern void UI_setLedFlag( bool); +// publish functions +extern void UI_Init(SSD1306Wire *); +extern void UI_displayDecorate(int, int, bool); +extern void UI_setLedFlag(bool); extern void UI_clearPlotter(void); -extern void UI_drawCurrsor(int16_t); +extern void UI_drawCursor(int16_t); #endif // __UI_H__ - diff --git a/src/main.cpp b/src/main.cpp index 27ac455..81f4462 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,7 @@ */ // Turns the 'PRG' button into the power button, long press is off -// TODO add it to compiller options using -DHELTEC_POWER_BUTTON +// TODO add it to compiler options using -DHELTEC_POWER_BUTTON #define HELTEC_POWER_BUTTON // must be before "#include " #include @@ -30,25 +30,21 @@ // This file contains a binary patch for the SX1262 #include "modules/SX126x/patches/SX126x_patch_scan.h" - -// project components +// project components #include "global_config.h" -#include "images.h" #include "ui.h" - // ----------------------------------------------------------------- // CONFIGURATION OPTIONS // ----------------------------------------------------------------- - - -typedef enum { - METHOD_RSSI = 0u, - METHOD_SPECTRAL +typedef enum +{ + METHOD_RSSI = 0u, + METHOD_SPECTRAL } TSCAN_METOD_ENUM; -#define SCAN_METHOD METHOD_SPECTRAL +#define SCAN_METHOD METHOD_SPECTRAL // Feature to scan diapazones. Other frequency settings will be ignored. int SCAN_RANGES[] = {}; @@ -72,7 +68,7 @@ unsigned int RANGE_PER_PAGE = FREQ_END - FREQ_BEGIN; // FREQ_END - FREQ_BEGIN // if more than 100 it can freez #define SAMPLES 100 //(scan time = 1294) // number of samples for RSSI method -#define SAMPLES_RSSI 21// +#define SAMPLES_RSSI 21 // #define RANGE (int)(FREQ_END - FREQ_BEGIN) @@ -89,8 +85,6 @@ unsigned int iterations = RANGE / RANGE_PER_PAGE; // unsigned int range_frequency = FREQ_END - FREQ_BEGIN; unsigned int median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2; - - // #define OSD_ENABLED true // unused // #define DISABLE_PLOT_CHART false // unused @@ -99,7 +93,7 @@ uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; uint16_t filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE]; // Waterfall array -bool waterfall[10][STEPS][10]; // 10 - ??? +bool waterfall[10][STEPS][10]; // 10 - ??? // global variable // Used as a Led Light and Buzzer/count trigger @@ -114,13 +108,12 @@ unsigned int detection_count = 0; bool single_page_scan = false; bool SOUND_ON = true; - unsigned int scan_time = 0; unsigned int scan_start_time = 0; uint64_t start = 0; -unsigned int x, y, scan_iterration, w = 0; +unsigned int x, y, scan_iteration, w = 0; unsigned int ranges_count = 0; float freq = 0; @@ -130,510 +123,482 @@ int result_index = 0; unsigned int button_pressed_counter = 0; - -void setup() +void setup(void) { - pinMode(LED, OUTPUT); - pinMode(BUZZER_PIN, OUTPUT); - pinMode(REB_PIN, OUTPUT); - heltec_setup(); + float vbat; + float resolution; - UI_Init(&display); - - for (int i = 0; i < 200; i++) - { - button.update(); - delay(10); - if (button.pressed()) + pinMode(LED, OUTPUT); + pinMode(BUZZER_PIN, OUTPUT); + pinMode(REB_PIN, OUTPUT); + heltec_setup(); + UI_Init(&display); + for (int i = 0; i < 200; i++) { - SOUND_ON = false; - tone(BUZZER_PIN, 205, 100); - delay(50); - tone(BUZZER_PIN, 205, 100); - break; + button.update(); + delay(10); + if (button.pressed()) + { + SOUND_ON = false; + tone(BUZZER_PIN, 205, 100); + delay(50); + tone(BUZZER_PIN, 205, 100); + break; + } } - } - // initialize SX1262 FSK modem at the initial frequency - both.println("Init radio"); - RADIOLIB_OR_HALT(radio.beginFSK(FREQ_BEGIN)); - // upload a patch to the SX1262 to enable spectral scan - // NOTE: this patch is uploaded into volatile memory, - // and must be re-uploaded on every power up - both.println("Upload SX1262 patch"); - // Upload binary patch into the SX126x device RAM. Patch is needed to e.g., enable spectral scan and must be uploaded again on every power cycle. - RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan))); - // configure scan bandwidth and disable the data shaping - both.println("Setting up radio"); - RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); - // and disable the data shaping - RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); - both.println("Starting scanning..."); - float vbat = heltec_vbat(); - both.printf("V battery: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat)); - - delay(300); - display.clear(); + // initialize SX1262 FSK modem at the initial frequency + both.println("Init radio"); + RADIOLIB_OR_HALT(radio.beginFSK(FREQ_BEGIN)); - float resolution = RANGE / STEPS; - if (RANGE_PER_PAGE == range) - { - single_page_scan = true; - } - else - { - single_page_scan = false; - } + // upload a patch to the SX1262 to enable spectral scan + // NOTE: this patch is uploaded into volatile memory, + // and must be re-uploaded on every power up + both.println("Upload SX1262 patch"); - // Adjust range if it is not even to RANGE_PER_PAGE - if (!single_page_scan && range % RANGE_PER_PAGE != 0) - { - // range = range + range % RANGE_PER_PAGE; - } + // Upload binary patch into the SX126x device RAM. Patch is needed to e.g., + // enable spectral scan and must be uploaded again on every power cycle. + RADIOLIB_OR_HALT(radio.uploadPatch(sx126x_patch_scan, sizeof(sx126x_patch_scan))); + // configure scan bandwidth and disable the data shaping - if (single_page_scan) - { - both.println("Single Page Screen MODE"); - both.println("Multi Screen View Press P - button"); - both.println("Single Screen Resolution: " + String(resolution) + "Mhz/tick"); - both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); + both.println("Setting up radio"); + RADIOLIB_OR_HALT(radio.setRxBandwidth(BANDWIDTH)); - for (int i = 0; i < 500; i++) + // and disable the data shaping + RADIOLIB_OR_HALT(radio.setDataShaping(RADIOLIB_SHAPING_NONE)); + both.println("Starting scanning..."); + vbat = heltec_vbat(); + both.printf("V battery: %.2fV (%d%%)\n", vbat, + heltec_battery_percent(vbat)); + delay(300); + display.clear(); + resolution = RANGE / STEPS; + if (RANGE_PER_PAGE == range) { - button.update(); - delay(10); - both.print("."); - if (button.pressed()) - { - RANGE_PER_PAGE = DEFAULT_RANGE_PER_PAGE; - single_page_scan = false; - tone(BUZZER_PIN, 205, 100); - delay(50); - tone(BUZZER_PIN, 205, 100); - break; - } - } - } - else - { - both.println("Multi Page Screen MODE"); - both.println("Single screen View Press P - button"); - both.println("Single screen Resolution: " + String(resolution) + "Mhz/tick"); - both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); - - for (int i = 0; i < 500; i++) - { - button.update(); - delay(10); - both.print("."); - if (button.pressed()) - { - RANGE_PER_PAGE = range; single_page_scan = true; - tone(BUZZER_PIN, 205, 100); - - break; - } - } - } - display.clear(); - - // waterfall start line y-axis - w = WATERFALL_START; -} - -void loop() -{ - UI_displayDecorate(0,0,false); // some default values - drone_detected = false; - detection_count = 0; - drone_detected_frequency_start = 0; -#ifdef PRINT_PROFILE_TIME - start = millis(); -#endif - - if (!ANIMATED_RELOAD || !single_page_scan) - { - // clear the scan plot rectangle - UI_clearPlotter(); - } - - // do the scan - range = FREQ_END - FREQ_BEGIN; - - if (RANGE_PER_PAGE > range) - { - RANGE_PER_PAGE = range; - } - - fr_begin = FREQ_BEGIN; - fr_end = fr_begin; - // 50 is a single-screen range - // TODO: Make 50 a variable with the option to show the full range - iterations = range / RANGE_PER_PAGE; - - single_step = RANGE_PER_PAGE / 128; - if (range % RANGE_PER_PAGE != 0) - { - // add more scan - //++; - } - - if (RANGE_PER_PAGE == range) - { - single_page_scan = true; - } - else - { - single_page_scan = false; - } - - ranges_count = 0; - - for (int range : SCAN_RANGES) - { - ranges_count++; - } - - if (ranges_count > 0) - { - iterations = ranges_count; - single_page_scan = false; - } - - // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz - for (scan_iterration = 0; scan_iterration < iterations; scan_iterration++) - { - range = RANGE_PER_PAGE; - - if (ranges_count == 0) - { - fr_begin = (scan_iterration == 0) ? fr_begin : fr_begin += range; - fr_end = fr_begin + RANGE_PER_PAGE; } else { - fr_begin = SCAN_RANGES[scan_iterration] / 1000; - fr_end = SCAN_RANGES[scan_iterration] % 1000; - range = fr_end - fr_begin; + single_page_scan = false; } + // Adjust range if it is not even to RANGE_PER_PAGE + if (!single_page_scan && range % RANGE_PER_PAGE != 0) + { + // range = range + range % RANGE_PER_PAGE; + } + if (single_page_scan) + { + both.println("Single Page Screen MODE"); + both.println("Multi Screen View Press P - button"); + both.println("Single Screen Resolution: " + String(resolution) + "Mhz/tick"); + both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); + for (int i = 0; i < 500; i++) + { + button.update(); + delay(10); + both.print("."); + if (button.pressed()) + { + RANGE_PER_PAGE = DEFAULT_RANGE_PER_PAGE; + single_page_scan = false; + tone(BUZZER_PIN, 205, 100); + delay(50); + tone(BUZZER_PIN, 205, 100); + break; + } + } + } + else + { + both.println("Multi Page Screen MODE"); + both.println("Single screen View Press P - button"); + both.println("Single screen Resolution: " + String(resolution) + "Mhz/tick"); + both.println("Curent Resolution: " + String((float)RANGE_PER_PAGE / STEPS) + "Mhz/tick"); + for (int i = 0; i < 500; i++) + { + button.update(); + delay(10); + both.print("."); + if (button.pressed()) + { + RANGE_PER_PAGE = range; + single_page_scan = true; + tone(BUZZER_PIN, 205, 100); + break; + } + } + } + display.clear(); + // waterfall start line y-axis + w = WATERFALL_START; +} +void loop(void) +{ + UI_displayDecorate(0, 0, false); // some default values + drone_detected = false; + detection_count = 0; + drone_detected_frequency_start = 0; +#ifdef PRINT_PROFILE_TIME + start = millis(); +#endif if (!ANIMATED_RELOAD || !single_page_scan) { - // clear the scan plot rectangle - UI_clearPlotter(); + // clear the scan plot rectangle + UI_clearPlotter(); } - - if (single_page_scan == false) + // do the scan + range = FREQ_END - FREQ_BEGIN; + if (RANGE_PER_PAGE > range) { - UI_displayDecorate(fr_begin, fr_end, true); + RANGE_PER_PAGE = range; } - - drone_detected_frequency_start = 0; - - display.setTextAlignment(TEXT_ALIGN_RIGHT); - // horizontal x axis loop - for (x = 0; x < STEPS; x++) + fr_begin = FREQ_BEGIN; + fr_end = fr_begin; + // 50 is a single-screen range + // TODO: Make 50 a variable with the option to show the full range + iterations = range / RANGE_PER_PAGE; + single_step = RANGE_PER_PAGE / 128; + if (range % RANGE_PER_PAGE != 0) { - scan_start_time = millis(); - -#if ANIMATED_RELOAD - UI_drawCurrsor(x); -#endif - - waterfall[scan_iterration][x][w] = false; - freq = fr_begin + (range * ((float)x / STEPS)); - radio.setFrequency(freq); - // TODO: RSSI METHOD - // Gets RSSI (Recorded Signal Strength Indicator) - // Restart continuous receive mode on the new frequency - // state = radio.startReceive(); - // if (state == RADIOLIB_ERR_NONE) { - // Serial.println(F("Started continuous receive mode")); - //} else { - // Serial.print(F("Failed to start receive mode, error code: ")); - // Serial.println(state); - //} - // rssi = radio.getRSSI(false); - // Serial.println(String(rssi) + "db"); - // delay(25); - // This code will iterate over the specified frequencies, changing the frequency every - // second and printing the RSSI value for each frequency to the serial monitor. Adjust the frequencies array - // to include the specific frequencies you're interested in monitoring. - // A short delay after changing the frequency - // ensures the module has time to stabilize and get an accurate RSSI reading. -#ifdef PRINT_DEBUG - Serial.println(); - Serial.print("step-"); - Serial.print(x); - Serial.print(" Frequency:"); - Serial.print(freq); - Serial.println(); -#endif - - - // SpectralScan Method -#if SCAN_METHOD == METHOD_SPECTRAL - { - // start spectral scan third parameter is a sleep interval - radio.spectralScanStart(SAMPLES, 1); - // wait for spectral scan to finish - while (radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) + // add more scan + //++; + } + if (RANGE_PER_PAGE == range) + { + single_page_scan = true; + } + else + { + single_page_scan = false; + } + ranges_count = 0; + for (int range : SCAN_RANGES) + { + ranges_count++; + } + if (ranges_count > 0) + { + iterations = ranges_count; + single_page_scan = false; + } + // Iterating by small ranges by 50 Mhz each pixel is 0.4 Mhz + for (scan_iteration = 0; scan_iteration < iterations; scan_iteration++) + { + range = RANGE_PER_PAGE; + if (ranges_count == 0) { - Serial.print("radio.spectralScanGetStatus ERROR: "); - Serial.println(radio.spectralScanGetStatus()); - heltec_delay(ONE_MILLISEC); - } - // read the results Array to which the results will be saved - radio.spectralScanGetResult(result); - } -#endif - -#if SCAN_METHOD == METHOD_RSSI - // Spectrum analyzer using getRSSI - { - state = radio.startReceive(0); - if (state == RADIOLIB_ERR_NONE) - { -#ifdef PRINT_DEBUG - Serial.println(F("Started continuous receive mode")); -#endif + fr_begin = (scan_iteration == 0) ? fr_begin : fr_begin += range; + fr_end = fr_begin + RANGE_PER_PAGE; } else { - Serial.print(F("Failed to start receive mode, error code: ")); - Serial.println(state); - } - - for (int r = 1; r < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; r++) - { - result[r] = 0; - } - result_index = 0; - // N of samples - for (int r = 1; r < SAMPLES_RSSI; r++) - { - rssi = radio.getRSSI(false); - // delay(ONE_MILLISEC); - // ToDO: check if 4 is correct value for 33 power bins - result_index = (abs(rssi) / 4); - - // Debug Information -#ifdef PRINT_DEBUG - Serial.print("Frequency: "); - Serial.println(freq); - Serial.println(rssi); - Serial.println(result_index); -#endif - // Saving max value only rss is negative so smaller is bigger - if (result[result_index] > rssi) - { - result[result_index] = rssi; - } - } - } -#endif - - - detected = false; -#ifdef FILTER_SPECTRUM_RESULTS - // Filter Elements without neighbors - for (y = 1; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) - { - // if RSSI method actual value is -xxx dB - if (result[y] && (result[y + 1] != 0 || result[y - 1] != 0)) - { - // Filling the empty pixel between signals int the level < 27 (noise level) - /* if (y < 27 && result[y + 1] == 0 && result[y + 2] > 0) - { - result[y + 1] = 1; - filtered_result[y + 1] = 1; - }*/ - filtered_result[y] = 1; - } - else - { - filtered_result[y] = 0; - } - } -#endif - for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) - { -#ifdef PRINT_DEBUG - Serial.printf("%04X,", result[y]); -#endif - if (result[y] || y == drone_detection_level) - { - // check if we should alarm about a drone presence - if (filtered_result[y] == 1 && y <= drone_detection_level) - { - drone_detected = true; -#ifdef WATERFALL_ENABLED - if (single_page_scan) - { - // Drone detection true for waterfall - waterfall[scan_iterration][x][w] = true; - display.setColor(WHITE); - display.setPixel(x, w); - } -#endif - if (drone_detected_frequency_start == 0) - { - drone_detected_frequency_start = freq; - } - drone_detected_frequency_end = freq; - - UI_setLedFlag(true); - - // If level is set to sensitive, start beeping every 10th frequency and shorter - if (drone_detection_level <= 25) - { - if (detection_count == 1 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); - if (detection_count % 5 == 0 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); - } - else - { - if (detection_count % 20 == 0 && SOUND_ON) - tone(BUZZER_PIN, 205, 10); - } - display.setPixel(x, 1); - display.setPixel(x, 2); - display.setPixel(x, 3); - display.setPixel(x, 4); + fr_begin = SCAN_RANGES[scan_iteration] / 1000; + fr_end = SCAN_RANGES[scan_iteration] % 1000; + range = fr_end - fr_begin; } -#ifdef WATERFALL_ENABLED - if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iterration][x][w] != true) + if (!ANIMATED_RELOAD || !single_page_scan) { - // If drone not found set dark pixel on the waterfall - // TODO: make something like scrolling up if possible - waterfall[scan_iterration][x][w] = false; - display.setColor(BLACK); - display.setPixel(x, w); - display.setColor(WHITE); + // clear the scan plot rectangle + UI_clearPlotter(); } + + if (single_page_scan == false) + { + UI_displayDecorate(fr_begin, fr_end, true); + } + + drone_detected_frequency_start = 0; + display.setTextAlignment(TEXT_ALIGN_RIGHT); + + // horizontal x axis loop + for (x = 0; x < STEPS; x++) + { + scan_start_time = millis(); + +#if ANIMATED_RELOAD + UI_drawCursor(x); #endif - if (filtered_result[y] == 1) - { - // Set signal level pixel - display.setPixel(x, y); - detected = true; - } - - // Draw detection Level line evere 2 pixel - if (y == drone_detection_level && x % 2 == 0) - { - display.setPixel(x, y); - } - } - + waterfall[scan_iteration][x][w] = false; + freq = fr_begin + (range * ((float)x / STEPS)); + radio.setFrequency(freq); + // TODO: RSSI METHOD + // Gets RSSI (Recorded Signal Strength Indicator) + // Restart continuous receive mode on the new frequency + // state = radio.startReceive(); + // if (state == RADIOLIB_ERR_NONE) { + // Serial.println(F("Started continuous receive mode")); + //} else { + // Serial.print(F("Failed to start receive mode, error code: ")); + // Serial.println(state); + //} + // rssi = radio.getRSSI(false); + // Serial.println(String(rssi) + "db"); + // delay(25); + // This code will iterate over the specified frequencies, + // changing the frequency every + // second and printing the RSSI value for each frequency to the serial monitor. Adjust the frequencies array + // to include the specific frequencies you're interested in monitoring. + // A short delay after changing the frequency + // ensures the module has time to stabilize and get an accurate RSSI reading. +#ifdef PRINT_DEBUG + Serial.println(); + Serial.print("step-"); + Serial.print(x); + Serial.print(" Frequency:"); + Serial.print(freq); + Serial.println(); +#endif + // SpectralScan Method +#if SCAN_METHOD == METHOD_SPECTRAL + { + // start spectral scan third parameter is a sleep interval + radio.spectralScanStart(SAMPLES, 1); + // wait for spectral scan to finish + while (radio.spectralScanGetStatus() != RADIOLIB_ERR_NONE) + { + Serial.print("radio.spectralScanGetStatus ERROR: "); + Serial.println(radio.spectralScanGetStatus()); + heltec_delay(ONE_MILLISEC); + } + // read the results Array to which the results will be saved + radio.spectralScanGetResult(result); + } +#endif +#if SCAN_METHOD == METHOD_RSSI + // Spectrum analyzer using getRSSI + { + state = radio.startReceive(0); + if (state == RADIOLIB_ERR_NONE) + { +#ifdef PRINT_DEBUG + Serial.println(F("Started continuous receive mode")); +#endif + } + else + { + Serial.print(F("Failed to start receive mode, error code: ")); + Serial.println(state); + } + for (int r = 1; r < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; r++) + { + result[r] = 0; + } + result_index = 0; + // N of samples + for (int r = 1; r < SAMPLES_RSSI; r++) + { + rssi = radio.getRSSI(false); + // delay(ONE_MILLISEC); + // ToDO: check if 4 is correct value for 33 power bins + result_index = (abs(rssi) / 4); + // Debug Information +#ifdef PRINT_DEBUG + Serial.print("Frequency: "); + Serial.println(freq); + Serial.println(rssi); + Serial.println(result_index); +#endif + // Saving max value only rss is negative so smaller is bigger + if (result[result_index] > rssi) + { + result[result_index] = rssi; + } + } + } +#endif + detected = false; +#ifdef FILTER_SPECTRUM_RESULTS + // Filter Elements without neighbors + for (y = 1; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + { + // if RSSI method actual value is -xxx dB + if (result[y] && (result[y + 1] != 0 || result[y - 1] != 0)) + { + // Filling the empty pixel between signals int the level < 27 (noise level) + /* if (y < 27 && result[y + 1] == 0 && result[y + 2] > 0) + { + result[y + 1] = 1; + filtered_result[y + 1] = 1; + }*/ + filtered_result[y] = 1; + } + else + { + filtered_result[y] = 0; + } + } +#endif + for (y = 0; y < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; y++) + { +#ifdef PRINT_DEBUG + Serial.printf("%04X,", result[y]); +#endif + if (result[y] || y == drone_detection_level) + { + // check if we should alarm about a drone presence + if (filtered_result[y] == 1 && y <= drone_detection_level) + { + drone_detected = true; +#ifdef WATERFALL_ENABLED + if (single_page_scan) + { + // Drone detection true for waterfall + waterfall[scan_iteration][x][w] = true; + display.setColor(WHITE); + display.setPixel(x, w); + } +#endif + if (drone_detected_frequency_start == 0) + { + drone_detected_frequency_start = freq; + } + drone_detected_frequency_end = freq; + UI_setLedFlag(true); + // If level is set to sensitive, + // start beeping every 10th frequency and shorter + if (drone_detection_level <= 25) + { + if (detection_count == 1 && SOUND_ON) + tone(BUZZER_PIN, 205, 10); + if (detection_count % 5 == 0 && SOUND_ON) + tone(BUZZER_PIN, 205, 10); + } + else + { + if (detection_count % 20 == 0 && SOUND_ON) + tone(BUZZER_PIN, 205, 10); + } + display.setPixel(x, 1); + display.setPixel(x, 2); + display.setPixel(x, 3); + display.setPixel(x, 4); + } +#ifdef WATERFALL_ENABLED + if (filtered_result[y] == 1 && y > drone_detection_level && single_page_scan && waterfall[scan_iteration][x][w] != true) + { + // If drone not found set dark pixel on the waterfall + // TODO: make something like scrolling up if possible + waterfall[scan_iteration][x][w] = false; + display.setColor(BLACK); + display.setPixel(x, w); + display.setColor(WHITE); + } +#endif + if (filtered_result[y] == 1) + { + // Set signal level pixel + display.setPixel(x, y); + detected = true; + } + // Draw detection Level line evere 2 pixel + if (y == drone_detection_level && x % 2 == 0) + { + display.setPixel(x, y); + } + } #ifdef PRINT_PROFILE_TIME - scan_time = millis() - scan_start_time; - // Huge performance issue if enable - // Serial.printf("Single Scan took %lld ms\n", scan_time); + scan_time = millis() - scan_start_time; + // Huge performance issue if enable + // Serial.printf("Single Scan took %lld ms\n", scan_time); #endif - } - if (detected) - { - detection_count++; - } - detected = false; + } + if (detected) + { + detection_count++; + } + detected = false; #ifdef PRINT_DEBUG - Serial.println("...."); + Serial.println("...."); #endif - if (first_run || ANIMATED_RELOAD) - { - display.display(); - } - - // Detection level button short press - if (button.pressedFor(100)) - { - button.update(); - button_pressed_counter = 0; - // if long press stop - while (button.pressedNow()) - { - delay(10); - // Print Curent frequency - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.drawString(128 / 2, 0, String(freq)); - display.display(); - button_pressed_counter++; - if (button_pressed_counter > 150) - { - digitalWrite(LED, HIGH); - delay(150); - digitalWrite(LED, LOW); + if (first_run || ANIMATED_RELOAD) + { + display.display(); + } + // Detection level button short press + if (button.pressedFor(100)) + { + button.update(); + button_pressed_counter = 0; + // if long press stop + while (button.pressedNow()) + { + delay(10); + // Print Curent frequency + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(128 / 2, 0, String(freq)); + display.display(); + button_pressed_counter++; + if (button_pressed_counter > 150) + { + digitalWrite(LED, HIGH); + delay(150); + digitalWrite(LED, LOW); + } + } + if (button_pressed_counter > 150) + { + // Remove Curent Freqancy Text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.setColor(BLACK); + display.drawString(128 / 2, 0, String(freq)); + display.setColor(WHITE); + display.display(); + break; + } + if (button_pressed_counter > 50 && button_pressed_counter < 150) + { + // Visually confirm it's off so user releases button + display.displayOff(); + // Deep sleep (has wait for release so we don't wake up immediately) + heltec_deep_sleep(); + break; + } + button.update(); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + // erase old value + display.setColor(BLACK); + display.fillRect(128 - 13, 0, 13, 13); + display.setColor(WHITE); + drone_detection_level++; + // print new value + display.drawString(128, 0, String(drone_detection_level)); + tone(BUZZER_PIN, 104, 150); + if (drone_detection_level > 30) + { + drone_detection_level = 1; + } + } + // wait a little bit before the next scan, + // otherwise the SX1262 hangs + // Add more logic before insead of long delay... + // heltec_delay(1); + // Loop is needed if heltec_delay(1) not used + heltec_loop(); + } + w++; + if (w > STATUS_TEXT_TOP + 1) + { + w = WATERFALL_START; } - } - if (button_pressed_counter > 150) - { - // Remove Curent Freqancy Text - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setColor(BLACK); - display.drawString(128 / 2, 0, String(freq)); - display.setColor(WHITE); - display.display(); - break; - } - if (button_pressed_counter > 50 && button_pressed_counter < 150) - { - // Visually confirm it's off so user releases button - display.displayOff(); - // Deep sleep (has wait for release so we don't wake up immediately) - heltec_deep_sleep(); - break; - } - button.update(); - - display.setTextAlignment(TEXT_ALIGN_RIGHT); - // erase old value - display.setColor(BLACK); - display.fillRect(128 - 13, 0, 13, 13); - display.setColor(WHITE); - drone_detection_level++; - // print new value - display.drawString(128, 0, String(drone_detection_level)); - - tone(BUZZER_PIN, 104, 150); - - if (drone_detection_level > 30) - { - drone_detection_level = 1; - } - } - // wait a little bit before the next scan, otherwise the SX1262 hangs - // Add more logic before insead of long delay... - // heltec_delay(1); - // Loop is needed if heltec_delay(1) not used - heltec_loop(); - } - w++; - if (w > STATUS_TEXT_TOP + 1) - { - w = WATERFALL_START; - } #ifdef WATERFALL_ENABLED - // Draw waterfall position cursor - if (single_page_scan) - { - display.setColor(BLACK); - display.drawHorizontalLine(0, w, STEPS); - display.setColor(WHITE); - } + // Draw waterfall position cursor + if (single_page_scan) + { + display.setColor(BLACK); + display.drawHorizontalLine(0, w, STEPS); + display.setColor(WHITE); + } #endif - display.display(); -} - + display.display(); + } #ifdef PRINT_DEBUG -Serial.println("----"); + Serial.println("----"); #endif // display.display(); #ifdef PRINT_PROFILE_TIME -scan_time = millis() - start; -Serial.printf("Scan took %lld ms\n", scan_time); + scan_time = millis() - start; + Serial.printf("Scan took %lld ms\n", scan_time); #endif } diff --git a/src/ui.cpp b/src/ui.cpp index a28ede3..7b1fd81 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -1,12 +1,10 @@ - - #include "ui.h" #include "images.h" #include "global_config.h" #include "RadioLib.h" // ------------------------------------------------- -// LCOAL DEFINES +// LOCAL DEFINES // Height of the plotter area // ------------------------------------------------- @@ -14,16 +12,14 @@ // #define SCALE_TEXT_TOP (HEIGHT + X_AXIS_WEIGHT + MAJOR_TICK_LENGTH) - static unsigned int start_scan_text = (128 / 2) - 3; // initialized flag static bool ui_initialized = false; static bool led_flag = false; static unsigned short int scan_progress_count = 0; - -static SSD1306Wire *display_instance; //(0x3c, SDA_OLED, SCL_OLED, DISPLAY_GEOMETRY); -// PrintSplitter both(Serial, display); +//(0x3c, SDA_OLED, SCL_OLED, DISPLAY_GEOMETRY); +static SSD1306Wire *display_instance; // temporary dirty import ... to be solved durring upcoming refactoring extern unsigned int drone_detection_level; @@ -33,45 +29,43 @@ extern unsigned int detection_count; extern bool SOUND_ON; extern bool drone_detected; extern unsigned int drone_detected_frequency_start; -extern unsigned int drone_detected_frequency_end ; +extern unsigned int drone_detected_frequency_end; extern unsigned int ranges_count; extern int SCAN_RANGES[]; extern unsigned int ranges_count; -extern unsigned int iterations ; -extern unsigned int scan_iterration; +extern unsigned int iterations; +extern unsigned int scan_iteration; void UI_Init(SSD1306Wire *display_ptr) { - // init pointer to display instance. - display_instance = display_ptr; - - // check for null ??? - display_instance->clear(); - // draw the UCOG welcome logo - display_instance->drawXbm(0, 2, 128, 64, epd_bitmap_ucog); - display_instance->display(); + // init pointer to display instance. + display_instance = display_ptr; + // check for null ??? + display_instance->clear(); + // draw the UCOG welcome logo + display_instance->drawXbm(0, 2, 128, 64, epd_bitmap_ucog); + display_instance->display(); } -void UI_setLedFlag( bool new_status) +void UI_setLedFlag(bool new_status) { - led_flag = new_status; + led_flag = new_status; } - void clearStatus(void) { - // clear status line - display_instance->setColor(BLACK); - display_instance->fillRect(0, STATUS_TEXT_TOP + 2, 128, 13); - display_instance->setColor(WHITE); + // clear status line + display_instance->setColor(BLACK); + display_instance->fillRect(0, STATUS_TEXT_TOP + 2, 128, 13); + display_instance->setColor(WHITE); } void UI_clearPlotter(void) { - // clear the scan plot rectangle - display_instance->setColor(BLACK); - display_instance->fillRect(0, 0, STEPS, HEIGHT); - display_instance->setColor(WHITE); + // clear the scan plot rectangle + display_instance->setColor(BLACK); + display_instance->fillRect(0, 0, STEPS, HEIGHT); + display_instance->setColor(WHITE); } /** @@ -82,189 +76,193 @@ void UI_clearPlotter(void) */ void drawTicks(float every, int length) { - int first_tick = 0; - //+ (every - (fr_begin - (int)(fr_begin / every) * every)); - /*if (first_tick < fr_begin) - { - first_tick += every; - }*/ - bool correction = false; - int pixels_per_step = STEPS / (RANGE_PER_PAGE / every); - if (STEPS / RANGE_PER_PAGE != 0) - { - correction = true; - } + int first_tick; + bool correction; + int pixels_per_step; + int correction_number; + int tick; + int tick_minor; + int median; - int correction_number = STEPS - (int)(pixels_per_step * (RANGE_PER_PAGE / every)); - int tick = 0; - int tick_minor = 0; - int median = (RANGE_PER_PAGE / every) / 2; - // TODO: (RANGE_PER_PAGE / every) * 2 has twice extra steps we need to figureout correct logic or minor ticks is not showing to the end - for (int t = 0; t <= (RANGE_PER_PAGE / every) * 2; t++) - { - // fix if pixels per step is not int and we have shift - if (correction && t % 2 != 0 && correction_number > 1) + first_tick = 0; + //+ (every - (fr_begin - (int)(fr_begin / every) * every)); + /*if (first_tick < fr_begin) { - // pixels_per_step++; - correction_number--; - } - - tick += pixels_per_step; - tick_minor = tick / 2; - - if (tick <= 128 - 3) + first_tick += every; + }*/ + correction = false; + pixels_per_step = STEPS / (RANGE_PER_PAGE / every); + if (STEPS / RANGE_PER_PAGE != 0) { - display_instance->drawLine(tick, HEIGHT + X_AXIS_WEIGHT, tick, HEIGHT + X_AXIS_WEIGHT + length); - // Central tick - if (tick > (128 / 2) - 3 && tick < (128 / 2) + 3) - { - display_instance->drawLine(tick + 1, HEIGHT + X_AXIS_WEIGHT, tick + 1, HEIGHT + X_AXIS_WEIGHT + length); - } + correction = true; } - + correction_number = STEPS - (int)(pixels_per_step * (RANGE_PER_PAGE / every)); + tick = 0; + tick_minor = 0; + median = (RANGE_PER_PAGE / every) / 2; + // TODO: (RANGE_PER_PAGE / every) + // * 2 has twice extra steps we need to figureout correct logic or minor ticks is not showing to the end + for (int t = 0; t <= (RANGE_PER_PAGE / every) * 2; t++) + { + // fix if pixels per step is not int and we have shift + if (correction && t % 2 != 0 && correction_number > 1) + { + // pixels_per_step++; + correction_number--; + } + tick += pixels_per_step; + tick_minor = tick / 2; + if (tick <= 128 - 3) + { + display_instance->drawLine(tick, HEIGHT + X_AXIS_WEIGHT, tick, + HEIGHT + X_AXIS_WEIGHT + length); + // Central tick + if (tick > (128 / 2) - 3 && tick < (128 / 2) + 3) + { + display_instance->drawLine(tick + 1, HEIGHT + X_AXIS_WEIGHT, + tick + 1, HEIGHT + X_AXIS_WEIGHT + length); + } + } #ifdef MINOR_TICKS - // Fix two ticks together - if (tick_minor + 1 != tick && tick_minor - 1 != tick && tick_minor + 2 != tick && tick_minor - 2 != tick) - { - display_instance->drawLine(tick_minor, HEIGHT + X_AXIS_WEIGHT, tick_minor, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); - } - // Central tick - if (tick_minor > (128 / 2) - 3 && tick_minor < (128 / 2) + 3) - { - display_instance->drawLine(tick_minor + 1, HEIGHT + X_AXIS_WEIGHT, tick_minor + 1, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); - } + // Fix two ticks together + if ((tick_minor + 1 != tick) && (tick_minor - 1 != tick) && (tick_minor + 2 != tick) && (tick_minor - 2 != tick)) + { + display_instance->drawLine(tick_minor, HEIGHT + X_AXIS_WEIGHT, + tick_minor, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); + } + // Central tick + if (tick_minor > (128 / 2) - 3 && tick_minor < (128 / 2) + 3) + { + display_instance->drawLine(tick_minor + 1, HEIGHT + X_AXIS_WEIGHT, + tick_minor + 1, HEIGHT + X_AXIS_WEIGHT + MINOR_TICK_LENGTH); + } #endif - } + } } -void UI_drawCurrsor(int16_t possition) +void UI_drawCursor(int16_t possition) { - // Draw animated cursor on reload process - display_instance->setColor(BLACK); - display_instance->drawVerticalLine(possition, 0, HEIGHT); - display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); - display_instance->setColor(WHITE); - + // Draw animated cursor on reload process + display_instance->setColor(BLACK); + display_instance->drawVerticalLine(possition, 0, HEIGHT); + display_instance->drawVerticalLine(possition + 1, 0, HEIGHT); + display_instance->setColor(WHITE); } - /** * @brief Decorates the display: everything but the plot itself. */ void UI_displayDecorate(int begin = 0, int end = 0, bool redraw = false) { - if (!ui_initialized) - { - // Start and end ticks - display_instance->fillRect(0, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); - display_instance->fillRect(126, HEIGHT + X_AXIS_WEIGHT, 2, MAJOR_TICK_LENGTH + 1); - - // Drone detection level - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, 0, String(drone_detection_level)); - } - - if (!ui_initialized || redraw) - { - // Clear something - display_instance->setColor(BLACK); - display_instance->fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); - display_instance->setColor(WHITE); - - // Drone detection level - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, 0, String(drone_detection_level)); - - // Frequency start - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, SCALE_TEXT_TOP, (begin == 0) ? String(FREQ_BEGIN) : String(begin)); - - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - display_instance->drawString(128 / 2, SCALE_TEXT_TOP, (begin == 0) ? String(median_frequency) : String(begin + ((end - begin) / 2))); - - // Frequency end - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, SCALE_TEXT_TOP, (end == 0) ? String(FREQ_END) : String(end)); - } - - if (led_flag == true && detection_count >= 5) - { - digitalWrite(LED, HIGH); - if (SOUND_ON) + if (!ui_initialized) { - tone(BUZZER_PIN, 104, 100); + // Start and end ticks + display_instance->fillRect(0, HEIGHT + X_AXIS_WEIGHT, 2, + MAJOR_TICK_LENGTH + 1); + display_instance->fillRect(126, HEIGHT + X_AXIS_WEIGHT, 2, + MAJOR_TICK_LENGTH + 1); + // Drone detection level + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, 0, String(drone_detection_level)); } - digitalWrite(REB_PIN, HIGH); - led_flag = false; - } - - else if (!redraw) - { - digitalWrite(LED, LOW); - } - // Status text block - if (!drone_detected) - { - // "Scanning" - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - // clear status line - clearStatus(); - if (scan_progress_count == 0) + if (!ui_initialized || redraw) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan. "); + // Clear something + display_instance->setColor(BLACK); + display_instance->fillRect(0, SCALE_TEXT_TOP + 1, 128, 12); + display_instance->setColor(WHITE); + // Drone detection level + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, 0, String(drone_detection_level)); + // Frequency start + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, SCALE_TEXT_TOP, + (begin == 0) ? String(FREQ_BEGIN) : String(begin)); + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + display_instance->drawString(128 / 2, SCALE_TEXT_TOP, + (begin == 0) ? String(median_frequency) : String(begin + ((end - begin) / 2))); + // Frequency end + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, SCALE_TEXT_TOP, + (end == 0) ? String(FREQ_END) : String(end)); } - else if (scan_progress_count == 1) + if (led_flag == true && detection_count >= 5) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan.. "); + digitalWrite(LED, HIGH); + if (SOUND_ON) + { + tone(BUZZER_PIN, 104, 100); + } + digitalWrite(REB_PIN, HIGH); + led_flag = false; } - else if (scan_progress_count == 2) + else if (!redraw) { - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, "Scan..."); + digitalWrite(LED, LOW); } - scan_progress_count++; - - if (scan_progress_count == 3) + // Status text block + if (!drone_detected) { - scan_progress_count = 0; + // "Scanning" + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + // clear status line + clearStatus(); + if (scan_progress_count == 0) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + "Scan. "); + } + else if (scan_progress_count == 1) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + "Scan.. "); + } + else if (scan_progress_count == 2) + { + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + "Scan..."); + } + scan_progress_count++; + if (scan_progress_count == 3) + { + scan_progress_count = 0; + } } - } - - if (drone_detected) - { - display_instance->setTextAlignment(TEXT_ALIGN_CENTER); - // clear status line - clearStatus(); - - display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); - } - - if (ranges_count == 0) - { - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, STATUS_TEXT_TOP, String(FREQ_BEGIN)); - - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, STATUS_TEXT_TOP, String(FREQ_END)); - } - else if (ranges_count > 0) - { - display_instance->setTextAlignment(TEXT_ALIGN_LEFT); - display_instance->drawString(0, STATUS_TEXT_TOP, String(SCAN_RANGES[scan_iterration] / 1000) + "-" + String(SCAN_RANGES[scan_iterration] % 1000)); - if (scan_iterration + 1 < iterations) + if (drone_detected) { - display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); - display_instance->drawString(128, STATUS_TEXT_TOP, String(SCAN_RANGES[scan_iterration + 1] / 1000) + "-" + String(SCAN_RANGES[scan_iterration + 1] % 1000)); + display_instance->setTextAlignment(TEXT_ALIGN_CENTER); + // clear status line + clearStatus(); + display_instance->drawString(start_scan_text, STATUS_TEXT_TOP, + String(drone_detected_frequency_start) + ">RF<" + String(drone_detected_frequency_end)); } - } - - if (!ui_initialized) - { - // X-axis - display_instance->fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); + if (ranges_count == 0) + { + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, STATUS_TEXT_TOP, String(FREQ_BEGIN)); + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, STATUS_TEXT_TOP, String(FREQ_END)); + } + else if (ranges_count > 0) + { + display_instance->setTextAlignment(TEXT_ALIGN_LEFT); + display_instance->drawString(0, STATUS_TEXT_TOP, + String(SCAN_RANGES[scan_iteration] / 1000) + "-" + String(SCAN_RANGES[scan_iteration] % 1000)); + if (scan_iteration + 1 < iterations) + { + display_instance->setTextAlignment(TEXT_ALIGN_RIGHT); + display_instance->drawString(128, STATUS_TEXT_TOP, + String(SCAN_RANGES[scan_iteration + 1] / 1000) + "-" + String(SCAN_RANGES[scan_iteration + 1] % 1000)); + } + } + if (!ui_initialized) + { + // X-axis + display_instance->fillRect(0, HEIGHT, STEPS, X_AXIS_WEIGHT); // ticks #ifdef MAJOR_TICKS - drawTicks(MAJOR_TICKS, MAJOR_TICK_LENGTH); + drawTicks(MAJOR_TICKS, MAJOR_TICK_LENGTH); #endif - } - ui_initialized = true; + } + ui_initialized = true; }