diff --git a/examples/LR11x0/LR11x0_GNSS_Almanac_Update/LR11x0_GNSS_Almanac_Update.ino b/examples/LR11x0/LR11x0_GNSS_Almanac_Update/LR11x0_GNSS_Almanac_Update.ino new file mode 100644 index 00000000..964e865f --- /dev/null +++ b/examples/LR11x0/LR11x0_GNSS_Almanac_Update/LR11x0_GNSS_Almanac_Update.ino @@ -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 + +// 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); + +} diff --git a/examples/LR11x0/LR11x0_GNSS_Autonomous_Position/LR11x0_GNSS_Autonomous_Position.ino b/examples/LR11x0/LR11x0_GNSS_Autonomous_Position/LR11x0_GNSS_Autonomous_Position.ino new file mode 100644 index 00000000..84a40aa0 --- /dev/null +++ b/examples/LR11x0/LR11x0_GNSS_Autonomous_Position/LR11x0_GNSS_Autonomous_Position.ino @@ -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 + +// 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); +} diff --git a/examples/LR11x0/LR11x0_GNSS_Satellites/LR11x0_GNSS_Satellites.ino b/examples/LR11x0/LR11x0_GNSS_Satellites/LR11x0_GNSS_Satellites.ino new file mode 100644 index 00000000..4104a23f --- /dev/null +++ b/examples/LR11x0/LR11x0_GNSS_Satellites/LR11x0_GNSS_Satellites.ino @@ -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 + +// 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); +} diff --git a/keywords.txt b/keywords.txt index b22dbebd..c5fd90b5 100644 --- a/keywords.txt +++ b/keywords.txt @@ -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 diff --git a/src/Module.cpp b/src/Module.cpp index 7b78f3f1..30d38b03 100644 --- a/src/Module.cpp +++ b/src/Module.cpp @@ -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); + } } } } diff --git a/src/TypeDef.h b/src/TypeDef.h index 4c953866..a2525685 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -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)) + /*! \} */ diff --git a/src/modules/LR11x0/LR11x0.cpp b/src/modules/LR11x0/LR11x0.cpp index 70da0d35..ee2e4b1c 100644 --- a/src/modules/LR11x0/LR11x0.cpp +++ b/src/modules/LR11x0/LR11x0.cpp @@ -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); } diff --git a/src/modules/LR11x0/LR11x0.h b/src/modules/LR11x0/LR11x0.h index a52d64af..3f1939a5 100644 --- a/src/modules/LR11x0/LR11x0.h +++ b/src/modules/LR11x0/LR11x0.h @@ -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);