feat: add shared GMSK radio profile helpers

Add a fixed 6-byte 100 kbps GMSK profile matching the health-band telemetry packet, plus FSK packet-status helpers for RSSI and error checks. Expose instantaneous RSSI reads for CCA and image calibration for 430-440 MHz GFSK bring-up.
This commit is contained in:
2026-05-19 12:20:49 +08:00
parent f01aed8a56
commit 41b4aed456
3 changed files with 73 additions and 0 deletions
+2
View File
@@ -125,6 +125,7 @@ struct LLCC68 {
expected<status_t, error_code> get_status(); expected<status_t, error_code> get_status();
expected<packet_status_t, error_code> get_packet_status(); expected<packet_status_t, error_code> get_packet_status();
expected<uint8_t, error_code> get_rssi_inst_x2_neg();
expected<unit, error_code> reset(); expected<unit, error_code> reset();
expected<unit, error_code> set_standby(); expected<unit, error_code> set_standby();
expected<ChipType, error_code> hal_get_chip_type(); expected<ChipType, error_code> hal_get_chip_type();
@@ -164,6 +165,7 @@ struct LLCC68 {
expected<unit, error_code> set_gfsk_crc_seed(uint16_t seed); expected<unit, error_code> set_gfsk_crc_seed(uint16_t seed);
expected<unit, error_code> set_gfsk_crc_polynomial(uint16_t polynomial); expected<unit, error_code> set_gfsk_crc_polynomial(uint16_t polynomial);
expected<unit, error_code> set_gfsk_whitening_seed(uint16_t seed); expected<unit, error_code> set_gfsk_whitening_seed(uint16_t seed);
expected<unit, error_code> calibrate_image(uint8_t freq1, uint8_t freq2);
expected<unit, error_code> set_tx_params(tx_params_t params); expected<unit, error_code> set_tx_params(tx_params_t params);
expected<unit, error_code> set_tx_params(int8_t pwr, expected<unit, error_code> set_tx_params(int8_t pwr,
LoRaTxRampTime ramp_time); LoRaTxRampTime ramp_time);
+54
View File
@@ -476,6 +476,40 @@ struct gfsk_parameters_t {
.broadcast_address = std::nullopt, .broadcast_address = std::nullopt,
}; };
} }
static constexpr gfsk_parameters_t
Gmsk100kFixed6(freq_t frequency_mhz = 433.45,
tx_params_t tx_params = tx_params_t::Default()) {
return {
.mod_params =
{
.bitrate_bps = 100'000,
.frequency_deviation_hz = 25'000,
.pulse_shape = GfskPulseShape::Gauss05,
.rx_bandwidth = GfskRxBandwidth::Bw187200,
},
.packet_params =
{
.preamble_bits = 32,
.detector_length = GfskPreambleDetector::Bits16,
.sync_length_bits = 32,
.address_filtering = GfskAddressFiltering::Disabled,
.packet_length_mode = GfskPacketLengthMode::Fixed,
.payload_length = 6,
.crc_type = GfskCrcType::Off,
.whitening = GfskWhitening::On,
},
.tx_params = tx_params,
.frequency_mhz = frequency_mhz,
.sync_word = {0x2D, 0xD4, 0xA1, 0x7E, 0, 0, 0, 0},
.sync_word_length = 4,
.crc_seed = 0x1D0F,
.crc_polynomial = 0x1021,
.whitening_seed = 0x0100,
.node_address = std::nullopt,
.broadcast_address = std::nullopt,
};
}
void log(const char *tag) const; void log(const char *tag) const;
}; };
@@ -656,6 +690,26 @@ struct __attribute__((packed)) fsk_packet_status_t {
// the pkt_done IRQ. [negated, dBm, fixdt(0,8,1)] Actual signal power is // the pkt_done IRQ. [negated, dBm, fixdt(0,8,1)] Actual signal power is
// RssiAvg/2 (dBm) // RssiAvg/2 (dBm)
uint8_t rssi_avg; uint8_t rssi_avg;
[[nodiscard]]
bool has_error() const {
return (rx_status & 0b11111100U) != 0;
}
[[nodiscard]]
bool packet_received() const {
return (rx_status & 0b00000010U) != 0;
}
[[nodiscard]]
auto rssi_sync_dbm() const -> float {
return -static_cast<float>(rssi_sync) / 2.0F;
}
[[nodiscard]]
auto rssi_avg_dbm() const -> float {
return -static_cast<float>(rssi_avg) / 2.0F;
}
}; };
union packet_status_t { union packet_status_t {
+17
View File
@@ -569,6 +569,14 @@ expected<packet_status_t, error_code> LLCC68::get_packet_status() {
return ps; return ps;
} }
expected<uint8_t, error_code> LLCC68::get_rssi_inst_x2_neg() {
uint8_t raw = 0;
auto r = read_stream(RADIOLIB_SX126X_CMD_GET_RSSI_INST,
std::span<uint8_t>{&raw, 1});
APP_RADIO_RETURN_ERR_IGNORE_PROC_ERR(r);
return raw;
}
expected<unit, error_code> LLCC68::reset() { expected<unit, error_code> LLCC68::reset() {
// Drive RESET low-high with delays per datasheet, then poll standby // Drive RESET low-high with delays per datasheet, then poll standby
if (not device_is_ready(config().reset_gpio.port)) { if (not device_is_ready(config().reset_gpio.port)) {
@@ -899,6 +907,12 @@ expected<unit, error_code> LLCC68::set_gfsk_whitening_seed(uint16_t seed) {
std::span<const uint8_t>{&lsb, 1}); std::span<const uint8_t>{&lsb, 1});
} }
expected<unit, error_code> LLCC68::calibrate_image(uint8_t freq1,
uint8_t freq2) {
const uint8_t data[] = {freq1, freq2};
return write_stream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data);
}
expected<unit, error_code> LLCC68::fix_inverted_iq(uint8_t iq_config) { expected<unit, error_code> LLCC68::fix_inverted_iq(uint8_t iq_config) {
uint8_t iqCfg = 0; uint8_t iqCfg = 0;
auto r = read_register(RADIOLIB_SX126X_REG_IQ_CONFIG, auto r = read_register(RADIOLIB_SX126X_REG_IQ_CONFIG,
@@ -1202,6 +1216,9 @@ LLCC68::hal_gfsk_modem_init(gfsk_parameters_t params) {
APP_RADIO_RETURN_ERR_CTX(set_packet_type_gfsk(), APP_RADIO_RETURN_ERR_CTX(set_packet_type_gfsk(),
"gfsk_init::set_packet_type"); "gfsk_init::set_packet_type");
APP_RADIO_RETURN_ERR_CTX(calibrate_image(RADIOLIB_SX126X_CAL_IMG_430_MHZ_1,
RADIOLIB_SX126X_CAL_IMG_430_MHZ_2),
"gfsk_init::calibrate_image");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_modulation_params(params.mod_params), APP_RADIO_RETURN_ERR_CTX(set_gfsk_modulation_params(params.mod_params),
"gfsk_init::set_modulation_params"); "gfsk_init::set_modulation_params");
APP_RADIO_RETURN_ERR_CTX( APP_RADIO_RETURN_ERR_CTX(