From b776867eaaef48849235c126df99da251b592406 Mon Sep 17 00:00:00 2001 From: Patryk Sikora Date: Tue, 19 May 2026 19:45:19 +0200 Subject: [PATCH 1/5] =?UTF-8?q?usuni=C4=99cie=20deleya=20z=20funkcji=20upd?= =?UTF-8?q?ateRadioStatus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/esp32s3/esp32s3.ino | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/esp32s3/esp32s3.ino b/src/esp32s3/esp32s3.ino index 5f87dcb..8424338 100644 --- a/src/esp32s3/esp32s3.ino +++ b/src/esp32s3/esp32s3.ino @@ -143,6 +143,11 @@ void loop() { void updateRadioStatus() { + static bool turnOffLedInNextLoop = false; + if (turnOffLedInNextLoop) { + turnOffLedInNextLoop = false; + ledOff(); + } if (radioEvent) { radioEvent = false; // get irq status @@ -158,7 +163,7 @@ void updateRadioStatus() if (irq & RADIOLIB_SX126X_IRQ_HEADER_VALID) { ledOn(); - delay(1); + turnOffLedInNextLoop = true; } // RX is done for RX_DONE, CRC_ERR, HEADER_ERR, TIMEOUT @@ -170,7 +175,7 @@ void updateRadioStatus() if ((irq & RADIOLIB_SX126X_IRQ_CRC_ERR) || (irq & RADIOLIB_SX126X_IRQ_HEADER_ERR)) { rxErrors++; } radioStatus.RXdone = true; - ledOff(); + turnOffLedInNextLoop = true; } } } From c34da451583e47974adfe1061b7f69fae1af42c7 Mon Sep 17 00:00:00 2001 From: Patryk Sikora Date: Tue, 19 May 2026 20:12:52 +0200 Subject: [PATCH 2/5] =?UTF-8?q?przygotowanie=20pod=20module=5Fpin;=20spraw?= =?UTF-8?q?dzanie=20czy=20modu=C5=82=20gnss=20jest=20pod=C5=82=C4=85czony;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + przeniesienie rxErrors do RadioStatus --- src/esp32s3/esp32s3.ino | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/esp32s3/esp32s3.ino b/src/esp32s3/esp32s3.ino index 8424338..e86b71a 100644 --- a/src/esp32s3/esp32s3.ino +++ b/src/esp32s3/esp32s3.ino @@ -37,7 +37,7 @@ SPIClass spiLora(HSPI); SX1262 radio = new Module(LORA_NSS, LORA_DIO1, LORA_RESET, LORA_BUSY, spiLora); TinyGPSPlus gpsInternal; HardwareSerial SerialMAV(2); -uint16_t rxErrors = 0; + void readInternalGPSPos(); void sendInternalGPSPos(); @@ -63,12 +63,24 @@ inline void ledOn() { digitalWrite(LED_BUILTIN, LOW); } inline void ledOff() { digitalWrite(LED_BUILTIN, HIGH); } +enum RadioMode +{ + RECEIVER = 0, + TRANSMITTER, + TRANSCEIVER +}; + struct RadioStatus { bool TXdone = true; bool TXsending = false; bool RXdone = true; bool RXreceiving = false; + + uint16_t rxErrors = 0; + + bool hasGNSSModule = false; + RadioMode mode = (RadioMode)MODULE_MODE; } radioStatus; void setup() { @@ -119,17 +131,21 @@ void loop() { // read Main computer mavlink bytes & queue up for transmiton readMavlinkUART(); - #if MODULE_MODE == MODE_TRANSMITTER + // #if MODULE_MODE == MODE_TRANSMITTER + if (radioStatus.mode == RadioMode::TRANSMITTER || radioStatus.mode == RadioMode::TRANSCEIVER) { // queue up InternalGPSPos for transmition once per X millis sendInternalGPSPos(); // send radio transmission sendRadioTransmission(); - #endif + } + // #endif - #if MODULE_MODE == MODE_RECEIVER // later this define should be replaced by a boot pin check and even later by synchronization beetween the two sides + // #if MODULE_MODE == MODE_RECEIVER // later this define should be replaced by a boot pin check and even later by synchronization beetween the two sides + if (radioStatus.mode == RadioMode::RECEIVER || radioStatus.mode == RadioMode::TRANSCEIVER) { // read radio transmission readRadioTransmission(); - #endif + } + // #endif // send link stats to Serial once in a while sendRadioStatsToComputer(); @@ -173,7 +189,7 @@ void updateRadioStatus() (irq & RADIOLIB_SX126X_IRQ_TIMEOUT)) { if ((irq & RADIOLIB_SX126X_IRQ_CRC_ERR) || - (irq & RADIOLIB_SX126X_IRQ_HEADER_ERR)) { rxErrors++; } + (irq & RADIOLIB_SX126X_IRQ_HEADER_ERR)) { radioStatus.rxErrors++; } radioStatus.RXdone = true; turnOffLedInNextLoop = true; } @@ -183,11 +199,18 @@ void updateRadioStatus() void readInternalGPSPos() { while (Serial1.available() > 0) + { + radioStatus.hasGNSSModule = true; gpsInternal.encode(Serial1.read()); + } } void sendInternalGPSPos() { + if (!radioStatus.hasGNSSModule) { + return; // no GNSS module, nothing to send + } + static unsigned long lastSend = 0; if (millis() - lastSend < TX_INTERVAL_MS) { @@ -204,6 +227,10 @@ void sendInternalGPSPos() void sendInternalGPSToComputer() { + if (!radioStatus.hasGNSSModule) { + return; // no GNSS module, nothing to send + } + static unsigned long lastSend = 0; if (millis() - lastSend < TX_GNSS_TO_COMPUTER_INTERVAL_MS) { @@ -244,7 +271,7 @@ void sendRadioStatsToComputer() uint8_t rssiByte = (uint8_t)(rssi + 200); uint8_t noiseByte = (uint8_t)(noise + 200); - mavlink_msg_radio_status_pack(1, 221, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), noiseByte, 0, rxErrors, 0); + mavlink_msg_radio_status_pack(1, 221, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), noiseByte, 0, radioStatus.rxErrors, 0); uint8_t txBuffer[300]; uint16_t len = mavlink_msg_to_send_buffer(txBuffer, &msg); sendBytesToComputer(txBuffer, len); From a042ca8cd8c1bf76c2b3e24de38a2bb5f6f8d826 Mon Sep 17 00:00:00 2001 From: Patryk Sikora Date: Wed, 20 May 2026 01:33:51 +0200 Subject: [PATCH 3/5] true_noise prototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trzeba jeszcze przerobić na częste odczyty + rzadkie wysyłki --- src/esp32s3/esp32s3.ino | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/esp32s3/esp32s3.ino b/src/esp32s3/esp32s3.ino index e86b71a..9dc408c 100644 --- a/src/esp32s3/esp32s3.ino +++ b/src/esp32s3/esp32s3.ino @@ -26,7 +26,7 @@ #define TX_INTERVAL_MS 2000UL #define TX_GNSS_TO_COMPUTER_INTERVAL_MS 1000UL -#define TX_RADIO_STATS_TO_COMPUTER_INTERVAL_MS 1000UL +#define TX_RADIO_STATS_TO_COMPUTER_INTERVAL_MS 100 #define LOOP_DELAY_MS 10UL // main loop delay #define UART_RING_BUFFER_SIZE 8096 // zabezpieczenie przed burstami danych @@ -80,6 +80,8 @@ struct RadioStatus uint16_t rxErrors = 0; bool hasGNSSModule = false; + bool hasReceivedLoRaMessage = false; + unsigned long lastReceivedLoRaMessageTime = 0; RadioMode mode = (RadioMode)MODULE_MODE; } radioStatus; @@ -180,6 +182,8 @@ void updateRadioStatus() { ledOn(); turnOffLedInNextLoop = true; + radioStatus.hasReceivedLoRaMessage = true; + radioStatus.lastReceivedLoRaMessageTime = millis(); } // RX is done for RX_DONE, CRC_ERR, HEADER_ERR, TIMEOUT @@ -258,23 +262,50 @@ void sendRadioStatsToComputer() static mavlink_message_t msg; static mavlink_status_t mav_status; + static float noise = 0.0f; + static float weird_noise = 0.0f; + static float true_snr = 0.0f; + float rssi = radio.getRSSI(true); float snr = radio.getSNR(); - float noise = rssi - snr; + weird_noise = rssi - snr; + float noiseCandidate = radio.getRSSI(false); + + if (!radioStatus.hasReceivedLoRaMessage) { noise = noiseCandidate; } + auto cut_threshold = rssi - snr - 3.0; + + if (noiseCandidate < cut_threshold) + { + noise = noiseCandidate; + true_snr = rssi - noise; + } + else if (snr < 6.0) + { + noise = rssi - snr; + } + + + Serial.printf("RSSI: %.2f, SNR: %.2f, Noise: %.2f, True SNR: %.2f\n", rssi, snr, noise, true_snr); if (rssi < -200) rssi = -200; if (noise < -200) noise = -200; + if (weird_noise < -200) weird_noise = -200; if (rssi > 55) rssi = 55; if (noise > 55) noise = 55; + if (weird_noise > 55) weird_noise = 55; // convert rssi and noise to 0-255 range by adding 200 uint8_t rssiByte = (uint8_t)(rssi + 200); uint8_t noiseByte = (uint8_t)(noise + 200); + uint8_t weirdNoiseByte = (uint8_t)(weird_noise + 200); - mavlink_msg_radio_status_pack(1, 221, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), noiseByte, 0, radioStatus.rxErrors, 0); uint8_t txBuffer[300]; + mavlink_msg_radio_status_pack(1, 221, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), noiseByte, 0, radioStatus.rxErrors, 0); uint16_t len = mavlink_msg_to_send_buffer(txBuffer, &msg); sendBytesToComputer(txBuffer, len); + mavlink_msg_radio_status_pack(1, 222, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), weirdNoiseByte, 0, radioStatus.rxErrors, 0); + len = mavlink_msg_to_send_buffer(txBuffer, &msg); + sendBytesToComputer(txBuffer, len); } void readMavlinkUART() From a0b377e49094831a1ade347dbf71b4332a2ed387 Mon Sep 17 00:00:00 2001 From: Patryk Sikora Date: Wed, 20 May 2026 11:56:21 +0200 Subject: [PATCH 4/5] =?UTF-8?q?cz=C4=99ste=20odczyty=20noise=20i=20rzadsze?= =?UTF-8?q?=20wysy=C5=82ki?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit receiver wysyła do komputera także drugi radio_status z estymowanym noisem. Estymowany noise jest tylko w celach pomocnicznych do testów, nie jest to estymacja na której można się opierać, gdyż może zawierać błędy i czasami przedstawiać coś innego niż noise, zwłaszcza dla niskich poziomów sygnału. --- src/esp32s3/esp32s3.ino | 72 +++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/src/esp32s3/esp32s3.ino b/src/esp32s3/esp32s3.ino index 9dc408c..fa97b4d 100644 --- a/src/esp32s3/esp32s3.ino +++ b/src/esp32s3/esp32s3.ino @@ -26,7 +26,8 @@ #define TX_INTERVAL_MS 2000UL #define TX_GNSS_TO_COMPUTER_INTERVAL_MS 1000UL -#define TX_RADIO_STATS_TO_COMPUTER_INTERVAL_MS 100 +#define TX_RADIO_STATS_TO_COMPUTER_INTERVAL_MS 1000 +#define READ_NOISE_INTERVAL_MS 50UL #define LOOP_DELAY_MS 10UL // main loop delay #define UART_RING_BUFFER_SIZE 8096 // zabezpieczenie przed burstami danych @@ -48,6 +49,8 @@ void sendRadioStatsToComputer(); void readRadioTransmission(); void sendRadioTransmission(); +void readNoise(); + void readMavlinkUART(); void sendBytesToComputer(uint8_t* data, size_t length); @@ -78,6 +81,10 @@ struct RadioStatus bool RXreceiving = false; uint16_t rxErrors = 0; + + float noise_level = 0.0f; + float rssi_last_msg = 0.0f; + float snr_last_msg = 0.0f; bool hasGNSSModule = false; bool hasReceivedLoRaMessage = false; @@ -123,9 +130,11 @@ void loop() { unsigned long loopStart = millis(); // update radio status based on events - // HAS delay(1) INSIDE updateRadioStatus(); + // try to read noise level + readNoise(); + // read InternalGNSS bytes readInternalGPSPos(); sendInternalGPSToComputer(); @@ -262,30 +271,12 @@ void sendRadioStatsToComputer() static mavlink_message_t msg; static mavlink_status_t mav_status; - static float noise = 0.0f; - static float weird_noise = 0.0f; - static float true_snr = 0.0f; - - float rssi = radio.getRSSI(true); - float snr = radio.getSNR(); - weird_noise = rssi - snr; - float noiseCandidate = radio.getRSSI(false); - - if (!radioStatus.hasReceivedLoRaMessage) { noise = noiseCandidate; } - auto cut_threshold = rssi - snr - 3.0; - - if (noiseCandidate < cut_threshold) - { - noise = noiseCandidate; - true_snr = rssi - noise; - } - else if (snr < 6.0) - { - noise = rssi - snr; - } - + float rssi = radioStatus.rssi_last_msg; + float noise = radioStatus.noise_level; + float weird_noise = radioStatus.rssi_last_msg - radioStatus.snr_last_msg; - Serial.printf("RSSI: %.2f, SNR: %.2f, Noise: %.2f, True SNR: %.2f\n", rssi, snr, noise, true_snr); + // Serial.printf("\nRSSI: %.2f, Noise: %.2f, WeirdNoise: %.2f\n", rssi, noise, weird_noise); + // Serial.printf("SNR: %.2f True SNR: %.2f\n", radioStatus.snr_last_msg, rssi - noise); if (rssi < -200) rssi = -200; if (noise < -200) noise = -200; @@ -300,14 +291,38 @@ void sendRadioStatsToComputer() uint8_t weirdNoiseByte = (uint8_t)(weird_noise + 200); uint8_t txBuffer[300]; - mavlink_msg_radio_status_pack(1, 221, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), noiseByte, 0, radioStatus.rxErrors, 0); + // reliable rssi and link quality stats + mavlink_msg_radio_status_pack(1, 221, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), weirdNoiseByte, 0, radioStatus.rxErrors, 0); uint16_t len = mavlink_msg_to_send_buffer(txBuffer, &msg); sendBytesToComputer(txBuffer, len); - mavlink_msg_radio_status_pack(1, 222, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), weirdNoiseByte, 0, radioStatus.rxErrors, 0); + + if (radioStatus.mode == RadioMode::TRANSMITTER) { + return; // in transmitter mode we don't listen to the ether, so we don't know what is in there + } + // estimated, randomly tried noise floor value + mavlink_msg_radio_status_pack(1, 222, &msg, rssiByte, 0, (uint8_t)loraQueue.getFillPercentage(), noiseByte, 0, radioStatus.rxErrors, 0); len = mavlink_msg_to_send_buffer(txBuffer, &msg); sendBytesToComputer(txBuffer, len); } +void readNoise() +{ + static unsigned long lastRead = 0; + + if (millis() - lastRead < READ_NOISE_INTERVAL_MS) { + return; // not time to read yet + } + lastRead = millis(); + + float noiseCandidate = radio.getRSSI(false); + if (noiseCandidate < radioStatus.rssi_last_msg - radioStatus.snr_last_msg - 3.0) { + radioStatus.noise_level = noiseCandidate; + } + else if (radioStatus.snr_last_msg < 6.0) { + radioStatus.noise_level = radioStatus.rssi_last_msg - radioStatus.snr_last_msg; + } +} + void readMavlinkUART() { static mavlink_message_t msg; @@ -356,6 +371,9 @@ void readRadioTransmission() radioStatus.RXreceiving = false; radio.finishReceive(); + radioStatus.rssi_last_msg = radio.getRSSI(true); + radioStatus.snr_last_msg = radio.getSNR(); + byte data[300]; int numBytes = radio.getPacketLength(); int state = radio.readData(data, numBytes); From 4c6af12056a3b8f73d01c6caf76e8c8cfeb34b19 Mon Sep 17 00:00:00 2001 From: Patryk Sikora Date: Wed, 20 May 2026 12:16:38 +0200 Subject: [PATCH 5/5] =?UTF-8?q?dodano=20topic=20z=20estymacj=C4=85=20noise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lora_node.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lora_node.py b/lora_node.py index 149c8f5..cd1f571 100644 --- a/lora_node.py +++ b/lora_node.py @@ -21,6 +21,7 @@ def __init__(self): self.publisher_lora_gps = self.create_publisher(GpsShort, '/rocket/lora/gps', 10) self.publisher_rocket_gps = self.create_publisher(GpsShort, '/rocket/gps', 10) self.publisher_lora_status = self.create_publisher(LoraStatus, '/mission_control/lora/status', 10) + self.publisher_lora_extra_status = self.create_publisher(LoraStatus, '/mission_control/lora/extra_status', 10) self.publisher_lora_gnss = self.create_publisher(LoraGnss, '/mission_control/lora/gnss', 10) self.mavlink_connection = mavutil.mavlink_connection('/dev/ttyACM0', baud=57600) @@ -65,7 +66,6 @@ def reading_thread(self): elif msg.get_type() == 'RADIO_STATUS': ros_msg = LoraStatus() ros_msg.header.stamp = self.get_clock().now().to_msg() - ros_msg.header.frame_id = '/mission_control/lora' ros_msg.rssi = float(msg.rssi) - 200 ros_msg.noise = float(msg.noise) - 200 ros_msg.snr = float(msg.rssi) - float(msg.noise) @@ -75,7 +75,13 @@ def reading_thread(self): ros_msg.rem_snr = float(msg.remrssi) - float(msg.remnoise) ros_msg.rx_errors = msg.rxerrors ros_msg.rx_fixed = msg.fixed - self.publisher_lora_status.publish(ros_msg) + + ros_msg.header.frame_id = '/mission_control/lora' + + if msg.get_srcComponent() == 222: + self.publisher_lora_extra_status.publish(ros_msg) + else: + self.publisher_lora_status.publish(ros_msg) # self.get_logger().info(f'Published LoRa Status: {ros_msg}') elif msg.get_type() == 'SIMBA_GPS': ros_msg = GpsShort()