mirror of
https://github.com/Genaker/LoraSA.git
synced 2026-03-28 17:42:59 +01:00
dB output
This commit is contained in:
126
gps_src/main.cpp
Normal file
126
gps_src/main.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <TinyGPS++.h>
|
||||
#include <time.h>
|
||||
|
||||
// Objects for GNSS parsing and serial communication
|
||||
TinyGPSPlus gps;
|
||||
HardwareSerial GNSSSerial(2);
|
||||
|
||||
// Pin definitions for GNSS module communication
|
||||
const int GNSS_RXPin = 34;
|
||||
const int GNSS_TXPin = 33;
|
||||
const int GNSS_RSTPin = 35; // There is a function built for this in the example below-
|
||||
// currently it isn't used
|
||||
const int GNSS_PPS_Pin = 36;
|
||||
|
||||
// Flags for PPS handling and synchronization status
|
||||
volatile bool ppsFlag = false;
|
||||
volatile bool initialSyncDone = false;
|
||||
|
||||
// Timestamp for the last valid GNSS data received
|
||||
unsigned long lastGNSSDataMillis = 0;
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Initialize serial communication for debugging
|
||||
USBSerial.begin(115200);
|
||||
while (!USBSerial)
|
||||
;
|
||||
|
||||
// Start GNSS module communication
|
||||
GNSSSerial.begin(115200, SERIAL_8N1, GNSS_TXPin, GNSS_RXPin);
|
||||
while (!GNSSSerial)
|
||||
;
|
||||
|
||||
// Configure GNSS reset pin
|
||||
pinMode(GNSS_RSTPin, OUTPUT);
|
||||
digitalWrite(GNSS_RSTPin, HIGH);
|
||||
|
||||
// Set up PPS pin and attach an interrupt handler
|
||||
pinMode(GNSS_PPS_Pin, INPUT);
|
||||
attachInterrupt(digitalPinToInterrupt(GNSS_PPS_Pin), ppsInterrupt, RISING);
|
||||
|
||||
// Short delay for GNSS module initialization
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Process incoming GNSS data
|
||||
while (GNSSSerial.available())
|
||||
{
|
||||
if (gps.encode(GNSSSerial.read()))
|
||||
{
|
||||
// Update the timestamp when valid GNSS data is received
|
||||
lastGNSSDataMillis = millis();
|
||||
displayGNSSData(); // Display GNSS data for debugging
|
||||
}
|
||||
}
|
||||
|
||||
// Perform initial synchronization using NMEA time data
|
||||
if (!initialSyncDone && gps.date.isValid() && gps.time.isValid())
|
||||
{
|
||||
setSystemTime();
|
||||
initialSyncDone = true;
|
||||
USBSerial.println("Initial time synchronization done using NMEA data.");
|
||||
}
|
||||
|
||||
// Disable interrupts to safely check and reset the PPS flag
|
||||
noInterrupts();
|
||||
if (ppsFlag)
|
||||
{
|
||||
fineTuneSystemTime(); // Adjust system time based on the PPS pulse
|
||||
ppsFlag = false;
|
||||
}
|
||||
// Re-enable interrupts
|
||||
interrupts();
|
||||
|
||||
// Check if GNSS data has been absent for more than a minute
|
||||
if (millis() - lastGNSSDataMillis > 60000)
|
||||
{
|
||||
USBSerial.println("Warning: Haven't received GNSS data for more than 1 minute!");
|
||||
// Additional actions can be added here, like alerts or module resets.
|
||||
}
|
||||
}
|
||||
|
||||
// Interrupt handler for the PPS signal
|
||||
void ppsInterrupt() { ppsFlag = true; }
|
||||
|
||||
// Function to set system time using GNSS data
|
||||
void setSystemTime()
|
||||
{
|
||||
struct tm timeinfo;
|
||||
timeinfo.tm_year = gps.date.year() - 1900;
|
||||
timeinfo.tm_mon = gps.date.month() - 1;
|
||||
timeinfo.tm_mday = gps.date.day();
|
||||
timeinfo.tm_hour = gps.time.hour();
|
||||
timeinfo.tm_min = gps.time.minute();
|
||||
timeinfo.tm_sec = gps.time.second();
|
||||
time_t t = mktime(&timeinfo);
|
||||
|
||||
timeval tv = {t, 0};
|
||||
settimeofday(&tv, NULL); // Update system time
|
||||
}
|
||||
|
||||
// Function to fine-tune system time using the PPS pulse
|
||||
void fineTuneSystemTime()
|
||||
{
|
||||
timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
tv.tv_usec = 0; // Reset microseconds to zero
|
||||
settimeofday(&tv, NULL); // Update system time
|
||||
USBSerial.println("System time fine-tuned using PPS signal.");
|
||||
}
|
||||
|
||||
// Debugging function to display GNSS data
|
||||
void displayGNSSData()
|
||||
{
|
||||
USBSerial.print("Latitude: ");
|
||||
USBSerial.println(gps.location.lat(), 6);
|
||||
USBSerial.print("Longitude: ");
|
||||
USBSerial.println(gps.location.lng(), 6);
|
||||
USBSerial.print("Altitude: ");
|
||||
USBSerial.println(gps.altitude.meters());
|
||||
USBSerial.print("Speed: ");
|
||||
USBSerial.println(gps.speed.kmph());
|
||||
USBSerial.println("-----------------------------");
|
||||
}
|
||||
@@ -40,4 +40,5 @@ 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_clearTopStatus(void);
|
||||
extern void UI_drawCursor(int16_t);
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
; src_dir = eink_src
|
||||
; for env:heltec_wifi_lora_32_V3
|
||||
; src_dir = src ;;Default
|
||||
; for heltec_wifi_lora_32_V3-test-signal-generator
|
||||
; src_dir = trans_src
|
||||
|
||||
[env:heltec_wifi_lora_32_V3]
|
||||
platform = espressif32
|
||||
|
||||
100
src/main.cpp
100
src/main.cpp
@@ -194,12 +194,15 @@ bool ANIMATED_RELOAD = false;
|
||||
#define FILTER_SPECTRUM_RESULTS true
|
||||
#define FILTER_SAMPLES_MIN
|
||||
constexpr bool DRAW_DETECTION_TICKS = true;
|
||||
|
||||
int16_t max_x_rssi[STEPS] = {999};
|
||||
int16_t max_x_window[STEPS / 14] = {999};
|
||||
int x_window = 0;
|
||||
constexpr int WINDOW_SIZE = 15;
|
||||
// Number of samples for each frequency scan. Fewer samples = better temporal resolution.
|
||||
// if more than 100 it can freeze
|
||||
#define SAMPLES 35 //(scan time = 1294)
|
||||
// number of samples for RSSI method
|
||||
#define SAMPLES_RSSI 20 // 21 //
|
||||
#define SAMPLES_RSSI 12 // 21 //
|
||||
|
||||
#define RANGE (int)(FREQ_END - FREQ_BEGIN)
|
||||
|
||||
@@ -217,10 +220,9 @@ uint64_t median_frequency = FREQ_BEGIN + FREQ_END - FREQ_BEGIN / 2;
|
||||
// #define DISABLE_PLOT_CHART false // unused
|
||||
|
||||
// Array to store the scan results
|
||||
uint16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
|
||||
uint16_t result_display_set[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
|
||||
uint16_t result_detections[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
|
||||
uint16_t filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
|
||||
int16_t result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
|
||||
|
||||
bool filtered_result[RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
|
||||
|
||||
int max_bins_array_value[MAX_POWER_LEVELS];
|
||||
int max_step_range = 32;
|
||||
@@ -235,6 +237,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;
|
||||
uint64_t drone_detected_frequency_start = 0;
|
||||
uint64_t drone_detected_frequency_end = 0;
|
||||
uint64_t detection_count = 0;
|
||||
@@ -610,11 +613,14 @@ bool buttonPressHandler(float freq)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// Print Curent frequency once
|
||||
if (button_pressed_counter == 0)
|
||||
{
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display.drawString(128 / 2, 0, String(freq));
|
||||
display.display();
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -663,21 +669,28 @@ bool buttonPressHandler(float freq)
|
||||
return true;
|
||||
}
|
||||
|
||||
void drone_sound_alarm(int drone_detection_level, int detection_count)
|
||||
void drone_sound_alarm(int drone_detection_level, int detection_count,
|
||||
int tone_freq_db = 205)
|
||||
{
|
||||
// If level is set to sensitive,
|
||||
// start beeping every 10th frequency and shorter
|
||||
// it improves performance less short beep delays...
|
||||
if (drone_detection_level <= 25)
|
||||
{
|
||||
|
||||
if (tone_freq_db != 205)
|
||||
{
|
||||
tone_freq_db = 285 - tone_freq_db;
|
||||
}
|
||||
|
||||
if (detection_count == 1 && SOUND_ON)
|
||||
{
|
||||
tone(BUZZER_PIN, 205,
|
||||
tone(BUZZER_PIN, tone_freq_db,
|
||||
10); // same action ??? but first time
|
||||
}
|
||||
if (detection_count % 5 == 0 && SOUND_ON)
|
||||
{
|
||||
tone(BUZZER_PIN, 205,
|
||||
tone(BUZZER_PIN, tone_freq_db,
|
||||
10); // same action ??? but every 5th time
|
||||
}
|
||||
}
|
||||
@@ -775,6 +788,7 @@ void loop(void)
|
||||
{
|
||||
// clear the scan plot rectangle
|
||||
UI_clearPlotter();
|
||||
UI_clearTopStatus();
|
||||
}
|
||||
|
||||
// do the scan
|
||||
@@ -934,6 +948,7 @@ void loop(void)
|
||||
for (int r = 0; r < SAMPLES_RSSI; r++)
|
||||
{
|
||||
rssi = radio.getRSSI(false);
|
||||
int abs_rssi = abs(rssi);
|
||||
// ToDO: check if 4 is correct value for 33 power bins
|
||||
// Now we have more space because we are ignoring low dB values
|
||||
// we can / 3 default 4
|
||||
@@ -941,12 +956,12 @@ void loop(void)
|
||||
{
|
||||
result_index =
|
||||
/// still not clear formula but it works
|
||||
uint8_t(abs(rssi) / 4);
|
||||
uint8_t(abs_rssi / 4);
|
||||
}
|
||||
else if (RSSI_OUTPUT_FORMULA == 2)
|
||||
{
|
||||
// I like this formula better
|
||||
result_index = uint8_t(abs(rssi) / 2) - 22;
|
||||
result_index = uint8_t(abs_rssi / 2) - 22;
|
||||
}
|
||||
if (result_index >= RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE)
|
||||
{
|
||||
@@ -960,11 +975,15 @@ void loop(void)
|
||||
// avoid buffer overflow
|
||||
if (result_index < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE)
|
||||
{
|
||||
// Saving max ABS value of rssi. dB is negative so smaller is
|
||||
// bigger
|
||||
if (result[result_index] == 0 || result[result_index] > abs(rssi))
|
||||
// Saving max ABS value of RSSI. dB is negative, so smaller
|
||||
// absolute value represents stronger signal.
|
||||
if (result[result_index] == 0 || result[result_index] > abs_rssi)
|
||||
{
|
||||
result[result_index] = abs(rssi);
|
||||
result[result_index] = abs_rssi;
|
||||
}
|
||||
if (max_x_rssi[display_x] > abs_rssi)
|
||||
{
|
||||
max_x_rssi[display_x] = abs_rssi;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1024,7 +1043,7 @@ void loop(void)
|
||||
{
|
||||
// do not process 'first' and 'last' row to avoid out of index
|
||||
// access.
|
||||
if ((y > 0) && (y < (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 1)))
|
||||
if ((y > 0) && (y < (RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE - 2)))
|
||||
{
|
||||
if (((result[y + 1] != 0) && (result[y + 2] != 0)) ||
|
||||
(result[y - 1] != 0))
|
||||
@@ -1040,12 +1059,23 @@ void loop(void)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // not filtering if samples == 1
|
||||
} // not filtering if samples == 1 because it will be filtered
|
||||
else if (result[y] > 0 && samples == 1)
|
||||
{
|
||||
filtered_result[y] = 1;
|
||||
}
|
||||
|
||||
// calculating max window x RSSI after filters
|
||||
x_window = (int)(display_x / WINDOW_SIZE);
|
||||
int abs_result = abs(result[y]);
|
||||
if (filtered_result[y] == 1 && result[y] != 0 && result[y] != 1 &&
|
||||
max_x_window[x_window] > abs_result)
|
||||
{
|
||||
max_x_window[x_window] = abs_result;
|
||||
#ifdef PRINT_DEBUG
|
||||
Serial.println("MAX x window: " + String(x_window) + " " +
|
||||
String(abs_result));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
// check if we should alarm about a drone presence
|
||||
if ((filtered_result[y] == 1) // we have some data and
|
||||
@@ -1076,18 +1106,21 @@ void loop(void)
|
||||
drone_detected_frequency_end = freq;
|
||||
if (SOUND_ON == true)
|
||||
{
|
||||
drone_sound_alarm(drone_detection_level, detection_count);
|
||||
drone_sound_alarm(drone_detection_level, detection_count,
|
||||
max_rssi_x * 2);
|
||||
}
|
||||
|
||||
if (DRAW_DETECTION_TICKS == true)
|
||||
{
|
||||
// draw vertical line on top of display for "drone detected"
|
||||
// frequencies
|
||||
// draw vertical line on top of display for "drone detected"
|
||||
// frequencies
|
||||
#ifdef METHOD_SPECTRAL
|
||||
if (!detected_y[display_x])
|
||||
{
|
||||
display.drawLine(display_x, 1, display_x, 4);
|
||||
detected_y[display_x] = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if (WATERFALL_ENABLED == true)
|
||||
@@ -1114,7 +1147,7 @@ void loop(void)
|
||||
// MAx bin Value not RSSI
|
||||
max_rssi_x = y;
|
||||
}
|
||||
// Set signal level pixel
|
||||
// Set MAIN signal level pixel
|
||||
if (y < MAX_POWER_LEVELS - START_LOW)
|
||||
{
|
||||
display.setPixel(display_x, y + START_LOW);
|
||||
@@ -1232,6 +1265,19 @@ void loop(void)
|
||||
display.setColor(WHITE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef METHOD_RSSI
|
||||
// Printing Max Window DB.
|
||||
for (int x2 = 0; x2 < STEPS / WINDOW_SIZE; x2++)
|
||||
{
|
||||
if (max_x_window[x2] < show_db_after && max_x_window[x2] != 0)
|
||||
{
|
||||
display.drawString(x2 * WINDOW_SIZE + WINDOW_SIZE, 0,
|
||||
"-" + String(max_x_window[x2]));
|
||||
}
|
||||
max_x_window[x2] = 999;
|
||||
}
|
||||
#endif
|
||||
// Render display data here
|
||||
display.display();
|
||||
#ifdef OSD_ENABLED
|
||||
@@ -1251,7 +1297,7 @@ void loop(void)
|
||||
#endif
|
||||
}
|
||||
#ifdef PRINT_DEBUG
|
||||
// Serial.println("----");
|
||||
// Serial.println("----");
|
||||
#endif
|
||||
|
||||
loop_time = millis() - loop_start;
|
||||
|
||||
10
src/ui.cpp
10
src/ui.cpp
@@ -80,7 +80,15 @@ void UI_clearPlotter(void)
|
||||
{
|
||||
// clear the scan plot rectangle (top part)
|
||||
display_instance->setColor(BLACK);
|
||||
display_instance->fillRect(0, 0, STEPS, HEIGHT);
|
||||
display_instance->fillRect(0, 10, STEPS, HEIGHT - 10);
|
||||
display_instance->setColor(WHITE);
|
||||
}
|
||||
|
||||
void UI_clearTopStatus(void)
|
||||
{
|
||||
// clear the scan plot rectangle (top part)
|
||||
display_instance->setColor(BLACK);
|
||||
display_instance->fillRect(0, 0, STEPS, 10);
|
||||
display_instance->setColor(WHITE);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* This works on the stick, but the output on the screen gets cut off.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
// Turns the 'PRG' button into the power button, long press is off
|
||||
#define HELTEC_POWER_BUTTON // must be before "#include <heltec_unofficial.h>"
|
||||
#include <heltec_unofficial.h>
|
||||
@@ -15,11 +16,11 @@
|
||||
// Pause between transmited packets in mseconds.
|
||||
// Set to zero to only transmit a packet when pressing the user button
|
||||
// Will not exceed 1% duty cycle, even if you set a lower value.
|
||||
#define PAUSE 20
|
||||
#define PAUSE 10
|
||||
|
||||
// Frequency in MHz. Keep the decimal point to designate float.
|
||||
// Check your own rules and regulations to see what is legal where you are.
|
||||
#define FREQUENCY 866.3 // for Europe
|
||||
#define FREQUENCY 915 // for Europe
|
||||
// #define FREQUENCY 905.2 // for US
|
||||
|
||||
// LoRa bandwidth. Keep the decimal point to designate float.
|
||||
@@ -29,13 +30,13 @@
|
||||
|
||||
// Number from 5 to 12. Higher means slower but higher "processor gain",
|
||||
// meaning (in nutshell) longer range and more robust against interference.
|
||||
#define SPREADING_FACTOR 9
|
||||
#define SPREADING_FACTOR 7
|
||||
|
||||
// Transmit power in dBm. 0 dBm = 1 mW, enough for tabletop-testing. This value can be
|
||||
// set anywhere between -9 dBm (0.125 mW) to 22 dBm (158 mW). Note that the maximum ERP
|
||||
// (which is what your antenna maximally radiates) on the EU ISM band is 25 mW, and that
|
||||
// transmissting without an antenna can damage your hardware.
|
||||
#define TRANSMIT_POWER -9
|
||||
#define TRANSMIT_POWER 22
|
||||
|
||||
String rxdata;
|
||||
volatile bool rxFlag = false;
|
||||
@@ -67,35 +68,41 @@ void setup()
|
||||
RADIOLIB_OR_HALT(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF));
|
||||
}
|
||||
|
||||
int FHSS = 0.25;
|
||||
float FHSS_counter = -10;
|
||||
void loop()
|
||||
{
|
||||
heltec_loop();
|
||||
|
||||
bool tx_legal = millis() > last_tx + minimum_pause;
|
||||
bool tx_legal = true; // millis() > last_tx + minimum_pause;
|
||||
// Emulate frequency hopping spread spectrum (FHSS) is a method of transmitting radio
|
||||
// signals by rapidly switching the carrier between different frequency channels.
|
||||
float fr = (float)(FREQUENCY + (float)(FHSS_counter));
|
||||
RADIOLIB_OR_HALT(radio.setFrequency(fr, false));
|
||||
// Transmit a packet every PAUSE seconds or when the button is pressed
|
||||
if ((PAUSE && tx_legal && millis() - last_tx > (PAUSE)) || button.isSingleClick())
|
||||
{
|
||||
// In case of button click, tell user to wait
|
||||
if (!tx_legal)
|
||||
if (button.isSingleClick())
|
||||
{
|
||||
both.printf("Legal limit, wait %i sec.\n",
|
||||
(int)((minimum_pause - (millis() - last_tx)) / 1000) + 1);
|
||||
return;
|
||||
fr = 1000;
|
||||
RADIOLIB_OR_HALT(radio.setFrequency(fr, false));
|
||||
}
|
||||
both.printf("TX [%s] ", String(counter).c_str());
|
||||
// In case of button click, tell user to wait
|
||||
display.printf("TX[%s]", String(counter).c_str());
|
||||
radio.clearDio1Action();
|
||||
heltec_led(50); // 50% brightness is plenty for this LED
|
||||
tx_time = millis();
|
||||
RADIOLIB(radio.transmit(String(counter++).c_str()));
|
||||
RADIOLIB(radio.transmit(String("Putin Huylo!!! LA-LA-LA-LA").c_str()));
|
||||
tx_time = millis() - tx_time;
|
||||
heltec_led(0);
|
||||
if (_radiolib_status == RADIOLIB_ERR_NONE)
|
||||
{
|
||||
both.printf("OK (%i ms)\n", (int)tx_time);
|
||||
display.printf("OK(%ims)/%.2fMhz\n", (int)tx_time, fr);
|
||||
}
|
||||
else
|
||||
{
|
||||
both.printf("fail (%i)\n", _radiolib_status);
|
||||
display.printf("fail (%i)\n", _radiolib_status);
|
||||
heltec_delay(100);
|
||||
}
|
||||
// Maximum 1% duty cycle
|
||||
minimum_pause = tx_time * 100;
|
||||
@@ -111,10 +118,16 @@ void loop()
|
||||
radio.readData(rxdata);
|
||||
if (_radiolib_status == RADIOLIB_ERR_NONE)
|
||||
{
|
||||
both.printf("RX [%s]\n", rxdata.c_str());
|
||||
both.printf(" RSSI: %.2f dBm\n", radio.getRSSI());
|
||||
both.printf(" SNR: %.2f dB\n", radio.getSNR());
|
||||
display.printf("RX [%s]\n", rxdata.c_str());
|
||||
display.printf(" RSSI: %.2f dBm\n", radio.getRSSI());
|
||||
display.printf(" SNR: %.2f dB\n", radio.getSNR());
|
||||
}
|
||||
RADIOLIB_OR_HALT(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF));
|
||||
}
|
||||
FHSS_counter += 0.250;
|
||||
counter++;
|
||||
if (FHSS_counter > 20)
|
||||
{
|
||||
FHSS_counter = -10;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user