Files
ZephCore/zephcore/adapters/radio/LoRaRadioBase.h
T
liquidraver 30379ee48a LoRaRadioBase volatile → atomic_t
LR1110 BUSY pin → interrupt-driven
2026-03-04 22:36:34 +01:00

180 lines
5.3 KiB
C++

/*
* SPDX-License-Identifier: Apache-2.0
* LoRa radio base class — all shared state and algorithms.
*
* Subclasses (SX126xRadio, LR1110Radio, …) only implement the hw*()
* primitives that talk to real hardware. Everything else lives here.
*/
#pragma once
#include <mesh/Radio.h>
#include <mesh/Board.h>
#include <NodePrefs.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include "radio_common.h"
namespace mesh {
class LoRaRadioBase : public Radio {
public:
LoRaRadioBase(const struct device *lora_dev, MainBoard &board,
NodePrefs *prefs = nullptr);
void setPrefs(NodePrefs *prefs) { _prefs = prefs; }
/* Callbacks */
void setRxCallback(RadioRxCallback cb, void *user_data) {
_rx_cb = cb;
_rx_cb_user_data = user_data;
}
void setTxDoneCallback(RadioTxDoneCallback cb, void *user_data) {
_tx_done_cb = cb;
_tx_done_cb_user_data = user_data;
}
/* Radio interface (all implemented in base) */
void begin() override;
void reconfigure();
void reconfigureWithParams(float freq, float bw, uint8_t sf, uint8_t cr);
int recvRaw(uint8_t *bytes, int sz) override;
uint32_t getEstAirtimeFor(int len_bytes) override;
float packetScore(float snr, int packet_len) override;
bool startSendRaw(const uint8_t *bytes, int len) override;
bool isSendComplete() override;
void onSendFinished() override;
bool isInRecvMode() const override;
float getLastRSSI() const override;
float getLastSNR() const override;
/* Packet statistics */
uint32_t getPacketsRecv() const override { return (uint32_t)atomic_get(&_packets_recv); }
uint32_t getPacketsSent() const override { return (uint32_t)atomic_get(&_packets_sent); }
uint32_t getPacketsRecvErrors() const override { return (uint32_t)atomic_get(&_packets_recv_errors); }
void resetStats() {
atomic_set(&_packets_recv, 0);
atomic_set(&_packets_sent, 0);
atomic_set(&_packets_recv_errors, 0);
}
/* Advanced radio features */
int getNoiseFloor() const override;
void triggerNoiseFloorCalibrate(int threshold) override;
void resetAGC() override;
bool isReceiving() override;
/* Extended API */
bool isChannelActive(int threshold = 0);
/* Power saving */
void enableRxDutyCycle(bool enable);
bool isRxDutyCycleEnabled() const { return _rx_duty_cycle_enabled; }
void setRxBoost(bool enable);
bool isRxBoostEnabled() const { return _rx_boost_enabled; }
protected:
/* ── Hardware primitives — subclass MUST implement ─────────── */
/** Apply lora_modem_config + any chip-specific extras (image cal, etc.) */
virtual void hwConfigure(const struct lora_modem_config &cfg) = 0;
/** Start async receive (lora_recv_async + boost/duty-cycle setup) */
virtual void hwStartReceive() = 0;
/** Cancel current async receive */
virtual void hwCancelReceive() = 0;
/** Start async send — returns 0 on success */
virtual int hwSendAsync(uint8_t *buf, uint32_t len,
struct k_poll_signal *sig) = 0;
/** Read instantaneous RSSI from hardware */
virtual int16_t hwGetCurrentRSSI() = 0;
/** Check IRQ flags for preamble/header detection */
virtual bool hwIsPreambleDetected() = 0;
/** Set RX LNA boost on/off */
virtual void hwSetRxBoost(bool enable) = 0;
/** Set RX duty cycle on/off (hardware level) */
virtual void hwSetRxDutyCycle(bool enable) = 0;
/** Reset AGC (chip-specific, may be no-op) */
virtual void hwResetAGC() = 0;
/* ── Shared helpers available to subclasses ────────────────── */
void buildModemConfig(struct lora_modem_config &cfg, bool tx);
void configureRx();
void configureTx();
void startReceive();
/* Subclass begin() should call this to start TX wait thread */
void startTxThread(k_thread_stack_t *stack, size_t stack_size);
const struct device *_dev;
NodePrefs *_prefs;
MainBoard *_board;
atomic_t _in_recv_mode;
atomic_t _tx_active;
volatile float _last_rssi; /* word-aligned float — atomic on ARM */
volatile float _last_snr; /* word-aligned float — atomic on ARM */
/* RX ring buffer */
struct RxPacket {
uint8_t data[256];
uint16_t len;
int16_t rssi;
int8_t snr;
};
RxPacket _rx_ring[RX_RING_SIZE];
atomic_t _rx_head;
atomic_t _rx_tail;
/* TX buffer + signal */
uint8_t _tx_buf[256];
struct k_poll_signal _tx_signal;
/* Noise floor calibration state */
int _noise_floor;
int _calibration_threshold;
uint8_t _ema_unguarded; /* ticks until next unfiltered sample */
/* Power saving */
bool _rx_duty_cycle_enabled;
bool _rx_boost_enabled;
/* Config cache — skip redundant hwConfigure() on TX↔RX transitions */
struct lora_modem_config _last_cfg;
bool _config_cached;
/* Static RX callback — passed to lora_recv_async() by subclass hwStartReceive() */
static void rxCallbackStatic(const struct device *dev, uint8_t *data,
uint16_t size, int16_t rssi, int8_t snr,
void *user_data);
private:
/* RX notification callback */
RadioRxCallback _rx_cb;
void *_rx_cb_user_data;
/* TX done callback */
RadioTxDoneCallback _tx_done_cb;
void *_tx_done_cb_user_data;
/* TX completion thread */
static void txWaitThreadFn(void *p1, void *p2, void *p3);
struct k_thread _tx_wait_thread;
struct k_sem _tx_start_sem;
bool _tx_thread_running;
/* Packet statistics */
atomic_t _packets_recv;
atomic_t _packets_sent;
atomic_t _packets_recv_errors;
};
} /* namespace mesh */