// // Created by Kurosu Chan on 2024/1/26. // #ifndef LLCC68_DEFINITIONS_H #define LLCC68_DEFINITIONS_H #include #include #include #include #include #include #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, }; enum RegulatorMode : uint8_t { REGULATOR_LDO = RADIOLIB_SX126X_REGULATOR_LDO, REGULATOR_DC_DC = RADIOLIB_SX126X_REGULATOR_DC_DC, }; inline const char *to_str(LoRaBandwidth bw) { switch (bw) { case LoRaBandwidth::BW_7_8: return "7.8kHz"; case LoRaBandwidth::BW_10_4: return "10.4kHz"; case LoRaBandwidth::BW_15_6: return "15.6kHz"; case LoRaBandwidth::BW_20_8: return "20.8kHz"; case LoRaBandwidth::BW_31_25: return "31.25kHz"; case LoRaBandwidth::BW_41_7: return "41.7kHz"; case LoRaBandwidth::BW_62_5: return "62.5kHz"; case LoRaBandwidth::BW_125_0: return "125.0kHz"; case LoRaBandwidth::BW_250_0: return "250.0kHz"; case LoRaBandwidth::BW_500_0: return "500.0kHz"; 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 *to_str(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 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, }; } [[nodiscard]] auto to_string() const -> std::string { return std::format("mod_params: bw={}, sf={}, cr={}, ldr_optimize={}", to_str(static_cast(bw)), sf, to_str(static_cast(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; 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 }; inline const char *to_str(const CommandStatus &status) { switch (status) { case CommandStatus::RESERVED: return "RESERVED"; case CommandStatus::RFU: return "RFU"; case CommandStatus::DATA_AVAILABLE: return "DATA_AVAILABLE"; case CommandStatus::COMMAND_TIMEOUT: return "COMMAND_TIMEOUT"; case CommandStatus::COMMAND_PROCESSING_ERROR: return "COMMAND_PROCESSING_ERROR"; case CommandStatus::FAILURE_TO_EXECUTE_COMMAND: return "FAILURE_TO_EXECUTE_COMMAND"; case CommandStatus::COMMAND_TX_DONE: return "COMMAND_TX_DONE"; default: return "Unknown"; } } 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 }; inline const char *to_str(const ChipMode &mode) { switch (mode) { case ChipMode::UNUSED: return "UNUSED"; case ChipMode::RFU: return "RFU"; case ChipMode::STBY_RC: return "STBY_RC"; case ChipMode::STBY_XOSC: return "STBY_XOSC"; case ChipMode::FS: return "FS"; case ChipMode::RX: return "RX"; case ChipMode::TX: return "TX"; default: return "Unknown"; } } 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 two’s 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; [[nodiscard]] auto rssi_pkt_dbm() const -> float { return -rssi_pkt / 2.0F; } [[nodiscard]] auto snr_pkt_db() const -> float { return snr_pkt / 4.0F; } [[nodiscard]] auto signal_rssi_pkt_dbm() const -> float { 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 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) { return static_cast(ldr_optimize <= RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON); } 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