Files
esp_llcc68_driver/inc/llcc68_definitions.hpp
crosstyan c549538844 Enhance llcc68 definitions with new bandwidth and coding rate enums
- Added a new bandwidth enum value for 7.8MHz and corresponding string conversion function.
- Introduced a string conversion function for coding rates.
- Implemented a utility function to safely convert uint8_t to optional for spreading factors.
- Enhanced the modulation parameters structure with a to_string method for better representation.
2025-06-03 18:33:51 +08:00

421 lines
9.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by Kurosu Chan on 2024/1/26.
//
#ifndef LLCC68_DEFINITIONS_H
#define LLCC68_DEFINITIONS_H
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <format>
#include <tuple>
#include <variant>
#include "radiolib_definitions.hpp"
namespace app::driver::llcc68 {
using freq_t = float;
enum LoRaBandwidth : uint8_t {
BW_7_8 = 0x00,
BW_10_4 = 0x08,
BW_15_6 = 0x01,
BW_20_8 = 0x09,
BW_31_25 = 0x02,
BW_41_7 = 0x0A,
BW_62_5 = 0x03,
BW_125_0 = 0x04,
BW_250_0 = 0x05,
BW_500_0 = 0x06,
};
inline const char *bw_to_string(const LoRaBandwidth &bw) {
switch (bw) {
case LoRaBandwidth::BW_7_8:
return "7.8MHz";
case LoRaBandwidth::BW_10_4:
return "10.4MHz";
case LoRaBandwidth::BW_15_6:
return "15.6MHz";
case LoRaBandwidth::BW_20_8:
return "20.8MHz";
case LoRaBandwidth::BW_31_25:
return "31.25MHz";
case LoRaBandwidth::BW_41_7:
return "41.7MHz";
case LoRaBandwidth::BW_62_5:
return "62.5MHz";
case LoRaBandwidth::BW_125_0:
return "125.0MHz";
case LoRaBandwidth::BW_250_0:
return "250.0MHz";
case LoRaBandwidth::BW_500_0:
return "500.0MHz";
default:
return "Unknown";
}
}
enum LoRaCodingRate : uint8_t {
CR_4_5 = 0x01,
CR_4_6 = 0x02,
CR_4_7 = 0x03,
CR_4_8 = 0x04,
};
inline const char *cr_to_string(const LoRaCodingRate &cr) {
switch (cr) {
case LoRaCodingRate::CR_4_5:
return "4/5";
case LoRaCodingRate::CR_4_6:
return "4/6";
case LoRaCodingRate::CR_4_7:
return "4/7";
case LoRaCodingRate::CR_4_8:
return "4/8";
default:
return "Unknown";
}
}
constexpr uint16_t DEFAULT_PREAMBLE_LEN = 8;
constexpr uint8_t DEFAULT_SYNC_WORD = RADIOLIB_SX126X_SYNC_WORD_PRIVATE;
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 auto DEFAULT_SF = 7;
constexpr auto DEFAULT_FREQUENCY = freq_t{433.05};
constexpr auto DEFAULT_BW = BW_250_0;
constexpr auto DEFAULT_CR = CR_4_5;
constexpr auto DEFAULT_LDR_OPTIMIZE = RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF;
inline std::optional<uint8_t> sf_from_uint8_t(const uint8_t sf) {
if (sf < 6 || sf > 12) {
return std::nullopt;
}
return sf;
}
struct modulation_params_t {
uint8_t bw;
uint8_t sf;
uint8_t cr;
uint8_t ldr_optimize;
static modulation_params_t Default() {
return modulation_params_t{
.bw = DEFAULT_BW,
.sf = DEFAULT_SF,
.cr = DEFAULT_CR,
.ldr_optimize = DEFAULT_LDR_OPTIMIZE,
};
}
std::string to_string() const {
return std::format("mod_params: bw={}, sf={}, cr={}, ldr_optimize={}",
bw_to_string(static_cast<LoRaBandwidth>(bw)),
sf,
cr_to_string(static_cast<LoRaCodingRate>(cr)),
ldr_optimize ? "ON" : "OFF");
}
};
struct packet_params_t {
uint16_t preamble_len;
uint8_t payload_len;
uint8_t crc_type;
uint8_t hdr_type;
static packet_params_t Default() {
return packet_params_t{
.preamble_len = DEFAULT_PREAMBLE_LEN,
.payload_len = 0xff,
.crc_type = DEFAULT_CRC_TYPE,
.hdr_type = DEFAULT_HEADER_TYPE,
};
}
};
struct lora_parameters_t {
modulation_params_t mod_params;
packet_params_t packet_params;
freq_t frequency;
uint8_t sync_word;
static lora_parameters_t Default() {
return lora_parameters_t{
.mod_params = modulation_params_t::Default(),
.packet_params = packet_params_t::Default(),
.frequency = DEFAULT_FREQUENCY,
.sync_word = DEFAULT_SYNC_WORD,
};
}
};
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 __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);
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});
}
}
#endif // LLCC68_DEFINITIONS_H