[LR11x0] GNSS support (#1275)

* [LR11x0] Added WIP GNSS control

* [LR11x0] Added almanac update

* [LR11x0] Added almanac update example

* [LR11x0] Add missing memory deallocation

* [LR11x0] Fix underflow in delay until subframe

* [LR11x0] Remove pin mapping from example

* [LR11x0] Finish rework of the GNSS API

* [LR11x0] Added position and satellite examples

* [LR11x0] Fix result member in example

* Added LR11x0 GNSS keywords

* [LR11x0] Fix typo in macro name

* [LR11x0] Print scan failed in example

* [LR11x0] Added GNSS abort
This commit is contained in:
Jan Gromeš 2024-10-18 16:50:36 +02:00 committed by GitHub
parent 710a1540b5
commit 00699ce225
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 925 additions and 16 deletions

View file

@ -0,0 +1,151 @@
/*
RadioLib LR11x0 GNSS Almanac Update Example
This example updates the LR11x0 GNSS almanac.
Almanac is a database of orbital predictions of
GNSS satellites, which allows the module to predict
when different satellites will appear in the sky,
and frequency of their signal.
Up-to-date almanac is necessary for operation!
After an update, data will remain valid for 30 days.
All GNSS examples require at least limited
visibility of the sky!
NOTE: This example will only work for LR11x0 devices
with sufficiently recent firmware!
LR1110: 4.1
LR1120: 2.1
If your device firmware reports older firmware,
update it using the LR11x0_Firmware_Update example.
For full API reference, see the GitHub Pages
https://jgromes.github.io/RadioLib/
*/
// include the library
#include <RadioLib.h>
// LR1110 has the following connections:
// NSS pin: 10
// DIO1 pin: 2
// NRST pin: 3
// BUSY pin: 9
LR1110 radio = new Module(10, 2, 3, 9);
// structure to save information about the GNSS almanac
LR11x0GnssAlmanacStatus_t almStatus;
void setup() {
Serial.begin(9600);
// initialize LR1110 with default settings
Serial.print(F("[LR1110] Initializing ... "));
int state = radio.beginGNSS(RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS);
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true) { delay(10); }
}
// check the firmware version
Serial.print(F("[LR1110] Checking firmware version ... "));
state = radio.isGnssScanCapable();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("check passed!"));
} else {
Serial.println(F("check failed, firmware update needed."));
while (true) { delay(10); }
}
// run GNSS scans until we get at least the time
// NOTE: Depending on visibility of satellites,
// this may take multiple attempts!
while(true) {
// run GNSS scan
Serial.print(F("[LR1110] Running GNSS scan ... "));
state = radio.gnssScan(NULL);
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true) { delay(10); }
}
// check almanac status
Serial.print(F("[LR1110] Checking GNSS almanac ... "));
state = radio.getGnssAlmanacStatus(&almStatus);
if (state != RADIOLIB_ERR_NONE) {
Serial.print(F("failed, code "));
Serial.println(state);
while (true) { delay(10); }
}
// we have the status, check if we have demodulated time
if(almStatus.gps.status < RADIOLIB_LR11X0_GNSS_ALMANAC_STATUS_UP_TO_DATE) {
Serial.println(F("time unknown, another scan needed."));
} else if(almStatus.gps.numUpdateNeeded > 0) {
Serial.print(almStatus.gps.numUpdateNeeded);
Serial.println(F(" satellites out-of-date."));
break;
} else {
Serial.println(F("no update needed!"));
while (true) { delay(10); }
}
}
}
void loop() {
// wait until almanac data is available in the signal
// multiple attempts are needed for this
Serial.print(F("[LR1110] Waiting for subframe ... "));
int state = radio.gnssDelayUntilSubframe(&almStatus, RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS);
if(state == RADIOLIB_ERR_GNSS_SUBFRAME_NOT_AVAILABLE) {
Serial.println(F("not enough time left."));
// wait until the next update window
delay(2000);
} else {
Serial.println(F("done!"));
// we have enough time to start the update
Serial.print(F("[LR1110] Starting update ... "));
state = radio.updateGnssAlmanac(RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS);
if(state != RADIOLIB_ERR_NONE) {
Serial.print(F("failed, code "));
Serial.println(state);
} else {
Serial.println(F("done!"));
}
}
// check whether another update is needed
Serial.print(F("[LR1110] Checking GNSS almanac ... "));
state = radio.getGnssAlmanacStatus(&almStatus);
if(state != RADIOLIB_ERR_NONE) {
Serial.print(F("failed, code "));
Serial.println(state);
while (true) { delay(10); }
}
// check if we have completed the update
if(almStatus.gps.numUpdateNeeded == 0) {
Serial.println(F("all satellites up-to-date!"));
while (true) { delay(10); }
} else {
Serial.print(almStatus.gps.numUpdateNeeded);
Serial.println(F(" satellites out-of-date."));
}
// wait a bit before the next update attempt
delay(1000);
}

View file

