2022-02-27 08:18:35 +00:00
|
|
|
#include "PositionModule.h"
|
2020-12-03 08:48:44 +00:00
|
|
|
#include "MeshService.h"
|
2020-11-28 05:51:51 +00:00
|
|
|
#include "NodeDB.h"
|
2020-12-03 08:48:44 +00:00
|
|
|
#include "RTC.h"
|
|
|
|
#include "Router.h"
|
2022-01-27 06:32:33 +00:00
|
|
|
#include "airtime.h"
|
2021-11-29 03:41:34 +00:00
|
|
|
#include "configuration.h"
|
2021-11-27 03:12:00 +00:00
|
|
|
#include "gps/GeoCoord.h"
|
|
|
|
|
2022-02-27 10:21:02 +00:00
|
|
|
PositionModule *positionModule;
|
2020-11-28 05:51:51 +00:00
|
|
|
|
2022-02-27 10:21:02 +00:00
|
|
|
PositionModule::PositionModule()
|
2022-03-09 08:01:43 +00:00
|
|
|
: ProtobufModule("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionModule")
|
2021-02-14 04:26:51 +00:00
|
|
|
{
|
2021-03-27 08:17:01 +00:00
|
|
|
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
|
|
|
|
setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
|
2021-02-14 04:26:51 +00:00
|
|
|
}
|
|
|
|
|
2022-02-27 10:21:02 +00:00
|
|
|
bool PositionModule::handleReceivedProtobuf(const MeshPacket &mp, Position *pptr)
|
2020-11-28 05:51:51 +00:00
|
|
|
{
|
2021-02-17 11:04:41 +00:00
|
|
|
auto p = *pptr;
|
2020-12-03 08:48:44 +00:00
|
|
|
|
2021-10-28 11:25:45 +00:00
|
|
|
// If inbound message is a replay (or spoof!) of our own messages, we shouldn't process
|
2021-10-26 12:41:44 +00:00
|
|
|
// (why use second-hand sources for our own data?)
|
2021-10-28 11:25:45 +00:00
|
|
|
|
|
|
|
// FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER)
|
|
|
|
// to set fixed location, EUD-GPS location or just the time (see also issue #900)
|
2021-10-26 12:41:44 +00:00
|
|
|
if (nodeDB.getNodeNum() == getFrom(&mp)) {
|
2021-10-28 11:25:45 +00:00
|
|
|
DEBUG_MSG("Incoming update from MYSELF\n");
|
|
|
|
// DEBUG_MSG("Ignored an incoming update from MYSELF\n");
|
|
|
|
// return false;
|
2021-10-26 12:41:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Log packet size and list of fields
|
2022-03-20 14:55:38 +00:00
|
|
|
DEBUG_MSG("POSITION node=%08x l=%d %s%s%s%s%s%s%s%s%s%s%s%s%s\n", getFrom(&mp), mp.decoded.payload.size,
|
2021-11-29 03:41:34 +00:00
|
|
|
p.latitude_i ? "LAT " : "", p.longitude_i ? "LON " : "", p.altitude ? "MSL " : "", p.altitude_hae ? "HAE " : "",
|
2022-09-12 12:55:17 +00:00
|
|
|
p.altitude_geoidal_separation ? "GEO " : "", p.PDOP ? "PDOP " : "", p.HDOP ? "HDOP " : "", p.VDOP ? "VDOP " : "",
|
2022-09-09 10:51:41 +00:00
|
|
|
p.sats_in_view ? "SIV " : "", p.fix_quality ? "FXQ " : "", p.fix_type ? "FXT " : "", p.timestamp ? "PTS " : "",
|
2022-03-20 14:55:38 +00:00
|
|
|
p.time ? "TIME " : "");
|
2021-10-26 12:41:44 +00:00
|
|
|
|
2020-12-05 02:00:46 +00:00
|
|
|
if (p.time) {
|
|
|
|
struct timeval tv;
|
|
|
|
uint32_t secs = p.time;
|
|
|
|
|
|
|
|
tv.tv_sec = secs;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
|
|
|
perhapsSetRTC(RTCQualityFromNet, &tv);
|
|
|
|
}
|
|
|
|
|
2021-03-05 02:19:27 +00:00
|
|
|
nodeDB.updatePosition(getFrom(&mp), p);
|
2020-11-28 05:51:51 +00:00
|
|
|
|
|
|
|
return false; // Let others look at this message also if they want
|
|
|
|
}
|
2020-12-03 08:48:44 +00:00
|
|
|
|
2022-02-27 10:21:02 +00:00
|
|
|
MeshPacket *PositionModule::allocReply()
|
2020-12-03 08:48:44 +00:00
|
|
|
{
|
2021-01-04 01:59:53 +00:00
|
|
|
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
|
|
|
assert(node->has_position);
|
2021-02-14 03:37:32 +00:00
|
|
|
|
2022-10-03 18:30:11 +00:00
|
|
|
node->position.seq_number++;
|
|
|
|
|
2021-10-24 00:10:36 +00:00
|
|
|
// configuration of POSITION packet
|
|
|
|
// consider making this a function argument?
|
2022-05-21 20:38:33 +00:00
|
|
|
uint32_t pos_flags = config.position.position_flags;
|
2021-10-24 00:10:36 +00:00
|
|
|
|
|
|
|
// Populate a Position struct with ONLY the requested fields
|
2021-11-29 03:41:34 +00:00
|
|
|
Position p = Position_init_default; // Start with an empty structure
|
2021-10-24 00:10:36 +00:00
|
|
|
|
|
|
|
// lat/lon are unconditionally included - IF AVAILABLE!
|
|
|
|
p.latitude_i = node->position.latitude_i;
|
|
|
|
p.longitude_i = node->position.longitude_i;
|
|
|
|
p.time = node->position.time;
|
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_ALTITUDE) {
|
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_ALTITUDE_MSL)
|
2021-10-24 00:10:36 +00:00
|
|
|
p.altitude = node->position.altitude;
|
|
|
|
else
|
|
|
|
p.altitude_hae = node->position.altitude_hae;
|
2022-09-12 07:37:21 +00:00
|
|
|
|
2022-09-12 12:55:17 +00:00
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION)
|
|
|
|
p.altitude_geoidal_separation = node->position.altitude_geoidal_separation;
|
2021-10-24 00:10:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_DOP) {
|
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_HVDOP) {
|
2021-10-24 00:10:36 +00:00
|
|
|
p.HDOP = node->position.HDOP;
|
|
|
|
p.VDOP = node->position.VDOP;
|
|
|
|
} else
|
|
|
|
p.PDOP = node->position.PDOP;
|
|
|
|
}
|
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_SATINVIEW)
|
2021-10-24 00:10:36 +00:00
|
|
|
p.sats_in_view = node->position.sats_in_view;
|
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_TIMESTAMP)
|
|
|
|
p.timestamp = node->position.timestamp;
|
2021-10-24 00:10:36 +00:00
|
|
|
|
2022-09-27 12:03:02 +00:00
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_SEQ_NO)
|
|
|
|
p.seq_number = node->position.seq_number;
|
|
|
|
|
2022-10-03 18:30:11 +00:00
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_HEADING)
|
|
|
|
p.ground_track = node->position.ground_track;
|
|
|
|
|
|
|
|
if (pos_flags & Config_PositionConfig_PositionFlags_SPEED)
|
|
|
|
p.ground_speed = node->position.ground_speed;
|
|
|
|
|
2021-02-14 03:37:32 +00:00
|
|
|
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
|
|
|
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
|
|
|
|
// devices can get time.
|
|
|
|
if (getRTCQuality() < RTCQualityGPS) {
|
|
|
|
DEBUG_MSG("Stripping time %u from position send\n", p.time);
|
|
|
|
p.time = 0;
|
|
|
|
} else
|
|
|
|
DEBUG_MSG("Providing time to mesh %u\n", p.time);
|
|
|
|
|
2022-08-07 20:08:46 +00:00
|
|
|
DEBUG_MSG("Position reply: time=%i, latI=%i, lonI=-%i\n", p.time, p.latitude_i, p.longitude_i);
|
|
|
|
|
2021-02-14 03:37:32 +00:00
|
|
|
return allocDataProtobuf(p);
|
2020-12-07 02:18:11 +00:00
|
|
|
}
|
|
|
|
|
2022-02-27 10:21:02 +00:00
|
|
|
void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies)
|
2020-12-07 02:18:11 +00:00
|
|
|
{
|
2021-02-11 09:39:53 +00:00
|
|
|
// cancel any not yet sent (now stale) position packets
|
2021-02-14 03:37:32 +00:00
|
|
|
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
2021-02-11 09:39:53 +00:00
|
|
|
service.cancelSending(prevPacketId);
|
2021-02-11 11:00:17 +00:00
|
|
|
|
2020-12-07 02:18:11 +00:00
|
|
|
MeshPacket *p = allocReply();
|
2020-12-03 08:48:44 +00:00
|
|
|
p->to = dest;
|
|
|
|
p->decoded.want_response = wantReplies;
|
2021-02-11 11:00:17 +00:00
|
|
|
p->priority = MeshPacket_Priority_BACKGROUND;
|
2021-02-11 09:39:53 +00:00
|
|
|
prevPacketId = p->id;
|
2020-12-03 08:48:44 +00:00
|
|
|
|
2022-04-27 00:40:24 +00:00
|
|
|
service.sendToMesh(p, RX_SRC_LOCAL, true);
|
2020-12-03 08:48:44 +00:00
|
|
|
}
|
2021-02-14 03:57:48 +00:00
|
|
|
|
2022-02-27 10:21:02 +00:00
|
|
|
int32_t PositionModule::runOnce()
|
2021-02-14 04:26:51 +00:00
|
|
|
{
|
2021-10-26 12:41:44 +00:00
|
|
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
2021-11-29 03:41:34 +00:00
|
|
|
|
2021-02-14 03:57:48 +00:00
|
|
|
// We limit our GPS broadcasts to a max rate
|
|
|
|
uint32_t now = millis();
|
2022-08-24 22:29:09 +00:00
|
|
|
uint32_t intervalMs = config.position.position_broadcast_secs > 0 ? config.position.position_broadcast_secs * 1000 : default_broadcast_interval_secs * 1000;
|
|
|
|
if (lastGpsSend == 0 || (now - lastGpsSend) >= intervalMs) {
|
2021-10-26 12:41:44 +00:00
|
|
|
|
2022-01-27 06:32:33 +00:00
|
|
|
// Only send packets if the channel is less than 40% utilized.
|
2022-10-11 15:21:30 +00:00
|
|
|
if (airTime->channelUtilizationPercent() < max_channel_util_percent) {
|
2022-07-30 12:36:21 +00:00
|
|
|
if (node->has_position && (node->position.latitude_i != 0 || node->position.longitude_i != 0)) {
|
|
|
|
lastGpsSend = now;
|
2021-02-14 03:57:48 +00:00
|
|
|
|
2022-07-30 12:36:21 +00:00
|
|
|
lastGpsLatitude = node->position.latitude_i;
|
|
|
|
lastGpsLongitude = node->position.longitude_i;
|
2021-11-27 03:12:00 +00:00
|
|
|
|
2022-07-30 12:36:21 +00:00
|
|
|
// If we changed channels, ask everyone else for their latest info
|
|
|
|
bool requestReplies = currentGeneration != radioGeneration;
|
|
|
|
currentGeneration = radioGeneration;
|
2022-01-27 06:32:33 +00:00
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
DEBUG_MSG("Sending pos@%x:6 to mesh (wantReplies=%d)\n", node->position.timestamp, requestReplies);
|
2022-07-30 12:36:21 +00:00
|
|
|
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
|
|
|
}
|
2022-01-27 06:32:33 +00:00
|
|
|
} else {
|
2022-07-30 12:36:21 +00:00
|
|
|
DEBUG_MSG("Channel utilization is >40 percent. Skipping this opportunity to send.\n");
|
2022-01-27 06:32:33 +00:00
|
|
|
}
|
2021-02-14 03:57:48 +00:00
|
|
|
|
2022-09-11 13:36:47 +00:00
|
|
|
} else if (config.position.position_broadcast_smart_enabled) {
|
2021-11-27 05:26:36 +00:00
|
|
|
|
2022-03-15 01:09:11 +00:00
|
|
|
// Only send packets if the channel is less than 25% utilized.
|
2022-10-11 15:21:30 +00:00
|
|
|
if (airTime->channelUtilizationPercent() < polite_channel_util_percent) {
|
2022-01-27 06:32:33 +00:00
|
|
|
|
|
|
|
NodeInfo *node2 = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
|
|
|
|
|
|
|
if (node2->has_position && (node2->position.latitude_i != 0 || node2->position.longitude_i != 0)) {
|
|
|
|
// The minimum distance to travel before we are able to send a new position packet.
|
|
|
|
const uint32_t distanceTravelMinimum = 30;
|
2021-12-15 03:38:54 +00:00
|
|
|
|
2022-01-27 06:32:33 +00:00
|
|
|
// The minimum time that would pass before we are able to send a new position packet.
|
|
|
|
const uint32_t timeTravelMinimum = 30;
|
2021-12-15 03:38:54 +00:00
|
|
|
|
2022-01-27 06:32:33 +00:00
|
|
|
// Determine the distance in meters between two points on the globe
|
|
|
|
float distance = GeoCoord::latLongToMeter(lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7,
|
|
|
|
node->position.latitude_i * 1e-7, node->position.longitude_i * 1e-7);
|
2021-11-27 03:12:00 +00:00
|
|
|
|
2022-01-27 06:32:33 +00:00
|
|
|
// Yes, this has a bunch of magic numbers. Sorry. This is to make the scale non-linear.
|
|
|
|
const float distanceTravelMath = 1203 / (sqrt(pow(myNodeInfo.bitrate, 1.5) / 1.1));
|
2022-07-11 18:18:02 +00:00
|
|
|
uint32_t distanceTravelThreshold =
|
2022-01-27 06:32:33 +00:00
|
|
|
(distanceTravelMath >= distanceTravelMinimum) ? distanceTravelMath : distanceTravelMinimum;
|
2021-11-27 03:12:00 +00:00
|
|
|
|
2022-01-27 06:32:33 +00:00
|
|
|
// Yes, this has a bunch of magic numbers. Sorry.
|
|
|
|
uint32_t timeTravel =
|
|
|
|
((1500 / myNodeInfo.bitrate) >= timeTravelMinimum) ? (1500 / myNodeInfo.bitrate) : timeTravelMinimum;
|
2022-07-16 14:57:35 +00:00
|
|
|
// If the distance traveled since the last update is greater than distanceTravelMinimum meters
|
|
|
|
// and it's been at least timeTravelMinimum seconds since the last update
|
2022-07-11 18:18:02 +00:00
|
|
|
if ((abs(distance) >= distanceTravelThreshold) && (now - lastGpsSend) >= (timeTravel * 1000)) {
|
2022-01-27 06:32:33 +00:00
|
|
|
bool requestReplies = currentGeneration != radioGeneration;
|
|
|
|
currentGeneration = radioGeneration;
|
2021-11-27 03:12:00 +00:00
|
|
|
|
2022-09-09 10:51:41 +00:00
|
|
|
DEBUG_MSG("Sending smart pos@%x:6 to mesh (wantReplies=%d, d=%d, dtt=%d, tt=%d)\n", node2->position.timestamp,
|
2022-07-11 18:18:02 +00:00
|
|
|
requestReplies, distance, distanceTravelThreshold, timeTravel);
|
2022-01-27 06:32:33 +00:00
|
|
|
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
2021-12-02 01:24:01 +00:00
|
|
|
|
2022-07-11 18:18:02 +00:00
|
|
|
// Set the current coords as our last ones, after we've compared distance with current and decided to send
|
|
|
|
lastGpsLatitude = node->position.latitude_i;
|
|
|
|
lastGpsLongitude = node->position.longitude_i;
|
|
|
|
|
2022-01-27 06:32:33 +00:00
|
|
|
/* Update lastGpsSend to now. This means if the device is stationary, then
|
|
|
|
getPref_position_broadcast_secs will still apply.
|
|
|
|
*/
|
|
|
|
lastGpsSend = now;
|
|
|
|
}
|
2021-11-27 03:12:00 +00:00
|
|
|
}
|
2022-01-27 06:32:33 +00:00
|
|
|
} else {
|
2022-03-15 01:09:11 +00:00
|
|
|
DEBUG_MSG("Channel utilization is >25 percent. Skipping this opportunity to send.\n");
|
2021-11-27 03:12:00 +00:00
|
|
|
}
|
2021-02-14 03:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 5000; // to save power only wake for our callback occasionally
|
2022-01-24 18:39:17 +00:00
|
|
|
}
|