// // Created by Kurosu Chan on 2024/1/26. // #ifndef LLCC68_DEFINITIONS_H #define LLCC68_DEFINITIONS_H #include #include #include #include #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; 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 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; 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 ¶ms) { 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 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 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 ¶ms) { 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