diff --git a/.github/workflows/compile-rtk-everywhere.yml b/.github/workflows/compile-rtk-everywhere.yml index e17b5047e..c056656e1 100644 --- a/.github/workflows/compile-rtk-everywhere.yml +++ b/.github/workflows/compile-rtk-everywhere.yml @@ -5,8 +5,8 @@ on: env: FILENAME_PREFIX: RTK_Everywhere_Firmware - FIRMWARE_VERSION_MAJOR: 2 - FIRMWARE_VERSION_MINOR: 3 + FIRMWARE_VERSION_MAJOR: 3 + FIRMWARE_VERSION_MINOR: 0 CORE_VERSION: 3.0.7 jobs: diff --git a/Firmware/RTK_Everywhere/AP-Config/index.html b/Firmware/RTK_Everywhere/AP-Config/index.html index 9e7c8c0a8..c16fc0aaa 100644 --- a/Firmware/RTK_Everywhere/AP-Config/index.html +++ b/Firmware/RTK_Everywhere/AP-Config/index.html @@ -297,17 +297,17 @@ -
+
-
diff --git a/Firmware/RTK_Everywhere/AP-Config/src/main.js b/Firmware/RTK_Everywhere/AP-Config/src/main.js index 20d0e4088..f373f1ce5 100644 --- a/Firmware/RTK_Everywhere/AP-Config/src/main.js +++ b/Firmware/RTK_Everywhere/AP-Config/src/main.js @@ -278,8 +278,8 @@ function parseIncoming(msg) { show("constellationNavic"); hide("dynamicModelDropdown"); //Not supported on LG290P - show("minElevConfig"); - show("minCNOConfig"); + hide("minElevConfig"); //Not supported on LG290P + hide("minCN0Config"); //Not supported on LG290P ge("rtcmRateInfoText").setAttribute('data-bs-original-title', 'RTCM is transmitted by the base at a default of 1Hz for messages 1005, 1074, 1084, 1094, 1114, 1124, 1134. This can be lowered for radios with low bandwidth or tailored to transmit any/all RTCM messages. Limits: 0 to 20. Note: The measurement rate is overridden to 1Hz when in Base mode.'); @@ -346,8 +346,8 @@ function parseIncoming(msg) { hide("enableNmeaOnRadio"); hide("dynamicModelDropdown"); //Not supported on LG290P - show("minElevConfig"); - show("minCNOConfig"); + hide("minElevConfig"); //Not supported on LG290P + hide("minCN0Config"); //Not supported on LG290P ge("rtcmRateInfoText").setAttribute('data-bs-original-title', 'RTCM is transmitted by the base at a default of 1Hz for messages 1005, 1074, 1084, 1094, 1124, and 0.1Hz for 1033. This can be lowered for radios with low bandwidth or tailored to transmit any/all RTCM messages. Limits: 0 to 20. Note: The measurement rate is overridden to 1Hz when in Base mode.'); @@ -930,7 +930,7 @@ function validateFields() { checkConstellations(); checkElementValue("minElev", 0, 90, "Must be between 0 and 90", "collapseGNSSConfig"); - checkElementValue("minCNO", 0, 90, "Must be between 0 and 90", "collapseGNSSConfig"); + checkElementValue("minCN0", 0, 90, "Must be between 0 and 90", "collapseGNSSConfig"); if (isElementShown("lg290pGnssSettings") == true) { checkElementValue("rtcmMinElev", -90, 90, "Must be between -90 and 90", "collapseGNSSConfig"); } diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 1d206d6c8..8ca6fef21 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -94,7 +94,7 @@ void identifyBoard() // 0x08 - HUSB238 - USB C PD Sink Controller bool husb238Present = i2cIsDevicePresent(i2c_0, 0x08); - // 0x10 -MFI343S00177 Authentication Coprocessor + // 0x10 - MFI343S00177 Authentication Coprocessor bool mfiPresent = i2cIsDevicePresent(i2c_0, 0x10); i2c_0->end(); @@ -158,16 +158,6 @@ void identifyBoard() // Torch X2: 8.2/3.3 --> 836mV < 947mV < 1067mV (8.5% tolerance) else if (idWithAdc(idValue, 8.2, 3.3, 8.5)) productVariant = RTK_TORCH_X2; - -#ifdef FLEX_OVERRIDE - systemPrintln("<<<<<<<<<< !!!!!!!!!! FLEX OVERRIDE !!!!!!!!!! >>>>>>>>>>"); - productVariant = RTK_FLEX; // TODO remove once v1.1 Flex has ID resistors -#endif - -#ifdef TORCH_X2_OVERRIDE - systemPrintln("<<<<<<<<<< !!!!!!!!!! TORCH X2 OVERRIDE !!!!!!!!!! >>>>>>>>>>"); - productVariant = RTK_TORCH_X2; // TODO remove once v1.1 Torch X2 has ID resistors -#endif } if (ENABLE_DEVELOPER) @@ -237,9 +227,10 @@ void beginBoard() present.needsExternalPpl = true; // Uses the PointPerfect Library present.galileoHasCapable = true; present.multipathMitigation = true; // UM980 has MPM, other platforms do not - present.minCno = true; + present.minCN0 = true; present.minElevation = true; present.dynamicModel = true; + present.display_type = DISPLAY_MAX_NONE; #ifdef COMPILE_IM19_IMU present.imu_im19 = true; // Allow tiltUpdate() to run @@ -354,7 +345,7 @@ void beginBoard() present.display_i2c1 = true; present.display_type = DISPLAY_128x64; present.i2c1BusSpeed_400 = true; // Run display bus at higher speed - present.minCno = true; + present.minCN0 = true; present.minElevation = true; present.dynamicModel = true; @@ -451,7 +442,6 @@ void beginBoard() // Facet V2 is based on the ESP32-WROVER // ZED-F9P is interfaced via I2C and UART1 // NEO-D9S is interfaced via I2C. UART2 TX is also connected to ESP32 pin 4 - // TODO: pass PMP over serial to save I2C traffic? // Specify the GNSS radio #ifdef COMPILE_ZED @@ -479,7 +469,7 @@ void beginBoard() present.fastPowerOff = true; present.invertedFastPowerOff = true; present.gnss_to_uart = true; - present.minCno = true; + present.minCN0 = true; present.minElevation = true; present.dynamicModel = true; @@ -561,7 +551,7 @@ void beginBoard() present.fastPowerOff = true; present.invertedFastPowerOff = true; present.gnss_to_uart = true; - present.minCno = true; + present.minCN0 = true; present.minElevation = true; present.dynamicModel = true; @@ -660,7 +650,7 @@ void beginBoard() present.mosaicMicroSd = true; present.microSdCardDetectLow = true; // Except microSD is connected to mosaic... present.microSd is false - present.minCno = true; + present.minCN0 = true; present.minElevation = true; present.needsExternalPpl = true; // Uses the PointPerfect Library for L-Band present.dynamicModel = true; @@ -734,7 +724,7 @@ void beginBoard() // We can't enable here because we don't know if lg290pFirmwareVersion is >= v05 // present.minElevation = true; - // present.minCno = true; + // present.minCN0 = true; pin_I2C0_SDA = 7; pin_I2C0_SCL = 20; @@ -782,15 +772,14 @@ void beginBoard() present.antennaPhaseCenter_mm = 62.0; // APC from drawings present.radio_lora = true; present.fuelgauge_bq40z50 = true; - present.charger_mp2762a = true; present.button_powerLow = true; // Button is pressed when high - // present.button_mode = true; //TODO remove comment. This won't be available until v1.1 of hardware + present.button_mode = true; present.beeper = true; present.gnss_to_uart = true; present.gpioExpanderSwitches = true; - // present.microSd = true; // TODO remove comment out - v1.0 hardware does not have pullup on #CD so card detection does not work + present.microSd = true; present.microSdCardDetectLow = true; present.display_i2c0 = true; @@ -799,6 +788,9 @@ void beginBoard() present.displayInverted = true; present.tiltPossible = true; + present.fastPowerOff = true; + present.invertedFastPowerOff = true; // Drive POWER_KILL high to cause powerdown + pin_I2C0_SDA = 15; pin_I2C0_SCL = 4; @@ -806,7 +798,8 @@ void beginBoard() pin_GnssUart_TX = 27; pin_powerSenseAndControl = 34; - // pin_modeButton = 25; //TODO remove comment. This won't be available until v1.1 of hardware + pin_powerFastOff = 23; + pin_modeButton = 25; pin_IMU_RX = 14; // ESP32 UART2 pin_IMU_TX = 17; @@ -824,8 +817,8 @@ void beginBoard() pin_microSD_CS = 22; pin_microSD_CardDetect = 39; - pin_gpioExpanderInterrupt = - 2; // TODO remove on v1.1 hardware. Not used since all GPIO expanded pins are outputs + pinMode(pin_powerFastOff, OUTPUT); + digitalWrite(pin_powerFastOff, LOW); // Low = Stay on. High = turn off. DMW_if systemPrintf("pin_bluetoothStatusLED: %d\r\n", pin_bluetoothStatusLED); pinMode(pin_bluetoothStatusLED, OUTPUT); @@ -870,15 +863,16 @@ void beginBoard() present.gnss_lg290p = true; present.antennaPhaseCenter_mm = 116.5; // Default to Torch helical APC, average of L1/L2 present.fuelgauge_bq40z50 = true; - present.charger_mp2762a = true; - present.button_powerHigh = true; // Button is pressed when high + present.button_powerLow = true; // Button is pressed when low present.beeper = true; present.gnss_to_uart = true; present.needsExternalPpl = true; // Uses the PointPerfect Library + present.fastPowerOff = true; + present.invertedFastPowerOff = true; // Drive PWRKILL high to cause powerdown // We can't enable GNSS features here because we don't know if lg290pFirmwareVersion is >= v05 // present.minElevation = true; - // present.minCno = true; + // present.minCN0 = true; pin_I2C0_SDA = 15; pin_I2C0_SCL = 4; @@ -887,7 +881,7 @@ void beginBoard() pin_GnssUart_TX = 17; pin_GNSS_DR_Reset = 22; // Push low to reset GNSS/DR. - pin_GNSS_TimePulse = 39; // PPS on UM980 + pin_GNSS_TimePulse = 39; // PPS on LG290P pin_usbSelect = 12; // Controls U18 switch between ESP UART0 to USB or GNSS UART1 pin_powerAdapterDetect = 36; // Goes low when USB cable is plugged in @@ -899,12 +893,14 @@ void beginBoard() pin_beeper = 33; pin_powerButton = 34; - // pin_powerSenseAndControl = 18; // PWRKILL + pin_powerFastOff = 18; // PWRKILL pin_loraRadio_power = 19; // LoRa_EN // pin_loraRadio_boot = 23; // LoRa_BOOT0 // pin_loraRadio_reset = 5; // LoRa_NRST + pinMode(pin_powerFastOff, INPUT); // Leave this as an input. powerDown() will drive high for fast power off + DMW_if systemPrintf("pin_bluetoothStatusLED: %d\r\n", pin_bluetoothStatusLED); pinMode(pin_bluetoothStatusLED, OUTPUT); @@ -1504,6 +1500,12 @@ void beginButtons() buttonCount++; if (present.gpioExpanderButtons == true) buttonCount++; + if (productVariant == RTK_FLEX) + { + Serial.println("<<<<<<<<<<<<<<<<<<<< Deal with Flex buttons >>>>>>>>>>>>>>"); + buttonCount = 1; + } + if (buttonCount > 1) reportFatalError("Illegal button assignment."); @@ -1523,9 +1525,15 @@ void beginButtons() else { // Use the Button library - // Facet main/power button - if (present.button_powerLow == true && pin_powerSenseAndControl != PIN_UNDEFINED) - userBtn = new Button(pin_powerSenseAndControl); + if (present.button_powerLow == true) + { + // Torch X2 has both a powerButton and powerSenseAndControl (PWRKILL). Assign button task to powerButton. + if (pin_powerButton != PIN_UNDEFINED) + userBtn = new Button(pin_powerButton); + // Facet main/power button + else if (pin_powerSenseAndControl != PIN_UNDEFINED) + userBtn = new Button(pin_powerSenseAndControl); + } // Torch main/power button if (present.button_powerHigh == true && pin_powerButton != PIN_UNDEFINED) @@ -1789,6 +1797,12 @@ bool i2cBusInitialization(TwoWire *i2cBus, int sda, int scl, int clockKHz) // SDA/VCC shorted: 1000ms, response 5 // SDA/GND shorted: 14ms, response 5 timer = millis(); + + // If there is nothing else on the bus, the authentication coprocessor can be asleep + // Ping it twice to be sure + if (addr == 0x10) + i2cIsDevicePresent(i2cBus, addr); // Throw away result. Just wake it up. + if (i2cIsDevicePresent(i2cBus, addr)) { if (deviceFound == false) diff --git a/Firmware/RTK_Everywhere/Bluetooth.ino b/Firmware/RTK_Everywhere/Bluetooth.ino index c8e333d67..e94f73323 100644 --- a/Firmware/RTK_Everywhere/Bluetooth.ino +++ b/Firmware/RTK_Everywhere/Bluetooth.ino @@ -19,6 +19,8 @@ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +BluetoothRadioType_e bluetoothRadioPreviousOnType = BLUETOOTH_RADIO_OFF; + #ifdef COMPILE_BT //---------------------------------------- @@ -386,7 +388,10 @@ void BTConfirmRequestCallback(uint32_t numVal) { // TODO: if the RTK device has an OLED, we should display the PIN so user can confirm systemPrintf("Device sent PIN: %06lu. Sending confirmation\r\n", numVal); +#ifdef COMPILE_BT bluetoothSerialSpp->confirmReply(true); // AUTO_PAIR - equivalent to enableSSP(false, true); +#endif // COMPILE_BT + // TODO: if the RTK device has an OLED, we should display the PIN so user can confirm } void deviceNameSpacesToUnderscores() @@ -556,14 +561,14 @@ void bluetoothStart(bool skipOnlineCheck) bluetoothSerialSpp->enableSSP(false, false); // Enable secure pairing with PIN : - //bluetoothSerialSpp->enableSSP(false, true); + // bluetoothSerialSpp->enableSSP(false, true); // Accessory Protocol recommends using a PIN // Support Apple Accessory: Device to Accessory // 1. Search for an accessory from the device and initiate pairing. // 2. Verify pairing is successful after exchanging a pin code. - //bluetoothSerialSpp->enableSSP(true, true); // Enable secure pairing with PIN - //bluetoothSerialSpp->onConfirmRequest(&BTConfirmRequestCallback); // Callback to verify the PIN + // bluetoothSerialSpp->enableSSP(true, true); // Enable secure pairing with PIN + // bluetoothSerialSpp->onConfirmRequest(&BTConfirmRequestCallback); // Callback to verify the PIN beginSuccess &= bluetoothSerialSpp->begin( deviceName, true, true, settings.sppRxQueueSize, settings.sppTxQueueSize, 0, 0, @@ -595,9 +600,9 @@ void bluetoothStart(bool skipOnlineCheck) record.type = ESP_SDP_TYPE_RAW; record.uuid.len = sizeof(UUID_IAP2); memcpy(record.uuid.uuid.uuid128, UUID_IAP2, sizeof(UUID_IAP2)); - //record.service_name_length = strlen(sdp_service_name) + 1; - //record.service_name = (char *)sdp_service_name; - // Use the same EIR Local Name parameter as the Name in the IdentificationInformation + // record.service_name_length = strlen(sdp_service_name) + 1; + // record.service_name = (char *)sdp_service_name; + // Use the same EIR Local Name parameter as the Name in the IdentificationInformation record.service_name_length = strlen(deviceName) + 1; record.service_name = (char *)deviceName; // record.rfcomm_channel_number = 1; // Doesn't seem to help the failed connects @@ -672,7 +677,7 @@ void bluetoothStart(bool skipOnlineCheck) if (pin_bluetoothStatusLED != PIN_UNDEFINED) { - bluetoothLedTask.detach(); // Reset BT LED blinker task rate to 2Hz + bluetoothLedTask.detach(); // Reset BT LED blinker task rate to 2Hz bluetoothLedTask.attach(bluetoothLedTaskPace2Hz, tickerBluetoothLedUpdate); // Rate in seconds, callback } @@ -682,11 +687,11 @@ void bluetoothStart(bool skipOnlineCheck) { if (bluetoothCommandTaskHandle == nullptr) xTaskCreatePinnedToCore( - bluetoothCommandTask, // Function to run - "BluetoothCommandTask", // Just for humans - 4000, // Stack Size - must be ~4000 - nullptr, // Task input parameter - 0, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest + bluetoothCommandTask, // Function to run + "BluetoothCommandTask", // Just for humans + 4000, // Stack Size - must be ~4000 + nullptr, // Task input parameter + 0, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest &bluetoothCommandTaskHandle, // Task handle settings.bluetoothInterruptsCore); // Core where task should run, 0 = core, 1 = Arduino } diff --git a/Firmware/RTK_Everywhere/Developer.ino b/Firmware/RTK_Everywhere/Developer.ino index 59cb82bc9..88b36c8e6 100644 --- a/Firmware/RTK_Everywhere/Developer.ino +++ b/Firmware/RTK_Everywhere/Developer.ino @@ -290,7 +290,7 @@ void wifiVerifyTables() {} void menuTilt() {} void nmeaApplyCompensation(char *nmeaSentence, int arraySize) {} -void tiltDetect() {} +void tiltDetect() {systemPrintln("**Tilt Not Compiled**");} bool tiltIsCorrecting() {return(false);} void tiltRequestStop() {} void tiltSensorFactoryReset() {} diff --git a/Firmware/RTK_Everywhere/Display.ino b/Firmware/RTK_Everywhere/Display.ino index bcb1ef59a..d70336ac5 100644 --- a/Firmware/RTK_Everywhere/Display.ino +++ b/Firmware/RTK_Everywhere/Display.ino @@ -218,7 +218,8 @@ void displayUpdate() forceDisplayUpdate = false; if (present.displayInverted == false) - oled->reset(false); // Incase of previous corruption, force re-alignment of CGRAM. Do not init buffers as it + oled->reset( + false); // Incase of previous corruption, force re-alignment of CGRAM. Do not init buffers as it // takes time and causes screen to blink. oled->erase(); @@ -283,13 +284,23 @@ void displayUpdate() */ case (STATE_ROVER_NOT_STARTED): - displayHorizontalAccuracy(&iconPropertyList, &CrossHairProperties, - 0b11111111); // Single crosshair, no blink - paintLogging(&iconPropertyList); - displaySivVsOpenShort(&iconPropertyList); - displayBatteryVsEthernet(&iconPropertyList); - displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only - setRadioIcons(&iconPropertyList); + // displayHorizontalAccuracy(&iconPropertyList, &CrossHairProperties, + // 0b11111111); // Single crosshair, no blink + // paintLogging(&iconPropertyList); + // displaySivVsOpenShort(&iconPropertyList); + // displayBatteryVsEthernet(&iconPropertyList); + // displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only + // setRadioIcons(&iconPropertyList); + // break; + case (STATE_ROVER_CONFIG_WAIT): + // displayHorizontalAccuracy(&iconPropertyList, &CrossHairProperties, + // 0b11111111); // Single crosshair, no blink + // paintLogging(&iconPropertyList); + // displaySivVsOpenShort(&iconPropertyList); + // displayBatteryVsEthernet(&iconPropertyList); + // displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only + // setRadioIcons(&iconPropertyList); + displayRoverStart(0); break; case (STATE_ROVER_NO_FIX): displayHorizontalAccuracy(&iconPropertyList, &CrossHairProperties, @@ -332,7 +343,8 @@ void displayUpdate() case (STATE_BASE_CASTER_NOT_STARTED): case (STATE_BASE_NOT_STARTED): - // Do nothing. Static display shown during state change. + case (STATE_BASE_CONFIG_WAIT): + displayBaseStart(0); // Show 'Base' while the system configures the Base break; // Start of base / survey in / NTRIP mode @@ -364,9 +376,10 @@ void displayUpdate() displayBaseSiv(&iconPropertyList); // 128x64 only break; case (STATE_BASE_FIXED_NOT_STARTED): - displayBatteryVsEthernet(&iconPropertyList); // Top right - displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only - setRadioIcons(&iconPropertyList); + displayBaseSuccess(0); // Show 'Base Started' while the system configures the Base + // displayBatteryVsEthernet(&iconPropertyList); // Top right + // displayFullIPAddress(&iconPropertyList); // Bottom left - 128x64 only + // setRadioIcons(&iconPropertyList); break; case (STATE_BASE_FIXED_TRANSMITTING): paintLogging(&iconPropertyList); @@ -378,8 +391,7 @@ void displayUpdate() break; case (STATE_NTPSERVER_NOT_STARTED): - case (STATE_NTPSERVER_NO_SYNC): - { + case (STATE_NTPSERVER_NO_SYNC): { paintClock(&iconPropertyList, true); // Blink displaySivVsOpenShort(&iconPropertyList); @@ -400,8 +412,7 @@ void displayUpdate() } break; - case (STATE_NTPSERVER_SYNC): - { + case (STATE_NTPSERVER_SYNC): { paintClock(&iconPropertyList, false); // No blink displaySivVsOpenShort(&iconPropertyList); paintLogging(&iconPropertyList, false, true); // No pulse, NTP @@ -917,22 +928,19 @@ void setRadioIcons(std::vector *iconList) paintDynamicModel(iconList); break; case (STATE_BASE_TEMP_SETTLE): - case (STATE_BASE_TEMP_SURVEY_STARTED): - { + case (STATE_BASE_TEMP_SURVEY_STARTED): { prop.duty = 0b00001111; prop.icon = BaseTemporaryProperties.iconDisplay[present.display_type]; iconList->push_back(prop); } break; - case (STATE_BASE_TEMP_TRANSMITTING): - { + case (STATE_BASE_TEMP_TRANSMITTING): { prop.duty = 0b11111111; prop.icon = BaseTemporaryProperties.iconDisplay[present.display_type]; iconList->push_back(prop); } break; - case (STATE_BASE_FIXED_TRANSMITTING): - { + case (STATE_BASE_FIXED_TRANSMITTING): { prop.duty = 0b11111111; prop.icon = BaseFixedProperties.iconDisplay[present.display_type]; iconList->push_back(prop); @@ -1253,6 +1261,8 @@ void setModeIcon(std::vector *iconList) { case (STATE_ROVER_NOT_STARTED): break; + case (STATE_ROVER_CONFIG_WAIT): + break; case (STATE_ROVER_NO_FIX): paintDynamicModel(iconList); break; @@ -1268,26 +1278,24 @@ void setModeIcon(std::vector *iconList) case (STATE_BASE_CASTER_NOT_STARTED): case (STATE_BASE_NOT_STARTED): + case (STATE_BASE_CONFIG_WAIT): // Do nothing. Static display shown during state change. break; - case (STATE_BASE_TEMP_SETTLE): - { + case (STATE_BASE_TEMP_SETTLE): { iconPropertyBlinking prop; prop.duty = 0b00001111; prop.icon = BaseTemporaryProperties.iconDisplay[present.display_type]; iconList->push_back(prop); } break; - case (STATE_BASE_TEMP_SURVEY_STARTED): - { + case (STATE_BASE_TEMP_SURVEY_STARTED): { iconPropertyBlinking prop; prop.duty = 0b00001111; prop.icon = BaseTemporaryProperties.iconDisplay[present.display_type]; iconList->push_back(prop); } break; - case (STATE_BASE_TEMP_TRANSMITTING): - { + case (STATE_BASE_TEMP_TRANSMITTING): { iconPropertyBlinking prop; prop.duty = 0b11111111; prop.icon = BaseTemporaryProperties.iconDisplay[present.display_type]; @@ -1297,8 +1305,7 @@ void setModeIcon(std::vector *iconList) case (STATE_BASE_FIXED_NOT_STARTED): // Do nothing. Static display shown during state change. break; - case (STATE_BASE_FIXED_TRANSMITTING): - { + case (STATE_BASE_FIXED_TRANSMITTING): { iconPropertyBlinking prop; prop.duty = 0b11111111; prop.icon = BaseFixedProperties.iconDisplay[present.display_type]; @@ -2101,8 +2108,6 @@ void displayBaseStart(uint16_t displayTime) oled->display(); - oled->display(); - delay(displayTime); } } @@ -2462,8 +2467,8 @@ void paintProfile(uint8_t profileUnit) if (profileNumber >= 0) { - settings.gnssConfiguredBase = false; // On the next boot, reapply all settings - settings.gnssConfiguredRover = false; + gnssConfigureDefaults(); // Set all bits in the request bitfield to cause the GNSS receiver to go through a + // full (re)configuration recordSystemSettings(); // Before switching, we need to record the current settings to LittleFS and SD recordProfileNumber( @@ -2601,35 +2606,6 @@ void paintSystemTest() oled->print(macAddress); oled->print(":"); - // Verify the ESP UART can communicate TX/RX to ZED UART1 - if (zedUartPassed == false) - { - systemPrintln("GNSS test"); - - setMuxport(MUX_GNSS_UART); // Set mux to UART so we can debug over data port - delay(20); - - // Clear out buffer before starting - while (serialGNSS->available()) - serialGNSS->read(); - serialGNSS->flush(); - -#ifdef COMPILE_ZED - SFE_UBLOX_GNSS_SERIAL myGNSS; - - // begin() attempts 3 connections - if (myGNSS.begin(*serialGNSS) == true) - { - - zedUartPassed = true; - oled->print("OK"); - } - else - oled->print("FAIL"); -#endif // COMPILE_ZED - } - else - oled->print("OK"); } // End display 1 if (systemTestDisplayNumber == 0) @@ -2672,8 +2648,7 @@ void paintDisplaySetup() { constructSetupDisplay(&setupButtons); // Construct the vector (linked list) of buttons - uint8_t maxButtons = - ((present.display_type == DISPLAY_128x64) ? 5 : 4); + uint8_t maxButtons = ((present.display_type == DISPLAY_128x64) ? 5 : 4); uint8_t printedButtons = 0; @@ -2688,10 +2663,7 @@ void paintDisplaySetup() { if (it->newState == STATE_PROFILE) { - int nameWidth = - ((present.display_type == DISPLAY_128x64) - ? 17 - : 9); + int nameWidth = ((present.display_type == DISPLAY_128x64) ? 17 : 9); char miniProfileName[nameWidth] = {0}; snprintf(miniProfileName, sizeof(miniProfileName), "%d_%s", it->newProfile, it->name); // Prefix with index # @@ -3102,6 +3074,11 @@ void paintEspNowPaired() displayMessage("ESP-NOW Paired", 2000); } +void paintMosaicBooting() +{ + displayMessage("GNSS Booting", 0); +} + void displayNtpStart(uint16_t displayTime) { if (online.display == true) diff --git a/Firmware/RTK_Everywhere/GNSS.h b/Firmware/RTK_Everywhere/GNSS.h index 5628a90a4..a1045380f 100644 --- a/Firmware/RTK_Everywhere/GNSS.h +++ b/Firmware/RTK_Everywhere/GNSS.h @@ -9,397 +9,403 @@ GNSS.h class GNSS { -protected: - float _altitude; // Altitude in meters - float _horizontalAccuracy; // Horizontal position accuracy in meters - double _latitude; // Latitude in degrees - double _longitude; // Longitude in degrees - - uint8_t _day; // Day number - uint8_t _month; // Month number - uint16_t _year; - uint8_t _hour; // Hours for 24 hour clock - uint8_t _minute; - uint8_t _second; - uint8_t _leapSeconds; - uint16_t _millisecond; // Limited to first two digits - uint32_t _nanosecond; - - uint8_t _satellitesInView; - uint8_t _fixType; - uint8_t _carrierSolution; - - bool _validDate; // True when date is valid - bool _validTime; // True when time is valid - bool _confirmedDate; - bool _confirmedTime; - bool _fullyResolved; - uint32_t _tAcc; - - unsigned long _pvtArrivalMillis; - bool _pvtUpdated; - - bool _corrRadioExtPortEnabled = false; - - unsigned long _autoBaseStartTimer; // Tracks how long the base auto / averaging mode has been running - - // Setup the general configuration of the GNSS - // Not Rover or Base specific (ie, baud rates) - // Outputs: - // Returns true if successfully configured and false upon failure - virtual bool configureGNSS(); - - // Set the minimum satellite signal level for navigation. - virtual bool setMinCnoRadio(uint8_t cnoValue); + protected: + float _altitude; // Altitude in meters + float _horizontalAccuracy; // Horizontal position accuracy in meters + double _latitude; // Latitude in degrees + double _longitude; // Longitude in degrees + + uint8_t _day; // Day number + uint8_t _month; // Month number + uint16_t _year; + uint8_t _hour; // Hours for 24 hour clock + uint8_t _minute; + uint8_t _second; + uint8_t _leapSeconds; + uint16_t _millisecond; // Limited to first two digits + uint32_t _nanosecond; -public: - // Constructor - GNSS() : _leapSeconds(18), _pvtArrivalMillis(0), _pvtUpdated(0), _satellitesInView(0) - { - } - - // If we have decryption keys, configure module - // Note: don't check online.lband_neo here. We could be using ip corrections - virtual void applyPointPerfectKeys(); + uint8_t _satellitesInView; + uint8_t _fixType; + uint8_t _carrierSolution; - // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) - virtual void baseRtcmDefault(); + bool _validDate; // True when date is valid + bool _validTime; // True when time is valid + bool _confirmedDate; + bool _confirmedTime; + bool _fullyResolved; + uint32_t _tAcc; - // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) - virtual void baseRtcmLowDataRate(); + unsigned long _pvtArrivalMillis; + bool _pvtUpdated; - // Check if a given baud rate is supported by this module - virtual bool baudIsAllowed(uint32_t baudRate); - virtual uint32_t baudGetMinimum(); - virtual uint32_t baudGetMaximum(); + bool _corrRadioExtPortEnabled = false; - // Connect to GNSS and identify particulars - virtual void begin(); + unsigned long _autoBaseStartTimer; // Tracks how long the base auto / averaging mode has been running - // Setup TM2 time stamp input as need - // Outputs: - // Returns true when an external event occurs and false if no event - virtual bool beginExternalEvent(); + public: + // Constructor + GNSS() : _leapSeconds(18), _pvtArrivalMillis(0), _pvtUpdated(0), _satellitesInView(0) + { + } - // Setup the timepulse output on the PPS pin for external triggering - // Outputs - // Returns true if the pin was successfully setup and false upon - // failure - virtual bool beginPPS(); + // If we have decryption keys, configure module + // Note: don't check online.lband_neo here. We could be using ip corrections + virtual void applyPointPerfectKeys(); - virtual bool checkNMEARates(); + // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) + virtual void baseRtcmDefault(); - virtual bool checkPPPRates(); + // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) + virtual void baseRtcmLowDataRate(); - // Setup the general configuration of the GNSS - // Not Rover or Base specific (ie, baud rates) - // Outputs: - // Returns true if successfully configured and false upon failure - bool configure(); + // Check if a given baud rate is supported by this module + virtual bool baudIsAllowed(uint32_t baudRate); + virtual uint32_t baudGetMinimum(); + virtual uint32_t baudGetMaximum(); - // Configure the Base - // Outputs: - // Returns true if successfully configured and false upon failure - virtual bool configureBase(); + // Connect to GNSS and identify particulars + virtual void begin(); - // Configure specific aspects of the receiver for NTP mode - virtual bool configureNtpMode(); + // Setup TM2 time stamp input as need + // Outputs: + // Returns true when an external event occurs and false if no event + virtual bool beginExternalEvent(); - // Configure the Rover - // Outputs: - // Returns true if successfully configured and false upon failure - virtual bool configureRover(); + virtual bool checkNMEARates(); - // Responds with the messages supported on this platform - // Inputs: - // returnText: String to receive message names - // Returns message names in the returnText string - virtual void createMessageList(String &returnText); + virtual bool checkPPPRates(); - // Responds with the RTCM/Base messages supported on this platform - // Inputs: - // returnText: String to receive message names - // Returns message names in the returnText string - virtual void createMessageListBase(String &returnText); + // Setup the general configuration of the GNSS + // Not Rover or Base specific (ie, baud rates) + // Outputs: + // Returns true if successfully configured and false upon failure + virtual bool configure(); - virtual void debuggingDisable(); + // Configure the Base + // Outputs: + // Returns true if successfully configured and false upon failure + virtual bool configureBase(); - virtual void debuggingEnable(); + // Configure specific aspects of the receiver for NTP mode + virtual bool configureNtpMode(); - virtual void enableGgaForNtrip(); + // Configure the Rover + // Outputs: + // Returns true if successfully configured and false upon failure + virtual bool configureRover(); - // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted - // even if there is no GPS fix. We use it to test serial output. - // Outputs: - // Returns true if successfully started and false upon failure - virtual bool enableRTCMTest(); + // Responds with the messages supported on this platform + // Inputs: + // returnText: String to receive message names + // Returns message names in the returnText string + virtual void createMessageList(String &returnText); - // Restore the GNSS to the factory settings - virtual void factoryReset(); + // Responds with the RTCM/Base messages supported on this platform + // Inputs: + // returnText: String to receive message names + // Returns message names in the returnText string + virtual void createMessageListBase(String &returnText); - virtual uint16_t fileBufferAvailable(); + virtual void debuggingDisable(); - virtual uint16_t fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead); + virtual void debuggingEnable(); - // Start the base using fixed coordinates - // Outputs: - // Returns true if successfully started and false upon failure - virtual bool fixedBaseStart(); + // Restore the GNSS to the factory settings + virtual void factoryReset(); - // Return the number of active/enabled messages - virtual uint8_t getActiveMessageCount(); + virtual uint16_t fileBufferAvailable(); - // Return the number of active/enabled RTCM messages - virtual uint8_t getActiveRtcmMessageCount(); + virtual uint16_t fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead); - // Get the altitude - // Outputs: - // Returns the altitude in meters or zero if the GNSS is offline - virtual double getAltitude(); + // Start the base using fixed coordinates + // Outputs: + // Returns true if successfully started and false upon failure + virtual bool fixedBaseStart(); - // Returns the carrier solution or zero if not online - virtual uint8_t getCarrierSolution(); + virtual bool fixRateIsAllowed(uint32_t fixRateMs); - virtual uint32_t getDataBaudRate(); + //Return min/max rate in ms + virtual uint32_t fixRateGetMinimumMs(); - // Returns the day number or zero if not online - virtual uint8_t getDay(); + virtual uint32_t fixRateGetMaximumMs(); - // Return the number of milliseconds since GNSS data was last updated - virtual uint16_t getFixAgeMilliseconds(); + // Return the number of active/enabled messages + virtual uint8_t getActiveMessageCount(); - // Returns the fix type or zero if not online - virtual uint8_t getFixType(); + // Return the number of active/enabled RTCM messages + virtual uint8_t getActiveRtcmMessageCount(); - // Returns the hours of 24 hour clock or zero if not online - virtual uint8_t getHour(); + // Get the altitude + // Outputs: + // Returns the altitude in meters or zero if the GNSS is offline + virtual double getAltitude(); - // Get the horizontal position accuracy - // Outputs: - // Returns the horizontal position accuracy or zero if offline - virtual float getHorizontalAccuracy(); + // Returns the carrier solution or zero if not online + virtual uint8_t getCarrierSolution(); - virtual const char *getId(); + virtual uint32_t getDataBaudRate(); - // Get the latitude value - // Outputs: - // Returns the latitude value or zero if not online - virtual double getLatitude(); + // Returns the day number or zero if not online + virtual uint8_t getDay(); - // Query GNSS for current leap seconds - virtual uint8_t getLeapSeconds(); + // Return the number of milliseconds since GNSS data was last updated + virtual uint16_t getFixAgeMilliseconds(); - // Return the type of logging that matches the enabled messages - drives the logging icon - virtual uint8_t getLoggingType(); + // Returns the fix type or zero if not online + virtual uint8_t getFixType(); - // Get the longitude value - // Outputs: - // Returns the longitude value or zero if not online - virtual double getLongitude(); + // Returns the hours of 24 hour clock or zero if not online + virtual uint8_t getHour(); - // Returns two digits of milliseconds or zero if not online - virtual uint8_t getMillisecond(); + // Get the horizontal position accuracy + // Outputs: + // Returns the horizontal position accuracy or zero if offline + virtual float getHorizontalAccuracy(); - // Get the minimum satellite signal level for navigation. - uint8_t getMinCno(); + virtual const char *getId(); - // Returns minutes or zero if not online - virtual uint8_t getMinute(); + // Get the latitude value + // Outputs: + // Returns the latitude value or zero if not online + virtual double getLatitude(); - // Returns month number or zero if not online - virtual uint8_t getMonth(); + // Query GNSS for current leap seconds + virtual uint8_t getLeapSeconds(); - // Returns nanoseconds or zero if not online - virtual uint32_t getNanosecond(); + // Return the type of logging that matches the enabled messages - drives the logging icon + virtual uint8_t getLoggingType(); - virtual uint32_t getRadioBaudRate(); + // Get the longitude value + // Outputs: + // Returns the longitude value or zero if not online + virtual double getLongitude(); - // Returns the seconds between solutions - virtual double getRateS(); + // Returns two digits of milliseconds or zero if not online + virtual uint8_t getMillisecond(); - virtual const char *getRtcmDefaultString(); + // Returns minutes or zero if not online + virtual uint8_t getMinute(); - virtual const char *getRtcmLowDataRateString(); + // Returns the current mode: Base/Rover/etc + virtual uint8_t getMode(); - // Returns the number of satellites in view or zero if offline - virtual uint8_t getSatellitesInView(); + // Returns month number or zero if not online + virtual uint8_t getMonth(); - // Returns seconds or zero if not online - virtual uint8_t getSecond(); + // Returns nanoseconds or zero if not online + virtual uint32_t getNanosecond(); - // Get the survey-in mean accuracy - // Outputs: - // Returns the mean accuracy or zero (0) - virtual float getSurveyInMeanAccuracy(); + virtual uint32_t getRadioBaudRate(); - // Return the number of seconds the survey-in process has been running - virtual int getSurveyInObservationTime(); + // Returns the seconds between solutions + virtual double getRateS(); - float getSurveyInStartingAccuracy(); + virtual const char *getRtcmDefaultString(); - // Returns timing accuracy or zero if not online - virtual uint32_t getTimeAccuracy(); + virtual const char *getRtcmLowDataRateString(); - // Returns full year, ie 2023, not 23. - virtual uint16_t getYear(); + // Returns the number of satellites in view or zero if offline + virtual uint8_t getSatellitesInView(); - // Antenna Short / Open detection - virtual bool isAntennaShorted(); - virtual bool isAntennaOpen(); + // Returns seconds or zero if not online + virtual uint8_t getSecond(); - virtual bool isBlocking(); + // Get the survey-in mean accuracy + // Outputs: + // Returns the mean accuracy or zero (0) + virtual float getSurveyInMeanAccuracy(); - // Date is confirmed once we have GNSS fix - virtual bool isConfirmedDate(); + // Return the number of seconds the survey-in process has been running + virtual int getSurveyInObservationTime(); - // Date is confirmed once we have GNSS fix - virtual bool isConfirmedTime(); + // Returns timing accuracy or zero if not online + virtual uint32_t getTimeAccuracy(); - // Returns true if data is arriving on the Radio Ext port - virtual bool isCorrRadioExtPortActive(); + // Returns full year, ie 2023, not 23. + virtual uint16_t getYear(); - // Return true if GNSS receiver has a higher quality DGPS fix than 3D - virtual bool isDgpsFixed(); + // Helper functions for the current mode as read from the GNSS receiver + // Not to be confused with inRoverMode() and inBaseMode() used in States.ino + virtual bool gnssInBaseFixedMode(); + virtual bool gnssInBaseSurveyInMode(); + virtual bool gnssInRoverMode(); - // Some functions (L-Band area frequency determination) merely need - // to know if we have a valid fix, not what type of fix - // This function checks to see if the given platform has reached - // sufficient fix type to be considered valid - virtual bool isFixed(); + // Antenna Short / Open detection + virtual bool isAntennaShorted(); + virtual bool isAntennaOpen(); - // Used in tpISR() for time pulse synchronization - virtual bool isFullyResolved(); + virtual bool isBlocking(); - virtual bool isPppConverged(); + // Date is confirmed once we have GNSS fix + virtual bool isConfirmedDate(); - virtual bool isPppConverging(); + // Date is confirmed once we have GNSS fix + virtual bool isConfirmedTime(); - // Some functions (L-Band area frequency determination) merely need - // to know if we have an RTK Fix. This function checks to see if the - // given platform has reached sufficient fix type to be considered valid - virtual bool isRTKFix(); + // Returns true if data is arriving on the Radio Ext port + virtual bool isCorrRadioExtPortActive(); - // Some functions (L-Band area frequency determination) merely need - // to know if we have an RTK Float. This function checks to see if - // the given platform has reached sufficient fix type to be considered - // valid - virtual bool isRTKFloat(); + // Return true if GNSS receiver has a higher quality DGPS fix than 3D + virtual bool isDgpsFixed(); - // Determine if the survey-in operation is complete - // Outputs: - // Returns true if the survey-in operation is complete and false - // if the operation is still running - virtual bool isSurveyInComplete(); + // Some functions (L-Band area frequency determination) merely need + // to know if we have a valid fix, not what type of fix + // This function checks to see if the given platform has reached + // sufficient fix type to be considered valid + virtual bool isFixed(); - // Date will be valid if the RTC is reporting (regardless of GNSS fix) - virtual bool isValidDate(); + // Used in tpISR() for time pulse synchronization + virtual bool isFullyResolved(); - // Time will be valid if the RTC is reporting (regardless of GNSS fix) - virtual bool isValidTime(); + virtual bool isPppConverged(); - // Controls the constellations that are used to generate a fix and logged - virtual void menuConstellations(); + virtual bool isPppConverging(); - virtual void menuMessageBaseRtcm(); + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Fix. This function checks to see if the + // given platform has reached sufficient fix type to be considered valid + virtual bool isRTKFix(); - // Control the messages that get broadcast over Bluetooth and logged (if enabled) - virtual void menuMessages(); + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Float. This function checks to see if + // the given platform has reached sufficient fix type to be considered + // valid + virtual bool isRTKFloat(); - // Print the module type and firmware version - virtual void printModuleInfo(); + // Determine if the survey-in operation is complete + // Outputs: + // Returns true if the survey-in operation is complete and false + // if the operation is still running + virtual bool isSurveyInComplete(); - // Send correction data to the GNSS - // Inputs: - // dataToSend: Address of a buffer containing the data - // dataLength: The number of valid data bytes in the buffer - // Outputs: - // Returns the number of correction data bytes written - virtual int pushRawData(uint8_t *dataToSend, int dataLength); + // Date will be valid if the RTC is reporting (regardless of GNSS fix) + virtual bool isValidDate(); - virtual uint16_t rtcmBufferAvailable(); + // Time will be valid if the RTC is reporting (regardless of GNSS fix) + virtual bool isValidTime(); - // If LBand is being used, ignore any RTCM that may come in from the GNSS - virtual void rtcmOnGnssDisable(); + // Controls the constellations that are used to generate a fix and logged + virtual void menuConstellations(); - // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver - virtual void rtcmOnGnssEnable(); + virtual void menuMessageBaseRtcm(); - virtual uint16_t rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead); + // Control the messages that get broadcast over Bluetooth and logged (if enabled) + virtual void menuMessages(); - // Save the current configuration - // Outputs: - // Returns true when the configuration was saved and false upon failure - virtual bool saveConfiguration(); + // Print the module type and firmware version + virtual void printModuleInfo(); - // Enable all the valid constellations and bands for this platform - virtual bool setConstellations(); + // Send correction data to the GNSS + // Inputs: + // dataToSend: Address of a buffer containing the data + // dataLength: The number of valid data bytes in the buffer + // Outputs: + // Returns the number of correction data bytes written + virtual int pushRawData(uint8_t *dataToSend, int dataLength); - // Enable / disable corrections protocol(s) on the Radio External port - // Always update if force is true. Otherwise, only update if enable has changed state - virtual bool setCorrRadioExtPort(bool enable, bool force); + // Hardware or software reset the GNSS receiver + virtual bool reset(); - virtual bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); + virtual uint16_t rtcmBufferAvailable(); - virtual bool setDataBaudRate(uint32_t baud); + // If LBand is being used, ignore any RTCM that may come in from the GNSS + virtual void rtcmOnGnssDisable(); - // Set the elevation in degrees - // Inputs: - // elevationDegrees: The elevation value in degrees - virtual bool setElevation(uint8_t elevationDegrees); + // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver + virtual void rtcmOnGnssEnable(); - // Enable all the valid messages for this platform - virtual bool setMessages(int maxRetries); + virtual uint16_t rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead); - // Enable all the valid messages for this platform over the USB port - virtual bool setMessagesUsb(int maxRetries); + // Save the current configuration + // Outputs: + // Returns true when the configuration was saved and false upon failure + virtual bool saveConfiguration(); - // Set the minimum satellite signal level for navigation. - bool setMinCno(uint8_t cnoValue); + // Enable all the valid constellations and bands for this platform + virtual bool setConstellations(); - // Set the dynamic model to use for RTK - // Inputs: - // modelNumber: Number of the model to use, provided by radio library - virtual bool setModel(uint8_t modelNumber); + // Enable / disable corrections protocol(s) on the Radio External port + // Always update if force is true. Otherwise, only update if enable has changed state + virtual bool setCorrRadioExtPort(bool enable, bool force); - virtual bool setRadioBaudRate(uint32_t baud); + virtual bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); - // Specify the interval between solutions - // Inputs: - // secondsBetweenSolutions: Number of seconds between solutions - // Outputs: - // Returns true if the rate was successfully set and false upon - // failure - virtual bool setRate(double secondsBetweenSolutions); + virtual bool setBaudRateComm(uint32_t baud); - virtual bool setTalkerGNGGA(); + virtual bool setBaudRateData(uint32_t baud); - // Hotstart GNSS to try to get RTK lock - virtual bool softwareReset(); + virtual bool setBaudRateRadio(uint32_t baud); - virtual bool standby(); + // Set the elevation in degrees + // Inputs: + // elevationDegrees: The elevation value in degrees + virtual bool setElevation(uint8_t elevationDegrees); - // Antenna Short / Open detection - virtual bool supportsAntennaShortOpen(); + virtual bool setHighAccuracyService(bool enableGalileoHas); - // Reset the survey-in operation - // Outputs: - // Returns true if the survey-in operation was reset successfully - // and false upon failure - virtual bool surveyInReset(); + // Configure any logging settings - currently mosaic-X5 specific + virtual bool setLogging(); - // Start the survey-in operation - // Outputs: - // Return true if successful and false upon failure - virtual bool surveyInStart(); + virtual bool setNmeaMessageRateByName(const char *msgName, uint8_t msgRate); - // Poll routine to update the GNSS state - virtual void update(); + // Enable/disable messages according to the NMEA array + virtual bool setMessagesNMEA(); + + // Enable/disable messages according to the RTCM Base array + virtual bool setMessagesRTCMBase(); + + // Enable/disable messages according to the NMEA array + virtual bool setMessagesRTCMRover(); + + // Set the minimum satellite signal level for navigation. + virtual bool setMinCN0(uint8_t cnoValue); + + // Set the dynamic model to use for RTK + // Inputs: + // modelNumber: Number of the model to use, provided by radio library + virtual bool setModel(uint8_t modelNumber); + + virtual bool setMultipathMitigation(bool enableMultipathMitigation); + + // Configure the Pulse-per-second pin based on user settings + virtual bool setPPS(); + + // Specify the interval between solutions + // Inputs: + // secondsBetweenSolutions: Number of seconds between solutions + // Outputs: + // Returns true if the rate was successfully set and false upon + // failure + virtual bool setRate(double secondsBetweenSolutions); + + // Enable/disable any output needed for tilt compensation + virtual bool setTilt(); + + virtual bool standby(); + + // Antenna Short / Open detection + virtual bool supportsAntennaShortOpen(); + + // Reset the survey-in operation + // Outputs: + // Returns true if the survey-in operation was reset successfully + // and false upon failure + virtual bool surveyInReset(); + + // Start the survey-in operation + // Outputs: + // Return true if successful and false upon failure + virtual bool surveyInStart(); + + // Poll routine to update the GNSS state + virtual void update(); }; // Update the constellations following a set command -bool gnssCmdUpdateConstellations(int commandIndex); +bool gnssCmdUpdateConstellations(const char *settingName, void *settingData, int settingType); // Update the message rates following a set command -bool gnssCmdUpdateMessageRates(int commandIndex); +bool gnssCmdUpdateMessageRates(const char *settingName, void *settingData, int settingType); #endif // __GNSS_H__ diff --git a/Firmware/RTK_Everywhere/GNSS.ino b/Firmware/RTK_Everywhere/GNSS.ino index 2f3e361c1..2991fd731 100644 --- a/Firmware/RTK_Everywhere/GNSS.ino +++ b/Firmware/RTK_Everywhere/GNSS.ino @@ -2,71 +2,489 @@ GNSS.ino GNSS layer implementation -------------------------------------------------------------------------------*/ + + For any given GNSS receiver, the following functions need to be implemented: + * begin() - Start communication with the device and its library + * configure() - Runs once after a system wide factory reset. Any settings that need to be set but are not exposed to +the user. + * configureRover() - Change mode to Rover. Request NMEA and RTCM changes as needed. + * configureBase() - Change mode to Base. Fixed/Temp are controlled in states.ino. Request NMEA and RTCM changes as +needed. + * setBaudRateComm() - Set baud rate for connection between microcontroller and GNSS receiver + * setBaudRateData() - Set baud rate for connection to the GNSS UART connected to the connector labeled DATA + * setBaudRateRadio() - Set baud rate for connection to the GNSS UART connected to the connector labeled RADIO + * setRate() - Set the report rate of the GNSS receiver. May or may not drive NMEA/RTCM rates directly. + * setConstellations() - Set the constellations and bands for the GNSS receiver + * setElevation() - Set the degrees a GNSS satellite must be above the horizon in order to be used in location +calculation + * setMinCN0() - Set dBHz a GNSS satellite's signal strength must be above in order to be used in location calculation + * setPPS() - Set the width, period, and polarity of the pulse-per-second signal + * setModel() - Set the model used when calculating a location + * setMessagesNMEA() - Set the NMEA messages output during Base or Rover mode + * setMessagesRTCMBase() - Set the RTCM messages output during Base mode + * setMessagesRTCMRover() - Set the RTCM messages output during Rover mode + * setHighAccuracyService() - Set the PPP/HAS E6 capabilities of the receiver + * setMultipathMitigation() - Set the multipath capabilities of the receiver + * setTilt() - Set the GNSS receiver's output to be compatible with a tilt sensor + * setCorrRadioExtPort() - Set corrections protocol(s) on the UART connected to the RADIO port + * saveConfiguration() - Save the current receiver's settings to the receiver's NVM + * reset() - Reset the receiver (through software or hardware) + * factoryReset() - Reset the receiver to factory settings + There are many more but these form the core of any configuration interface. + ------------------------------------------------------------------------------*/ + +// We may receive a command or the user may change a setting that needs to modify the configuration of the GNSS receiver +// Because this can take time, we group all the changes together and re-configure the receiver once the user has exited +// the menu system, closed the Web Config, or the CLI is closed. +enum +{ + GNSS_CONFIG_ONCE, // Settings specific to a receiver that don't fit into other setting categories + GNSS_CONFIG_ROVER, + GNSS_CONFIG_BASE, // Apply any settings before the start of survey-in or fixed base + GNSS_CONFIG_BASE_SURVEY, // Start survey in base + GNSS_CONFIG_BASE_FIXED, // Start fixed base + GNSS_CONFIG_BAUD_RATE_RADIO, + GNSS_CONFIG_BAUD_RATE_DATA, + GNSS_CONFIG_FIX_RATE, + GNSS_CONFIG_CONSTELLATION, // Turn on/off a constellation + GNSS_CONFIG_ELEVATION, + GNSS_CONFIG_CN0, + GNSS_CONFIG_PPS, + GNSS_CONFIG_MODEL, + GNSS_CONFIG_MESSAGE_RATE_NMEA, // Update NMEA message rates + GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER, // Update RTCM Rover message rates + GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE, // Update RTCM Base message rates + GNSS_CONFIG_MESSAGE_RATE_OTHER, // Update any other messages (UBX, PQTM, etc) + GNSS_CONFIG_HAS_E6, // Enable/disable HAS E6 capabilities + GNSS_CONFIG_MULTIPATH, + GNSS_CONFIG_TILT, // Enable/disable any output needed for tilt compensation + GNSS_CONFIG_EXT_CORRECTIONS, // Enable / disable corrections protocol(s) on the Radio External port + GNSS_CONFIG_LOGGING, // Enable / disable logging + GNSS_CONFIG_SAVE, // Indicates current settings be saved to GNSS receiver NVM + GNSS_CONFIG_RESET, // Indicates receiver needs resetting + + // Add new entries above here + GNSS_CONFIG_MAX, +}; + +static const char *gnssConfigDisplayNames[] = { + "ONCE", + "ROVER", + "BASE", + "BASE SURVEY", + "BASE FIXED", + "BAUD_RATE_RADIO", + "BAUD_RATE_DATA", + "RATE", + "CONSTELLATION", + "ELEVATION", + "CN0", + "PPS", + "MODEL", + "MESSAGE_RATE_NMEA", + "MESSAGE_RATE_RTCM_ROVER", + "MESSAGE_RATE_RTCM_BASE", + "MESSAGE_RATE_RTCM_OTHER", + "HAS_E6", + "MULTIPATH", + "TILT", + "EXT_CORRECTIONS", + "LOGGING", + "SAVE", + "RESET", +}; + +static const int gnssConfigStateEntries = sizeof(gnssConfigDisplayNames) / sizeof(gnssConfigDisplayNames[0]); + +//---------------------------------------- +// Returns true if the antenna is shorted +//---------------------------------------- +bool GNSS::isAntennaShorted() +{ + return false; +} //---------------------------------------- -// Setup the general configuration of the GNSS -// Not Rover or Base specific (ie, baud rates) -// Returns true if successfully configured and false otherwise +// Returns true if the antenna is shorted //---------------------------------------- -bool GNSS::configure() +bool GNSS::isAntennaOpen() +{ + return false; +} + +// Antenna Short / Open detection +bool GNSS::supportsAntennaShortOpen() +{ + return false; +} + +void gnssUpdate() { if (online.gnss == false) - return (false); + return; + + // Belt and suspender + if (gnss == nullptr) + return; + + // Allow the GNSS platform to update itself + gnss->update(); + + if (gnssConfigureComplete() == true) + { + // We need to establish the logging type: + // After a device has completed boot up (the GNSS may or may not have been reconfigured) + // After a user changes the message configurations (NMEA, RTCM, or OTHER). + if (loggingType == LOGGING_UNKNOWN) + setLoggingType(); // Update Standard, PPP, or custom for icon selection + + return; // No configuration requests + } + + // Handle any requested configuration changes + // Only update the GNSS receiver once the CLI, serial menu, and Web Config interfaces are disconnected + // This is to avoid multiple reconfigure delays when multiple commands are received, ie enable GPS, disable Galileo, + // should only trigger one GNSS reconfigure + if (bluetoothCommandIsConnected() == false && inMainMenu == false && inWebConfigMode() == false) + { + bool result = true; + + // Service requests + // Clear the requests as they are completed successfully + // If a platform requires a device reset to complete the config (ie, LG290P changing constellations) then + // the platform specific function should call gnssConfigure(GNSS_CONFIG_RESET) + + if (gnssConfigureRequested(GNSS_CONFIG_ONCE)) + { + if (gnss->configure() == true) + { + gnssConfigureClear(GNSS_CONFIG_ONCE); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_ROVER)) + { + if (gnss->configureRover() == true) + { + gnssConfigureClear(GNSS_CONFIG_ROVER); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_BASE)) + { + if (gnss->configureBase() == true) + { + gnssConfigureClear(GNSS_CONFIG_BASE); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_BASE_SURVEY)) + { + if (gnss->surveyInStart() == true) + { + gnssConfigureClear(GNSS_CONFIG_BASE_SURVEY); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_BASE_FIXED)) + { + if (gnss->fixedBaseStart() == true) + { + gnssConfigureClear(GNSS_CONFIG_BASE_FIXED); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_BAUD_RATE_RADIO)) + { + if (gnss->setBaudRateRadio(settings.radioPortBaud) == true) + { + gnssConfigureClear(GNSS_CONFIG_BAUD_RATE_RADIO); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_BAUD_RATE_DATA)) + { + if (gnss->setBaudRateData(settings.dataPortBaud) == true) + { + gnssConfigureClear(GNSS_CONFIG_BAUD_RATE_DATA); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + // For some receivers (ie, UM980) changing the model changes to Rover/Base. + // Configure model before setting message rates + if (gnssConfigureRequested(GNSS_CONFIG_MODEL)) + { + if (gnss->setModel(settings.dynamicModel) == true) + { + gnssConfigureClear(GNSS_CONFIG_MODEL); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_FIX_RATE)) + { + if (gnss->setRate(settings.measurementRateMs / 1000.0) == true) + { + gnssConfigureClear(GNSS_CONFIG_FIX_RATE); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_CONSTELLATION)) + { + if (gnss->setConstellations() == true) + { + gnssConfigureClear(GNSS_CONFIG_CONSTELLATION); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_ELEVATION)) + { + if (gnss->setElevation(settings.minElev) == true) + { + gnssConfigureClear(GNSS_CONFIG_ELEVATION); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_CN0)) + { + if (gnss->setMinCN0(settings.minCN0) == true) + { + gnssConfigureClear(GNSS_CONFIG_CN0); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_PPS)) + { + if (gnss->setPPS() == true) + { + gnssConfigureClear(GNSS_CONFIG_PPS); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_HAS_E6)) + { + if (gnss->setHighAccuracyService(settings.enableGalileoHas) == true) + { + gnssConfigureClear(GNSS_CONFIG_HAS_E6); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_MULTIPATH)) + { + if (gnss->setMultipathMitigation(settings.enableMultipathMitigation) == true) + { + gnssConfigureClear(GNSS_CONFIG_MULTIPATH); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_MESSAGE_RATE_NMEA)) + { + if (gnss->setMessagesNMEA() == true) + { + gnssConfigureClear(GNSS_CONFIG_MESSAGE_RATE_NMEA); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + setLoggingType(); // Update Standard, PPP, or custom for icon selection + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER)) + { + if (settings.debugGnssConfig == true && gnss->gnssInRoverMode() == false) + systemPrintln("Warning: Change to RTCM Rover rates requested but not in Rover mode."); + + if (gnss->setMessagesRTCMRover() == true) + { + gnssConfigureClear(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + setLoggingType(); // Update Standard, PPP, or custom for icon selection + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE)) + { + if (settings.debugGnssConfig == true) + if (gnss->gnssInBaseFixedMode() == false && gnss->gnssInBaseSurveyInMode() == false) + systemPrintln("Warning: Change to RTCM Base rates requested but not in Base mode."); + + if (gnss->setMessagesRTCMBase() == true) + { + gnssConfigureClear(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + setLoggingType(); // Update Standard, PPP, or custom for icon selection + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_MESSAGE_RATE_OTHER)) + { + // TODO - It is not clear where LG290P PQTM messages are being enabled + gnssConfigureClear(GNSS_CONFIG_MESSAGE_RATE_OTHER); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + setLoggingType(); // Update Standard, PPP, or custom for icon selection + } + + if (gnssConfigureRequested(GNSS_CONFIG_TILT)) + { + if (gnss->setTilt() == true) + { + gnssConfigureClear(GNSS_CONFIG_TILT); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } - // Check various setting arrays (message rates, etc) to see if they need to be reset to defaults - checkGNSSArrayDefaults(); + if (gnssConfigureRequested(GNSS_CONFIG_EXT_CORRECTIONS)) + { + if (gnss->setCorrRadioExtPort(settings.enableExtCorrRadio, true) == true) // Force the setting + { + gnssConfigureClear(GNSS_CONFIG_EXT_CORRECTIONS); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } + + if (gnssConfigureRequested(GNSS_CONFIG_LOGGING)) + { + if (gnss->setLogging() == true) + { + gnssConfigureClear(GNSS_CONFIG_LOGGING); + gnssConfigure(GNSS_CONFIG_SAVE); // Request receiver commit this change to NVM + } + } - // Configure the GNSS receiver - return configureGNSS(); + // Save changes to NVM + if (gnssConfigureRequested(GNSS_CONFIG_SAVE)) + { + if (gnss->saveConfiguration()) + gnssConfigureClear(GNSS_CONFIG_SAVE); + } + + if (gnssConfigureRequested(GNSS_CONFIG_RESET)) + { + if (gnss->reset()) + gnssConfigureClear(GNSS_CONFIG_RESET); + } + + // If gnssConfigureRequest bits are still set, the next update will attempt to service them. + + if (settings.gnssConfigureRequest != 0 && settings.debugGnssConfig) + { + systemPrint("Remaining gnssConfigureRequest: "); + + for (int x = 0; x < GNSS_CONFIG_MAX; x++) + { + if (gnssConfigureRequested(x)) + systemPrintf("%s ", gnssConfigDisplayNames[x]); + } + systemPrintln(); + } + + // settings.gnssConfigureRequest was likely changed. Record the current config state to ESP32 NVM + recordSystemSettings(); + } // end bluetoothCommandIsConnected(), inMainMenu, inWebConfigMode() } //---------------------------------------- -// Get the minimum satellite signal level for navigation. +// Verify the GNSS tables //---------------------------------------- -uint8_t GNSS::getMinCno() +void gnssVerifyTables() { - return (settings.minCNO); + if (gnssConfigStateEntries != GNSS_CONFIG_MAX) + reportFatalError("Fix gnssConfigStateEntries to match GNSS Config Enum"); } -//---------------------------------------- -float GNSS::getSurveyInStartingAccuracy() +// Given a bit to configure, set that bit in the overall bitfield +void gnssConfigure(uint8_t configureBit) { - return (settings.surveyInStartingAccuracy); + uint32_t mask = (1 << configureBit); + settings.gnssConfigureRequest |= mask; // Set the bit } -//---------------------------------------- -// Returns true if the antenna is shorted -//---------------------------------------- -bool GNSS::isAntennaShorted() +// Given a bit to configure, clear that bit from the overall bitfield +void gnssConfigureClear(uint8_t configureBit) { - return false; + uint32_t mask = (1 << configureBit); + + if (settings.debugGnssConfig && (settings.gnssConfigureRequest & mask)) + systemPrintf("GNSS Config Clear: %s\r\n", gnssConfigDisplayNames[configureBit]); + + settings.gnssConfigureRequest &= ~mask; // Clear the bit +} + +// Return true if a given bit is set +bool gnssConfigureRequested(uint8_t configureBit) +{ + uint32_t mask = (1 << configureBit); + + if (settings.debugGnssConfig && (settings.gnssConfigureRequest & mask)) + systemPrintf("GNSS Config Request: %s\r\n", gnssConfigDisplayNames[configureBit]); + + return (settings.gnssConfigureRequest & mask); +} + +// Set all bits in the request bitfield to cause the GNSS receiver to go through a full (re)configuration +void gnssConfigureDefaults() +{ + for (int x = 0; x < GNSS_CONFIG_MAX; x++) + gnssConfigure(x); + + // Clear request bits that do not need to be set after a factory reset + gnssConfigureClear(GNSS_CONFIG_BASE); + gnssConfigureClear(GNSS_CONFIG_BASE_SURVEY); + gnssConfigureClear(GNSS_CONFIG_BASE_FIXED); + gnssConfigureClear(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); + gnssConfigureClear(GNSS_CONFIG_RESET); +} + +// Returns true once all configuration requests are cleared +bool gnssConfigureComplete() +{ + if (settings.gnssConfigureRequest == 0) + return (true); + return (false); } //---------------------------------------- -// Returns true if the antenna is shorted +// Update the constellations following a set command //---------------------------------------- -bool GNSS::isAntennaOpen() +bool gnssCmdUpdateConstellations(const char *settingName, void *settingData, int settingType) { - return false; + gnssConfigure(GNSS_CONFIG_CONSTELLATION); // Request receiver to use new settings + + return (true); } //---------------------------------------- -// Set the minimum satellite signal level for navigation. +// Update the message rates following a set command //---------------------------------------- -bool GNSS::setMinCno(uint8_t cnoValue) +// TODO make RTCM and NMEA specific call backs +bool gnssCmdUpdateMessageRates(const char *settingName, void *settingData, int settingType) { - // Update the setting - settings.minCNO = cnoValue; - - // Pass the value to the GNSS receiver - return gnss->setMinCnoRadio(cnoValue); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings + return (true); } -// Antenna Short / Open detection -bool GNSS::supportsAntennaShortOpen() +//---------------------------------------- +// Update the PointPerfect service following a set command +//---------------------------------------- +// TODO move to PointPerfect once callback is in place +bool pointPerfectCmdUpdateServiceType(const char *settingName, void *settingData, int settingType) { - return false; + // Require a rover restart to enable / disable RTCM for PPL + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); + return (true); } // Periodically push GGA sentence over NTRIP Client, to Caster, if enabled @@ -114,13 +532,6 @@ void gnssDetectReceiverType() gnssBoot(); // Tell GNSS to run - // TODO remove after testing, force retest on each boot - // Note: with this in place, the X5 detection will take a lot longer due to the baud rate change -#ifdef FLEX_OVERRIDE - systemPrintln("<<<<<<<<<< !!!!!!!!!! FLEX FORCED !!!!!!!!!! >>>>>>>>>>"); - // settings.detectedGnssReceiver = GNSS_RECEIVER_UNKNOWN; // This may be causing weirdness on the LG290P. Commenting for now -#endif - // Start auto-detect if NVM is not yet set if (settings.detectedGnssReceiver == GNSS_RECEIVER_UNKNOWN) { @@ -129,7 +540,8 @@ void gnssDetectReceiverType() do { #ifdef COMPILE_LG290P - if (lg290pIsPresent() == true) + systemPrintln("Testing for LG290P"); + if (lg290pIsPresentOnFlex() == true) { systemPrintln("Auto-detected GNSS receiver: LG290P"); settings.detectedGnssReceiver = GNSS_RECEIVER_LG290P; @@ -141,6 +553,7 @@ void gnssDetectReceiverType() #endif // COMPILE_LGP290P #ifdef COMPILE_MOSAICX5 + systemPrintln("Testing for mosaic-X5"); if (mosaicIsPresentOnFlex() == true) // Note: this changes the COM1 baud from 115200 to 460800 { systemPrintln("Auto-detected GNSS receiver: mosaic-X5"); @@ -161,7 +574,7 @@ void gnssDetectReceiverType() gnss = (GNSS *)new GNSS_LG290P(); present.gnss_lg290p = true; - present.minCno = true; + present.minCN0 = true; present.minElevation = true; present.needsExternalPpl = true; // Uses the PointPerfect Library @@ -173,7 +586,7 @@ void gnssDetectReceiverType() gnss = (GNSS *)new GNSS_MOSAIC(); present.gnss_mosaicX5 = true; - present.minCno = true; + present.minCN0 = true; present.minElevation = true; present.dynamicModel = true; present.mosaicMicroSd = true; @@ -208,6 +621,8 @@ void gnssBoot() { digitalWrite(pin_GNSS_Reset, HIGH); // Tell LG290P to boot } + else + systemPrintln("Uncaught gnssBoot()"); } // Based on the platform, put the GNSS receiver into reset @@ -229,6 +644,8 @@ void gnssReset() { digitalWrite(pin_GNSS_Reset, LOW); // Tell LG290P to reset } + else + systemPrintln("Uncaught gnssReset()"); } //---------------------------------------- @@ -239,6 +656,7 @@ bool createGNSSPassthrough() { return createPassthrough("/updateGnssFirmware.txt"); } + bool createPassthrough(const char *filename) { if (online.fs == false) @@ -246,18 +664,21 @@ bool createPassthrough(const char *filename) if (LittleFS.exists(filename)) { - if (settings.debugGnss) + if (settings.debugGnssConfig) systemPrintf("LittleFS %s already exists\r\n", filename); return true; } - File updateUm980Firmware = LittleFS.open(filename, FILE_WRITE); - updateUm980Firmware.close(); + if (settings.debugGnssConfig) + systemPrintf("Creating passthrough file: %s \r\n", filename); + + File simpleFile = LittleFS.open(filename, FILE_WRITE); + simpleFile.close(); if (LittleFS.exists(filename)) return true; - if (settings.debugGnss) + if (settings.debugGnssConfig) systemPrintf("Unable to create %s on LittleFS\r\n", filename); return false; } @@ -392,46 +813,23 @@ bool gnssFirmwareCheckUpdateFile(const char *filename) //---------------------------------------- void gnssFirmwareRemoveUpdate() { - return gnssFirmwareRemoveUpdateFile("/updateGnssFirmware.txt"); + gnssFirmwareRemoveUpdateFile("/updateGnssFirmware.txt"); } + void gnssFirmwareRemoveUpdateFile(const char *filename) { if (online.fs == false) return; + if (settings.debugGnssConfig) + systemPrintf("Removing passthrough file: %s \r\n", filename); + + Serial.println("1"); if (LittleFS.exists(filename)) { - if (settings.debugGnss) - systemPrintf("Removing %s\r\n", filename); + Serial.println("2"); + delay(50); LittleFS.remove(filename); } } - -//---------------------------------------- -// Update the constellations following a set command -//---------------------------------------- -bool gnssCmdUpdateConstellations(int commandIndex) -{ - if (gnss == nullptr) - return false; - - //return gnss->setConstellations(); - // setConstellations() can take multiple seconds. Avoid calling during WebConfig - // as this can lead to >10 seconds required for parsing of the incoming settings blob - return true; -} - -//---------------------------------------- -// Update the message rates following a set command -//---------------------------------------- -bool gnssCmdUpdateMessageRates(int commandIndex) -{ - if (gnss == nullptr) - return false; - - //return gnss->setMessages(MAX_SET_MESSAGES_RETRIES); - return true; -} - -//---------------------------------------- diff --git a/Firmware/RTK_Everywhere/GNSS_LG290P.h b/Firmware/RTK_Everywhere/GNSS_LG290P.h index 541f1435b..70fb9692b 100644 --- a/Firmware/RTK_Everywhere/GNSS_LG290P.h +++ b/Firmware/RTK_Everywhere/GNSS_LG290P.h @@ -14,12 +14,7 @@ GNSS_LG290P.h // Constellations monitored/used for fix // Available constellations: GPS, BDS, GLO, GAL, QZSS, NavIC const char *lg290pConstellationNames[] = { - "GPS", - "GLONASS", - "Galileo", - "BeiDou", - "QZSS", - "NavIC", + "GPS", "GLONASS", "Galileo", "BeiDou", "QZSS", "NavIC", }; #define MAX_LG290P_CONSTELLATIONS (6) @@ -28,31 +23,22 @@ const char *lg290pConstellationNames[] = { // Each message will have the serial command and its default value typedef struct { - const char msgTextName[11]; - const float msgDefaultRate; - const uint8_t firmwareVersionSupported; // The minimum version this message is supported. - // 0 = all versions. - // 9999 = Not supported + const char msgTextName[11]; + const float msgDefaultRate; + const uint8_t firmwareVersionSupported; // The minimum version this message is supported. + // 0 = all versions. + // 9999 = Not supported } lg290pMsg; // Static array containing all the compatible messages // Rate = Output once every N position fix(es). const lg290pMsg lgMessagesNMEA[] = { - {"RMC", 1, 0}, - {"GGA", 1, 0}, - {"GSV", 1, 0}, - {"GSA", 1, 0}, - {"VTG", 1, 0}, - {"GLL", 1, 0}, - {"GBS", 0, 4}, - {"GNS", 0, 4}, - {"GST", 1, 4}, - {"ZDA", 0, 4}, + {"RMC", 1, 0}, {"GGA", 1, 0}, {"GSV", 1, 0}, {"GSA", 1, 0}, {"VTG", 1, 0}, + {"GLL", 1, 0}, {"GBS", 0, 4}, {"GNS", 0, 4}, {"GST", 1, 4}, {"ZDA", 0, 4}, }; const lg290pMsg lgMessagesRTCM[] = { - {"RTCM3-1005", 1, 0}, - {"RTCM3-1006", 0, 0}, + {"RTCM3-1005", 1, 0}, {"RTCM3-1006", 0, 0}, {"RTCM3-1019", 0, 0}, @@ -60,17 +46,10 @@ const lg290pMsg lgMessagesRTCM[] = { {"RTCM3-1033", 0, 4}, // v4 and above - {"RTCM3-1041", 0, 0}, - {"RTCM3-1042", 0, 0}, - {"RTCM3-1044", 0, 0}, - {"RTCM3-1046", 0, 0}, - - {"RTCM3-107X", 1, 0}, - {"RTCM3-108X", 1, 0}, - {"RTCM3-109X", 1, 0}, - {"RTCM3-111X", 1, 0}, - {"RTCM3-112X", 1, 0}, - {"RTCM3-113X", 1, 0}, + {"RTCM3-1041", 0, 0}, {"RTCM3-1042", 0, 0}, {"RTCM3-1044", 0, 0}, {"RTCM3-1046", 0, 0}, + + {"RTCM3-107X", 1, 0}, {"RTCM3-108X", 1, 0}, {"RTCM3-109X", 1, 0}, {"RTCM3-111X", 1, 0}, + {"RTCM3-112X", 1, 0}, {"RTCM3-113X", 1, 0}, }; // Quectel Proprietary messages @@ -85,64 +64,52 @@ const lg290pMsg lgMessagesPQTM[] = { enum lg290p_Models { - // LG290P does not have models - LG290P_DYN_MODEL_SURVEY = 0, - LG290P_DYN_MODEL_UAV, - LG290P_DYN_MODEL_AUTOMOTIVE, + // LG290P does not have models + LG290P_DYN_MODEL_SURVEY = 0, + LG290P_DYN_MODEL_UAV, + LG290P_DYN_MODEL_AUTOMOTIVE, }; class GNSS_LG290P : GNSS { -private: - LG290P *_lg290p; // Library class instance - -protected: - bool configureOnce(); - - // Setup the general configuration of the GNSS - // Not Rover or Base specific (ie, baud rates) - // Outputs: - // Returns true if successfully configured and false upon failure - bool configureGNSS(); - - // Turn on all the enabled NMEA messages on COM3 - bool enableNMEA(); + private: + LG290P *_lg290p; // Library class instance - // Turn on all the enabled RTCM Rover messages on COM3 - bool enableRTCMRover(); + protected: + bool configureOnce(); - // Turn on all the enabled RTCM Base messages on COM3 - bool enableRTCMBase(); + // Setup the general configuration of the GNSS + // Not Rover or Base specific (ie, baud rates) + // Outputs: + // Returns true if successfully configured and false upon failure + bool configure(); - uint8_t getActiveNmeaMessageCount(); + uint8_t getActiveNmeaMessageCount(); - // Given the name of an NMEA message, return the array number - uint8_t getNmeaMessageNumberByName(const char *msgName); + // Given the name of an NMEA message, return the array number + uint8_t getNmeaMessageNumberByName(const char *msgName); - // Given the name of an RTCM message, return the array number - uint8_t getRtcmMessageNumberByName(const char *msgName); + // Given the name of an RTCM message, return the array number + uint8_t getRtcmMessageNumberByName(const char *msgName); - // Return true if the GPGGA message is active - bool isGgaActive(); + // Return true if the GPGGA message is active + bool isGgaActive(); - // Given a sub type (ie "RTCM", "NMEA") present menu showing messages with this subtype - // Controls the messages that get broadcast over Bluetooth and logged (if enabled) - void menuMessagesSubtype(int *localMessageRate, const char *messageType); + // Given a sub type (ie "RTCM", "NMEA") present menu showing messages with this subtype + // Controls the messages that get broadcast over Bluetooth and logged (if enabled) + void menuMessagesSubtype(int *localMessageRate, const char *messageType); - // Set the minimum satellite signal level for navigation. - bool setMinCnoRadio(uint8_t cnoValue); + // Set the minimum satellite signal level for navigation. + bool setMinCN0(uint8_t cnoValue); - // Set all NMEA message report rates to one value - void setNmeaMessageRates(uint8_t msgRate); + // Given the name of a message, find it, and set the rate + bool setNmeaMessageRateByName(const char *msgName, uint8_t msgRate); - // Given the name of a message, find it, and set the rate - bool setNmeaMessageRateByName(const char *msgName, uint8_t msgRate); + // Set all RTCM Rover message report rates to one value + void setRtcmRoverMessageRates(uint8_t msgRate); - // Set all RTCM Rover message report rates to one value - void setRtcmRoverMessageRates(uint8_t msgRate); - - // Given the name of a message, find it, and set the rate - bool setRtcmRoverMessageRateByName(const char *msgName, uint8_t msgRate); + // Given the name of a message, find it, and set the rate + bool setRtcmRoverMessageRateByName(const char *msgName, uint8_t msgRate); public: // Constructor @@ -150,353 +117,356 @@ class GNSS_LG290P : GNSS { } - // If we have decryption keys, configure module - // Note: don't check online.lband_neo here. We could be using ip corrections - void applyPointPerfectKeys(); + // If we have decryption keys, configure module + // Note: don't check online.lband_neo here. We could be using ip corrections + void applyPointPerfectKeys(); + + // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) + void baseRtcmDefault(); + + // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) + void baseRtcmLowDataRate(); + + // Check if a given baud rate is supported by this module + bool baudIsAllowed(uint32_t baudRate); + uint32_t baudGetMinimum(); + uint32_t baudGetMaximum(); - // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) - void baseRtcmDefault(); + // Connect to GNSS and identify particulars + void begin(); - // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) - void baseRtcmLowDataRate(); + // Setup TM2 time stamp input as need + // Outputs: + // Returns true when an external event occurs and false if no event + bool beginExternalEvent(); - // Check if a given baud rate is supported by this module - bool baudIsAllowed(uint32_t baudRate); - uint32_t baudGetMinimum(); - uint32_t baudGetMaximum(); + // Setup the PPS pin for PPS LED + bool setPPS(); - // Connect to GNSS and identify particulars - void begin(); + bool checkNMEARates(); - // Setup TM2 time stamp input as need - // Outputs: - // Returns true when an external event occurs and false if no event - bool beginExternalEvent(); + bool checkPPPRates(); - // Setup the timepulse output on the PPS pin for external triggering - // Outputs - // Returns true if the pin was successfully setup and false upon - // failure - bool beginPPS(); + // Configure the Base + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureBase(); - bool checkNMEARates(); + // Configure specific aspects of the receiver for NTP mode + bool configureNtpMode(); - bool checkPPPRates(); + // Configure the Rover + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureRover(); - // Configure the Base - // Outputs: - // Returns true if successfully configured and false upon failure - bool configureBase(); + // Responds with the messages supported on this platform + // Inputs: + // returnText: String to receive message names + // Returns message names in the returnText string + void createMessageList(String &returnText); - // Configure specific aspects of the receiver for NTP mode - bool configureNtpMode(); + // Responds with the RTCM/Base messages supported on this platform + // Inputs: + // returnText: String to receive message names + // Returns message names in the returnText string + void createMessageListBase(String &returnText); - // Configure the Rover - // Outputs: - // Returns true if successfully configured and false upon failure - bool configureRover(); + void debuggingDisable(); - // Responds with the messages supported on this platform - // Inputs: - // returnText: String to receive message names - // Returns message names in the returnText string - void createMessageList(String &returnText); + void debuggingEnable(); - // Responds with the RTCM/Base messages supported on this platform - // Inputs: - // returnText: String to receive message names - // Returns message names in the returnText string - void createMessageListBase(String &returnText); + bool disableSurveyIn(bool saveAndReset); - void debuggingDisable(); + bool enterConfigMode(unsigned long waitForSemaphoreTimeout_millis); - void debuggingEnable(); + bool exitConfigMode(); - bool disableSurveyIn(bool saveAndReset); + // Restore the GNSS to the factory settings + void factoryReset(); - void enableGgaForNtrip(); + uint16_t fileBufferAvailable(); - // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted - // even if there is no GPS fix. We use it to test serial output. - // Outputs: - // Returns true if successfully started and false upon failure - bool enableRTCMTest(); + uint16_t fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead); - bool enterConfigMode(unsigned long waitForSemaphoreTimeout_millis); + // Start the base using fixed coordinates + // Outputs: + // Returns true if successfully started and false upon failure + bool fixedBaseStart(); - bool exitConfigMode(); + bool fixRateIsAllowed(uint32_t fixRateMs); - // Restore the GNSS to the factory settings - void factoryReset(); + // Return min/max rate in ms + uint32_t fixRateGetMinimumMs(); - uint16_t fileBufferAvailable(); + uint32_t fixRateGetMaximumMs(); - uint16_t fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead); + // Return the number of active/enabled messages + uint8_t getActiveMessageCount(); - // Start the base using fixed coordinates - // Outputs: - // Returns true if successfully started and false upon failure - bool fixedBaseStart(); + // Return the number of active/enabled RTCM messages + uint8_t getActiveRtcmMessageCount(); - // Return the number of active/enabled messages - uint8_t getActiveMessageCount(); + // Get the altitude + // Outputs: + // Returns the altitude in meters or zero if the GNSS is offline + double getAltitude(); - // Return the number of active/enabled RTCM messages - uint8_t getActiveRtcmMessageCount(); + uint32_t getBaudRate(uint8_t uartNumber); - // Get the altitude - // Outputs: - // Returns the altitude in meters or zero if the GNSS is offline - double getAltitude(); + // Returns the carrier solution or zero if not online + uint8_t getCarrierSolution(); - uint32_t getBaudRate(uint8_t uartNumber); + uint32_t getCommBaudRate(); - // Returns the carrier solution or zero if not online - uint8_t getCarrierSolution(); + uint32_t getDataBaudRate(); - uint32_t getCommBaudRate(); + // Returns the day number or zero if not online + uint8_t getDay(); - uint32_t getDataBaudRate(); + // Return the number of milliseconds since GNSS data was last updated + uint16_t getFixAgeMilliseconds(); - // Returns the day number or zero if not online - uint8_t getDay(); + // Returns the fix type or zero if not online + uint8_t getFixType(); - // Return the number of milliseconds since GNSS data was last updated - uint16_t getFixAgeMilliseconds(); + // Returns the hours of 24 hour clock or zero if not online + uint8_t getHour(); - // Returns the fix type or zero if not online - uint8_t getFixType(); + // Get the horizontal position accuracy + // Outputs: + // Returns the horizontal position accuracy or zero if offline + float getHorizontalAccuracy(); - // Returns the hours of 24 hour clock or zero if not online - uint8_t getHour(); + const char *getId(); - // Get the horizontal position accuracy - // Outputs: - // Returns the horizontal position accuracy or zero if offline - float getHorizontalAccuracy(); + // Get the latitude value + // Outputs: + // Returns the latitude value or zero if not online + double getLatitude(); - const char *getId(); + // Query GNSS for current leap seconds + uint8_t getLeapSeconds(); - // Get the latitude value - // Outputs: - // Returns the latitude value or zero if not online - double getLatitude(); + // Return the type of logging that matches the enabled messages - drives the logging icon + uint8_t getLoggingType(); - // Query GNSS for current leap seconds - uint8_t getLeapSeconds(); + // Get the longitude value + // Outputs: + // Returns the longitude value or zero if not online + double getLongitude(); - // Return the type of logging that matches the enabled messages - drives the logging icon - uint8_t getLoggingType(); + // Returns two digits of milliseconds or zero if not online + uint8_t getMillisecond(); - // Get the longitude value - // Outputs: - // Returns the longitude value or zero if not online - double getLongitude(); + // Returns minutes or zero if not online + uint8_t getMinute(); - // Returns two digits of milliseconds or zero if not online - uint8_t getMillisecond(); + // Returns 0 - Unknown, 1 - Rover, 2 - Base + uint8_t getMode(); - // Get the minimum satellite signal level for navigation. - uint8_t getMinCno(); + // Returns month number or zero if not online + uint8_t getMonth(); - // Returns minutes or zero if not online - uint8_t getMinute(); + // Returns nanoseconds or zero if not online + uint32_t getNanosecond(); - // Returns 0 - Unknown, 1 - Rover, 2 - Base - uint8_t getMode(); + uint32_t getRadioBaudRate(); - // Returns month number or zero if not online - uint8_t getMonth(); + // Returns the seconds between solutions + double getRateS(); - // Returns nanoseconds or zero if not online - uint32_t getNanosecond(); + const char *getRtcmDefaultString(); - uint32_t getRadioBaudRate(); + const char *getRtcmLowDataRateString(); - // Returns the seconds between solutions - double getRateS(); + // Returns the number of satellites in view or zero if offline + uint8_t getSatellitesInView(); - const char *getRtcmDefaultString(); + // Returns seconds or zero if not online + uint8_t getSecond(); - const char *getRtcmLowDataRateString(); + // Get the survey-in mean accuracy + // Outputs: + // Returns the mean accuracy or zero (0) + float getSurveyInMeanAccuracy(); - // Returns the number of satellites in view or zero if offline - uint8_t getSatellitesInView(); + uint8_t getSurveyInMode(); - // Returns seconds or zero if not online - uint8_t getSecond(); + // Return the number of seconds the survey-in process has been running + int getSurveyInObservationTime(); - // Get the survey-in mean accuracy - // Outputs: - // Returns the mean accuracy or zero (0) - float getSurveyInMeanAccuracy(); + // Returns timing accuracy or zero if not online + uint32_t getTimeAccuracy(); - uint8_t getSurveyInMode(); + // Returns full year, ie 2023, not 23. + uint16_t getYear(); - // Return the number of seconds the survey-in process has been running - int getSurveyInObservationTime(); + // Helper functions for the current mode as read from the GNSS receiver + bool gnssInBaseFixedMode(); + bool gnssInBaseSurveyInMode(); + bool gnssInRoverMode(); - float getSurveyInStartingAccuracy(); + bool isBlocking(); - // Returns timing accuracy or zero if not online - uint32_t getTimeAccuracy(); + // Date is confirmed once we have GNSS fix + bool isConfirmedDate(); - // Returns full year, ie 2023, not 23. - uint16_t getYear(); + // Date is confirmed once we have GNSS fix + bool isConfirmedTime(); - // Returns true if the device is in Rover mode - // Currently the only two modes are Rover or Base - bool inRoverMode(); + // Returns true if data is arriving on the Radio Ext port + bool isCorrRadioExtPortActive(); - bool isBlocking(); + // Return true if GNSS receiver has a higher quality DGPS fix than 3D + bool isDgpsFixed(); - // Date is confirmed once we have GNSS fix - bool isConfirmedDate(); + // Some functions (L-Band area frequency determination) merely need + // to know if we have a valid fix, not what type of fix + // This function checks to see if the given platform has reached + // sufficient fix type to be considered valid + bool isFixed(); - // Date is confirmed once we have GNSS fix - bool isConfirmedTime(); + // Used in tpISR() for time pulse synchronization + bool isFullyResolved(); - // Returns true if data is arriving on the Radio Ext port - bool isCorrRadioExtPortActive(); + bool isPppConverged(); - // Return true if GNSS receiver has a higher quality DGPS fix than 3D - bool isDgpsFixed(); + bool isPppConverging(); - // Some functions (L-Band area frequency determination) merely need - // to know if we have a valid fix, not what type of fix - // This function checks to see if the given platform has reached - // sufficient fix type to be considered valid - bool isFixed(); + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Fix. This function checks to see if the + // given platform has reached sufficient fix type to be considered valid + bool isRTKFix(); - // Used in tpISR() for time pulse synchronization - bool isFullyResolved(); + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Float. This function checks to see if + // the given platform has reached sufficient fix type to be considered + // valid + bool isRTKFloat(); - bool isPppConverged(); + // Determine if the survey-in operation is complete + // Outputs: + // Returns true if the survey-in operation is complete and false + // if the operation is still running + bool isSurveyInComplete(); - bool isPppConverging(); + // Date will be valid if the RTC is reporting (regardless of GNSS fix) + bool isValidDate(); - // Some functions (L-Band area frequency determination) merely need - // to know if we have an RTK Fix. This function checks to see if the - // given platform has reached sufficient fix type to be considered valid - bool isRTKFix(); + // Time will be valid if the RTC is reporting (regardless of GNSS fix) + bool isValidTime(); - // Some functions (L-Band area frequency determination) merely need - // to know if we have an RTK Float. This function checks to see if - // the given platform has reached sufficient fix type to be considered - // valid - bool isRTKFloat(); + // Controls the constellations that are used to generate a fix and logged + void menuConstellations(); - // Determine if the survey-in operation is complete - // Outputs: - // Returns true if the survey-in operation is complete and false - // if the operation is still running - bool isSurveyInComplete(); + void menuMessageBaseRtcm(); - // Date will be valid if the RTC is reporting (regardless of GNSS fix) - bool isValidDate(); + // Control the messages that get broadcast over Bluetooth and logged (if enabled) + void menuMessages(); - // Time will be valid if the RTC is reporting (regardless of GNSS fix) - bool isValidTime(); + // Print the module type and firmware version + void printModuleInfo(); - // Controls the constellations that are used to generate a fix and logged - void menuConstellations(); + // Send correction data to the GNSS + // Inputs: + // dataToSend: Address of a buffer containing the data + // dataLength: The number of valid data bytes in the buffer + // Outputs: + // Returns the number of correction data bytes written + int pushRawData(uint8_t *dataToSend, int dataLength); - void menuMessageBaseRtcm(); + // Hardware or software reset the GNSS + bool reset(); - // Control the messages that get broadcast over Bluetooth and logged (if enabled) - void menuMessages(); + uint16_t rtcmBufferAvailable(); - // Print the module type and firmware version - void printModuleInfo(); + // If LBand is being used, ignore any RTCM that may come in from the GNSS + void rtcmOnGnssDisable(); - // Send correction data to the GNSS - // Inputs: - // dataToSend: Address of a buffer containing the data - // dataLength: The number of valid data bytes in the buffer - // Outputs: - // Returns the number of correction data bytes written - int pushRawData(uint8_t *dataToSend, int dataLength); + // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver + void rtcmOnGnssEnable(); - uint16_t rtcmBufferAvailable(); + uint16_t rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead); - // If LBand is being used, ignore any RTCM that may come in from the GNSS - void rtcmOnGnssDisable(); + // Save the current configuration + // Outputs: + // Returns true when the configuration was saved and false upon failure + bool saveConfiguration(); - // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver - void rtcmOnGnssEnable(); + bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); - uint16_t rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead); + bool setBaudRateComm(uint32_t baudRate); - // Save the current configuration - // Outputs: - // Returns true when the configuration was saved and false upon failure - bool saveConfiguration(); + bool setBaudRateData(uint32_t baudRate); - bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); + bool setBaudRateRadio(uint32_t baudRate); - bool setCommBaudrate(uint32_t baud); + // Enable all the valid constellations and bands for this platform + bool setConstellations(); - // Enable all the valid constellations and bands for this platform - bool setConstellations(); + // Enable / disable corrections protocol(s) on the Radio External port + // Always update if force is true. Otherwise, only update if enable has changed state + bool setCorrRadioExtPort(bool enable, bool force); - // Enable / disable corrections protocol(s) on the Radio External port - // Always update if force is true. Otherwise, only update if enable has changed state - bool setCorrRadioExtPort(bool enable, bool force); + // Set the elevation in degrees + // Inputs: + // elevationDegrees: The elevation value in degrees + bool setElevation(uint8_t elevationDegrees); - bool setDataBaudRate(uint32_t baud); + bool setHighAccuracyService(bool enableGalileoHas, const char *configurePPP); - // Set the elevation in degrees - // Inputs: - // elevationDegrees: The elevation value in degrees - bool setElevation(uint8_t elevationDegrees); + bool setHighAccuracyService(bool enableGalileoHas); - bool setHighAccuracyService(bool enableGalileoHas, const char *configurePPP); + // Configure any logging settings - currently mosaic-X5 specific + bool setLogging(); - // Enable all the valid messages for this platform - bool setMessages(int maxRetries); + // Set the NMEA messages + bool setMessagesNMEA(); - // Enable all the valid messages for this platform over the USB port - bool setMessagesUsb(int maxRetries); + // Set then RTCM Base messages + bool setMessagesRTCMBase(); - // Set the dynamic model to use for RTK - // Inputs: - // modelNumber: Number of the model to use, provided by radio library - bool setModel(uint8_t modelNumber); + // Set the RTCM Rover messages + bool setMessagesRTCMRover(); - bool setRadioBaudRate(uint32_t baud); + // Set the dynamic model to use for RTK + // Inputs: + // modelNumber: Number of the model to use, provided by radio library + bool setModel(uint8_t modelNumber); - // Specify the interval between solutions - // Inputs: - // secondsBetweenSolutions: Number of seconds between solutions - // Outputs: - // Returns true if the rate was successfully set and false upon - // failure - bool setRate(double secondsBetweenSolutions); + bool setMultipathMitigation(bool enableMultipathMitigation); - bool setTalkerGNGGA(); + // Specify the interval between solutions + // Inputs: + // secondsBetweenSolutions: Number of seconds between solutions + // Outputs: + // Returns true if the rate was successfully set and false upon + // failure + bool setRate(double secondsBetweenSolutions); - // Hotstart GNSS to try to get RTK lock - bool softwareReset(); + // Enable/disable any output needed for tilt compensation + bool setTilt(); - bool standby(); + bool standby(); - // Reset the survey-in operation - // Outputs: - // Returns true if the survey-in operation was reset successfully - // and false upon failure - bool surveyInReset(); + // Reset the survey-in operation + // Outputs: + // Returns true if the survey-in operation was reset successfully + // and false upon failure + bool surveyInReset(); - // Start the survey-in operation - // Outputs: - // Return true if successful and false upon failure - bool surveyInStart(); + // Start the survey-in operation + // Outputs: + // Return true if successful and false upon failure + bool surveyInStart(); - // If we have received serial data from the LG290P outside of the library (ie, from processUart1Message task) - // we can pass data back into the LG290P library to allow it to update its own variables - void lg290pUpdate(uint8_t *incomingBuffer, int bufferLength); + // If we have received serial data from the LG290P outside of the library (ie, from processUart1Message task) + // we can pass data back into the LG290P library to allow it to update its own variables + void lg290pUpdate(uint8_t *incomingBuffer, int bufferLength); - // Poll routine to update the GNSS state - void update(); + // Poll routine to update the GNSS state + void update(); }; #endif // COMPILE_LG290P diff --git a/Firmware/RTK_Everywhere/GNSS_LG290P.ino b/Firmware/RTK_Everywhere/GNSS_LG290P.ino index 4c44e68ae..6d547d4a3 100644 --- a/Firmware/RTK_Everywhere/GNSS_LG290P.ino +++ b/Firmware/RTK_Everywhere/GNSS_LG290P.ino @@ -78,10 +78,6 @@ void GNSS_LG290P::begin() // Instantiate the library _lg290p = new LG290P(); - // // Turn on/off debug messages - if (settings.debugGnss) - debuggingEnable(); - if (_lg290p->begin(*serialGNSS) == false) // Give the serial port over to the library { if (settings.debugGnss) @@ -96,10 +92,21 @@ void GNSS_LG290P::begin() return; } } - systemPrintln("GNSS LG290P online"); online.gnss = true; + systemPrintln("GNSS LG290P online"); + + // Turn on debug messages if needed + if (settings.debugGnss) + debuggingEnable(); + + // Check baud settings. LG290P has a limited number of allowable bauds + if (baudIsAllowed(settings.dataPortBaud) == false) + settings.dataPortBaud = 460800; + if (baudIsAllowed(settings.radioPortBaud) == false) + settings.radioPortBaud = 115200; + // Check firmware version and print info _lg290p->getFirmwareVersion(lg290pFirmwareVersion); // Needs LG290P library v1.0.7 @@ -138,7 +145,7 @@ void GNSS_LG290P::begin() { // Supported starting in v05 present.minElevation = true; - present.minCno = true; + present.minCN0 = true; } // Determine if PPP temp firmware is detected. @@ -175,10 +182,13 @@ bool GNSS_LG290P::beginExternalEvent() //---------------------------------------- // Setup the timepulse output on the PPS pin for external triggering //---------------------------------------- -bool GNSS_LG290P::beginPPS() +bool GNSS_LG290P::setPPS() { - // TODO LG290P - return (false); + bool response = _lg290p->setPPS(200, false, true); // duration time ms, alwaysOutput, polarity + if (settings.debugGnssConfig && response == false) + systemPrintln("setPPS failed"); + + return (response); } //---------------------------------------- @@ -194,255 +204,11 @@ bool GNSS_LG290P::checkPPPRates() } //---------------------------------------- -// Setup the GNSS module for any setup (base or rover) -// In general we check if the setting is different than setting stored in NVM before writing it. -//---------------------------------------- -bool GNSS_LG290P::configureGNSS() -{ - for (int x = 0; x < 3; x++) - { - // Wait up to 5 seconds for device to come online - for (int x = 0; x < 5; x++) - { - if (_lg290p->isConnected()) - break; - else - systemPrintln("Device still rebooting"); - delay(1000); // Wait for device to reboot - } - - if (configureOnce()) - return (true); - - // If we fail, reset LG290P - systemPrintln("Resetting LG290P to complete configuration"); - - gnssReset(); - delay(500); - gnssBoot(); - } - - systemPrintln("LG290P failed to configure"); - return (false); -} - -//---------------------------------------- -// Configure the basic LG290P settings (rover/base agnostic) -//---------------------------------------- -bool GNSS_LG290P::configureOnce() -{ - /* - Disable all message traffic - Set COM port baud rates, - LG290P COM1 - DATA / Direct to USB, 115200 - LG290P COM2 - BT and GNSS config. Configured for 115200 from begin(). - LG290P COM3 - RADIO / Direct output to locking JST connector. - Set minCNO - Set elevationAngle - Set Constellations - Set messages - Enable selected NMEA messages on COM2 - Enable selected RTCM messages on COM2 -*/ - - if (settings.gnssConfiguredOnce) - { - systemPrintln("LG290P configuration maintained"); - return (true); - } - - if (settings.debugGnss) - debuggingEnable(); // Print all debug to Serial - - serialGNSS->flush(); // Remove any incoming characters - - bool response = true; - - uint8_t retries = 4; - - while ((retries > 0) && (!enterConfigMode(500))) - { - online.gnss = true; // Mark online so enterConfigMode can re-enter - retries--; - systemPrintf("configureOnce: Enter config mode failed. %d retries remaining\r\n", retries); - } - - response &= (retries > 0); - if (settings.debugGnss && response == false) - systemPrintln("configureOnce: Enter config mode failed"); - - // Check baud settings. LG290P has a limited number of allowable bauds - if (baudIsAllowed(settings.dataPortBaud) == false) - settings.dataPortBaud = 460800; - if (baudIsAllowed(settings.radioPortBaud) == false) - settings.radioPortBaud = 115200; - - // Set the baud rate for the three UARTs - if (response == true) - { - response &= setDataBaudRate(settings.dataPortBaud); // If available, set baud of DATA port - - // The following setCommBaudrate() is redundant because to get this far, the comm interface must already be - // working response &= setCommBaudrate(115200 * 4); // Set baud for main comm channel - - response &= setRadioBaudRate(settings.radioPortBaud); // If available, set baud of RADIO port - - if (response == false && settings.debugGnss) - systemPrintln("configureOnce: setBauds failed."); - } - - // Enable PPS signal with a width of 200ms - if (response == true) - { - response &= _lg290p->setPPS(200, false, true); // duration time ms, alwaysOutput, polarity - if (settings.debugGnss && response == false) - systemPrintln("configureOnce: setPPS failed"); - } - - if (response == true) - { - response &= setConstellations(); - if (settings.debugGnss && response == false) - systemPrintln("configureOnce: setConstellations failed"); - } - - // We do not set Rover or fix rate here because fix rate only applies in rover mode. - - response &= exitConfigMode(); // We must exit config before we save otherwise we will save with NMEA/RTCM off - - if (response) - { - online.gnss = true; // If we failed before, mark as online now - - systemPrintln("LG290P configuration updated"); - - // Save the current configuration into non-volatile memory (NVM) - response &= saveConfiguration(); - } - else - online.gnss = false; // Take it offline - - settings.gnssConfiguredOnce = response; - - return (response); -} - -//---------------------------------------- -// Configure specific aspects of the receiver for rover mode +// begin() has already established communication. There are no one-time config requirements for the LG290P //---------------------------------------- -bool GNSS_LG290P::configureRover() +bool GNSS_LG290P::configure() { - /* - Set mode to Rover - Enable RTCM messages on UART1/2/3 - Enable NMEA on UART1/2/3 - */ - if (online.gnss == false) - { - systemPrintln("GNSS not online"); - return (false); - } - - // If our settings haven't changed, trust GNSS's settings - if (settings.gnssConfiguredRover) - { - systemPrintln("Skipping LG290P Rover configuration"); - return (true); - } - - bool response = true; - - serialGNSS->flush(); // Remove any incoming characters - - uint8_t retries = 4; - - while ((retries > 0) && (!enterConfigMode(500))) - { - retries--; - systemPrintf("configureRover: Enter config mode failed. %d retries remaining\r\n", retries); - } - - response &= (retries > 0); - if (settings.debugGnss && response == false) - systemPrintln("configureRover: Enter config mode failed"); - - // We must force receiver into Rover mode so that we can set fix rate - int currentMode = getMode(); - if (currentMode != 1) // 0 - Unknown, 1 - Rover, 2 - Base - { - // response &= _lg290p->setModeRover(); // Wait for save and reset - // Ignore result for now. enterConfigMode disables NMEA, which causes the built-in write/save/reset methods to - // fail because NMEA is not present. - _lg290p->setModeRover(); // Wait for save and reset - if (settings.debugGnss && response == false) - systemPrintln("configureRover: Set mode rover failed"); - } - - response &= setElevation(settings.minElev); - - response &= setMinCnoRadio(settings.minCNO); - - // If we are on a platform that supports tilt - if (present.tiltPossible == true) - { - // And tilt is present and enabled - if (present.imu_im19 == true && settings.enableTiltCompensation == true) - { - // Configure GNSS to support the tilt sensor - - // Tilt sensor requires 5Hz at a minimum - if (settings.measurementRateMs > 200) - { - systemPrintln("Increasing GNSS measurement rate to 5Hz for tilt support"); - settings.measurementRateMs = 200; - } - - // On the LG290P Flex module, UART 3 of the GNSS is connected to the IMU UART 1 - response &= setBaudRate(3, 115200); - - if (response == false && settings.debugGnss) - systemPrintln("configureRover: setBaud failed."); - - // Enable of GGA, RMC, GST for tilt sensor is done in enableNMEA() - } - } - - // Set the fix rate. Default on LG290P is 10Hz so set accordingly. - response &= setRate(settings.measurementRateMs / 1000.0); // May require save/reset - if (settings.debugGnss && response == false) - systemPrintln("configureRover: Set rate failed"); - - response &= setHighAccuracyService(settings.enableGalileoHas, (const char *)settings.configurePPP); - - response &= enableRTCMRover(); - if (settings.debugGnss && response == false) - systemPrintln("configureRover: Enable RTCM failed"); - - response &= enableNMEA(); - if (settings.debugGnss && response == false) - systemPrintln("configureRover: Enable NMEA failed"); - - response &= exitConfigMode(); // We must exit config before we save otherwise we will save with NMEA/RTCM off - - if (response == false) - { - systemPrintln("LG290P Rover failed to configure"); - } - else - { - // Save the current configuration into non-volatile memory (NVM) - response &= saveConfiguration(); - - // For RTCM and MSM messages to take effect (ie, PointPerfect is active) we must save/reset - softwareReset(); - - if (settings.debugGnss && response) - systemPrintln("LG290P Rover configured"); - } - - settings.gnssConfiguredRover = response; - - return (response); + return (true); } //---------------------------------------- @@ -450,46 +216,23 @@ bool GNSS_LG290P::configureRover() //---------------------------------------- bool GNSS_LG290P::configureBase() { - /* - Disable all messages - Enable RTCM Base messages - Enable NMEA messages - */ - - if (online.gnss == false) - { - systemPrintln("GNSS not online"); - return (false); - } + bool response = true; - // If the device is set to Survey-In, we must allow the device to be configured. - // Otherwise PQTMEPE (estimated position error) is never populated, so the survey - // never starts (Waiting for Horz Accuracy < 2.00m...) - if (settings.fixedBase == false) // Not a fixed base = Survey-in + if (settings.fixedBase == false && gnssInBaseSurveyInMode()) { - if (settings.gnssConfiguredBase) - { - if (settings.debugGnss) - systemPrintln("Skipping LG290P Base configuration"); - return true; - } + if (settings.debugGnssConfig) + systemPrintln("Skipping - LG290P is already in Survey-In Base configuration"); + return (true); // No changes needed } - bool response = true; - - serialGNSS->flush(); // Remove any incoming characters - - uint8_t retries = 4; - - while ((retries > 0) && (!enterConfigMode(500))) + if (settings.fixedBase == true && gnssInBaseFixedMode()) { - retries--; - systemPrintf("configureBase: Enter config mode failed. %d retries remaining\r\n", retries); + if (settings.debugGnssConfig) + systemPrintln("Skipping - LG290P is already in Fixed Base configuration"); + return (true); // No changes needed } - response &= (retries > 0); - if (settings.debugGnss && response == false) - systemPrintln("configureBase: Enter config mode failed"); + // Assume we are changing from Rover to Base, request any additional config changes // "When set to Base Station mode, the receiver will automatically disable NMEA message output and enable RTCM MSM4 // and RTCM3-1005 message output." @@ -499,67 +242,70 @@ bool GNSS_LG290P::configureBase() // We set base mode here because we don't want to reset the module right before we (potentially) start a survey-in // We wait for States.ino to change us from base settle, to survey started, at which time the surveyInStart() is // issued. - int currentMode = getMode(); - if (currentMode != 2) // 0 - Unknown, 1 - Rover, 2 - Base + if (gnssInRoverMode() == true) // 0 - Unknown, 1 - Rover, 2 - Base { - // response &= _lg290p->setModeBase(); // Wait for save and reset - // Ignore result for now. enterConfigMode disables NMEA, which causes the built-in write/save/reset methods to - // fail because NMEA is not present. - _lg290p->setModeBase(false); // Don't save and reset - if (settings.debugGnss && response == false) + response &= _lg290p->setModeBase(false); // Don't save and reset + if (settings.debugGnssConfig && response == false) systemPrintln("configureBase: Set mode base failed"); - - // Device should now have survey mode disabled } - // If we are in survey mode, then disable it - uint8_t surveyInMode = getSurveyInMode(); // 0 - Disabled, 1 - Survey-in mode, 2 - Fixed mode - if (surveyInMode == 1 || surveyInMode == 2) + // Switching to Base Mode should disable any currently running survey-in + // But if we were already in base mode, then disable any currently running survey-in + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) { - // response &= disableSurveyIn(); // Wait for save and reset - // Ignore result for now. enterConfigMode disables NMEA, which causes the built-in write/save/reset methods to - // fail because NMEA is not present. - disableSurveyIn(false); // Don't save and reset - if (settings.debugGnss && response == false) + response &= disableSurveyIn(false); // Don't save and reset + if (settings.debugGnssConfig && response == false) systemPrintln("configureBase: disable survey in failed"); } - response &= setElevation(settings.minElev); - - response &= setMinCnoRadio(settings.minCNO); - - response &= setHighAccuracyService(settings.enableGalileoHas, (const char *)settings.configurePPP); - - response &= enableRTCMBase(); // Set RTCM messages - if (settings.debugGnss && response == false) - systemPrintln("configureBase: Enable RTCM failed"); - - response &= enableNMEA(); // Set NMEA messages - if (settings.debugGnss && response == false) - systemPrintln("configureBase: Enable NMEA failed"); - - response &= exitConfigMode(); // We must exit config before we save otherwise we will save with NMEA/RTCM off - if (response == false) { systemPrintln("LG290P Base failed to configure"); } else { - // Save the current configuration into non-volatile memory (NVM) + // When switching between modes, we have to do a save, reset, then + // enable messages. Because of how GNSS.ino works, we force the + // save/reset here. response &= saveConfiguration(); - softwareReset(); + reset(); + + // When a device is changed from Rover to Base, NMEA messages are disabled. Turn them back on. + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); - // When a device is changed from Rover to Base, NMEA messages settings do not survive PQTMSAVEPAR - // Re-enable NMEA post reset - response &= enableNMEA(); // Set NMEA messages + // In Survey-In mode, configuring the RTCM Base will trigger a print warning because the survey-in + // takes a few seconds to start during which gnssInBaseSurveyInMode() incorrectly reports false. + // The print warning should be ignored. + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); - if (settings.debugGnss && response) + if (settings.debugGnssConfig && response) systemPrintln("LG290P Base configured"); } - settings.gnssConfiguredBase = response; + return (response); +} + +//---------------------------------------- +// Configure specific aspects of the receiver for rover mode +//---------------------------------------- +bool GNSS_LG290P::configureRover() +{ + if (gnssInRoverMode()) // 0 - Unknown, 1 - Rover, 2 - Base + { + if (settings.debugGnssConfig) + systemPrintln("Skipping Rover configuration"); + return (true); // No changes needed + } + + // Assume we are changing from Base to Rover, request any additional config changes + + bool response = _lg290p->setModeRover(false); // Don't wait for save and reset + // Setting mode to rover should disable any survey-in + + gnssConfigure(GNSS_CONFIG_FIX_RATE); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); + gnssConfigure(GNSS_CONFIG_RESET); // Mode change requires reset return (response); } @@ -617,60 +363,30 @@ void GNSS_LG290P::createMessageListBase(String &returnText) //---------------------------------------- uint8_t GNSS_LG290P::getSurveyInMode() { - if (online.gnss) - return (_lg290p->getSurveyMode()); - return (false); -} - -//---------------------------------------- -// Configure specific aspects of the receiver for NTP mode -//---------------------------------------- -bool GNSS_LG290P::configureNtpMode() -{ - return false; -} + // Note: _lg290p->getSurveyMode() returns 0 while a survey-in is *running* + // so we check PQTMSVINSTATUS to see if a survey is in progress. -//---------------------------------------- -// Disable NMEA and RTCM on UART2 to reduce the serial traffic -//---------------------------------------- -bool GNSS_LG290P::enterConfigMode(unsigned long waitForSemaphoreTimeout_millis) -{ if (online.gnss) { - unsigned long start = millis(); - bool isBlocking; - do - { // Wait for up to waitForSemaphoreTimeout for library to stop blocking - isBlocking = _lg290p->isBlocking(); - } while (isBlocking && ((millis() - start) < waitForSemaphoreTimeout_millis)); - - // This will fail if the library is still blocking, but it is worth a punt... - - if (lg290pFirmwareVersion >= 6) // See #747 - // Disable NMEA and RTCM on the LG290P UART2, but leave the undocumented Bit 1 enabled - return (_lg290p->sendOkCommand("$PQTMCFGPROT", ",W,1,2,00000007,00000002")); - - // Disable NMEA and RTCM on the LG290P UART2 - return (_lg290p->sendOkCommand("$PQTMCFGPROT", ",W,1,2,00000000,00000000")); + if (_lg290p->getSurveyMode() == 2) + return (2); // We know we are fixed + else + { + // Determine if a survey is running + int surveyStatus = _lg290p->getSurveyInStatus(); // 0 = Invalid, 1 = In-progress, 2 = Valid + if (surveyStatus == 1 || surveyStatus == 2) + return (1); // We're in survey mode + } } - return (false); + return (0); } //---------------------------------------- -// Enable NMEA and RTCM on UART2 +// Configure specific aspects of the receiver for NTP mode //---------------------------------------- -bool GNSS_LG290P::exitConfigMode() +bool GNSS_LG290P::configureNtpMode() { - if (online.gnss) - { - if (lg290pFirmwareVersion >= 6) // See #747 - // Enable NMEA and RTCM on the LG290P UART2, plus the undocumented Bit 1 - return (_lg290p->sendOkCommand("$PQTMCFGPROT", ",W,1,2,00000007,00000007")); - - // Enable NMEA and RTCM on the LG290P UART2 - return (_lg290p->sendOkCommand("$PQTMCFGPROT", ",W,1,2,00000005,00000005")); - } - return (false); + return false; } //---------------------------------------- @@ -690,7 +406,7 @@ bool GNSS_LG290P::disableSurveyIn(bool saveAndReset) response &= saveConfiguration(); if (settings.debugGnss && response == false) systemPrintln("disableSurveyIn: save failed"); - response &= softwareReset(); + response &= reset(); if (settings.debugGnss && response == false) systemPrintln("disableSurveyIn: reset failed"); } @@ -717,436 +433,59 @@ void GNSS_LG290P::debuggingEnable() } //---------------------------------------- -void GNSS_LG290P::enableGgaForNtrip() +// Restore the GNSS to the factory settings +//---------------------------------------- +void GNSS_LG290P::factoryReset() +{ + if (online.gnss) + { + _lg290p->factoryRestore(); // Restores the parameters configured by all commands to their default values. + // This command takes effect after restarting. + + reset(); // Reboot the receiver. + + systemPrintln("Waiting for LG290P to reboot"); + while (1) + { + delay(1000); // Wait for device to reboot + if (_lg290p->isConnected()) + break; + else + systemPrintln("Device still rebooting"); + } + systemPrintln("LG290P has been factory reset"); + } +} + +//---------------------------------------- +uint16_t GNSS_LG290P::fileBufferAvailable() +{ + // TODO return(lg290pFileBufferAvailable()); + return (0); +} + +//---------------------------------------- +uint16_t GNSS_LG290P::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) { - // TODO lg290pEnableGgaForNtrip(); + // TODO return(lg290pFileBufferAvailable()); + return (0); } //---------------------------------------- -// Turn on all the enabled NMEA messages +// Start the base using fixed coordinates //---------------------------------------- -bool GNSS_LG290P::enableNMEA() +bool GNSS_LG290P::fixedBaseStart() { bool response = true; - bool gpggaEnabled = false; - int portNumber = 1; + if (online.gnss == false) + return (false); - while (portNumber < 4) - { - for (int messageNumber = 0; messageNumber < MAX_LG290P_NMEA_MSG; messageNumber++) - { - // Check if this NMEA message is supported by the current LG290P firmware - if (lg290pFirmwareVersion >= lgMessagesNMEA[messageNumber].firmwareVersionSupported) - { - // Disable NMEA output on UART3 RADIO - int msgRate = settings.lg290pMessageRatesNMEA[messageNumber]; - if ((portNumber == 3) && (settings.enableNmeaOnRadio == false)) - msgRate = 0; - - // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate - if (lg290pFirmwareVersion >= 4) - // Enable this message, at this rate, on this port - response &= - _lg290p->setMessageRateOnPort(lgMessagesNMEA[messageNumber].msgTextName, msgRate, portNumber); - else - // Enable this message, at this rate - response &= _lg290p->setMessageRate(lgMessagesNMEA[messageNumber].msgTextName, msgRate); - if (response == false && settings.debugGnss) - systemPrintf("Enable NMEA failed at messageNumber %d %s.\r\n", messageNumber, - lgMessagesNMEA[messageNumber].msgTextName); - - // If we are using IP based corrections, we need to send local data to the PPL - // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 - if (pointPerfectServiceUsesKeys()) - { - // Mark PPL required messages as enabled if rate > 0 - if (settings.lg290pMessageRatesNMEA[messageNumber] > 0) - { - if (strcmp(lgMessagesNMEA[messageNumber].msgTextName, "GGA") == 0) - gpggaEnabled = true; - } - } - } - } - - portNumber++; - - // setMessageRateOnPort only supported on v4 and above - if (lg290pFirmwareVersion < 4) - break; // Don't step through portNumbers - } - - if (pointPerfectServiceUsesKeys()) - { - // Force on any messages that are needed for PPL - // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate - if (lg290pFirmwareVersion >= 4) - { - // Enable GGA on UART 2 (connected to ESP32) only - if (gpggaEnabled == false) - response &= _lg290p->setMessageRateOnPort("GGA", 1, 2); - } - else - { - // Enable GGA on all UARTs. It's the best we can do. - if (gpggaEnabled == false) - response &= _lg290p->setMessageRate("GGA", 1); - } - } - - // If this is Flex, we may need to enable NMEA for Tilt IMU - if (present.tiltPossible == true) - { - if (present.imu_im19 == true && settings.enableTiltCompensation == true) - { - // Regardless of user settings, enable GGA, RMC, GST on UART3 - // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate - if (lg290pFirmwareVersion >= 4) - { - // Enable GGA/RMS/GST on UART 3 (connected to the IMU) only - response &= _lg290p->setMessageRateOnPort("GGA", 1, 3); - response &= _lg290p->setMessageRateOnPort("RMC", 1, 3); - response &= _lg290p->setMessageRateOnPort("GST", 1, 3); - } - else - { - // GST not supported below 4 - systemPrintf( - "Current LG290P firmware: v%d (full form: %s). Tilt compensation requires GST on firmware v4 " - "or newer. Please " - "update the " - "firmware on your LG290P to allow for these features. Please see " - "https://bit.ly/sfe-rtk-lg290p-update\r\n Marking tilt compensation offline.", - lg290pFirmwareVersion, gnssFirmwareVersion); - - present.imu_im19 = false; - } - } - } - return (response); -} - -//---------------------------------------- -// Turn on all the enabled RTCM Base messages -//---------------------------------------- -bool GNSS_LG290P::enableRTCMBase() -{ - bool response = true; - bool enableRTCM = false; // Goes true if we need to enable RTCM output reporting - - int portNumber = 1; - - int minimumRtcmRate = 1000; - - while (portNumber < 4) - { - for (int messageNumber = 0; messageNumber < MAX_LG290P_RTCM_MSG; messageNumber++) - { - // Check if this RTCM message is supported by the current LG290P firmware - if (lg290pFirmwareVersion >= lgMessagesRTCM[messageNumber].firmwareVersionSupported) - { - // Setting RTCM-1005 must have only the rate - // Setting RTCM-107X must have rate and offset - if (strchr(lgMessagesRTCM[messageNumber].msgTextName, 'X') == nullptr) - { - // No X found. This is RTCM-1??? message. No offset. - - // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate - if (lg290pFirmwareVersion >= 4) - // Enable this message, at this rate, on this port - response &= _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, - settings.lg290pMessageRatesRTCMBase[messageNumber], - portNumber); - else - // Enable this message, at this rate - response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, - settings.lg290pMessageRatesRTCMBase[messageNumber]); - - if (response == false && settings.debugGnss) - systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, - lgMessagesRTCM[messageNumber].msgTextName); - } - else - { - // X found. This is RTCM-1??X message. Assign 'offset' of 0 - - // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate - if (lg290pFirmwareVersion >= 4) - // Enable this message, at this rate, on this port - response &= _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, - settings.lg290pMessageRatesRTCMBase[messageNumber], - portNumber, 0); - else - // Enable this message, at this rate - response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, - settings.lg290pMessageRatesRTCMBase[messageNumber], 0); - - if (response == false && settings.debugGnss) - systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, - lgMessagesRTCM[messageNumber].msgTextName); - } - - // If any message is enabled, enable RTCM output - if (settings.lg290pMessageRatesRTCMBase[messageNumber] > 0) - { - enableRTCM = true; - // Capture the message with the lowest rate - if (settings.lg290pMessageRatesRTCMBase[messageNumber] < minimumRtcmRate) - minimumRtcmRate = settings.lg290pMessageRatesRTCMBase[messageNumber]; - } - } - } - - portNumber++; - - // setMessageRateOnPort only supported on v4 and above - if (lg290pFirmwareVersion < 4) - break; // Don't step through portNumbers - } - - if (enableRTCM == true) - { - if (settings.debugGnss) - systemPrintf("Enabling Base RTCM MSM%c output with rate of %d\r\n", settings.useMSM7 ? '7' : '4', - minimumRtcmRate); - - // PQTMCFGRTCM fails to respond with OK over UART2 of LG290P, so don't look for it - char cfgRtcm[40]; - snprintf(cfgRtcm, sizeof(cfgRtcm), "PQTMCFGRTCM,W,%c,0,%d,07,06,2,%d", settings.useMSM7 ? '7' : '4', - settings.rtcmMinElev, minimumRtcmRate); - _lg290p->sendOkCommand(cfgRtcm); // Enable MSM4/7, output regular intervals, interval (seconds) - } - - return (response); -} - -//---------------------------------------- -// Turn on all the enabled RTCM Rover messages -//---------------------------------------- -bool GNSS_LG290P::enableRTCMRover() -{ - bool response = true; - bool rtcm1019Enabled = false; - bool rtcm1020Enabled = false; - bool rtcm1042Enabled = false; - bool rtcm1046Enabled = false; - bool enableRTCM = false; // Goes true if we need to enable RTCM output reporting - - int portNumber = 1; - - int minimumRtcmRate = 1000; - - while (portNumber < 4) - { - for (int messageNumber = 0; messageNumber < MAX_LG290P_RTCM_MSG; messageNumber++) - { - // 1019 to 1046 can only be set to 1 fix per report - // 107x to 112x can be set to 1-1200 fixes between reports - // So we set all RTCM to 1, and set PQTMCFGRTCM to the lowest value found - - // Capture the message with the lowest rate - if (settings.lg290pMessageRatesRTCMRover[messageNumber] > 0 && - settings.lg290pMessageRatesRTCMRover[messageNumber] < minimumRtcmRate) - minimumRtcmRate = settings.lg290pMessageRatesRTCMRover[messageNumber]; - - // Force all RTCM messages to 1 or 0. See above for reasoning. - int rate = settings.lg290pMessageRatesRTCMRover[messageNumber]; - if (rate > 1) - rate = 1; - - // Check if this RTCM message is supported by the current LG290P firmware - if (lg290pFirmwareVersion >= lgMessagesRTCM[messageNumber].firmwareVersionSupported) - { - // Setting RTCM-1005 must have only the rate - // Setting RTCM-107X must have rate and offset - if (strchr(lgMessagesRTCM[messageNumber].msgTextName, 'X') == nullptr) - { - // No X found. This is RTCM-1??? message. No offset. - - // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate - if (lg290pFirmwareVersion >= 4) - { - // If any one of the commands fails, report failure overall - response &= - _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, rate, portNumber); - } - else - response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, rate); - - if (response == false && settings.debugGnss) - systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, - lgMessagesRTCM[messageNumber].msgTextName); - } - else - { - // X found. This is RTCM-1??X message. Assign 'offset' of 0 - - // The rate of these type of messages can be 1 to 1200, so we allow the full rate - - // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate - if (lg290pFirmwareVersion >= 4) - { - response &= _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, - settings.lg290pMessageRatesRTCMRover[messageNumber], - portNumber, 0); - } - else - response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, - settings.lg290pMessageRatesRTCMRover[messageNumber], 0); - - if (response == false && settings.debugGnss) - systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, - lgMessagesRTCM[messageNumber].msgTextName); - } - - // If any message is enabled, enable RTCM output - if (settings.lg290pMessageRatesRTCMRover[messageNumber] > 0) - enableRTCM = true; - - // If we are using IP based corrections, we need to send local data to the PPL - // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 - if (pointPerfectServiceUsesKeys()) - { - // Mark PPL required messages as enabled if rate > 0 - if (settings.lg290pMessageRatesRTCMRover[messageNumber] > 0) - { - if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1019") == 0) - rtcm1019Enabled = true; - else if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1020") == 0) - rtcm1020Enabled = true; - else if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1042") == 0) - rtcm1042Enabled = true; - else if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1046") == 0) - rtcm1046Enabled = true; - } - } - } - } - - portNumber++; - - // setMessageRateOnPort only supported on v4 and above - if (lg290pFirmwareVersion < 4) - break; // Don't step through portNumbers - } - - if (pointPerfectServiceUsesKeys()) - { - enableRTCM = true; // Force enable RTCM output - - // Force on any messages that are needed for PPL - if (rtcm1019Enabled == false) - { - if (settings.debugCorrections) - systemPrintln("PPL Enabling RTCM3-1019"); - response &= _lg290p->setMessageRate("RTCM3-1019", 1); - } - if (rtcm1020Enabled == false) - { - if (settings.debugCorrections) - systemPrintln("PPL Enabling RTCM3-1020"); - - response &= _lg290p->setMessageRate("RTCM3-1020", 1); - } - if (rtcm1042Enabled == false) - { - if (settings.debugCorrections) - systemPrintln("PPL Enabling RTCM3-1042"); - - response &= _lg290p->setMessageRate("RTCM3-1042", 1); - } - if (rtcm1046Enabled == false) - { - if (settings.debugCorrections) - systemPrintln("PPL Enabling RTCM3-1046"); - response &= _lg290p->setMessageRate("RTCM3-1046", 1); - } - } - - // If any RTCM message is enabled, send CFGRTCM - if (enableRTCM == true) - { - if (settings.debugCorrections) - systemPrintf("Enabling Rover RTCM MSM%c output with rate of %d\r\n", settings.useMSM7 ? '7' : '4', - minimumRtcmRate); - - // Enable MSM4/7 (for faster PPP CSRS results), output at a rate equal to the minimum RTCM rate (EPH Mode = 2) - // PQTMCFGRTCM, W, , , , , , , - // Set MSM_ElevThd to 15 degrees from rftop suggestion - - char msmCommand[40] = {0}; - snprintf(msmCommand, sizeof(msmCommand), "PQTMCFGRTCM,W,%c,0,%d,07,06,2,%d", settings.useMSM7 ? '7' : '4', - settings.rtcmMinElev, minimumRtcmRate); - - // PQTMCFGRTCM fails to respond with OK over UART2 of LG290P, so don't look for it - _lg290p->sendOkCommand(msmCommand); - } - - return (response); -} - -//---------------------------------------- -// Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted -// even if there is no GPS fix. We use it to test serial output. -// Returns true if successfully started and false upon failure -//---------------------------------------- -bool GNSS_LG290P::enableRTCMTest() -{ - // RTCM-1230 not supported on the LG290P - return false; -} - -//---------------------------------------- -// Restore the GNSS to the factory settings -//---------------------------------------- -void GNSS_LG290P::factoryReset() -{ - if (online.gnss) - { - _lg290p->factoryRestore(); // Restores the parameters configured by all commands to their default values. - // This command takes effect after restarting. - - softwareReset(); // Reboot the receiver. - - systemPrintln("Waiting for LG290P to reboot"); - while (1) - { - delay(1000); // Wait for device to reboot - if (_lg290p->isConnected()) - break; - else - systemPrintln("Device still rebooting"); - } - systemPrintln("LG290P has been factory reset"); - } -} - -//---------------------------------------- -uint16_t GNSS_LG290P::fileBufferAvailable() -{ - // TODO return(lg290pFileBufferAvailable()); - return (0); -} - -//---------------------------------------- -uint16_t GNSS_LG290P::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) -{ - // TODO return(lg290pFileBufferAvailable()); - return (0); -} - -//---------------------------------------- -// Start the base using fixed coordinates -//---------------------------------------- -bool GNSS_LG290P::fixedBaseStart() -{ - bool response = true; - - if (online.gnss == false) - return (false); - - if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) + // If we are already in the appropriate base mode, no changes needed + if (gnssInBaseFixedMode()) + return (true); // No changes needed + + if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) { // Waits for save and reset response &= _lg290p->setSurveyFixedMode(settings.fixedEcefX, settings.fixedEcefY, settings.fixedEcefZ); @@ -1173,6 +512,33 @@ bool GNSS_LG290P::fixedBaseStart() return (response); } +//---------------------------------------- +// Check if given GNSS fix rate is allowed +// Rates are expressed in ms between fixes. +//---------------------------------------- +const uint32_t lgAllowedFixRatesHz[] = {1, 2, 5, 10, 20}; // The LG290P doesn't support slower speeds than 1Hz +const int lgAllowedFixRatesCount = sizeof(lgAllowedFixRatesHz) / sizeof(lgAllowedFixRatesHz[0]); + +bool GNSS_LG290P::fixRateIsAllowed(uint32_t fixRateMs) +{ + for (int x = 0; x < lgAllowedFixRatesCount; x++) + if (fixRateMs == (1000.0 / lgAllowedFixRatesHz[x])) + return (true); + return (false); +} + +// Return minimum in milliseconds +uint32_t GNSS_LG290P::fixRateGetMinimumMs() +{ + return (1000.0 / lgAllowedFixRatesHz[lgAllowedFixRatesCount - 1]); // The max Hz value is the min ms value +} + +// Return maximum in milliseconds +uint32_t GNSS_LG290P::fixRateGetMaximumMs() +{ + return (1000.0 / lgAllowedFixRatesHz[0]); +} + //---------------------------------------- // Return the number of active/enabled messages //---------------------------------------- @@ -1302,6 +668,12 @@ uint32_t GNSS_LG290P::getDataBaudRate() // This is nicknamed the DATA port dataUart = 1; } + else if (productVariant == RTK_FLEX) + { + // On the Flex, the DATA connector is not connected to the GNSS + // Return 0. + return (0); + } else if (productVariant == RTK_TORCH_X2) { // UART3 of the LG290P is connected to USB CH342 (Port A) @@ -1309,16 +681,15 @@ uint32_t GNSS_LG290P::getDataBaudRate() dataUart = 3; } else - { systemPrintln("getDataBaudRate: Uncaught platform"); - } + return (getBaudRate(dataUart)); } //---------------------------------------- // Set the baud rate of port nicknamed DATA //---------------------------------------- -bool GNSS_LG290P::setDataBaudRate(uint32_t baud) +bool GNSS_LG290P::setBaudRateData(uint32_t baud) { if (online.gnss) { @@ -1334,20 +705,20 @@ bool GNSS_LG290P::setDataBaudRate(uint32_t baud) // This is nicknamed the DATA port return (setBaudRate(1, baud)); } - else if (productVariant == RTK_TORCH_X2) + else if (productVariant == RTK_FLEX) { - if (getDataBaudRate() != baud) - { - // UART3 of the LG290P is connected to USB CH342 (Port A) - // This is nicknamed the DATA port - return (setBaudRate(3, baud)); - } + // On the Flex, the DATA connector is not connected to the GNSS + // Return true so that configuration can proceed. + return (true); } - else + else if (productVariant == RTK_TORCH_X2) { - // On products that don't have a DATA port (Flex), act as if we have set the baud successfully - return (true); + // UART3 of the LG290P is connected to USB CH342 (Port A) + // This is nicknamed the DATA port + return (setBaudRate(3, baud)); } + else + systemPrintln("setDataBaudRate: Uncaught platform"); } } return (false); @@ -1375,13 +746,16 @@ uint32_t GNSS_LG290P::getRadioBaudRate() // Not really used at this time but available for configuration radioUart = 1; } + else + systemPrintln("getDataBaudRate: Uncaught platform"); + return (getBaudRate(radioUart)); } //---------------------------------------- // Set the baud rate for the Radio connection //---------------------------------------- -bool GNSS_LG290P::setRadioBaudRate(uint32_t baud) +bool GNSS_LG290P::setBaudRateRadio(uint32_t baud) { if (online.gnss) { @@ -1408,6 +782,9 @@ bool GNSS_LG290P::setRadioBaudRate(uint32_t baud) // Not really used at this time but available for configuration radioUart = 1; } + else + systemPrintln("setBaudRateRadio: Uncaught platform"); + return (setBaudRate(radioUart, baud)); } } @@ -1682,22 +1059,48 @@ uint32_t GNSS_LG290P::getTimeAccuracy() //---------------------------------------- uint16_t GNSS_LG290P::getYear() { - if (online.gnss) - return (_lg290p->getYear()); - return 0; + if (online.gnss) + return (_lg290p->getYear()); + return 0; +} + +//---------------------------------------- +// Returns true if the device is in Base Fixed mode +//---------------------------------------- +bool GNSS_LG290P::gnssInBaseFixedMode() +{ + // 0 - Unknown, 1 - Rover, 2 - Base + if (getMode() == 2) + { + if (getSurveyInMode() == 2) // 0 - Disabled, 1 - Survey-in mode, 2 - Fixed mode + return (true); + } + return (false); +} + +//---------------------------------------- +// Returns true if the device is in Base Survey In mode +//---------------------------------------- +bool GNSS_LG290P::gnssInBaseSurveyInMode() +{ + // 0 - Unknown, 1 - Rover, 2 - Base + if (getMode() == 2) + { + if (getSurveyInMode() == 1) // 0 - Disabled, 1 - Survey-in mode, 2 - Fixed mode + return (true); + } + return (false); } //---------------------------------------- // Returns true if the device is in Rover mode -// Currently the only two modes are Rover or Base //---------------------------------------- -bool GNSS_LG290P::inRoverMode() +bool GNSS_LG290P::gnssInRoverMode() { - // Determine which state we are in - if (settings.lastState == STATE_BASE_NOT_STARTED) - return (false); - - return (true); // Default to Rover + // 0 - Unknown, 1 - Rover, 2 - Base + if (getMode() == 1) + return (true); + return (false); } // If we issue a library command that must wait for a response, we don't want @@ -1913,8 +1316,7 @@ void GNSS_LG290P::menuConstellations() systemPrintf("%d) Galileo E6 Corrections: %s\r\n", MAX_LG290P_CONSTELLATIONS + 1, settings.enableGalileoHas ? "Enabled" : "Disabled"); if (settings.enableGalileoHas) - systemPrintf("%d) PPP Configuration: \"%s\"\r\n", MAX_LG290P_CONSTELLATIONS + 2, - settings.configurePPP); + systemPrintf("%d) PPP Configuration: \"%s\"\r\n", MAX_LG290P_CONSTELLATIONS + 2, settings.configurePPP); } systemPrintln("x) Exit"); @@ -1926,10 +1328,12 @@ void GNSS_LG290P::menuConstellations() incoming--; // Align choice to constellation array of 0 to 5 settings.lg290pConstellations[incoming] ^= 1; + gnssConfigure(GNSS_CONFIG_CONSTELLATION); // Request receiver to use new settings } else if ((incoming == MAX_LG290P_CONSTELLATIONS + 1) && present.galileoHasCapable) { settings.enableGalileoHas ^= 1; + gnssConfigure(GNSS_CONFIG_HAS_E6); // Request receiver to use new settings } else if ((incoming == MAX_LG290P_CONSTELLATIONS + 2) && present.galileoHasCapable && settings.enableGalileoHas) { @@ -1964,11 +1368,6 @@ void GNSS_LG290P::menuConstellations() printUnknown(incoming); } - // Apply current settings to module - gnss->setConstellations(); - - setHighAccuracyService(settings.enableGalileoHas, (const char *)settings.configurePPP); - clearBuffer(); // Empty buffer of any newline chars } @@ -2034,6 +1433,12 @@ void GNSS_LG290P::menuMessages() for (int x = 0; x < MAX_LG290P_PQTM_MSG; x++) settings.lg290pMessageRatesPQTM[x] = lgMessagesPQTM[x].msgDefaultRate; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); + if (inBaseMode()) // If the system is in Base mode + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); + systemPrintln("Reset to Defaults"); } else if (incoming == 11 || incoming == 12) @@ -2060,26 +1465,30 @@ void GNSS_LG290P::menuMessages() setRtcmRoverMessageRateByName("RTCM3-109X", rtcmReportRate); // setRtcmRoverMessageRateByName("RTCM3-112X", rtcmReportRate); //BeiDou not used by CSRS-PPP - // MSM7 is set during enableRTCMRover() + // MSM7 is set during setMessagesRTCMRover() // Override settings for PPP logging - setElevation(15); - setMinCnoRadio(30); + settings.minElev = 15; // Degrees + gnssConfigure(GNSS_CONFIG_ELEVATION); + settings.minCN0 = 30; // dBHz + gnssConfigure(GNSS_CONFIG_CN0); + settings.measurementRateMs = 1000; // Go to 1 Hz + gnssConfigure(GNSS_CONFIG_FIX_RATE); - setRate(1); // Go to 1 Hz + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings if (incoming == 12) - { systemPrintln("Reset to High-rate PPP Logging Defaults (NMEAx7 / RTCMx4 - 1Hz)"); - } else - { systemPrintln("Reset to PPP Logging Defaults (NMEAx7 / RTCMx4 - 30 second decimation)"); - } } else if ((incoming == 13) && (namedSettingAvailableOnPlatform("useMSM7"))) // Redundant - but good practice for code reuse) + { settings.useMSM7 ^= 1; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings + } else if ((incoming == 14) && (namedSettingAvailableOnPlatform("rtcmMinElev"))) { systemPrintf("Enter minimum elevation for RTCM: "); @@ -2089,6 +1498,7 @@ void GNSS_LG290P::menuMessages() if (elevation >= -90 && elevation <= 90) { settings.rtcmMinElev = elevation; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings } } @@ -2101,14 +1511,6 @@ void GNSS_LG290P::menuMessages() } clearBuffer(); // Empty buffer of any newline chars - - // Apply these changes at menu exit - if (inRoverMode()) - restartRover = true; - else - restartBase = true; - - setLoggingType(); // Determine if we are standard, PPP, or custom. Changes logging icon accordingly. } //---------------------------------------- @@ -2215,22 +1617,34 @@ void GNSS_LG290P::menuMessagesSubtype(int *localMessageRate, const char *message if (strcmp(messageType, "NMEA") == 0) { if (getNewSetting(messageString, 0, 1, &newSetting) == INPUT_RESPONSE_VALID) + { settings.lg290pMessageRatesNMEA[incoming] = newSetting; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Configure receiver to use new setting + } } if (strcmp(messageType, "RTCMRover") == 0) { if (getNewSetting(messageString, 0, 1200, &newSetting) == INPUT_RESPONSE_VALID) + { settings.lg290pMessageRatesRTCMRover[incoming] = newSetting; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Configure receiver to use new setting + } } if (strcmp(messageType, "RTCMBase") == 0) { if (getNewSetting(messageString, 0, 1200, &newSetting) == INPUT_RESPONSE_VALID) + { settings.lg290pMessageRatesRTCMBase[incoming] = newSetting; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Configure receiver to use new setting + } } if (strcmp(messageType, "PQTM") == 0) { if (getNewSetting(messageString, 0, 1, &newSetting) == INPUT_RESPONSE_VALID) + { settings.lg290pMessageRatesPQTM[incoming] = newSetting; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_OTHER); // Configure receiver to use new setting + } } } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) @@ -2241,10 +1655,6 @@ void GNSS_LG290P::menuMessagesSubtype(int *localMessageRate, const char *message printUnknown(incoming); } - settings.gnssConfiguredOnce = false; // Update the GNSS config at the next boot - settings.gnssConfiguredBase = false; - settings.gnssConfiguredRover = false; - clearBuffer(); // Empty buffer of any newline chars } @@ -2283,260 +1693,668 @@ int GNSS_LG290P::pushRawData(uint8_t *dataToSend, int dataLength) return (0); } -//---------------------------------------- -uint16_t GNSS_LG290P::rtcmBufferAvailable() -{ - // TODO return(lg290pRtcmBufferAvailable()); - return (0); +//---------------------------------------- +// Hardware or software reset the GNSS receiver +// Reset GNSS via software command +// Poll for isConnected() +//---------------------------------------- +bool GNSS_LG290P::reset() +{ + if (online.gnss) + { + if (settings.debugGnss || settings.debugGnssConfig) + systemPrintln("Rebooting LG290P"); + + _lg290p->reset(); + + // Poll for a limited amount of time before unit comes back + int x = 0; + while (x++ < 50) + { + delay(100); // Wait for device to reboot + if (_lg290p->isConnected() == true) + break; + else + systemPrintln("GNSS still rebooting"); + } + if (x < 50) + return (true); + + systemPrintln("GNSS failed to connect after reboot"); + } + return (false); +} + +//---------------------------------------- +uint16_t GNSS_LG290P::rtcmBufferAvailable() +{ + // TODO return(lg290pRtcmBufferAvailable()); + return (0); +} + +//---------------------------------------- +// If LBand is being used, ignore any RTCM that may come in from the GNSS +//---------------------------------------- +void GNSS_LG290P::rtcmOnGnssDisable() +{ + // LG290P does not have a separate interface for RTCM +} + +//---------------------------------------- +// If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver +//---------------------------------------- +void GNSS_LG290P::rtcmOnGnssEnable() +{ + // LG290P does not have a separate interface for RTCM +} + +//---------------------------------------- +uint16_t GNSS_LG290P::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) +{ + // TODO return(lg290pRtcmRead(rtcmBuffer, rtcmBytesToRead)); + return (0); +} + +//---------------------------------------- +// Save the current configuration +// Returns true when the configuration was saved and false upon failure +//---------------------------------------- +bool GNSS_LG290P::saveConfiguration() +{ + if (online.gnss) + return (_lg290p->save()); + + return false; +} + +//---------------------------------------- +// Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS +// This just sets the GNSS side +//---------------------------------------- +bool GNSS_LG290P::setBaudRateComm(uint32_t baud) +{ + if (online.gnss) + { + if (getCommBaudRate() == baud) + { + return (true); // Baud is set! + } + else + { + uint8_t commUart = 0; + if (productVariant == RTK_POSTCARD) + { + // UART2 of the LG290P is connected to the ESP32 for the main config/comm + commUart = 2; + } + else if (productVariant == RTK_FLEX) + { + // UART1 of the LG290P is connected to the ESP32 for the main config/comm + commUart = 1; + } + else + systemPrintln("setBaudRateComm: Uncaught platform"); + + return (setBaudRate(commUart, baud)); + } + } + return (false); +} + +//---------------------------------------- +// Return the baud rate of the UART connected to the ESP32 UART1 +//---------------------------------------- +uint32_t GNSS_LG290P::getCommBaudRate() +{ + uint8_t commUart = 0; + if (productVariant == RTK_POSTCARD) + { + // On the Postcard, the ESP32 UART1 is connected to LG290P UART2 + commUart = 2; + } + else if (productVariant == RTK_FLEX) + { + // On the Flex, the ESP32 UART1 is connected to LG290P UART1 + commUart = 1; + } + else + systemPrintln("getCommBaudRate: Uncaught platform"); + + return (getBaudRate(commUart)); +} + +//---------------------------------------- +// Enable all the valid constellations and bands for this platform +// Band support varies between platforms and firmware versions +//---------------------------------------- +bool GNSS_LG290P::setConstellations() +{ + bool response = true; + + if (online.gnss) + { + response = _lg290p->setConstellations(settings.lg290pConstellations[0], // GPS + settings.lg290pConstellations[1], // GLONASS + settings.lg290pConstellations[2], // Galileo + settings.lg290pConstellations[3], // BDS + settings.lg290pConstellations[4], // QZSS + settings.lg290pConstellations[5]); // NavIC + } + + gnssConfigure(GNSS_CONFIG_RESET); // Constellation changes require device save/restart + + return (response); +} + +// Enable / disable corrections protocol(s) on the Radio External port +// Always update if force is true. Otherwise, only update if enable has changed state +bool GNSS_LG290P::setCorrRadioExtPort(bool enable, bool force) +{ + if (online.gnss) + { + // Someday, read/modify/write setPortInputProtocols + + if (force || (enable != _corrRadioExtPortEnabled)) + { + // Set UART3 InputProt: RTCM3 (4) vs NMEA (1) + if (_lg290p->setPortInputProtocols(3, enable ? 4 : 1)) + { + if ((settings.debugCorrections == true) && !inMainMenu) + { + systemPrintf("Radio Ext corrections: %s -> %s%s\r\n", + _corrRadioExtPortEnabled ? "enabled" : "disabled", enable ? "enabled" : "disabled", + force ? " (Forced)" : ""); + } + + _corrRadioExtPortEnabled = enable; + return true; + } + else + { + systemPrintf("Radio Ext corrections FAILED: %s -> %s%s\r\n", + _corrRadioExtPortEnabled ? "enabled" : "disabled", enable ? "enabled" : "disabled", + force ? " (Forced)" : ""); + } + } + } + + return false; +} + +//---------------------------------------- +// Set the elevation in degrees +//---------------------------------------- +bool GNSS_LG290P::setElevation(uint8_t elevationDegrees) +{ + // Present on >= v05 + if (lg290pFirmwareVersion >= 5) + return (_lg290p->setElevationAngle(elevationDegrees)); + + // Because we call this during module setup we rely on a positive result + return true; +} + +//---------------------------------------- +// Control whether HAS E6 is used in location fixes or not +//---------------------------------------- +bool GNSS_LG290P::setHighAccuracyService(bool enableGalileoHas) +{ + // E6 reception requires version v03/v06 with 'PPP_TEMP' in firmware title + // Present is set during LG290P begin() + if (present.galileoHasCapable == false) + return (true); // Return true to clear gnssConfigure test + + // TODO - We should read/modify/write on PQTMCFGPPP + + bool result = true; + + // Enable E6 and PPP if enabled + if (enableGalileoHas) + { + // $PQTMCFGPPP,R*68 + // $PQTMCFGPPP,OK,00,2,120,0.10,0.15*0A + + // $PQTMCFGPPP,W,2,1,120,0.10,0.15*68 + // Enable E6 HAS, WGS84, 120 timeout, 0.10m Horizontal convergence accuracy threshold, 0.15m Vertical + // threshold + char paramConfigurePPP[sizeof(settings.configurePPP) + 4]; + snprintf(paramConfigurePPP, sizeof(paramConfigurePPP), ",W,%s", configPppSpacesToCommas(settings.configurePPP)); + if (_lg290p->sendOkCommand("$PQTMCFGPPP", paramConfigurePPP) == true) + { + systemPrintln("Galileo E6 HAS service enabled"); + } + else + { + systemPrintln("Galileo E6 HAS service failed to enable"); + result = false; + } + } + else + { + // Turn off HAS/E6 + // $PQTMCFGPPP,W,0* + if (_lg290p->sendOkCommand("$PQTMCFGPPP", ",W,0") == true) + { + systemPrintln("Galileo E6 HAS service disabled"); + } + else + { + systemPrintln("Galileo E6 HAS service failed to disable"); + result = false; + } + } + + return (result); } //---------------------------------------- -// If LBand is being used, ignore any RTCM that may come in from the GNSS +// Configure device-direct logging. Currently mosaic-X5 specific. //---------------------------------------- -void GNSS_LG290P::rtcmOnGnssDisable() +bool GNSS_LG290P::setLogging() { - // LG290P does not have a separate interface for RTCM + // Not supported on this platform + return (true); // Return true to clear gnssConfigure test } //---------------------------------------- -// If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver +// Set the minimum satellite signal level for navigation. //---------------------------------------- -void GNSS_LG290P::rtcmOnGnssEnable() +bool GNSS_LG290P::setMinCN0(uint8_t cnoValue) { - // LG290P does not have a separate interface for RTCM -} + // Present on >= v05 + if (lg290pFirmwareVersion >= 5) + return (_lg290p->setCNR((float)cnoValue)); // 0.0 to 99.0 -//---------------------------------------- -uint16_t GNSS_LG290P::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) -{ - // TODO return(lg290pRtcmRead(rtcmBuffer, rtcmBytesToRead)); - return (0); + // Because we call this during module setup we rely on a positive result + return true; } //---------------------------------------- -// Save the current configuration -// Returns true when the configuration was saved and false upon failure +// Enable/disable NMEA messages according to the NMEA array //---------------------------------------- -bool GNSS_LG290P::saveConfiguration() +bool GNSS_LG290P::setMessagesNMEA() { - if (online.gnss) - return (_lg290p->save()); + bool response = true; + bool gpggaEnabled = false; - return false; -} + int portNumber = 1; -//---------------------------------------- -// Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS -// This just sets the GNSS side -//---------------------------------------- -bool GNSS_LG290P::setCommBaudrate(uint32_t baud) -{ - if (online.gnss) + while (portNumber < 4) { - if (getCommBaudRate() == baud) + for (int messageNumber = 0; messageNumber < MAX_LG290P_NMEA_MSG; messageNumber++) { - return (true); // Baud is set! + // Check if this NMEA message is supported by the current LG290P firmware + if (lg290pFirmwareVersion >= lgMessagesNMEA[messageNumber].firmwareVersionSupported) + { + // Disable NMEA output on UART3 RADIO + int msgRate = settings.lg290pMessageRatesNMEA[messageNumber]; + if ((portNumber == 3) && (settings.enableNmeaOnRadio == false)) + msgRate = 0; + + // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate + if (lg290pFirmwareVersion >= 4) + // Enable this message, at this rate, on this port + response &= + _lg290p->setMessageRateOnPort(lgMessagesNMEA[messageNumber].msgTextName, msgRate, portNumber); + else + // Enable this message, at this rate + response &= _lg290p->setMessageRate(lgMessagesNMEA[messageNumber].msgTextName, msgRate); + if (response == false && settings.debugGnss) + systemPrintf("Enable NMEA failed at messageNumber %d %s.\r\n", messageNumber, + lgMessagesNMEA[messageNumber].msgTextName); + + // Mark messages needed for other services (NTRIP Client, PointPerfect, etc) as enabled if rate > 0 + if (settings.lg290pMessageRatesNMEA[messageNumber] > 0) + { + if (strcmp(lgMessagesNMEA[messageNumber].msgTextName, "GGA") == 0) + gpggaEnabled = true; + } + } } - else + + portNumber++; + + // setMessageRateOnPort only supported on v4 and above + if (lg290pFirmwareVersion < 4) + break; // Don't step through portNumbers + } + + // Enable GGA if needed for other services + if (gpggaEnabled == false) + { + // Force on any messages that are needed for PPL + // Enable GGA for NTRIP + if (pointPerfectServiceUsesKeys() || + (settings.enableNtripClient == true && settings.ntripClient_TransmitGGA == true)) { - uint8_t commUart = 0; - if (productVariant == RTK_POSTCARD) + if (settings.debugGnssConfig) + systemPrintln("Enabling GGA for NTRIP and PointPerfect"); + + // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate + if (lg290pFirmwareVersion >= 4) { - // UART2 of the LG290P is connected to the ESP32 for the main config/comm - commUart = 2; + // Enable GGA on a specific port + // On Torch X2 and Postcard, the LG290P UART 2 is connected to ESP32. + response &= _lg290p->setMessageRateOnPort("GGA", 1, 2); } - else if (productVariant == RTK_FLEX) + else + // Enable GGA on all UARTs. It's the best we can do. + response &= _lg290p->setMessageRate("GGA", 1); + } + } + + // If this is Flex, we may need to enable NMEA for Tilt IMU + if (present.tiltPossible == true) + { + if (present.imu_im19 == true && settings.enableTiltCompensation == true) + { + // Regardless of user settings, enable GGA, RMC, GST on UART3 + // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate + if (lg290pFirmwareVersion >= 4) { - // UART1 of the LG290P is connected to the ESP32 for the main config/comm - commUart = 1; + // Enable GGA/RMS/GST on UART 3 (connected to the IMU) only + response &= _lg290p->setMessageRateOnPort("GGA", 1, 3); + response &= _lg290p->setMessageRateOnPort("RMC", 1, 3); + response &= _lg290p->setMessageRateOnPort("GST", 1, 3); + } + else + { + // GST not supported below 4 + systemPrintf( + "Current LG290P firmware: v%d (full form: %s). Tilt compensation requires GST on firmware v4 " + "or newer. Please " + "update the " + "firmware on your LG290P to allow for these features. Please see " + "https://bit.ly/sfe-rtk-lg290p-update\r\n Marking tilt compensation offline.", + lg290pFirmwareVersion, gnssFirmwareVersion); + + present.imu_im19 = false; } - return (setBaudRate(commUart, baud)); } } - return (false); + + // Messages take effect immediately. Save/Reset is not needed. + + return (response); } //---------------------------------------- -// Return the baud rate of the UART connected to the ESP32 UART1 +// Turn on all the enabled RTCM Base messages //---------------------------------------- -uint32_t GNSS_LG290P::getCommBaudRate() +bool GNSS_LG290P::setMessagesRTCMBase() { - uint8_t commUart = 0; - if (productVariant == RTK_POSTCARD) + bool response = true; + bool enableRTCM = false; // Goes true if we need to enable RTCM output reporting + + int portNumber = 1; + + while (portNumber < 4) { - // On the Postcard, the ESP32 UART1 is connected to LG290P UART2 - commUart = 2; + for (int messageNumber = 0; messageNumber < MAX_LG290P_RTCM_MSG; messageNumber++) + { + // Check if this RTCM message is supported by the current LG290P firmware + if (lg290pFirmwareVersion >= lgMessagesRTCM[messageNumber].firmwareVersionSupported) + { + // Setting RTCM-1005 must have only the rate + // Setting RTCM-107X must have rate and offset + if (strchr(lgMessagesRTCM[messageNumber].msgTextName, 'X') == nullptr) + { + // No X found. This is RTCM-1??? message. No offset. + + // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate + if (lg290pFirmwareVersion >= 4) + // Enable this message, at this rate, on this port + response &= _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, + settings.lg290pMessageRatesRTCMBase[messageNumber], + portNumber); + else + // Enable this message, at this rate + response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, + settings.lg290pMessageRatesRTCMBase[messageNumber]); + + if (response == false && settings.debugGnss) + systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, + lgMessagesRTCM[messageNumber].msgTextName); + } + else + { + // X found. This is RTCM-1??X message. Assign 'offset' of 0 + + // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate + if (lg290pFirmwareVersion >= 4) + // Enable this message, at this rate, on this port + response &= _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, + settings.lg290pMessageRatesRTCMBase[messageNumber], + portNumber, 0); + else + // Enable this message, at this rate + response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, + settings.lg290pMessageRatesRTCMBase[messageNumber], 0); + + if (response == false && settings.debugGnss) + systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, + lgMessagesRTCM[messageNumber].msgTextName); + } + + // If any message is enabled, enable RTCM output + if (settings.lg290pMessageRatesRTCMBase[messageNumber] > 0) + enableRTCM = true; + } + } + + portNumber++; + + // setMessageRateOnPort only supported on v4 and above + if (lg290pFirmwareVersion < 4) + break; // Don't step through portNumbers } - else if (productVariant == RTK_FLEX) + + if (enableRTCM == true) { - // On the Flex, the ESP32 UART1 is connected to LG290P UART1 - commUart = 1; + if (settings.debugGnss) + systemPrintln("Enabling Base RTCM output"); + + // PQTMCFGRTCM fails to respond with OK over UART2 of LG290P, so don't look for it + char cfgRtcm[40]; + snprintf(cfgRtcm, sizeof(cfgRtcm), "PQTMCFGRTCM,W,%c,0,-90,07,06,2,1", settings.useMSM7 ? '7' : '4'); + _lg290p->sendOkCommand(cfgRtcm); // Enable MSM4/7, output regular intervals, interval (seconds) } - return (getBaudRate(commUart)); + + // Messages take effect immediately. Save/Reset is not needed. + + return (response); } //---------------------------------------- -// Enable all the valid constellations and bands for this platform -// Band support varies between platforms and firmware versions +// Turn on all the enabled RTCM Rover messages //---------------------------------------- -bool GNSS_LG290P::setConstellations() +bool GNSS_LG290P::setMessagesRTCMRover() { bool response = true; + bool rtcm1019Enabled = false; + bool rtcm1020Enabled = false; + bool rtcm1042Enabled = false; + bool rtcm1046Enabled = false; + bool enableRTCM = false; // Goes true if we need to enable RTCM output reporting - if (online.gnss) + int portNumber = 1; + + int minimumRtcmRate = 1000; + + while (portNumber < 4) { - response = _lg290p->setConstellations(settings.lg290pConstellations[0], // GPS - settings.lg290pConstellations[1], // GLONASS - settings.lg290pConstellations[2], // Galileo - settings.lg290pConstellations[3], // BDS - settings.lg290pConstellations[4], // QZSS - settings.lg290pConstellations[5]); // NavIC - } + for (int messageNumber = 0; messageNumber < MAX_LG290P_RTCM_MSG; messageNumber++) + { + // 1019 to 1046 can only be set to 1 fix per report + // 107x to 112x can be set to 1-1200 fixes between reports + // So we set all RTCM to 1, and set PQTMCFGRTCM to the lowest value found - return (response); -} + // Capture the message with the lowest rate + if (settings.lg290pMessageRatesRTCMRover[messageNumber] > 0 && + settings.lg290pMessageRatesRTCMRover[messageNumber] < minimumRtcmRate) + minimumRtcmRate = settings.lg290pMessageRatesRTCMRover[messageNumber]; + + // Force all RTCM messages to 1 or 0. See above for reasoning. + int rate = settings.lg290pMessageRatesRTCMRover[messageNumber]; + if (rate > 1) + rate = 1; -// Enable / disable corrections protocol(s) on the Radio External port -// Always update if force is true. Otherwise, only update if enable has changed state -bool GNSS_LG290P::setCorrRadioExtPort(bool enable, bool force) -{ - if (online.gnss) - { - if (force || (enable != _corrRadioExtPortEnabled)) - { - // Set UART3 InputProt: RTCM3 (4) vs NMEA (1) - if (_lg290p->setPortInputProtocols(3, enable ? 4 : 1)) + // Check if this RTCM message is supported by the current LG290P firmware + if (lg290pFirmwareVersion >= lgMessagesRTCM[messageNumber].firmwareVersionSupported) { - if ((settings.debugCorrections == true) && !inMainMenu) + // Setting RTCM-1005 must have only the rate + // Setting RTCM-107X must have rate and offset + if (strchr(lgMessagesRTCM[messageNumber].msgTextName, 'X') == nullptr) { - systemPrintf("Radio Ext corrections: %s -> %s%s\r\n", - _corrRadioExtPortEnabled ? "enabled" : "disabled", enable ? "enabled" : "disabled", - force ? " (Forced)" : ""); + // No X found. This is RTCM-1??? message. No offset. + + // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate + if (lg290pFirmwareVersion >= 4) + { + // If any one of the commands fails, report failure overall + response &= + _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, rate, portNumber); + } + else + response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, rate); + + if (response == false && settings.debugGnss) + systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, + lgMessagesRTCM[messageNumber].msgTextName); } + else + { + // X found. This is RTCM-1??X message. Assign 'offset' of 0 - _corrRadioExtPortEnabled = enable; - return true; - } - else - { - systemPrintf("Radio Ext corrections FAILED: %s -> %s%s\r\n", - _corrRadioExtPortEnabled ? "enabled" : "disabled", enable ? "enabled" : "disabled", - force ? " (Forced)" : ""); - } - } - } + // The rate of these type of messages can be 1 to 1200, so we allow the full rate - return false; -} + // If firmware is 4 or higher, use setMessageRateOnPort, otherwise setMessageRate + if (lg290pFirmwareVersion >= 4) + { + response &= _lg290p->setMessageRateOnPort(lgMessagesRTCM[messageNumber].msgTextName, + settings.lg290pMessageRatesRTCMRover[messageNumber], + portNumber, 0); + } + else + response &= _lg290p->setMessageRate(lgMessagesRTCM[messageNumber].msgTextName, + settings.lg290pMessageRatesRTCMRover[messageNumber], 0); -//---------------------------------------- -// Set the elevation in degrees -//---------------------------------------- -bool GNSS_LG290P::setElevation(uint8_t elevationDegrees) -{ - // Present on >= v05 - if (lg290pFirmwareVersion >= 5) - return (_lg290p->setElevationAngle(elevationDegrees)); + if (response == false && settings.debugGnss) + systemPrintf("Enable RTCM failed at messageNumber %d %s\r\n", messageNumber, + lgMessagesRTCM[messageNumber].msgTextName); + } - // Because we call this during module setup we rely on a positive result - return true; -} + // If any message is enabled, enable RTCM output + if (settings.lg290pMessageRatesRTCMRover[messageNumber] > 0) + enableRTCM = true; -//---------------------------------------- -bool GNSS_LG290P::setHighAccuracyService(bool enableGalileoHas, const char *configurePPP) -{ - bool result = true; + // If we are using IP based corrections, we need to send local data to the PPL + // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 + if (pointPerfectServiceUsesKeys()) + { + // Mark PPL required messages as enabled if rate > 0 + if (settings.lg290pMessageRatesRTCMRover[messageNumber] > 0) + { + if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1019") == 0) + rtcm1019Enabled = true; + else if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1020") == 0) + rtcm1020Enabled = true; + else if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1042") == 0) + rtcm1042Enabled = true; + else if (strcmp(lgMessagesRTCM[messageNumber].msgTextName, "RTCM3-1046") == 0) + rtcm1046Enabled = true; + } + } + } + } - // E6 reception requires version v03/v06 with 'PPP_TEMP' in firmware title - // Present is set during LG290P begin() - if (present.galileoHasCapable == false) - return (result); // We are unable to set this setting to report success + portNumber++; - // Enable E6 and PPP if enabled - if (settings.enableGalileoHas) + // setMessageRateOnPort only supported on v4 and above + if (lg290pFirmwareVersion < 4) + break; // Don't step through portNumbers + } + + if (pointPerfectServiceUsesKeys()) { - // $PQTMCFGPPP,W,2,1,120,0.10,0.15*68 - // Enable E6 HAS, WGS84, 120 timeout, 0.10m Horizontal convergence accuracy threshold, 0.15m Vertical threshold - char paramConfigurePPP[sizeof(settings.configurePPP) + 4]; - snprintf(paramConfigurePPP, sizeof(paramConfigurePPP), ",W,%s", configPppSpacesToCommas(configurePPP)); - if (_lg290p->sendOkCommand("$PQTMCFGPPP", paramConfigurePPP) == true) + enableRTCM = true; // Force enable RTCM output + + // Force on any messages that are needed for PPL + if (rtcm1019Enabled == false) { - systemPrintln("Galileo E6 HAS service enabled"); + if (settings.debugCorrections) + systemPrintln("PPL Enabling RTCM3-1019"); + response &= _lg290p->setMessageRate("RTCM3-1019", 1); } - else + if (rtcm1020Enabled == false) { - systemPrintln("Galileo E6 HAS service failed to enable"); - result = false; + if (settings.debugCorrections) + systemPrintln("PPL Enabling RTCM3-1020"); + + response &= _lg290p->setMessageRate("RTCM3-1020", 1); } - } - else - { - // Turn off HAS/E6 - // $PQTMCFGPPP,W,0* - if (_lg290p->sendOkCommand("$PQTMCFGPPP", ",W,0") == true) + if (rtcm1042Enabled == false) { - systemPrintln("Galileo E6 HAS service disabled"); + if (settings.debugCorrections) + systemPrintln("PPL Enabling RTCM3-1042"); + + response &= _lg290p->setMessageRate("RTCM3-1042", 1); } - else + if (rtcm1046Enabled == false) { - systemPrintln("Galileo E6 HAS service failed to disable"); - result = false; + if (settings.debugCorrections) + systemPrintln("PPL Enabling RTCM3-1046"); + response &= _lg290p->setMessageRate("RTCM3-1046", 1); } } - return (result); -} + // If any RTCM message is enabled, send CFGRTCM + if (enableRTCM == true) + { + if (settings.debugCorrections) + systemPrintf("Enabling Rover RTCM MSM output with rate of %d\r\n", minimumRtcmRate); -//---------------------------------------- -// Enable all the valid messages for this platform -// There are many messages so split into batches. VALSET is limited to 64 max per batch -// Uses dummy newCfg and sendCfg values to be sure we open/close a complete set -//---------------------------------------- -bool GNSS_LG290P::setMessages(int maxRetries) -{ - // Not needed for LG290P - return (true); -} + // Enable MSM4/7 (for faster PPP CSRS results), output at a rate equal to the minimum RTCM rate (EPH Mode = + // 2) PQTMCFGRTCM, W, , , , , , , + // Set MSM_ElevThd to 15 degrees from rftop suggestion -//---------------------------------------- -// Enable all the valid messages for this platform over the USB port -// Add 2 to every UART1 key. This is brittle and non-perfect, but works. -//---------------------------------------- -bool GNSS_LG290P::setMessagesUsb(int maxRetries) -{ - // Not needed for LG290P - return (true); + char msmCommand[40] = {0}; + snprintf(msmCommand, sizeof(msmCommand), "PQTMCFGRTCM,W,%c,0,15,07,06,2,%d", settings.useMSM7 ? '7' : '4', + minimumRtcmRate); + + // PQTMCFGRTCM fails to respond with OK over UART2 of LG290P, so don't look for it + _lg290p->sendOkCommand(msmCommand); + } + + // Messages take effect immediately. Save/Reset is not needed. + + return (response); } //---------------------------------------- -// Set the minimum satellite signal level for navigation. +// Set the dynamic model to use for RTK //---------------------------------------- -bool GNSS_LG290P::setMinCnoRadio(uint8_t cnoValue) +bool GNSS_LG290P::setModel(uint8_t modelNumber) { - // Present on >= v05 - if (lg290pFirmwareVersion >= 5) - return (_lg290p->setCNR((float)cnoValue)); // 0.0 to 99.0 - - // Because we call this during module setup we rely on a positive result + // Not a feature on LG290p return true; } //---------------------------------------- -// Set the dynamic model to use for RTK +// Configure multipath mitigation //---------------------------------------- -bool GNSS_LG290P::setModel(uint8_t modelNumber) +bool GNSS_LG290P::setMultipathMitigation(bool enableMultipathMitigation) { - // Not a feature on LG290p - return (false); + // Does not exist on this platform + return true; } //---------------------------------------- -// Returns the current mode +// Returns the current mode, Base/Rover/etc // 0 - Unknown, 1 - Rover, 2 - Base //---------------------------------------- uint8_t GNSS_LG290P::getMode() { - // The fix rate can only be set in rover mode. Return false if we are in base mode. int currentMode = 0; if (online.gnss) _lg290p->getMode(currentMode); @@ -2551,13 +2369,21 @@ bool GNSS_LG290P::setRate(double secondsBetweenSolutions) if (online.gnss == false) return (false); - // The fix rate can only be set in rover mode. Return false if we are in base mode. - int currentMode = getMode(); - if (currentMode == 2) // Base + // Read, modify, write + + // The fix rate can only be set in rover mode. Return true if we are in base mode. + // This allows the gnssUpdate() to clear the bit. + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) // Base + return (true); // Nothing to modify at this time + + bool response = true; + + // Change to rover mode + if (gnssInRoverMode() == false) { - if (settings.debugGnss || settings.debugCorrections) - systemPrintln("Error: setRate can only be used in Rover mode"); - return (false); + response &= _lg290p->setModeRover(); // Wait for save and reset + if (response == false && settings.debugGnssConfig) + systemPrintln("setRate: Set mode rover failed"); } // The LG290P has a fix interval and a message output rate @@ -2567,8 +2393,6 @@ bool GNSS_LG290P::setRate(double secondsBetweenSolutions) // LG290P has fix interval in milliseconds uint16_t msBetweenSolutions = secondsBetweenSolutions * 1000; - bool response = true; - // The LG290P requires some settings to be applied and then a software reset to occur to take affect // A soft reset takes multiple seconds so we will read, then write if required. uint16_t fixInterval; @@ -2582,76 +2406,53 @@ bool GNSS_LG290P::setRate(double secondsBetweenSolutions) // Set the fix interval response &= _lg290p->setFixInterval(msBetweenSolutions); - // Reboot receiver to apply changes if (response == true) - { - if (settings.debugGnss || settings.debugCorrections) - systemPrintln("Rebooting LG290P"); - - response &= saveConfiguration(); - - response &= softwareReset(); - - int maxTries = 10; - for (int x = 0; x < maxTries; x++) - { - delay(1000); // Wait for device to reboot - if (_lg290p->isConnected()) - break; - else - systemPrintln("Device still rebooting"); - } - } + gnssConfigure(GNSS_CONFIG_RESET); // Reboot receiver to apply changes } } - // If we successfully set rates, then record to settings - if (response) - { - settings.measurementRateMs = msBetweenSolutions; - } - else - { - systemPrintln("Failed to set measurement and navigation rates"); - return (false); - } - - return (true); -} + if (response == false) + systemPrintln("Failed to set measurement rate"); -//---------------------------------------- -bool GNSS_LG290P::setTalkerGNGGA() -{ - // TODO lg290pSetTalkerGNGGA(); - return false; + return (response); } //---------------------------------------- -// Reset GNSS via software command -// Poll for isConnected() +// Enable/disable any output needed for tilt compensation //---------------------------------------- -bool GNSS_LG290P::softwareReset() +bool GNSS_LG290P::setTilt() { - if (online.gnss) + if (present.tiltPossible == false) + return (true); // No tilt on this platform. Report success to clear request. + + if (present.imu_im19 == false) + return (true); // No tilt on this platform. Report success to clear request. + + bool response = true; + + // Tilt is present + if (settings.enableTiltCompensation == true) { - _lg290p->reset(); + // If enabled, configure GNSS to support the tilt sensor - // Poll for a limited amount of time before unit comes back - int x = 0; - while (x++ < 50) + // Tilt sensor requires 5Hz at a minimum + if (settings.measurementRateMs > 200) { - delay(100); // Wait for device to reboot - if (_lg290p->isConnected() == true) - break; - else - systemPrintln("GNSS still rebooting"); + systemPrintln("Increasing GNSS measurement rate to 5Hz for tilt support"); + settings.measurementRateMs = 200; + gnssConfigure(GNSS_CONFIG_FIX_RATE); } - if (x < 50) - return (true); - systemPrintln("GNSS failed to connect after reboot"); + // On the LG290P Flex module, UART 3 of the GNSS is connected to the IMU UART 1 + response &= setBaudRate(3, 115200); + + if (response == false && settings.debugGnssConfig) + systemPrintln("setTilt: setBaud failed."); + + // Enable of GGA, RMC, GST for tilt sensor is done in setMessagesNMEA() } - return (false); + + return response; } //---------------------------------------- @@ -2677,24 +2478,24 @@ bool GNSS_LG290P::surveyInReset() //---------------------------------------- bool GNSS_LG290P::surveyInStart() { - if (online.gnss) - { - bool response = true; + _autoBaseStartTimer = millis(); // Stamp when averaging began - response &= - _lg290p->setSurveyInMode(settings.observationSeconds); // Average for a number of seconds (default is 60) + // We may have already started a survey-in from GNSS's previous NVM settings + if (gnssInBaseSurveyInMode()) + return (true); // No changes needed - if (response == false) - { - systemPrintln("Survey start failed"); - return (false); - } + bool response = true; - _autoBaseStartTimer = millis(); // Stamp when averaging began + // Average for a number of seconds (default is 60) + response &= _lg290p->setSurveyInMode(settings.observationSeconds); - return (response); + if (response == false) + { + systemPrintln("Survey start failed"); + return (false); } - return false; + + return (response); } //---------------------------------------- @@ -2753,13 +2554,6 @@ uint32_t GNSS_LG290P::baudGetMaximum() return (lg290pAllowedRates[lg290pAllowedRatesCount - 1]); } -// Set all NMEA message report rates to one value -void GNSS_LG290P::setNmeaMessageRates(uint8_t msgRate) -{ - for (int x = 0; x < MAX_LG290P_NMEA_MSG; x++) - settings.lg290pMessageRatesNMEA[x] = msgRate; -} - // Set all RTCM Rover message report rates to one value void GNSS_LG290P::setRtcmRoverMessageRates(uint8_t msgRate) { @@ -2853,7 +2647,7 @@ bool lg290pMessageEnabled(char *nmeaSentence, int sentenceLength) } // Return true if we detect this receiver type -bool lg290pIsPresent() +bool lg290pIsPresentOnFlex() { // Locally instantiate the hardware and library so it will release on exit diff --git a/Firmware/RTK_Everywhere/GNSS_Mosaic.h b/Firmware/RTK_Everywhere/GNSS_Mosaic.h index a2cace644..f3a3ac0c0 100644 --- a/Firmware/RTK_Everywhere/GNSS_Mosaic.h +++ b/Firmware/RTK_Everywhere/GNSS_Mosaic.h @@ -9,7 +9,8 @@ GNSS_Mosaic.h #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser -typedef struct { +typedef struct +{ const uint16_t ID; const bool fixedLength; const uint16_t length; // Padded to modulo-4 @@ -17,13 +18,9 @@ typedef struct { } mosaicExpectedID; const mosaicExpectedID mosaicExpectedIDs[] = { - { 4007, true, 96, "PVTGeodetic" }, - { 4013, false, 0, "ChannelStatus" }, - { 4014, false, 0, "ReceiverStatus" }, - { 4059, false, 0, "DiskStatus" }, - { 4090, false, 0, "InputLink" }, - { 4097, false, 0, "EncapsulatedOutput" }, - { 5914, true, 24, "ReceiverTime" }, + {4007, true, 96, "PVTGeodetic"}, {4013, false, 0, "ChannelStatus"}, {4014, false, 0, "ReceiverStatus"}, + {4059, false, 0, "DiskStatus"}, {4090, false, 0, "InputLink"}, {4097, false, 0, "EncapsulatedOutput"}, + {5914, true, 24, "ReceiverTime"}, }; #define MAX_MOSAIC_EXPECTED_SBF (sizeof(mosaicExpectedIDs) / sizeof(mosaicExpectedID)) @@ -34,7 +31,7 @@ const mosaicExpectedID mosaicExpectedIDs[] = { // We actually need four times this many as COM1, COM2, USB1 and DSK1 all need their own individual streams // COM1 uses streams 1 & 2; COM2 uses 3 & 4; USB1 uses 5 & 6; DSK1 uses 7 & 8 #define MOSAIC_NUM_NMEA_STREAMS 2 // X5 supports 10 streams in total -#define MOSAIC_DEFAULT_NMEA_STREAM_INTERVALS {MOSAIC_MSG_RATE_MSEC500,MOSAIC_MSG_RATE_SEC1} +#define MOSAIC_DEFAULT_NMEA_STREAM_INTERVALS {MOSAIC_MSG_RATE_MSEC500, MOSAIC_MSG_RATE_SEC1} // Output SBF PVTGeodetic and ReceiverTime on this stream - on COM1 only // The SBFOutput streams are separate to the NMEAOutput streams. It is OK to start at Stream1. @@ -64,7 +61,8 @@ const mosaicExpectedID mosaicExpectedIDs[] = { // eccf,RxDefault,Boot // eccf,RxDefault,Current -enum mosaicFileDuration_e { +enum mosaicFileDuration_e +{ MOSAIC_FILE_DURATION_HOUR1 = 0, MOSAIC_FILE_DURATION_HOUR6, MOSAIC_FILE_DURATION_HOUR24, @@ -82,15 +80,16 @@ typedef struct } mosaicFileDuration; const mosaicFileDuration mosaicFileDurations[] = { - { "hour1", "IGS1H", "1h", 60 }, - { "hour6", "IGS6H", "6h", 360 }, - { "hour24", "IGS24H", "24h", 1440 }, - { "minute15", "IGS15M", "15min", 15 }, + {"hour1", "IGS1H", "1h", 60}, + {"hour6", "IGS6H", "6h", 360}, + {"hour24", "IGS24H", "24h", 1440}, + {"minute15", "IGS15M", "15min", 15}, }; #define MAX_MOSAIC_FILE_DURATIONS (sizeof(mosaicFileDurations) / sizeof(mosaicFileDuration)) -enum mosaicObsInterval_e { +enum mosaicObsInterval_e +{ MOSAIC_OBS_INTERVAL_SEC1 = 0, MOSAIC_OBS_INTERVAL_SEC2, MOSAIC_OBS_INTERVAL_SEC5, @@ -110,18 +109,14 @@ typedef struct } mosaicObsInterval; const mosaicObsInterval mosaicObsIntervals[] = { - { "sec1", "1s", 1 }, - { "sec2", "2s", 2 }, - { "sec5", "5s", 5 }, - { "sec10", "10s", 10 }, - { "sec15", "15s", 15 }, - { "sec30", "30s", 30 }, - { "sec60", "60s", 60 }, + {"sec1", "1s", 1}, {"sec2", "2s", 2}, {"sec5", "5s", 5}, {"sec10", "10s", 10}, + {"sec15", "15s", 15}, {"sec30", "30s", 30}, {"sec60", "60s", 60}, }; #define MAX_MOSAIC_OBS_INTERVALS (sizeof(mosaicObsIntervals) / sizeof(mosaicObsInterval)) -enum mosaicCOMBaud { +enum mosaicCOMBaud +{ MOSAIC_COM_RATE_BAUD4800 = 0, MOSAIC_COM_RATE_BAUD9600, MOSAIC_COM_RATE_BAUD19200, @@ -143,20 +138,15 @@ typedef struct } mosaicComRate; const mosaicComRate mosaicComRates[] = { - { "baud4800", 4800 }, - { "baud9600", 9600 }, - { "baud19200", 19200 }, - { "baud38400", 38400 }, - { "baud57600", 57600 }, - { "baud115200", 115200 }, - { "baud230400", 230400 }, - { "baud460800", 460800 }, - { "baud921600", 921600 }, + {"baud4800", 4800}, {"baud9600", 9600}, {"baud19200", 19200}, + {"baud38400", 38400}, {"baud57600", 57600}, {"baud115200", 115200}, + {"baud230400", 230400}, {"baud460800", 460800}, {"baud921600", 921600}, }; #define MAX_MOSAIC_COM_RATES (sizeof(mosaicComRates) / sizeof(mosaicComRate)) -enum mosaicPpsIntervals { +enum mosaicPpsIntervals +{ // OFF is dealt with by settings.enableExternalPulse MOSAIC_PPS_INTERVAL_MSEC10 = 0, MOSAIC_PPS_INTERVAL_MSEC20, @@ -185,25 +175,17 @@ typedef struct } mosaicPPSInterval; const mosaicPPSInterval mosaicPPSIntervals[] = { - { "msec10", "10ms", 10000 }, - { "msec20", "20ms", 20000 }, - { "msec50", "50ms", 50000 }, - { "msec100", "100ms", 100000 }, - { "msec200", "200ms", 200000 }, - { "msec250", "250ms", 250000 }, - { "msec500", "500ms", 500000 }, - { "sec1", "1s", 1000000 }, - { "sec2", "2s", 2000000 }, - { "sec4", "4s", 4000000 }, - { "sec5", "5s", 5000000 }, - { "sec10", "10s", 10000000 }, - { "sec30", "30s", 30000000 }, - { "sec60", "60s", 60000000 }, + {"msec10", "10ms", 10000}, {"msec20", "20ms", 20000}, {"msec50", "50ms", 50000}, + {"msec100", "100ms", 100000}, {"msec200", "200ms", 200000}, {"msec250", "250ms", 250000}, + {"msec500", "500ms", 500000}, {"sec1", "1s", 1000000}, {"sec2", "2s", 2000000}, + {"sec4", "4s", 4000000}, {"sec5", "5s", 5000000}, {"sec10", "10s", 10000000}, + {"sec30", "30s", 30000000}, {"sec60", "60s", 60000000}, }; #define MAX_MOSAIC_PPS_INTERVALS (sizeof(mosaicPPSIntervals) / sizeof(mosaicPPSInterval)) -enum mosaicConstellations { +enum mosaicConstellations +{ MOSAIC_SIGNAL_CONSTELLATION_GPS = 0, MOSAIC_SIGNAL_CONSTELLATION_GLONASS, MOSAIC_SIGNAL_CONSTELLATION_GALILEO, @@ -224,13 +206,8 @@ typedef struct // Constellations monitored/used for fix const mosaicSignalConstellation mosaicSignalConstellations[] = { - {"GPS","GPS"}, - {"GLONASS","GLONASS"}, - {"GALILEO","Galileo"}, - {"SBAS","SBAS"}, - {"BEIDOU","BeiDou"}, - {"QZSS","QZSS"}, - {"NAVIC","NavIC"}, + {"GPS", "GPS"}, {"GLONASS", "GLONASS"}, {"GALILEO", "Galileo"}, {"SBAS", "SBAS"}, + {"BEIDOU", "BeiDou"}, {"QZSS", "QZSS"}, {"NAVIC", "NavIC"}, }; #define MAX_MOSAIC_CONSTELLATIONS (sizeof(mosaicSignalConstellations) / sizeof(mosaicSignalConstellation)) @@ -238,7 +215,8 @@ const mosaicSignalConstellation mosaicSignalConstellations[] = { // Enum to define message output rates // Don't allow rate to be "off" // If the user wants to disable a message, the stream (mosaicStreamIntervalsNMEA etc.) should be set to 0 instead -enum mosaicMessageRates { +enum mosaicMessageRates +{ // MOSAIC_MSG_RATE_OFF = 0, // MOSAIC_MSG_RATE_ONCHANGE, MOSAIC_MSG_RATE_MSEC10 = 0, @@ -276,26 +254,10 @@ typedef struct const mosaicMsgRate mosaicMsgRates[] = { // { "off"}, // { "OnChange"}, - { "msec10", "10ms" }, - { "msec20", "20ms" }, - { "msec40", "40ms" }, - { "msec50", "50ms" }, - { "msec100", "100ms" }, - { "msec200", "200ms" }, - { "msec500", "500ms" }, - { "sec1", "1s" }, - { "sec2", "2s" }, - { "sec5", "5s" }, - { "sec10", "10s" }, - { "sec15", "15s" }, - { "sec30", "30s" }, - { "sec60", "60s" }, - { "min2", "2min" }, - { "min5", "5min" }, - { "min10", "10min" }, - { "min15", "15min" }, - { "min30", "30min" }, - { "min60", "60min" }, + {"msec10", "10ms"}, {"msec20", "20ms"}, {"msec40", "40ms"}, {"msec50", "50ms"}, {"msec100", "100ms"}, + {"msec200", "200ms"}, {"msec500", "500ms"}, {"sec1", "1s"}, {"sec2", "2s"}, {"sec5", "5s"}, + {"sec10", "10s"}, {"sec15", "15s"}, {"sec30", "30s"}, {"sec60", "60s"}, {"min2", "2min"}, + {"min5", "5min"}, {"min10", "10min"}, {"min15", "15min"}, {"min30", "30min"}, {"min60", "60min"}, }; // Check MAX_MOSAIC_MSG_RATES == MOSAIC_NUM_MSG_RATES @@ -314,16 +276,10 @@ typedef struct // Stream 0 is off; stream 1 defaults to MSEC500; stream 2 defaults to SEC1 const mosaicNMEAMsg mosaicMessagesNMEA[] = { // NMEA - {"ALM", 0}, {"AVR", 0}, {"DTM", 0}, - {"GBS", 0}, {"GFA", 0}, {"GGA", 1}, - {"GGK", 0}, {"GGQ", 0}, {"GLL", 0}, - {"GMP", 0}, {"GNS", 0}, {"GRS", 0}, - {"GSA", 1}, {"GST", 1}, {"GSV", 2}, - {"HDT", 0}, {"HRP", 0}, {"LLK", 0}, - {"LLQ", 0}, {"RBD", 0}, {"RBP", 0}, - {"RBV", 0}, {"RMC", 1}, {"ROT", 0}, - {"SNC", 0}, {"TFM", 0}, {"THS", 0}, - {"TXTbase", 0}, {"VTG", 0}, {"ZDA", 0}, + {"ALM", 0}, {"AVR", 0}, {"DTM", 0}, {"GBS", 0}, {"GFA", 0}, {"GGA", 1}, {"GGK", 0}, {"GGQ", 0}, + {"GLL", 0}, {"GMP", 0}, {"GNS", 0}, {"GRS", 0}, {"GSA", 1}, {"GST", 1}, {"GSV", 2}, {"HDT", 0}, + {"HRP", 0}, {"LLK", 0}, {"LLQ", 0}, {"RBD", 0}, {"RBP", 0}, {"RBV", 0}, {"RMC", 1}, {"ROT", 0}, + {"SNC", 0}, {"TFM", 0}, {"THS", 0}, {"TXTbase", 0}, {"VTG", 0}, {"ZDA", 0}, }; #define MAX_MOSAIC_NMEA_MSG (sizeof(mosaicMessagesNMEA) / sizeof(mosaicNMEAMsg)) @@ -357,7 +313,8 @@ const mosaicRTCMv2Msg mosaicMessagesRTCMv2[] = { */ -enum mosaicRTCMv3IntervalGroups { +enum mosaicRTCMv3IntervalGroups +{ MOSAIC_RTCM_V3_INTERVAL_GROUP_RTCM1001_2 = 0, MOSAIC_RTCM_V3_INTERVAL_GROUP_RTCM1003_4, MOSAIC_RTCM_V3_INTERVAL_GROUP_RTCM1005_6, @@ -392,32 +349,15 @@ typedef struct } mosaicRTCMv3MsgIntervalGroup; const mosaicRTCMv3MsgIntervalGroup mosaicRTCMv3MsgIntervalGroups[] = { - { "RTCM1001|2", 1.0 }, - { "RTCM1003|4", 1.0 }, - { "RTCM1005|6", 1.0 }, - { "RTCM1007|8", 1.0 }, - { "RTCM1009|10", 1.0 }, - { "RTCM1011|12", 1.0 }, - { "RTCM1013", 1.0 }, - { "RTCM1019", 1.0 }, - { "RTCM1020", 1.0 }, - { "RTCM1029", 1.0 }, - { "RTCM1033", 10.0 }, - { "RTCM1042", 1.0 }, - { "RTCM1044", 1.0 }, - { "RTCM1045", 1.0 }, - { "RTCM1046", 1.0 }, - { "MSM1", 1.0 }, - { "MSM2", 1.0 }, - { "MSM3", 1.0 }, - { "MSM4", 1.0 }, - { "MSM5", 1.0 }, - { "MSM6", 1.0 }, - { "MSM7", 1.0 }, - { "RTCM1230", 1.0 }, + {"RTCM1001|2", 1.0}, {"RTCM1003|4", 1.0}, {"RTCM1005|6", 1.0}, {"RTCM1007|8", 1.0}, {"RTCM1009|10", 1.0}, + {"RTCM1011|12", 1.0}, {"RTCM1013", 1.0}, {"RTCM1019", 1.0}, {"RTCM1020", 1.0}, {"RTCM1029", 1.0}, + {"RTCM1033", 10.0}, {"RTCM1042", 1.0}, {"RTCM1044", 1.0}, {"RTCM1045", 1.0}, {"RTCM1046", 1.0}, + {"MSM1", 1.0}, {"MSM2", 1.0}, {"MSM3", 1.0}, {"MSM4", 1.0}, {"MSM5", 1.0}, + {"MSM6", 1.0}, {"MSM7", 1.0}, {"RTCM1230", 1.0}, }; -#define MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS (sizeof(mosaicRTCMv3MsgIntervalGroups) / sizeof(mosaicRTCMv3MsgIntervalGroup)) +#define MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS \ + (sizeof(mosaicRTCMv3MsgIntervalGroups) / sizeof(mosaicRTCMv3MsgIntervalGroup)) typedef struct { @@ -456,57 +396,57 @@ const mosaicRTCMv3Msg mosaicMessagesRTCMv3[] = { {"MSM5", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, {"MSM6", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, {"MSM7", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, -/* - {"RTCM1071", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, - {"RTCM1072", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, - {"RTCM1073", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, - {"RTCM1074", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, - {"RTCM1075", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, - {"RTCM1076", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, - {"RTCM1077", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, - {"RTCM1081", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, - {"RTCM1082", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, - {"RTCM1083", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, - {"RTCM1084", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, - {"RTCM1085", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, - {"RTCM1086", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, - {"RTCM1087", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, - {"RTCM1091", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, - {"RTCM1092", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, - {"RTCM1093", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, - {"RTCM1094", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, - {"RTCM1095", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, - {"RTCM1096", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, - {"RTCM1097", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, - {"RTCM1101", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, - {"RTCM1102", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, - {"RTCM1103", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, - {"RTCM1104", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, - {"RTCM1105", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, - {"RTCM1106", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, - {"RTCM1107", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, - {"RTCM1111", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, - {"RTCM1112", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, - {"RTCM1113", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, - {"RTCM1114", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, - {"RTCM1115", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, - {"RTCM1116", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, - {"RTCM1117", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, - {"RTCM1121", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, - {"RTCM1122", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, - {"RTCM1123", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, - {"RTCM1124", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, - {"RTCM1125", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, - {"RTCM1126", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, - {"RTCM1127", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, - {"RTCM1131", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, - {"RTCM1132", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, - {"RTCM1133", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, - {"RTCM1134", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, - {"RTCM1135", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, - {"RTCM1136", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, - {"RTCM1137", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, -*/ + /* + {"RTCM1071", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, + {"RTCM1072", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, + {"RTCM1073", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, + {"RTCM1074", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, + {"RTCM1075", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, + {"RTCM1076", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, + {"RTCM1077", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, + {"RTCM1081", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, + {"RTCM1082", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, + {"RTCM1083", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, + {"RTCM1084", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, + {"RTCM1085", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, + {"RTCM1086", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, + {"RTCM1087", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, + {"RTCM1091", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, + {"RTCM1092", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, + {"RTCM1093", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, + {"RTCM1094", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, + {"RTCM1095", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, + {"RTCM1096", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, + {"RTCM1097", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, + {"RTCM1101", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, + {"RTCM1102", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, + {"RTCM1103", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, + {"RTCM1104", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, + {"RTCM1105", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, + {"RTCM1106", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, + {"RTCM1107", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, + {"RTCM1111", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, + {"RTCM1112", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, + {"RTCM1113", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, + {"RTCM1114", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, + {"RTCM1115", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, + {"RTCM1116", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, + {"RTCM1117", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, + {"RTCM1121", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, + {"RTCM1122", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, + {"RTCM1123", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, + {"RTCM1124", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, + {"RTCM1125", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, + {"RTCM1126", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, + {"RTCM1127", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, + {"RTCM1131", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM1, false}, + {"RTCM1132", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM2, false}, + {"RTCM1133", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM3, false}, + {"RTCM1134", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM4, false}, + {"RTCM1135", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM5, false}, + {"RTCM1136", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM6, false}, + {"RTCM1137", MOSAIC_RTCM_V3_INTERVAL_GROUP_MSM7, false}, + */ {"RTCM1230", MOSAIC_RTCM_V3_INTERVAL_GROUP_RTCM1230, false}, }; @@ -533,14 +473,14 @@ typedef struct } mosaicReceiverDynamic; const mosaicReceiverDynamic mosaicReceiverDynamics[] = { - { "Static", "Static" }, - { "Quasistatic","Quasistatic" }, - { "Pedestrian","Pedestrian" }, - { "Automotive", "Automotive" }, - { "RaceCar", "Race Car" }, - { "HeavyMachinery", "Heavy Machinery" }, - { "UAV", "UAV" }, - { "Unlimited", "Unlimited" }, + {"Static", "Static"}, + {"Quasistatic", "Quasistatic"}, + {"Pedestrian", "Pedestrian"}, + {"Automotive", "Automotive"}, + {"RaceCar", "Race Car"}, + {"HeavyMachinery", "Heavy Machinery"}, + {"UAV", "UAV"}, + {"Unlimited", "Unlimited"}, }; #define MAX_MOSAIC_RX_DYNAMICS (sizeof(mosaicReceiverDynamics) / sizeof(mosaicReceiverDynamic)) @@ -550,23 +490,23 @@ bool mosaicX5waitCR(unsigned long timeout = 25); // Header class GNSS_MOSAIC : GNSS { - // The mosaic-X5 does not have self-contained interface library. - // But the ZED-F9P, UM980 and LG290P all do. - // On the X5, we communicate manually over serial2GNSS using functions like - // sendWithResponse and sendAndWaitForIdle. - // In essence, the interface library is wholly contained in this class. - // TODO: consider breaking the mosaic comms functions out into their own library - // and add a private library class instance here. + // The mosaic-X5 does not have self-contained interface library. + // But the ZED-F9P, UM980 and LG290P all do. + // On the X5, we communicate manually over serial2GNSS using functions like + // sendWithResponse and sendAndWaitForIdle. + // In essence, the interface library is wholly contained in this class. + // TODO: consider breaking the mosaic comms functions out into their own library + // and add a private library class instance here. protected: // Flag which indicates GNSS is blocking (needs exclusive access to the UART) bool _isBlocking = false; // These globals are updated regularly via the SBF parser - double _clkBias_ms; // PVTGeodetic RxClkBias (will be sawtooth unless clock steering is enabled) + double _clkBias_ms; // PVTGeodetic RxClkBias (will be sawtooth unless clock steering is enabled) bool _determiningFixedPosition; // PVTGeodetic Mode Bit 6 - bool _antennaIsOpen; // ReceiverStatus RxState Bit 1 ACTIVEANTENNA indicates antenna current draw - bool _antennaIsShorted; // ReceiverStatus RxError Bit 5 ANTENNA indicates antenna overcurrent + bool _antennaIsOpen; // ReceiverStatus RxState Bit 1 ACTIVEANTENNA indicates antenna current draw + bool _antennaIsShorted; // ReceiverStatus RxError Bit 5 ANTENNA indicates antenna overcurrent // Record NrBytesReceived so we can tell if Radio Ext (COM2) is receiving correction data. // On the mosaic, we know that InputLink will arrive at 1Hz. But on the ZED, UBX-MON-COMMS @@ -584,18 +524,17 @@ class GNSS_MOSAIC : GNSS // Not Rover or Base specific (ie, baud rates) // Outputs: // Returns true if successfully configured and false upon failure - bool configureGNSS(); + bool configure(); // Set the minimum satellite signal level for navigation. - bool setMinCnoRadio (uint8_t cnoValue); + bool setMinCN0(uint8_t cnoValue); public: - // Allow access from parser routines - float _latStdDev; - float _lonStdDev; - bool _receiverSetupSeen; - bool _diskStatusSeen; + float _latStdDev; + float _lonStdDev; + bool _receiverSetupSeen; + bool _diskStatusSeen; struct svTracking_t { uint8_t SVID; @@ -607,8 +546,10 @@ class GNSS_MOSAIC : GNSS struct find_sv { uint8_t findThisSv; - find_sv(uint8_t sv) : findThisSv(sv) {} - bool operator () (const svTracking_t& m) const + find_sv(uint8_t sv) : findThisSv(sv) + { + } + bool operator()(const svTracking_t &m) const { return m.SVID == findThisSv; } @@ -618,19 +559,20 @@ class GNSS_MOSAIC : GNSS { const unsigned long expireAfter_millis = 2000; unsigned long millisNow; - find_stale_sv(unsigned long now) : millisNow(now) {} - bool operator () (const svTracking_t& m) const + find_stale_sv(unsigned long now) : millisNow(now) + { + } + bool operator()(const svTracking_t &m) const { return (millisNow > (m.lastSeen + expireAfter_millis)); } }; // Constructor - GNSS_MOSAIC() : _determiningFixedPosition(true), _clkBias_ms(0), - _latStdDev(999.9), _lonStdDev(999.9), _receiverSetupSeen(false), - _radioExtBytesReceived_millis(0), _diskStatusSeen(false), - _antennaIsOpen(false), _antennaIsShorted(false), - GNSS() + GNSS_MOSAIC() + : _determiningFixedPosition(true), _clkBias_ms(0), _latStdDev(999.9), _lonStdDev(999.9), + _receiverSetupSeen(false), _radioExtBytesReceived_millis(0), _diskStatusSeen(false), _antennaIsOpen(false), + _antennaIsShorted(false), GNSS() { svInTracking.clear(); } @@ -649,7 +591,7 @@ class GNSS_MOSAIC : GNSS bool baudIsAllowed(uint32_t baudRate); uint32_t baudGetMinimum(); uint32_t baudGetMaximum(); - + // Connect to GNSS and identify particulars void begin(); @@ -658,12 +600,6 @@ class GNSS_MOSAIC : GNSS // Returns true when an external event occurs and false if no event bool beginExternalEvent(); - // Setup the timepulse output on the PPS pin for external triggering - // Outputs - // Returns true if the pin was successfully setup and false upon - // failure - bool beginPPS(); - bool checkNMEARates(); bool checkPPPRates(); @@ -679,8 +615,6 @@ class GNSS_MOSAIC : GNSS // Configure mosaic-X5 L-Band bool configureLBand(bool enableLBand, uint32_t LBandFreq = 0); - bool configureLogging(); - // Configure specific aspects of the receiver for NTP mode bool configureNtpMode(); @@ -710,23 +644,6 @@ class GNSS_MOSAIC : GNSS void debuggingEnable(); - void enableGgaForNtrip(); - - // Turn on all the enabled NMEA messages on COM1 - bool enableNMEA(); - - // Turn on all the enabled RTCM Base messages on COM1, COM2 and USB1 (if enabled) - bool enableRTCMBase(); - - // Turn on all the enabled RTCM Rover messages on COM1, COM2 and USB1 (if enabled) - bool enableRTCMRover(); - - // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted - // even if there is no GPS fix. We use it to test serial output. - // Outputs: - // Returns true if successfully started and false upon failure - bool enableRTCMTest(); - // Restore the GNSS to the factory settings void factoryReset(); @@ -739,11 +656,21 @@ class GNSS_MOSAIC : GNSS // Returns true if successfully started and false upon failure bool fixedBaseStart(); + bool fixRateIsAllowed(uint32_t fixRateMs); + + // Return min/max rate in ms + uint32_t fixRateGetMinimumMs(); + + uint32_t fixRateGetMaximumMs(); + // Return the number of active/enabled messages uint8_t getActiveMessageCount(); // Return the number of active/enabled RTCM messages - uint8_t getActiveRtcmMessageCount() {return(0);} + uint8_t getActiveRtcmMessageCount() + { + return (0); + } // Get the altitude // Outputs: @@ -780,7 +707,7 @@ class GNSS_MOSAIC : GNSS // Returns the horizontal position accuracy or zero if offline float getHorizontalAccuracy(); - const char * getId(); + const char *getId(); // Get the latitude value // Outputs: @@ -804,6 +731,9 @@ class GNSS_MOSAIC : GNSS // Returns minutes or zero if not online uint8_t getMinute(); + // Returns the current mode: Base/Rover/etc + uint8_t getMode(); + // Returns month number or zero if not online uint8_t getMonth(); @@ -821,9 +751,9 @@ class GNSS_MOSAIC : GNSS // Returns the seconds between solutions double getRateS(); - const char * getRtcmDefaultString(); + const char *getRtcmDefaultString(); - const char * getRtcmLowDataRateString(); + const char *getRtcmLowDataRateString(); // Given the name of a message, return the array number int getRtcmMessageNumberByName(const char *msgName); @@ -848,9 +778,10 @@ class GNSS_MOSAIC : GNSS // Returns full year, ie 2023, not 23. uint16_t getYear(); - // Returns true if the device is in Rover mode - // Currently the only two modes are Rover or Base - bool inRoverMode(); + // Helper functions for the current mode as read from the GNSS receiver + bool gnssInBaseFixedMode(); + bool gnssInBaseSurveyInMode(); + bool gnssInRoverMode(); // Antenna Short / Open detection bool isAntennaShorted(); @@ -887,7 +818,8 @@ class GNSS_MOSAIC : GNSS // Send commands out the UART to see if a mosaic module is present bool isPresent(); - bool isPresentOnSerial(HardwareSerial *serialPort, const char *command, const char *response, const char *console, int retryLimit = 20); + bool isPresentOnSerial(HardwareSerial *serialPort, const char *command, const char *response, const char *console, + int retryLimit = 20); bool mosaicIsPresentOnFlex(); // Some functions (L-Band area frequency determination) merely need @@ -936,6 +868,9 @@ class GNSS_MOSAIC : GNSS // Returns the number of correction data bytes written int pushRawData(uint8_t *dataToSend, int dataLength); + // Hardware or software reset the GNSS receiver + bool reset(); + uint16_t rtcmBufferAvailable(); // If LBand is being used, ignore any RTCM that may come in from the GNSS @@ -965,21 +900,12 @@ class GNSS_MOSAIC : GNSS // responseSize: Maximum number of bytes to copy // Outputs: // Returns true if the response was received and false upon failure - bool sendAndWaitForIdle(const char *message, - const char *reply, - unsigned long timeout = 1000, - unsigned long idle = 25, - char *response = nullptr, - size_t responseSize = 0, - bool debug = true); - bool sendAndWaitForIdle(HardwareSerial *serialPort, - const char *message, - const char *reply, - unsigned long timeout = 1000, - unsigned long idle = 25, - char *response = nullptr, - size_t responseSize = 0, + bool sendAndWaitForIdle(const char *message, const char *reply, unsigned long timeout = 1000, + unsigned long idle = 25, char *response = nullptr, size_t responseSize = 0, bool debug = true); + bool sendAndWaitForIdle(HardwareSerial *serialPort, const char *message, const char *reply, + unsigned long timeout = 1000, unsigned long idle = 25, char *response = nullptr, + size_t responseSize = 0, bool debug = true); // Send message. Wait for up to timeout millis for reply to arrive // If the reply is received, keep reading bytes until the serial port has @@ -994,13 +920,8 @@ class GNSS_MOSAIC : GNSS // responseSize: Maximum number of bytes to copy // Outputs: // Returns true if the response was received and false upon failure - bool sendAndWaitForIdle(String message, - const char *reply, - unsigned long timeout = 1000, - unsigned long idle = 25, - char *response = nullptr, - size_t responseSize = 0, - bool debug = true); + bool sendAndWaitForIdle(String message, const char *reply, unsigned long timeout = 1000, unsigned long idle = 25, + char *response = nullptr, size_t responseSize = 0, bool debug = true); // Send message. Wait for up to timeout millis for reply to arrive // If the reply has started to be received when timeout is reached, wait for a further wait millis @@ -1016,18 +937,10 @@ class GNSS_MOSAIC : GNSS // responseSize: Maximum number of bytes to copy // Outputs: // Returns true if the response was received and false upon failure - bool sendWithResponse(const char *message, - const char *reply, - unsigned long timeout = 1000, - unsigned long wait = 25, - char *response = nullptr, - size_t responseSize = 0); - bool sendWithResponse(HardwareSerial *serialPort, - const char *message, - const char *reply, - unsigned long timeout = 1000, - unsigned long wait = 25, - char *response = nullptr, + bool sendWithResponse(const char *message, const char *reply, unsigned long timeout = 1000, unsigned long wait = 25, + char *response = nullptr, size_t responseSize = 0); + bool sendWithResponse(HardwareSerial *serialPort, const char *message, const char *reply, + unsigned long timeout = 1000, unsigned long wait = 25, char *response = nullptr, size_t responseSize = 0); // Send message. Wait for up to timeout millis for reply to arrive @@ -1043,12 +956,8 @@ class GNSS_MOSAIC : GNSS // responseSize: Maximum number of bytes to copy // Outputs: // Returns true if the response was received and false upon failure - bool sendWithResponse(String message, - const char *reply, - unsigned long timeout = 1000, - unsigned long wait = 25, - char *response = nullptr, - size_t responseSize = 0); + bool sendWithResponse(String message, const char *reply, unsigned long timeout = 1000, unsigned long wait = 25, + char *response = nullptr, size_t responseSize = 0); // Set the baud rate of mosaic-X5 COM1 // This is used during the Bluetooth test @@ -1060,6 +969,12 @@ class GNSS_MOSAIC : GNSS bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); // From the super class bool setBaudRateCOM(uint8_t port, uint32_t baudRate); // Original X5 implementation + bool setBaudRateComm(uint32_t baudRate); + + bool setBaudRateData(uint32_t baudRate); + + bool setBaudRateRadio(uint32_t baudRate); + // Enable all the valid constellations and bands for this platform bool setConstellations(); @@ -1067,25 +982,41 @@ class GNSS_MOSAIC : GNSS // Always update if force is true. Otherwise, only update if enable has changed state bool setCorrRadioExtPort(bool enable, bool force); - bool setDataBaudRate(uint32_t baud); - // Set the elevation in degrees // Inputs: // elevationDegrees: The elevation value in degrees bool setElevation(uint8_t elevationDegrees); - // Enable all the valid messages for this platform - bool setMessages(int maxRetries); + // Enable or disable HAS E6 capability + bool setHighAccuracyService(bool enableGalileoHas); + + // Configure any logging settings - currently mosaic-X5 specific + bool setLogging(); + + // Turn on all the enabled NMEA messages on COM1 + bool setMessagesNMEA(); + + // Turn on all the enabled RTCM Base messages on COM1, COM2 and USB1 (if enabled) + bool setMessagesRTCMBase(); - // Enable all the valid messages for this platform over the USB port - bool setMessagesUsb(int maxRetries); + // Turn on all the enabled RTCM Rover messages on COM1, COM2 and USB1 (if enabled) + bool setMessagesRTCMRover(); // Set the dynamic model to use for RTK // Inputs: // modelNumber: Number of the model to use, provided by radio library bool setModel(uint8_t modelNumber); - bool setRadioBaudRate(uint32_t baud); + bool setMultipathMitigation(bool enableMultipathMitigation); + + // Given the name of a message, find it, and set the rate + bool setNmeaMessageRateByName(const char *msgName, uint8_t msgRate); + + // Setup the timepulse output on the PPS pin for external triggering + // Outputs + // Returns true if the pin was successfully setup and false upon + // failure + bool setPPS(); // Specify the interval between solutions // Inputs: @@ -1095,10 +1026,8 @@ class GNSS_MOSAIC : GNSS // failure bool setRate(double secondsBetweenSolutions); - bool setTalkerGNGGA(); - - // Hotstart GNSS to try to get RTK lock - bool softwareReset(); + // Enable/disable any output needed for tilt compensation + bool setTilt(); bool standby(); @@ -1142,4 +1071,4 @@ class GNSS_MOSAIC : GNSS void waitSBFReceiverSetup(HardwareSerial *serialPort, unsigned long timeout); }; -#endif // __GNSS_MOSAIC_H__ +#endif // __GNSS_MOSAIC_H__ diff --git a/Firmware/RTK_Everywhere/GNSS_Mosaic.ino b/Firmware/RTK_Everywhere/GNSS_Mosaic.ino index 063b0cbf3..48ce17524 100644 --- a/Firmware/RTK_Everywhere/GNSS_Mosaic.ino +++ b/Firmware/RTK_Everywhere/GNSS_Mosaic.ino @@ -53,10 +53,10 @@ void printMosaicCardSpace() //---------------------------------------- void menuLogMosaic() { - if (!present.mosaicMicroSd) // This may be needed for the G5 P3 ? + if (present.mosaicMicroSd == false) // This may be needed for the G5 P3 ? + { return; - - bool applyChanges = false; + } while (1) { @@ -95,12 +95,12 @@ void menuLogMosaic() if (incoming == 1) { settings.enableLogging ^= 1; - applyChanges = true; + gnssConfigure(GNSS_CONFIG_LOGGING); // Request receiver to use new settings } else if (incoming == 2) { settings.enableLoggingRINEX ^= 1; - applyChanges = true; + gnssConfigure(GNSS_CONFIG_LOGGING); // Request receiver to use new settings } else if (incoming == 3 && settings.enableLoggingRINEX == true) { @@ -116,7 +116,7 @@ void menuLogMosaic() if (duration >= 1 && duration <= MAX_MOSAIC_FILE_DURATIONS) { settings.RINEXFileDuration = duration - 1; - applyChanges = true; + gnssConfigure(GNSS_CONFIG_LOGGING); // Request receiver to use new settings } } else if (incoming == 4 && settings.enableLoggingRINEX == true) @@ -133,7 +133,7 @@ void menuLogMosaic() if (interval >= 1 && interval <= MAX_MOSAIC_OBS_INTERVALS) { settings.RINEXObsInterval = interval - 1; - applyChanges = true; + gnssConfigure(GNSS_CONFIG_LOGGING); // Request receiver to use new settings } } else if (incoming == 'x') @@ -146,17 +146,6 @@ void menuLogMosaic() printUnknown(incoming); } - // Apply changes - if (applyChanges) - { - GNSS_MOSAIC *mosaic = (GNSS_MOSAIC *)gnss; - - mosaic->configureLogging(); // This will enable / disable RINEX logging - mosaic->enableNMEA(); // Enable NMEA messages - this will enable/disable the DSK1 streams - mosaic->saveConfiguration(); // Save the configuration - setLoggingType(); // Update Standard, PPP, or custom for icon selection - } - clearBuffer(); // Empty buffer of any newline chars } @@ -251,14 +240,14 @@ void GNSS_MOSAIC::begin() return; } - if(isPresent() == false) //Detect if the module is present + if (isPresent() == false) // Detect if the module is present return; int retries = 0; int retryLimit = 3; // Set COM1 baud rate. X5 defaults to 115200. Settings default to 230400bps - while (!setBaudRateCOM(1, settings.dataPortBaud)) + while (!setBaudRateComm(settings.dataPortBaud)) { if (retries == retryLimit) break; @@ -272,13 +261,6 @@ void GNSS_MOSAIC::begin() return; } - // Set COM2 (Radio) and COM3 (Data) baud rates - setRadioBaudRate(settings.radioPortBaud); - setDataBaudRate(settings.dataPortBaud); - - // Set COM2 (Radio) protocol(s) - setCorrRadioExtPort(settings.enableExtCorrRadio, true); // Force the setting - updateSD(); // Check card size and free space _receiverSetupSeen = false; @@ -309,13 +291,9 @@ void GNSS_MOSAIC::begin() return; } - if(isPresent() == false) //Detect if the module is present + if (isPresent() == false) // Detect if the module is present return; - // Set COM2 (Radio) and COM3 (Data) baud rates - setRadioBaudRate(settings.radioPortBaud); - setDataBaudRate(settings.dataPortBaud); // Probably redundant - // Set COM2 (Radio) protocol(s) setCorrRadioExtPort(settings.enableExtCorrRadio, true); // Force the setting @@ -363,9 +341,6 @@ bool GNSS_MOSAIC::beginExternalEvent() // Note: You can't disable events via sep. Event cannot be set to "none"... // All you can do is disable the ExtEvent stream - if (online.gnss == false) - return (false); - if (settings.dataPortChannel != MUX_PPS_EVENTTRIGGER) return (true); // No need to configure PPS if port is not selected @@ -384,11 +359,8 @@ bool GNSS_MOSAIC::beginExternalEvent() // Returns true if the pin was successfully setup and false upon // failure //---------------------------------------- -bool GNSS_MOSAIC::beginPPS() +bool GNSS_MOSAIC::setPPS() { - if (online.gnss == false) - return (false); - if (settings.dataPortChannel != MUX_PPS_EVENTTRIGGER) return (true); // No need to configure PPS if port is not selected @@ -436,68 +408,9 @@ bool GNSS_MOSAIC::checkPPPRates() return settings.enableLoggingRINEX; } +// Enable / disable RINEX logging //---------------------------------------- -// Configure the Base -// Outputs: -// Returns true if successfully configured and false upon failure -//---------------------------------------- -bool GNSS_MOSAIC::configureBase() -{ - /* - Set mode to Static + dynamic model - Enable RTCM Base messages - Enable NMEA messages - - mosaicX5AutoBaseStart() will start "survey-in" - mosaicX5FixedBaseStart() will start fixed base - */ - - if (online.gnss == false) - { - systemPrintln("GNSS not online"); - return (false); - } - - if (settings.gnssConfiguredBase) - { - systemPrintln("Skipping mosaic Base configuration"); - setLoggingType(); // Needed because logUpdate exits early and never calls setLoggingType - return true; - } - - bool response = true; - - response &= setModel(MOSAIC_DYN_MODEL_STATIC); - - response &= setElevation(settings.minElev); - - response &= setMinCnoRadio(settings.minCNO); - - response &= setConstellations(); - - response &= enableRTCMBase(); - - response &= enableNMEA(); - - response &= configureLogging(); - - setLoggingType(); // Update Standard, PPP, or custom for icon selection - - // Save the current configuration into non-volatile memory (NVM) - response &= saveConfiguration(); - - if (response == false) - { - systemPrintln("mosaic-X5 Base failed to configure"); - } - - settings.gnssConfiguredBase = response; - - return (response); -} - -//---------------------------------------- -bool GNSS_MOSAIC::configureLogging() +bool GNSS_MOSAIC::setLogging() { bool response = true; String setting; @@ -588,6 +501,62 @@ bool GNSS_MOSAIC::configureLBand(bool enableLBand, uint32_t LBandFreq) return result; } +//---------------------------------------- +// Setup the general configuration of the GNSS +// Not Rover or Base specific (ie, baud rates) +// Outputs: +// Returns true if successfully configured and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::configure() +{ + // Attempt 3 tries on MOSAICX5 config + for (int x = 0; x < 3; x++) + { + if (configureOnce() == true) + return (true); + } + + systemPrintln("mosaic-X5 failed to configure"); + return (false); +} + +//---------------------------------------- +// Configure the Base +// Outputs: +// Returns true if successfully configured and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::configureBase() +{ + if (settings.fixedBase == false && gnssInBaseSurveyInMode()) + return (true); // No changes needed + + if (settings.fixedBase == true) + { + // 0 - Unknown, 1 - Rover, 2 - Base Survey In, 3 - Base Fixed Geodetic, 4 - Base Fixed Cartesian + int currentMode = getMode(); + if (currentMode == 3 && settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) + return (true); // No changes needed + if (currentMode == 4 && settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) + return (true); // No changes needed + } + + // Assume we are changing from Rover to Base, request any additional config changes + + bool response = true; + + // Set the model to static for Base mode + response &= setModel(MOSAIC_DYN_MODEL_STATIC); + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); + + if (response == false) + { + systemPrintln("mosaic-X5 Base failed to configure"); + } + + return (response); +} + //---------------------------------------- // Perform the GNSS configuration // Outputs: @@ -597,20 +566,14 @@ bool GNSS_MOSAIC::configureOnce() { /* Configure COM1 - Set minCNO + Set minCN0 Set elevationAngle Set Constellations NMEA Messages are enabled by enableNMEA - RTCMv3 messages are enabled by enableRTCMRover / enableRTCMBase + RTCMv3 messages are enabled by setMessagesRTCMRover / setMessagesRTCMBase */ - if (settings.gnssConfiguredOnce) - { - systemPrintln("mosaic configuration maintained"); - return (true); - } - bool response = true; // Configure COM1. NMEA and RTCMv3 will be encapsulated in SBF format @@ -645,44 +608,18 @@ bool GNSS_MOSAIC::configureOnce() response &= sendWithResponse("snt,+GPSL5\n\r", "SignalTracking", 1000, 200); response &= sendWithResponse("snu,+GPSL5,+GPSL5\n\r", "SignalUsage", 1000, 200); - configureLogging(); - if (response == true) { online.gnss = true; // If we failed before, mark as online now systemPrintln("mosaic-X5 configuration updated"); - - // Save the current configuration into non-volatile memory (NVM) - response &= saveConfiguration(); } else online.gnss = false; // Take it offline - settings.gnssConfiguredOnce = response; - return (response); } -//---------------------------------------- -// Setup the general configuration of the GNSS -// Not Rover or Base specific (ie, baud rates) -// Outputs: -// Returns true if successfully configured and false upon failure -//---------------------------------------- -bool GNSS_MOSAIC::configureGNSS() -{ - // Attempt 3 tries on MOSAICX5 config - for (int x = 0; x < 3; x++) - { - if (configureOnce() == true) - return (true); - } - - systemPrintln("mosaic-X5 failed to configure"); - return (false); -} - //---------------------------------------- // Configure the Rover // Outputs: @@ -690,55 +627,21 @@ bool GNSS_MOSAIC::configureGNSS() //---------------------------------------- bool GNSS_MOSAIC::configureRover() { - /* - Set mode to Rover + dynamic model - Set minElevation - Enable RTCM messages on COM1 - Enable NMEA on COM1 - */ - if (online.gnss == false) - { - systemPrintln("GNSS not online"); - return (false); - } + if (gnssInRoverMode()) + return (true); // No changes needed - // If our settings haven't changed, trust GNSS's settings - if (settings.gnssConfiguredRover) - { - systemPrintln("Skipping mosaic Rover configuration"); - setLoggingType(); // Needed because logUpdate exits early and never calls setLoggingType - return (true); - } + // Assume we are changing from Base to Rover, request any additional config changes bool response = true; response &= sendWithResponse("spm,Rover,all,auto\n\r", "PVTMode"); - response &= setModel(settings.dynamicModel); // Set by menuGNSS which calls gnss->setModel - - response &= setElevation(settings.minElev); // Set by menuGNSS which calls gnss->setElevation - - response &= setMinCnoRadio(settings.minCNO); - - response &= setConstellations(); - - response &= enableRTCMRover(); - - response &= enableNMEA(); - - response &= configureLogging(); - - setLoggingType(); // Update Standard, PPP, or custom for icon selection - - // Save the current configuration into non-volatile memory (NVM) - response &= saveConfiguration(); + gnssConfigure(GNSS_CONFIG_FIX_RATE); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); if (response == false) - { systemPrintln("mosaic-X5 Rover failed to configure"); - } - - settings.gnssConfiguredRover = response; return (response); } @@ -806,386 +709,108 @@ void GNSS_MOSAIC::debuggingEnable() } //---------------------------------------- -void GNSS_MOSAIC::enableGgaForNtrip() +// Restore the GNSS to the factory settings +//---------------------------------------- +void GNSS_MOSAIC::factoryReset() { - // Set the talker ID to GP - // enableNMEA() will enable GGA if needed - sendWithResponse("snti,GP\n\r", "NMEATalkerID"); + unsigned long start = millis(); + bool result = sendWithResponse("eccf,RxDefault,Boot\n\r", "CopyConfigFile", 5000); + if (settings.debugGnss) + systemPrintf("factoryReset: sendWithResponse eccf,RxDefault,Boot returned %s after %d ms\r\n", + result ? "true" : "false", millis() - start); + + start = millis(); + result = sendWithResponse("eccf,RxDefault,Current\n\r", "CopyConfigFile", 5000); + if (settings.debugGnss) + systemPrintf("factoryReset: sendWithResponse eccf,RxDefault,Current returned %s after %d ms\r\n", + result ? "true" : "false", millis() - start); } //---------------------------------------- -// Turn on all the enabled NMEA messages on COM1 -//---------------------------------------- -bool GNSS_MOSAIC::enableNMEA() +uint16_t GNSS_MOSAIC::fileBufferAvailable() { - bool gpggaEnabled = false; - bool gpzdaEnabled = false; - bool gpgstEnabled = false; - - String streams[MOSAIC_NUM_NMEA_STREAMS]; // Build a string for each stream - for (int messageNumber = 0; messageNumber < MAX_MOSAIC_NMEA_MSG; messageNumber++) // For each NMEA message - { - int stream = settings.mosaicMessageStreamNMEA[messageNumber]; - if (stream > 0) - { - stream--; + // TODO + return 0; +} - if (streams[stream].length() > 0) - streams[stream] += String("+"); - streams[stream] += String(mosaicMessagesNMEA[messageNumber].msgTextName); +//---------------------------------------- +uint16_t GNSS_MOSAIC::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) +{ + // TODO + return 0; +} - // If we are using IP based corrections, we need to send local data to the PPL - // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 - if (strstr(settings.pointPerfectKeyDistributionTopic, "/ip") != nullptr) - { - // Mark PPL required messages as enabled if stream > 0 - if (strcmp(mosaicMessagesNMEA[messageNumber].msgTextName, "GGA") == 0) - gpggaEnabled = true; - if (strcmp(mosaicMessagesNMEA[messageNumber].msgTextName, "ZDA") == 0) - gpzdaEnabled = true; - } +//---------------------------------------- +// Start the base using fixed coordinates +// Outputs: +// Returns true if successfully started and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::fixedBaseStart() +{ + // If we are already in the appropriate base mode, no changes needed + // 0 - Unknown, 1 - Rover, 2 - Base Survey In, 3 - Base Fixed Geodetic, 4 - Base Fixed Cartesian + int currentMode = getMode(); + if (currentMode == 3 && settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) + return (true); // No changes needed + if (currentMode == 4 && settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) + return (true); // No changes needed - if (strcmp(mosaicMessagesNMEA[messageNumber].msgTextName, "GST") == 0) - gpgstEnabled = true; - } - } + bool response = true; - if (pointPerfectIsEnabled()) + // TODO: support alternate Datums (ETRS89, NAD83, NAD83_PA, NAD83_MA, GDA94, GDA2020) + if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) { - // Force on any messages that are needed for PPL - if (gpggaEnabled == false) - { - // Add GGA to Stream1 (streams[0]) - // TODO: We may need to be cleverer about which stream we choose, - // depending on the stream intervals - if (streams[0].length() > 0) - streams[0] += String("+"); - streams[0] += String("GGA"); - gpggaEnabled = true; - } - if (gpzdaEnabled == false) - { - if (streams[0].length() > 0) - streams[0] += String("+"); - streams[0] += String("ZDA"); - gpzdaEnabled = true; - } + char pos[100]; + snprintf(pos, sizeof(pos), "sspc,Cartesian1,%.4f,%.4f,%.4f,WGS84\n\r", settings.fixedEcefX, settings.fixedEcefY, + settings.fixedEcefZ); + response &= sendWithResponse(pos, "StaticPosCartesian"); + response &= sendWithResponse("spm,Static,,Cartesian1\n\r", "PVTMode"); } - - if (settings.ntripClient_TransmitGGA == true) + else if (settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) { - // Force on GGA if needed for NTRIP - if (gpggaEnabled == false) - { - if (streams[0].length() > 0) - streams[0] += String("+"); - streams[0] += String("GGA"); - gpggaEnabled = true; - } + // Add height of instrument (HI) to fixed altitude + // https://www.e-education.psu.edu/geog862/node/1853 + // For example, if HAE is at 100.0m, + 2m stick + 73mm APC = 102.073 + float totalFixedAltitude = + settings.fixedAltitude + ((settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000.0); + char pos[100]; + snprintf(pos, sizeof(pos), "sspg,Geodetic1,%.8f,%.8f,%.4f,WGS84\n\r", settings.fixedLat, settings.fixedLong, + totalFixedAltitude); + response &= sendWithResponse(pos, "StaticPosGeodetic"); + response &= sendWithResponse("spm,Static,,Geodetic1\n\r", "PVTMode"); } - // Force GST on so we can extract the lat and lon standard deviations - if (gpgstEnabled == false) - { - if (streams[0].length() > 0) - streams[0] += String("+"); - streams[0] += String("GST"); - gpgstEnabled = true; - } - - bool response = true; - - for (int stream = 0; stream < MOSAIC_NUM_NMEA_STREAMS; stream++) - { - if (streams[stream].length() == 0) - streams[stream] = String("none"); - - String setting = String("sno,Stream" + String(stream + 1) + ",COM1," + streams[stream] + "," + - String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); - response &= sendWithResponse(setting, "NMEAOutput"); - - if (settings.enableNmeaOnRadio) - setting = String("sno,Stream" + String(stream + MOSAIC_NUM_NMEA_STREAMS + 1) + ",COM2," + streams[stream] + - "," + String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); - else - setting = String("sno,Stream" + String(stream + MOSAIC_NUM_NMEA_STREAMS + 1) + ",COM2,none,off\n\r"); - response &= sendWithResponse(setting, "NMEAOutput"); - - if (settings.enableGnssToUsbSerial) - { - setting = - String("sno,Stream" + String(stream + (2 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",USB1," + streams[stream] + - "," + String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); - response &= sendWithResponse(setting, "NMEAOutput"); - } - else - { - // Disable the USB1 NMEA streams if settings.enableGnssToUsbSerial is not enabled - setting = String("sno,Stream" + String(stream + (2 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",USB1,none,off\n\r"); - response &= sendWithResponse(setting, "NMEAOutput"); - } - - if (settings.enableLogging) - { - setting = - String("sno,Stream" + String(stream + (3 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",DSK1," + streams[stream] + - "," + String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); - response &= sendWithResponse(setting, "NMEAOutput"); - } - else - { - // Disable the DSK1 NMEA streams if settings.enableLogging is not enabled - setting = String("sno,Stream" + String(stream + (3 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",DSK1,none,off\n\r"); - response &= sendWithResponse(setting, "NMEAOutput"); - } - } - - return (response); -} - -//---------------------------------------- -// Turn on all the enabled RTCM Base messages on COM1, COM2 and USB1 (if enabled) -//---------------------------------------- -bool GNSS_MOSAIC::enableRTCMBase() -{ - bool response = true; - - // Set RTCMv3 Intervals - for (int group = 0; group < MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS; group++) - { - char flt[10]; - snprintf(flt, sizeof(flt), "%.1f", settings.mosaicMessageIntervalsRTCMv3Base[group]); - String setting = - String("sr3i," + String(mosaicRTCMv3MsgIntervalGroups[group].name) + "," + String(flt) + "\n\r"); - response &= sendWithResponse(setting, "RTCMv3Interval"); - } - - // Enable RTCMv3 - String messages = String(""); - for (int message = 0; message < MAX_MOSAIC_RTCM_V3_MSG; message++) - { - if (settings.mosaicMessageEnabledRTCMv3Base[message]) - { - if (messages.length() > 0) - messages += String("+"); - messages += String(mosaicMessagesRTCMv3[message].name); - } - } - - if (messages.length() == 0) - messages = String("none"); - - String setting = String("sr3o,COM1+COM2"); - if (settings.enableGnssToUsbSerial) - setting += String("+USB1"); - setting += String("," + messages + "\n\r"); - response &= sendWithResponse(setting, "RTCMv3Output"); - - if (!settings.enableGnssToUsbSerial) - { - response &= sendWithResponse("sr3o,USB1,none\n\r", "RTCMv3Output"); - } - - return (response); -} - -//---------------------------------------- -// Turn on all the enabled RTCM Rover messages on COM1, COM2 and USB1 (if enabled) -//---------------------------------------- -bool GNSS_MOSAIC::enableRTCMRover() -{ - bool response = true; - bool rtcm1019Enabled = false; - bool rtcm1020Enabled = false; - bool rtcm1042Enabled = false; - bool rtcm1046Enabled = false; - - // Set RTCMv3 Intervals - for (int group = 0; group < MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS; group++) - { - char flt[10]; - snprintf(flt, sizeof(flt), "%.1f", settings.mosaicMessageIntervalsRTCMv3Rover[group]); - String setting = - String("sr3i," + String(mosaicRTCMv3MsgIntervalGroups[group].name) + "," + String(flt) + "\n\r"); - response &= sendWithResponse(setting, "RTCMv3Interval"); - } - - // Enable RTCMv3 - String messages = String(""); - for (int message = 0; message < MAX_MOSAIC_RTCM_V3_MSG; message++) - { - if (settings.mosaicMessageEnabledRTCMv3Rover[message]) - { - if (messages.length() > 0) - messages += String("+"); - messages += String(mosaicMessagesRTCMv3[message].name); - - // If we are using IP based corrections, we need to send local data to the PPL - // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 - if (pointPerfectIsEnabled()) - { - // Mark PPL required messages as enabled if rate > 0 - if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1019") == 0) - rtcm1019Enabled = true; - if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1020") == 0) - rtcm1020Enabled = true; - if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1042") == 0) - rtcm1042Enabled = true; - if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1046") == 0) - rtcm1046Enabled = true; - } - } - } - - if (pointPerfectIsEnabled()) - { - // Force on any messages that are needed for PPL - if (rtcm1019Enabled == false) - { - if (messages.length() > 0) - messages += String("+"); - messages += String("RTCM1019"); - } - if (rtcm1020Enabled == false) - { - if (messages.length() > 0) - messages += String("+"); - messages += String("RTCM1020"); - } - if (rtcm1042Enabled == false) - { - if (messages.length() > 0) - messages += String("+"); - messages += String("RTCM1042"); - } - if (rtcm1046Enabled == false) - { - if (messages.length() > 0) - messages += String("+"); - messages += String("RTCM1046"); - } - } - - if (messages.length() == 0) - messages = String("none"); - - String setting = String("sr3o,COM1+COM2"); - if (settings.enableGnssToUsbSerial) - setting += String("+USB1"); - setting += String("," + messages + "\n\r"); - response &= sendWithResponse(setting, "RTCMv3Output"); - - if (!settings.enableGnssToUsbSerial) - { - response &= sendWithResponse("sr3o,USB1,none\n\r", "RTCMv3Output"); - } + if (response == false) + systemPrintln("Fixed base start failed"); return (response); } //---------------------------------------- -// Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted -// even if there is no GPS fix. We use it to test serial output. -// Outputs: -// Returns true if successfully started and false upon failure +// Check if given GNSS fix rate is allowed +// Rates are expressed in ms between fixes. //---------------------------------------- -bool GNSS_MOSAIC::enableRTCMTest() -{ - // Enable RTCM1230 on COM2 (Radio connector) - // Called by STATE_TEST. Mosaic could still be starting up, so allow many retries - - int retries = 0; - const int retryLimit = 20; - // Add RTCMv3 output on COM2 - while (!sendWithResponse("sdio,COM2,,+RTCMv3\n\r", "DataInOut")) - { - if (retries == retryLimit) - break; - retries++; - sendWithResponse("SSSSSSSSSSSSSSSSSSSS\n\r", "COM"); // Send escape sequence - } - - if (retries == retryLimit) - return false; - - bool success = true; - success &= sendWithResponse("sr3i,RTCM1230,1.0\n\r", "RTCMv3Interval"); // Set message interval to 1s - success &= sendWithResponse("sr3o,COM2,+RTCM1230\n\r", "RTCMv3Output"); // Add RTCMv3 1230 output - - return success; -} - -//---------------------------------------- -// Restore the GNSS to the factory settings -//---------------------------------------- -void GNSS_MOSAIC::factoryReset() -{ - unsigned long start = millis(); - bool result = sendWithResponse("eccf,RxDefault,Boot\n\r", "CopyConfigFile", 5000); - if (settings.debugGnss) - systemPrintf("factoryReset: sendWithResponse eccf,RxDefault,Boot returned %s after %d ms\r\n", - result ? "true" : "false", millis() - start); - - start = millis(); - result = sendWithResponse("eccf,RxDefault,Current\n\r", "CopyConfigFile", 5000); - if (settings.debugGnss) - systemPrintf("factoryReset: sendWithResponse eccf,RxDefault,Current returned %s after %d ms\r\n", - result ? "true" : "false", millis() - start); -} - -//---------------------------------------- -uint16_t GNSS_MOSAIC::fileBufferAvailable() +bool GNSS_MOSAIC::fixRateIsAllowed(uint32_t fixRateMs) { // TODO - return 0; + if (fixRateMs != 1000) + return (false); + return (true); } -//---------------------------------------- -uint16_t GNSS_MOSAIC::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) +// Return minimum in milliseconds +uint32_t GNSS_MOSAIC::fixRateGetMinimumMs() { // TODO - return 0; + return (1000); } -//---------------------------------------- -// Start the base using fixed coordinates -// Outputs: -// Returns true if successfully started and false upon failure -//---------------------------------------- -bool GNSS_MOSAIC::fixedBaseStart() +// Return maximum in milliseconds +uint32_t GNSS_MOSAIC::fixRateGetMaximumMs() { - bool response = true; - - // TODO: support alternate Datums (ETRS89, NAD83, NAD83_PA, NAD83_MA, GDA94, GDA2020) - if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) - { - char pos[100]; - snprintf(pos, sizeof(pos), "sspc,Cartesian1,%.4f,%.4f,%.4f,WGS84\n\r", settings.fixedEcefX, settings.fixedEcefY, - settings.fixedEcefZ); - response &= sendWithResponse(pos, "StaticPosCartesian"); - response &= sendWithResponse("spm,Static,,Cartesian1\n\r", "PVTMode"); - } - else if (settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) - { - // Add height of instrument (HI) to fixed altitude - // https://www.e-education.psu.edu/geog862/node/1853 - // For example, if HAE is at 100.0m, + 2m stick + 73mm APC = 102.073 - float totalFixedAltitude = - settings.fixedAltitude + ((settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000.0); - char pos[100]; - snprintf(pos, sizeof(pos), "sspg,Geodetic1,%.8f,%.8f,%.4f,WGS84\n\r", settings.fixedLat, settings.fixedLong, - totalFixedAltitude); - response &= sendWithResponse(pos, "StaticPosGeodetic"); - response &= sendWithResponse("spm,Static,,Geodetic1\n\r", "PVTMode"); - } - - if (response == false) - { - systemPrintln("Fixed base start failed"); - } - - return (response); + // TODO + return (1000); } //---------------------------------------- @@ -1415,6 +1040,47 @@ uint8_t GNSS_MOSAIC::getMinute() return _minute; } +//---------------------------------------- +// Returns the current mode: Base/Rover/etc +// 0 - Unknown, 1 - Rover, 2 - Base Survey In, 3 - Base Fixed Geodetic, 4 - Base Fixed Cartesian +//---------------------------------------- +uint8_t GNSS_MOSAIC::getMode() +{ + // Example responses to gpm: + // PVTMode, Rover, StandAlone+SBAS+DGNSS+RTKFloat+RTKFixed, auto + // PVTMode, Static, StandAlone+SBAS+DGNSS+RTKFloat+RTKFixed, auto + // PVTMode, Static, StandAlone+SBAS+DGNSS+RTKFloat+RTKFixed, Geodetic1 + // PVTMode, Static, StandAlone+SBAS+DGNSS+RTKFloat+RTKFixed, Cartesian1 + + char receiverResponse[500]; + // Send gpm, look for correct PVTMode response, and store the entire response for searching later + if (sendWithResponse("gpm\n\r", "PVTMode", 1000, 25, receiverResponse, sizeof(receiverResponse)) == true) + { + if (strnstr(receiverResponse, "Cartesian", sizeof(receiverResponse)) != nullptr) // Found + { + Serial.println("Mode: Base fixed Cartesian"); + return (4); + } + if (strnstr(receiverResponse, "Geodetic", sizeof(receiverResponse)) != nullptr) // Found + { + Serial.println("Mode: Base fixed Geodetic"); + return (3); + } + if (strnstr(receiverResponse, "Static", sizeof(receiverResponse)) != nullptr) // Found + { + Serial.println("Mode: Base survey-in"); + return (2); + } + if (strnstr(receiverResponse, "Rover", sizeof(receiverResponse)) != nullptr) // Found + { + Serial.println("Mode: Rover"); + return (1); + } + } + + return 0; // Unknown +} + //---------------------------------------- // Returns month number or zero if not online //---------------------------------------- @@ -1554,17 +1220,36 @@ uint16_t GNSS_MOSAIC::getYear() return _year; } +//---------------------------------------- +// Returns true if the device is in Base Fixed mode +//---------------------------------------- +bool GNSS_MOSAIC::gnssInBaseFixedMode() +{ + // 0 - Unknown, 1 - Rover, 2 - Base Survey In, 3 - Base Fixed Geodetic, 4 - Base Fixed Cartesian + int currentMode = getMode(); + if (currentMode == 3 || currentMode == 4) + return (true); + return (false); +} +//---------------------------------------- +// Returns true if the device is in Base Survey In mode +//---------------------------------------- +bool GNSS_MOSAIC::gnssInBaseSurveyInMode() +{ + // 0 - Unknown, 1 - Rover, 2 - Base Survey In, 3 - Base Fixed Geodetic, 4 - Base Fixed Cartesian + if (getMode() == 2) + return (true); + return (false); +} //---------------------------------------- // Returns true if the device is in Rover mode -// Currently the only two modes are Rover or Base //---------------------------------------- -bool GNSS_MOSAIC::inRoverMode() +bool GNSS_MOSAIC::gnssInRoverMode() { - // Determine which state we are in - if (settings.lastState == STATE_BASE_NOT_STARTED) - return (false); - - return (true); // Default to Rover + // 0 - Unknown, 1 - Rover, 2 - Base Survey In, 3 - Base Fixed Geodetic, 4 - Base Fixed Cartesian + if (getMode() == 1) + return (true); + return (false); } //---------------------------------------- @@ -1773,6 +1458,7 @@ void GNSS_MOSAIC::menuConstellations() incoming--; // Align choice to constellation array of 0 to 5 settings.mosaicConstellations[incoming] ^= 1; + gnssConfigure(GNSS_CONFIG_CONSTELLATION); // Request receiver to use new settings } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) break; @@ -1782,11 +1468,6 @@ void GNSS_MOSAIC::menuConstellations() printUnknown(incoming); } - // Apply current settings to module - setConstellations(); - - saveConfiguration(); // Save the updated constellations - clearBuffer(); // Empty buffer of any newline chars } @@ -1833,6 +1514,8 @@ void GNSS_MOSAIC::menuMessagesNMEA() settings.mosaicMessageStreamNMEA[incoming] += 1; if (settings.mosaicMessageStreamNMEA[incoming] > MOSAIC_NUM_NMEA_STREAMS) settings.mosaicMessageStreamNMEA[incoming] = 0; // Wrap around + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings } else if (incoming > MAX_MOSAIC_NMEA_MSG && incoming <= (MAX_MOSAIC_NMEA_MSG + MOSAIC_NUM_NMEA_STREAMS)) // Stream intervals @@ -1851,6 +1534,7 @@ void GNSS_MOSAIC::menuMessagesNMEA() if (interval >= 1 && interval <= MAX_MOSAIC_MSG_RATES) { settings.mosaicStreamIntervalsNMEA[incoming] = interval - 1; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings } } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) @@ -1861,9 +1545,6 @@ void GNSS_MOSAIC::menuMessagesNMEA() printUnknown(incoming); } - settings.gnssConfiguredBase = false; // Update the GNSS config at the next boot - settings.gnssConfiguredRover = false; - clearBuffer(); // Empty buffer of any newline chars } @@ -1913,7 +1594,13 @@ void GNSS_MOSAIC::menuMessagesRTCM(bool rover) if (getUserInputDouble(&interval) == INPUT_RESPONSE_VALID) // Returns EXIT, TIMEOUT, or long { if ((interval >= 0.1) && (interval <= 600.0)) + { intervalPtr[incoming] = interval; + if (inBaseMode()) // If the system state is Base mode + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings + } else systemPrintln("Invalid interval: Min 0.1; Max 600.0"); } @@ -1924,6 +1611,11 @@ void GNSS_MOSAIC::menuMessagesRTCM(bool rover) incoming--; incoming -= MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS; enabledPtr[incoming] ^= 1; + + if (inBaseMode()) // If the system state is Base mode + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) break; @@ -1933,9 +1625,6 @@ void GNSS_MOSAIC::menuMessagesRTCM(bool rover) printUnknown(incoming); } - settings.gnssConfiguredBase = false; // Update the GNSS config at the next boot - settings.gnssConfiguredRover = false; - clearBuffer(); // Empty buffer of any newline chars } @@ -1991,6 +1680,12 @@ void GNSS_MOSAIC::menuMessages() for (int x = 0; x < MAX_MOSAIC_RTCM_V3_MSG; x++) settings.mosaicMessageEnabledRTCMv3Base[x] = mosaicMessagesRTCMv3[x].defaultEnabled; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings + if (inBaseMode()) // If the system state is Base mode + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings + systemPrintln("Reset to Defaults"); } @@ -2003,14 +1698,6 @@ void GNSS_MOSAIC::menuMessages() } clearBuffer(); // Empty buffer of any newline chars - - setLoggingType(); // Update Standard, PPP, or custom for icon selection - - // Apply these changes at menu exit - if (inRoverMode() == true) - restartRover = true; - else - restartBase = true; } //---------------------------------------- @@ -2021,113 +1708,238 @@ void GNSS_MOSAIC::printModuleInfo() systemPrintf("mosaic-X5 firmware: %s\r\n", gnssFirmwareVersion); } -//---------------------------------------- -// Send correction data to the GNSS -// Inputs: -// dataToSend: Address of a buffer containing the data -// dataLength: The number of valid data bytes in the buffer -// Outputs: -// Returns the number of correction data bytes written -//---------------------------------------- -int GNSS_MOSAIC::pushRawData(uint8_t *dataToSend, int dataLength) -{ - // Send data directly from ESP GNSS UART1 to mosaic-X5 COM1 - return (serialGNSS->write(dataToSend, dataLength)); -} +//---------------------------------------- +// Send correction data to the GNSS +// Inputs: +// dataToSend: Address of a buffer containing the data +// dataLength: The number of valid data bytes in the buffer +// Outputs: +// Returns the number of correction data bytes written +//---------------------------------------- +int GNSS_MOSAIC::pushRawData(uint8_t *dataToSend, int dataLength) +{ + // Send data directly from ESP GNSS UART1 to mosaic-X5 COM1 + return (serialGNSS->write(dataToSend, dataLength)); +} + +//---------------------------------------- +uint16_t GNSS_MOSAIC::rtcmBufferAvailable() +{ + // TODO + return 0; +} + +//---------------------------------------- +// Hardware or software reset the GNSS receiver +//---------------------------------------- +bool GNSS_MOSAIC::reset() +{ + // We could restart L-Band here if needed, but gnss->reset is never called on the X5 + // Instead, update() does it when spartnCorrectionsReceived times out + return false; +} + +//---------------------------------------- +// If LBand is being used, ignore any RTCM that may come in from the GNSS +//---------------------------------------- +void GNSS_MOSAIC::rtcmOnGnssDisable() +{ + // TODO: is this needed? +} + +//---------------------------------------- +// If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver +//---------------------------------------- +void GNSS_MOSAIC::rtcmOnGnssEnable() +{ + // TODO: is this needed? +} + +//---------------------------------------- +uint16_t GNSS_MOSAIC::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) +{ + // TODO + return 0; +} + +//---------------------------------------- +// Save the current configuration +// Outputs: +// Returns true when the configuration was saved and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::saveConfiguration() +{ + unsigned long start = millis(); + bool result = sendWithResponse("eccf,Current,Boot\n\r", "CopyConfigFile", 5000); + if (settings.debugGnss) + systemPrintf("saveConfiguration: sendWithResponse returned %s after %d ms\r\n", result ? "true" : "false", + millis() - start); + return result; +} + +//---------------------------------------- +// Send message. Wait for up to timeout millis for reply to arrive +// If the reply is received, keep reading bytes until the serial port has +// been idle for idle millis +// If response is defined, copy up to responseSize bytes +// Inputs: +// message: Zero terminated string of characters containing the message +// to send to the GNSS +// reply: String containing the first portion of the expected response +// timeout: Number of milliseconds to wat for the reply to arrive +// idle: Number of milliseconds to wait after last reply character is received +// response: Address of buffer to receive the response +// responseSize: Maximum number of bytes to copy +// Outputs: +// Returns true if the response was received and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::sendAndWaitForIdle(const char *message, const char *reply, unsigned long timeout, unsigned long wait, + char *response, size_t responseSize, bool debug) +{ + if (productVariant == RTK_FACET_MOSAIC) + return sendAndWaitForIdle(serial2GNSS, message, reply, timeout, wait, response, responseSize, debug); + else + return sendAndWaitForIdle(serialGNSS, message, reply, timeout, wait, response, responseSize, debug); +} +bool GNSS_MOSAIC::sendAndWaitForIdle(HardwareSerial *serialPort, const char *message, const char *reply, + unsigned long timeout, unsigned long idle, char *response, size_t responseSize, + bool debug) +{ + if (strlen(reply) == 0) // Reply can't be zero-length + return false; + + _isBlocking = true; // Suspend the GNSS read task + + if (debug && (settings.debugGnss == true) && (!inMainMenu)) + systemPrintf("sendAndWaitForIdle: sending %s\r\n", message); + + if (strlen(message) > 0) + serialPort->write(message, strlen(message)); // Send the message + + unsigned long startTime = millis(); + size_t replySeen = 0; + + while (((millis() - startTime) < timeout) && (replySeen < strlen(reply))) // While not timed out and reply not seen + { + if (serialPort->available()) // If a char is available + { + uint8_t c = serialPort->read(); // Read it + // if (debug && (settings.debugGnss == true) && (!inMainMenu)) + // systemPrintf("%c", (char)c); + if (c == *(reply + replySeen)) // Is it a char from reply? + { + if (response && (replySeen < (responseSize - 1))) + { + *(response + replySeen) = c; + *(response + replySeen + 1) = 0; + } + replySeen++; + } + else + replySeen = 0; // Reset replySeen on an unexpected char + } + } + + if (replySeen == strlen(reply)) // If the reply was seen + { + startTime = millis(); + while ((millis() - startTime) < idle) + { + if (serialPort->available()) + { + uint8_t c = serialPort->read(); + // if (debug && (settings.debugGnss == true) && (!inMainMenu)) + // systemPrintf("%c", (char)c); + if (response && (replySeen < (responseSize - 1))) + { + *(response + replySeen) = c; + *(response + replySeen + 1) = 0; + } + replySeen++; + startTime = millis(); + } + } -//---------------------------------------- -uint16_t GNSS_MOSAIC::rtcmBufferAvailable() -{ - // TODO - return 0; -} + _isBlocking = false; -//---------------------------------------- -// If LBand is being used, ignore any RTCM that may come in from the GNSS -//---------------------------------------- -void GNSS_MOSAIC::rtcmOnGnssDisable() -{ - // TODO: is this needed? -} + return true; + } -//---------------------------------------- -// If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver -//---------------------------------------- -void GNSS_MOSAIC::rtcmOnGnssEnable() -{ - // TODO: is this needed? -} + _isBlocking = false; -//---------------------------------------- -uint16_t GNSS_MOSAIC::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) -{ - // TODO - return 0; + return false; } //---------------------------------------- -// Save the current configuration +// Send message. Wait for up to timeout millis for reply to arrive +// If the reply is received, keep reading bytes until the serial port has +// been idle for idle millis +// If response is defined, copy up to responseSize bytes +// Inputs: +// message: String containing the message to send to the GNSS +// reply: String containing the first portion of the expected response +// timeout: Number of milliseconds to wat for the reply to arrive +// idle: Number of milliseconds to wait after last reply character is received +// response: Address of buffer to receive the response +// responseSize: Maximum number of bytes to copy // Outputs: -// Returns true when the configuration was saved and false upon failure +// Returns true if the response was received and false upon failure //---------------------------------------- -bool GNSS_MOSAIC::saveConfiguration() +bool GNSS_MOSAIC::sendAndWaitForIdle(String message, const char *reply, unsigned long timeout, unsigned long idle, + char *response, size_t responseSize, bool debug) { - unsigned long start = millis(); - bool result = sendWithResponse("eccf,Current,Boot\n\r", "CopyConfigFile", 5000); - if (settings.debugGnss) - systemPrintf("saveConfiguration: sendWithResponse returned %s after %d ms\r\n", result ? "true" : "false", - millis() - start); - return result; + return sendAndWaitForIdle(message.c_str(), reply, timeout, idle, response, responseSize, debug); } //---------------------------------------- // Send message. Wait for up to timeout millis for reply to arrive -// If the reply is received, keep reading bytes until the serial port has -// been idle for idle millis -// If response is defined, copy up to responseSize bytes +// If the reply has started to be received when timeout is reached, wait for a further wait millis +// If the reply is seen, wait for a further wait millis +// During wait, keep reading incoming serial. If response is defined, copy up to responseSize bytes // Inputs: // message: Zero terminated string of characters containing the message // to send to the GNSS // reply: String containing the first portion of the expected response // timeout: Number of milliseconds to wat for the reply to arrive -// idle: Number of milliseconds to wait after last reply character is received +// wait: Number of additional milliseconds if the reply is detected // response: Address of buffer to receive the response // responseSize: Maximum number of bytes to copy // Outputs: // Returns true if the response was received and false upon failure //---------------------------------------- -bool GNSS_MOSAIC::sendAndWaitForIdle(const char *message, const char *reply, unsigned long timeout, unsigned long wait, - char *response, size_t responseSize, bool debug) +bool GNSS_MOSAIC::sendWithResponse(const char *message, const char *reply, unsigned long timeout, unsigned long wait, + char *response, size_t responseSize) { if (productVariant == RTK_FACET_MOSAIC) - return sendAndWaitForIdle(serial2GNSS, message, reply, timeout, wait, response, responseSize, debug); + return sendWithResponse(serial2GNSS, message, reply, timeout, wait, response, responseSize); else - return sendAndWaitForIdle(serialGNSS, message, reply, timeout, wait, response, responseSize, debug); + return sendWithResponse(serialGNSS, message, reply, timeout, wait, response, responseSize); } -bool GNSS_MOSAIC::sendAndWaitForIdle(HardwareSerial *serialPort, const char *message, const char *reply, unsigned long timeout, unsigned long idle, - char *response, size_t responseSize, bool debug) +bool GNSS_MOSAIC::sendWithResponse(HardwareSerial *serialPort, const char *message, const char *reply, + unsigned long timeout, unsigned long wait, char *response, size_t responseSize) { if (strlen(reply) == 0) // Reply can't be zero-length return false; _isBlocking = true; // Suspend the GNSS read task - if (debug && (settings.debugGnss == true) && (!inMainMenu)) - systemPrintf("sendAndWaitForIdle: sending %s\r\n", message); + if ((settings.debugGnss == true) && (!inMainMenu)) + systemPrintf("sendWithResponse: sending %s\r\n", message); if (strlen(message) > 0) serialPort->write(message, strlen(message)); // Send the message unsigned long startTime = millis(); size_t replySeen = 0; + bool keepGoing = true; - while (((millis() - startTime) < timeout) && (replySeen < strlen(reply))) // While not timed out and reply not seen + while ((keepGoing) && (replySeen < strlen(reply))) // While not timed out and reply not seen { if (serialPort->available()) // If a char is available { uint8_t c = serialPort->read(); // Read it - //if (debug && (settings.debugGnss == true) && (!inMainMenu)) - // systemPrintf("%c", (char)c); + // if ((settings.debugGnss == true) && (!inMainMenu)) + // systemPrintf("%c", (char)c); if (c == *(reply + replySeen)) // Is it a char from reply? { if (response && (replySeen < (responseSize - 1))) @@ -2140,339 +1952,495 @@ bool GNSS_MOSAIC::sendAndWaitForIdle(HardwareSerial *serialPort, const char *mes else replySeen = 0; // Reset replySeen on an unexpected char } + + // If the reply has started to arrive at the timeout, allow extra time + if ((millis() - startTime) > timeout) // Have we timed out? + if (replySeen == 0) // If replySeen is zero, don't keepGoing + keepGoing = false; + + if ((millis() - startTime) > (timeout + wait)) // Have we really timed out? + keepGoing = false; // Don't keepGoing } if (replySeen == strlen(reply)) // If the reply was seen { startTime = millis(); - while ((millis() - startTime) < idle) + while ((millis() - startTime) < wait) + { + if (serialPort->available()) + { + uint8_t c = serialPort->read(); + if ((settings.debugGnss == true) && (!inMainMenu)) + systemPrintf("%c", (char)c); + if (response && (replySeen < (responseSize - 1))) + { + *(response + replySeen) = c; + *(response + replySeen + 1) = 0; + } + replySeen++; + } + } + + _isBlocking = false; + + return true; + } + + _isBlocking = false; + + return false; +} + +//---------------------------------------- +// Send message. Wait for up to timeout millis for reply to arrive +// If the reply has started to be received when timeout is reached, wait for a further wait millis +// If the reply is seen, wait for a further wait millis +// During wait, keep reading incoming serial. If response is defined, copy up to responseSize bytes +// Inputs: +// message: String containing the message to send to the GNSS +// reply: String containing the first portion of the expected response +// timeout: Number of milliseconds to wat for the reply to arrive +// wait: Number of additional milliseconds if the reply is detected +// response: Address of buffer to receive the response +// responseSize: Maximum number of bytes to copy +// Outputs: +// Returns true if the response was received and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::sendWithResponse(String message, const char *reply, unsigned long timeout, unsigned long wait, + char *response, size_t responseSize) +{ + return sendWithResponse(message.c_str(), reply, timeout, wait, response, responseSize); +} + +//---------------------------------------- +// Set the baud rate of mosaic-X5 COMn - from the super class +// Inputs: +// port: COM port number +// baudRate: New baud rate for the COM port +// Outputs: +// Returns true if the baud rate was set and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::setBaudRate(uint8_t port, uint32_t baudRate) +{ + if (port < 1 || port > 4) + { + systemPrintln("setBaudRate error: out of range"); + return (false); + } + + return setBaudRateCOM(port, baudRate); +} + +bool GNSS_MOSAIC::setBaudRateComm(uint32_t baud) +{ + return setBaudRateCOM(1, baud); +} + +bool GNSS_MOSAIC::setBaudRateData(uint32_t baud) +{ + return setBaudRateCOM(3, baud); +} + +bool GNSS_MOSAIC::setBaudRateRadio(uint32_t baud) +{ + return setBaudRateCOM(2, baud); +} + +//---------------------------------------- +// Set the baud rate of mosaic-X5 COM1 +// This is used during the Bluetooth test +// Inputs: +// port: COM port number +// baudRate: New baud rate for the COM port +// Outputs: +// Returns true if the baud rate was set and false upon failure +//---------------------------------------- +bool GNSS_MOSAIC::setBaudRateCOM(uint8_t port, uint32_t baudRate) +{ + for (int i = 0; i < MAX_MOSAIC_COM_RATES; i++) + { + if (baudRate == mosaicComRates[i].rate) + { + String setting = + String("scs,COM" + String(port) + "," + String(mosaicComRates[i].name) + ",bits8,No,bit1,none\n\r"); + return (sendWithResponse(setting, "COMSettings")); + } + } + + return false; // Invalid baud +} + +//---------------------------------------- +// Enable all the valid constellations and bands for this platform +//---------------------------------------- +bool GNSS_MOSAIC::setConstellations() +{ + String enabledConstellations = ""; + + for (int constellation = 0; constellation < MAX_MOSAIC_CONSTELLATIONS; constellation++) + { + if (settings.mosaicConstellations[constellation] > 0) // == true + { + if (enabledConstellations.length() > 0) + enabledConstellations += String("+"); + enabledConstellations += String(mosaicSignalConstellations[constellation].name); + } + } + + if (enabledConstellations.length() == 0) + enabledConstellations = String("none"); + + String setting = String("sst," + enabledConstellations + "\n\r"); + return (sendWithResponse(setting, "SatelliteTracking", 1000, 200)); +} + +// Enable / disable corrections protocol(s) on the Radio External port +// Always update if force is true. Otherwise, only update if enable has changed state +// Notes: +// NrBytesReceived is reset when sdio,COM2 is sent. This causes NrBytesReceived to +// be less than previousNrBytesReceived, which in turn causes a corrections timeout. +// So, we need to reset previousNrBytesReceived and firstTimeNrBytesReceived here. +bool GNSS_MOSAIC::setCorrRadioExtPort(bool enable, bool force) +{ + if (force || (enable != _corrRadioExtPortEnabled)) + { + String setting = String("sdio,COM2,"); + if (enable) + setting += String("RTCMv3,"); + else + setting += String("none,"); + // Configure COM2 for NMEA and RTCMv3 output. No L-Band. Not encapsulated. + setting += String("RTCMv3+NMEA\n\r"); + + if (sendWithResponse(setting, "DataInOut")) { - if (serialPort->available()) + if ((settings.debugCorrections == true) && !inMainMenu) { - uint8_t c = serialPort->read(); - //if (debug && (settings.debugGnss == true) && (!inMainMenu)) - // systemPrintf("%c", (char)c); - if (response && (replySeen < (responseSize - 1))) - { - *(response + replySeen) = c; - *(response + replySeen + 1) = 0; - } - replySeen++; - startTime = millis(); + systemPrintf("Radio Ext corrections: %s -> %s%s\r\n", _corrRadioExtPortEnabled ? "enabled" : "disabled", + enable ? "enabled" : "disabled", force ? " (Forced)" : ""); } - } - _isBlocking = false; - - return true; + _corrRadioExtPortEnabled = enable; + previousNrBytesReceived = 0; + firstTimeNrBytesReceived = true; + return true; + } + else + { + systemPrintf("Radio Ext corrections FAILED: %s -> %s%s\r\n", + _corrRadioExtPortEnabled ? "enabled" : "disabled", enable ? "enabled" : "disabled", + force ? " (Forced)" : ""); + } } - _isBlocking = false; - return false; } //---------------------------------------- -// Send message. Wait for up to timeout millis for reply to arrive -// If the reply is received, keep reading bytes until the serial port has -// been idle for idle millis -// If response is defined, copy up to responseSize bytes +// Set the elevation in degrees // Inputs: -// message: String containing the message to send to the GNSS -// reply: String containing the first portion of the expected response -// timeout: Number of milliseconds to wat for the reply to arrive -// idle: Number of milliseconds to wait after last reply character is received -// response: Address of buffer to receive the response -// responseSize: Maximum number of bytes to copy -// Outputs: -// Returns true if the response was received and false upon failure +// elevationDegrees: The elevation value in degrees +// Notes: +// mosaic supports negative elevations, but our firmware only support 0-90 //---------------------------------------- -bool GNSS_MOSAIC::sendAndWaitForIdle(String message, const char *reply, unsigned long timeout, unsigned long idle, - char *response, size_t responseSize, bool debug) +bool GNSS_MOSAIC::setElevation(uint8_t elevationDegrees) { - return sendAndWaitForIdle(message.c_str(), reply, timeout, idle, response, responseSize, debug); + if (elevationDegrees > 90) + elevationDegrees = 90; + String elev = String(elevationDegrees); + String setting = String("sem,PVT," + elev + "\n\r"); + return (sendWithResponse(setting, "ElevationMask")); } //---------------------------------------- -// Send message. Wait for up to timeout millis for reply to arrive -// If the reply has started to be received when timeout is reached, wait for a further wait millis -// If the reply is seen, wait for a further wait millis -// During wait, keep reading incoming serial. If response is defined, copy up to responseSize bytes -// Inputs: -// message: Zero terminated string of characters containing the message -// to send to the GNSS -// reply: String containing the first portion of the expected response -// timeout: Number of milliseconds to wat for the reply to arrive -// wait: Number of additional milliseconds if the reply is detected -// response: Address of buffer to receive the response -// responseSize: Maximum number of bytes to copy -// Outputs: -// Returns true if the response was received and false upon failure +// Control whether HAS E6 is used in location fixes or not //---------------------------------------- -bool GNSS_MOSAIC::sendWithResponse(const char *message, const char *reply, unsigned long timeout, unsigned long wait, - char *response, size_t responseSize) +bool GNSS_MOSAIC::setHighAccuracyService(bool enableGalileoHas) { - if (productVariant == RTK_FACET_MOSAIC) - return sendWithResponse(serial2GNSS, message, reply, timeout, wait, response, responseSize); - else - return sendWithResponse(serialGNSS, message, reply, timeout, wait, response, responseSize); + // Not yet supported on this platform + return (true); // Return true to clear gnssConfigure test } -bool GNSS_MOSAIC::sendWithResponse(HardwareSerial *serialPort, const char *message, const char *reply, unsigned long timeout, unsigned long wait, - char *response, size_t responseSize) -{ - if (strlen(reply) == 0) // Reply can't be zero-length - return false; - - _isBlocking = true; // Suspend the GNSS read task - - if ((settings.debugGnss == true) && (!inMainMenu)) - systemPrintf("sendWithResponse: sending %s\r\n", message); - if (strlen(message) > 0) - serialPort->write(message, strlen(message)); // Send the message +//---------------------------------------- +// Set the minimum satellite signal level for navigation. +//---------------------------------------- +bool GNSS_MOSAIC::setMinCN0(uint8_t cnoValue) +{ + if (cnoValue > 60) + cnoValue = 60; + String cn0 = String(cnoValue); + String setting = String("scm,all," + cn0 + "\n\r"); + return (sendWithResponse(setting, "CN0Mask", 1000, 200)); +} - unsigned long startTime = millis(); - size_t replySeen = 0; - bool keepGoing = true; +//---------------------------------------- +// Turn on all the enabled NMEA messages on COM1 +//---------------------------------------- +bool GNSS_MOSAIC::setMessagesNMEA() +{ + bool gpggaEnabled = false; + bool gpzdaEnabled = false; + bool gpgstEnabled = false; - while ((keepGoing) && (replySeen < strlen(reply))) // While not timed out and reply not seen + String streams[MOSAIC_NUM_NMEA_STREAMS]; // Build a string for each stream + for (int messageNumber = 0; messageNumber < MAX_MOSAIC_NMEA_MSG; messageNumber++) // For each NMEA message { - if (serialPort->available()) // If a char is available + int stream = settings.mosaicMessageStreamNMEA[messageNumber]; + if (stream > 0) { - uint8_t c = serialPort->read(); // Read it - //if ((settings.debugGnss == true) && (!inMainMenu)) - // systemPrintf("%c", (char)c); - if (c == *(reply + replySeen)) // Is it a char from reply? + stream--; + + if (streams[stream].length() > 0) + streams[stream] += String("+"); + streams[stream] += String(mosaicMessagesNMEA[messageNumber].msgTextName); + + // If we are using IP based corrections, we need to send local data to the PPL + // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 + if (strstr(settings.pointPerfectKeyDistributionTopic, "/ip") != nullptr) { - if (response && (replySeen < (responseSize - 1))) - { - *(response + replySeen) = c; - *(response + replySeen + 1) = 0; - } - replySeen++; + // Mark PPL required messages as enabled if stream > 0 + if (strcmp(mosaicMessagesNMEA[messageNumber].msgTextName, "GGA") == 0) + gpggaEnabled = true; + if (strcmp(mosaicMessagesNMEA[messageNumber].msgTextName, "ZDA") == 0) + gpzdaEnabled = true; } - else - replySeen = 0; // Reset replySeen on an unexpected char - } - - // If the reply has started to arrive at the timeout, allow extra time - if ((millis() - startTime) > timeout) // Have we timed out? - if (replySeen == 0) // If replySeen is zero, don't keepGoing - keepGoing = false; - if ((millis() - startTime) > (timeout + wait)) // Have we really timed out? - keepGoing = false; // Don't keepGoing + if (strcmp(mosaicMessagesNMEA[messageNumber].msgTextName, "GST") == 0) + gpgstEnabled = true; + } } - if (replySeen == strlen(reply)) // If the reply was seen + if (pointPerfectIsEnabled()) { - startTime = millis(); - while ((millis() - startTime) < wait) + // Force on any messages that are needed for PPL + if (gpggaEnabled == false) { - if (serialPort->available()) - { - uint8_t c = serialPort->read(); - //if ((settings.debugGnss == true) && (!inMainMenu)) - // systemPrintf("%c", (char)c); - if (response && (replySeen < (responseSize - 1))) - { - *(response + replySeen) = c; - *(response + replySeen + 1) = 0; - } - replySeen++; - } + // Add GGA to Stream1 (streams[0]) + // TODO: We may need to be cleverer about which stream we choose, + // depending on the stream intervals + if (streams[0].length() > 0) + streams[0] += String("+"); + streams[0] += String("GGA"); + gpggaEnabled = true; + } + if (gpzdaEnabled == false) + { + if (streams[0].length() > 0) + streams[0] += String("+"); + streams[0] += String("ZDA"); + gpzdaEnabled = true; } + } - _isBlocking = false; + if (settings.ntripClient_TransmitGGA == true && settings.enableNtripClient == true) + { + // Force on GGA if needed for NTRIP + if (gpggaEnabled == false) + { + if (streams[0].length() > 0) + streams[0] += String("+"); + streams[0] += String("GGA"); + gpggaEnabled = true; + } + } - return true; + // Force GST on so we can extract the lat and lon standard deviations + if (gpgstEnabled == false) + { + if (streams[0].length() > 0) + streams[0] += String("+"); + streams[0] += String("GST"); + gpgstEnabled = true; } - _isBlocking = false; + bool response = true; - return false; -} + for (int stream = 0; stream < MOSAIC_NUM_NMEA_STREAMS; stream++) + { + if (streams[stream].length() == 0) + streams[stream] = String("none"); -//---------------------------------------- -// Send message. Wait for up to timeout millis for reply to arrive -// If the reply has started to be received when timeout is reached, wait for a further wait millis -// If the reply is seen, wait for a further wait millis -// During wait, keep reading incoming serial. If response is defined, copy up to responseSize bytes -// Inputs: -// message: String containing the message to send to the GNSS -// reply: String containing the first portion of the expected response -// timeout: Number of milliseconds to wat for the reply to arrive -// wait: Number of additional milliseconds if the reply is detected -// response: Address of buffer to receive the response -// responseSize: Maximum number of bytes to copy -// Outputs: -// Returns true if the response was received and false upon failure -//---------------------------------------- -bool GNSS_MOSAIC::sendWithResponse(String message, const char *reply, unsigned long timeout, unsigned long wait, - char *response, size_t responseSize) -{ - return sendWithResponse(message.c_str(), reply, timeout, wait, response, responseSize); + String setting = String("sno,Stream" + String(stream + 1) + ",COM1," + streams[stream] + "," + + String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); + response &= sendWithResponse(setting, "NMEAOutput"); + + if (settings.enableNmeaOnRadio) + setting = String("sno,Stream" + String(stream + MOSAIC_NUM_NMEA_STREAMS + 1) + ",COM2," + streams[stream] + + "," + String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); + else + setting = String("sno,Stream" + String(stream + MOSAIC_NUM_NMEA_STREAMS + 1) + ",COM2,none,off\n\r"); + response &= sendWithResponse(setting, "NMEAOutput"); + + if (settings.enableGnssToUsbSerial) + { + setting = + String("sno,Stream" + String(stream + (2 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",USB1," + streams[stream] + + "," + String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); + response &= sendWithResponse(setting, "NMEAOutput"); + } + else + { + // Disable the USB1 NMEA streams if settings.enableGnssToUsbSerial is not enabled + setting = String("sno,Stream" + String(stream + (2 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",USB1,none,off\n\r"); + response &= sendWithResponse(setting, "NMEAOutput"); + } + + if (settings.enableLogging) + { + setting = + String("sno,Stream" + String(stream + (3 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",DSK1," + streams[stream] + + "," + String(mosaicMsgRates[settings.mosaicStreamIntervalsNMEA[stream]].name) + "\n\r"); + response &= sendWithResponse(setting, "NMEAOutput"); + } + else + { + // Disable the DSK1 NMEA streams if settings.enableLogging is not enabled + setting = String("sno,Stream" + String(stream + (3 * MOSAIC_NUM_NMEA_STREAMS) + 1) + ",DSK1,none,off\n\r"); + response &= sendWithResponse(setting, "NMEAOutput"); + } + } + + return (response); } //---------------------------------------- -// Set the baud rate of mosaic-X5 COMn - from the super class -// Inputs: -// port: COM port number -// baudRate: New baud rate for the COM port -// Outputs: -// Returns true if the baud rate was set and false upon failure +// Turn on all the enabled RTCM Base messages on COM1, COM2 and USB1 (if enabled) //---------------------------------------- -bool GNSS_MOSAIC::setBaudRate(uint8_t port, uint32_t baudRate) +bool GNSS_MOSAIC::setMessagesRTCMBase() { - if (port < 1 || port > 4) + bool response = true; + + // Set RTCMv3 Intervals + for (int group = 0; group < MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS; group++) { - systemPrintln("setBaudRate error: out of range"); - return (false); + char flt[10]; + snprintf(flt, sizeof(flt), "%.1f", settings.mosaicMessageIntervalsRTCMv3Base[group]); + String setting = + String("sr3i," + String(mosaicRTCMv3MsgIntervalGroups[group].name) + "," + String(flt) + "\n\r"); + response &= sendWithResponse(setting, "RTCMv3Interval"); } - return setBaudRateCOM(port, baudRate); -} - -//---------------------------------------- -// Set the baud rate of mosaic-X5 COM1 -// This is used during the Bluetooth test -// Inputs: -// port: COM port number -// baudRate: New baud rate for the COM port -// Outputs: -// Returns true if the baud rate was set and false upon failure -//---------------------------------------- -bool GNSS_MOSAIC::setBaudRateCOM(uint8_t port, uint32_t baudRate) -{ - for (int i = 0; i < MAX_MOSAIC_COM_RATES; i++) + // Enable RTCMv3 + String messages = String(""); + for (int message = 0; message < MAX_MOSAIC_RTCM_V3_MSG; message++) { - if (baudRate == mosaicComRates[i].rate) + if (settings.mosaicMessageEnabledRTCMv3Base[message]) { - String setting = - String("scs,COM" + String(port) + "," + String(mosaicComRates[i].name) + ",bits8,No,bit1,none\n\r"); - return (sendWithResponse(setting, "COMSettings")); + if (messages.length() > 0) + messages += String("+"); + messages += String(mosaicMessagesRTCMv3[message].name); } } - return false; // Invalid baud -} + if (messages.length() == 0) + messages = String("none"); -//---------------------------------------- -// Enable all the valid constellations and bands for this platform -//---------------------------------------- -bool GNSS_MOSAIC::setConstellations() -{ - String enabledConstellations = ""; + String setting = String("sr3o,COM1+COM2"); + if (settings.enableGnssToUsbSerial) + setting += String("+USB1"); + setting += String("," + messages + "\n\r"); + response &= sendWithResponse(setting, "RTCMv3Output"); - for (int constellation = 0; constellation < MAX_MOSAIC_CONSTELLATIONS; constellation++) + if (!settings.enableGnssToUsbSerial) { - if (settings.mosaicConstellations[constellation] > 0) // == true - { - if (enabledConstellations.length() > 0) - enabledConstellations += String("+"); - enabledConstellations += String(mosaicSignalConstellations[constellation].name); - } + response &= sendWithResponse("sr3o,USB1,none\n\r", "RTCMv3Output"); } - if (enabledConstellations.length() == 0) - enabledConstellations = String("none"); - - String setting = String("sst," + enabledConstellations + "\n\r"); - return (sendWithResponse(setting, "SatelliteTracking", 1000, 200)); + return (response); } -// Enable / disable corrections protocol(s) on the Radio External port -// Always update if force is true. Otherwise, only update if enable has changed state -// Notes: -// NrBytesReceived is reset when sdio,COM2 is sent. This causes NrBytesReceived to -// be less than previousNrBytesReceived, which in turn causes a corrections timeout. -// So, we need to reset previousNrBytesReceived and firstTimeNrBytesReceived here. -bool GNSS_MOSAIC::setCorrRadioExtPort(bool enable, bool force) +//---------------------------------------- +// Turn on all the enabled RTCM Rover messages on COM1, COM2 and USB1 (if enabled) +//---------------------------------------- +bool GNSS_MOSAIC::setMessagesRTCMRover() { - if (force || (enable != _corrRadioExtPortEnabled)) + bool response = true; + bool rtcm1019Enabled = false; + bool rtcm1020Enabled = false; + bool rtcm1042Enabled = false; + bool rtcm1046Enabled = false; + + // Set RTCMv3 Intervals + for (int group = 0; group < MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS; group++) { - String setting = String("sdio,COM2,"); - if (enable) - setting += String("RTCMv3,"); - else - setting += String("none,"); - // Configure COM2 for NMEA and RTCMv3 output. No L-Band. Not encapsulated. - setting += String("RTCMv3+NMEA\n\r"); + char flt[10]; + snprintf(flt, sizeof(flt), "%.1f", settings.mosaicMessageIntervalsRTCMv3Rover[group]); + String setting = + String("sr3i," + String(mosaicRTCMv3MsgIntervalGroups[group].name) + "," + String(flt) + "\n\r"); + response &= sendWithResponse(setting, "RTCMv3Interval"); + } - if (sendWithResponse(setting, "DataInOut")) + // Enable RTCMv3 + String messages = String(""); + for (int message = 0; message < MAX_MOSAIC_RTCM_V3_MSG; message++) + { + if (settings.mosaicMessageEnabledRTCMv3Rover[message]) { - if ((settings.debugCorrections == true) && !inMainMenu) + if (messages.length() > 0) + messages += String("+"); + messages += String(mosaicMessagesRTCMv3[message].name); + + // If we are using IP based corrections, we need to send local data to the PPL + // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 + if (pointPerfectIsEnabled()) { - systemPrintf("Radio Ext corrections: %s -> %s%s\r\n", _corrRadioExtPortEnabled ? "enabled" : "disabled", - enable ? "enabled" : "disabled", force ? " (Forced)" : ""); + // Mark PPL required messages as enabled if rate > 0 + if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1019") == 0) + rtcm1019Enabled = true; + if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1020") == 0) + rtcm1020Enabled = true; + if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1042") == 0) + rtcm1042Enabled = true; + if (strcmp(mosaicMessagesRTCMv3[message].name, "RTCM1046") == 0) + rtcm1046Enabled = true; } + } + } - _corrRadioExtPortEnabled = enable; - previousNrBytesReceived = 0; - firstTimeNrBytesReceived = true; - return true; + if (pointPerfectIsEnabled()) + { + // Force on any messages that are needed for PPL + if (rtcm1019Enabled == false) + { + if (messages.length() > 0) + messages += String("+"); + messages += String("RTCM1019"); } - else + if (rtcm1020Enabled == false) { - systemPrintf("Radio Ext corrections FAILED: %s -> %s%s\r\n", - _corrRadioExtPortEnabled ? "enabled" : "disabled", enable ? "enabled" : "disabled", - force ? " (Forced)" : ""); + if (messages.length() > 0) + messages += String("+"); + messages += String("RTCM1020"); + } + if (rtcm1042Enabled == false) + { + if (messages.length() > 0) + messages += String("+"); + messages += String("RTCM1042"); + } + if (rtcm1046Enabled == false) + { + if (messages.length() > 0) + messages += String("+"); + messages += String("RTCM1046"); } } - return false; -} - -//---------------------------------------- -bool GNSS_MOSAIC::setDataBaudRate(uint32_t baud) -{ - return setBaudRateCOM(3, baud); -} - -//---------------------------------------- -// Set the elevation in degrees -// Inputs: -// elevationDegrees: The elevation value in degrees -// Notes: -// mosaic supports negative elevations, but our firmware only support 0-90 -//---------------------------------------- -bool GNSS_MOSAIC::setElevation(uint8_t elevationDegrees) -{ - if (elevationDegrees > 90) - elevationDegrees = 90; - String elev = String(elevationDegrees); - String setting = String("sem,PVT," + elev + "\n\r"); - return (sendWithResponse(setting, "ElevationMask")); -} + if (messages.length() == 0) + messages = String("none"); -//---------------------------------------- -// Enable all the valid messages for this platform -//---------------------------------------- -bool GNSS_MOSAIC::setMessages(int maxRetries) -{ - // TODO : do we need this? - return true; -} + String setting = String("sr3o,COM1+COM2"); + if (settings.enableGnssToUsbSerial) + setting += String("+USB1"); + setting += String("," + messages + "\n\r"); + response &= sendWithResponse(setting, "RTCMv3Output"); -//---------------------------------------- -// Enable all the valid messages for this platform over the USB port -//---------------------------------------- -bool GNSS_MOSAIC::setMessagesUsb(int maxRetries) -{ - // TODO : do we need this? - return true; -} + if (!settings.enableGnssToUsbSerial) + { + response &= sendWithResponse("sr3o,USB1,none\n\r", "RTCMv3Output"); + } -//---------------------------------------- -// Set the minimum satellite signal level for navigation. -//---------------------------------------- -bool GNSS_MOSAIC::setMinCnoRadio(uint8_t cnoValue) -{ - if (cnoValue > 60) - cnoValue = 60; - String cn0 = String(cnoValue); - String setting = String("scm,all," + cn0 + "\n\r"); - return (sendWithResponse(setting, "CN0Mask", 1000, 200)); + return (response); } //---------------------------------------- @@ -2495,9 +2463,19 @@ bool GNSS_MOSAIC::setModel(uint8_t modelNumber) } //---------------------------------------- -bool GNSS_MOSAIC::setRadioBaudRate(uint32_t baud) +// Configure multipath mitigation +//---------------------------------------- +bool GNSS_MOSAIC::setMultipathMitigation(bool enableMultipathMitigation) { - return setBaudRateCOM(2, baud); + // Does not exist on this platform + return true; +} + +// Given the name of a message, find it, and set the rate +bool GNSS_MOSAIC::setNmeaMessageRateByName(const char *msgName, uint8_t msgRate) +{ + // TODO + return (false); } //---------------------------------------- @@ -2521,19 +2499,12 @@ bool GNSS_MOSAIC::setRate(double secondsBetweenSolutions) } //---------------------------------------- -bool GNSS_MOSAIC::setTalkerGNGGA() -{ - return sendWithResponse("snti,GN\n\r", "NMEATalkerID"); -} - -//---------------------------------------- -// Hotstart GNSS to try to get RTK lock +// Enable/disable any output needed for tilt compensation //---------------------------------------- -bool GNSS_MOSAIC::softwareReset() +bool GNSS_MOSAIC::setTilt() { - // We could restart L-Band here if needed, but gnss->softwareReset is never called on the X5 - // Instead, update() does it when spartnCorrectionsReceived times out - return false; + // Not yet supported on this platform + return (true); // Return true to clear gnssConfigure test } //---------------------------------------- @@ -2595,7 +2566,8 @@ void GNSS_MOSAIC::storeBlock4013(SEMP_PARSE_STATE *parse) if (Tracking) { // SV is being tracked - std::vector::iterator pos = std::find_if(svInTracking.begin(), svInTracking.end(), find_sv(SVID)); + std::vector::iterator pos = + std::find_if(svInTracking.begin(), svInTracking.end(), find_sv(SVID)); if (pos == svInTracking.end()) // If it is not in svInTracking, add it svInTracking.push_back({SVID, millis()}); else // Update lastSeen @@ -2608,7 +2580,8 @@ void GNSS_MOSAIC::storeBlock4013(SEMP_PARSE_STATE *parse) else { // SV is not being tracked. If it is in svInTracking, remove it - std::vector::iterator pos = std::find_if(svInTracking.begin(), svInTracking.end(), find_sv(SVID)); + std::vector::iterator pos = + std::find_if(svInTracking.begin(), svInTracking.end(), find_sv(SVID)); if (pos != svInTracking.end()) svInTracking.erase(pos); } @@ -2621,7 +2594,8 @@ void GNSS_MOSAIC::storeBlock4013(SEMP_PARSE_STATE *parse) bool keepGoing = true; while (keepGoing) { - std::vector::iterator pos = std::find_if(svInTracking.begin(), svInTracking.end(), find_stale_sv(millis())); + std::vector::iterator pos = + std::find_if(svInTracking.begin(), svInTracking.end(), find_stale_sv(millis())); if (pos != svInTracking.end()) svInTracking.erase(pos); else @@ -2657,7 +2631,7 @@ void GNSS_MOSAIC::storeBlock4059(SEMP_PARSE_STATE *parse) { if (!present.mosaicMicroSd) return; - + if (sempSbfGetU1(parse, 14) < 1) // Check N is at least 1 return; @@ -2765,17 +2739,19 @@ bool GNSS_MOSAIC::surveyInReset() //---------------------------------------- bool GNSS_MOSAIC::surveyInStart() { - // Start a Self-optimizing Base Station - bool response = sendWithResponse("spm,Static,,auto\n\r", "PVTMode"); - _determiningFixedPosition = true; // Ensure flag is set initially _autoBaseStartTimer = millis(); // Stamp when averaging began + // If we are already in the appropriate base mode, no changes needed + if (gnssInBaseSurveyInMode()) + return (true); // No changes needed + + // Start a Self-optimizing Base Station + bool response = sendWithResponse("spm,Static,,auto\n\r", "PVTMode"); + if (response == false) - { systemPrintln("Survey start failed"); - } return (response); } @@ -2955,9 +2931,12 @@ uint32_t GNSS_MOSAIC::baudGetMaximum() return (mosaicComRates[MAX_MOSAIC_COM_RATES - 1].rate); } -//Return true if the receiver is detected +// Return true if the receiver is detected bool GNSS_MOSAIC::isPresent() { + systemPrintln("Starting communication with mosaic-X5"); + paintMosaicBooting(); + if (productVariant != RTK_FLEX) // productVariant == RTK_FACET_MOSAIC { // Set COM4 to: CMD input (only), SBF output (only) @@ -2968,12 +2947,14 @@ bool GNSS_MOSAIC::isPresent() { // Set COM1 to: auto input, RTCMv3+SBF+NMEA+Encapsulate output // Mosaic could still be starting up, so allow many retries - return isPresentOnSerial(serialGNSS, "sdio,COM1,auto,RTCMv3+SBF+NMEA+Encapsulate\n\r", "DataInOut", "COM1>", 20); + return isPresentOnSerial(serialGNSS, "sdio,COM1,auto,RTCMv3+SBF+NMEA+Encapsulate\n\r", "DataInOut", "COM1>", + 20); } } -//Return true if the receiver is detected -bool GNSS_MOSAIC::isPresentOnSerial(HardwareSerial *serialPort, const char *command, const char *response, const char *console, int retryLimit) +// Return true if the receiver is detected +bool GNSS_MOSAIC::isPresentOnSerial(HardwareSerial *serialPort, const char *command, const char *response, + const char *console, int retryLimit) { // Mosaic could still be starting up, so allow many retries int retries = 0; @@ -3005,7 +2986,7 @@ bool GNSS_MOSAIC::isPresentOnSerial(HardwareSerial *serialPort, const char *comm if (retries == retryLimit) { systemPrintln("Could not communicate with mosaic-X5 at selected baud rate"); - return(false); + return (false); } } @@ -3277,7 +3258,8 @@ bool mosaicIsPresentOnFlex() serialTestGNSS.begin(115200, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // Only try 3 times. LG290P detection will have been done first. X5 should have booted. Baud rate could be wrong. - if (mosaic.isPresentOnSerial(&serialTestGNSS, "sdio,COM1,auto,RTCMv3+SBF+NMEA+Encapsulate\n\r", "DataInOut", "COM1>", 3) == true) + if (mosaic.isPresentOnSerial(&serialTestGNSS, "sdio,COM1,auto,RTCMv3+SBF+NMEA+Encapsulate\n\r", "DataInOut", + "COM1>", 3) == true) { if (settings.debugGnss) systemPrintln("mosaic-X5 detected at 115200 baud"); @@ -3297,14 +3279,15 @@ bool mosaicIsPresentOnFlex() serialTestGNSS.begin(460800, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // Only try 3 times, so we fail and pass on to the next Facet GNSS detection - if (mosaic.isPresentOnSerial(&serialTestGNSS, "sdio,COM1,auto,RTCMv3+SBF+NMEA+Encapsulate\n\r", "DataInOut", "COM1>", 3) == true) + if (mosaic.isPresentOnSerial(&serialTestGNSS, "sdio,COM1,auto,RTCMv3+SBF+NMEA+Encapsulate\n\r", "DataInOut", + "COM1>", 3) == true) { // serialGNSS and serial2GNSS have not yet been begun. We need to saveConfiguration manually unsigned long start = millis(); bool result = mosaic.sendWithResponse(&serialTestGNSS, "eccf,Current,Boot\n\r", "CopyConfigFile", 5000); if (settings.debugGnss) systemPrintf("saveConfiguration: sendWithResponse returned %s after %d ms\r\n", result ? "true" : "false", - millis() - start); + millis() - start); if (settings.debugGnss) systemPrintf("mosaic-X5 baud rate %supdated\r\n", result ? "" : "NOT "); serialTestGNSS.end(); diff --git a/Firmware/RTK_Everywhere/GNSS_None.h b/Firmware/RTK_Everywhere/GNSS_None.h index 879804b6e..dd5e94b10 100644 --- a/Firmware/RTK_Everywhere/GNSS_None.h +++ b/Firmware/RTK_Everywhere/GNSS_None.h @@ -9,23 +9,14 @@ GNSS_None.h class GNSS_None : public GNSS { -protected: - // Setup the general configuration of the GNSS - // Not Rover or Base specific (ie, baud rates) - // Outputs: - // Returns true if successfully configured and false upon failure - bool configureGNSS() - { - return false; - } - + protected: // Set the minimum satellite signal level for navigation. - bool setMinCnoRadio(uint8_t cnoValue) + bool setMinCN0(uint8_t cnoValue) { return false; } -public: + public: // Constructor GNSS_None() : GNSS() { @@ -74,21 +65,21 @@ class GNSS_None : public GNSS return false; } - // Setup the timepulse output on the PPS pin for external triggering - // Outputs - // Returns true if the pin was successfully setup and false upon - // failure - bool beginPPS() + bool checkNMEARates() { return false; } - bool checkNMEARates() + bool checkPPPRates() { return false; } - bool checkPPPRates() + // Setup the general configuration of the GNSS + // Not Rover or Base specific (ie, baud rates) + // Outputs: + // Returns true if successfully configured and false upon failure + bool configure() { return false; } @@ -155,19 +146,6 @@ class GNSS_None : public GNSS { } - void enableGgaForNtrip() - { - } - - // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted - // even if there is no GPS fix. We use it to test serial output. - // Outputs: - // Returns true if successfully started and false upon failure - bool enableRTCMTest() - { - return false; - } - // Restore the GNSS to the factory settings void factoryReset() { @@ -191,6 +169,22 @@ class GNSS_None : public GNSS return false; } + bool fixRateIsAllowed(uint32_t fixRateMs) + { + return false; + } + + // Return min/max rate in ms + uint32_t fixRateGetMinimumMs() + { + return 0; + } + + uint32_t fixRateGetMaximumMs() + { + return 0; + } + // Return the number of active/enabled messages uint8_t getActiveMessageCount() { @@ -299,6 +293,12 @@ class GNSS_None : public GNSS return _minute; } + // Returns the current mode: Base/Rover/etc + uint8_t getMode() + { + return 0; + } + // Returns month number or zero if not online uint8_t getMonth() { @@ -358,11 +358,6 @@ class GNSS_None : public GNSS return 0; } - float getSurveyInStartingAccuracy() - { - return 0; - } - // Returns timing accuracy or zero if not online uint32_t getTimeAccuracy() { @@ -375,6 +370,20 @@ class GNSS_None : public GNSS return _year; } + // Helper functions for the current mode as read from the GNSS receiver + bool gnssInBaseFixedMode() + { + return false; + } + bool gnssInBaseSurveyInMode() + { + return false; + } + bool gnssInRoverMode() + { + return false; + } + bool isBlocking() { return false; @@ -501,6 +510,12 @@ class GNSS_None : public GNSS return dataLength; } + // Hardware or software reset the GNSS receiver + bool reset() + { + return true; + } + uint16_t rtcmBufferAvailable() { return 0; @@ -534,19 +549,29 @@ class GNSS_None : public GNSS return false; } - // Enable all the valid constellations and bands for this platform - bool setConstellations() + bool setBaudRateData(uint32_t baud) { return true; } - // Enable / disable corrections protocol(s) on the Radio External port - bool setCorrRadioExtPort(bool enable, bool force) + bool setBaudRateComm(uint32_t baud) + { + return true; + } + + bool setBaudRateRadio(uint32_t baud) + { + return true; + } + + // Enable all the valid constellations and bands for this platform + bool setConstellations() { return true; } - bool setDataBaudRate(uint32_t baud) + // Enable / disable corrections protocol(s) on the Radio External port + bool setCorrRadioExtPort(bool enable, bool force) { return true; } @@ -559,14 +584,32 @@ class GNSS_None : public GNSS return true; } - // Enable all the valid messages for this platform - bool setMessages(int maxRetries) + // Control whether HAS E6 is used in location fixes or not + bool setHighAccuracyService(bool enableGalileoHas) + { + return true; + } + + // Configure device-direct logging. Currently mosaic-X5 specific. + bool setLogging() + { + return true; + } + + // Configure NMEA messages + bool setMessagesNMEA() + { + return true; + } + + // Configure RTCM Base messages + bool setMessagesRTCMBase() { return true; } - // Enable all the valid messages for this platform over the USB port - bool setMessagesUsb(int maxRetries) + // Configure RTCM Base messages + bool setMessagesRTCMRover() { return true; } @@ -579,11 +622,23 @@ class GNSS_None : public GNSS return true; } - bool setRadioBaudRate(uint32_t baud) + bool setMultipathMitigation(bool enableMultipathMitigation) { return true; } + // Given the name of a message, find it, and set the rate + bool setNmeaMessageRateByName(const char *msgName, uint8_t msgRate) + { + return true; + } + + // Configure the Pulse-per-second pin based on user settings + bool setPPS() + { + return false; + } + // Specify the interval between solutions // Inputs: // secondsBetweenSolutions: Number of seconds between solutions @@ -595,13 +650,7 @@ class GNSS_None : public GNSS return true; } - bool setTalkerGNGGA() - { - return true; - } - - // Hotstart GNSS to try to get RTK lock - bool softwareReset() + bool setTilt() { return true; } diff --git a/Firmware/RTK_Everywhere/GNSS_UM980.h b/Firmware/RTK_Everywhere/GNSS_UM980.h index 588f228c1..3d28b6e8f 100644 --- a/Firmware/RTK_Everywhere/GNSS_UM980.h +++ b/Firmware/RTK_Everywhere/GNSS_UM980.h @@ -111,23 +111,11 @@ class GNSS_UM980 : GNSS // Not Rover or Base specific (ie, baud rates) // Outputs: // Returns true if successfully configured and false upon failure - bool configureGNSS(); + bool configure(); // Turn off all NMEA and RTCM void disableAllOutput(); - // Disable all output, then re-enable - void disableRTCM(); - - // Turn on all the enabled NMEA messages on COM3 - bool enableNMEA(); - - // Turn on all the enabled RTCM Rover messages on COM3 - bool enableRTCMRover(); - - // Turn on all the enabled RTCM Base messages on COM3 - bool enableRTCMBase(); - uint8_t getActiveNmeaMessageCount(); // Given the name of an NMEA message, return the array number @@ -143,19 +131,10 @@ class GNSS_UM980 : GNSS // Controls the messages that get broadcast over Bluetooth and logged (if enabled) void menuMessagesSubtype(float *localMessageRate, const char *messageType); - bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); // From the super class - - // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS - // Inputs: - // baudRate: The desired baudrate - bool setBaudRateCOM3(uint32_t baudRate); - bool setHighAccuracyService(bool enableGalileoHas); // Set the minimum satellite signal level for navigation. - bool setMinCnoRadio(uint8_t cnoValue); - - bool setMultipathMitigation(bool enableMultipathMitigation); + bool setMinCN0(uint8_t cnoValue); public: // Constructor @@ -186,12 +165,6 @@ class GNSS_UM980 : GNSS // Returns true when an external event occurs and false if no event bool beginExternalEvent(); - // Setup the timepulse output on the PPS pin for external triggering - // Outputs - // Returns true if the pin was successfully setup and false upon - // failure - bool beginPPS(); - bool checkNMEARates(); bool checkPPPRates(); @@ -225,14 +198,6 @@ class GNSS_UM980 : GNSS void debuggingEnable(); - void enableGgaForNtrip(); - - // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted - // even if there is no GPS fix. We use it to test serial output. - // Outputs: - // Returns true if successfully started and false upon failure - bool enableRTCMTest(); - // Restore the GNSS to the factory settings void factoryReset(); @@ -245,6 +210,13 @@ class GNSS_UM980 : GNSS // Returns true if successfully started and false upon failure bool fixedBaseStart(); + bool fixRateIsAllowed(uint32_t fixRateMs); + + //Return min/max rate in ms + uint32_t fixRateGetMinimumMs(); + + uint32_t fixRateGetMaximumMs(); + // Return the number of active/enabled messages uint8_t getActiveMessageCount(); @@ -299,12 +271,13 @@ class GNSS_UM980 : GNSS // Returns two digits of milliseconds or zero if not online uint8_t getMillisecond(); - // Get the minimum satellite signal level for navigation. - uint8_t getMinCno(); - // Returns minutes or zero if not online uint8_t getMinute(); + // Returns the current mode + // 0 - Unknown, 1 - Rover Survey, 2 - Rover UAV, 3 - Rover Auto, 4 - Base Survey-in, 5 - Base fixed + uint8_t getMode(); + // Returns month number or zero if not online uint8_t getMonth(); @@ -334,17 +307,16 @@ class GNSS_UM980 : GNSS // Return the number of seconds the survey-in process has been running int getSurveyInObservationTime(); - float getSurveyInStartingAccuracy(); - // Returns timing accuracy or zero if not online uint32_t getTimeAccuracy(); // Returns full year, ie 2023, not 23. uint16_t getYear(); - // Returns true if the device is in Rover mode - // Currently the only two modes are Rover or Base - bool inRoverMode(); + // Helper functions for the current mode as read from the GNSS receiver + bool gnssInBaseFixedMode(); + bool gnssInBaseSurveyInMode(); + bool gnssInRoverMode(); bool isBlocking(); @@ -420,6 +392,9 @@ class GNSS_UM980 : GNSS uint16_t rtcmBufferAvailable(); + // Hardware or software reset the GNSS + bool reset(); + // If LBand is being used, ignore any RTCM that may come in from the GNSS void rtcmOnGnssDisable(); @@ -433,6 +408,17 @@ class GNSS_UM980 : GNSS // Returns true when the configuration was saved and false upon failure bool saveConfiguration(); + bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); // From the super class + + // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS + // Inputs: + // baudRate: The desired baudrate + bool setBaudRateComm(uint32_t baudRate); + + bool setBaudRateData(uint32_t baudRate); + + bool setBaudRateRadio(uint32_t baudRate); + // Enable all the valid constellations and bands for this platform bool setConstellations(); @@ -442,38 +428,42 @@ class GNSS_UM980 : GNSS return true; } - bool setDataBaudRate(uint32_t baud); - // Set the elevation in degrees // Inputs: // elevationDegrees: The elevation value in degrees bool setElevation(uint8_t elevationDegrees); - // Enable all the valid messages for this platform - bool setMessages(int maxRetries); + // Configure any logging settings - currently mosaic-X5 specific + bool setLogging(); + + // Turn on all the enabled NMEA messages on COM3 + bool setMessagesNMEA(); - // Enable all the valid messages for this platform over the USB port - bool setMessagesUsb(int maxRetries); + // Turn on all the enabled RTCM Rover messages on COM3 + bool setMessagesRTCMRover(); + + // Turn on all the enabled RTCM Base messages on COM3 + bool setMessagesRTCMBase(); // Set the dynamic model to use for RTK // Inputs: // modelNumber: Number of the model to use, provided by radio library bool setModel(uint8_t modelNumber); - // Set all NMEA message report rates to one value - void setNmeaMessageRates(uint8_t msgRate); + bool setMultipathMitigation(bool enableMultipathMitigation); // Given the name of a message, find it, and set the rate bool setNmeaMessageRateByName(const char *msgName, uint8_t msgRate); + // Configure the Pulse-per-second pin based on user settings + bool setPPS(); + // Set all RTCM Rover message report rates to one value void setRtcmRoverMessageRates(uint8_t msgRate); // Given the name of a message, find it, and set the rate bool setRtcmRoverMessageRateByName(const char *msgName, uint8_t msgRate); - bool setRadioBaudRate(uint32_t baud); - // Specify the interval between solutions // Inputs: // secondsBetweenSolutions: Number of seconds between solutions @@ -482,10 +472,8 @@ class GNSS_UM980 : GNSS // failure bool setRate(double secondsBetweenSolutions); - bool setTalkerGNGGA(); - - // Hotstart GNSS to try to get RTK lock - bool softwareReset(); + // Enable/disable any output needed for tilt compensation + bool setTilt(); bool standby(); diff --git a/Firmware/RTK_Everywhere/GNSS_UM980.ino b/Firmware/RTK_Everywhere/GNSS_UM980.ino index 9a67c9757..1614794f7 100644 --- a/Firmware/RTK_Everywhere/GNSS_UM980.ino +++ b/Firmware/RTK_Everywhere/GNSS_UM980.ino @@ -15,6 +15,10 @@ GNSS_UM980.ino #ifdef COMPILE_UM980 +bool um980MessagesEnabled_NMEA = false; // Goes true when we enable NMEA messages +bool um980MessagesEnabled_RTCM_Rover = false; // Goes true when we enable RTCM Rover messages +bool um980MessagesEnabled_RTCM_Base = false; // Goes true when we enable RTCM Base messages + //---------------------------------------- // If we have decryption keys, configure module // Note: don't check online.lband_neo here. We could be using ip corrections @@ -70,7 +74,7 @@ void GNSS_UM980::begin() _um980 = new UM980(); // In order to reduce UM980 configuration time, the UM980 library blocks the start of BESTNAV and RECTIME until 3D - // fix is achieved However, if all NMEA messages are disabled, the UM980 will never detect a 3D fix. + // fix is achieved. However, if all NMEA messages are disabled, the UM980 will never detect a 3D fix. if (isGgaActive()) // If NMEA GPGGA is turned on, suppress BESTNAV messages until GPGGA reports a 3D fix _um980->disableBinaryBeforeFix(); @@ -81,7 +85,7 @@ void GNSS_UM980::begin() if (_um980->begin(*serialGNSS) == false) // Give the serial port over to the library { - if (settings.debugGnss) + if (settings.debugGnssConfig) systemPrintln("GNSS UM980 failed to begin. Trying again."); // Try again with power on delay @@ -98,11 +102,9 @@ void GNSS_UM980::begin() systemPrintln("GNSS UM980 online"); + // Turn on/off debug messages if (settings.debugGnss) - debuggingEnable(); // Print all debug to Serial - - // Check firmware version and print info - printModuleInfo(); + debuggingEnable(); // Shortly after reset, the UM980 responds to the VERSIONB command with OK but doesn't report version information snprintf(gnssFirmwareVersion, sizeof(gnssFirmwareVersion), "%s", _um980->getVersion()); @@ -112,11 +114,12 @@ void GNSS_UM980::begin() // Shortly after reset, the UM980 responds to the VERSIONB command with OK but doesn't report version // information delay(2000); // 1s fails, 2s ok - - // Ask for the version again after a short delay - snprintf(gnssFirmwareVersion, sizeof(gnssFirmwareVersion), "%s", _um980->getVersion()); } + // Ask for the version again after a short delay + // Check firmware version and print info + printModuleInfo(); + if (sscanf(gnssFirmwareVersion, "%d", &gnssFirmwareVersionInt) != 1) gnssFirmwareVersionInt = 99; @@ -124,8 +127,6 @@ void GNSS_UM980::begin() } //---------------------------------------- -// Setup the timepulse output on the PPS pin for external triggering -// Setup TM2 time stamp input as need //---------------------------------------- bool GNSS_UM980::beginExternalEvent() { @@ -133,13 +134,19 @@ bool GNSS_UM980::beginExternalEvent() return (false); } -//---------------------------------------- -// Setup the timepulse output on the PPS pin for external triggering -//---------------------------------------- -bool GNSS_UM980::beginPPS() +// Configure the Pulse-per-second pin based on user settings +bool GNSS_UM980::setPPS() { - // UM980 PPS signal not exposed - return (false); + // The PPS signal is not exposed on the Torch so we don't configure the PPS based on internal settings, but we do + // configure the PPS so that the GNSS LED blinks + + // Read, modify, write + // The UM980 does have the ability to read the current PPS settings from CONFIG output, but this function + // gets called very rarely. Just do a write for now. + + // Enable PPS signal with a width of 200ms, and a period of 1 second + return (_um980->enablePPS(settings.externalPulseLength_us, settings.externalPulseTimeBetweenPulse_us / + 1000)); // widthMicroseconds, periodMilliseconds } //---------------------------------------- @@ -159,130 +166,57 @@ bool GNSS_UM980::checkPPPRates() //---------------------------------------- bool GNSS_UM980::configureBase() { - /* - Disable all messages - Start base - Enable RTCM Base messages - Enable NMEA messages - */ - - if (online.gnss == false) - { - systemPrintln("GNSS not online"); - return (false); - } - - // Trusting the saved configuration does not seem to work on the UM980. - // It looks like the GPGGA NMEA output does not restart...? - // (Re)configuration is quick. Doing this every time is not much of an overhead. - // - // if (settings.gnssConfiguredBase) - // { - // if (settings.debugGnss) - // systemPrintln("Skipping UM980 Base configuration"); - // return true; - // } + // If we are already in the appropriate base mode, no changes needed + if (settings.fixedBase == false && gnssInBaseSurveyInMode()) + return (true); + if (settings.fixedBase == true && gnssInBaseFixedMode()) + return (true); - disableAllOutput(); - - bool response = true; + // Assume we are changing from Rover to Base, request any additional config changes // Set the dynamic mode. This will cancel any base averaging mode and is needed // to allow a freshly started device to settle in regular GNSS reception mode before issuing - // um980BaseAverageStart(). - response &= setModel(settings.dynamicModel); - - response &= setMultipathMitigation(settings.enableMultipathMitigation); - - response &= setHighAccuracyService(settings.enableGalileoHas); - - response &= enableRTCMBase(); // Only turn on messages, do not turn off messages. We assume the caller has - // UNLOG or similar. + // a surveyInStart(). + gnssConfigure(GNSS_CONFIG_MODEL); - // Only turn on messages, do not turn off messages. We assume the caller has UNLOG or similar. - response &= enableNMEA(); + // Request a change to Base RTCM + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); - // Save the current configuration into non-volatile memory (NVM) - response &= _um980->saveConfiguration(); - - if (response == false) - { - systemPrintln("UM980 Base failed to configure"); - } - - if (settings.debugGnss) - systemPrintln("UM980 Base configured"); - - settings.gnssConfiguredBase = response; - - return (response); + return (true); } //---------------------------------------- bool GNSS_UM980::configureOnce() { - /* - Disable all message traffic - Set COM port baud rates, - UM980 COM1 - Direct to USB, 115200 - UM980 COM2 - To IMU. From settings. - UM980 COM3 - BT, config and LoRa Radio. Configured for 115200 from begin(). - Set minCNO - Set elevationAngle - Set Constellations - Set messages - Enable selected NMEA messages on COM3 - Enable selected RTCM messages on COM3 -*/ - - // // If our settings haven't changed, trust GNSS's settings - if (settings.gnssConfiguredOnce) - { - systemPrintln("UM980 configuration maintained"); - return (true); - } - - if (settings.debugGnss) - debuggingEnable(); // Print all debug to Serial - - disableAllOutput(); // Disable COM1/2/3 - bool response = true; - response &= _um980->setPortBaudrate("COM1", 115200); // COM1 is connected to switch, then USB - response &= _um980->setPortBaudrate("COM2", 115200); // COM2 is connected to the IMU - response &= _um980->setPortBaudrate("COM3", 115200); // COM3 is connected to the switch, then ESP32 - - // // For now, let's not change the baud rate of the interface. We'll be using the default 115200 for now. - // response &= setBaudRateCOM3(settings.dataPortBaud); // COM3 is connected to ESP UART2 - // Enable PPS signal with a width of 200ms, and a period of 1 second - response &= _um980->enablePPS(200000, 1000); // widthMicroseconds, periodMilliseconds - - response &= setElevation(settings.minElev); // UM980 default is 5 degrees. Our default is 10. + if (settings.debugGnssConfig) + systemPrintln("Configuring UM980"); - response &= setMinCnoRadio(settings.minCNO); + // Read, modify, write - response &= setConstellations(); + // Output must be disabled before sending SIGNALGROUP command in order to get the OK response + disableAllOutput(); // Disable COM1/2/3 - if (_um980->isConfigurationPresent("CONFIG SIGNALGROUP 2") == false) + if (_um980->sendCommand("CONFIG SIGNALGROUP 2") == false) { - if (_um980->sendCommand("CONFIG SIGNALGROUP 2") == false) - systemPrintln("Signal group 2 command failed"); - else - { - systemPrintln("Enabling additional reception on UM980. This can take a few seconds."); - - while (1) - { - delay(1000); // Wait for device to reboot - if (_um980->isConnected()) - break; - else - systemPrintln("UM980 rebooting"); - } + systemPrintln("Signal group 2 command failed"); + response = false; + } + else + { + systemPrintln("Enabling additional reception on UM980. This can take a few seconds."); - systemPrintln("UM980 has completed reboot."); + while (1) + { + delay(1000); // Wait for device to reboot + if (_um980->isConnected()) + break; + else + systemPrintln("UM980 rebooting"); } + + systemPrintln("UM980 has completed reboot."); } if (response) @@ -290,15 +224,10 @@ bool GNSS_UM980::configureOnce() online.gnss = true; // If we failed before, mark as online now systemPrintln("UM980 configuration updated"); - - // Save the current configuration into non-volatile memory (NVM) - response &= _um980->saveConfiguration(); } else online.gnss = false; // Take it offline - settings.gnssConfiguredOnce = response; - return (response); } @@ -314,7 +243,7 @@ bool GNSS_UM980::configureNtpMode() // Setup the GNSS module for any setup (base or rover) // In general we check if the setting is different than setting stored in NVM before writing it. //---------------------------------------- -bool GNSS_UM980::configureGNSS() +bool GNSS_UM980::configure() { for (int x = 0; x < 3; x++) { @@ -324,10 +253,7 @@ bool GNSS_UM980::configureGNSS() // If we fail, reset UM980 systemPrintln("Resetting UM980 to complete configuration"); - gnssReset(); - delay(500); - gnssBoot(); - delay(500); + reset(); // Hardware reset the UM980 } systemPrintln("UM980 failed to configure"); @@ -339,70 +265,25 @@ bool GNSS_UM980::configureGNSS() //---------------------------------------- bool GNSS_UM980::configureRover() { - /* - Disable all message traffic - Cancel any survey-in modes - Set mode to Rover + dynamic model - Set minElevation - Enable RTCM messages on COM3 - Enable NMEA on COM3 - */ - if (online.gnss == false) - { - systemPrintln("GNSS not online"); - return (false); - } - - // Trusting the saved configuration does not seem to work on the UM980. - // It looks like the GPGGA NMEA output does not restart...? - // (Re)configuration is quick. Doing this every time is not much of an overhead. - // - // if (settings.gnssConfiguredRover) - // { - // systemPrintln("Skipping UM980 Rover configuration"); - // return (true); - // } - - disableAllOutput(); - - bool response = true; - - response &= setModel(settings.dynamicModel); // This will cancel any base averaging mode - - response &= setElevation(settings.minElev); // UM980 default is 5 degrees. Our default is 10. - - response &= setMultipathMitigation(settings.enableMultipathMitigation); - - response &= setHighAccuracyService(settings.enableGalileoHas); - - // Configure UM980 to output binary reports out COM2, connected to IM19 COM3 - response &= _um980->sendCommand("BESTPOSB COM2 0.2"); // 5Hz - response &= _um980->sendCommand("PSRVELB COM2 0.2"); - - // Configure UM980 to output NMEA reports out COM2, connected to IM19 COM3 - response &= _um980->setNMEAPortMessage("GPGGA", "COM2", 0.2); // 5Hz - - // Enable the NMEA sentences and RTCM on COM3 last. This limits the traffic on the config - // interface port during config. + // Determine current mode. If we are already in Rover, no changes needed + // 0 - Unknown, 1 - Rover Survey, 2 - Rover UAV, 3 - Rover Auto, 4 - Base Survey-in, 5 - Base fixed + int currentMode = getMode(); + if (settings.dynamicModel == UM980_DYN_MODEL_SURVEY && currentMode == 1) + return (true); + if (settings.dynamicModel == UM980_DYN_MODEL_UAV && currentMode == 2) + return (true); + if (settings.dynamicModel == UM980_DYN_MODEL_AUTOMOTIVE && currentMode == 3) + return (true); - // Only turn on messages, do not turn off messages. We assume the caller has UNLOG or similar. - response &= enableRTCMRover(); - // TODO consider reducing the GSV sentence to 1/4 of the GPGGA setting + // Assume we are changing from Base to Rover, request any additional config changes - // Only turn on messages, do not turn off messages. We assume the caller has UNLOG or similar. - response &= enableNMEA(); + // Sets the dynamic model (Survey/UAV/Automotive) and puts the device into Rover mode + gnssConfigure(GNSS_CONFIG_MODEL); - // Save the current configuration into non-volatile memory (NVM) - response &= _um980->saveConfiguration(); + // Request a change to Rover RTCM + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); - if (response == false) - { - systemPrintln("UM980 Rover failed to configure"); - } - - settings.gnssConfiguredRover = response; - - return (response); + return (true); } //---------------------------------------- @@ -440,6 +321,8 @@ void GNSS_UM980::createMessageListBase(String &returnText) } } +// GNSS debugging has to be outside of gnssUpdate() because we often need to immediately turn on/off debugging +// ie, entering the system menu //---------------------------------------- void GNSS_UM980::debuggingDisable() { @@ -461,7 +344,7 @@ void GNSS_UM980::debuggingEnable() // Turn off all NMEA and RTCM void GNSS_UM980::disableAllOutput() { - if (settings.debugGnss) + if (settings.debugGnssConfig) systemPrintln("UM980 disable output"); // Turn off local noise before moving to other ports @@ -481,171 +364,6 @@ void GNSS_UM980::disableAllOutput() systemPrintln("UM980 failed to disable output"); } -//---------------------------------------- -// Disable all output, then re-enable NMEA -//---------------------------------------- -void GNSS_UM980::disableRTCM() -{ - disableAllOutput(); - enableNMEA(); -} - -//---------------------------------------- -void GNSS_UM980::enableGgaForNtrip() -{ - // TODO um980EnableGgaForNtrip(); -} - -//---------------------------------------- -// Turn on all the enabled NMEA messages on COM3 -//---------------------------------------- -bool GNSS_UM980::enableNMEA() -{ - bool response = true; - bool gpggaEnabled = false; - bool gpzdaEnabled = false; - - for (int messageNumber = 0; messageNumber < MAX_UM980_NMEA_MSG; messageNumber++) - { - // Only turn on messages, do not turn off messages set to 0. This saves on command sending. We assume the caller - // has UNLOG or similar. - if (settings.um980MessageRatesNMEA[messageNumber] > 0) - { - // If any one of the commands fails, report failure overall - response &= _um980->setNMEAPortMessage(umMessagesNMEA[messageNumber].msgTextName, "COM3", - settings.um980MessageRatesNMEA[messageNumber]); - - if (response == false && settings.debugGnss) - systemPrintf("Enable NMEA failed at messageNumber %d %s.\r\n", messageNumber, - umMessagesNMEA[messageNumber].msgTextName); - - // If we are using IP based corrections, we need to send local data to the PPL - // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 - if (pointPerfectServiceUsesKeys()) - { - // Mark PPL required messages as enabled if rate > 0 - if (settings.um980MessageRatesNMEA[messageNumber] > 0) - { - if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "GPGGA") == 0) - gpggaEnabled = true; - else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "GPZDA") == 0) - gpzdaEnabled = true; - } - } - } - } - - if (pointPerfectServiceUsesKeys()) - { - // Force on any messages that are needed for PPL - if (gpggaEnabled == false) - response &= _um980->setNMEAPortMessage("GPGGA", "COM3", 1); - if (gpzdaEnabled == false) - response &= _um980->setNMEAPortMessage("GPZDA", "COM3", 1); - } - - return (response); -} - -//---------------------------------------- -// Turn on all the enabled RTCM Base messages on COM3 -//---------------------------------------- -bool GNSS_UM980::enableRTCMBase() -{ - bool response = true; - - for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) - { - // Only turn on messages, do not turn off messages set to 0. This saves on command sending. We assume the caller - // has UNLOG or similar. - if (settings.um980MessageRatesRTCMBase[messageNumber] > 0) - { - // If any one of the commands fails, report failure overall - response &= _um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", - settings.um980MessageRatesRTCMBase[messageNumber]); - - if (response == false && settings.debugGnss) - systemPrintf("Enable RTCM failed at messageNumber %d %s.", messageNumber, - umMessagesRTCM[messageNumber].msgTextName); - } - } - - return (response); -} - -//---------------------------------------- -// Turn on all the enabled RTCM Rover messages on COM3 -//---------------------------------------- -bool GNSS_UM980::enableRTCMRover() -{ - bool response = true; - bool rtcm1019Enabled = false; - bool rtcm1020Enabled = false; - bool rtcm1042Enabled = false; - bool rtcm1046Enabled = false; - - for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) - { - // Only turn on messages, do not turn off messages set to 0. This saves on command sending. We assume the caller - // has UNLOG or similar. - if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) - { - if (_um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", - settings.um980MessageRatesRTCMRover[messageNumber]) == false) - { - if (settings.debugGnss) - systemPrintf("Enable RTCM failed at messageNumber %d %s.", messageNumber, - umMessagesRTCM[messageNumber].msgTextName); - response &= false; // If any one of the commands fails, report failure overall - } - - // If we are using IP based corrections, we need to send local data to the PPL - // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 - if (pointPerfectServiceUsesKeys()) - { - // Mark PPL required messages as enabled if rate > 0 - if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) - { - if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1019") == 0) - rtcm1019Enabled = true; - else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1020") == 0) - rtcm1020Enabled = true; - else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1042") == 0) - rtcm1042Enabled = true; - else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1046") == 0) - rtcm1046Enabled = true; - } - } - } - } - - if (pointPerfectServiceUsesKeys()) - { - // Force on any messages that are needed for PPL - if (rtcm1019Enabled == false) - response &= _um980->setRTCMPortMessage("RTCM1019", "COM3", 1); - if (rtcm1020Enabled == false) - response &= _um980->setRTCMPortMessage("RTCM1020", "COM3", 1); - if (rtcm1042Enabled == false) - response &= _um980->setRTCMPortMessage("RTCM1042", "COM3", 1); - if (rtcm1046Enabled == false) - response &= _um980->setRTCMPortMessage("RTCM1046", "COM3", 1); - } - - return (response); -} - -//---------------------------------------- -// Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted -// even if there is no GPS fix. We use it to test serial output. -// Returns true if successfully started and false upon failure -//---------------------------------------- -bool GNSS_UM980::enableRTCMTest() -{ - // There is no data port on devices with the UM980 - return false; -} - //---------------------------------------- // Restore the GNSS to the factory settings //---------------------------------------- @@ -685,11 +403,15 @@ uint16_t GNSS_UM980::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToR //---------------------------------------- bool GNSS_UM980::fixedBaseStart() { - bool response = true; - if (online.gnss == false) return (false); + // If we are already in the appropriate base mode, no changes needed + if (gnssInBaseFixedMode()) + return (true); + + bool response = true; + if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) { _um980->setModeBaseECEF(settings.fixedEcefX, settings.fixedEcefY, settings.fixedEcefZ); @@ -708,6 +430,32 @@ bool GNSS_UM980::fixedBaseStart() return (response); } +//---------------------------------------- +// Check if given GNSS fix rate is allowed +// Rates are expressed in ms between fixes. +//---------------------------------------- +const float um980MinRateHz = 0.02; // 1 / 65 = 0.015384 Hz = Found experimentally +const float um980MaxRateHz = 20.0; // 20Hz + +bool GNSS_UM980::fixRateIsAllowed(uint32_t fixRateMs) +{ + if (fixRateMs > (1000.0 / um980MinRateHz) && fixRateMs < (1000.0 / um980MaxRateHz)) + return (true); + return (false); +} + +// Return minimum in milliseconds +uint32_t GNSS_UM980::fixRateGetMinimumMs() +{ + return (1000.0 / um980MinRateHz); +} + +// Return maximum in milliseconds +uint32_t GNSS_UM980::fixRateGetMaximumMs() +{ + return (1000.0 / um980MaxRateHz); +} + //---------------------------------------- // Return the number of active/enabled messages //---------------------------------------- @@ -939,6 +687,23 @@ uint8_t GNSS_UM980::getMinute() return 0; } +//---------------------------------------- +// Returns the current mode +// 0 - Unknown, 1 - Rover Survey, 2 - Rover UAV, 3 - Rover Auto, 4 - Base Survey-in, 5 - Base fixed +//---------------------------------------- +uint8_t GNSS_UM980::getMode() +{ + if (online.gnss) + { + int mode = _um980->getMode(); + if (settings.debugGnssConfig) + systemPrintf("getMode(): %d\r\n", mode); + + return (mode); + } + return (0); +} + //---------------------------------------- // Returns month number or zero if not online //---------------------------------------- @@ -1085,17 +850,40 @@ uint16_t GNSS_UM980::getYear() return 0; } +//---------------------------------------- +// Returns true if the device is in Base Fixed mode +//---------------------------------------- +bool GNSS_UM980::gnssInBaseFixedMode() +{ + // 0 - Unknown, 1 - Rover Survey, 2 - Rover UAV, 3 - Rover Auto, 4 - Base Survey-in, 5 - Base fixed + if (getMode() == 5) + return (true); + return (false); +} + +//---------------------------------------- +// Returns true if the device is in Base Survey-in mode +//---------------------------------------- +bool GNSS_UM980::gnssInBaseSurveyInMode() +{ + // 0 - Unknown, 1 - Rover Survey, 2 - Rover UAV, 3 - Rover Auto, 4 - Base Survey-in, 5 - Base fixed + if (getMode() == 4) + return (true); + + return (false); +} + //---------------------------------------- // Returns true if the device is in Rover mode -// Currently the only two modes are Rover or Base //---------------------------------------- -bool GNSS_UM980::inRoverMode() +bool GNSS_UM980::gnssInRoverMode() { - // Determine which state we are in - if (settings.lastState == STATE_BASE_NOT_STARTED) - return (false); + // 0 - Unknown, 1 - Rover Survey, 2 - Rover UAV, 3 - Rover Auto, 4 - Base Survey-in, 5 - Base fixed + int currentMode = getMode(); + if (currentMode >= 1 && currentMode <= 3) + return (true); - return (true); // Default to Rover + return (false); } // If we issue a library command that must wait for a response, we don't want @@ -1291,10 +1079,12 @@ void GNSS_UM980::menuConstellations() incoming--; // Align choice to constellation array of 0 to 5 settings.um980Constellations[incoming] ^= 1; + gnssConfigure(GNSS_CONFIG_CONSTELLATION); // Request receiver to use new settings } else if ((incoming == MAX_UM980_CONSTELLATIONS + 1) && present.galileoHasCapable) { settings.enableGalileoHas ^= 1; + gnssConfigure(GNSS_CONFIG_HAS_E6); // Request receiver to use new settings } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) break; @@ -1304,9 +1094,6 @@ void GNSS_UM980::menuConstellations() printUnknown(incoming); } - // Apply current settings to module - gnss->setConstellations(); - clearBuffer(); // Empty buffer of any newline chars } @@ -1361,6 +1148,12 @@ void GNSS_UM980::menuMessages() settings.um980MessageRatesRTCMBase[x] = umMessagesRTCM[x].msgDefaultRate; systemPrintln("Reset to Defaults"); + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings + if (inBaseMode()) // If the current system state is Base + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings } else if (incoming == 11 || incoming == 12) { @@ -1388,13 +1181,15 @@ void GNSS_UM980::menuMessages() // setRtcmRoverMessageRateByName("RTCM1124", reportRate); //BeiDou not used by CSRS-PPP if (incoming == 12) - { systemPrintln("Reset to High-rate PPP Logging (NMEAx5 / RTCMx4 - 1Hz)"); - } else - { systemPrintln("Reset to PPP Logging (NMEAx5 / RTCMx4 - 30 second decimation)"); - } + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings + if (inBaseMode()) // If the current system state is Base + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) @@ -1406,12 +1201,6 @@ void GNSS_UM980::menuMessages() } clearBuffer(); // Empty buffer of any newline chars - - // Apply these changes at menu exit - if (inRoverMode()) - restartRover = true; - else - restartBase = true; } //---------------------------------------- @@ -1505,11 +1294,20 @@ void GNSS_UM980::menuMessagesSubtype(float *localMessageRate, const char *messag newSetting = floor(newSetting); if (strcmp(messageType, "NMEA") == 0) + { settings.um980MessageRatesNMEA[incoming] = (float)newSetting; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings + } if (strcmp(messageType, "RTCMRover") == 0) + { settings.um980MessageRatesRTCMRover[incoming] = (float)newSetting; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings + } if (strcmp(messageType, "RTCMBase") == 0) + { settings.um980MessageRatesRTCMBase[incoming] = (float)newSetting; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings + } } } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) @@ -1520,10 +1318,6 @@ void GNSS_UM980::menuMessagesSubtype(float *localMessageRate, const char *messag printUnknown(incoming); } - settings.gnssConfiguredOnce = false; // Update the GNSS config at the next boot - settings.gnssConfiguredBase = false; - settings.gnssConfiguredRover = false; - clearBuffer(); // Empty buffer of any newline chars } @@ -1602,54 +1396,80 @@ bool GNSS_UM980::saveConfiguration() //---------------------------------------- bool GNSS_UM980::setBaudRate(uint8_t uartNumber, uint32_t baudRate) { - if (uartNumber != 3) + if (uartNumber < 1 || uartNumber > 3) { systemPrintln("setBaudRate error: out of range"); return (false); } - return setBaudRateCOM3(baudRate); + // The UART on the UM980 is passed as a string, ie "COM2" + char comName[5]; // COM3 + snprintf(comName, sizeof(comName), "COM%d", uartNumber); + + // Read, modify, write + uint32_t currentBaudRate = _um980->getPortBaudrate(comName); + if (currentBaudRate == baudRate) + return (true); // No change needed + + return _um980->setPortBaudrate(comName, baudRate); //("COM3", 115200) } +// UM980 COM1 - (DATA) Connected to the USB CH342 +// UM980 COM2 - Connected To IMU +// UM980 COM3 - (COMM) Connected to ESP32 for BT, configuration, and LoRa Radio. +// No RADIO connection. //---------------------------------------- // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS +// This just sets the GNSS side //---------------------------------------- -bool GNSS_UM980::setBaudRateCOM3(uint32_t baudRate) +bool GNSS_UM980::setBaudRateComm(uint32_t baudRate) { - if (online.gnss) - return _um980->setPortBaudrate("COM3", baudRate); - return false; + return (setBaudRate(3, baudRate)); +} + +bool GNSS_UM980::setBaudRateData(uint32_t baudRate) +{ + return (setBaudRate(1, baudRate)); // The DATA port on the Torch is the USB C connector +} + +bool GNSS_UM980::setBaudRateRadio(uint32_t baudRate) +{ + return true; // UM980 has no RADIO port } //---------------------------------------- // Enable all the valid constellations and bands for this platform -// Band support varies between platforms and firmware versions -// We open/close a complete set 19 messages //---------------------------------------- bool GNSS_UM980::setConstellations() { bool response = true; + // Read, modify, write + // The UM980 does not have a way to read the currently enabled constellations so we do only a write + for (int constellationNumber = 0; constellationNumber < MAX_UM980_CONSTELLATIONS; constellationNumber++) { - if (settings.um980Constellations[constellationNumber]) + if (settings.um980Constellations[constellationNumber] > 0) { - if (_um980->enableConstellation(um980ConstellationCommands[constellationNumber].textCommand) == false) + response &= _um980->enableConstellation(um980ConstellationCommands[constellationNumber].textCommand); + if (response == false) { - if (settings.debugGnss) - systemPrintf("Enable constellation failed at constellationNumber %d %s.", constellationNumber, - um980ConstellationCommands[constellationNumber].textName); - response &= false; // If any one of the commands fails, report failure overall + if (settings.debugGnssConfig) + systemPrintf("setConstellations failed to enable constellation %s [%d].\r\n", + um980ConstellationCommands[constellationNumber].textName, constellationNumber); + return (false); // Don't attempt other messages, assume communication is down } } else { - if (_um980->disableConstellation(um980ConstellationCommands[constellationNumber].textCommand) == false) + response &= _um980->disableConstellation(um980ConstellationCommands[constellationNumber].textCommand); + + if (response == false) { - if (settings.debugGnss) - systemPrintf("Disable constellation failed at constellationNumber %d %s.", constellationNumber, - um980ConstellationCommands[constellationNumber].textName); - response &= false; // If any one of the commands fails, report failure overall + if (settings.debugGnssConfig) + systemPrintf("setConstellations failed to disable constellation %s [%d].\r\n", + um980ConstellationCommands[constellationNumber].textName, constellationNumber); + return (false); // Don't attempt other messages, assume communication is down } } } @@ -1657,22 +1477,25 @@ bool GNSS_UM980::setConstellations() return (response); } -//---------------------------------------- -bool GNSS_UM980::setDataBaudRate(uint32_t baud) -{ - return false; // UM980 has no multiplexer -} - //---------------------------------------- // Set the elevation in degrees //---------------------------------------- bool GNSS_UM980::setElevation(uint8_t elevationDegrees) { if (online.gnss) + { + // Read, modify, write + float currentElevation = _um980->getElevationAngle(); + if (currentElevation == elevationDegrees) + return (true); // Nothing to change + return _um980->setElevationAngle(elevationDegrees); + } return false; } +//---------------------------------------- +// Control whether HAS E6 is used in location fixes or not //---------------------------------------- bool GNSS_UM980::setHighAccuracyService(bool enableGalileoHas) { @@ -1685,10 +1508,13 @@ bool GNSS_UM980::setHighAccuracyService(bool enableGalileoHas) int um980Version = String(_um980->getVersion()).toInt(); // Convert the string response to a value if (um980Version >= 11833) { + // Read, modify, write if (_um980->isConfigurationPresent("CONFIG PPP ENABLE E6-HAS") == false) { if (_um980->sendCommand("CONFIG PPP ENABLE E6-HAS")) + { systemPrintln("Galileo E6 HAS service enabled"); + } else { systemPrintln("Galileo E6 HAS service failed to enable"); @@ -1696,7 +1522,9 @@ bool GNSS_UM980::setHighAccuracyService(bool enableGalileoHas) } if (_um980->sendCommand("CONFIG PPP DATUM WGS84")) + { systemPrintln("WGS84 Datum applied"); + } else { systemPrintln("WGS84 Datum failed to apply"); @@ -1708,7 +1536,8 @@ bool GNSS_UM980::setHighAccuracyService(bool enableGalileoHas) { systemPrintf( "Current UM980 firmware: v%d. Galileo E6 reception requires v11833 or newer. Please update the " - "firmware on your UM980 to allow for HAS operation. Please see https://bit.ly/sfe-rtk-um980-update\r\n", + "firmware on your UM980 to allow for HAS operation. Please see " + "https://bit.ly/sfe-rtk-um980-update\r\n", um980Version); // Don't fail the result. Module is still configured, just without HAS. } @@ -1719,7 +1548,9 @@ bool GNSS_UM980::setHighAccuracyService(bool enableGalileoHas) if (_um980->isConfigurationPresent("CONFIG PPP ENABLE E6-HAS")) { if (_um980->sendCommand("CONFIG PPP DISABLE")) + { systemPrintln("Galileo E6 HAS service disabled"); + } else { systemPrintln("Galileo E6 HAS service failed to disable"); @@ -1731,39 +1562,229 @@ bool GNSS_UM980::setHighAccuracyService(bool enableGalileoHas) } //---------------------------------------- -// Enable all the valid messages for this platform -// There are many messages so split into batches. VALSET is limited to 64 max per batch -// Uses dummy newCfg and sendCfg values to be sure we open/close a complete set +// Configure device-direct logging. Currently mosaic-X5 specific. //---------------------------------------- -bool GNSS_UM980::setMessages(int maxRetries) +bool GNSS_UM980::setLogging() { - // We probably don't need this for the UM980 - // TODO return(um980SetMessages(maxRetries)); - return (true); + // Not supported on this platform + return (true); // Return true to clear gnssConfigure test } //---------------------------------------- -// Enable all the valid messages for this platform over the USB port -// Add 2 to every UART1 key. This is brittle and non-perfect, but works. +// Set the minimum satellite signal level (carrier to noise ratio) for navigation. //---------------------------------------- -bool GNSS_UM980::setMessagesUsb(int maxRetries) +bool GNSS_UM980::setMinCN0(uint8_t cn0Value) { - // We probably don't need this for the UM980 - // TODO return(um980SetMessagesUsb(maxRetries)); - return (true); + if (online.gnss) + { + // Read, modify, write + // The UM980 does not currently have a way to read the CN0, so we must write only + _um980->setMinCNO(cn0Value); + return true; + } + return false; } //---------------------------------------- -// Set the minimum satellite signal level for navigation. +// Turn on all the enabled NMEA messages on COM3 //---------------------------------------- -bool GNSS_UM980::setMinCnoRadio(uint8_t cnoValue) +bool GNSS_UM980::setMessagesNMEA() { - if (online.gnss) + bool response = true; + bool gpggaEnabled = false; + bool gpzdaEnabled = false; + + // The UM980 is unique in that there is a UNLOG command that turns off all + // reported NMEA/RTCM messages. Sending message rates of 0 works, until a + // message rate >0 is sent. Any following sending of message rates of 0 do not + // get a response. Our approach: UNLOG and set a global, and request + // RTCM be reconfigured. Send config requests only for >0 messages. + // At the end of RTCM reconfig, clear global. This approach + // presumes NMEA then RTCM will be configured in that order. Brittle but moving on. + + if (settings.debugGnssConfig == true) + systemPrintln("setMessagesNMEA disabling output"); + + disableAllOutput(); + um980MessagesEnabled_NMEA = false; + + if (um980MessagesEnabled_RTCM_Rover == true || um980MessagesEnabled_RTCM_Base == true) { - _um980->setMinCNO(cnoValue); - return true; + um980MessagesEnabled_RTCM_Rover = false; + um980MessagesEnabled_RTCM_Base = false; + + // Request reconfigure of RTCM + if (inBaseMode()) // If the current system state is Base + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); } - return false; + + for (int messageNumber = 0; messageNumber < MAX_UM980_NMEA_MSG; messageNumber++) + { + if (settings.um980MessageRatesNMEA[messageNumber] > 0) + { + // If any one of the commands fails, report failure overall + response &= _um980->setNMEAPortMessage(umMessagesNMEA[messageNumber].msgTextName, "COM3", + settings.um980MessageRatesNMEA[messageNumber]); + + if (response == false) + { + if (settings.debugGnssConfig) + systemPrintf("setMessagesNMEA failed to set %0.2f for message %s [%d].\r\n", + settings.um980MessageRatesNMEA[messageNumber], + umMessagesNMEA[messageNumber].msgTextName, messageNumber); + return (false); // Don't attempt other messages, assume communication is down + } + } + + // Mark certain required messages as enabled if rate > 0 + if (settings.um980MessageRatesNMEA[messageNumber] > 0) + { + if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "GPGGA") == 0) + gpggaEnabled = true; + else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "GPZDA") == 0) + gpzdaEnabled = true; + } + } + + // Enable GGA if needed for other services + if (gpggaEnabled == false) + { + // If we are using MQTT based corrections, we need to send local data to the PPL + // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 + // Enable GGA for NTRIP + if (pointPerfectServiceUsesKeys() || + (settings.enableNtripClient == true && settings.ntripClient_TransmitGGA == true)) + { + response &= _um980->setNMEAPortMessage("GPGGA", "COM3", 1); + } + } + + if (gpzdaEnabled == false) + { + if (pointPerfectServiceUsesKeys()) + { + response &= _um980->setNMEAPortMessage("GPZDA", "COM3", 1); + } + } + + if (response == true) + um980MessagesEnabled_NMEA = true; + + return (response); +} + +//---------------------------------------- +// Configure RTCM Base messages on COM3 (the connection between ESP32 and UM980) +//---------------------------------------- +bool GNSS_UM980::setMessagesRTCMBase() +{ + bool response = true; + + if (um980MessagesEnabled_NMEA == false) + { + // If this function was called by itself (without NMEA running previously) then + // force call NMEA enable here. It will disable all output, then should um980MessagesEnabled_NMEA = true. + setMessagesNMEA(); + } + + for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) + { + if (settings.um980MessageRatesRTCMBase[messageNumber] > 0) + { + + // If any one of the commands fails, report failure overall + response &= _um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", + settings.um980MessageRatesRTCMBase[messageNumber]); + + if (response == false) + { + if (settings.debugGnssConfig) + systemPrintf("setMessagesRTCMBase failed to set %0.2f for message %s [%d].\r\n", + settings.um980MessageRatesRTCMBase[messageNumber], + umMessagesRTCM[messageNumber].msgTextName, messageNumber); + return (false); // Don't attempt other messages, assume communication is down + } + } + } + + if (response == true) + um980MessagesEnabled_RTCM_Base = true; + + return (response); +} + +//---------------------------------------- +// Set the RTCM Rover messages on COM3 +//---------------------------------------- +bool GNSS_UM980::setMessagesRTCMRover() +{ + bool response = true; + bool rtcm1019Enabled = false; + bool rtcm1020Enabled = false; + bool rtcm1042Enabled = false; + bool rtcm1046Enabled = false; + + if (um980MessagesEnabled_NMEA == false) + { + // If this function was called by itself (without NMEA running previously) then + // force call NMEA enable here. It will disable all output, then should um980MessagesEnabled_NMEA = true. + setMessagesNMEA(); + } + + for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) + { + if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) + { + response &= _um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", + settings.um980MessageRatesRTCMRover[messageNumber]); + if (response == false) + { + if (settings.debugGnssConfig) + systemPrintf("setMessagesRTCMRover failed to set %0.2f for message %s [%d].\r\n", + settings.um980MessageRatesRTCMRover[messageNumber], + umMessagesRTCM[messageNumber].msgTextName, messageNumber); + return (false); // Don't attempt other messages, assume communication is down + } + } + + // If we are using IP based corrections, we need to send local data to the PPL + // The PPL requires being fed GPGGA/ZDA, and RTCM1019/1020/1042/1046 + if (pointPerfectServiceUsesKeys()) + { + // Mark PPL required messages as enabled if rate > 0 + if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) + { + if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1019") == 0) + rtcm1019Enabled = true; + else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1020") == 0) + rtcm1020Enabled = true; + else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1042") == 0) + rtcm1042Enabled = true; + else if (strcmp(umMessagesNMEA[messageNumber].msgTextName, "RTCM1046") == 0) + rtcm1046Enabled = true; + } + } + } + + if (pointPerfectServiceUsesKeys()) + { + // Force on any messages that are needed for PPL + if (rtcm1019Enabled == false) + response &= _um980->setRTCMPortMessage("RTCM1019", "COM3", 1); + if (rtcm1020Enabled == false) + response &= _um980->setRTCMPortMessage("RTCM1020", "COM3", 1); + if (rtcm1042Enabled == false) + response &= _um980->setRTCMPortMessage("RTCM1042", "COM3", 1); + if (rtcm1046Enabled == false) + response &= _um980->setRTCMPortMessage("RTCM1046", "COM3", 1); + } + + if (response == true) + um980MessagesEnabled_RTCM_Rover = true; + + return (response); } //---------------------------------------- @@ -1773,16 +1794,26 @@ bool GNSS_UM980::setModel(uint8_t modelNumber) { if (online.gnss) { + // Read, modify, write + // #MODE,97,GPS,FINE,2387,501442000,0,0,18,511;MODE ROVER SURVEY,*10 + // There is the ability to check the #MODE response, but for now, just write it + if (modelNumber == UM980_DYN_MODEL_SURVEY) return (_um980->setModeRoverSurvey()); else if (modelNumber == UM980_DYN_MODEL_UAV) return (_um980->setModeRoverUAV()); else if (modelNumber == UM980_DYN_MODEL_AUTOMOTIVE) return (_um980->setModeRoverAutomotive()); + else + { + systemPrintf("Uncaught model: %d\r\n", modelNumber); + } } return (false); } +//---------------------------------------- +// Configure multipath mitigation //---------------------------------------- bool GNSS_UM980::setMultipathMitigation(bool enableMultipathMitigation) { @@ -1794,7 +1825,9 @@ bool GNSS_UM980::setMultipathMitigation(bool enableMultipathMitigation) if (_um980->isConfigurationPresent("CONFIG MMP ENABLE") == false) { if (_um980->sendCommand("CONFIG MMP ENABLE")) + { systemPrintln("Multipath Mitigation enabled"); + } else { systemPrintln("Multipath Mitigation failed to enable"); @@ -1808,7 +1841,9 @@ bool GNSS_UM980::setMultipathMitigation(bool enableMultipathMitigation) if (_um980->isConfigurationPresent("CONFIG MMP ENABLE")) { if (_um980->sendCommand("CONFIG MMP DISABLE")) + { systemPrintln("Multipath Mitigation disabled"); + } else { systemPrintln("Multipath Mitigation failed to disable"); @@ -1820,13 +1855,7 @@ bool GNSS_UM980::setMultipathMitigation(bool enableMultipathMitigation) } //---------------------------------------- -bool GNSS_UM980::setRadioBaudRate(uint32_t baud) -{ - return false; // UM980 has no multiplexer -} - -//---------------------------------------- -// Given the number of seconds between desired solution reports, determine measurementRateMs and navigationRate +// Given the number of seconds between desired solution reports, determine measurementRateMs //---------------------------------------- bool GNSS_UM980::setRate(double secondsBetweenSolutions) { @@ -1838,62 +1867,84 @@ bool GNSS_UM980::setRate(double secondsBetweenSolutions) // ie, if a message != 0, then it will be output at the measurementRate. // All RTCM for a base will be based on a measurementRateMs of 1000 with messages // that can be reported more slowly than that (ie 1 per 10 seconds). - bool response = true; - disableAllOutput(); + // Read/Modify/Write + // Determine if we need to modify the setting at all + bool changeRequired = false; - // Overwrite any enabled messages with this rate + // Determine if the given setting different from our current settings for (int messageNumber = 0; messageNumber < MAX_UM980_NMEA_MSG; messageNumber++) { if (settings.um980MessageRatesNMEA[messageNumber] > 0) - { - settings.um980MessageRatesNMEA[messageNumber] = secondsBetweenSolutions; - } + if (settings.um980MessageRatesNMEA[messageNumber] != secondsBetweenSolutions) + changeRequired = true; } - response &= enableNMEA(); // Enact these rates - - // TODO We don't know what state we are in, so we don't - // know which RTCM settings to update. Assume we are - // in rover for now for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) { if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) - { - settings.um980MessageRatesRTCMRover[messageNumber] = secondsBetweenSolutions; - } + if (settings.um980MessageRatesRTCMRover[messageNumber] != secondsBetweenSolutions) + changeRequired = true; } - response &= enableRTCMRover(); // Enact these rates - // If we successfully set rates, only then record to settings - if (response) - { - uint16_t msBetweenSolutions = secondsBetweenSolutions * 1000; - settings.measurementRateMs = msBetweenSolutions; - } - else + if (changeRequired == false) { - systemPrintln("Failed to set measurement and navigation rates"); - return (false); + if (settings.debugGnssConfig) + systemPrintln("setRate: No change required"); + return (true); // Success } + if (settings.debugGnssConfig) + systemPrintln("setRate: Modifying rates"); + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); + return (true); } //---------------------------------------- -bool GNSS_UM980::setTalkerGNGGA() +// Enable/disable any output needed for tilt compensation +//---------------------------------------- +bool GNSS_UM980::setTilt() { - // TODO um980SetTalkerGNGGA(); - return false; + if (present.imu_im19 == false) + return (true); // Report success + + bool response = true; + + // Read, modify, write + // The UM980 does not have a way to read the currently enabled messages so we do only a write + if (settings.enableTiltCompensation == true) + { + // Configure UM980 to output binary and NMEA reports out COM2, connected to IM19 COM3 + response &= _um980->sendCommand("BESTPOSB COM2 0.2"); // 5Hz + response &= _um980->sendCommand("PSRVELB COM2 0.2"); + response &= _um980->setNMEAPortMessage("GPGGA", "COM2", 0.2); // 5Hz + response &= setBaudRate(2, 115200); // UM980 UART2 is connected to the IMU + } + else + { + // We could turn off these messages but because they are only fed into the IMU, it doesn't cause any harm. + } + + return (response); } //---------------------------------------- -// Hotstart GNSS -// Needed on ZED based products where RTK Float lock is seen using L-Band -// Not used on UM980 based devices +// Reset the GNSS receiver either through hardware or software //---------------------------------------- -bool GNSS_UM980::softwareReset() +bool GNSS_UM980::reset() { - return false; + // Hardware reset the Torch in case UM980 is unresponsive + if (productVariant == RTK_TORCH) + digitalWrite(pin_GNSS_DR_Reset, LOW); // Tell UM980 and DR to reset + + delay(500); + + if (productVariant == RTK_TORCH) + digitalWrite(pin_GNSS_DR_Reset, HIGH); // Tell UM980 and DR to boot + + return true; } //---------------------------------------- @@ -1920,14 +1971,19 @@ bool GNSS_UM980::surveyInStart() { if (online.gnss) { + // If we are already in the appropriate base mode, no changes needed + if (gnssInBaseSurveyInMode()) + return (true); + bool response = true; // Start a Self-optimizing Base Station // We do not use the distance parameter (settings.observationPositionAccuracy) because that // setting on the UM980 is related to automatically restarting base mode // at power on (very different from ZED-F9P). - response &= - _um980->setModeBaseAverage(settings.observationSeconds); // Average for a number of seconds (default is 60) + + // Average for a number of seconds (default is 60) + response &= _um980->setModeBaseAverage(settings.observationSeconds); if (response == false) { @@ -1945,25 +2001,25 @@ bool GNSS_UM980::surveyInStart() //---------------------------------------- // Check if given baud rate is allowed //---------------------------------------- -const uint32_t um980AllowedRates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600}; -const int um980AllowedRatesCount = sizeof(um980AllowedRates) / sizeof(um980AllowedRates[0]); +const uint32_t um980AllowedBaudRates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600}; +const int um980AllowedBaudRatesCount = sizeof(um980AllowedBaudRates) / sizeof(um980AllowedBaudRates[0]); bool GNSS_UM980::baudIsAllowed(uint32_t baudRate) { - for (int x = 0; x < um980AllowedRatesCount; x++) - if (um980AllowedRates[x] == baudRate) + for (int x = 0; x < um980AllowedBaudRatesCount; x++) + if (um980AllowedBaudRates[x] == baudRate) return (true); return (false); } uint32_t GNSS_UM980::baudGetMinimum() { - return (um980AllowedRates[0]); + return (um980AllowedBaudRates[0]); } uint32_t GNSS_UM980::baudGetMaximum() { - return (um980AllowedRates[um980AllowedRatesCount - 1]); + return (um980AllowedBaudRates[um980AllowedBaudRatesCount - 1]); } //---------------------------------------- @@ -1993,13 +2049,6 @@ void GNSS_UM980::update() // We don't check serial data here; the gnssReadTask takes care of serial consumption } -// Set all NMEA message report rates to one value -void GNSS_UM980::setNmeaMessageRates(uint8_t msgRate) -{ - for (int x = 0; x < MAX_UM980_NMEA_MSG; x++) - settings.um980MessageRatesNMEA[x] = msgRate; -} - // Set all RTCM Rover message report rates to one value void GNSS_UM980::setRtcmRoverMessageRates(uint8_t msgRate) { @@ -2041,15 +2090,6 @@ bool GNSS_UM980::setRtcmRoverMessageRateByName(const char *msgName, uint8_t msgR //---------------------------------------- -//---------------------------------------- -// Force UART connection to GNSS for firmware update on the next boot by special file in -// LittleFS -//---------------------------------------- -bool createUm980Passthrough() -{ - return createPassthrough("/updateUm980Firmware.txt"); -} - //---------------------------------------- void um980FirmwareBeginUpdate() { @@ -2062,7 +2102,7 @@ void um980FirmwareBeginUpdate() // Note: UM980 needs its own dedicated update function, due to the T@ and bootloader trigger - // Note: UM980 is cuurrently only available on Torch. + // Note: UM980 is currently only available on Torch. // But um980FirmwareBeginUpdate has been reworked so it will work on Facet too. // Note: um980FirmwareBeginUpdate is called during setup, after identify board. I2C, gpio expanders, buttons @@ -2070,10 +2110,10 @@ void um980FirmwareBeginUpdate() // This makes our job much easier... // Flag that we are in direct connect mode. Button task will um980FirmwareRemoveUpdate and exit - inDirectConnectMode = true; + // inDirectConnectMode = true; // Paint GNSS Update - paintGnssUpdate(); + // paintGnssUpdate(); // Stop all UART tasks. Redundant tasksStopGnssUart(); @@ -2107,8 +2147,8 @@ void um980FirmwareBeginUpdate() while (!task.endDirectConnectMode) { // Data coming from UM980 to external USB - if (serialGNSS->available()) // Note: use if, not while - Serial.write(serialGNSS->read()); + // if (serialGNSS->available()) // Note: use if, not while + // Serial.write(serialGNSS->read()); // Data coming from external USB to UM980 if (Serial.available()) // Note: use if, not while @@ -2124,14 +2164,36 @@ void um980FirmwareBeginUpdate() { // Reset UM980 gnssReset(); - delay(25); + delay(500); gnssBoot(); - + delay(500); inBootMode = true; } } } + // if (digitalRead(pin_powerButton) == HIGH) + // { + // while (digitalRead(pin_powerButton) == HIGH) + // delay(100); + + // // Remove file and reset to exit pass-through mode + // um980FirmwareRemoveUpdate(); + + // // Beep to indicate exit + // beepOn(); + // delay(300); + // beepOff(); + // delay(100); + // beepOn(); + // delay(300); + // beepOff(); + + // systemPrintln("Exiting UM980 passthrough mode"); + // systemFlush(); // Complete prints + + // ESP.restart(); + // } // Button task will set task.endDirectConnectMode true } @@ -2143,12 +2205,22 @@ void um980FirmwareBeginUpdate() ESP.restart(); } +const char *um980FirmwareFileName = "/updateUm980Firmware.txt"; + +//---------------------------------------- +// Force UART connection to GNSS for firmware update on the next boot by special file in LittleFS +//---------------------------------------- +bool um980CreatePassthrough() +{ + return createPassthrough(um980FirmwareFileName); +} + //---------------------------------------- // Check if direct connection file exists //---------------------------------------- bool um980FirmwareCheckUpdate() { - return gnssFirmwareCheckUpdateFile("/updateUm980Firmware.txt"); + return gnssFirmwareCheckUpdateFile(um980FirmwareFileName); } //---------------------------------------- @@ -2156,7 +2228,7 @@ bool um980FirmwareCheckUpdate() //---------------------------------------- void um980FirmwareRemoveUpdate() { - gnssFirmwareRemoveUpdateFile("/updateUm980Firmware.txt"); + gnssFirmwareRemoveUpdateFile(um980FirmwareFileName); } //---------------------------------------- diff --git a/Firmware/RTK_Everywhere/GNSS_ZED.h b/Firmware/RTK_Everywhere/GNSS_ZED.h index 5da4d942f..5a236d5ef 100644 --- a/Firmware/RTK_Everywhere/GNSS_ZED.h +++ b/Firmware/RTK_Everywhere/GNSS_ZED.h @@ -408,12 +408,6 @@ class GNSS_ZED : GNSS // Returns true when an external event occurs and false if no event bool beginExternalEvent(); - // Setup the timepulse output on the PPS pin for external triggering - // Outputs - // Returns true if the pin was successfully setup and false upon - // failure - bool beginPPS(); - bool checkNMEARates(); bool checkPPPRates(); @@ -430,7 +424,7 @@ class GNSS_ZED : GNSS // Not Rover or Base specific (ie, baud rates) // Outputs: // Returns true if successfully configured and false upon failure - bool configureGNSS(); + bool configure(); // Configure the Rover // Outputs: @@ -453,14 +447,6 @@ class GNSS_ZED : GNSS void debuggingEnable(); - void enableGgaForNtrip(); - - // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted - // even if there is no GPS fix. We use it to test serial output. - // Outputs: - // Returns true if successfully started and false upon failure - bool enableRTCMTest(); - // Restore the GNSS to the factory settings void factoryReset(); @@ -473,6 +459,13 @@ class GNSS_ZED : GNSS // Returns true if successfully started and false upon failure bool fixedBaseStart(); + bool fixRateIsAllowed(uint32_t fixRateMs); + + // Return min/max rate in ms + uint32_t fixRateGetMinimumMs(); + + uint32_t fixRateGetMaximumMs(); + // Return the number of active/enabled messages uint8_t getActiveMessageCount(); @@ -538,6 +531,10 @@ class GNSS_ZED : GNSS // Returns minutes or zero if not online uint8_t getMinute(); + // Returns the current mode: Base/Rover/etc + // 0 - Rover, 1 - Base Survey-In, 2 - Base Fixed + uint8_t getMode(); + // Returns month number or zero if not online uint8_t getMonth(); @@ -577,6 +574,11 @@ class GNSS_ZED : GNSS // Returns full year, ie 2023, not 23. uint16_t getYear(); + // Helper functions for the current mode as read from the GNSS receiver + bool gnssInBaseFixedMode(); + bool gnssInBaseSurveyInMode(); + bool gnssInRoverMode(); + // Antenna Short / Open detection bool isAntennaShorted(); bool isAntennaOpen(); @@ -662,6 +664,12 @@ class GNSS_ZED : GNSS // Returns the number of correction data bytes written int pushRawData(uint8_t *dataToSend, int dataLength); + // Set callback functions (PVT, etc) for u-blox library + bool registerCallbacks(); + + // Hardware or software reset the GNSS + bool reset(); + uint16_t rtcmBufferAvailable(); // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-NOW) to GNSS receiver @@ -680,6 +688,12 @@ class GNSS_ZED : GNSS // Set the baud rate on the designated port bool setBaudRate(uint8_t uartNumber, uint32_t baudRate); // From the super class + bool setBaudRateComm(uint32_t baudRate); + + bool setBaudRateData(uint32_t baudRate); + + bool setBaudRateRadio(uint32_t baudRate); + // Enable all the valid constellations and bands for this platform bool setConstellations(); @@ -687,34 +701,50 @@ class GNSS_ZED : GNSS // Always update if force is true. Otherwise, only update if enable has changed state bool setCorrRadioExtPort(bool enable, bool force); - bool setDataBaudRate(uint32_t baud); - // Set the elevation in degrees // Inputs: // elevationDegrees: The elevation value in degrees bool setElevation(uint8_t elevationDegrees); + // Enable or disable HAS E6 capability + bool setHighAccuracyService(bool enableGalileoHas); + + // Configure any logging settings - currently mosaic-X5 specific + bool setLogging(); + // Given a unique string, find first and last records containing that string in message array void setMessageOffsets(const ubxMsg *localMessage, const char *messageType, int &startOfBlock, int &endOfBlock); // Given the name of a message, find it, and set the rate bool setMessageRateByName(const char *msgName, uint8_t msgRate); - // Enable all the valid messages for this platform - bool setMessages(int maxRetries); + // Set the minimum satellite signal level for navigation. + bool setMinCN0(uint8_t cnoValue); + + // Set the NMEA messages + bool setMessagesNMEA(); - // Enable all the valid messages for this platform over the USB port - bool setMessagesUsb(int maxRetries); + // Set then RTCM Base messages + bool setMessagesRTCMBase(); - // Set the minimum satellite signal level for navigation. - bool setMinCnoRadio(uint8_t cnoValue); + // Set the RTCM Rover messages + bool setMessagesRTCMRover(); // Set the dynamic model to use for RTK // Inputs: // modelNumber: Number of the model to use, provided by radio library bool setModel(uint8_t modelNumber); - bool setRadioBaudRate(uint32_t baud); + bool setMultipathMitigation(bool enableMultipathMitigation); + + // Given the name of a message, find it, and set the rate + bool setNmeaMessageRateByName(const char *msgName, uint8_t msgRate); + + // Setup the timepulse output on the PPS pin for external triggering + // Outputs + // Returns true if the pin was successfully setup and false upon + // failure + bool setPPS(); // Specify the interval between solutions // Inputs: @@ -724,10 +754,8 @@ class GNSS_ZED : GNSS // failure bool setRate(double secondsBetweenSolutions); - bool setTalkerGNGGA(); - - // Hotstart GNSS to try to get RTK lock - bool softwareReset(); + // Enable/disable any output needed for tilt compensation + bool setTilt(); bool standby(); diff --git a/Firmware/RTK_Everywhere/GNSS_ZED.ino b/Firmware/RTK_Everywhere/GNSS_ZED.ino index 089d6b377..ca20e2aa5 100644 --- a/Firmware/RTK_Everywhere/GNSS_ZED.ino +++ b/Firmware/RTK_Everywhere/GNSS_ZED.ino @@ -58,8 +58,8 @@ void GNSS_ZED::applyPointPerfectKeys() _zed->setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1, VAL_LAYER_ALL); // Enable UBX-RXM-COR messages on I2C - _zed->setVal8(UBLOX_CFG_NAVHPG_DGNSSMODE, - 3, VAL_LAYER_ALL); // Set the differential mode - ambiguities are fixed whenever possible + _zed->setVal8(UBLOX_CFG_NAVHPG_DGNSSMODE, 3, + VAL_LAYER_ALL); // Set the differential mode - ambiguities are fixed whenever possible bool response = _zed->setDynamicSPARTNKeys(currentKeyLengthBytes, currentKeyGPSWeek, currentKeyGPSToW, settings.pointPerfectCurrentKey, nextKeyLengthBytes, nextKeyGPSWeek, @@ -137,18 +137,22 @@ void GNSS_ZED::begin() if (_zed->begin(*i2c_0) == false) { - systemPrintln("GNSS ZED failed to begin. Trying again."); + systemPrintln("GNSS ZED-F9P failed to begin. Trying again."); // Try again with power on delay delay(1000); // Wait for ZED-F9P to power up before it can respond to ACK if (_zed->begin(*i2c_0) == false) { - systemPrintln("GNSS ZED offline"); + systemPrintln("GNSS ZED-F9P offline"); displayGNSSFail(1000); return; } } + // Turn on debug messages if needed + if (settings.debugGnss) + debuggingEnable(); + // Increase transactions to reduce transfer time _zed->i2cTransactionSize = 128; @@ -170,10 +174,11 @@ void GNSS_ZED::begin() //"1.30" - ZED-F9P (HPG) released Dec, 2021. Also ZED-F9R (HPS) released Sept, 2022 //"1.32" - ZED-F9P released May, 2022 //"1.50" - ZED-F9P released July, 2024 + //"1.51" - ZED-F9P released November, 2024 - const uint8_t knownFirmwareVersions[] = {100, 112, 113, 120, 121, 130, 132, 150}; + const uint8_t knownFirmwareVersions[] = {100, 112, 113, 120, 121, 130, 132, 150, 151}; bool knownFirmware = false; - for (uint8_t i = 0; i < (sizeof(knownFirmwareVersions) / sizeof(uint8_t)); i++) + for (uint8_t i = 0; i < (sizeof(knownFirmwareVersions) / sizeof(knownFirmwareVersions[0])); i++) { if (gnssFirmwareVersionInt == knownFirmwareVersions[i]) knownFirmware = true; @@ -214,13 +219,18 @@ void GNSS_ZED::begin() { snprintf(gnssUniqueId, sizeof(gnssUniqueId), "%s", _zed->getUniqueChipIdStr(&chipID)); - systemPrintln("GNSS ZED online"); + // Callbacks are volatile and must be set after each reset + registerCallbacks(); + + systemPrintln("GNSS ZED-F9P online"); + online.gnss = true; + return; } } - systemPrintln("GNSS ZED offline"); + systemPrintln("GNSS ZED-F9P offline"); displayGNSSFail(1000); } @@ -243,7 +253,8 @@ bool GNSS_ZED::beginExternalEvent() if (settings.enableExternalHardwareEventLogging && (settings.dataPortChannel == MUX_PPS_EVENTTRIGGER)) { response &= _zed->setAutoTIMTM2callbackPtr( - &eventTriggerReceived, VAL_LAYER_ALL); // Enable automatic TIM TM2 messages with callback to eventTriggerReceived + &eventTriggerReceived, + VAL_LAYER_ALL); // Enable automatic TIM TM2 messages with callback to eventTriggerReceived } else { @@ -262,7 +273,7 @@ bool GNSS_ZED::beginExternalEvent() // This will be called once by setup and possibly multiple times by // menuPortsMultiplexed. //---------------------------------------- -bool GNSS_ZED::beginPPS() +bool GNSS_ZED::setPPS() { if (online.gnss == false) return (false); @@ -277,7 +288,7 @@ bool GNSS_ZED::beginPPS() response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_LENGTH_DEF, 1); // Define timepulse by length (not ratio) response &= _zed->addCfgValset(UBLOX_CFG_TP_USE_LOCKED_TP1, - 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid + 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid response &= _zed->addCfgValset(UBLOX_CFG_TP_TP1_ENA, settings.enableExternalPulse); // Enable/disable timepulse response &= _zed->addCfgValset(UBLOX_CFG_TP_POL_TP1, settings.externalPulsePolarity); // 0 = falling, 1 = rising edge @@ -287,15 +298,16 @@ bool GNSS_ZED::beginPPS() response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_TP1, 0); // Set the pulse length in us // When the module is _locked_ to GNSS time, make it generate 1Hz (Default is 100ms high, 900ms low) - response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, - settings.externalPulseTimeBetweenPulse_us); // Set the period between pulses is us response &= - _zed->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, settings.externalPulseLength_us); // Set the pulse length in us + _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, + settings.externalPulseTimeBetweenPulse_us); // Set the period between pulses is us + response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, + settings.externalPulseLength_us); // Set the pulse length in us response &= _zed->sendCfgValset(); } if (response == false) - systemPrintln("beginPPS failed"); + systemPrintln("setPPS failed"); return (response); } @@ -320,291 +332,38 @@ bool GNSS_ZED::checkPPPRates() return false; } -//---------------------------------------- -// Configure specific aspects of the receiver for base mode -//---------------------------------------- -bool GNSS_ZED::configureBase() -{ - if (online.gnss == false) - return (false); - - if (settings.gnssConfiguredBase) - { - if (settings.debugGnss) - systemPrintln("Skipping ZED Base configuration"); - return true; - } - - update(); // Regularly poll to get latest data - - _zed->setNMEAGPGGAcallbackPtr( - nullptr); // Disable GPGGA call back that may have been set during Rover NTRIP Client mode - - bool success = false; - int tryNo = -1; - - // Try up to MAX_SET_MESSAGES_RETRIES times to configure the GNSS - // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI - // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being - // processed. - while ((++tryNo < MAX_SET_MESSAGES_RETRIES) && !success) - { - bool response = true; - - // In Base mode we force 1Hz - response &= _zed->newCfgValset(VAL_LAYER_ALL); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - - // Since we are at 1Hz, allow GSV NMEA to be reported at whatever the user has chosen - response &= _zed->addCfgValset(ubxMessages[8].msgConfigKey, - settings.ubxMessageRates[8]); // Update rate on module - - response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, - 0); // Disable NMEA message that may have been set during Rover NTRIP Client mode - - // Survey mode is only available on ZED-F9P modules - if (commandSupported(UBLOX_CFG_TMODE_MODE)) - response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - - // Note that using UBX-CFG-TMODE3 to set the receiver mode to Survey In or to Fixed Mode, will set - // automatically the dynamic platform model (CFG-NAVSPG-DYNMODEL) to Stationary. - // response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)settings.dynamicModel); //Not needed - - // For most RTK products, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages - // are configured over I2C. Any messages that need to be logged are output on UART1, and received by this code - // using serialGNSS-> In base mode the RTK device should output RTCM over all ports: (Primary) UART2 in case the - // RTK device is connected via radio to rover (Optional) I2C in case user wants base to connect to WiFi and - // NTRIP Caster (Seconday) USB in case the RTK device is used as an NTRIP caster connected to SBC or other - // (Tertiary) UART1 in case RTK device is sending RTCM to a phone that is then NTRIP Caster - - // Find first RTCM record in ubxMessage array - int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); - - // ubxMessageRatesBase is an array of ~12 uint8_ts - // ubxMessage is an array of ~80 messages - // We use firstRTCMRecord as an offset for the keys, but use x as the rate - - for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) - { - response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 - 1 = I2C - response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 - - // Disable messages on SPI - response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 3, - 0); // UBLOX_CFG UART1 + 3 = SPI - } - - // Update message rates for UART2 and USB - for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) - { - response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 1 = UART2 - response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 2 = USB - } - - // Set minimum elevation - // Note: ZED supports negative elevations, but our firmware only allows 0-90 - response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); - - response &= _zed->sendCfgValset(); // Closing value - - if (response) - success = true; - } - - if (!success) - systemPrintln("Base config fail"); - - // The configuration should be saved to RAM+BBR+FLASH. No need to saveConfiguration here. - - settings.gnssConfiguredBase = success; - - return (success); -} - -//---------------------------------------- -// Configure specific aspects of the receiver for NTP mode -//---------------------------------------- -bool GNSS_ZED::configureNtpMode() -{ - bool success = false; - - if (online.gnss == false) - return (false); - - // This is only called by STATE_NTPSERVER_NOT_STARTED - // I guess it is OK to always do the configuration? - // stateUpdate clear the Base and Rover configuration flags - // to ensure the configuration is re-applied when we exit this mode - - update(); // Regularly poll to get latest data - - // Disable GPGGA call back that may have been set during Rover NTRIP Client mode - _zed->setNMEAGPGGAcallbackPtr(nullptr); - - int tryNo = -1; - - // Try up to MAX_SET_MESSAGES_RETRIES times to configure the GNSS - // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI - // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being - // processed. - while ((++tryNo < MAX_SET_MESSAGES_RETRIES) && !success) - { - bool response = true; - - // In NTP mode we force 1Hz - response &= _zed->newCfgValset(VAL_LAYER_ALL); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - - // Survey mode is only available on ZED-F9P modules - response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - - // Set dynamic model to stationary - response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, DYN_MODEL_STATIONARY); // Set dynamic model - - // Set time pulse to 1Hz (100:900) - response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_DEF, 0); // Time pulse definition is a period (in us) - response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_LENGTH_DEF, 1); // Define timepulse by length (not ratio) - response &= - _zed->addCfgValset(UBLOX_CFG_TP_USE_LOCKED_TP1, - 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid - response &= _zed->addCfgValset(UBLOX_CFG_TP_TP1_ENA, 1); // Enable timepulse - response &= _zed->addCfgValset(UBLOX_CFG_TP_POL_TP1, 1); // 1 = rising edge - - // While the module is _locking_ to GNSS time, turn off pulse - response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_TP1, 1000000); // Set the period between pulses in us - response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_TP1, 0); // Set the pulse length in us - - // When the module is _locked_ to GNSS time, make it generate 1Hz (100ms high, 900ms low) - response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, 1000000); // Set the period between pulses is us - response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, 100000); // Set the pulse length in us - - // Ensure pulse is aligned to top-of-second. This is the default. Set it here just to make sure. - response &= _zed->addCfgValset(UBLOX_CFG_TP_ALIGN_TO_TOW_TP1, 1); - - // Set the time grid to UTC. This is the default. Set it here just to make sure. - response &= _zed->addCfgValset(UBLOX_CFG_TP_TIMEGRID_TP1, 0); // 0=UTC; 1=GPS - - // Sync to GNSS. This is the default. Set it here just to make sure. - response &= _zed->addCfgValset(UBLOX_CFG_TP_SYNC_GNSS_TP1, 1); - - response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation - - // Ensure PVT, HPPOSLLH and TP messages are being output at 1Hz on the correct port - response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_PVT_I2C, 1); - response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_HPPOSLLH_I2C, 1); - response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_TIM_TP_I2C, 1); - - response &= _zed->sendCfgValset(); // Closing value - - if (response) - success = true; - } - - if (!success) - systemPrintln("NTP config fail"); - - // The configuration should be saved to RAM+BBR+FLASH. No need to saveConfiguration here. - - return (success); -} - //---------------------------------------- // Setup the u-blox module for any setup (base or rover) -// This is the equivalent of configureOnce on the other platforms +// This gets run once after a factory reset, and is called until it completes successfully. // In general we check if the setting is incorrect before writing it. Otherwise, the set commands have, on rare // occasion, become corrupt. The worst is when the I2C port gets turned off or the I2C address gets borked. //---------------------------------------- -bool GNSS_ZED::configureGNSS() +bool GNSS_ZED::configure() { if (online.gnss == false) return (false); bool response = true; - bool success = true; - - // Turn on/off debug messages - if (settings.debugGnss) - _zed->enableDebugging(Serial, true); // Enable only the critical debug messages over Serial - else - _zed->disableDebugging(); - - // Check if the ubxMessageRates or ubxMessageRatesBase need to be defaulted - // Redundant - also done by gnssConfigure - // checkGNSSArrayDefaults(); - - // Configure the callbacks - response &= - _zed->setAutoPVTcallbackPtr(&storePVTdata, VAL_LAYER_ALL); // Enable automatic NAV PVT messages with callback to storePVTdata - response &= _zed->setAutoHPPOSLLHcallbackPtr( - &storeHPdata, VAL_LAYER_ALL); // Enable automatic NAV HPPOSLLH messages with callback to storeHPdata - - if (present.timePulseInterrupt) - response &= _zed->setAutoTIMTPcallbackPtr( - &storeTIMTPdata, VAL_LAYER_ALL); // Enable automatic TIM TP messages with callback to storeTIMTPdata - - if (present.antennaShortOpen) - { - response &= _zed->newCfgValset(VAL_LAYER_ALL); - - response &= _zed->addCfgValset(UBLOX_CFG_HW_ANT_CFG_SHORTDET, 1); // Enable antenna short detection - response &= _zed->addCfgValset(UBLOX_CFG_HW_ANT_CFG_OPENDET, 1); // Enable antenna open detection - - response &= _zed->sendCfgValset(); - response &= _zed->setAutoMONHWcallbackPtr( - &storeMONHWdata, VAL_LAYER_ALL); // Enable automatic MON HW messages with callback to storeMONHWdata - } - - // Add a callback for UBX-MON-COMMS - response &= _zed->setAutoMONCOMMScallbackPtr(&storeMONCOMMSdata, VAL_LAYER_ALL); + // // Wait for initial report from module + // int maxWait = 2000; + // startTime = millis(); + // while (_pvtUpdated == false) + // { + // update(); // Regularly poll to get latest data + + // delay(10); + // if ((millis() - startTime) > maxWait) + // { + // systemPrintln("PVT Update failed"); + // break; + // } + // } - // Enable RTCM3 if needed - if not enable NMEA IN to keep skipped updated - response &= setCorrRadioExtPort(settings.enableExtCorrRadio, true); // Force the setting - - if (!response) - { - systemPrintln("GNSS initial configuration (callbacks, short detection, radio port) failed"); - } - success &= response; - response = true; // Reset - - // Configuring the ZED can take more than 2000ms. Configuration is saved to - // ZED RAM+BBR so there is no need to update settings unless user has modified - // the settings file or internal settings. - if (settings.gnssConfiguredOnce) - { - systemPrintln("ZED-F9x configuration maintained"); - return (true); - } - - // Wait for initial report from module - int maxWait = 2000; - startTime = millis(); - while (_pvtUpdated == false) - { - update(); // Regularly poll to get latest data - - delay(10); - if ((millis() - startTime) > maxWait) - { - log_d("PVT Update failed"); - break; - } - } - - // The first thing we do is go to 1Hz to lighten any I2C traffic from a previous configuration response &= _zed->newCfgValset(VAL_LAYER_ALL); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - if (commandSupported(UBLOX_CFG_TMODE_MODE)) - response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode + response &= _zed->addCfgValset(UBLOX_CFG_HW_ANT_CFG_SHORTDET, 1); // Enable antenna short detection + response &= _zed->addCfgValset(UBLOX_CFG_HW_ANT_CFG_OPENDET, 1); // Enable antenna open detection // UART1 will primarily be used to pass NMEA and UBX from ZED to ESP32 (eventually to cell phone) // but the phone can also provide RTCM data and a user may want to configure the ZED over Bluetooth. @@ -619,12 +378,6 @@ bool GNSS_ZED::configureGNSS() if (commandSupported(UBLOX_CFG_UART1INPROT_SPARTN)) response &= _zed->addCfgValset(UBLOX_CFG_UART1INPROT_SPARTN, 0); - response &= _zed->addCfgValset(UBLOX_CFG_UART1_BAUDRATE, - settings.dataPortBaud); // Defaults to 230400 to maximize message output support - response &= - _zed->addCfgValset(UBLOX_CFG_UART2_BAUDRATE, - settings.radioPortBaud); // Defaults to 57600 to match SiK telemetry radio firmware default - // Disable SPI port - This is just to remove some overhead by ZED response &= _zed->addCfgValset(UBLOX_CFG_SPIOUTPROT_UBX, 0); response &= _zed->addCfgValset(UBLOX_CFG_SPIOUTPROT_NMEA, 0); @@ -677,13 +430,6 @@ bool GNSS_ZED::configureGNSS() if (commandSupported(UBLOX_CFG_USBINPROT_SPARTN)) response &= _zed->addCfgValset(UBLOX_CFG_USBINPROT_SPARTN, 0); - if (commandSupported(UBLOX_CFG_NAVSPG_INFIL_MINCNO)) - { - response &= - _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINCNO, - settings.minCNO); // Set minimum satellite signal level for navigation - default 6 - } - if (commandSupported(UBLOX_CFG_NAV2_OUT_ENABLED)) { // Count NAV2 messages and enable NAV2 as needed. @@ -697,26 +443,13 @@ bool GNSS_ZED::configureGNSS() response &= _zed->addCfgValset(UBLOX_CFG_NAV2_OUT_ENABLED, 0); // Disable NAV2 messages } + response &= _zed->addCfgValset(UBLOX_CFG_NMEA_HIGHPREC, 1); // Enable high precision NMEA + response &= _zed->addCfgValset(UBLOX_CFG_NMEA_SVNUMBERING, 1); // Enable extended satellite numbering + response &= _zed->sendCfgValset(); if (response == false) systemPrintln("Module failed config block 0"); - success &= response; - response = true; // Reset - - // Enable the constellations the user has set - response &= setConstellations(); // 19 messages. Send newCfg or sendCfg with value set - if (response == false) - systemPrintln("Module failed config block 1"); - success &= response; - response = true; // Reset - - // Make sure the appropriate messages are enabled - response &= setMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set - if (response == false) - systemPrintln("Module failed config block 2"); - success &= response; - response = true; // Reset // Disable NMEA messages on all but UART1 response &= _zed->newCfgValset(VAL_LAYER_ALL); @@ -748,43 +481,76 @@ bool GNSS_ZED::configureGNSS() response &= _zed->sendCfgValset(); if (response == false) - systemPrintln("Module failed config block 3"); - - success &= response; - - if (success) - { - systemPrintln("ZED-F9x configuration updated"); - } + systemPrintln("Module failed config block 1"); - settings.gnssConfiguredOnce = success; + // Enable RTCM3 if needed - if not enable NMEA IN to keep skipped updated + gnssConfigure(GNSS_CONFIG_EXT_CORRECTIONS); // Request receiver to use new settings - // The configuration should be saved to RAM+BBR+FLASH. No need to saveConfiguration here. + if (response) + systemPrintln("ZED-F9P configured"); - return (success); + return (response); } //---------------------------------------- -// Configure specific aspects of the receiver for rover mode +// Configure specific aspects of the receiver for base mode //---------------------------------------- -bool GNSS_ZED::configureRover() +bool GNSS_ZED::configureBase() { - if (online.gnss == false) + // If we are already in the appropriate base mode, no changes needed + + // We may have been in a previous survey-in. We want to restart a survey-in regardless. + // If we are already in the appropriate base mode, no changes needed + // if (settings.fixedBase == false && gnssInBaseSurveyInMode()) + // return (true); // No changes needed + + if (settings.fixedBase == true) { - systemPrintln("GNSS not online"); - return (false); + // 0 - Rover, 1 - Base Survey-In, 2 - Base Fixed ECEF, 3 - Base Fixed LLH + int currentMode = getMode(); + if (currentMode == 2 && settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) + return (true); // No changes needed + if (currentMode == 3 && settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) + return (true); // No changes needed } - // If our settings haven't changed, trust GNSS's settings - if (settings.gnssConfiguredRover) + // Assume we are changing from Rover to Base, request any additional config changes + + bool response = true; + + if (settings.fixedBase == false) { - systemPrintln("Skipping ZED Rover configuration"); - return (true); + // If we are doing a Survey-In (temporary) style Base, change to Rover Mode so our location can settle + // Base config resumes at the end of startSurveyIn() + response &= _zed->setVal8(UBLOX_CFG_TMODE_MODE, 0); // Change to Rover Mode + gnssConfigure(GNSS_CONFIG_FIX_RATE); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); + } + else + { + // If we are doing a Fixed Base, config occurs in fixedBaseStart() } - update(); // Regularly poll to get latest data + if (response == false) + systemPrintln("Base config fail"); + return (response); +} + +//---------------------------------------- +// Configure specific aspects of the receiver for NTP mode +//---------------------------------------- +bool GNSS_ZED::configureNtpMode() +{ bool success = false; + + // This is only called by STATE_NTPSERVER_NOT_STARTED + // I guess it is OK to always do the configuration? + // stateUpdate clear the Base and Rover configuration flags + // to ensure the configuration is re-applied when we exit this mode + + update(); // Regularly poll to get latest data + int tryNo = -1; // Try up to MAX_SET_MESSAGES_RETRIES times to configure the GNSS @@ -795,68 +561,91 @@ bool GNSS_ZED::configureRover() { bool response = true; - // Set output rate + // In NTP mode we force 1Hz response &= _zed->newCfgValset(VAL_LAYER_ALL); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, settings.measurementRateMs); - response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, settings.navigationRate); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, 1); // Survey mode is only available on ZED-F9P modules - if (commandSupported(UBLOX_CFG_TMODE_MODE)) - response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)settings.dynamicModel); // Set dynamic model + // Set dynamic model to stationary + response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, DYN_MODEL_STATIONARY); // Set dynamic model - // RTCM is only available on ZED-F9P modules - // - // For most RTK products, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages - // are configured over I2C. Any messages that need to be logged are output on UART1, and received by this code - // using serialGNSS-> So in Rover mode, we want to disable any RTCM messages on I2C (and USB and UART2). - // - // But, on the Reference Station, the GNSS is interfaced via SPI. It has no access to I2C and UART1. So for that - // product - in Rover mode - we want to leave any RTCM messages enabled on SPI so they can be logged if desired. + // Set time pulse to 1Hz (100:900) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_DEF, 0); // Time pulse definition is a period (in us) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_LENGTH_DEF, 1); // Define timepulse by length (not ratio) + response &= + _zed->addCfgValset(UBLOX_CFG_TP_USE_LOCKED_TP1, + 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid + response &= _zed->addCfgValset(UBLOX_CFG_TP_TP1_ENA, 1); // Enable timepulse + response &= _zed->addCfgValset(UBLOX_CFG_TP_POL_TP1, 1); // 1 = rising edge - // Find first RTCM record in ubxMessage array - int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); + // While the module is _locking_ to GNSS time, turn off pulse + response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_TP1, 1000000); // Set the period between pulses in us + response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_TP1, 0); // Set the pulse length in us - // Set RTCM messages to user's settings - for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) - response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, - settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 - 1 = I2C + // When the module is _locked_ to GNSS time, make it generate 1Hz (100ms high, 900ms low) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, 1000000); // Set the period between pulses is us + response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, 100000); // Set the pulse length in us - // Set RTCM messages to user's settings - for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) - { - response &= - _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, - settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 + 1 = UART2 - response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, - settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 + 2 = USB - } + // Ensure pulse is aligned to top-of-second. This is the default. Set it here just to make sure. + response &= _zed->addCfgValset(UBLOX_CFG_TP_ALIGN_TO_TOW_TP1, 1); - response &= _zed->addCfgValset(UBLOX_CFG_NMEA_MAINTALKERID, - 3); // Return talker ID to GNGGA after NTRIP Client set to GPGGA + // Set the time grid to UTC. This is the default. Set it here just to make sure. + response &= _zed->addCfgValset(UBLOX_CFG_TP_TIMEGRID_TP1, 0); // 0=UTC; 1=GPS - response &= _zed->addCfgValset(UBLOX_CFG_NMEA_HIGHPREC, 1); // Enable high precision NMEA - response &= _zed->addCfgValset(UBLOX_CFG_NMEA_SVNUMBERING, 1); // Enable extended satellite numbering + // Sync to GNSS. This is the default. Set it here just to make sure. + response &= _zed->addCfgValset(UBLOX_CFG_TP_SYNC_GNSS_TP1, 1); response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation - response &= _zed->sendCfgValset(); // Closing + // Ensure PVT, HPPOSLLH and TP messages are being output at 1Hz on the correct port + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_PVT_I2C, 1); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_HPPOSLLH_I2C, 1); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_TIM_TP_I2C, 1); + + response &= _zed->sendCfgValset(); // Closing value if (response) success = true; } if (!success) - systemPrintln("Rover config fail"); - - settings.gnssConfiguredRover = success; + systemPrintln("NTP config fail"); // The configuration should be saved to RAM+BBR+FLASH. No need to saveConfiguration here. return (success); } +//---------------------------------------- +// Configure specific aspects of the receiver for rover mode +//---------------------------------------- +bool GNSS_ZED::configureRover() +{ + if (gnssInRoverMode()) + { + if (settings.debugGnssConfig) + systemPrintln("Skipping Rover configuration"); + return (true); // No changes needed + } + + // Assume we are changing from Base to Rover, request any additional config changes + + bool response = true; + + gnssConfigure(GNSS_CONFIG_FIX_RATE); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); + + response &= _zed->setVal8(UBLOX_CFG_TMODE_MODE, 0); // Switch to Rover mode + + if (response == false) + systemPrintln("Rover config fail"); + + return (response); +} + //---------------------------------------- // Responds with the messages supported on this platform // Inputs: @@ -907,42 +696,6 @@ void GNSS_ZED::debuggingEnable() _zed->enableDebugging(Serial, true); } -//---------------------------------------- -void GNSS_ZED::enableGgaForNtrip() -{ - if (online.gnss) - { - // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA - _zed->setVal8(UBLOX_CFG_NMEA_MAINTALKERID, 1, VAL_LAYER_ALL); - _zed->setNMEAGPGGAcallbackPtr(&zedPushGPGGA); // Set up the callback for GPGGA - - float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; - if (measurementFrequency < 0.2) - measurementFrequency = 0.2; // 0.2Hz * 5 = 1 measurement every 5 seconds - if (settings.debugGnss) - systemPrintf("Adjusting GGA setting to %f\r\n", measurementFrequency); - _zed->setVal8(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, - measurementFrequency, VAL_LAYER_ALL); // Enable GGA over I2C. Tell the module to output GGA every second - } -} - -//---------------------------------------- -// Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted -// even if there is no GPS fix. We use it to test serial output. -// Returns true if successfully started and false upon failure -//---------------------------------------- -bool GNSS_ZED::enableRTCMTest() -{ - if (online.gnss) - { - _zed->newCfgValset(VAL_LAYER_RAM); // Create a new Configuration Item VALSET message - _zed->addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1230_UART2, 1); // Enable message 1230 every second - _zed->sendCfgValset(); // Send the VALSET - return true; - } - return false; -} - //---------------------------------------- // Restore the GNSS to the factory settings //---------------------------------------- @@ -951,13 +704,13 @@ void GNSS_ZED::factoryReset() if (online.gnss) { // Set the clearMask and loadMask to 0xFFFF. Set deviceMask to devBBR | devFlash - uint8_t clearAndLoadMask[] = { 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0x03 }; + uint8_t clearAndLoadMask[] = {0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0x03}; _zed->cfgCfg(clearAndLoadMask, 13); delay(2000); // Set the saveMask to 0xFFFF. Set deviceMask to devBBR | devFlash - uint8_t saveMask[] = { 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0x03 }; + uint8_t saveMask[] = {0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0x03}; _zed->cfgCfg(saveMask, 13); _zed->hardReset(); // Perform a reset leading to a cold start (zero info start-up) @@ -992,10 +745,16 @@ bool GNSS_ZED::fixedBaseStart() bool response = true; if (online.gnss == false) - { - systemPrintln("GNSS not online"); return (false); - } + + // If we are already in the appropriate base mode, no changes needed + + // 0 - Rover, 1 - Base Survey-In, 2 - Base Fixed ECEF, 3 - Base Fixed LLH + int currentMode = getMode(); + if (currentMode == 2 && settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) + return (true); // No changes needed + if (currentMode == 3 && settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) + return (true); // No changes needed if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) { @@ -1016,7 +775,7 @@ bool GNSS_ZED::fixedBaseStart() //-1280208.308,-4716803.847,4086665.811 is SparkFun HQ so... response &= _zed->newCfgValset(VAL_LAYER_ALL); - response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Fixed + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Switch to Fixed Base mode response &= _zed->addCfgValset(UBLOX_CFG_TMODE_POS_TYPE, 0); // Position in ECEF response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_X, majorEcefX); response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_X_HP, minorEcefX); @@ -1056,7 +815,7 @@ bool GNSS_ZED::fixedBaseStart() // systemPrintf("minor (should be 84): %ld\r\n", minorAlt); response &= _zed->newCfgValset(VAL_LAYER_ALL); - response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Fixed + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Switch to Fixed Base mode response &= _zed->addCfgValset(UBLOX_CFG_TMODE_POS_TYPE, 1); // Position in LLH response &= _zed->addCfgValset(UBLOX_CFG_TMODE_LAT, majorLat); response &= _zed->addCfgValset(UBLOX_CFG_TMODE_LAT_HP, minorLat); @@ -1067,9 +826,39 @@ bool GNSS_ZED::fixedBaseStart() response &= _zed->sendCfgValset(); } + // Now that the module is set to base mode, complete the base config. + gnssConfigure(GNSS_CONFIG_FIX_RATE); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); + return (response); } +//---------------------------------------- +// Check if given GNSS fix rate is allowed +// Rates are expressed in ms between fixes. +//---------------------------------------- +const float f9pMinRateHz = 0.00012; // Limit of 127 (navRate) * 65000ms (measRate) = 137 minute limit. +const float f9pMaxRateHz = 20.0; // 20Hz + +bool GNSS_ZED::fixRateIsAllowed(uint32_t fixRateMs) +{ + if (fixRateMs >= fixRateGetMinimumMs() && fixRateMs <= fixRateGetMaximumMs()) + return (true); + return (false); +} + +// Return minimum in milliseconds +uint32_t GNSS_ZED::fixRateGetMinimumMs() +{ + return (1000.0 / f9pMaxRateHz); // Max Hz is min ms +} + +// Return maximum in milliseconds +uint32_t GNSS_ZED::fixRateGetMaximumMs() +{ + return (1000.0 / f9pMinRateHz); // Min Hz is max ms +} + //---------------------------------------- // Return the number of active/enabled messages //---------------------------------------- @@ -1277,6 +1066,37 @@ uint8_t GNSS_ZED::getMinute() return (_minute); } +//---------------------------------------- +// Returns the current mode +// 0 - Rover, 1 - Base Survey-In, 2 - Base Fixed ECEF, 3 - Base Fixed LLH +//---------------------------------------- +uint8_t GNSS_ZED::getMode() +{ + if (online.gnss) + { + // Survey mode is only available on ZED-F9P modules + if (commandSupported(UBLOX_CFG_TMODE_MODE)) + { + int currentMode = _zed->getVal8(UBLOX_CFG_TMODE_MODE); + + if (currentMode == 0) // Rover + return (0); + if (currentMode == 1) // Survey-in + return (1); + if (currentMode == 2) // Fixed + { + int baseType = _zed->getVal8(UBLOX_CFG_TMODE_POS_TYPE); + if (baseType == 0) // ECEF + return (2); // Base Fixed ECEF + if (baseType == 1) // LLH + return (3); // Base Fixed LLH + } + } + } + + return (0); // Rover +} + //---------------------------------------- // Returns month number or zero if not online //---------------------------------------- @@ -1338,7 +1158,7 @@ uint32_t GNSS_ZED::getRadioBaudRate() double GNSS_ZED::getRateS() { // Because we may be in base mode, do not get freq from module, use settings instead - float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; + float measurementFrequency = (1000.0 / settings.measurementRateMs); double measurementRateS = 1.0 / measurementFrequency; // 1 / 4Hz = 0.25s return (measurementRateS); @@ -1404,6 +1224,9 @@ int GNSS_ZED::getSurveyInObservationTime() if (online.gnss == false) return (0); + if (gnssConfigureComplete() == false) + return (0); + // Use a local static so we don't have to request these values multiple times (ZED takes many ms to respond // to this command) if ((millis() - lastCheck) > 1000) @@ -1430,6 +1253,39 @@ uint16_t GNSS_ZED::getYear() return (_year); } +//---------------------------------------- +// Returns true if the GNSS receiver is in Base Fixed mode +//---------------------------------------- +bool GNSS_ZED::gnssInBaseFixedMode() +{ + if (getMode() == 2 || getMode() == 3) // 0 - Rover, 1 - Base Survey-In, 2 - Base Fixed ECEF, 3 - Base Fixed LLH + return (true); + + return (false); +} + +//---------------------------------------- +// Returns true if the GNSS receiver is in Base Survey-in mode +//---------------------------------------- +bool GNSS_ZED::gnssInBaseSurveyInMode() +{ + if (getMode() == 1) // 0 - Rover, 1 - Base Survey-In, 2 - Base Fixed ECEF, 3 - Base Fixed LLH + return (true); + + return (false); +} + +//---------------------------------------- +// Returns true if the GNSS receiver is in Rover mode +//---------------------------------------- +bool GNSS_ZED::gnssInRoverMode() +{ + if (getMode() == 0) // 0 - Rover, 1 - Base Survey-In, 2 - Base Fixed + return (true); + + return (false); +} + //---------------------------------------- // Returns true if the antenna is shorted //---------------------------------------- @@ -1769,6 +1625,8 @@ void GNSS_ZED::menuConstellations() settings.ubxConstellations[ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GPS)].enabled = settings.ubxConstellations[incoming].enabled; } + + gnssConfigure(GNSS_CONFIG_CONSTELLATION); // Request receiver to use new settings } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) break; @@ -1778,9 +1636,6 @@ void GNSS_ZED::menuConstellations() printUnknown(incoming); } - // Apply current settings to module - setConstellations(); - clearBuffer(); // Empty buffer of any newline chars } @@ -1848,13 +1703,15 @@ void GNSS_ZED::menuMessages() setMessageRateByName("NMEA_GST", 1); // We want GSV NMEA to be reported at 1Hz to avoid swamping SPP connection - float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; + float measurementFrequency = (1000.0 / settings.measurementRateMs); if (measurementFrequency < 1.0) measurementFrequency = 1.0; setMessageRateByName("NMEA_GSV", measurementFrequency); // One report per second setMessageRateByName("NMEA_RMC", 1); systemPrintln("Reset to Surveying Defaults (NMEAx5)"); + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings } else if (incoming == 11) { @@ -1864,7 +1721,7 @@ void GNSS_ZED::menuMessages() setMessageRateByName("NMEA_GST", 1); // We want GSV NMEA to be reported at 1Hz to avoid swamping SPP connection - float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; + float measurementFrequency = (1000.0 / settings.measurementRateMs); if (measurementFrequency < 1.0) measurementFrequency = 1.0; setMessageRateByName("NMEA_GSV", measurementFrequency); // One report per second @@ -1874,16 +1731,22 @@ void GNSS_ZED::menuMessages() setMessageRateByName("RXM_RAWX", 1); setMessageRateByName("RXM_SFRBX", 1); systemPrintln("Reset to PPP Logging Defaults (NMEAx5 + RXMx2)"); + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings } else if (incoming == 12) { setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages systemPrintln("All messages disabled"); + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings } else if (incoming == 13) { setGNSSMessageRates(settings.ubxMessageRates, 1); // Turn on all messages to report once per fix systemPrintln("All messages enabled"); + + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) break; @@ -1894,15 +1757,6 @@ void GNSS_ZED::menuMessages() } clearBuffer(); // Empty buffer of any newline chars - - // Make sure the appropriate messages are enabled - bool response = setMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set - if (response == false) - systemPrintf("menuMessages: Failed to enable messages - after %d tries", MAX_SET_MESSAGES_RETRIES); - else - systemPrintln("menuMessages: Messages successfully enabled"); - - setLoggingType(); // Update Standard, PPP, or custom for icon selection } // Given a sub type (ie "RTCM", "NMEA") present menu showing messages with this subtype @@ -1949,7 +1803,19 @@ void GNSS_ZED::menuMessagesSubtype(uint8_t *localMessageRate, const char *messag int msgNumber = (incoming - 1) + startOfBlock; if (messageSupported(msgNumber + rtcmOffset) == true) + { inputMessageRate(localMessageRate[msgNumber], msgNumber + rtcmOffset); + + // Depending on the message type, trigger different config requests + // Note: anything not handled triggers a NMEA config which runs the whole array including + // NMEA, RTCM (Rover), RXM, NAV, NAV2, NMEA NAV2, MON, TIM, PUBX + if (strcmp(messageType, "RTCM") == 0) + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings + else if (strcmp(messageType, "RTCM-Base") == 0) + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings + else + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request receiver to use new settings + } else printUnknown(incoming); } @@ -1983,6 +1849,44 @@ int GNSS_ZED::pushRawData(uint8_t *dataToSend, int dataLength) return (0); } +// These are settings used inside the library, not setting on the GNSS receiver so they are not saved to the receiver's +// NVM We have to re-enable them each time +bool GNSS_ZED::registerCallbacks() +{ + bool response = true; + + // Enable automatic NAV PVT messages with callback to storePVTdata + response &= _zed->setAutoPVTcallbackPtr(&storePVTdata, VAL_LAYER_ALL); + + // Enable automatic NAV HPPOSLLH messages with callback to storeHPdata + response &= _zed->setAutoHPPOSLLHcallbackPtr(&storeHPdata, VAL_LAYER_ALL); + + // Enable automatic TIM TP messages with callback to storeTIMTPdata + if (present.timePulseInterrupt) + response &= _zed->setAutoTIMTPcallbackPtr(&storeTIMTPdata, VAL_LAYER_ALL); + + // Enable automatic MON HW messages with callback to storeMONHWdata + if (present.antennaShortOpen) + response &= _zed->setAutoMONHWcallbackPtr(&storeMONHWdata, VAL_LAYER_ALL); + + // Add a callback for UBX-MON-COMMS + response &= _zed->setAutoMONCOMMScallbackPtr(&storeMONCOMMSdata, VAL_LAYER_ALL); + + return (response); +} + +//---------------------------------------- +// Hardware or software reset the GNSS receiver +// Hotstart GNSS to try to get RTK lock +//---------------------------------------- +bool GNSS_ZED::reset() +{ + if (online.gnss == false) + return false; + _zed->softwareResetGNSSOnly(); + return true; +} + //---------------------------------------- uint16_t GNSS_ZED::rtcmBufferAvailable() { @@ -2034,11 +1938,30 @@ bool GNSS_ZED::setBaudRate(uint8_t port, uint32_t baudRate) } if (port == 1) - return setDataBaudRate(baudRate); + return setBaudRateData(baudRate); else - return setRadioBaudRate(baudRate); + return setBaudRateRadio(baudRate); +} + +// With ZED modules, configuration communication is done over I2C. No 'Comm' UART exists. +bool GNSS_ZED::setBaudRateComm(uint32_t baudRate) +{ + return false; +} + +bool GNSS_ZED::setBaudRateData(uint32_t baudRate) +{ + if (online.gnss) + return _zed->setVal32(UBLOX_CFG_UART1_BAUDRATE, baudRate, VAL_LAYER_ALL); + return false; } +bool GNSS_ZED::setBaudRateRadio(uint32_t baudRate) +{ + if (online.gnss) + return _zed->setVal32(UBLOX_CFG_UART2_BAUDRATE, baudRate, VAL_LAYER_ALL); + return false; +} //---------------------------------------- // Save the current configuration // Returns true when the configuration was saved and false upon failure @@ -2170,14 +2093,6 @@ bool GNSS_ZED::setCorrRadioExtPort(bool enable, bool force) return false; } -//---------------------------------------- -bool GNSS_ZED::setDataBaudRate(uint32_t baud) -{ - if (online.gnss) - return _zed->setVal32(UBLOX_CFG_UART1_BAUDRATE, baud, VAL_LAYER_ALL); - return false; -} - //---------------------------------------- // Set the elevation in degrees //---------------------------------------- @@ -2191,6 +2106,24 @@ bool GNSS_ZED::setElevation(uint8_t elevationDegrees) return false; } +//---------------------------------------- +// Control whether HAS E6 is used in location fixes or not +//---------------------------------------- +bool GNSS_ZED::setHighAccuracyService(bool enableGalileoHas) +{ + // Not yet supported on this platform + return (true); // Return true to clear gnssConfigure test +} + +//---------------------------------------- +// Configure device-direct logging. Currently mosaic-X5 specific. +//---------------------------------------- +bool GNSS_ZED::setLogging() +{ + // Not supported on this platform + return (true); // Return true to clear gnssConfigure test +} + //---------------------------------------- // Given a unique string, find first and last records containing that string in message array //---------------------------------------- @@ -2239,117 +2172,193 @@ bool GNSS_ZED::setMessageRateByName(const char *msgName, uint8_t msgRate) return (false); } +// Given the name of a message, find it, and set the rate +bool GNSS_ZED::setNmeaMessageRateByName(const char *msgName, uint8_t msgRate) +{ + // TODO + return (false); +} + //---------------------------------------- -// Enable all the valid messages for this platform -// There are many messages so split into batches. VALSET is limited to 64 max per batch -// Uses dummy newCfg and sendCfg values to be sure we open/close a complete set +// Set the rate for all messages +// The ZED has a lot more messages than just NMEA in ubxMessageRates[], but other platforms generally just have NMEA and +// RTCM. There are many messages so split into batches. VALSET is limited to 64 per batch. Uses dummy newCfg and +// sendCfg values to be sure we open/close a complete set. //---------------------------------------- -bool GNSS_ZED::setMessages(int maxRetries) +bool GNSS_ZED::setMessagesNMEA() { - bool success = false; + int maxRetries = MAX_SET_MESSAGES_RETRIES; - if (online.gnss) + bool gpggaEnabled = false; + + bool success = true; + + bool response = true; + int messageNumber = 0; + + while (messageNumber < MAX_UBX_MSG) { - int tryNo = -1; + response &= _zed->newCfgValset(VAL_LAYER_ALL); - // Try up to maxRetries times to configure the messages - // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI - // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being - // processed. - while ((++tryNo < maxRetries) && !success) + do { - bool response = true; - int messageNumber = 0; - - while (messageNumber < MAX_UBX_MSG) + if (messageSupported(messageNumber)) { - response &= _zed->newCfgValset(VAL_LAYER_ALL); + uint8_t rate = settings.ubxMessageRates[messageNumber]; - do - { - if (messageSupported(messageNumber)) - { - uint8_t rate = settings.ubxMessageRates[messageNumber]; - - response &= _zed->addCfgValset(ubxMessages[messageNumber].msgConfigKey, rate); - } - messageNumber++; - } while (((messageNumber % 43) < 42) && - (messageNumber < MAX_UBX_MSG)); // Limit 1st batch to 42. Batches after that will be (up to) 43 - // in size. It's a HHGTTG thing. - - if (_zed->sendCfgValset() == false) + // Set NMEA messages to user's settings on UART1 interface + response &= _zed->addCfgValset(ubxMessages[messageNumber].msgConfigKey, + rate); // msgConfigKey defaults to UART1 + + // Mark messages needed for other services (NTRIP Client, PointPerfect, etc) as enabled if rate + // > 0 + if (settings.ubxMessageRates[messageNumber] > 0) { - systemPrintf("sendCfg failed at messageNumber %d %s. Try %d of %d.\r\n", messageNumber - 1, - (messageNumber - 1) < MAX_UBX_MSG ? ubxMessages[messageNumber - 1].msgTextName : "", - tryNo + 1, maxRetries); - response &= false; // If any one of the Valset fails, report failure overall + if (strcmp(ubxMessages[messageNumber].msgTextName, "NMEA_GGA") == 0) + gpggaEnabled = true; } } + messageNumber++; + } while (((messageNumber % 43) < 42) && + (messageNumber < MAX_UBX_MSG)); // Limit 1st batch to 42. Batches after that will be (up to) 43 + // in size. It's a HHGTTG thing. - if (response) - success = true; + if (_zed->sendCfgValset() == false) + { + systemPrintf("sendCfg failed at messageNumber %d %s.\r\n", messageNumber - 1, + (messageNumber - 1) < MAX_UBX_MSG ? ubxMessages[messageNumber - 1].msgTextName : ""); + response &= false; // If any one of the Valset fails, report failure overall } } - return (success); + + // Enable GGA if needed for other services + if (gpggaEnabled == false) + { + // Enable GGA for NTRIP + if (settings.enableNtripClient == true && settings.ntripClient_TransmitGGA == true) + { + float measurementFrequency = (1000.0 / settings.measurementRateMs); + if (measurementFrequency < 0.2) + measurementFrequency = 0.2; // 0.2Hz * 5 = 1 measurement every 5 seconds + if (settings.debugGnssConfig) + systemPrintf("Adjusting GGA setting to %f\r\n", measurementFrequency); + response &= _zed->setVal8(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_UART1, measurementFrequency, + VAL_LAYER_ALL); // Enable GGA over UART1. Tell the module to output GGA every second + } + } + + // Configure the callback for GGA as needed + if (settings.enableNtripClient == true && settings.ntripClient_TransmitGGA == true) + response &= _zed->setNMEAGPGGAcallbackPtr(&zedPushGPGGA); + else + response &= _zed->setNMEAGPGGAcallbackPtr(nullptr); + + return (response); } //---------------------------------------- -// Enable all the valid messages for this platform over the USB port -// Add 2 to every UART1 key. This is brittle and non-perfect, but works. +// Configure RTCM Base messages //---------------------------------------- -bool GNSS_ZED::setMessagesUsb(int maxRetries) +bool GNSS_ZED::setMessagesRTCMBase() { - bool success = false; + // RTCM is only available on ZED-F9P modules + // + // For the RTK EVK, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages + // are configured over I2C. Any messages that need to be logged are output on UART1, and received by this code + // using serialGNSS->. In base mode the RTK device should output RTCM over all ports: + // (Primary) UART2 in case the RTK device is connected via radio to rover + // (Optional) I2C in case user wants the base to connect to WiFi and NTRIP Caster + // (Secondary) USB in case the RTK device is used as an NTRIP caster connected to SBC or other + // (Tertiary) UART1 in case RTK device is sending RTCM to a phone that is then NTRIP Caster - if (online.gnss) + // ubxMessageRatesBase is an array of ~12 uint8_ts + // ubxMessage is an array of ~80 messages + // We use firstRTCMRecord as an offset for the keys, but use x as the rate + // Find first RTCM record in ubxMessage array + int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); + + bool response = true; + + response &= _zed->newCfgValset(VAL_LAYER_ALL); + + // VALSET is limited to 64 per batch + // Update RTCM message rates for all interfaces. This is 12 * 5 = 60 valsets. + for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) { - int tryNo = -1; + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 - 1 = I2C + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 1 = UART2 + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 2 = USB + // Disable messages on SPI + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 3, + 0); // UBLOX_CFG UART1 + 3 = SPI + } - // Try up to maxRetries times to configure the messages - // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI - // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being - // processed. - while ((++tryNo < maxRetries) && !success) - { - bool response = true; - int messageNumber = 0; + response &= _zed->sendCfgValset(); // Closing value - while (messageNumber < MAX_UBX_MSG) - { - response &= _zed->newCfgValset(VAL_LAYER_ALL); + return (response); +} - do - { - if (messageSupported(messageNumber)) - response &= _zed->addCfgValset(ubxMessages[messageNumber].msgConfigKey + 2, - settings.ubxMessageRates[messageNumber]); - messageNumber++; - } while (((messageNumber % 43) < 42) && - (messageNumber < MAX_UBX_MSG)); // Limit 1st batch to 42. Batches after that will be (up to) 43 - // in size. It's a HHGTTG thing. - - response &= _zed->sendCfgValset(); - } +//---------------------------------------- +// Configure RTCM Base messages +//---------------------------------------- +bool GNSS_ZED::setMessagesRTCMRover() +{ + // RTCM is only available on ZED-F9P modules + // + // For the RTK EVK, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages + // are configured over I2C. Any messages that need to be logged are output on UART1, and received by using + // serialGNSS->. So in Rover mode, we want to disable any RTCM messages on I2C (and USB and UART2). - if (response) - success = true; - } + // Find first RTCM record in ubxMessage array + int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); + + bool response = true; + + response &= _zed->newCfgValset(VAL_LAYER_ALL); + + // VALSET is limited to 64 per batch + // Update RTCM message rates for all interfaces. This is 12 * 5 = 60 valsets. + for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) + { + // Disable RTCM on all interfaces but UART1 + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, + 0); // UBLOX_CFG UART1 - 1 = I2C + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey, + settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, + 0); // UBLOX_CFG UART1 + 1 = UART2 + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, + 0); // UBLOX_CFG UART1 + 2 = USB + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 3, + 0); // UBLOX_CFG UART1 + 3 = SPI } - return (success); + + response &= _zed->sendCfgValset(); // Closing + + return (response); } //---------------------------------------- // Set the minimum satellite signal level for navigation. //---------------------------------------- -bool GNSS_ZED::setMinCnoRadio(uint8_t cnoValue) +bool GNSS_ZED::setMinCN0(uint8_t cnoValue) { if (online.gnss) { - _zed->setVal8(UBLOX_CFG_NAVSPG_INFIL_MINCNO, cnoValue, VAL_LAYER_ALL); - return true; + if (commandSupported(UBLOX_CFG_NAVSPG_INFIL_MINCNO)) + { + if (_zed->setVal8(UBLOX_CFG_NAVSPG_INFIL_MINCNO, + settings.minCN0) == + false) // Set minimum satellite signal level for navigation - default 6 + return (false); + } } - return false; + return true; // If command not supported, we still need to return true to allow gnssConfigure() to complete } //---------------------------------------- @@ -2366,27 +2375,35 @@ bool GNSS_ZED::setModel(uint8_t modelNumber) } //---------------------------------------- -bool GNSS_ZED::setRadioBaudRate(uint32_t baud) +// Configure multipath mitigation +//---------------------------------------- +bool GNSS_ZED::setMultipathMitigation(bool enableMultipathMitigation) { - if (online.gnss) - return _zed->setVal32(UBLOX_CFG_UART2_BAUDRATE, baud, VAL_LAYER_ALL); - return false; + // Does not exist on the ZED platform + return true; } //---------------------------------------- -// Given the number of seconds between desired solution reports, determine measurementRateMs and navigationRate -// measurementRateS > 25 & <= 65535 -// navigationRate >= 1 && <= 127 +// Given the number of seconds between desired solution reports, determine measurementRateMs setting // We give preference to limiting a measurementRate to 30 or below due to reported problems with measRates above 30. //---------------------------------------- bool GNSS_ZED::setRate(double secondsBetweenSolutions) { - uint16_t measRate = 0; // Calculate these locally and then attempt to apply them to ZED at completion - uint16_t navRate = 0; + // Calculate these locally and then attempt to apply them to ZED at completion + uint16_t measRate = 0; // 25 < measRate <= 65535 + uint16_t navRate = 0; // 1 <= navRate <= 127 if (online.gnss == false) return (false); + // In Base mode we force 1Hz, and avoid overwriting the setting + bool baseOverride = false; + if (gnssInBaseSurveyInMode() || gnssInBaseFixedMode()) + { + baseOverride = true; + secondsBetweenSolutions = 1; + } + // If we have more than an hour between readings, increase mesaurementRate to near max of 65,535 if (secondsBetweenSolutions > 3600.0) { @@ -2437,8 +2454,8 @@ bool GNSS_ZED::setRate(double secondsBetweenSolutions) // If we successfully set rates, only then record to settings if (response) { - settings.measurementRateMs = measRate; - settings.navigationRate = navRate; + if (baseOverride == false) + settings.measurementRateMs = secondsBetweenSolutions * 1000; } else { @@ -2450,27 +2467,11 @@ bool GNSS_ZED::setRate(double secondsBetweenSolutions) } //---------------------------------------- -bool GNSS_ZED::setTalkerGNGGA() -{ - if (online.gnss) - { - bool success = true; - success &= - _zed->setVal8(UBLOX_CFG_NMEA_MAINTALKERID, 3, VAL_LAYER_ALL); // Return talker ID to GNGGA after NTRIP Client set to GPGGA - success &= _zed->setNMEAGPGGAcallbackPtr(nullptr); // Remove callback - return success; - } - return false; -} - -//---------------------------------------- -// Hotstart GNSS to try to get RTK lock +// Enable/disable any output needed for tilt compensation //---------------------------------------- -bool GNSS_ZED::softwareReset() +bool GNSS_ZED::setTilt() { - if (online.gnss == false) - return false; - _zed->softwareResetGNSSOnly(); + // Not yet available on this platform return true; } @@ -2552,8 +2553,8 @@ void GNSS_ZED::storePVTdataRadio(UBX_NAV_PVT_data_t *ubxDataStruct) _millisecond = ceil((ubxDataStruct->iTOW % 1000) / 10.0); // Limit to first two digits _satellitesInView = ubxDataStruct->numSV; - _fixType = ubxDataStruct->fixType; // 0 = no fix, 1 = dead reckoning only, 2 = 2D-fix, 3 = 3D-fix, 4 = GNSS + dead - // reckoning combined, 5 = time only fix + _fixType = ubxDataStruct->fixType; // 0 = no fix, 1 = dead reckoning only, 2 = 2D-fix, 3 = 3D-fix, 4 = GNSS + + // dead reckoning combined, 5 = time only fix _carrierSolution = ubxDataStruct->flags.bits.carrSoln; _validDate = ubxDataStruct->valid.bits.validDate; @@ -2687,13 +2688,17 @@ bool GNSS_ZED::surveyInReset() //---------------------------------------- // Start the survey-in operation -// The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info. //---------------------------------------- bool GNSS_ZED::surveyInStart() { if (online.gnss == false) return (false); + // We may have been in a previous survey-in. We want to restart a survey-in regardless. + // If we are already in the appropriate base mode, no changes needed + // if (gnssInBaseSurveyInMode()) + // return (true); // No changes needed + _zed->setVal8(UBLOX_CFG_TMODE_MODE, 0, VAL_LAYER_ALL); // Disable survey-in mode delay(100); @@ -2723,7 +2728,8 @@ bool GNSS_ZED::surveyInStart() bool response = true; response &= _zed->setVal8(UBLOX_CFG_TMODE_MODE, 1, VAL_LAYER_ALL); // Survey-in enable - response &= _zed->setVal32(UBLOX_CFG_TMODE_SVIN_ACC_LIMIT, settings.observationPositionAccuracy * 10000, VAL_LAYER_ALL); + response &= + _zed->setVal32(UBLOX_CFG_TMODE_SVIN_ACC_LIMIT, settings.observationPositionAccuracy * 10000, VAL_LAYER_ALL); response &= _zed->setVal32(UBLOX_CFG_TMODE_SVIN_MIN_DUR, settings.observationSeconds, VAL_LAYER_ALL); if (response == false) @@ -2746,6 +2752,13 @@ bool GNSS_ZED::surveyInStart() return (false); // Reset of survey failed } + // The ZED-F9P starts a Survey-in in Rover mode to allow the location fix to settle. + // Once settle is complete, the survey starts. + // Here we change the fix rate and enable RTCM messages for base mode. + // Essentially completing the Base configuration. + gnssConfigure(GNSS_CONFIG_FIX_RATE); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); + return (true); } @@ -2813,7 +2826,8 @@ const int zedAllowedRatesCount = sizeof(zedAllowedRates) / sizeof(zedAllowedRate bool GNSS_ZED::baudIsAllowed(uint32_t baudRate) { for (int x = 0; x < zedAllowedRatesCount; x++) - if (zedAllowedRates[x] == baudRate) return (true); + if (zedAllowedRates[x] == baudRate) + return (true); return (false); } diff --git a/Firmware/RTK_Everywhere/NVM.ino b/Firmware/RTK_Everywhere/NVM.ino index ed0fdf70e..94dc3f771 100644 --- a/Firmware/RTK_Everywhere/NVM.ino +++ b/Firmware/RTK_Everywhere/NVM.ino @@ -60,19 +60,15 @@ void loadSettings() // Temp store any variables from LFS that should override SD int resetCount = settings.resetCount; - bool gnssConfiguredOnce = settings.gnssConfiguredOnce; - bool gnssConfiguredRover = settings.gnssConfiguredRover; - bool gnssConfiguredBase = settings.gnssConfiguredBase; + uint32_t gnssConfigureRequest = settings.gnssConfigureRequest; loadSystemSettingsFromFileSD(settingsFileName); settings.resetCount = resetCount; // resetCount from LFS should override SD - // Trust gnssConfigured from LittleFS over SD. + // Trust gnssConfigureRequest from LittleFS over SD. // LittleFS may have been erased, SD could be stale. - settings.gnssConfiguredOnce = gnssConfiguredOnce; - settings.gnssConfiguredRover = gnssConfiguredRover; - settings.gnssConfiguredBase = gnssConfiguredBase; + settings.gnssConfigureRequest = gnssConfigureRequest; // Change empty profile name to 'Profile1' etc if (strlen(settings.profileName) == 0) @@ -340,13 +336,13 @@ void recordSystemSettingsToFile(File *settingsFile) break; case tCmnCnst: - break; // Nothing to do here. Let each GNSS add its settings + break; // Nothing to do here. Let each GNSS add its settings case tCmnRtNm: - break; // Nothing to do here. Let each GNSS add its settings + break; // Nothing to do here. Let each GNSS add its settings case tCnRtRtB: - break; // Nothing to do here. Let each GNSS add its settings + break; // Nothing to do here. Let each GNSS add its settings case tCnRtRtR: - break; // Nothing to do here. Let each GNSS add its settings + break; // Nothing to do here. Let each GNSS add its settings case tEspNowPr: { // Record ESP-NOW peer MAC addresses @@ -1752,29 +1748,26 @@ void loadProfileNumber() else { systemPrintln("profileNumber.txt not found"); - settings.gnssConfiguredOnce = false; // On the next boot, reapply all settings - settings.gnssConfiguredBase = false; - settings.gnssConfiguredRover = false; - recordProfileNumber(0); // Record profile + gnssConfigureDefaults(); // Set all bits in the request bitfield to cause the GNSS receiver to go through a + // full (re)configuration + recordProfileNumber(0); // Record profile } } else { systemPrintln("profileNumber.txt not found"); - settings.gnssConfiguredOnce = false; // On the next boot, reapply all settings - settings.gnssConfiguredBase = false; - settings.gnssConfiguredRover = false; - recordProfileNumber(0); // Record profile + gnssConfigureDefaults(); // Set all bits in the request bitfield to cause the GNSS receiver to go through a full + // (re)configuration + recordProfileNumber(0); // Record profile } // We have arbitrary limit of user profiles if (profileNumber >= MAX_PROFILE_COUNT) { systemPrintln("ProfileNumber invalid. Going to zero."); - settings.gnssConfiguredOnce = false; // On the next boot, reapply all settings - settings.gnssConfiguredBase = false; - settings.gnssConfiguredRover = false; - recordProfileNumber(0); // Record profile + gnssConfigureDefaults(); // Set all bits in the request bitfield to cause the GNSS receiver to go through a full + // (re)configuration + recordProfileNumber(0); // Record profile } systemPrintf("Using profile #%d\r\n", profileNumber); diff --git a/Firmware/RTK_Everywhere/NtripClient.ino b/Firmware/RTK_Everywhere/NtripClient.ino index 7c4ff0a3d..9dedcb419 100644 --- a/Firmware/RTK_Everywhere/NtripClient.ino +++ b/Firmware/RTK_Everywhere/NtripClient.ino @@ -200,6 +200,8 @@ unsigned long lastGGAPush; bool ntripClientForcedShutdown = false; // NTRIP Client was turned off due to an error. Don't allow restart. +bool settingsChanged = false; // Goes true when a menu or command modified the client credentials + //---------------------------------------- // NTRIP Client Routines //---------------------------------------- @@ -229,7 +231,8 @@ bool ntripClientConnect() systemPrintf("NTRIP Client connecting to %s:%d\r\n", settings.ntripClient_CasterHost, settings.ntripClient_CasterPort); - int connectResponse = ntripClient->connect(settings.ntripClient_CasterHost, settings.ntripClient_CasterPort, NTRIP_CLIENT_RESPONSE_TIMEOUT); + int connectResponse = ntripClient->connect(settings.ntripClient_CasterHost, settings.ntripClient_CasterPort, + NTRIP_CLIENT_RESPONSE_TIMEOUT); if (connectResponse < 1) { @@ -392,6 +395,13 @@ bool ntripClientEnabled(const char **line) // Verify still enabled enabled = settings.enableNtripClient; + // Allow restart if settings change + if(settingsChanged == true) + { + settingsChanged = false; + ntripClientForcedShutdown = false; + } + // Determine if the shutdown is being forced if (enabled && ntripClientForcedShutdown) { @@ -587,6 +597,12 @@ void ntripClientSetState(uint8_t newState) } } +// Called from CLI call backs or serial menus to let machine know it can restart the client if it is shut down +void ntripClientSettingsChanged() +{ + settingsChanged = true; +} + //---------------------------------------- // Start the NTRIP client //---------------------------------------- @@ -624,8 +640,9 @@ void ntripClientStop(bool shutdown) // Mark the Client stop so that we don't immediately attempt re-connect to Caster ntripClientTimer = millis(); - // Return the Main Talker ID to "GN". - gnss->setTalkerGNGGA(); + // If we modified the GGA report rate, return it to whatever is in settings + if (settings.ntripClient_TransmitGGA == true) + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request configure so that GGA returns to user defined setting // Determine the next NTRIP client state online.ntripClient = false; @@ -823,9 +840,7 @@ void ntripClientUpdate() if (settings.ntripClient_TransmitGGA == true) { - // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA - // Tell the module to output GGA every 5 seconds - gnss->enableGgaForNtrip(); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request configure so that GGA gets enabled lastGGAPush = millis() - NTRIPCLIENT_MS_BETWEEN_GGA; // Force immediate transmission of GGA message @@ -900,8 +915,14 @@ void ntripClientUpdate() systemPrintln("NTRIP Client resetting connection attempt counter and timeout"); } + // Check if the there have been changes to the client settings + if(settingsChanged == true) + { + settingsChanged = false; + ntripClientRestart(); + } // Check for timeout receiving NTRIP data - if (ntripClientReceiveDataAvailable() == 0) + else if (ntripClientReceiveDataAvailable() == 0) { // Don't fail during retransmission attempts if ((millis() - ntripClientTimer) > NTRIP_CLIENT_RECEIVE_DATA_TIMEOUT) @@ -954,7 +975,7 @@ void ntripClientUpdate() sempParseNextBytes(rtcmParse, rtcmData, rtcmCount); // Parse the data for RTCM1005/1006 if ((settings.debugCorrections || settings.debugNtripClientRtcm || - PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) && + PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) && (!inMainMenu)) { PERIODIC_CLEAR(PD_NTRIP_CLIENT_DATA); @@ -964,7 +985,7 @@ void ntripClientUpdate() else { if ((settings.debugCorrections || settings.debugNtripClientRtcm || - PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) && + PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) && (!inMainMenu)) { PERIODIC_CLEAR(PD_NTRIP_CLIENT_DATA); diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index 4c1b31ef1..1e700c93b 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -78,10 +78,6 @@ */ -// While we wait for the next hardware revisions, Flex and Torch can be manually enabled: -//#define FLEX_OVERRIDE // Uncomment to force support for Flex -//#define TORCH_X2_OVERRIDE // Uncomment to force support for Torch X2 - // To reduce compile times, various parts of the firmware can be disabled/removed if they are not // needed during development #define COMPILE_BT // Comment out to remove Bluetooth functionality @@ -304,7 +300,7 @@ const int gpioExpanderSwitch_S4 = 3; // Controls U19 switch 4: connect GNSS UART const int gpioExpanderSwitch_LoraEnable = 4; // LoRa_EN const int gpioExpanderSwitch_GNSS_Reset = 5; // RST_GNSS const int gpioExpanderSwitch_LoraBoot = 6; // LoRa_BOOT0 - Used for bootloading the STM32 radio IC -const int gpioExpanderSwitch_PowerFastOff = 7; // PWRKILL +const int gpioExpanderSwitch_S5 = 7; // Controls U61 switch 5: connect GNSS UART1 to Port A of CH342 const int gpioExpanderNumSwitches = 8; //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= @@ -629,7 +625,6 @@ volatile static int combinedSpaceRemaining; // Overrun indicator volatile static uint64_t logFileSize; // Updated with each write int bufferOverruns; // Running count of possible data losses since power-on -bool zedUartPassed; // Goes true during testing if ESP can communicate with ZED over UART const uint8_t btEscapeCharacter = '+'; const uint8_t btMaxEscapeCharacters = 3; // Number of characters needed to enter remote command mode over Bluetooth @@ -936,8 +931,6 @@ uint32_t triggerTowSubMsR; // Global copy - Millisecond fraction of Time Of Week uint32_t triggerAccEst; // Global copy - Accuracy estimate in nanoseconds unsigned long splashStart; // Controls how long the splash is displayed for. Currently min of 2s. -bool restartBase; // If the user modifies any NTRIP Server settings, we need to restart the base -bool restartRover; // If the user modifies any NTRIP Client or PointPerfect settings, we need to restart the rover unsigned long startTime; // Used for checking longest-running functions bool lbandCorrectionsReceived; // Used to display L-Band SIV icon when corrections are successfully decrypted (NEO-D9S @@ -1402,6 +1395,9 @@ void setup() checkArrayDefaults(); // Check for uninitialized arrays that won't be initialized by gnssConfigure // (checkGNSSArrayDefaults) + checkGNSSArrayDefaults(); // Check various setting arrays (message rates, etc) to see if they need to be reset to + // defaults + DMW_b("printPartitionTable"); if (settings.printPartitionTable) printPartitionTable(); @@ -1421,14 +1417,14 @@ void setup() DMW_b("beginCharger"); beginCharger(); // Configure battery charger - DMW_b("gnss->configure"); - gnss->configure(); // Requires settings. Configure GNSS module + // DMW_b("gnss->configure"); + // gnss->configure(); // Requires settings. Configure GNSS module DMW_b("beginExternalEvent"); gnss->beginExternalEvent(); // Configure the event input - DMW_b("beginPPS"); - gnss->beginPPS(); // Configure the time pulse output + DMW_b("setPPS"); + gnss->setPPS(); // Configure the pulse per second pin DMW_b("beginInterrupts"); beginInterrupts(); // Begin the TP interrupts @@ -1515,9 +1511,6 @@ void loop() DMW_c("periodicDisplay"); updatePeriodicDisplay(); - DMW_c("gnss->update"); - gnss->update(); - DMW_c("stateUpdate"); stateUpdate(); @@ -1527,6 +1520,9 @@ void loop() DMW_c("displayUpdate"); displayUpdate(); + DMW_c("gnssUpdate"); + gnssUpdate(); + DMW_c("rtcUpdate"); rtcUpdate(); // Set system time to GNSS once we have fix @@ -1643,8 +1639,6 @@ void logUpdate() blockLogging = true; return; } - - setLoggingType(); // Determine if we are standard, PPP, or custom. Changes logging icon accordingly. } else if (online.logging == true && settings.enableLogging == false) { @@ -1665,7 +1659,6 @@ void logUpdate() systemPrintln("Log file: log length reached"); endLogging(false, true); //(gotSemaphore, releaseSemaphore) Close file. Reset parser stats. beginLogging(); // Create new file based on current RTC. - setLoggingType(); // Determine if we are standard, PPP, or custom. Changes logging icon accordingly. } } diff --git a/Firmware/RTK_Everywhere/States.ino b/Firmware/RTK_Everywhere/States.ino index a5c3be861..2b7892256 100644 --- a/Firmware/RTK_Everywhere/States.ino +++ b/Firmware/RTK_Everywhere/States.ino @@ -96,17 +96,7 @@ void stateUpdate() baseStatusLedOff(); - // Configure for rover mode - displayRoverStart(0); - if (gnss->configureRover() == false) - { - settings.gnssConfiguredRover = false; // On the next boot, reapply all settings - recordSystemSettings(); // Record this state for next POR - - systemPrintln("Rover config failed"); - displayRoverFail(1000); - return; - } + gnssConfigure(GNSS_CONFIG_ROVER); // Request reconfigure to rover mode setMuxport(settings.dataPortChannel); // Return mux to original channel @@ -116,23 +106,29 @@ void stateUpdate() baseCasterDisableOverride(); // Disable casting overrides // Start the UART connected to the GNSS receiver for NMEA data (enables logging) - if (tasksStartGnssUart() == false) - displayRoverFail(1000); - else + if (tasksStartGnssUart() == true) { - settings.gnssConfiguredBase = false; // When the mode changes, reapply all settings settings.lastState = STATE_ROVER_NOT_STARTED; recordSystemSettings(); // Record this state for next POR - displayRoverSuccess(500); - - changeState(STATE_ROVER_NO_FIX); + changeState(STATE_ROVER_CONFIG_WAIT); firstRoverStart = false; // Do not allow entry into test menu again } } break; + case (STATE_ROVER_CONFIG_WAIT): { + if (gnssConfigureComplete()) + { + systemPrintln("Rover configured"); + displayRoverSuccess(500); // Show 'Rover Started' + + changeState(STATE_ROVER_NO_FIX); + } + } + break; + case (STATE_ROVER_NO_FIX): { if (gnss->isFixed()) // 3D, 3D+DR changeState(STATE_ROVER_FIX); @@ -221,38 +217,42 @@ void stateUpdate() baseStatusLedOff(); - displayBaseStart(0); // Show 'Base' + gnssConfigure(GNSS_CONFIG_BASE); // Request reconfigure to base mode bluetoothStart(); // Start Bluetooth if it is not already started webServerStop(); // Stop the web config server // Start the UART connected to the GNSS receiver for NMEA data (enables logging) - if (tasksStartGnssUart() && gnss->configureBase()) + if (tasksStartGnssUart()) { - // settings.gnssConfiguredBase is set by gnss->configureBase() - settings.gnssConfiguredRover = false; // When the mode changes, reapply all settings settings.lastState = STATE_BASE_NOT_STARTED; // Record this state for next POR recordSystemSettings(); // Record this state for next POR + changeState(STATE_BASE_CONFIG_WAIT); + } + } + break; + + case (STATE_BASE_CONFIG_WAIT): { + if (gnssConfigureComplete()) + { + systemPrintln("Base configured"); displayBaseSuccess(500); // Show 'Base Started' if (settings.fixedBase == false) changeState(STATE_BASE_TEMP_SETTLE); else + { + gnssConfigure(GNSS_CONFIG_BASE_FIXED); // Request start of fixed base changeState(STATE_BASE_FIXED_NOT_STARTED); - } - else - { - settings.gnssConfiguredBase = false; // On the next boot, reapply all settings - recordSystemSettings(); // Record this state for next POR - - displayBaseFail(1000); + RTK_MODE(RTK_MODE_BASE_FIXED); + } } } break; - // Wait for horz acc of 5m or less before starting survey in + // Wait for horizontal accuracy to reach a certain level before starting survey in case (STATE_BASE_TEMP_SETTLE): { // Blink base LED slowly while we wait for first fix if ((millis() - lastBaseLEDupdate) > 1000) @@ -265,29 +265,27 @@ void stateUpdate() int siv = gnss->getSatellitesInView(); float hpa = gnss->getHorizontalAccuracy(); - // Check for <1m horz accuracy before starting surveyIn + // Check for horizontal accuracy threshold before starting survey in char accuracy[20]; char temp[20]; const char *units = getHpaUnits(hpa, temp, sizeof(temp), 2, true); - // gnssGetSurveyInStartingAccuracy is 10m max - const char *accUnits = - getHpaUnits(gnss->getSurveyInStartingAccuracy(), accuracy, sizeof(accuracy), 2, false); + + // surveyInStartingAccuracy is 10m max + const char *accUnits = getHpaUnits(settings.surveyInStartingAccuracy, accuracy, sizeof(accuracy), 2, false); + systemPrintf("Waiting for Horz Accuracy < %s (%s): %s%s%s%s, SIV: %d\r\n", accuracy, accUnits, temp, (accUnits != units) ? " (" : "", (accUnits != units) ? units : "", (accUnits != units) ? ")" : "", siv); // On the mosaic-X5, the HPA is undefined while the GNSS is determining its fixed position // We need to skip the HPA check... - if ((hpa > 0.0 && hpa < gnss->getSurveyInStartingAccuracy()) || present.gnss_mosaicX5) + if ((hpa > 0.0 && hpa < settings.surveyInStartingAccuracy) || present.gnss_mosaicX5) { - displaySurveyStart(0); // Show 'Survey' + gnssConfigure(GNSS_CONFIG_BASE_SURVEY); // Request reconfigure to base survey in mode - if (gnss->surveyInStart() == true) // Begin survey - { - displaySurveyStarted(500); // Show 'Survey Started' + displaySurveyStarted(500); // Show 'Survey Started' - changeState(STATE_BASE_TEMP_SURVEY_STARTED); - } + changeState(STATE_BASE_TEMP_SURVEY_STARTED); } } break; @@ -374,22 +372,13 @@ void stateUpdate() */ // User has switched to base with fixed option enabled. Let's configure and try to get there. - // If fixed base fails, we'll handle it here + // If fixed base fails, gnssConfigure() will attempt again case (STATE_BASE_FIXED_NOT_STARTED): { - RTK_MODE(RTK_MODE_BASE_FIXED); - bool response = gnss->fixedBaseStart(); - if (response == true) + if (gnssConfigureComplete()) { baseStatusLedOn(); // Turn on the base/status LED changeState(STATE_BASE_FIXED_TRANSMITTING); } - else - { - systemPrintln("Fixed base start failed"); - displayBaseFail(1000); - - changeState(STATE_ROVER_NOT_STARTED); // Return to rover mode to avoid being in fixed base mode - } } break; @@ -461,9 +450,9 @@ void stateUpdate() parseIncomingSettings(); - settings.gnssConfiguredOnce = false; // On the next boot, reapply all settings - settings.gnssConfiguredBase = false; - settings.gnssConfiguredRover = false; + gnssConfigureDefaults(); // Set all bits in the request bitfield to cause the GNSS receiver to go + // through a full (re)configuration + recordSystemSettings(); // Record these settings to unit // Clear buffer @@ -512,11 +501,6 @@ void stateUpdate() // Debounce entry into test menu if ((millis() - lastTestMenuChange) > 500) { - tasksStopGnssUart(); // Stop absoring GNSS serial via task - zedUartPassed = false; - - gnss->enableRTCMTest(); - RTK_MODE(RTK_MODE_TESTING); changeState(STATE_TESTING); } @@ -578,8 +562,8 @@ void stateUpdate() if (tasksStartGnssUart() && ntpConfigureUbloxModule()) { settings.lastState = STATE_NTPSERVER_NOT_STARTED; // Record this state for next POR - settings.gnssConfiguredBase = false; // On the next boot, reapply all settings - settings.gnssConfiguredRover = false; + gnssConfigureDefaults(); // Set all bits in the request bitfield to cause the GNSS receiver to go + // through a full (re)configuration recordSystemSettings(); if (online.ethernetNTPServer) @@ -660,6 +644,8 @@ const char *getState(SystemState state, char *buffer) { case (STATE_ROVER_NOT_STARTED): return "STATE_ROVER_NOT_STARTED"; + case (STATE_ROVER_CONFIG_WAIT): + return "STATE_ROVER_CONFIG_WAIT"; case (STATE_ROVER_NO_FIX): return "STATE_ROVER_NO_FIX"; case (STATE_ROVER_FIX): @@ -672,6 +658,8 @@ const char *getState(SystemState state, char *buffer) return "STATE_BASE_CASTER_NOT_STARTED"; case (STATE_BASE_NOT_STARTED): return "STATE_BASE_NOT_STARTED"; + case (STATE_BASE_CONFIG_WAIT): + return "STATE_BASE_CONFIG_WAIT"; case (STATE_BASE_TEMP_SETTLE): return "STATE_BASE_TEMP_SETTLE"; case (STATE_BASE_TEMP_SURVEY_STARTED): diff --git a/Firmware/RTK_Everywhere/System.ino b/Firmware/RTK_Everywhere/System.ino index 5a94befe1..e991487a3 100644 --- a/Firmware/RTK_Everywhere/System.ino +++ b/Firmware/RTK_Everywhere/System.ino @@ -924,11 +924,10 @@ void beginGpioExpanderSwitches() // SW1 is on pin 0. Driving it high will disconnect the ESP32 from USB // GNSS_RST is on pin 5. Driving it low when an LG290P is connected will kill the I2C bus. - // PWRKILL is on pin 7. Driving it low will turn off the system for (int i = 0; i < gpioExpanderNumSwitches; i++) { - // Set all pins to low except GNSS RESET and PWRKILL - if (i == gpioExpanderSwitch_GNSS_Reset || i == gpioExpanderSwitch_PowerFastOff) + // Set all pins to low except GNSS RESET + if (i == gpioExpanderSwitch_GNSS_Reset) gpioExpanderSwitches->digitalWrite(i, HIGH); else gpioExpanderSwitches->digitalWrite(i, LOW); diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index c651537bd..7b2509362 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -38,11 +38,11 @@ Tasks.ino // Macros //---------------------------------------- -#define WRAP_OFFSET(offset, increment, arraySize) \ - { \ - offset += increment; \ - if (offset >= arraySize) \ - offset -= arraySize; \ +#define WRAP_OFFSET(offset, increment, arraySize) \ + { \ + offset += increment; \ + if (offset >= arraySize) \ + offset -= arraySize; \ } //---------------------------------------- @@ -62,12 +62,7 @@ enum RingBufferConsumers }; const char *const ringBufferConsumer[] = { - "Bluetooth", - "TCP Client", - "TCP Server", - "SD Card", - "UDP Server", - "USB Serial", + "Bluetooth", "TCP Client", "TCP Server", "SD Card", "UDP Server", "USB Serial", }; const int ringBufferConsumerEntries = sizeof(ringBufferConsumer) / sizeof(ringBufferConsumer[0]); @@ -81,21 +76,13 @@ const int ringBufferConsumerEntries = sizeof(ringBufferConsumer) / sizeof(ringBu // List the parsers to be included SEMP_PARSE_ROUTINE const parserTable[] = { - sempNmeaPreamble, - sempUnicoreHashPreamble, - sempRtcmPreamble, - sempUbloxPreamble, - sempUnicoreBinaryPreamble, + sempNmeaPreamble, sempUnicoreHashPreamble, sempRtcmPreamble, sempUbloxPreamble, sempUnicoreBinaryPreamble, }; const int parserCount = sizeof(parserTable) / sizeof(parserTable[0]); // List the names of the parsers const char *const parserNames[] = { - "NMEA", - "Unicore Hash_(#)", - "RTCM", - "u-Blox", - "Unicore Binary", + "NMEA", "Unicore Hash_(#)", "RTCM", "u-Blox", "Unicore Binary", }; const int parserNameCount = sizeof(parserNames) / sizeof(parserNames[0]); @@ -115,9 +102,9 @@ const char *const spartnParserNames[] = { }; const int spartnParserNameCount = sizeof(spartnParserNames) / sizeof(spartnParserNames[0]); -SEMP_PARSE_ROUTINE const rtcmParserTable[] = { sempRtcmPreamble }; +SEMP_PARSE_ROUTINE const rtcmParserTable[] = {sempRtcmPreamble}; const int rtcmParserCount = sizeof(rtcmParserTable) / sizeof(rtcmParserTable[0]); -const char *const rtcmParserNames[] = { "RTCM" }; +const char *const rtcmParserNames[] = {"RTCM"}; const int rtcmParserNameCount = sizeof(rtcmParserNames) / sizeof(rtcmParserNames[0]); //---------------------------------------- @@ -153,7 +140,7 @@ void btReadTask(void *e) unsigned long btLastByteReceived = 0; // Track when the last BT transmission was received. const long btMinEscapeTime = - 2000; // Bluetooth serial traffic must stop this amount before an escape char is recognized + 2000; // Bluetooth serial traffic must stop this amount before an escape char is recognized uint8_t btEscapeCharsReceived = 0; // Used to enter remote command mode // Start notification @@ -206,7 +193,6 @@ void btReadTask(void *e) } } - else // This character is not a command character, pass along to GNSS { // Pass any escape characters that turned out to not be a complete escape sequence @@ -280,7 +266,8 @@ void sendGnssBuffer() { if (correctionLastSeen(CORR_BLUETOOTH)) { - sempParseNextBytes(rtcmParse, bluetoothOutgoingToGnss, bluetoothOutgoingToGnssHead); // Parse the data for RTCM1005/1006 + sempParseNextBytes(rtcmParse, bluetoothOutgoingToGnss, + bluetoothOutgoingToGnssHead); // Parse the data for RTCM1005/1006 if (gnss->pushRawData(bluetoothOutgoingToGnss, bluetoothOutgoingToGnssHead)) { if ((settings.debugCorrections || PERIODIC_DISPLAY(PD_GNSS_DATA_TX)) && !inMainMenu) @@ -396,11 +383,12 @@ void gnssReadTask(void *e) sempSbfSetInvalidDataCallback(sbfParse, processNonSBFData); // Initialize the SPARTN parser for the mosaic-X5 - spartnParse = sempBeginParser(spartnParserTable, spartnParserCount, spartnParserNames, spartnParserNameCount, - 0, // Scratchpad bytes - 1200, // Buffer length - SPARTN payload is 1024 bytes max - processUart1SPARTN, // eom Call Back - in mosaic.ino - "spartnParse"); // Parser Name + spartnParse = + sempBeginParser(spartnParserTable, spartnParserCount, spartnParserNames, spartnParserNameCount, + 0, // Scratchpad bytes + 1200, // Buffer length - SPARTN payload is 1024 bytes max + processUart1SPARTN, // eom Call Back - in mosaic.ino + "spartnParse"); // Parser Name if (!spartnParse) reportFatalError("Failed to initialize the SPARTN parser"); @@ -499,7 +487,7 @@ void gnssReadTask(void *e) if (!expected) // SBF is not expected so restart the parsers { sbfParse->state = sempFirstByte; - if (spartnParserNeeded) + if (spartnParserNeeded) spartnParse->state = sempFirstByte; if (settings.debugGnss) systemPrintf("Unexpected SBF block %d - rejected on ID or length\r\n", @@ -540,7 +528,7 @@ void gnssReadTask(void *e) if (!expected) // SBF is not expected so restart the parsers { sbfParse->state = sempFirstByte; - if (spartnParserNeeded) + if (spartnParserNeeded) spartnParse->state = sempFirstByte; if (settings.debugGnss) systemPrintf("Unexpected EncapsulatedOutput block - rejected\r\n"); @@ -581,12 +569,12 @@ void forceTalkerId(const char *Id, char *msg, size_t maxLen) char oldTalker = msg[2]; msg[2] = *Id; // Force the Talker ID - + // Update the checksum: XOR chars between '$' and '*' size_t len = 1; uint8_t csum = 0; while ((len < maxLen) && (msg[len] != '*')) - csum = csum ^ msg[len++]; + csum = csum ^ msg[len++]; if (len >= (maxLen - 3)) { @@ -638,7 +626,7 @@ void forceRmcCog(char *msg, size_t maxLen) len = 1; uint8_t csum = 0; while ((len < maxLen) && (msg[len] != '*')) - csum = csum ^ msg[len++]; + csum = csum ^ msg[len++]; len++; // Point at the checksum and update it sprintf(&msg[len], "%02X", csum); } @@ -683,7 +671,7 @@ void removeRmcNavStat(char *msg, size_t maxLen) len = 1; uint8_t csum = 0; while ((len < maxLen) && (msg[len] != '*')) - csum = csum ^ msg[len++]; + csum = csum ^ msg[len++]; len++; // Point at the checksum and update it sprintf(&msg[len], "%02X", csum); } @@ -754,7 +742,7 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) latestGPGGA[parse->length] = 0; // NULL terminate if ((strlen(latestGPGGA) > 10) && (latestGPGGA[strlen(latestGPGGA) - 2] == '\r')) latestGPGGA[strlen(latestGPGGA) - 2] = 0; // Truncate the \r\n - forceTalkerId("P",latestGPGGA,latestNmeaMaxLen); + forceTalkerId("P", latestGPGGA, latestNmeaMaxLen); } else systemPrintf("Increase latestNmeaMaxLen to > %d\r\n", parse->length); @@ -767,9 +755,9 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) latestGPRMC[parse->length] = 0; // NULL terminate if ((strlen(latestGPRMC) > 10) && (latestGPRMC[strlen(latestGPRMC) - 2] == '\r')) latestGPRMC[strlen(latestGPRMC) - 2] = 0; // Truncate the \r\n - forceTalkerId("P",latestGPRMC,latestNmeaMaxLen); - forceRmcCog(latestGPRMC,latestNmeaMaxLen); - removeRmcNavStat(latestGPRMC,latestNmeaMaxLen); + forceTalkerId("P", latestGPRMC, latestNmeaMaxLen); + forceRmcCog(latestGPRMC, latestNmeaMaxLen); + removeRmcNavStat(latestGPRMC, latestNmeaMaxLen); } else systemPrintf("Increase latestNmeaMaxLen to > %d\r\n", parse->length); @@ -782,7 +770,7 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) latestGPGST[parse->length] = 0; // NULL terminate if ((strlen(latestGPGST) > 10) && (latestGPGST[strlen(latestGPGST) - 2] == '\r')) latestGPGST[strlen(latestGPGST) - 2] = 0; // Truncate the \r\n - forceTalkerId("P",latestGPGST,latestNmeaMaxLen); + forceTalkerId("P", latestGPGST, latestNmeaMaxLen); } else systemPrintf("Increase latestNmeaMaxLen to > %d\r\n", parse->length); @@ -795,13 +783,13 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) latestGPVTG[parse->length] = 0; // NULL terminate if ((strlen(latestGPVTG) > 10) && (latestGPVTG[strlen(latestGPVTG) - 2] == '\r')) latestGPVTG[strlen(latestGPVTG) - 2] = 0; // Truncate the \r\n - forceTalkerId("P",latestGPVTG,latestNmeaMaxLen); + forceTalkerId("P", latestGPVTG, latestNmeaMaxLen); } else systemPrintf("Increase latestNmeaMaxLen to > %d\r\n", parse->length); } - else if ((strstr(sempNmeaGetSentenceName(parse), "GSA") != nullptr) - || (strstr(sempNmeaGetSentenceName(parse), "GSV") != nullptr)) + else if ((strstr(sempNmeaGetSentenceName(parse), "GSA") != nullptr) || + (strstr(sempNmeaGetSentenceName(parse), "GSV") != nullptr)) { // If the Apple Accessory is sending the data to the EA Session, // discard this GSA / GSV. Bad things would happen if we were to @@ -811,14 +799,14 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) { size_t spaceAvailable = latestEASessionDataMaxLen - strlen(latestEASessionData); if (spaceAvailable >= 3) - spaceAvailable -= 3; // Leave room for the CR, LF and NULL + spaceAvailable -= 3; // Leave room for the CR, LF and NULL while (spaceAvailable < parse->length) // If the buffer is full, delete the oldest message(s) { const char *lfPtr = strstr(latestEASessionData, "\n"); // Find the first LF if (lfPtr == nullptr) - break; // Something has gone badly wrong... - lfPtr++; // Point at the byte after the LF - size_t oldLen = lfPtr - latestEASessionData; // This much data is old + break; // Something has gone badly wrong... + lfPtr++; // Point at the byte after the LF + size_t oldLen = lfPtr - latestEASessionData; // This much data is old size_t newLen = strlen(latestEASessionData) - oldLen; // This much is new (not old) for (size_t i = 0; i <= newLen; i++) // Move the new data over the old. Include the NULL latestEASessionData[i] = latestEASessionData[oldLen + i]; @@ -830,14 +818,14 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) latestEASessionData[dataLen] = 0; // NULL terminate if (latestEASessionData[dataLen - 1] != '\n') { - latestEASessionData[dataLen] = '\r'; // Add CR + latestEASessionData[dataLen] = '\r'; // Add CR latestEASessionData[dataLen + 1] = '\n'; // Add LF - latestEASessionData[dataLen + 2] = 0; // NULL terminate + latestEASessionData[dataLen + 2] = 0; // NULL terminate } } else if (settings.debugNetworkLayer) systemPrintf("Discarding %d GSA/GSV bytes - latestEASessionDataIsBlocking\r\n", parse->length); -#endif //COMPILE_AUTHENTICATION +#endif // COMPILE_AUTHENTICATION } } @@ -865,8 +853,17 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) // Suppress PQTM/NMEA messages as needed if (lg290pMessageEnabled((char *)parse->buffer, parse->length) == false) { - parse->buffer[0] = 0; - parse->length = 0; + if (settings.enableNtripClient == true && settings.ntripClient_TransmitGGA == true) + { + // GGA is disabled, but the user has enabled the NTRIP Client. + // Allow GGA to get through, unmodified. + } + else + { + // Remove the contents of this message + parse->buffer[0] = 0; + parse->length = 0; + } } } } @@ -1005,8 +1002,8 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) // Use a semaphore to prevent handleGnssDataTask from gatecrashing if (ringBufferSemaphore == NULL) - ringBufferSemaphore = xSemaphoreCreateMutex(); // Create the mutex - + ringBufferSemaphore = xSemaphoreCreateMutex(); // Create the mutex + // Take the semaphore. Long wait. handleGnssDataTask could block // Enable printing of the ring buffer offsets (s d 10) and the SD buffer sizes (s h 7) // to see this in action. No more gatecrashing! @@ -1035,54 +1032,54 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) previousTail -= settings.gnssHandlerBufferSize; /* The rbOffsetArray holds the offsets into the ring buffer of the - * start of each of the parsed messages. A head (rbOffsetHead) and - * tail (rbOffsetTail) offsets are used for this array to insert and - * remove entries. Typically this task only manipulates the head as - * new messages are placed into the ring buffer. The handleGnssDataTask - * normally manipulates the tail as data is removed from the buffer. - * However this task will manipulate the tail under two conditions: - * - * 1. The ring buffer gets full and data must be discarded - * - * 2. The rbOffsetArray is too small to hold all of the message - * offsets for the data in the ring buffer. The array is full - * when (Head + 1) == Tail - * - * Notes: - * The rbOffsetArray is allocated along with the ring buffer in - * Begin.ino - * - * The first entry rbOffsetArray[0] is initialized to zero (0) - * in Begin.ino - * - * The array always has one entry in it containing the head offset - * which contains a valid offset into the ringBuffer, handled below - * - * The empty condition is Tail == Head - * - * The amount of data described by the rbOffsetArray is - * rbOffsetArray[Head] - rbOffsetArray[Tail] - * - * rbOffsetArray ringBuffer - * .-----------------. .-----------------. - * | | | | - * +-----------------+ | | - * Tail --> | Msg 1 Offset |---------->+-----------------+ <-- Tail n - * +-----------------+ | Msg 1 | - * | Msg 2 Offset |--------. | | - * +-----------------+ | | | - * | Msg 3 Offset |------. '->+-----------------+ - * +-----------------+ | | Msg 2 | - * Head --> | Head Offset |--. | | | - * +-----------------+ | | | | - * | | | | | | - * +-----------------+ | | | | - * | | | '--->+-----------------+ - * +-----------------+ | | Msg 3 | - * | | | | | - * +-----------------+ '------->+-----------------+ <-- dataHead - * | | | | - */ + * start of each of the parsed messages. A head (rbOffsetHead) and + * tail (rbOffsetTail) offsets are used for this array to insert and + * remove entries. Typically this task only manipulates the head as + * new messages are placed into the ring buffer. The handleGnssDataTask + * normally manipulates the tail as data is removed from the buffer. + * However this task will manipulate the tail under two conditions: + * + * 1. The ring buffer gets full and data must be discarded + * + * 2. The rbOffsetArray is too small to hold all of the message + * offsets for the data in the ring buffer. The array is full + * when (Head + 1) == Tail + * + * Notes: + * The rbOffsetArray is allocated along with the ring buffer in + * Begin.ino + * + * The first entry rbOffsetArray[0] is initialized to zero (0) + * in Begin.ino + * + * The array always has one entry in it containing the head offset + * which contains a valid offset into the ringBuffer, handled below + * + * The empty condition is Tail == Head + * + * The amount of data described by the rbOffsetArray is + * rbOffsetArray[Head] - rbOffsetArray[Tail] + * + * rbOffsetArray ringBuffer + * .-----------------. .-----------------. + * | | | | + * +-----------------+ | | + * Tail --> | Msg 1 Offset |---------->+-----------------+ <-- Tail n + * +-----------------+ | Msg 1 | + * | Msg 2 Offset |--------. | | + * +-----------------+ | | | + * | Msg 3 Offset |------. '->+-----------------+ + * +-----------------+ | | Msg 2 | + * Head --> | Head Offset |--. | | | + * +-----------------+ | | | | + * | | | | | | + * +-----------------+ | | | | + * | | | '--->+-----------------+ + * +-----------------+ | | Msg 3 | + * | | | | | + * +-----------------+ '------->+-----------------+ <-- dataHead + * | | | | + */ // Determine the index for the end of the circular ring buffer // offset list @@ -1191,7 +1188,8 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) if (!inMainMenu) { if (consumer) - systemPrintf("Ring buffer full: discarding %d bytes, %s could be slow\r\n", discardedBytes, consumer); + systemPrintf("Ring buffer full: discarding %d bytes, %s could be slow\r\n", discardedBytes, + consumer); else systemPrintf("Ring buffer full: discarding %d bytes\r\n", discardedBytes); Serial.flush(); // TODO - delete me! @@ -1204,10 +1202,10 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) if (bytesToCopy > (space + discardedBytes - 1)) // Sanity check { systemPrintf("Ring buffer update error %s: bytesToCopy (%d) is > space (%d) + discardedBytes (%d) - 1\r\n", - getTimeStamp(), bytesToCopy, space, discardedBytes); + getTimeStamp(), bytesToCopy, space, discardedBytes); Serial.flush(); // Flush Serial - the code is about to go bang...! } - + // Add another message to the ring buffer // Account for this message // Diagnostic prints are provided by settings.enablePrintSDBuffers and the handleGnssDataTask @@ -1260,7 +1258,8 @@ void processUart1Message(SEMP_PARSE_STATE *parse, uint16_t type) } else { - systemPrintf("processUart1Message could not get ringBuffer semaphore - held by %s\r\n", ringBufferSemaphoreHolder); + systemPrintf("processUart1Message could not get ringBuffer semaphore - held by %s\r\n", + ringBufferSemaphoreHolder); } } @@ -1355,8 +1354,8 @@ void handleGnssDataTask(void *e) // Use a semaphore to prevent handleGnssDataTask from gatecrashing if (ringBufferSemaphore == NULL) - ringBufferSemaphore = xSemaphoreCreateMutex(); // Create the mutex - + ringBufferSemaphore = xSemaphoreCreateMutex(); // Create the mutex + // Take the semaphore. Short wait. processUart1Message shouldn't block for long if (xSemaphoreTake(ringBufferSemaphore, ringBuffer_shortWait_ms) == pdPASS) { @@ -1545,7 +1544,7 @@ void handleGnssDataTask(void *e) // Block logging during Web Config to avoid SD collisions // See issue: https://github.com/sparkfun/SparkFun_RTK_Everywhere_Firmware/issues/693 - if(webServerIsRunning() == true) + if (webServerIsRunning() == true) connected = false; // If user wants to log, record to SD @@ -1574,15 +1573,17 @@ void handleGnssDataTask(void *e) int availableUARTSpace = settings.uartReceiveBufferSize - bufferAvailable; - systemPrintf("SD Incoming Serial @ %s: %04d\tToRead: %04d\tMovedToBuffer: %04d\tavailableUARTSpace: " - "%04d\tavailableHandlerSpace: %04d\tToRecord: %04d\tRecorded: %04d\tBO: %d\r\n", - getTimeStamp(), bufferAvailable, 0, 0, availableUARTSpace, availableHandlerSpace, - bytesToSend, 0, bufferOverruns); + systemPrintf( + "SD Incoming Serial @ %s: %04d\tToRead: %04d\tMovedToBuffer: " + "%04d\tavailableUARTSpace: " + "%04d\tavailableHandlerSpace: %04d\tToRecord: %04d\tRecorded: %04d\tBO: %d\r\n", + getTimeStamp(), bufferAvailable, 0, 0, availableUARTSpace, availableHandlerSpace, + bytesToSend, 0, bufferOverruns); } // For the SD card, we need to write everything we've got // to prevent the ARP Write and Events from gatecrashing... - + int32_t sendTheseBytes = bytesToSend; // Reduce bytes to record if we have more then the end of the buffer @@ -1601,8 +1602,8 @@ void handleGnssDataTask(void *e) if (bytesSent != sendTheseBytes) { - systemPrintf("SD write mismatch (1) @ %s: wrote %d bytes of %d\r\n", - getTimeStamp(), bytesSent, sendTheseBytes); + systemPrintf("SD write mismatch (1) @ %s: wrote %d bytes of %d\r\n", getTimeStamp(), + bytesSent, sendTheseBytes); break; // Exit the do loop } @@ -1620,8 +1621,8 @@ void handleGnssDataTask(void *e) if (bytesSent != sendTheseBytes) { - systemPrintf("SD write mismatch (2) @ %s: wrote %d bytes of %d\r\n", - getTimeStamp(), bytesSent, sendTheseBytes); + systemPrintf("SD write mismatch (2) @ %s: wrote %d bytes of %d\r\n", getTimeStamp(), + bytesSent, sendTheseBytes); break; // Exit the do loop } } @@ -1642,15 +1643,15 @@ void handleGnssDataTask(void *e) if ((settings.enablePrintLogFileStatus) && (!inMainMenu)) systemPrintln("Log file: recording event"); - // Record trigger count with Time Of Week of rising edge (ms), Millisecond fraction of Time Of Week of - // rising edge (ns), and accuracy estimate (ns) + // Record trigger count with Time Of Week of rising edge (ms), Millisecond fraction of + // Time Of Week of rising edge (ns), and accuracy estimate (ns) char eventData[82]; // Max NMEA sentence length is 82 snprintf(eventData, sizeof(eventData), "%lu,%lu,%lu,%lu", triggerCount, triggerTowMsR, triggerTowSubMsR, triggerAccEst); char nmeaMessage[82]; // Max NMEA sentence length is 82 createNMEASentence(CUSTOM_NMEA_TYPE_EVENT, nmeaMessage, sizeof(nmeaMessage), - eventData); // textID, buffer, sizeOfBuffer, text + eventData); // textID, buffer, sizeOfBuffer, text logFile->write(nmeaMessage, strlen(nmeaMessage)); const char *crlf = "\r\n"; @@ -1682,7 +1683,7 @@ void handleGnssDataTask(void *e) char nmeaMessage[82]; // Max NMEA sentence length is 82 createNMEASentence(CUSTOM_NMEA_TYPE_ARP_ECEF_XYZH, nmeaMessage, sizeof(nmeaMessage), - ARPData); // textID, buffer, sizeOfBuffer, text + ARPData); // textID, buffer, sizeOfBuffer, text logFile->write(nmeaMessage, strlen(nmeaMessage)); const char *crlf = "\r\n"; @@ -1716,10 +1717,10 @@ void handleGnssDataTask(void *e) { if (deltaMillis > 150) systemPrintf("Long Write! Time: %ld ms / Location: %ld / Recorded %d bytes / " - "spaceRemaining %d bytes\r\n", - deltaMillis, logFileSize, bytesToSend, combinedSpaceRemaining); + "spaceRemaining %d bytes\r\n", + deltaMillis, logFileSize, bytesToSend, combinedSpaceRemaining); } - } while(0); + } while (0); xSemaphoreGive(sdCardSemaphore); } // End sdCardSemaphore @@ -1728,7 +1729,7 @@ void handleGnssDataTask(void *e) char semaphoreHolder[50]; getSemaphoreFunction(semaphoreHolder); log_w("sdCardSemaphore failed to yield for SD write, held by %s, Tasks.ino line %d", - semaphoreHolder, __LINE__); + semaphoreHolder, __LINE__); feedWdt(); taskYIELD(); @@ -1787,7 +1788,8 @@ void handleGnssDataTask(void *e) } else { - systemPrintf("handleGnssDataTask could not get ringBuffer semaphore - held by %s\r\n", ringBufferSemaphoreHolder); + systemPrintf("handleGnssDataTask could not get ringBuffer semaphore - held by %s\r\n", + ringBufferSemaphoreHolder); } //---------------------------------------------------------------------- @@ -2036,7 +2038,7 @@ void buttonCheckTask(void *e) thisButtonRelease = 0; doubleTap = false; - if (firstButtonThrownOut == false) + if (firstButtonThrownOut == false && present.display_type != DISPLAY_MAX_NONE) firstButtonThrownOut = true; // Throw away the first button press else singleTap = true; @@ -2063,7 +2065,7 @@ void buttonCheckTask(void *e) // End button checking - // If in direct connect mode. Note: this is just a flag not a STATE. + // If in direct connect mode. Note: this is just a flag not a STATE. if (inDirectConnectMode) { // TODO: check if this works on both Torch and Flex. Note: Flex does not yet support buttons @@ -2086,10 +2088,9 @@ void buttonCheckTask(void *e) } } // Torch is a special case. Handle tilt stop and web config mode - else if (productVariant == RTK_TORCH) - //else if (present.imu_im19 && (present.display_type == DISPLAY_MAX_NONE)) // TODO delete me + else if (productVariant == RTK_TORCH || productVariant == RTK_TORCH_X2) { - // Platform has no display and tilt corrections, ie RTK Torch + // Platform has no display and possibly tilt corrections, ie RTK Torch and RTK Torch X2 // In in tilt mode, exit on button press if ((singleTap || doubleTap) && (tiltIsCorrecting() == true)) @@ -2162,10 +2163,14 @@ void buttonCheckTask(void *e) // from firing } + // If we have fast power off, use it + if (present.fastPowerOff == true) + powerDown(false); // Don't display info + while (1) ; } - } // End productVariant == Torch + } // End productVariant == Torch/Torch X2 else // RTK EVK, RTK Facet v2, RTK Facet mosaic, RTK Postcard { if (systemState == STATE_SHUTDOWN) @@ -2198,11 +2203,13 @@ void buttonCheckTask(void *e) { // If we are in any running state, change to STATE_DISPLAY_SETUP case STATE_ROVER_NOT_STARTED: + case STATE_ROVER_CONFIG_WAIT: case STATE_ROVER_NO_FIX: case STATE_ROVER_FIX: case STATE_ROVER_RTK_FLOAT: case STATE_ROVER_RTK_FIX: case STATE_BASE_NOT_STARTED: + case STATE_BASE_CONFIG_WAIT: case STATE_BASE_TEMP_SETTLE: case STATE_BASE_TEMP_SURVEY_STARTED: case STATE_BASE_TEMP_TRANSMITTING: @@ -2283,8 +2290,7 @@ void buttonCheckTask(void *e) { switch (systemState) { - case STATE_DISPLAY_SETUP: - { + case STATE_DISPLAY_SETUP: { // If we are displaying the setup menu, a single tap will cycle through possible system states - see // above Exit into new system state on double tap Exit display setup into previous state after ~10s // - see updateSystemState() @@ -2326,7 +2332,7 @@ void buttonCheckTask(void *e) break; } // End doubleTap switch (systemState) } // End doubleTap - } // End productVariant != Torch + } // End productVariant != (Torch | Torch X2) feedWdt(); taskYIELD(); @@ -2611,10 +2617,10 @@ void beginRtcmParse() { // Begin the RTCM parser - which will extract the base location from RTCM1005 / 1006 rtcmParse = sempBeginParser(rtcmParserTable, rtcmParserCount, rtcmParserNames, rtcmParserNameCount, - 0, // Scratchpad bytes - 1050, // Buffer length - processRTCMMessage, // eom Call Back - "rtcmParse"); // Parser Name + 0, // Scratchpad bytes + 1050, // Buffer length + processRTCMMessage, // eom Call Back + "rtcmParse"); // Parser Name if (!rtcmParse) reportFatalError("Failed to initialize the RTCM parser"); diff --git a/Firmware/RTK_Everywhere/Tilt.ino b/Firmware/RTK_Everywhere/Tilt.ino index f02b15fb7..bda374d6e 100644 --- a/Firmware/RTK_Everywhere/Tilt.ino +++ b/Firmware/RTK_Everywhere/Tilt.ino @@ -301,8 +301,9 @@ void beginTilt() { result &= tiltSensor->sendCommand("LEVER_ARM=0.03391,0.00272,0.02370"); // -28.2, 0. -23.7mm - //Send AT+INSTALL_ANGLE=180,0,0 if the IM19 module is mounted on the back of the GNSS receiver (so the IM19 faces downward instead of upward), before sending the save command. - result &= tiltSensor->sendCommand("INSTALL_ANGLE=180,0,-90"); //IMU is mounted facing down + // Send AT+INSTALL_ANGLE=180,0,0 if the IM19 module is mounted on the back of the GNSS receiver (so the IM19 + // faces downward instead of upward), before sending the save command. + result &= tiltSensor->sendCommand("INSTALL_ANGLE=180,0,-90"); // IMU is mounted facing down } // Set the overall length of the GNSS setup in meters: rod length 1800mm + internal length 96.45mm + antenna @@ -1063,14 +1064,14 @@ void tiltDetect() { present.imu_im19 = true; // Allow tiltUpdate() to run settings.detectedTilt = true; - settings.gnssConfiguredRover = false; // Update rover settings + gnssConfigure(GNSS_CONFIG_TILT); // Request receiver to use new settings break; } } SerialTiltTest.end(); // Release UART1 for reuse systemPrintf("Tilt sensor %sdetected\r\n", settings.detectedTilt ? "" : "not "); - settings.testedTilt = true; //Record this test so we don't do it again + settings.testedTilt = true; // Record this test so we don't do it again recordSystemSettings(); return; } diff --git a/Firmware/RTK_Everywhere/WiFi.ino b/Firmware/RTK_Everywhere/WiFi.ino index 6b409d5d2..f037753d7 100644 --- a/Firmware/RTK_Everywhere/WiFi.ino +++ b/Firmware/RTK_Everywhere/WiFi.ino @@ -1266,7 +1266,7 @@ void wifiUpdateSettings() //********************************************************************* // Determine if any of the WiFi station SSID values are set -bool wifiAfterCommand(int cmdIndex) +bool wifiAfterCommand(const char *settingName, void *settingData, int settingType) { wifiUpdateSettings(); return true; diff --git a/Firmware/RTK_Everywhere/bluetoothSelect.h b/Firmware/RTK_Everywhere/bluetoothSelect.h index cb74ea5ee..0f3dbd1c1 100644 --- a/Firmware/RTK_Everywhere/bluetoothSelect.h +++ b/Firmware/RTK_Everywhere/bluetoothSelect.h @@ -6,6 +6,7 @@ #include "src/BluetoothSerial/BluetoothSerial.h" #include //Click here to get the library: http://librarymanager/All#ESP32_BleSerial by Avinab Malla +#include #include "esp_sdp_api.h" @@ -170,14 +171,18 @@ class BTClassicSerial : public virtual BTSerialInterface, public BluetoothSerial } }; -class BTLESerial : public virtual BTSerialInterface, public BleSerial +//class BTLESerial : public virtual BTSerialInterface, public BleSerial + +//Use buffered BLE serial to handle LIST command over CLI interface +class BTLESerial : public virtual BTSerialInterface, public BleBufferedSerial { public: // Missing from BleSerial bool begin(String deviceName, bool isMaster, bool disableBLE, uint16_t rxQueueSize, uint16_t txQueueSize, const char *serviceID, const char *rxID, const char *txID) { - BleSerial::begin(deviceName.c_str(), serviceID, rxID, txID, -1); // name, service_uuid, rx_uuid, tx_uuid, led_pin + BleBufferedSerial::begin(deviceName.c_str(), serviceID, rxID, txID, -1); // name, service_uuid, rx_uuid, tx_uuid, led_pin + //BleSerial::begin(deviceName.c_str(), serviceID, rxID, txID, -1); // name, service_uuid, rx_uuid, tx_uuid, led_pin return true; } @@ -189,7 +194,8 @@ class BTLESerial : public virtual BTSerialInterface, public BleSerial void end() { - BleSerial::end(); + BleBufferedSerial::end(); + // BleSerial::end(); } esp_err_t register_callback(void * callback) @@ -199,42 +205,50 @@ class BTLESerial : public virtual BTSerialInterface, public BleSerial void setTimeout(unsigned long timeout) { - BleSerial::setTimeout(timeout); + BleBufferedSerial::setTimeout(timeout); + // BleSerial::setTimeout(timeout); } int available() { - return BleSerial::available(); + return BleBufferedSerial::available(); + // return BleSerial::available(); } size_t readBytes(uint8_t *buffer, size_t bufferSize) { - return BleSerial::readBytes(buffer, bufferSize); + return BleBufferedSerial::readBytes(buffer, bufferSize); + // return BleSerial::readBytes(buffer, bufferSize); } int read() { - return BleSerial::read(); + return BleBufferedSerial::read(); + // return BleSerial::read(); } int peek() { - return BleSerial::peek(); + return BleBufferedSerial::peek(); + // return BleSerial::peek(); } size_t write(const uint8_t *buffer, size_t size) { - return BleSerial::write(buffer, size); + return BleBufferedSerial::write(buffer, size); + // return BleSerial::write(buffer, size); } size_t write(uint8_t value) { - return BleSerial::write(value); + return BleBufferedSerial::write(value); + // return BleSerial::write(value); } void flush() { - BleSerial::flush(); + BleBufferedSerial::flush(); + // BleSerial::flush(); } bool connect(uint8_t remoteAddress[], int channel, esp_spp_sec_t sec_mask, esp_spp_role_t role) @@ -244,7 +258,8 @@ class BTLESerial : public virtual BTSerialInterface, public BleSerial bool connected() { - return (BleSerial::connected()); + return (BleBufferedSerial::connected()); + // return (BleSerial::connected()); } void enableSSP(bool inputCapability, bool outputCapability) {} diff --git a/Firmware/RTK_Everywhere/menuBase.ino b/Firmware/RTK_Everywhere/menuBase.ino index 4fb9e4937..8c279198c 100644 --- a/Firmware/RTK_Everywhere/menuBase.ino +++ b/Firmware/RTK_Everywhere/menuBase.ino @@ -27,9 +27,9 @@ void menuBase() // Print the combined HAE APC if we are in the given mode if (settings.fixedBase == true && settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) { - systemPrintf("Total Height Above Ellipsoid of Antenna Phase Center (HAE APC): %0.4fmm\r\n", - ((settings.fixedAltitude + - (settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000))); + systemPrintf( + "Total Height Above Ellipsoid of Antenna Phase Center (HAE APC): %0.4fmm\r\n", + ((settings.fixedAltitude + (settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000))); } systemPrint("1) Toggle Base Mode: "); @@ -100,7 +100,7 @@ void menuBase() } systemPrintf("4) Set required initial positional accuracy before Survey-In: %0.2f meters\r\n", - gnss->getSurveyInStartingAccuracy()); + settings.surveyInStartingAccuracy); } } @@ -142,12 +142,20 @@ void menuBase() if (incoming == 1) { settings.fixedBase ^= 1; - restartBase = true; + + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just change setting + // This prevents a user, while in Rover mode, but changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings } else if (settings.fixedBase == true && incoming == 2) { settings.fixedBaseCoordinateType ^= 1; - restartBase = true; + + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just change setting + // This prevents a user, while in Rover mode, but changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings } else if (settings.fixedBase == true && incoming == 3) @@ -174,7 +182,15 @@ void menuBase() systemPrint("\nECEF Z in meters (ex: 4086669.6393): "); double fixedEcefZ; if (getUserInputDouble(&fixedEcefZ) == INPUT_RESPONSE_VALID) + { settings.fixedEcefZ = fixedEcefZ; + + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just + // change setting This prevents a user, while in Rover mode, but changing a Base setting, + // from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings + } } } } @@ -214,7 +230,15 @@ void menuBase() systemPrint("\r\nAltitude in meters (ex: 1560.2284): "); double fixedAltitude; if (getUserInputDouble(&fixedAltitude) == INPUT_RESPONSE_VALID) + { settings.fixedAltitude = fixedAltitude; + + // Change GNSS receiver configuration if the receiver is in Base mode, + // otherwise, just change setting This prevents a user, while in Rover mode, but + // changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings + } } else { @@ -233,36 +257,69 @@ void menuBase() } else if (settings.fixedBase == true && settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC && incoming == 5) { - getNewSetting("Enter the antenna height (a.k.a. pole length) in millimeters", -15000, 15000, - &settings.antennaHeight_mm); + if (getNewSetting("Enter the antenna height (a.k.a. pole length) in millimeters", -15000, 15000, + &settings.antennaHeight_mm) == INPUT_RESPONSE_VALID) + { + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just change setting + // This prevents a user, while in Rover mode but changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings + // TODO Does any other hardware need to be reconfigured after this setting change? Tilt sensor? + } } else if (settings.fixedBase == true && settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC && incoming == 6) { - getNewSetting("Enter the antenna phase center (the distance between the ARP and the APC) in millimeters. " - "Common antennas " - "Torch=116mm", - -200.0, 200.0, &settings.antennaPhaseCenter_mm); + if (getNewSetting( + "Enter the antenna phase center (the distance between the ARP and the APC) in millimeters. " + "Common antennas " + "Torch/X2=116.5, Facet mosaic=68.5, EVK=42.0, Postcard=37.5, Flex=62.5", + -200.0, 200.0, &settings.antennaPhaseCenter_mm) == INPUT_RESPONSE_VALID) + { + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just change setting + // This prevents a user, while in Rover mode but changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings + } } else if (settings.fixedBase == false && incoming == 2 && (!present.gnss_mosaicX5)) { // Arbitrary 10 minute limit - getNewSetting("Enter the number of seconds for survey-in observation time", 60, 60 * 10, - &settings.observationSeconds); + if (getNewSetting("Enter the number of seconds for survey-in observation time", 60, 60 * 10, + &settings.observationSeconds) == INPUT_RESPONSE_VALID) + { + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just change setting + // This prevents a user, while in Rover mode but changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings + } } else if (settings.fixedBase == false && incoming == 3 && present.gnss_zedf9p) // UM980 does not support survey in minimum deviation { // Arbitrary 1m minimum - getNewSetting("Enter the number of meters for survey-in required position accuracy", 1.0, - (double)maxObservationPositionAccuracy, &settings.observationPositionAccuracy); + if (getNewSetting("Enter the number of meters for survey-in required position accuracy", 1.0, + (double)maxObservationPositionAccuracy, + &settings.observationPositionAccuracy) == INPUT_RESPONSE_VALID) + { + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just change setting + // This prevents a user, while in Rover mode but changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings + } } else if (settings.fixedBase == false && incoming == 4 && (!present.gnss_mosaicX5)) { // Arbitrary 0.1m minimum - - getNewSetting("Enter the positional accuracy required before Survey-In begins", 0.1, - (double)maxSurveyInStartingAccuracy, &settings.surveyInStartingAccuracy); + if (getNewSetting("Enter the positional accuracy required before Survey-In begins", 0.1, + (double)maxSurveyInStartingAccuracy, + &settings.surveyInStartingAccuracy) == INPUT_RESPONSE_VALID) + { + // Change GNSS receiver configuration if the receiver is in Base mode, otherwise, just change setting + // This prevents a user, while in Rover mode but changing a Base setting, from entering Base mode + if (gnss->gnssInBaseSurveyInMode() || gnss->gnssInBaseFixedMode()) + gnssConfigure(GNSS_CONFIG_BASE); // Request receiver to use new settings + } } else if (incoming == 7) @@ -273,7 +330,6 @@ void menuBase() else if (incoming == 8) { settings.enableNtripServer ^= 1; - restartBase = true; } // NTRIP Server entries @@ -290,7 +346,9 @@ void menuBase() systemPrintf("Enter Caster Address for Server %d: ", serverNumber + 1); if (getUserInputString(&settings.ntripServer_CasterHost[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) - restartBase = true; + { + // NTRIP Server state machine will update automatically + } } else if (incoming == 1) { @@ -303,7 +361,9 @@ void menuBase() if (getNewSetting(tempString, 1, 99999, &settings.ntripServer_CasterPort[serverNumber]) == INPUT_RESPONSE_VALID) - restartBase = true; + { + // NTRIP Server state machine will update automatically + } } else if (incoming == 2) { @@ -314,7 +374,9 @@ void menuBase() if (getUserInputString(&settings.ntripServer_CasterUser[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) - restartBase = true; + { + // NTRIP Server state machine will update automatically + } } else if (incoming == 3) { @@ -325,7 +387,9 @@ void menuBase() if (getUserInputString(&settings.ntripServer_CasterUserPW[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) - restartBase = true; + { + // NTRIP Server state machine will update automatically + } } else if (incoming == 4) { @@ -336,7 +400,9 @@ void menuBase() if (getUserInputString(&settings.ntripServer_MountPoint[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) - restartBase = true; + { + // NTRIP Server state machine will update automatically + } } else if (incoming == 5) { @@ -347,7 +413,9 @@ void menuBase() if (getUserInputString(&settings.ntripServer_MountPointPW[serverNumber][0], NTRIP_SERVER_STRING_SIZE) == INPUT_RESPONSE_VALID) - restartBase = true; + { + // NTRIP Server state machine will update automatically + } } } @@ -385,6 +453,7 @@ void menuBaseCoordinateType() if (incoming >= 1 && incoming < (COORDINATE_INPUT_TYPE_INVALID_UNKNOWN + 1)) { settings.coordinateInputType = (CoordinateInputType)(incoming - 1); // Align from 1-9 to 0-8 + // This setting does not affect the receiver configuration } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) break; diff --git a/Firmware/RTK_Everywhere/menuCommands.ino b/Firmware/RTK_Everywhere/menuCommands.ino index 91d81ae88..cdac62c90 100644 --- a/Firmware/RTK_Everywhere/menuCommands.ino +++ b/Firmware/RTK_Everywhere/menuCommands.ino @@ -1324,14 +1324,14 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting { // Determine if extra work needs to be done when the setting changes if (rtkSettingsEntries[i].afterSetCmd) - rtkSettingsEntries[i].afterSetCmd(i); + rtkSettingsEntries[i].afterSetCmd(settingName, (void *)settingValueStr, (int)type); return (SETTING_KNOWN_STRING); } else if (knownSetting == true) { // Determine if extra work needs to be done when the setting changes if (rtkSettingsEntries[i].afterSetCmd) - rtkSettingsEntries[i].afterSetCmd(i); + rtkSettingsEntries[i].afterSetCmd(settingName, &settingValue, (int)type); return (SETTING_KNOWN); } } @@ -1363,7 +1363,8 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting else if (strcmp(settingName, "measurementRateHz") == 0) { - gnss->setRate(1.0 / settingValue); + settings.measurementRateMs = 1000 / settingValue; // Convert Hz to ms + gnssConfigure(GNSS_CONFIG_FIX_RATE); // Request receiver to use new settings // This is one of the first settings to be received. If seen, remove the station files. removeFile(stationCoordinateECEFFileName); @@ -1373,8 +1374,6 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting knownSetting = true; } - // navigationRate is calculated using measurementRateHz - else if (strstr(settingName, "stationECEF") != nullptr) { replaceCharacter((char *)settingValueStr, ' ', ','); // Replace all ' ' with ',' before recording to file @@ -1393,10 +1392,11 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting systemPrintf("%s recorded\r\n", settingValueStr); knownSetting = true; } - else if (strcmp(settingName, "minCNO") == 0) + else if (strcmp(settingName, "minCN0") == 0) { - // Note: this sends the Min CNO to the GNSS, as well as saving it in settings... - gnss->setMinCno(settingValue); + settings.minCN0 = settingValue; + gnssConfigure(GNSS_CONFIG_CN0); // Request receiver to use new settings + knownSetting = true; } else if (strcmp(settingName, "fixedHAEAPC") == 0) @@ -1542,7 +1542,6 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting { endLogging(false, true); //(gotSemaphore, releaseSemaphore) Close file. Reset parser stats. beginLogging(); // Create new file based on current RTC. - setLoggingType(); // Determine if we are standard, PPP, or custom. Changes logging icon accordingly. char newFileNameCSV[sizeof("logFileName,") + sizeof(logFileName) + 1]; snprintf(newFileNameCSV, sizeof(newFileNameCSV), "logFileName,%s,", logFileName); @@ -2252,7 +2251,7 @@ void createSettingsString(char *newSettings) stringRecord(newSettings, "udpOverWiFiStation", 0); // 1 = WiFi mode, 0 = AP // Single variables needed on Config page - stringRecord(newSettings, "minCNO", gnss->getMinCno()); + stringRecord(newSettings, "minCN0", settings.minCN0); stringRecord(newSettings, "enableRCFirmware", enableRCFirmware); // Add SD Characteristics @@ -3210,7 +3209,7 @@ SettingValueResponse getSettingValue(bool inCommands, const char *settingName, c "getNewFirmware", "measurementRateHz", "measurementRateSec", - "minCNO", + "minCN0", "nicknameECEF", "nicknameGeodetic", "resetProfile", diff --git a/Firmware/RTK_Everywhere/menuGNSS.ino b/Firmware/RTK_Everywhere/menuGNSS.ino index 259d65ba1..8d5e046c7 100644 --- a/Firmware/RTK_Everywhere/menuGNSS.ino +++ b/Firmware/RTK_Everywhere/menuGNSS.ino @@ -2,10 +2,6 @@ // Update rate, constellations, etc void menuGNSS() { - // If user modifies any NTRIP settings etc., we need to restart the rover with "restartRover = true;"" - // But, don't set "restartRover = false;" here as that may prevent a restart requested by menuPointPerfect - // for example... - while (1) { systemPrintln(); @@ -129,8 +125,8 @@ void menuGNSS() systemPrintf("5) Minimum elevation for a GNSS satellite to be used in fix (degrees): %d\r\n", settings.minElev); - if (present.minCno) - systemPrintf("6) Minimum satellite signal level for navigation (dBHz): %d\r\n", gnss->getMinCno()); + if (present.minCN0) + systemPrintf("6) Minimum satellite signal level for navigation (dBHz): %d\r\n", settings.minCN0); systemPrint("7) Toggle NTRIP Client: "); if (settings.enableNtripClient == true) @@ -178,59 +174,38 @@ void menuGNSS() if ((incoming == 1) && (!present.gnss_mosaicX5)) { float rateHz = 0.0; - float minRateHz = 1.0; // Measurement rate per second - float maxRateHz = 1.0; - - if (present.gnss_zedf9p) - { - minRateHz = 0.00012; // Limit of 127 (navRate) * 65000ms (measRate) = 137 minute limit. - maxRateHz = 20; // 20Hz - } - else if (present.gnss_um980) - { - minRateHz = 0.02; // 1 / 65 = 0.015384 Hz = Found experimentally - maxRateHz = 20; // 20Hz - } - else if (present.gnss_lg290p) - { - minRateHz = 1.0; // The LG290P doesn't support slower speeds than 1Hz - maxRateHz = 20; // 20Hz - } + float minRateHz = 1000.0 / gnss->fixRateGetMaximumMs(); // Convert ms to Hz + float maxRateHz = 1000.0 / gnss->fixRateGetMinimumMs(); // The minimum in milliseconds is the max in Hz if (getNewSetting("Enter GNSS measurement rate in Hz", minRateHz, maxRateHz, &rateHz) == - INPUT_RESPONSE_VALID) // 20Hz limit with all constellations enabled + INPUT_RESPONSE_VALID) { - gnss->setRate(1.0 / rateHz); // Convert Hz to seconds. This will set settings.measurementRateMs, - // settings.navigationRate, and GSV message + float requestedRateMs = 1000.0 / rateHz; // Convert Hz to milliseconds. + if (gnss->fixRateIsAllowed(requestedRateMs)) + { + settings.measurementRateMs = requestedRateMs; + gnssConfigure(GNSS_CONFIG_FIX_RATE); + } + else + systemPrintln("Error: Illegal rate for this platform"); } } else if ((incoming == 2) && (!present.gnss_mosaicX5)) { - float rate_ms = 0.0; // - float minRate = 1.0; // Seconds between fixes - float maxRate = 1.0; + float requestedRateS = 0.0; + float minRateS = gnss->fixRateGetMinimumMs() / 1000.0; // Convert ms to seconds + float maxRateS = gnss->fixRateGetMaximumMs() / 1000.0; - if (present.gnss_zedf9p) + if (getNewSetting("Enter GNSS measurement rate in seconds between measurements", minRateS, maxRateS, + &requestedRateS) == INPUT_RESPONSE_VALID) { - minRate = 0.05; // 20Hz - maxRate = 8255.0; // Limit of 127 (navRate) * 65000ms (measRate) = 137 minute limit. - } - else if (present.gnss_um980) - { - minRate = 0.05; // 20Hz - maxRate = 65.0; // Found experimentally - } - else if (present.gnss_lg290p) - { - minRate = 0.05; // 20Hz - maxRate = 1.0; // The LG290P doesn't support slower speeds than 1Hz - } - - if (getNewSetting("Enter GNSS measurement rate in seconds between measurements", minRate, maxRate, - &rate_ms) == INPUT_RESPONSE_VALID) - { - // This will set settings.measurementRateMs, settings.navigationRate, and GSV message - gnss->setRate(rate_ms); + if (gnss->fixRateIsAllowed(requestedRateS * 1000.0)) // Convert S to ms + { + settings.measurementRateMs = requestedRateS * 1000.0; + gnssConfigure(GNSS_CONFIG_FIX_RATE); + } + else + systemPrintln("Error: Illegal rate for this platform"); } } else if (incoming == 3 && present.dynamicModel) @@ -279,7 +254,7 @@ void menuGNSS() else settings.dynamicModel = dynamicModel; // Recorded to NVM and file at main menu exit - gnss->setModel(settings.dynamicModel); + gnssConfigure(GNSS_CONFIG_MODEL); // Request receiver to use new settings } #endif // COMPILE_ZED } @@ -292,7 +267,7 @@ void menuGNSS() dynamicModel -= 1; // Align to 0 to 2 settings.dynamicModel = dynamicModel; // Recorded to NVM and file at main menu exit - gnss->setModel(settings.dynamicModel); + gnssConfigure(GNSS_CONFIG_MODEL); // Request receiver to use new settings } } else if (present.gnss_mosaicX5) @@ -304,7 +279,7 @@ void menuGNSS() dynamicModel -= 1; // Align to 0 to MAX_MOSAIC_RX_DYNAMICS - 1 settings.dynamicModel = dynamicModel; // Recorded to NVM and file at main menu exit - gnss->setModel(settings.dynamicModel); + gnssConfigure(GNSS_CONFIG_MODEL); // Request receiver to use new settings } } } @@ -319,30 +294,29 @@ void menuGNSS() // Arbitrary 90 degree max if (getNewSetting("Enter minimum elevation in degrees", 0, 90, &settings.minElev) == INPUT_RESPONSE_VALID) { - gnss->setElevation(settings.minElev); + gnssConfigure(GNSS_CONFIG_ELEVATION); // Request receiver to use new settings } } - else if (incoming == 6 && present.minCno) + else if (incoming == 6 && present.minCN0) { - int minCNO = 0; - // Arbitrary 90 dBHz max. mosaic-X5 is 60dBHz max. - if (getNewSetting("Enter minimum satellite signal level for navigation in dBHz", 0, - present.gnss_mosaicX5 ? 60 : 90, &minCNO) == INPUT_RESPONSE_VALID) + // mosaic-X5 is 60dBHz max. + if (getNewSetting("Enter minimum satellite signal level for navigation in dBHz", 0, 60, &settings.minCN0) == + INPUT_RESPONSE_VALID) { - gnss->setMinCno(minCNO); // Set the setting and configure the GNSS receiver + gnssConfigure(GNSS_CONFIG_CN0); // Request receiver to use new settings } } else if (incoming == 7) { settings.enableNtripClient ^= 1; - restartRover = true; } else if ((incoming == 8) && settings.enableNtripClient == true) { systemPrint("Enter new Caster Address: "); getUserInputString(settings.ntripClient_CasterHost, sizeof(settings.ntripClient_CasterHost)); - restartRover = true; + + ntripClientSettingsChanged(); // Notify the NTRIP Client state machine of new credentials } else if ((incoming == 9) && settings.enableNtripClient == true) { @@ -350,43 +324,45 @@ void menuGNSS() if (getNewSetting("Enter new Caster Port", 1, 99999, &settings.ntripClient_CasterPort) == INPUT_RESPONSE_VALID) { - restartRover = true; + ntripClientSettingsChanged(); // Notify the NTRIP Client state machine of new credentials } } else if ((incoming == 10) && settings.enableNtripClient == true) { systemPrintf("Enter user name for %s: ", settings.ntripClient_CasterHost); getUserInputString(settings.ntripClient_CasterUser, sizeof(settings.ntripClient_CasterUser)); - restartRover = true; + ntripClientSettingsChanged(); // Notify the NTRIP Client state machine of new credentials } else if ((incoming == 11) && settings.enableNtripClient == true) { systemPrintf("Enter user password for %s: ", settings.ntripClient_CasterHost); getUserInputString(settings.ntripClient_CasterUserPW, sizeof(settings.ntripClient_CasterUserPW)); - restartRover = true; + ntripClientSettingsChanged(); // Notify the NTRIP Client state machine of new credentials } else if ((incoming == 12) && settings.enableNtripClient == true) { systemPrint("Enter new Mount Point: "); getUserInputString(settings.ntripClient_MountPoint, sizeof(settings.ntripClient_MountPoint)); - restartRover = true; + ntripClientSettingsChanged(); // Notify the NTRIP Client state machine of new credentials } else if ((incoming == 13) && settings.enableNtripClient == true) { systemPrintf("Enter password for Mount Point %s: ", settings.ntripClient_MountPoint); getUserInputString(settings.ntripClient_MountPointPW, sizeof(settings.ntripClient_MountPointPW)); - restartRover = true; + ntripClientSettingsChanged(); // Notify the NTRIP Client state machine of new credentials } else if ((incoming == 14) && settings.enableNtripClient == true) { settings.ntripClient_TransmitGGA ^= 1; - restartRover = true; + + // We may need to enable the GGA message. Trigger GNSS receiver reconfigure. + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_NMEA); // Request update } else if ((incoming == 15) && present.multipathMitigation) { settings.enableMultipathMitigation ^= 1; - restartRover = true; + gnssConfigure(GNSS_CONFIG_MULTIPATH); // Request update } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) diff --git a/Firmware/RTK_Everywhere/menuMain.ino b/Firmware/RTK_Everywhere/menuMain.ino index 486900d01..377208c9f 100644 --- a/Firmware/RTK_Everywhere/menuMain.ino +++ b/Firmware/RTK_Everywhere/menuMain.ino @@ -274,31 +274,14 @@ void menuMain() printUnknown(incoming); } - // Reboot as base only if currently operating as a base station - if (restartBase == true && inBaseMode() == true) - { - restartBase = false; - settings.gnssConfiguredBase = false; // Reapply configuration - requestChangeState(STATE_BASE_NOT_STARTED); // Restart base upon exit for latest changes to take effect - } - - if (restartRover == true && inRoverMode() == true) - { - restartRover = false; - settings.gnssConfiguredRover = false; // Reapply configuration - requestChangeState(STATE_ROVER_NOT_STARTED); // Restart rover upon exit for latest changes to take effect - } - - gnss->saveConfiguration(); - - recordSystemSettings(); // Once all menus have exited, record the new settings to LittleFS and config file - if (settings.debugGnss == true) { // Re-enable GNSS debug once we exit config menus gnss->debuggingEnable(); } + recordSystemSettings(); // Once all menus have exited, record the new settings to LittleFS and config file + clearBuffer(); // Empty buffer of any newline chars btPrintEchoExit = false; // We are out of the menu system inMainMenu = false; @@ -473,9 +456,9 @@ void menuUserProfiles() // Change the active profile number, without unit reset void changeProfileNumber(byte newProfileNumber) { - settings.gnssConfiguredBase = false; // On the next boot, reapply all settings - settings.gnssConfiguredRover = false; - recordSystemSettings(); // Before switching, we need to record the current settings to LittleFS and SD + gnssConfigureDefaults(); // Set all bits in the request bitfield to cause the GNSS receiver to go through a full + // (re)configuration + recordSystemSettings(); // Before switching, we need to record the current settings to LittleFS and SD recordProfileNumber(newProfileNumber); profileNumber = newProfileNumber; @@ -588,8 +571,7 @@ BluetoothRadioType_e mmChangeBluetoothProtocol(BluetoothRadioType_e bluetoothUse // Update Bluetooth radio if settings have changed void mmSetBluetoothProtocol(BluetoothRadioType_e bluetoothUserChoice, bool clearBtPairings) { - if ((bluetoothUserChoice != settings.bluetoothRadioType) - || (clearBtPairings != settings.clearBtPairings)) + if ((bluetoothUserChoice != settings.bluetoothRadioType) || (clearBtPairings != settings.clearBtPairings)) { // To avoid connection failures, we may need to restart the ESP32 @@ -806,8 +788,8 @@ void menuRadio() { for (int x = 0; x < settings.espnowPeerCount; x++) espNowRemovePeer(settings.espnowPeers[x]); - - espNowStart(); //Restart ESP-NOW to enable broadcastMAC + + espNowStart(); // Restart ESP-NOW to enable broadcastMAC } settings.espnowPeerCount = 0; systemPrintln("Radios forgotten"); diff --git a/Firmware/RTK_Everywhere/menuMessages.ino b/Firmware/RTK_Everywhere/menuMessages.ino index f7730f1ab..345c921c9 100644 --- a/Firmware/RTK_Everywhere/menuMessages.ino +++ b/Firmware/RTK_Everywhere/menuMessages.ino @@ -181,7 +181,6 @@ void menuLog() { endLogging(false, true); //(gotSemaphore, releaseSemaphore) Close file. Reset parser stats. beginLogging(); // Create new file based on current RTC. - setLoggingType(); // Determine if we are standard, PPP, or custom. Changes logging icon accordingly. } else if (incoming == 5 && settings.enableLogging == true) { @@ -242,26 +241,25 @@ void menuMessagesBaseRTCM() if (incoming == 1) { gnss->menuMessageBaseRtcm(); - restartBase = true; } else if (incoming == 2) { gnss->baseRtcmDefault(); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings systemPrintf("Reset to Defaults (%s)\r\n", gnss->getRtcmDefaultString()); - restartBase = true; } else if (incoming == 3) { gnss->baseRtcmLowDataRate(); + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_BASE); // Request receiver to use new settings systemPrintf("Reset to Low Bandwidth Link (%s)\r\n", gnss->getRtcmLowDataRateString()); - restartBase = true; } else if ((incoming == 4) && (namedSettingAvailableOnPlatform("useMSM7"))) { settings.useMSM7 ^= 1; - restartBase = true; + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings } else if ((incoming == 5) && (namedSettingAvailableOnPlatform("rtcmMinElev"))) { @@ -272,7 +270,7 @@ void menuMessagesBaseRTCM() if (elevation >= -90 && elevation <= 90) { settings.rtcmMinElev = elevation; - restartBase = true; + gnssConfigure(GNSS_CONFIG_ELEVATION); // Request receiver to use new settings } } @@ -693,11 +691,14 @@ void checkGNSSArrayDefaults() settings.dynamicModel = UM980_DYN_MODEL_SURVEY; } - if (settings.enableExtCorrRadio == 254) - { - defaultsApplied = true; - settings.enableExtCorrRadio = false; - } + // This setting is not supported on the UM980 nor is it in the command array + // so it does not get used nor recorded to NVM leading to the defaults being + // applied at every boot. Commented out to prevent this issue. + // if (settings.enableExtCorrRadio == 254) + // { + // defaultsApplied = true; + // settings.enableExtCorrRadio = false; + // } if (settings.um980Constellations[0] == 254) { @@ -772,6 +773,7 @@ void checkGNSSArrayDefaults() if (settings.mosaicMessageIntervalsRTCMv3Rover[0] == 0.0) { + defaultsApplied = true; for (int x = 0; x < MAX_MOSAIC_RTCM_V3_INTERVAL_GROUPS; x++) @@ -807,9 +809,13 @@ void checkGNSSArrayDefaults() #ifdef COMPILE_LG290P else if (present.gnss_lg290p) { + // This setting is not supported on the Torch X2 nor is it in the command array + // so it does not get used nor recorded to NVM leading to the defaults being + // applied at every boot. if (settings.enableExtCorrRadio == 254) { - defaultsApplied = true; + if(productVariant != RTK_TORCH_X2) // Prevent defaults from being applied if this *is* a Torch X2 + defaultsApplied = true; settings.enableExtCorrRadio = false; } @@ -874,26 +880,29 @@ void checkGNSSArrayDefaults() { if (present.gnss_um980) { - settings.minCNO = 10; // Default 10 dBHz + settings.minCN0 = 10; // Default 10 dBHz settings.surveyInStartingAccuracy = 2.0; // Default 2m settings.measurementRateMs = 500; // Default 2Hz. } else if (present.gnss_zedf9p) { - settings.minCNO = 6; // Default 6 dBHz + settings.minCN0 = 6; // Default 6 dBHz settings.surveyInStartingAccuracy = 1.0; // Default 1m settings.measurementRateMs = 250; // Default 4Hz. } else if (present.gnss_lg290p) { - settings.minCNO = 10; // Default 10 dBHz + settings.minCN0 = 10; // Default 10 dBHz settings.surveyInStartingAccuracy = 2.0; // Default 2m settings.measurementRateMs = 500; // Default 2Hz. } } if (defaultsApplied == true) + { + gnssConfigureDefaults(); // Request a full reconfigure of the GNSS receiver recordSystemSettings(); + } } // Determine logging type based on the GNSS receiver diff --git a/Firmware/RTK_Everywhere/menuPP.ino b/Firmware/RTK_Everywhere/menuPP.ino index 76028074c..e4c94c2ea 100644 --- a/Firmware/RTK_Everywhere/menuPP.ino +++ b/Firmware/RTK_Everywhere/menuPP.ino @@ -340,7 +340,10 @@ void menuPointPerfectSelectService() { settings.pointPerfectService = incoming - 1; // Align incoming to array - restartRover = true; // Require a rover restart to enable / disable RTCM for PPL + // Request re-config of RTCM Rover messages to enable / disable necessary RTCM messages for PPL + if(inRoverMode()) + gnssConfigure(GNSS_CONFIG_MESSAGE_RATE_RTCM_ROVER); // Request receiver to use new settings + settings.requestKeyUpdate = settings.pointPerfectService != PP_NICKNAME_DISABLED; // Force a key update - or don't @@ -781,7 +784,7 @@ void updateLBandCorrections() millis(); // Restart timer for L-Band. Don't immediately reset ZED to achieve fix. // Hotstart GNSS to try to get RTK lock - gnss->softwareReset(); + gnss->reset(); if (settings.debugCorrections == true) systemPrintf("Restarting ZED. Number of Float lock restarts: %d\r\n", floatLockRestarts); diff --git a/Firmware/RTK_Everywhere/menuPorts.ino b/Firmware/RTK_Everywhere/menuPorts.ino index 598e14295..a44a5bbdb 100644 --- a/Firmware/RTK_Everywhere/menuPorts.ino +++ b/Firmware/RTK_Everywhere/menuPorts.ino @@ -57,11 +57,11 @@ void menuPortsNoMux() systemPrintln("Menu: Ports"); systemPrint("1) Set serial baud rate for Radio Port: "); - systemPrint(gnss->getRadioBaudRate()); + systemPrint(settings.radioPortBaud); systemPrintln(" bps"); systemPrint("2) Set serial baud rate for Data Port: "); - systemPrint(gnss->getDataBaudRate()); + systemPrint(settings.dataPortBaud); systemPrintln(" bps"); systemPrintf("3) Output GNSS data to USB serial: %s\r\n", @@ -80,7 +80,8 @@ void menuPortsNoMux() systemPrintf("4) Allow incoming corrections on RADIO port: %s\r\n", settings.enableExtCorrRadio ? "Enabled" : "Disabled"); systemPrintf("5) Limit RADIO port output to RTCM: %s\r\n", - settings.enableNmeaOnRadio ? "Disabled" : "Enabled"); //Reverse disabled/enabled to align with prompt + settings.enableNmeaOnRadio ? "Disabled" + : "Enabled"); // Reverse disabled/enabled to align with prompt } systemPrintln("x) Exit"); @@ -96,8 +97,7 @@ void menuPortsNoMux() if (gnss->baudIsAllowed(newBaud)) { settings.radioPortBaud = newBaud; - if (online.gnss == true) - gnss->setRadioBaudRate(newBaud); + gnssConfigure(GNSS_CONFIG_BAUD_RATE_RADIO); // Request receiver to use new settings } else { @@ -114,8 +114,7 @@ void menuPortsNoMux() if (gnss->baudIsAllowed(newBaud)) { settings.dataPortBaud = newBaud; - if (online.gnss == true) - gnss->setDataBaudRate(newBaud); + gnssConfigure(GNSS_CONFIG_BAUD_RATE_DATA); // Request receiver to use new settings } else { @@ -133,7 +132,7 @@ void menuPortsNoMux() { // Toggle the enable for the external corrections radio settings.enableExtCorrRadio ^= 1; - gnss->setCorrRadioExtPort(settings.enableExtCorrRadio, true); // Force the setting + gnssConfigure(GNSS_CONFIG_EXT_CORRECTIONS); // Request receiver to use new settings } else if ((incoming == 5) && (present.gnss_zedf9p)) { @@ -154,18 +153,6 @@ void menuPortsNoMux() printUnknown(incoming); } -#ifdef COMPILE_LG290P - if (present.gnss_lg290p) - { - // Apply these changes at menu exit - to enable/disable NMEA on radio - GNSS_LG290P *aLG290P = (GNSS_LG290P *)gnss; - if (aLG290P->inRoverMode() == true) - restartRover = true; - else - restartBase = true; - } -#endif // COMPILE_LG290P - clearBuffer(); // Empty buffer of any newline chars } @@ -178,7 +165,7 @@ void menuPortsMultiplexed() systemPrintln("Menu: Ports"); systemPrint("1) Set Radio port serial baud rate: "); - systemPrint(gnss->getRadioBaudRate()); + systemPrint(settings.radioPortBaud); systemPrintln(" bps"); systemPrint("2) Set Data port connections: "); @@ -194,7 +181,7 @@ void menuPortsMultiplexed() if (settings.dataPortChannel == MUX_GNSS_UART) { systemPrint("3) Set Data port serial baud rate: "); - systemPrint(gnss->getDataBaudRate()); + systemPrint(settings.dataPortBaud); systemPrintln(" bps"); } else if (settings.dataPortChannel == MUX_PPS_EVENTTRIGGER) @@ -218,7 +205,8 @@ void menuPortsMultiplexed() systemPrintf("5) Output GNSS data to USB1 serial: %s\r\n", settings.enableGnssToUsbSerial ? "Enabled" : "Disabled"); systemPrintf("6) Limit RADIO port output to RTCM: %s\r\n", - settings.enableNmeaOnRadio ? "Disabled" : "Enabled"); //Reverse disabled/enabled to align with prompt + settings.enableNmeaOnRadio ? "Disabled" + : "Enabled"); // Reverse disabled/enabled to align with prompt } systemPrintln("x) Exit"); @@ -234,8 +222,7 @@ void menuPortsMultiplexed() if (gnss->baudIsAllowed(newBaud)) { settings.radioPortBaud = newBaud; - if (online.gnss == true) - gnss->setRadioBaudRate(newBaud); + gnssConfigure(GNSS_CONFIG_BAUD_RATE_RADIO); // Request receiver to use new settings } else { @@ -260,6 +247,9 @@ void menuPortsMultiplexed() { settings.dataPortChannel = (muxConnectionType_e)(muxPort - 1); // Adjust user input from 1-4 to 0-3 setMuxport(settings.dataPortChannel); + + if (muxPort == 2) + gnssConfigure(GNSS_CONFIG_PPS); // Request receiver to use new settings } } else if (incoming == 3 && settings.dataPortChannel == MUX_GNSS_UART) @@ -271,8 +261,7 @@ void menuPortsMultiplexed() if (gnss->baudIsAllowed(newBaud)) { settings.dataPortBaud = newBaud; - if (online.gnss == true) - gnss->setDataBaudRate(newBaud); + gnssConfigure(GNSS_CONFIG_BAUD_RATE_DATA); // Request receiver to use new settings } else { @@ -288,7 +277,7 @@ void menuPortsMultiplexed() { // Toggle the enable for the external corrections radio settings.enableExtCorrRadio ^= 1; - gnss->setCorrRadioExtPort(settings.enableExtCorrRadio, true); // Force the setting + gnssConfigure(GNSS_CONFIG_EXT_CORRECTIONS); // Request receiver to use new settings } else if ((incoming == 5) && (present.gnss_zedf9p)) { @@ -315,27 +304,13 @@ void menuPortsMultiplexed() clearBuffer(); // Empty buffer of any newline chars -#ifdef COMPILE_MOSAICX5 - if (present.gnss_mosaicX5) - { - // Apply these changes at menu exit - to enable message output on USB1 - // and/or enable/disable NMEA on radio - GNSS_MOSAIC *mosaic = (GNSS_MOSAIC *)gnss; - if (mosaic->inRoverMode() == true) - restartRover = true; - else - restartBase = true; - } -#endif // COMPILE_MOSAICX5 - - gnss->beginExternalEvent(); // Update with new settings - gnss->beginPPS(); + gnss->beginExternalEvent(); // Update with new settings } // Configure the behavior of the PPS and INT pins. // Most often used for logging events (inputs) and when external triggers (outputs) occur. // menuPortHardwareTriggers is only called by menuPortsMultiplexed. -// Call gnss->beginExternalEvent() and gnss->beginPPS() in menuPortsMultiplexed, not here +// Call gnss->beginExternalEvent() and gnss->setPPS() in menuPortsMultiplexed, not here // since menuPortsMultiplexed has control of the multiplexer void menuPortHardwareTriggers() { diff --git a/Firmware/RTK_Everywhere/menuSystem.ino b/Firmware/RTK_Everywhere/menuSystem.ino index 6b686890c..1b78af4b3 100644 --- a/Firmware/RTK_Everywhere/menuSystem.ino +++ b/Firmware/RTK_Everywhere/menuSystem.ino @@ -521,6 +521,9 @@ void menuDebugHardware() systemPrintf("20) Delay between CLI LIST prints over BLE: %d\r\n", settings.cliBlePrintDelay_ms); + systemPrint("21) Print GNSS Config Debugging: "); + systemPrintf("%s\r\n", settings.debugGnssConfig ? "Enabled" : "Disabled"); + systemPrintln("e) Erase LittleFS"); systemPrintln("t) Test Screen"); @@ -568,7 +571,7 @@ void menuDebugHardware() if (present.gnss_um980) { // Create a file in LittleFS - if (createUm980Passthrough() == true) + if (um980CreatePassthrough() == true) { systemPrintln(); systemPrintln("UM980 passthrough mode has been recorded to LittleFS. Device will now reset."); @@ -643,6 +646,11 @@ void menuDebugHardware() } } + else if (incoming == 21) + { + settings.debugGnssConfig ^= 1; + } + else if (incoming == 'e') { systemPrintln("Erasing LittleFS and resetting"); @@ -993,10 +1001,6 @@ void menuOperation() systemPrint("9) UART Receive Buffer Size: "); systemPrintln(settings.uartReceiveBufferSize); - // ZED - if (present.gnss_zedf9p) - systemPrintln("10) Mirror ZED-F9x's UART1 settings to USB"); - // PPL Float Lock timeout systemPrint("11) Set PPL RTK Fix Timeout (seconds): "); if (settings.pplFixTimeoutS > 0) @@ -1095,17 +1099,6 @@ void menuOperation() ESP.restart(); } } - else if (incoming == 10 && present.gnss_zedf9p) - { -#ifdef COMPILE_ZED - bool response = gnss->setMessagesUsb(MAX_SET_MESSAGES_RETRIES); - - if (response == false) - systemPrintln(F("Failed to enable USB messages")); - else - systemPrintln(F("USB messages successfully enabled")); -#endif // COMPILE_ZED - } else if (incoming == 11) { getNewSetting("Enter number of seconds in RTK float using PPL, before reset", 0, 3600, diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index b99209dac..d69450b55 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -15,39 +15,41 @@ typedef enum { STATE_ROVER_NOT_STARTED = 0, // 0 - STATE_ROVER_NO_FIX, // 1 - STATE_ROVER_FIX, // 2 - STATE_ROVER_RTK_FLOAT, // 3 - STATE_ROVER_RTK_FIX, // 4 - - STATE_BASE_CASTER_NOT_STARTED, // 5, Set override flag - STATE_BASE_NOT_STARTED, // 6 - STATE_BASE_TEMP_SETTLE, // 7, User has indicated base, but current pos accuracy is too low - STATE_BASE_TEMP_SURVEY_STARTED, // 8 - STATE_BASE_TEMP_TRANSMITTING, // 9 - STATE_BASE_FIXED_NOT_STARTED, // 10 - STATE_BASE_FIXED_TRANSMITTING, // 11 - - STATE_DISPLAY_SETUP, // 12 - STATE_WEB_CONFIG_NOT_STARTED, // 13 - STATE_WEB_CONFIG_WAIT_FOR_NETWORK, // 14 - STATE_WEB_CONFIG, // 15 - STATE_TEST, // 16 - STATE_TESTING, // 17 - STATE_PROFILE, // 18 - - STATE_KEYS_REQUESTED, // 19 - - STATE_ESPNOW_PAIRING_NOT_STARTED, // 20 - STATE_ESPNOW_PAIRING, // 21 - - STATE_NTPSERVER_NOT_STARTED, // 22 - STATE_NTPSERVER_NO_SYNC, // 23 - STATE_NTPSERVER_SYNC, // 24 - - STATE_SHUTDOWN, // 25 - - STATE_NOT_SET, // 26, Must be last on list + STATE_ROVER_CONFIG_WAIT, // 1 + STATE_ROVER_NO_FIX, // 2 + STATE_ROVER_FIX, // 3 + STATE_ROVER_RTK_FLOAT, // 4 + STATE_ROVER_RTK_FIX, // 5 + + STATE_BASE_CASTER_NOT_STARTED, // 6, Set override flag + STATE_BASE_NOT_STARTED, // 7 + STATE_BASE_CONFIG_WAIT, // 8 + STATE_BASE_TEMP_SETTLE, // 9, User has indicated base, but current pos accuracy is too low + STATE_BASE_TEMP_SURVEY_STARTED, // 10 + STATE_BASE_TEMP_TRANSMITTING, // 11 + STATE_BASE_FIXED_NOT_STARTED, // 12 + STATE_BASE_FIXED_TRANSMITTING, // 13 + + STATE_DISPLAY_SETUP, // 14 + STATE_WEB_CONFIG_NOT_STARTED, // 15 + STATE_WEB_CONFIG_WAIT_FOR_NETWORK, // 16 + STATE_WEB_CONFIG, // 17 + STATE_TEST, // 18 + STATE_TESTING, // 19 + STATE_PROFILE, // 20 + + STATE_KEYS_REQUESTED, // 21 + + STATE_ESPNOW_PAIRING_NOT_STARTED, // 22 + STATE_ESPNOW_PAIRING, // 23 + + STATE_NTPSERVER_NOT_STARTED, // 24 + STATE_NTPSERVER_NO_SYNC, // 25 + STATE_NTPSERVER_SYNC, // 26 + + STATE_SHUTDOWN, // 27 + + STATE_NOT_SET, // 28, Must be last on list } SystemState; volatile SystemState systemState = STATE_NOT_SET; SystemState lastSystemState = STATE_NOT_SET; @@ -848,16 +850,6 @@ struct Settings bool debugGnss = false; // Turn on to display GNSS library debug messages bool enablePrintPosition = false; uint16_t measurementRateMs = 250; // Elapsed ms between GNSS measurements. 25ms to 65535ms. Default 4Hz. - uint16_t navigationRate = - 1; // Ratio between number of measurements and navigation solutions. Default 1 for 4Hz (with measurementRate). - - // Signatures to indicate how the GNSS is configured (Once, Base, Rover, etc.) - // Bit 0 indicates if the GNSS has been configured previously. - // Bits 1 onwards record the state of critical settings. - // Configuration is reapplied if any of those critical settings have changed - bool gnssConfiguredOnce = false; - bool gnssConfiguredBase = false; - bool gnssConfiguredRover = false; // GNSS UART uint16_t serialGNSSRxFullThreshold = 50; // RX FIFO full interrupt. Max of ~128. See pinUART2Task(). @@ -1037,7 +1029,7 @@ struct Settings // Pulse bool enableExternalPulse = true; // Send pulse once lock is achieved - uint64_t externalPulseLength_us = 100000; // us length of pulse, max of 60s = 60 * 1000 * 1000 + uint64_t externalPulseLength_us = 200000; // us length of pulse, max of 60s = 60 * 1000 * 1000 pulseEdgeType_e externalPulsePolarity = PULSE_RISING_EDGE; // Pulse rises for pulse length, then falls uint64_t externalPulseTimeBetweenPulse_us = 1000000; // us between pulses, max of 60s = 60 * 1000 * 1000 @@ -1049,7 +1041,7 @@ struct Settings // Rover operation uint8_t dynamicModel = 254; // Default will be applied by checkGNSSArrayDefaults bool enablePrintRoverAccuracy = true; - int16_t minCNO = 6; // Minimum satellite signal level for navigation. ZED-F9P default is 6 dBHz + int16_t minCN0 = 6; // Minimum satellite signal level for navigation. ZED-F9P default is 6 dBHz // Minimum elevation (in deg) for a GNSS satellite to be used in NAV // Note: we use 8-bit unsigned here, but some platforms (ZED, mosaic-X5) support negative elevation limits uint8_t minElev = 10; @@ -1221,6 +1213,8 @@ struct Settings bool baseCasterOverride = false; //When true, user has put device into 'BaseCast' mode. Change settings, but don't save to NVM. bool debugCLI = false; //When true, output BLE CLI interactions over serial uint16_t cliBlePrintDelay_ms = 50; // Time delayed between prints during a LIST command to avoid overwhelming the BLE connection + uint32_t gnssConfigureRequest = 0; // Bitfield containing the change requests for various settings on the GNSS receiver + bool debugGnssConfig = false; // Enable to print output during gnssUpdate // Add new settings to appropriate group above or create new group // Then also add to the same group in rtkSettingsEntries below @@ -1310,10 +1304,10 @@ typedef enum HAS = L29, // Platforms which support Galileo HAS } Facet_Flex_Variant; -typedef bool (* AFTER_CMD)(int cmdIndex); +typedef bool (* AFTER_CMD)(const char *settingName, void *settingData, int settingType); // Forward routines -bool wifiAfterCommand(int cmdIndex); +bool wifiAfterCommand(const char *settingName, void *settingData, int settingType); typedef struct { @@ -1554,12 +1548,9 @@ const RTK_Settings_Entry rtkSettingsEntries[] = // GNSS Receiver { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.debugGnss, "debugGnss", nullptr, }, + { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.debugGnssConfig, "debugGnssConfig", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.enablePrintPosition, "enablePrintPosition", nullptr, }, { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint16_t, 0, & settings.measurementRateMs, "measurementRateMs", nullptr, }, - { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint16_t, 0, & settings.navigationRate, "navigationRate", nullptr, }, - { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.gnssConfiguredOnce, "gnssConfiguredOnce", nullptr, }, - { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.gnssConfiguredBase, "gnssConfiguredBase", nullptr, }, - { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.gnssConfiguredRover, "gnssConfiguredRover", nullptr, }, // Hardware { 1, 1, 0, 1, 1, 1, 0, 1, 0, NON, 0, _bool, 0, & settings.enableExternalHardwareEventLogging, "enableExternalHardwareEventLogging", nullptr, }, @@ -1769,7 +1760,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] = // Rover operation { 1, 1, 0, 1, 1, 1, 1, 1, 0, ALL, 0, _uint8_t, 0, & settings.dynamicModel, "dynamicModel", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.enablePrintRoverAccuracy, "enablePrintRoverAccuracy", nullptr, }, - { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _int16_t, 0, & settings.minCNO, "minCNO", nullptr, }, // Not inWebConfig - createSettingsString gets from GNSS + { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _int16_t, 0, & settings.minCN0, "minCN0", nullptr, }, // Not inWebConfig - createSettingsString gets from GNSS { 1, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint8_t, 0, & settings.minElev, "minElev", nullptr, }, // RTC (Real Time Clock) @@ -1890,13 +1881,13 @@ const RTK_Settings_Entry rtkSettingsEntries[] = // i d i v V i c n r e X // g s x k 2 c h d d x 2 Type Qual Variable Name afterSetCmd - // UM980 GNSS Receiver - TODO these apply to more than UM980 { 1, 1, 0, 0, 0, 0, 1, 0, 1, HAS, 1, _bool, 0, & settings.enableGalileoHas, "enableGalileoHas", nullptr, }, { 1, 1, 0, 0, 0, 0, 1, 0, 0, ALL, 0, _bool, 3, & settings.enableMultipathMitigation, "enableMultipathMitigation", nullptr, }, { 0, 0, 0, 0, 0, 0, 1, 0, 0, ALL, 0, _bool, 0, & settings.enableImuCompensationDebug, "enableImuCompensationDebug", nullptr, }, { 0, 0, 0, 0, 0, 0, 1, 0, 0, ALL, 0, _bool, 0, & settings.enableImuDebug, "enableImuDebug", nullptr, }, { 1, 1, 0, 0, 0, 0, 1, 0, 0, ALL, 0, _bool, 0, & settings.enableTiltCompensation, "enableTiltCompensation", nullptr, }, + // UM980 GNSS Receiver #ifdef COMPILE_UM980 { 1, 1, 1, 0, 0, 0, 1, 0, 0, U98, 0, tUmConst, MAX_UM980_CONSTELLATIONS, & settings.um980Constellations, "constellation_", gnssCmdUpdateConstellations, }, { 0, 1, 1, 0, 0, 0, 1, 0, 0, U98, 0, tUmMRNmea, MAX_UM980_NMEA_MSG, & settings.um980MessageRatesNMEA, "messageRateNMEA_", gnssCmdUpdateMessageRates, }, @@ -1910,10 +1901,10 @@ const RTK_Settings_Entry rtkSettingsEntries[] = // WiFi { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.debugWebServer, "debugWebServer", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.debugWifiState, "debugWifiState", nullptr, }, - { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.enableCaptivePortal, "enableCaptivePortal", wifiAfterCommand, }, - { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint8_t, 0, & settings.wifiChannel, "wifiChannel", wifiAfterCommand, }, - { 1, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.wifiConfigOverAP, "wifiConfigOverAP", wifiAfterCommand, }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, ALL, 1, tWiFiNet, MAX_WIFI_NETWORKS, & settings.wifiNetworks, "wifiNetwork_", wifiAfterCommand, }, + { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.enableCaptivePortal, "enableCaptivePortal", nullptr, }, + { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint8_t, 0, & settings.wifiChannel, "wifiChannel", nullptr, }, + { 1, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.wifiConfigOverAP, "wifiConfigOverAP", nullptr, }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, ALL, 1, tWiFiNet, MAX_WIFI_NETWORKS, & settings.wifiNetworks, "wifiNetwork_", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint32_t, 0, & settings.wifiConnectTimeoutMs, "wifiConnectTimeoutMs", nullptr, }, { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.outputTipAltitude, "outputTipAltitude", nullptr, }, @@ -1960,7 +1951,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] = { 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.baseCasterOverride, "baseCasterOverride", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.debugCLI, "debugCLI", nullptr, }, { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint16_t, 0, & settings.cliBlePrintDelay_ms, "cliBlePrintDelay_ms", nullptr, }, - + { 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint32_t, 0, & settings.gnssConfigureRequest, "gnssConfigureRequest", nullptr, }, // Add new settings to appropriate group above or create new group // Then also add to the same group in settings above @@ -2092,7 +2083,7 @@ struct struct_present float antennaPhaseCenter_mm = 0.0; // Used to setup tilt compensation bool galileoHasCapable = false; // UM980 has HAS capabilities bool multipathMitigation = false; // UM980 has MPM, other platforms do not - bool minCno = false; // ZED, mosaic, UM980 have minCN0. LG290P does on version >= v5. + bool minCN0 = false; // ZED, mosaic, UM980 have minCN0. LG290P does on version >= v5. bool minElevation = false; // ZED, mosaic, UM980 have minElevation. LG290P does on versions >= v5. bool dynamicModel = false; // ZED, mosaic, UM980 have dynamic models. LG290P does not. bool gpioExpanderSwitches = false; // Used on Flex diff --git a/Firmware/RTK_Everywhere/support.ino b/Firmware/RTK_Everywhere/support.ino index 5240641bb..0a9c2f07f 100644 --- a/Firmware/RTK_Everywhere/support.ino +++ b/Firmware/RTK_Everywhere/support.ino @@ -751,7 +751,8 @@ void verifyTables() webServerVerifyTables(); pointPerfectVerifyTables(); wifiVerifyTables(); - + gnssVerifyTables(); + if (CORR_NUM >= (int)('x' - 'a')) reportFatalError("Too many correction sources"); } @@ -915,7 +916,7 @@ InputResponse getNewSetting(const char *settingPrompt, float min, float max, flo if (response == INPUT_RESPONSE_VALID) { - if (enteredValue >= min && enteredValue <= max) + if ((float)enteredValue >= min && enteredValue <= max) { *setting = (float)enteredValue; // Recorded to NVM and file at main menu exit return (INPUT_RESPONSE_VALID); diff --git a/Firmware/Test Sketches/System_Check/System_Check.ino b/Firmware/Test Sketches/System_Check/System_Check.ino index 8d7cbd6f9..737e6164b 100644 --- a/Firmware/Test Sketches/System_Check/System_Check.ino +++ b/Firmware/Test Sketches/System_Check/System_Check.ino @@ -233,7 +233,6 @@ uint8_t ethernetMACAddress[6]; // Display this address when Ethernet is enabled, #define platformPrefix platformPrefixTable[productVariant] // Sets the prefix for broadcast names -bool zedUartPassed = false; //Goes true during testing if ESP can communicate with ZED over UART //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void setup() diff --git a/Firmware/Test Sketches/System_Check/menuSystem.ino b/Firmware/Test Sketches/System_Check/menuSystem.ino index 4ca21dfff..77ec6bd61 100644 --- a/Firmware/Test Sketches/System_Check/menuSystem.ino +++ b/Firmware/Test Sketches/System_Check/menuSystem.ino @@ -236,39 +236,3 @@ void printCurrentConditions() } } -void testGNSS() -{ - //The following ZED test blocks the usage of UART1 for bootloading. - //Verify the ESP UART2 can communicate TX/RX to ZED UART1 - if (online.gnss == true) - { - if (zedUartPassed == false) - { - //stopUART2Tasks(); //Stop absoring ZED serial via task - - theGNSS.setSerialRate(460800, COM_PORT_UART1); //Defaults to 460800 to maximize message output support - serialGNSS.begin(460800); //UART2 on pins 16/17 for SPP. The ZED-F9P will be configured to output NMEA over its UART1 at the same rate. - - SFE_UBLOX_GNSS myGNSS; - if (myGNSS.begin(serialGNSS) == true) //begin() attempts 3 connections - { - zedUartPassed = true; - Serial.print(F("Online")); - } - else - Serial.print(F("Offline")); - - theGNSS.setSerialRate(settings.dataPortBaud, COM_PORT_UART1); //Defaults to 460800 to maximize message output support - serialGNSS.begin(settings.dataPortBaud); //UART2 on pins 16/17 for SPP. The ZED-F9P will be configured to output NMEA over its UART1 at the same rate. - - //startUART2Tasks(); //Return to normal operation - } - else - Serial.print(F("Online")); - } - else - { - Serial.print("Can't check (GNSS offline)"); - } - Serial.println(); -}