@ -0,0 +1,103 @@
/*
RadioLib LR11x0 GNSS Autonomous Position Example
This example performs GNSS scans and calculates
position of the device using autonomous mode.
In this mode, scan data does not need to be uploaded
to LoRaCloud, however, it requires up-to-date almanac
data. Run the LR11x0_Almanac_Update example to update
the device almanac.
NOTE: This example will only work for LR11x0 devices
with sufficiently recent firmware!
LR1110: 4.1
LR1120: 2.1
If your device firmware reports older firmware,
update it using the LR11x0_Firmware_Update example.
For full API reference, see the GitHub Pages
https://jgromes.github.io/RadioLib/
*/
// include the library
#include <RadioLib.h>
// LR1110 has the following connections:
// NSS pin: 10
// DIO1 pin: 2
// NRST pin: 3
// BUSY pin: 9
LR1110 radio = new Module(10, 2, 3, 9);
// structure to save information about the GNSS scan result
LR11x0GnssResult_t gnssResult;
// structure to save information about the calculated GNSS position
LR11x0GnssPosition_t gnssPosition;
void setup() {
Serial.begin(9600);
// initialize LR1110 with default settings
Serial.print(F("[LR1110] Initializing ... "));
int state = radio.beginGNSS(RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS);
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true) { delay(10); }
}
// check the firmware version
Serial.print(F("[LR1110] Checking firmware version ... "));
state = radio.isGnssScanCapable();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("check passed!"));
} else {
Serial.println(F("check failed, firmware update needed."));
while (true) { delay(10); }
}
Serial.println(F("Scan result\t| Latitude\t| Longitude\t| Accuracy\t| Number of satellites"));
}
void loop() {
// run GNSS scan
int state = radio.gnssScan(&gnssResult);
if(state == RADIOLIB_ERR_NONE) {
// success!
Serial.print(gnssResult.demodStat); Serial.print("\t\t| ");
// get the actual data
state = radio.getGnssPosition(&gnssPosition);
if(state == RADIOLIB_ERR_NONE) {
// print the position
Serial.print(gnssPosition.latitude, 6);
Serial.print("\t| ");
Serial.print(gnssPosition.longitude, 6);
Serial.print("\t| ");
Serial.print(gnssPosition.accuracy);
Serial.print("\t\t| ");
Serial.println(gnssPosition.numSatsUsed);
} else {
Serial.print(F("Failed to read result, code "));
Serial.print(state);
Serial.print(F(" (solver error "));
Serial.print(RADIOLIB_GET_GNSS_SOLVER_ERROR(state));
Serial.println(F(")"));
}
} else {
Serial.print(F("Scan failed, code "));
Serial.print(state);
Serial.print(F(" (demodulator error "));
Serial.print(RADIOLIB_GET_GNSS_DEMOD_ERROR(state));
Serial.println(F(")"));
}
// wait a bit before the next scan
delay(1000);
}

View file

@ -0,0 +1,102 @@
/*
RadioLib LR11x0 GNSS Satellites Example
This example performs GNSS scans and shows the satellites
currently in view. It is mostly useful to verify
visibility and antenna setup.
NOTE: This example will only work for LR11x0 devices
with sufficiently recent firmware!
LR1110: 4.1
LR1120: 2.1
If your device firmware reports older firmware,
update it using the LR11x0_Firmware_Update example.
For default module settings, see the wiki page
https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---wifi-scan
For full API reference, see the GitHub Pages
https://jgromes.github.io/RadioLib/
*/
// include the library
#include <RadioLib.h>
// LR1110 has the following connections:
// NSS pin: 10
// DIO1 pin: 2
// NRST pin: 3
// BUSY pin: 9
LR1110 radio = new Module(10, 2, 3, 9);
// structure to save information about the GNSS scan result
LR11x0GnssResult_t gnssResult;
void setup() {
Serial.begin(9600);
// initialize LR1110 with default settings
Serial.print(F("[LR1110] Initializing ... "));
int state = radio.beginGNSS(RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS);
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true) { delay(10); }
}
// check the firmware version
Serial.print(F("[LR1110] Checking firmware version ... "));
state = radio.isGnssScanCapable();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("check passed!"));
} else {
Serial.println(F("check failed, firmware update needed."));
while (true) { delay(10); }
}
}
void loop() {
Serial.print(F("[LR1110] Running GNSS scan ... "));
int state = radio.gnssScan(&gnssResult);
if(state != RADIOLIB_ERR_NONE) {
// some error occurred
Serial.print(F("failed, code "));
Serial.print(state);
Serial.print(F(" (demodulator error "));
Serial.print(RADIOLIB_GET_GNSS_DEMOD_ERROR(state));
Serial.println(F(")"));
} else {
Serial.println(F("success!"));
// print the table header
Serial.print(F("[LR1110] Detected "));
Serial.print(gnssResult.numSatsDet);
Serial.println(F(" satellite(s):"));
Serial.println(F(" # | ID | C/N0 [dB]\t| Doppler [Hz]"));
// read all results at once
LR11x0GnssSatellite_t satellites[32];
state = radio.getGnssSatellites(satellites, gnssResult.numSatsDet);
if(state != RADIOLIB_ERR_NONE) {
Serial.print(F("Failed to read results, code "));
Serial.println(state);
} else {
// print all the results
for(int i = 0; i < gnssResult.numSatsDet; i++) {
if(i < 10) { Serial.print(" "); } Serial.print(i); Serial.print(" | ");
Serial.print(satellites[i].svId); Serial.print(" | ");
Serial.print(satellites[i].c_n0); Serial.print("\t\t| ");
Serial.println(satellites[i].doppler);
}
}
}
// wait for a second before scanning again
delay(1000);
}

View file

@ -100,6 +100,11 @@ LR11x0WifiResult_t KEYWORD1
LR11x0WifiResultFull_t KEYWORD1
LR11x0WifiResultExtended_t KEYWORD1
LR11x0VersionInfo_t KEYWORD1
LR11x0GnssResult_t KEYWORD1
LR11x0GnssPosition_t KEYWORD1
LR11x0GnssSatellite_t KEYWORD1
LR11x0GnssAlmanacStatusPart_t KEYWORD1
LR11x0GnssAlmanacStatus_t KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -261,6 +266,14 @@ setWiFiScanAction KEYWORD2
clearWiFiScanAction KEYWORD2
getVersionInfo KEYWORD2
updateFirmware KEYWORD2
beginGNSS KEYWORD2
isGnssScanCapable KEYWORD2
gnssScan KEYWORD2
getGnssAlmanacStatus KEYWORD2
gnssDelayUntilSubframe KEYWORD2
updateGnssAlmanac KEYWORD2
getGnssPosition KEYWORD2
getGnssSatellites KEYWORD2
# RTTY
idle KEYWORD2
@ -473,6 +486,12 @@ RADIOLIB_ERR_SESSION_DISCARDED LITERAL1
RADIOLIB_ERR_INVALID_MODE LITERAL1
RADIOLIB_ERR_INVALID_WIFI_TYPE LITERAL1
RADIOLIB_ERR_GNSS_SUBFRAME_NOT_AVAILABLE LITERAL1
RADIOLIB_GET_GNSS_DEMOD_ERROR LITERAL1
RADIOLIB_GET_GNSS_SOLVER_ERROR LITERAL1
RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS LITERAL1
RADIOLIB_LR11X0_GNSS_CONSTELLATION_BEIDOU LITERAL1
RADIOLIB_LR11X0_GNSS_ALMANAC_STATUS_UP_TO_DATE LITERAL1
RADIOLIB_LR1110_FIRMWARE_IN_RAM LITERAL1
RADIOLIB_LR11X0_FIRMWARE_IMAGE_SIZE LITERAL1

