From cda423acab5aa08b969722d9e4124bab1047db1c Mon Sep 17 00:00:00 2001 From: Professr Date: Fri, 3 Jul 2020 02:53:56 -0700 Subject: [PATCH] Changed GPS DOP display to bars, added satellites display and compass rose --- src/GPSStatus.h | 24 +++++++- src/gps/GPS.h | 2 + src/gps/NEMAGPS.cpp | 12 +++- src/gps/UBloxGPS.cpp | 4 +- src/screen.cpp | 140 +++++++++++++++++++++++++------------------ 5 files changed, 116 insertions(+), 66 deletions(-) diff --git a/src/GPSStatus.h b/src/GPSStatus.h index 8daf97cd..ed9e0fbc 100644 --- a/src/GPSStatus.h +++ b/src/GPSStatus.h @@ -17,13 +17,15 @@ namespace meshtastic { int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double int32_t altitude = 0; uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use) + uint32_t heading = 0; + uint32_t numSatellites = 0; public: GPSStatus() { statusType = STATUS_TYPE_GPS; } - GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop ) : Status() + GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop, uint32_t heading, uint32_t numSatellites ) : Status() { this->hasLock = hasLock; this->isConnected = isConnected; @@ -31,6 +33,8 @@ namespace meshtastic { this->longitude = longitude; this->altitude = altitude; this->dop = dop; + this->heading = heading; + this->numSatellites = numSatellites; } GPSStatus(const GPSStatus &); GPSStatus &operator=(const GPSStatus &); @@ -70,6 +74,16 @@ namespace meshtastic { return dop; } + uint32_t getHeading() const + { + return heading; + } + + uint32_t getNumSatellites() const + { + return numSatellites; + } + bool matches(const GPSStatus *newStatus) const { return ( @@ -78,7 +92,9 @@ namespace meshtastic { newStatus->latitude != latitude || newStatus->longitude != longitude || newStatus->altitude != altitude || - newStatus->dop != dop + newStatus->dop != dop || + newStatus->heading != heading || + newStatus->numSatellites != numSatellites ); } int updateStatus(const GPSStatus *newStatus) { @@ -93,9 +109,11 @@ namespace meshtastic { longitude = newStatus->longitude; altitude = newStatus->altitude; dop = newStatus->dop; + heading = newStatus->heading; + numSatellites = newStatus->numSatellites; } if(isDirty) { - DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2); + DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5, numSatellites); onNewStatus.notifyObservers(this); } return 0; diff --git a/src/gps/GPS.h b/src/gps/GPS.h index c8b55eb2..5c52e7ca 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -36,6 +36,8 @@ class GPS : public Observable int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double int32_t altitude = 0; uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use) + uint32_t heading = 0; // Heading of motion, in degrees * 10^-5 + uint32_t numSatellites = 0; bool isConnected = false; // Do we have a GPS we are talking to diff --git a/src/gps/NEMAGPS.cpp b/src/gps/NEMAGPS.cpp index 4858806c..606d21ee 100644 --- a/src/gps/NEMAGPS.cpp +++ b/src/gps/NEMAGPS.cpp @@ -54,12 +54,18 @@ void NEMAGPS::loop() longitude = toDegInt(loc.lng); } // Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it - if(reader.hdop.isValid()) { + if (reader.hdop.isValid()) { dop = reader.hdop.value(); } + if (reader.course.isValid()) { + heading = reader.course.value() * 1e3; //Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5 + } + if (reader.satellites.isValid()) { + numSatellites = reader.satellites.value(); + } // expect gps pos lat=37.520825, lon=-122.309162, alt=158 - DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2); + DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5); hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0 if (hasValidLocation) @@ -67,7 +73,7 @@ void NEMAGPS::loop() } // Notify any status instances that are observing us - const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop); + const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites); newStatus.notifyObservers(&status); } } \ No newline at end of file diff --git a/src/gps/UBloxGPS.cpp b/src/gps/UBloxGPS.cpp index 7c940983..6c266067 100644 --- a/src/gps/UBloxGPS.cpp +++ b/src/gps/UBloxGPS.cpp @@ -116,6 +116,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s longitude = ublox.getLongitude(0); altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it + heading = ublox.getHeading(0); + numSatellites = ublox.getSIV(0); // bogus lat lon is reported as 0 or 0 (can be bogus just for one) // Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg! @@ -129,7 +131,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s wantNewLocation = true; // Notify any status instances that are observing us - const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop); + const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites); newStatus.notifyObservers(&status); // Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over diff --git a/src/screen.cpp b/src/screen.cpp index 74f0065d..11cee0ba 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -55,8 +55,10 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES]; static uint32_t targetFramerate = IDLE_FRAMERATE; static char btPIN[16] = "888888"; -uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C }; -static bool heartbeat = false; +uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C }; +uint8_t imgSatellite[8] = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 }; + +uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 }; static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -210,38 +212,39 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no // Draw GPS status summary static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) { - if (!gps->getIsConnected()) { + if (!gps->getIsConnected()) + { display->drawString(x, y - 2, "No GPS"); return; } display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty); - if (!gps->getHasLock()) { + if (!gps->getHasLock()) + { display->drawString(x + 8, y - 2, "No sats"); return; - } - if (gps->getDOP() <= 100) { - display->drawString(x + 8, y - 2, "Ideal"); - return; - } - if (gps->getDOP() <= 200) { - display->drawString(x + 8, y - 2, "Exc."); - return; - } - if (gps->getDOP() <= 500) { - display->drawString(x + 8, y - 2, "Good"); - return; - } - if (gps->getDOP() <= 1000) { - display->drawString(x + 8, y - 2, "Mod."); - return; - } - if (gps->getDOP() <= 2000) { - display->drawString(x + 8, y - 2, "Fair"); - return; - } - if (gps->getDOP() > 0) { - display->drawString(x + 8, y - 2, "Poor"); - 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; + //bar[1] = bar[0]; + 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 + sprintf(satsString, "%d", gps->getNumSatellites()); + display->drawString(x + 34, y - 2, satsString); } } @@ -384,28 +387,41 @@ static bool hasPosition(NodeInfo *n) static size_t nodeIndex; static int8_t prevFrame = -1; -// Draw the compass and arrow pointing to location -static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian) +// Draw the arrow pointing to a node's location +static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian) { - // display->drawXbm(compassX, compassY, compass_width, compass_height, - // (const uint8_t *)compass_bits); - Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f; Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY); - Point *points[] = {&tip, &tail, &leftArrow, &rightArrow}; + + Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow}; for (int i = 0; i < 4; i++) { - points[i]->rotate(headingRadian); - points[i]->scale(COMPASS_DIAM * 0.6); - points[i]->translate(compassX, compassY); + arrowPoints[i]->rotate(headingRadian); + arrowPoints[i]->scale(COMPASS_DIAM * 0.6); + arrowPoints[i]->translate(compassX, compassY); } drawLine(display, tip, tail); drawLine(display, leftArrow, tip); drawLine(display, rightArrow, tip); +} - display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); +// Draw the compass heading +static void drawCompassHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading) +{ + Point N1(-0.04f, -0.65f), N2( 0.04f, -0.65f); + Point N3(-0.04f, -0.55f), N4( 0.04f, -0.55f); + Point *rosePoints[] = {&N1, &N2, &N3, &N4}; + + for (int i = 0; i < 4; i++) { + rosePoints[i]->rotate(myHeading); + rosePoints[i]->scale(COMPASS_DIAM); + rosePoints[i]->translate(compassX, compassY); + } + drawLine(display, N1, N3); + drawLine(display, N2, N4); + drawLine(display, N1, N4); } /// Convert an integer GPS coords to a floating point @@ -459,29 +475,35 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ const char *fields[] = {username, distStr, signalStr, lastStr, NULL}; // coordinates for the center of the compass/circle - int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2; + int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2; - if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node - Position &op = ourNode->position, &p = node->position; - float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - if (d < 2000) - snprintf(distStr, sizeof(distStr), "%.0f m", d); - else - snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); + if(ourNode && hasPosition(ourNode)) + { + Position &op = ourNode->position; + float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); + drawCompassHeading(display, compassX, compassY, myHeading); - // FIXME, also keep the guess at the operators heading and add/substract - // it. currently we don't do this and instead draw north up only. - float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i)); - headingRadian = bearingToOther - myHeading; - drawCompass(display, compassX, compassY, headingRadian); - } else { // direction to node is unknown so display question mark - // Debug info for gps lock errors - // DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node)); + if(hasPosition(node)) { // display direction toward node + Position &p = node->position; + float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + if (d < 2000) + snprintf(distStr, sizeof(distStr), "%.0f m", d); + else + snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); - display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?"); - display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); + // FIXME, also keep the guess at the operators heading and add/substract + // it. currently we don't do this and instead draw north up only. + float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + headingRadian = bearingToOther - myHeading; + drawNodeHeading(display, compassX, compassY, headingRadian); + } else { // direction to node is unknown so display question mark + // Debug info for gps lock errors + // DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node)); + display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?"); + } } + display->drawCircle(compassX, compassY, COMPASS_DIAM / 2); + // Must be after distStr is populated drawColumns(display, x, y, fields); @@ -758,7 +780,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Display nodes status drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus); // Display GPS status - drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gpsStatus); + drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); } display->drawString(x, y + FONT_HEIGHT, channelStr); @@ -787,7 +809,7 @@ void Screen::adjustBrightness() } int Screen::handleStatusUpdate(const Status *arg) { - DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); + //DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); switch(arg->getStatusType()) { case STATUS_TYPE_NODE: