This commit is contained in:
2025-05-14 12:17:06 +08:00
commit d24d9d5dda
16 changed files with 5955 additions and 0 deletions

391
inc/llcc68_definitions.hpp Normal file
View File

@ -0,0 +1,391 @@
//
// Created by Kurosu Chan on 2024/1/26.
//
#ifndef LLCC68_DEFINITIONS_H
#define LLCC68_DEFINITIONS_H
#include <cstddef>
#include <cstdint>
#include <tuple>
#include <variant>
#include "radiolib_definitions.hpp"
namespace app::driver::llcc68 {
using freq_t = float;
constexpr uint8_t DEFAULT_SYNC_WORD = RADIOLIB_SX126X_SYNC_WORD_PRIVATE;
constexpr uint16_t DEFAULT_PREAMBLE_LEN = 6; // recommended is 8 symbols though
constexpr uint8_t DEFAULT_CRC_TYPE = RADIOLIB_SX126X_LORA_CRC_OFF;
constexpr uint8_t DEFAULT_HEADER_TYPE = RADIOLIB_SX126X_LORA_HEADER_EXPLICIT;
constexpr uint8_t DEFAULT_IQ_TYPE = RADIOLIB_SX126X_LORA_IQ_STANDARD;
constexpr uint8_t PACKET_TYPE = RADIOLIB_SX126X_PACKET_TYPE_LORA;
constexpr auto DEFAULT_BW = RADIOLIB_SX126X_LORA_BW_125_0;
constexpr auto DEFAULT_SF = 9;
constexpr auto DEFAULT_CR = RADIOLIB_SX126X_LORA_CR_4_6;
constexpr auto DEFAULT_LDR_OPTIMIZE = RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON;
constexpr auto DEFAULT_FREQUENCY = freq_t{433.2};
enum class ChipType {
Unknown,
LLCC68,
SX1261,
SX1262,
};
inline const char *chip_type_str(const ChipType &chip) {
switch (chip) {
case ChipType::LLCC68:
return "LLCC68";
case ChipType::SX1261:
return "SX1261";
case ChipType::SX1262:
return "SX1262";
default:
return "Unknown";
}
}
/// @note suitable SX1262/LLCC68
enum class PaOptimalPresetHigh : uint8_t {
DBM_14,
DBM_17,
DBM_20,
DBM_22,
};
/// @note suitable SX1261
enum class PaOptimalPresetLow : uint8_t {
DBM_10,
DBM_14,
DBM_15,
};
using PaOptimalPreset = std::variant<PaOptimalPresetHigh, PaOptimalPresetLow>;
struct pa_config_t {
uint8_t pa_duty_cycle;
uint8_t hp_max;
uint8_t device_sel;
};
enum class PacketType : uint8_t {
FSK = RADIOLIB_SX126X_PACKET_TYPE_GFSK,
LORA = RADIOLIB_SX126X_PACKET_TYPE_LORA,
LR_FHSS = RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS,
};
enum class TcxoVoltage : uint8_t {
V_1_6 = 0x00,
V_1_7 = 0x01,
V_1_8 = 0x02,
V_2_2 = 0x03,
V_2_4 = 0x04,
V_2_7 = 0x05,
V_3_0 = 0x06,
V_3_3 = 0x07,
};
enum class CommandStatus : uint8_t {
RESERVED = 0x00, // 0b000
RFU = 0x01, // 0b001
DATA_AVAILABLE = 0x02, // 0b010
COMMAND_TIMEOUT = 0x03, // 0b011 i.e. TIMEOUT
COMMAND_PROCESSING_ERROR = 0x04, // 0b100 i.e. INVALID
FAILURE_TO_EXECUTE_COMMAND = 0x05, // 0b101 i.e. FAILED
COMMAND_TX_DONE = 0x06, // 0b110
};
enum class ChipMode : uint8_t {
UNUSED = 0x00, // 0b000
RFU = 0x01, // 0b001
STBY_RC = 0x02, // 0b010
STBY_XOSC = 0x03, // 0b011
FS = 0x04, // 0b100
RX = 0x05, // 0b101
TX = 0x06, // 0b110
};
struct __attribute__((packed)) status_t {
uint8_t reserved_1 : 1;
CommandStatus command_status : 3;
ChipMode chip_mode : 3;
uint8_t reserved_2 : 1;
};
static_assert(sizeof(status_t) == 1);
struct parameters_t {
uint8_t bw;
uint8_t sf;
uint8_t cr;
uint8_t ldr_optimize;
uint16_t preamble_len;
uint8_t sync_word;
freq_t frequency;
static parameters_t Default() {
return parameters_t{
.bw = DEFAULT_BW,
.sf = DEFAULT_SF,
.cr = DEFAULT_CR,
.ldr_optimize = DEFAULT_LDR_OPTIMIZE,
.preamble_len = DEFAULT_PREAMBLE_LEN,
.sync_word = DEFAULT_SYNC_WORD,
.frequency = DEFAULT_FREQUENCY,
};
}
constexpr static size_t size() {
return sizeof(parameters_t);
}
} __attribute__((packed));
struct __attribute__((packed)) lora_packet_status_t {
// Average over last packet received of RSSI
// Actual signal power is RssiPkt/2 (dBm)
uint8_t rssi_pkt;
// Estimation of SNR on last packet received in twos compliment format multiplied by 4.
// Actual SNR in dB =SnrPkt/4
uint8_t snr_pkt;
// Estimation of RSSI of the LoRa® signal (after despreading) on last packet received.
// Actual Rssi in dB = -SignalRssiPkt/2
uint8_t signal_rssi_pkt;
float rssi_pkt_dbm() const {
return -rssi_pkt / 2.0f;
}
float snr_pkt_db() const {
return snr_pkt / 4.0f;
}
float signal_rssi_pkt_dbm() const {
return -signal_rssi_pkt / 2.0f;
}
};
struct __attribute__((packed)) fsk_packet_status_t {
// bit 7: preamble err
// bit 6: sync err
// bit 5: adrs err
// bit 4: crc err
// bit 3: length err
// bit 2: abort err
// bit 1: pkt received
// bit 0: pkt sent
uint8_t rx_status;
// RSSI value latched upon the detection of the sync address.
// [negated, dBm, fixdt(0,8,1)]
// Actual signal power is RssiSync/2 (dBm)
uint8_t rssi_sync;
// RSSI average value over the payload of the received packet. Latched upon the pkt_done IRQ.
// [negated, dBm, fixdt(0,8,1)]
// Actual signal power is RssiAvg/2 (dBm)
uint8_t rssi_avg;
};
union packet_status_t {
uint8_t raw[3];
lora_packet_status_t lora;
fsk_packet_status_t fsk;
};
struct __attribute__((packed)) op_error_t {
bool RC64K_CALIB_ERR : 1; // RC64k calibration failed
bool RC13M_CALIB_ERR : 1; // RC13M calibration failed
bool PLL_CALIB_ERR : 1; // PLL calibration failed
bool ADC_CALIB_ERR : 1; // ADC calibration failed
bool IMG_CALIB_ERR : 1; // IMG calibration failed
bool XOSC_START_ERR : 1; // XOSC failed to start
bool PLL_LOCK_ERR : 1; // PLL failed to lock
uint8_t rfu_1 : 1; // RFU
bool PA_RAMP_ERR : 1; // PA ramping failed
uint8_t rfu_2 : 7; // RFU
};
static_assert(sizeof(op_error_t) == 2);
struct calc_time_on_air_t {
uint8_t bw;
uint8_t sf;
uint8_t cr;
uint16_t preamble_length;
static constexpr uint8_t ldro = DEFAULT_LDR_OPTIMIZE;
static constexpr uint8_t crc_type = DEFAULT_CRC_TYPE;
static constexpr uint8_t header_type = DEFAULT_HEADER_TYPE;
static constexpr uint8_t iq_type = DEFAULT_IQ_TYPE;
static constexpr uint8_t sync_word = DEFAULT_SYNC_WORD;
static calc_time_on_air_t from_parameters(const parameters_t &params) {
return calc_time_on_air_t{
.bw = params.bw,
.sf = params.sf,
.cr = params.cr,
.preamble_length = params.preamble_len};
}
};
constexpr bool in_range(const auto v, const auto min, const auto max) {
return v >= min && v <= max;
}
constexpr bool valid_cr(const uint8_t cr) {
switch (cr) {
case RADIOLIB_SX126X_LORA_CR_4_5:
case RADIOLIB_SX126X_LORA_CR_4_6:
case RADIOLIB_SX126X_LORA_CR_4_7:
case RADIOLIB_SX126X_LORA_CR_4_8:
return true;
default:
return false;
}
}
constexpr std::tuple<uint8_t, uint8_t>
cr_to_ratio(const uint8_t cr) {
switch (cr) {
case RADIOLIB_SX126X_LORA_CR_4_5:
return {4, 5};
case RADIOLIB_SX126X_LORA_CR_4_6:
return {4, 6};
case RADIOLIB_SX126X_LORA_CR_4_7:
return {4, 7};
case RADIOLIB_SX126X_LORA_CR_4_8:
return {4, 8};
default:
return {0, 1};
}
}
constexpr bool valid_ldr_optimize(const uint8_t ldr_optimize) {
if (ldr_optimize > RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON) {
return false;
}
return true;
}
constexpr bool valid_bw(const uint8_t bw) {
switch (bw) {
case RADIOLIB_SX126X_LORA_BW_7_8:
case RADIOLIB_SX126X_LORA_BW_10_4:
case RADIOLIB_SX126X_LORA_BW_15_6:
case RADIOLIB_SX126X_LORA_BW_20_8:
case RADIOLIB_SX126X_LORA_BW_31_25:
case RADIOLIB_SX126X_LORA_BW_41_7:
case RADIOLIB_SX126X_LORA_BW_62_5:
case RADIOLIB_SX126X_LORA_BW_125_0:
case RADIOLIB_SX126X_LORA_BW_250_0:
case RADIOLIB_SX126X_LORA_BW_500_0:
return true;
default:
return false;
}
}
using bw_t = float;
constexpr bw_t bw_khz(const uint8_t bw) {
switch (bw) {
case RADIOLIB_SX126X_LORA_BW_7_8:
return bw_t{7.8f};
case RADIOLIB_SX126X_LORA_BW_10_4:
return bw_t{10.4f};
case RADIOLIB_SX126X_LORA_BW_15_6:
return bw_t{15.6f};
case RADIOLIB_SX126X_LORA_BW_20_8:
return bw_t{20.8f};
case RADIOLIB_SX126X_LORA_BW_31_25:
return bw_t{31.25f};
case RADIOLIB_SX126X_LORA_BW_41_7:
return bw_t{41.7f};
case RADIOLIB_SX126X_LORA_BW_62_5:
return bw_t{62.5f};
case RADIOLIB_SX126X_LORA_BW_125_0:
return bw_t{125.0f};
case RADIOLIB_SX126X_LORA_BW_250_0:
return bw_t{250.0f};
case RADIOLIB_SX126X_LORA_BW_500_0:
return bw_t{500.0f};
default:
return bw_t{0};
}
}
constexpr bool valid_sf(const uint8_t bw, const uint8_t sf) {
if (const bool ok = valid_bw(bw); not ok) {
return false;
}
switch (bw) {
case RADIOLIB_SX126X_LORA_BW_125_0:
return in_range(sf, 5, 9);
case RADIOLIB_SX126X_LORA_BW_250_0:
return in_range(sf, 5, 10);
case RADIOLIB_SX126X_LORA_BW_500_0:
return in_range(sf, 5, 11);
default:
return in_range(sf, 5, 12);
}
}
constexpr bool valid_freq(const freq_t freq) {
return in_range(freq, freq_t{150.0}, freq_t{960.0});
}
/**
* \brief check if the parameters are valid. if not, set them to default.
* \param params the parameters to check
* \return if the parameters are valid, and the modified parameters
*/
constexpr std::tuple<bool, parameters_t>
check_fix_params(parameters_t params) {
bool ok = true;
if (not valid_bw(params.bw)) {
params.bw = DEFAULT_BW;
ok = false;
}
if (not valid_sf(params.bw, params.sf)) {
params.sf = DEFAULT_SF;
ok = false;
}
if (not valid_cr(params.cr)) {
params.cr = DEFAULT_CR;
ok = false;
}
if (not valid_freq(params.frequency)) {
params.frequency = DEFAULT_FREQUENCY;
ok = false;
}
if (not valid_ldr_optimize(params.ldr_optimize)) {
params.ldr_optimize = DEFAULT_LDR_OPTIMIZE;
ok = false;
}
return {ok, params};
}
/**
* \brief only do the check
*/
constexpr bool check_params(const parameters_t &params) {
if (not valid_bw(params.bw)) {
return false;
}
if (not valid_sf(params.bw, params.sf)) {
return false;
}
if (not valid_cr(params.cr)) {
return false;
}
if (not valid_freq(params.frequency)) {
return false;
}
if (not valid_ldr_optimize(params.ldr_optimize)) {
return false;
}
return true;
}
}
#endif // LLCC68_DEFINITIONS_H