View file

@ -339,18 +339,20 @@ int16_t Module::SPItransferStream(const uint8_t* cmd, uint8_t cmdLen, bool write
}
// ensure GPIO is low
if(this->gpioPin == RADIOLIB_NC) {
this->hal->delay(50);
} else {
RadioLibTime_t start = this->hal->millis();
while(this->hal->digitalRead(this->gpioPin)) {
this->hal->yield();
if(this->hal->millis() - start >= this->spiConfig.timeout) {
RADIOLIB_DEBUG_BASIC_PRINTLN("GPIO pre-transfer timeout, is it connected?");
#if !RADIOLIB_STATIC_ONLY
delete[] buffOut;
#endif
return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
if(waitForGpio) {
if(this->gpioPin == RADIOLIB_NC) {
this->hal->delay(50);
} else {
RadioLibTime_t start = this->hal->millis();
while(this->hal->digitalRead(this->gpioPin)) {
this->hal->yield();
if(this->hal->millis() - start >= this->spiConfig.timeout) {
RADIOLIB_DEBUG_BASIC_PRINTLN("GPIO pre-transfer timeout, is it connected?");
#if !RADIOLIB_STATIC_ONLY
delete[] buffOut;
#endif
return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
}
}
}
}

View file

@ -605,6 +605,27 @@
*/
#define RADIOLIB_ERR_INVALID_WIFI_TYPE (-1200)
/*!
\brief GNSS subframe not available in the next 2.3 seconds.
*/
#define RADIOLIB_ERR_GNSS_SUBFRAME_NOT_AVAILABLE (-1201)
/*!
\brief Offset of GNSS demodulator errors.
See LR11x0 datasheet for details on the actual demodulator error
*/
#define RADIOLIB_ERR_GNSS_DEMOD_OFFSET (-1210)
#define RADIOLIB_ERR_GNSS_DEMOD(X) (RADIOLIB_ERR_GNSS_DEMOD_OFFSET + (X))
#define RADIOLIB_GET_GNSS_DEMOD_ERROR(X) ((X) - RADIOLIB_ERR_GNSS_DEMOD_OFFSET)
/*!
\brief GNSS solver errors.
See LR11x0 datasheet for details on the actual solver error
*/
#define RADIOLIB_ERR_GNSS_SOLVER_OFFSET (-1230)
#define RADIOLIB_ERR_GNSS_SOLVER(X) (RADIOLIB_ERR_GNSS_SOLVER_OFFSET - (X))
#define RADIOLIB_GET_GNSS_SOLVER_ERROR(X) (-((X) - RADIOLIB_ERR_GNSS_SOLVER_OFFSET))
/*!
\}
*/

View file

@ -120,6 +120,34 @@ int16_t LR11x0::beginLRFHSS(uint8_t bw, uint8_t cr, bool narrowGrid, float tcxoV
return(setModulationParamsLrFhss(RADIOLIB_LR11X0_LR_FHSS_BIT_RATE_RAW, RADIOLIB_LR11X0_LR_FHSS_SHAPING_GAUSSIAN_BT_1_0));
}
int16_t LR11x0::beginGNSS(uint8_t constellations, float tcxoVoltage) {
// set module properties and perform initial setup - packet type does not matter
int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LORA);
RADIOLIB_ASSERT(state);
state = this->clearErrors();
RADIOLIB_ASSERT(state);
state = this->configLfClock(RADIOLIB_LR11X0_LF_BUSY_RELEASE_DISABLED | RADIOLIB_LR11X0_LF_CLK_XOSC);
RADIOLIB_ASSERT(state);
uint16_t errs = 0;
state = this->getErrors(&errs);
RADIOLIB_ASSERT(state);
if(errs & 0x40) {
RADIOLIB_DEBUG_BASIC_PRINTLN("LF_XOSC_START_ERR");
return(RADIOLIB_ERR_SPI_CMD_FAILED);
}
state = this->gnssSetConstellationToUse(constellations);
RADIOLIB_ASSERT(state);
state = setRegulatorLDO();
RADIOLIB_ASSERT(state);
return(RADIOLIB_ERR_NONE);
}
int16_t LR11x0::reset() {
// run the reset sequence
this->mod->hal->pinMode(this->mod->getRst(), this->mod->hal->GpioModeOutput);
@ -1723,6 +1751,260 @@ int16_t LR11x0::updateFirmware(const uint32_t* image, size_t size, bool nonvolat
return(state);
}
int16_t LR11x0::isGnssScanCapable() {
// get the version
LR11x0VersionInfo_t version;
int16_t state = this->getVersionInfo(&version);
RADIOLIB_ASSERT(state);
// check the device firmware version is sufficient
uint16_t versionFull = ((uint16_t)version.fwMajor << 8) | (uint16_t)version.fwMinor;
state = RADIOLIB_ERR_UNSUPPORTED;
if((version.device == RADIOLIB_LR11X0_DEVICE_LR1110) && (versionFull >= 0x0401)) {
state = RADIOLIB_ERR_NONE;
} else if((version.device == RADIOLIB_LR11X0_DEVICE_LR1120) && (versionFull >= 0x0201)) {
state = RADIOLIB_ERR_NONE;
}
RADIOLIB_ASSERT(state);
// in debug mode, dump the almanac
#if RADIOLIB_DEBUG_PROTOCOL
uint32_t addr = 0;
uint16_t sz = 0;
state = this->gnssAlmanacReadAddrSize(&addr, &sz);
RADIOLIB_ASSERT(state);
RADIOLIB_DEBUG_BASIC_PRINTLN("Almanac@%08x, %d bytes", addr, sz);
uint32_t buff[32] = { 0 };
while(sz > 0) {
size_t len = sz > 32 ? 32 : sz/sizeof(uint32_t);
state = this->readRegMem32(addr, buff, len);
RADIOLIB_ASSERT(state);
Module::hexdump(NULL, (uint8_t*)buff, len*sizeof(uint32_t), addr);
addr += len*sizeof(uint32_t);
sz -= len*sizeof(uint32_t);
}
uint8_t almanac[22] = { 0 };
for(uint8_t i = 0; i < 128; i++) {
RADIOLIB_DEBUG_BASIC_PRINTLN("Almanac[%d]:", i);
state = this->gnssAlmanacReadSV(i, almanac);
RADIOLIB_ASSERT(state);
Module::hexdump(NULL, almanac, 22);
}
#endif
return(state);
}
int16_t LR11x0::gnssScan(LR11x0GnssResult_t* res) {
if(!res) {
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
}
// go to standby
int16_t state = standby();
RADIOLIB_ASSERT(state);
// set DIO mapping
state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_GNSS_DONE | RADIOLIB_LR11X0_IRQ_GNSS_ABORT);
RADIOLIB_ASSERT(state);
// set scan mode (single vs multiple)
state = this->gnssSetMode(0x03);
RADIOLIB_ASSERT(state);
// set RF switch
this->mod->setRfSwitchState(LR11x0::MODE_GNSS);
// start scan with high effort
RADIOLIB_DEBUG_BASIC_PRINTLN("GNSS scan start");
state = this->gnssPerformScan(RADIOLIB_LR11X0_GNSS_EFFORT_MID, 0x3C, 16);
RADIOLIB_ASSERT(state);
// wait for scan finished or timeout
// this can take very long if both GPS and BeiDou are enabled
RadioLibTime_t softTimeout = 300UL * 1000UL;
RadioLibTime_t start = this->mod->hal->millis();
while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
this->mod->hal->yield();
if(this->mod->hal->millis() - start > softTimeout) {
this->gnssAbort();
RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout waiting for IRQ");
}
}
// restore the switch
this->mod->setRfSwitchState(Module::MODE_IDLE);
RADIOLIB_DEBUG_BASIC_PRINTLN("GNSS scan done in %lu ms", (long unsigned int)(this->mod->hal->millis() - start));
// distinguish between GNSS-done and GNSS-abort outcomes and clear the flags
uint32_t irq = this->getIrqStatus();
this->clearIrq(RADIOLIB_LR11X0_IRQ_ALL);
if(irq & RADIOLIB_LR11X0_IRQ_GNSS_ABORT) {
return(RADIOLIB_ERR_RX_TIMEOUT);
}
// retrieve the demodulator status
uint8_t info = 0;
state = this->gnssReadDemodStatus(&res->demodStat, &info);
RADIOLIB_ASSERT(state);
RADIOLIB_DEBUG_BASIC_PRINTLN("Demod status %d, info %02x", (int)res->demodStat, (unsigned int)info);
// retrieve the number of detected satellites
state = this->gnssGetNbSvDetected(&res->numSatsDet);
RADIOLIB_ASSERT(state);
// retrieve the result size
state = this->gnssGetResultSize(&res->resSize);
RADIOLIB_ASSERT(state);
// check and return demodulator status
if(res->demodStat < RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_TOW_FOUND) {
return(RADIOLIB_ERR_GNSS_DEMOD(res->demodStat));
}
return(state);
}
int16_t LR11x0::getGnssAlmanacStatus(LR11x0GnssAlmanacStatus_t *stat) {
if(!stat) {
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
}
// save the time the time until subframe is relative to
stat->start = this->mod->hal->millis();
// get the raw data
uint8_t raw[53] = { 0 };
int16_t state = this->gnssReadAlmanacStatus(raw);
RADIOLIB_ASSERT(state);
// parse the reply
stat->gps.status = (int8_t)raw[0];
stat->gps.timeUntilSubframe = ((uint32_t)(raw[1]) << 24) | ((uint32_t)(raw[2]) << 16) | ((uint32_t)(raw[3]) << 8) | (uint32_t)raw[4];
stat->gps.numSubframes = raw[5];
stat->gps.nextSubframe4SvId = raw[6];
stat->gps.nextSubframe5SvId = raw[7];
stat->gps.nextSubframeStart = raw[8];
stat->gps.numUpdateNeeded = raw[9];
stat->gps.flagsUpdateNeeded[0] = ((uint32_t)(raw[10]) << 24) | ((uint32_t)(raw[11]) << 16) | ((uint32_t)(raw[12]) << 8) | (uint32_t)raw[13];
stat->gps.flagsActive[0] = ((uint32_t)(raw[14]) << 24) | ((uint32_t)(raw[15]) << 16) | ((uint32_t)(raw[16]) << 8) | (uint32_t)raw[17];
stat->beidou.status = (int8_t)raw[18];
stat->beidou.timeUntilSubframe = ((uint32_t)(raw[19]) << 24) | ((uint32_t)(raw[20]) << 16) | ((uint32_t)(raw[21]) << 8) | (uint32_t)raw[22];
stat->beidou.numSubframes = raw[23];
stat->beidou.nextSubframe4SvId = raw[24];
stat->beidou.nextSubframe5SvId = raw[25];
stat->beidou.nextSubframeStart = raw[26];
stat->beidou.numUpdateNeeded = raw[27];
stat->beidou.flagsUpdateNeeded[0] = ((uint32_t)(raw[28]) << 24) | ((uint32_t)(raw[29]) << 16) | ((uint32_t)(raw[30]) << 8) | (uint32_t)raw[31];
stat->beidou.flagsUpdateNeeded[1] = ((uint32_t)(raw[32]) << 24) | ((uint32_t)(raw[33]) << 16) | ((uint32_t)(raw[34]) << 8) | (uint32_t)raw[35];
stat->beidou.flagsActive[0] = ((uint32_t)(raw[36]) << 24) | ((uint32_t)(raw[37]) << 16) | ((uint32_t)(raw[38]) << 8) | (uint32_t)raw[39];
stat->beidou.flagsActive[1] = ((uint32_t)(raw[40]) << 24) | ((uint32_t)(raw[41]) << 16) | ((uint32_t)(raw[42]) << 8) | (uint32_t)raw[43];
stat->beidouSvNoAlmanacFlags[0] = ((uint32_t)(raw[44]) << 24) | ((uint32_t)(raw[45]) << 16) | ((uint32_t)(raw[46]) << 8) | (uint32_t)raw[47];
stat->beidouSvNoAlmanacFlags[1] = ((uint32_t)(raw[18]) << 24) | ((uint32_t)(raw[49]) << 16) | ((uint32_t)(raw[50]) << 8) | (uint32_t)raw[51];
stat->nextAlmanacId = raw[52];
return(state);
}
int16_t LR11x0::gnssDelayUntilSubframe(LR11x0GnssAlmanacStatus_t *stat, uint8_t constellation) {
if(!stat) {
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
}
// almanac update has to be called at least 1.3 seconds before the subframe
// we use 2.3 seconds to be on the safe side
// calculate absolute times
RadioLibTime_t window = stat->start + stat->gps.timeUntilSubframe - 2300;
if(constellation == RADIOLIB_LR11X0_GNSS_CONSTELLATION_BEIDOU) {
window = stat->start + stat->beidou.timeUntilSubframe - 2300;
}
RadioLibTime_t now = this->mod->hal->millis();
if(now > window) {
// we missed the window
return(RADIOLIB_ERR_GNSS_SUBFRAME_NOT_AVAILABLE);
}
RadioLibTime_t delay = window - now;
RADIOLIB_DEBUG_BASIC_PRINTLN("Time until subframe %lu ms", delay);
this->mod->hal->delay(delay);
return(RADIOLIB_ERR_NONE);
}
// TODO fix last satellite always out of date
int16_t LR11x0::updateGnssAlmanac(uint8_t constellation) {
int16_t state = this->setDioIrqParams(RADIOLIB_LR11X0_IRQ_GNSS_DONE | RADIOLIB_LR11X0_IRQ_GNSS_ABORT);
RADIOLIB_ASSERT(state);
state = this->gnssAlmanacUpdateFromSat(RADIOLIB_LR11X0_GNSS_EFFORT_MID, constellation);
RADIOLIB_ASSERT(state);
// wait for scan finished or timeout, assumes 2 subframes and up to 2.3s pre-roll
uint32_t softTimeout = 16UL * 1000UL;
uint32_t start = this->mod->hal->millis();
while (!this->mod->hal->digitalRead(this->mod->getIrq())) {
this->mod->hal->yield();
if(this->mod->hal->millis() - start > softTimeout) {
this->gnssAbort();
RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout waiting for almanac update");
}
}
RADIOLIB_DEBUG_BASIC_PRINTLN("GPS almanac update done in %lu ms", (long unsigned int)(this->mod->hal->millis() - start));
// distinguish between GNSS-done and GNSS-abort outcomes and clear the flags
uint32_t irq = this->getIrqStatus();
this->clearIrq(RADIOLIB_LR11X0_IRQ_ALL);
if(irq & RADIOLIB_LR11X0_IRQ_GNSS_ABORT) {
state = RADIOLIB_ERR_RX_TIMEOUT;
}
return(state);
}
int16_t LR11x0::getGnssPosition(LR11x0GnssPosition_t* pos, bool filtered) {
if(!pos) {
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
}
uint8_t error = 0;
int16_t state;
if(filtered) {
state = this->gnssReadDopplerSolverRes(&error, &pos->numSatsUsed, NULL, NULL, NULL, NULL, &pos->latitude, &pos->longitude, &pos->accuracy, NULL);
} else {
state = this->gnssReadDopplerSolverRes(&error, &pos->numSatsUsed, &pos->latitude, &pos->longitude, &pos->accuracy, NULL, NULL, NULL, NULL, NULL);
}
RADIOLIB_ASSERT(state);
// check the solver error
if(error != 0) {
return(RADIOLIB_ERR_GNSS_SOLVER(error));
}
return(state);
}
int16_t LR11x0::getGnssSatellites(LR11x0GnssSatellite_t* sats, uint8_t numSats) {
if((!sats) || (numSats >= 32)) {
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
}
uint8_t svId[32] = { 0 };
uint8_t snr[32] = { 0 };
int16_t doppler[32] = { 0 };
int16_t state = this->gnssGetSvDetected(svId, snr, doppler, numSats);
RADIOLIB_ASSERT(state);
for(size_t i = 0; i < numSats; i++) {
sats[i].svId = svId[i];
sats[i].c_n0 = snr[i] + 31;
sats[i].doppler = doppler[i];
}
return(state);
}
int16_t LR11x0::modSetup(float tcxoVoltage, uint8_t modem) {
this->mod->init();
this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput);
@ -1816,7 +2098,9 @@ bool LR11x0::findChip(uint8_t ver) {
// read the version
LR11x0VersionInfo_t info;
int16_t state = getVersionInfo(&info);
if((state == RADIOLIB_ERR_NONE) && (info.device == ver)) {
RADIOLIB_ASSERT(state);
if(info.device == ver) {
RADIOLIB_DEBUG_BASIC_PRINTLN("Found LR11x0: RADIOLIB_LR11X0_CMD_GET_VERSION = 0x%02x", info.device);
RADIOLIB_DEBUG_BASIC_PRINTLN("Base FW version: %d.%d", (int)info.fwMajor, (int)info.fwMinor);
if(this->chipType != RADIOLIB_LR11X0_DEVICE_LR1121) {
@ -2122,7 +2406,7 @@ int16_t LR11x0::setDioIrqParams(uint32_t irq1, uint32_t irq2) {
}
int16_t LR11x0::setDioIrqParams(uint32_t irq) {
return(setDioIrqParams(irq, irq));
return(setDioIrqParams(irq, 0));
}
int16_t LR11x0::clearIrq(uint32_t irq) {
@ -3314,6 +3598,16 @@ int16_t LR11x0::gnssWriteBitMaskSatActivated(uint8_t bitMask, uint32_t* bitMaskA
return(state);
}
void LR11x0::gnssAbort() {
// send the abort signal (single NOP)
this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8;
this->mod->SPIwriteStream(RADIOLIB_LR11X0_CMD_NOP, NULL, 0, false, false);
this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16;
// wait for at least 2.9 seconds as specified by the user manual
this->mod->hal->delay(3000);
}
int16_t LR11x0::cryptoSetKey(uint8_t keyId, uint8_t* key) {
if(!key) {
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
@ -3374,11 +3668,17 @@ int16_t LR11x0::cryptoProcessJoinAccept(uint8_t decKeyId, uint8_t verKeyId, uint
// check the crypto engine state
if(rplBuff[0] != RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS) {
RADIOLIB_DEBUG_BASIC_PRINTLN("Crypto Engine error: %02x", rplBuff[0]);
#if !RADIOLIB_STATIC_ONLY
delete[] rplBuff;
#endif
return(RADIOLIB_ERR_SPI_CMD_FAILED);
}
// pass the data
memcpy(dataOut, &rplBuff[1], len);
#if !RADIOLIB_STATIC_ONLY
delete[] rplBuff;
#endif
return(state);
}
@ -3609,6 +3909,9 @@ int16_t LR11x0::cryptoCommon(uint16_t cmd, uint8_t keyId, uint8_t* dataIn, size_
// pass the data
memcpy(dataOut, &rplBuff[1], len);
#if !RADIOLIB_STATIC_ONLY
delete[] rplBuff;
#endif
return(state);
}

View file

@ -274,7 +274,8 @@
#define RADIOLIB_LR11X0_IRQ_FSK_LEN_ERROR (0x01UL << 24) // 31 0 FSK packet received with length error
#define RADIOLIB_LR11X0_IRQ_FSK_ADDR_ERROR (0x01UL << 25) // 31 0 FSK packet received with address error
#define RADIOLIB_LR11X0_IRQ_LORA_RX_TIMESTAMP (0x01UL << 27) // 31 0 last LoRa symbol was received (timestamp source)
#define RADIOLIB_LR11X0_IRQ_ALL (0x0BF80FFCUL) // 31 0 all interrupts
#define RADIOLIB_LR11X0_IRQ_GNSS_ABORT (0x01UL << 28) // 31 0 GNSS scan aborted
#define RADIOLIB_LR11X0_IRQ_ALL (0x1BF80FFCUL) // 31 0 all interrupts
#define RADIOLIB_LR11X0_IRQ_NONE (0x00UL << 0) // 31 0 no interrupts
// RADIOLIB_LR11X0_CMD_CONFIG_LF_LOCK
@ -552,7 +553,7 @@
#define RADIOLIB_LR11X0_GNSS_CONTEXT_ERR_FLASH (0x03UL << 0) // 7 4 flash integrity error
#define RADIOLIB_LR11X0_GNSS_CONTEXT_ERR_ALMANAC_UPD (0x04UL << 0) // 7 4 almanac update not allowed
#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_250_HZ (0x00UL << 0) // 8 7 frequency search space: 250 Hz
#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_500_HZ (0x01UL << 0) // 8 7 500 H
#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_500_HZ (0x01UL << 0) // 8 7 500 Hz
#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_1000_HZ (0x02UL << 0) // 8 7 1000 Hz
#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_2000_HZ (0x03UL << 0) // 8 7 2000 Hz
@ -564,6 +565,65 @@
#define RADIOLIB_LR11X0_GNSS_ALMANAC_HEADER_ID (0x80UL << 0) // 7 0 starting byte of GNSS almanac header
#define RADIOLIB_LR11X0_GNSS_ALMANAC_BLOCK_SIZE (20)
// RADIOLIB_LR11X0_CMD_GNSS_FETCH_TIME
#define RADIOLIB_LR11X0_GNSS_EFFORT_LOW (0x00UL << 0) // 7 0 GNSS effort mode: low sensitivity
#define RADIOLIB_LR11X0_GNSS_EFFORT_MID (0x01UL << 0) // 7 0 medium sensitivity
#define RADIOLIB_LR11X0_GNSS_FETCH_TIME_OPT_TOW (0x00UL << 0) // 7 0 time fetch options: ToW only, requires WN to demodulated beforehand
#define RADIOLIB_LR11X0_GNSS_FETCH_TIME_OPT_TOW_WN (0x01UL << 0) // 7 0 ToW and WN
#define RADIOLIB_LR11X0_GNSS_FETCH_TIME_OPT_TOW_WN_ROLL (0x02UL << 0) // 7 0 ToW, WN and rollover
// RADIOLIB_LR11X0_CMD_GNSS_READ_DEMOD_STATUS
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_NOT_POSSIBLE (-21) // 7 0 GNSS demodulation status: not possible to demodulate
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_SAT_LOST (-20) // 7 0 satellite lost
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_ALMANAC_DEMOD_ERROR (-19) // 7 0 almanac demodulation error
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_TOO_LATE (-18) // 7 0 woke up after preamble (demodulation started too late)
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_20_MS_FAIL (-17) // 7 0 20ms real-time clock failed
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_WAKE_UP_FAIL (-16) // 7 0 wake up sync failed
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_WN_INVALID (-15) // 7 0 week number not validated
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_NO_ACTIVE_SAT (-14) // 7 0 no active satellite selected in satellite list
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_SLEEP_TOO_LONG (-13) // 7 0 sleep time too long
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_TOW_INVALID (-12) // 7 0 wrong time-of-week demodulated
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_PREAMBLE_INVALID (-11) // 7 0 preamble not validated
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_DISABLED (-10) // 7 0 demodulator disabled
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_EXTR_FAILED (-9) // 7 0 demodulator extraction failed
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_NO_BIT_CHANGE (-8) // 7 0 no bit change found during demodulation start
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_NO_BIT_CHANGE_ADV (-7) // 7 0 no bit change found during advanced scan
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_NO_SAT_FOUND (-6) // 7 0 no satellites found
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_SYNC_LOST (-5) // 7 0 word sync lost
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_PARITY_NOT_ENOUGH (-3) // 7 0 parity check fail (not enough)
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_PARITY_TOO_MANY (-2) // 7 0 parity check fail (too many)
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_NO_PARITY (-1) // 7 0 parity check fail (no parity found)
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_WORD_SYNC_NONE (0) // 7 0 word sync search not started
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_WORD_SYNC_POT (1) // 7 0 potential word sync found
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_WORD_SYNC_OK (2) // 7 0 word sync found
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_TOW_FOUND (3) // 7 0 time-of-week found
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_WN_FOUND (4) // 7 0 week number and time-of-week found
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_ALM_FOUND_UNSAVED (5) // 7 0 almanac found but not saved
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_HALF_ALM_SAVED (6) // 7 0 half of almanac found and saved
#define RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_FULL_ALM_SAVED (7) // 7 0 full almanac found and saved
#define RADIOLIB_LR11X0_GNSS_DEMOD_INFO_WORD_SYNC_FOUND (0x01UL << 0) // 7 0 GNSS demodulation info: word synchronization found
#define RADIOLIB_LR11X0_GNSS_DEMOD_INFO_TOW_FOUND (0x01UL << 1) // 7 0 time-of-week found
#define RADIOLIB_LR11X0_GNSS_DEMOD_INFO_WN_DEMODED (0x01UL << 2) // 7 0 week number demodulated
#define RADIOLIB_LR11X0_GNSS_DEMOD_INFO_WN_FOUND (0x01UL << 3) // 7 0 week number found
#define RADIOLIB_LR11X0_GNSS_DEMOD_INFO_SUBFRAME_1_FOUND (0x01UL << 4) // 7 0 subframe 1 found
#define RADIOLIB_LR11X0_GNSS_DEMOD_INFO_SUBFRAME_4_FOUND (0x01UL << 5) // 7 0 subframe 4 found
#define RADIOLIB_LR11X0_GNSS_DEMOD_INFO_SUBFRAME_5_FOUND (0x01UL << 6) // 7 0 subframe 5 found
// RADIOLIB_LR11X0_CMD_GNSS_READ_ALMANAC_STATUS
#define RADIOLIB_LR11X0_GNSS_ALMANAC_STATUS_UP_TO_DATE (0) // 7 0 GPS/BeiDou almanac status: all satellites up-to-date
#define RADIOLIB_LR11X0_GNSS_ALMANAC_STATUS_OUTDATED (1) // 7 0 at least one satellite needs update
// RADIOLIB_LR11X0_CMD_GNSS_READ_DOPPLER_SOLVER_RES
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_NONE (0) // 7 0 internal 2D solver error: no error
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_RES_HIGH (1) // 7 0 residue too high
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_NOT_CONVERGED (2) // 7 0 not converged on solution
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_NOT_ENOUGH_SV (3) // 7 0 not enough satellites
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_ILL_MATRIX (4) // 7 0 matrix error (?)
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_TIME (5) // 7 0 time error
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_ALM_PART_OLD (6) // 7 0 part of almanac too old or not available
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_INCONSISTENT (7) // 7 0 not consistent with history (?)
#define RADIOLIB_LR11X0_GNSS_SOLVER_ERR_ALM_OLD (8) // 7 0 all of almanac too old
// RADIOLIB_LR11X0_CMD_CRYPTO_SET_KEY
#define RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS (0x00UL << 0) // 7 0 crypto engine status: success
#define RADIOLIB_LR11X0_CRYPTO_STATUS_FAIL_CMAC (0x01UL << 0) // 7 0 MIC check failed
@ -717,8 +777,90 @@ struct LR11x0VersionInfo_t {
uint8_t almanacGNSS;
};
/*!
\struct LR11x0GnssResult_t
\brief Structure to report information results of a GNSS scan.
*/
struct LR11x0GnssResult_t {
/*! \brief Demodulator status. One of RADIOLIB_LR11X0_GNSS_DEMOD_STATUS_* */
int8_t demodStat;
/*! \brief Number of satellites detected during the scan. */
uint8_t numSatsDet;
/*! \brief Result size, used when passing data to LoRa cloud. */
uint16_t resSize;
};
/*!
\struct LR11x0GnssPosition_t
\brief Structure to report position from LR11x0 internal solver.
*/
struct LR11x0GnssPosition_t {
/*! \brief Latitude in degrees. */
float latitude;
/*! \brief Longitude in degrees. */
float longitude;
/*! \brief Accuracy of this result. */
uint16_t accuracy;
/*! \brief Number of satellites used to solve this position. */
uint8_t numSatsUsed;
};
/*!
\struct LR11x0GnssSatellite_t
\brief Structure to save information about a satellite found during GNSS scan.
*/
struct LR11x0GnssSatellite_t {
/*! \brief Satellite vehicle (SV) identifier. */
uint8_t svId;
/*! \brief C/N0 in dB. */
uint8_t c_n0;
/*! \brief Doppler shift of the signal in Hz. */
int16_t doppler;
};
/*!
\struct LR11x0GnssAlmanacStatusPart_t
\brief Structure to save information about one constellation of the GNSS almanac.
*/
struct LR11x0GnssAlmanacStatusPart_t {
int8_t status;
uint32_t timeUntilSubframe;
uint8_t numSubframes;
uint8_t nextSubframe4SvId;
uint8_t nextSubframe5SvId;
uint8_t nextSubframeStart;
uint8_t numUpdateNeeded;
uint32_t flagsUpdateNeeded[2];
uint32_t flagsActive[2];
};
/*!
\struct LR11x0GnssAlmanacStatus_t
\brief Structure to save information about the GNSS almanac.
This is not the actual almanac, just some context information about it.
*/
struct LR11x0GnssAlmanacStatus_t {
/*! \brief GPS part of the almanac */
LR11x0GnssAlmanacStatusPart_t gps;
/*! \brief BeiDou part of the almanac */
LR11x0GnssAlmanacStatusPart_t beidou;
/*! \brief Extra flags present for BeiDou only */
uint32_t beidouSvNoAlmanacFlags[2];
/*! \brief Next almanac ID */
uint8_t nextAlmanacId;
/*! \brief Timestamp of when almanac status was retrieved - timeUntilSubframe is relative to this value. */
RadioLibTime_t start;
};
/*!
@ -802,6 +944,14 @@ class LR11x0: public PhysicalLayer {
*/
int16_t beginLRFHSS(uint8_t bw, uint8_t cr, bool narrowGrid, float tcxoVoltage);
/*!
\brief Initialization method for GNSS scanning.
\param constellations GNSS constellations to use (GPS, BeiDou or both). Defaults to both.
\param tcxoVoltage TCXO reference voltage to be set.
\returns \ref status_codes
*/
int16_t beginGNSS(uint8_t constellations = RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS | RADIOLIB_LR11X0_GNSS_CONSTELLATION_BEIDOU, float tcxoVoltage = 1.6);
/*!
\brief Reset method. Will reset the chip to the default state using RST pin.
\returns \ref status_codes
@ -1396,6 +1546,63 @@ class LR11x0: public PhysicalLayer {
*/
int16_t updateFirmware(const uint32_t* image, size_t size, bool nonvolatile = true);
/*!
\brief Method to check whether the device is capable of performing a GNSS scan.
\returns \ref status_codes
*/
int16_t isGnssScanCapable();
/*!
\brief Performs GNSS scan.
\param res Pointer to LR11x0GnssPosition_t structure to populate.
Will not be saved if set to NULL, defaults to NULL.
\returns \ref status_codes
*/
int16_t gnssScan(LR11x0GnssResult_t* res = NULL);
/*!
\brief Read information about the almanac.
\param stat Pointer to structure to save the almanac status into.
This is not the actual almanac, just a structure providing information about it.
\returns \ref status_codes
*/
int16_t getGnssAlmanacStatus(LR11x0GnssAlmanacStatus_t *stat);
/*!
\brief Blocking wait until the next subframe with almanac data is available.
Used to control timing during almanac update from satellite.
\param stat Pointer to structure containing the almanac status read by getGnssAlmanacStatus.
This is not the actual almanac, just a structure providing information about it.
\param constellation Constellation to wait for, one of RADIOLIB_LR11X0_GNSS_CONSTELLATION_*.
Constellations cannot be updated at the same time, but rather must be updated sequentially!
\returns \ref status_codes
*/
int16_t gnssDelayUntilSubframe(LR11x0GnssAlmanacStatus_t *stat, uint8_t constellation);
/*!
\brief Perform almanac update. Must be called immediately after gnssDelayUntilSubframe.
\param constellation Constellation to update, one of RADIOLIB_LR11X0_GNSS_CONSTELLATION_*.
Constellations cannot be updated at the same time, but rather must be updated sequentially!
\returns \ref status_codes
*/
int16_t updateGnssAlmanac(uint8_t constellation);
/*!
\brief Get GNSS position. Called after gnssScan to retrieve the position calculated by the internal solver.
\param pos Pointer to LR11x0GnssPosition_t structure to populate.
\param filtered Whether to save the filtered, or unfiltered values. Defaults to true (filtered).
\returns \ref status_codes
*/
int16_t getGnssPosition(LR11x0GnssPosition_t* pos, bool filtered = true);
/*!
\brief Get GNSS satellites found during the last scan.
\param sats Pointer to array of LR11x0GnssSatellite_t structures to populate.
\param numSats Number of satellites to read. Can be retrieved from LR11x0GnssResult_t passed to gnssScan.
\returns \ref status_codes
*/
int16_t getGnssSatellites(LR11x0GnssSatellite_t* sats, uint8_t numSats);
#if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL
protected:
#endif
@ -1546,6 +1753,7 @@ class LR11x0: public PhysicalLayer {
int16_t gnssReadWarmStartStatus(uint8_t bitMask, uint8_t* nbVisSat, uint32_t* timeElapsed);
int16_t gnssGetSvSync(uint8_t mask, uint8_t nbSv, uint8_t* syncList);
int16_t gnssWriteBitMaskSatActivated(uint8_t bitMask, uint32_t* bitMaskActivated0, uint32_t* bitMaskActivated1);
void gnssAbort();
int16_t cryptoSetKey(uint8_t keyId, uint8_t* key);
int16_t cryptoDeriveKey(uint8_t srcKeyId, uint8_t dstKeyId, uint8_t* key);