mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-23 11:10:52 +00:00
191 lines
8.8 KiB
C++
191 lines
8.8 KiB
C++
|
|
#include "UIRenderer.h"
|
||
|
|
#include "GPSStatus.h"
|
||
|
|
#include "configuration.h"
|
||
|
|
#include "gps/GeoCoord.h"
|
||
|
|
#include "graphics/ScreenFonts.h"
|
||
|
|
#include "graphics/SharedUIDisplay.h"
|
||
|
|
#include "graphics/images.h"
|
||
|
|
#include <OLEDDisplay.h>
|
||
|
|
|
||
|
|
#if !MESHTASTIC_EXCLUDE_GPS
|
||
|
|
|
||
|
|
namespace graphics
|
||
|
|
{
|
||
|
|
|
||
|
|
// GeoCoord object for coordinate conversions
|
||
|
|
extern GeoCoord geoCoord;
|
||
|
|
|
||
|
|
// Threshold values for the GPS lock accuracy bar display
|
||
|
|
extern uint32_t dopThresholds[5];
|
||
|
|
|
||
|
|
namespace UIRenderer
|
||
|
|
{
|
||
|
|
|
||
|
|
// Draw GPS status summary
|
||
|
|
void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
|
||
|
|
{
|
||
|
|
if (config.position.fixed_position) {
|
||
|
|
// GPS coordinates are currently fixed
|
||
|
|
display->drawString(x - 1, y - 2, "Fixed GPS");
|
||
|
|
if (config.display.heading_bold)
|
||
|
|
display->drawString(x, y - 2, "Fixed GPS");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!gps->getIsConnected()) {
|
||
|
|
display->drawString(x, y - 2, "No GPS");
|
||
|
|
if (config.display.heading_bold)
|
||
|
|
display->drawString(x + 1, y - 2, "No GPS");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Adjust position if we're going to draw too wide
|
||
|
|
int maxDrawWidth = 6; // Position icon
|
||
|
|
|
||
|
|
if (!gps->getHasLock()) {
|
||
|
|
maxDrawWidth += display->getStringWidth("No sats") + 2; // icon + text + buffer
|
||
|
|
} else {
|
||
|
|
maxDrawWidth += (5 * 2) + 8 + display->getStringWidth("99") + 2; // bars + sat icon + text + buffer
|
||
|
|
}
|
||
|
|
|
||
|
|
if (x + maxDrawWidth > display->getWidth()) {
|
||
|
|
x = display->getWidth() - maxDrawWidth;
|
||
|
|
if (x < 0)
|
||
|
|
x = 0; // Clamp to screen
|
||
|
|
}
|
||
|
|
|
||
|
|
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
||
|
|
if (!gps->getHasLock()) {
|
||
|
|
// Draw "No sats" to the right of the icon with slightly more gap
|
||
|
|
int textX = x + 9; // 6 (icon) + 3px spacing
|
||
|
|
display->drawString(textX, y - 3, "No sats");
|
||
|
|
if (config.display.heading_bold)
|
||
|
|
display->drawString(textX + 1, y - 3, "No sats");
|
||
|
|
return;
|
||
|
|
} else {
|
||
|
|
char satsString[3];
|
||
|
|
uint8_t bar[2] = {0};
|
||
|
|
|
||
|
|
// Draw DOP signal bars
|
||
|
|
for (int i = 0; i < 5; i++) {
|
||
|
|
if (gps->getDOP() <= dopThresholds[i])
|
||
|
|
bar[0] = ~((1 << (5 - i)) - 1);
|
||
|
|
else
|
||
|
|
bar[0] = 0b10000000;
|
||
|
|
|
||
|
|
display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Draw satellite image
|
||
|
|
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||
|
|
|
||
|
|
// Draw the number of satellites
|
||
|
|
snprintf(satsString, sizeof(satsString), "%u", gps->getNumSatellites());
|
||
|
|
int textX = x + 34;
|
||
|
|
display->drawString(textX, y - 2, satsString);
|
||
|
|
if (config.display.heading_bold)
|
||
|
|
display->drawString(textX + 1, y - 2, satsString);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Draw status when GPS is disabled or not present
|
||
|
|
void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
|
||
|
|
{
|
||
|
|
String displayLine;
|
||
|
|
int pos;
|
||
|
|
if (y < FONT_HEIGHT_SMALL) { // Line 1: use short string
|
||
|
|
displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "No GPS" : "GPS off";
|
||
|
|
pos = display->getWidth() - display->getStringWidth(displayLine);
|
||
|
|
} else {
|
||
|
|
displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "GPS not present"
|
||
|
|
: "GPS is disabled";
|
||
|
|
pos = (display->getWidth() - display->getStringWidth(displayLine)) / 2;
|
||
|
|
}
|
||
|
|
display->drawString(x + pos, y, displayLine);
|
||
|
|
}
|
||
|
|
|
||
|
|
void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
|
||
|
|
{
|
||
|
|
String displayLine = "";
|
||
|
|
if (!gps->getIsConnected() && !config.position.fixed_position) {
|
||
|
|
// displayLine = "No GPS Module";
|
||
|
|
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
||
|
|
} else if (!gps->getHasLock() && !config.position.fixed_position) {
|
||
|
|
// displayLine = "No GPS Lock";
|
||
|
|
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
||
|
|
} else {
|
||
|
|
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
|
||
|
|
displayLine = "Altitude: " + String(geoCoord.getAltitude()) + "m";
|
||
|
|
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
|
||
|
|
displayLine = "Altitude: " + String(geoCoord.getAltitude() * METERS_TO_FEET) + "ft";
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Draw GPS status coordinates
|
||
|
|
void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
|
||
|
|
{
|
||
|
|
auto gpsFormat = config.display.gps_format;
|
||
|
|
String displayLine = "";
|
||
|
|
|
||
|
|
if (!gps->getIsConnected() && !config.position.fixed_position) {
|
||
|
|
displayLine = "No GPS present";
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
||
|
|
} else if (!gps->getHasLock() && !config.position.fixed_position) {
|
||
|
|
displayLine = "No GPS Lock";
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
|
||
|
|
} else {
|
||
|
|
|
||
|
|
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
|
||
|
|
|
||
|
|
if (gpsFormat != meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) {
|
||
|
|
char coordinateLine[22];
|
||
|
|
if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
|
||
|
|
snprintf(coordinateLine, sizeof(coordinateLine), "%f %f", geoCoord.getLatitude() * 1e-7,
|
||
|
|
geoCoord.getLongitude() * 1e-7);
|
||
|
|
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
|
||
|
|
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %06u %07u", geoCoord.getUTMZone(), geoCoord.getUTMBand(),
|
||
|
|
geoCoord.getUTMEasting(), geoCoord.getUTMNorthing());
|
||
|
|
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
|
||
|
|
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %1c%1c %05u %05u", geoCoord.getMGRSZone(),
|
||
|
|
geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k(),
|
||
|
|
geoCoord.getMGRSEasting(), geoCoord.getMGRSNorthing());
|
||
|
|
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OLC) { // Open Location Code
|
||
|
|
geoCoord.getOLCCode(coordinateLine);
|
||
|
|
} else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
|
||
|
|
if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') // OSGR is only valid around the UK region
|
||
|
|
snprintf(coordinateLine, sizeof(coordinateLine), "%s", "Out of Boundary");
|
||
|
|
else
|
||
|
|
snprintf(coordinateLine, sizeof(coordinateLine), "%1c%1c %05u %05u", geoCoord.getOSGRE100k(),
|
||
|
|
geoCoord.getOSGRN100k(), geoCoord.getOSGREasting(), geoCoord.getOSGRNorthing());
|
||
|
|
}
|
||
|
|
|
||
|
|
// If fixed position, display text "Fixed GPS" alternating with the coordinates.
|
||
|
|
if (config.position.fixed_position) {
|
||
|
|
if ((millis() / 10000) % 2) {
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y,
|
||
|
|
coordinateLine);
|
||
|
|
} else {
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth("Fixed GPS"))) / 2, y, "Fixed GPS");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y, coordinateLine);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
char latLine[22];
|
||
|
|
char lonLine[22];
|
||
|
|
snprintf(latLine, sizeof(latLine), "%2i° %2i' %2u\" %1c", geoCoord.getDMSLatDeg(), geoCoord.getDMSLatMin(),
|
||
|
|
geoCoord.getDMSLatSec(), geoCoord.getDMSLatCP());
|
||
|
|
snprintf(lonLine, sizeof(lonLine), "%3i° %2i' %2u\" %1c", geoCoord.getDMSLonDeg(), geoCoord.getDMSLonMin(),
|
||
|
|
geoCoord.getDMSLonSec(), geoCoord.getDMSLonCP());
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth(latLine))) / 2, y - FONT_HEIGHT_SMALL * 1,
|
||
|
|
latLine);
|
||
|
|
display->drawString(x + (display->getWidth() - (display->getStringWidth(lonLine))) / 2, y, lonLine);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace UIRenderer
|
||
|
|
|
||
|
|
} // namespace graphics
|
||
|
|
|
||
|
|
#endif // !MESHTASTIC_EXCLUDE_GPS
|