2024-03-25 05:33:57 -06:00
|
|
|
#if !MESHTASTIC_EXCLUDE_GPS
|
2022-02-27 00:18:35 -08:00
|
|
|
#include "PositionModule.h"
|
2024-03-17 08:18:30 -05:00
|
|
|
#include "Default.h"
|
2023-09-30 21:09:17 -05:00
|
|
|
#include "GPS.h"
|
2020-12-03 16:48:44 +08:00
|
|
|
#include "MeshService.h"
|
2020-11-28 13:51:51 +08:00
|
|
|
#include "NodeDB.h"
|
2020-12-03 16:48:44 +08:00
|
|
|
#include "RTC.h"
|
|
|
|
|
#include "Router.h"
|
2023-06-17 09:10:09 -05:00
|
|
|
#include "TypeConversions.h"
|
2022-01-26 22:32:33 -08:00
|
|
|
#include "airtime.h"
|
2021-11-28 19:41:34 -08:00
|
|
|
#include "configuration.h"
|
2021-11-26 19:12:00 -08:00
|
|
|
#include "gps/GeoCoord.h"
|
2024-02-16 20:04:21 -06:00
|
|
|
#include "main.h"
|
2024-07-23 06:16:53 -05:00
|
|
|
#include "mesh/compression/unishox2.h"
|
2024-09-25 13:50:00 -05:00
|
|
|
#include "meshUtils.h"
|
2024-02-16 20:04:21 -06:00
|
|
|
#include "meshtastic/atak.pb.h"
|
2023-09-30 21:09:17 -05:00
|
|
|
#include "sleep.h"
|
|
|
|
|
#include "target_specific.h"
|
2024-04-21 07:42:36 -05:00
|
|
|
#include <Throttle.h>
|
2024-02-16 20:04:21 -06:00
|
|
|
|
2022-02-27 02:21:02 -08:00
|
|
|
PositionModule *positionModule;
|
2020-11-28 13:51:51 +08:00
|
|
|
|
2022-02-27 02:21:02 -08:00
|
|
|
PositionModule::PositionModule()
|
2024-11-04 12:16:25 -06:00
|
|
|
: ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg), concurrency::OSThread("Position")
|
2021-02-14 12:26:51 +08:00
|
|
|
{
|
2024-02-22 12:32:01 -06:00
|
|
|
precision = 0; // safe starting value
|
2023-09-30 21:09:17 -05:00
|
|
|
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
|
2024-07-13 05:59:19 -05:00
|
|
|
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
|
|
|
|
|
2024-02-16 20:04:21 -06:00
|
|
|
if (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
|
2025-01-26 20:59:59 +01:00
|
|
|
config.device.role != meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) {
|
|
|
|
|
setIntervalFromNow(setStartDelay());
|
|
|
|
|
}
|
2023-09-30 21:09:17 -05:00
|
|
|
|
|
|
|
|
// Power saving trackers should clear their position on startup to avoid waking up and sending a stale position
|
2024-02-16 20:04:21 -06:00
|
|
|
if ((config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
|
|
|
|
|
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) &&
|
|
|
|
|
config.power.is_power_saving) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_DEBUG("Clear position on startup for sleepy tracker (ー。ー) zzz");
|
2024-03-21 14:43:10 -05:00
|
|
|
nodeDB->clearLocalPosition();
|
2023-09-30 21:09:17 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Position *pptr)
|
2020-11-28 13:51:51 +08:00
|
|
|
{
|
2021-02-17 19:04:41 +08:00
|
|
|
auto p = *pptr;
|
2020-12-03 16:48:44 +08: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)
|
2023-10-09 18:33:04 -05:00
|
|
|
bool isLocal = false;
|
2024-10-04 13:28:51 +02:00
|
|
|
if (isFromUs(&mp)) {
|
2023-10-09 18:33:04 -05:00
|
|
|
isLocal = true;
|
2024-03-13 20:27:26 -05:00
|
|
|
if (config.position.fixed_position) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true");
|
2024-05-26 08:04:31 -04:00
|
|
|
|
|
|
|
|
#ifdef T_WATCH_S3
|
|
|
|
|
// Since we return early if position.fixed_position is true, set the T-Watch's RTC to the time received from the
|
|
|
|
|
// client device here
|
|
|
|
|
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
|
|
|
|
|
trySetRtc(p, isLocal, true);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-03-21 09:06:37 -05:00
|
|
|
nodeDB->setLocalPosition(p, true);
|
2024-03-13 20:27:26 -05:00
|
|
|
return false;
|
|
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("Incoming update from MYSELF");
|
2024-03-21 09:06:37 -05:00
|
|
|
nodeDB->setLocalPosition(p);
|
2024-03-13 20:27:26 -05:00
|
|
|
}
|
2021-10-26 12:41:44 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-27 11:00:56 -10:00
|
|
|
// Log packet size and data fields
|
2024-06-21 17:25:54 -05:00
|
|
|
LOG_DEBUG("POSITION node=%08x l=%d lat=%d lon=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d "
|
2024-10-14 06:11:43 +02:00
|
|
|
"time=%d",
|
2024-04-21 07:42:36 -05:00
|
|
|
getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae,
|
|
|
|
|
p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp,
|
|
|
|
|
p.time);
|
2021-10-26 12:41:44 +00:00
|
|
|
|
2023-12-25 23:40:16 +01:00
|
|
|
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
|
2024-05-26 08:04:31 -04:00
|
|
|
bool force = false;
|
|
|
|
|
|
|
|
|
|
#ifdef T_WATCH_S3
|
|
|
|
|
// The T-Watch appears to "pause" its RTC when shut down, such that the time it reads upon powering on is the same as when
|
|
|
|
|
// it was shut down. So we need to force the update here, since otherwise RTC::perhapsSetRTC will ignore it because it
|
|
|
|
|
// will always be an equivalent or lesser RTCQuality (RTCQualityNTP or RTCQualityNet).
|
|
|
|
|
force = true;
|
|
|
|
|
#endif
|
2023-10-09 18:33:04 -05:00
|
|
|
// Set from phone RTC Quality to RTCQualityNTP since it should be approximately so
|
2024-05-26 08:04:31 -04:00
|
|
|
trySetRtc(p, isLocal, force);
|
2020-12-05 10:00:46 +08:00
|
|
|
}
|
|
|
|
|
|
2024-03-21 09:06:37 -05:00
|
|
|
nodeDB->updatePosition(getFrom(&mp), p);
|
2024-02-24 12:53:46 -06:00
|
|
|
if (channels.getByIndex(mp.channel).settings.has_module_settings) {
|
|
|
|
|
precision = channels.getByIndex(mp.channel).settings.module_settings.position_precision;
|
|
|
|
|
} else if (channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
|
|
|
|
|
precision = 32;
|
|
|
|
|
} else {
|
|
|
|
|
precision = 0;
|
|
|
|
|
}
|
2020-11-28 13:51:51 +08:00
|
|
|
|
|
|
|
|
return false; // Let others look at this message also if they want
|
|
|
|
|
}
|
2020-12-03 16:48:44 +08:00
|
|
|
|
2024-05-01 08:05:26 -05:00
|
|
|
void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_Position *p)
|
|
|
|
|
{
|
|
|
|
|
// Phone position packets need to be truncated to the channel precision
|
2024-10-04 13:28:51 +02:00
|
|
|
if (isFromUs(&mp) && (precision < 32 && precision > 0)) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_DEBUG("Truncate phone position to channel precision %i", precision);
|
2024-05-01 08:05:26 -05:00
|
|
|
p->latitude_i = p->latitude_i & (UINT32_MAX << (32 - precision));
|
|
|
|
|
p->longitude_i = p->longitude_i & (UINT32_MAX << (32 - precision));
|
|
|
|
|
|
|
|
|
|
// We want the imprecise position to be the middle of the possible location, not
|
|
|
|
|
p->latitude_i += (1 << (31 - precision));
|
|
|
|
|
p->longitude_i += (1 << (31 - precision));
|
|
|
|
|
|
|
|
|
|
mp.decoded.payload.size =
|
|
|
|
|
pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes), &meshtastic_Position_msg, p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-26 08:04:31 -04:00
|
|
|
void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate)
|
2024-04-21 14:40:47 -05:00
|
|
|
{
|
2024-08-13 06:56:20 -05:00
|
|
|
if (hasQualityTimesource() && !isLocal) {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_DEBUG("Ignore time from mesh because we have a GPS, RTC, or Phone/NTP time source in the past day");
|
2024-08-13 06:56:20 -05:00
|
|
|
return;
|
|
|
|
|
}
|
2024-08-30 19:02:48 -05:00
|
|
|
if (!isLocal && p.location_source < meshtastic_Position_LocSource_LOC_INTERNAL) {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_DEBUG("Ignore time from mesh because it has a unknown or manual source");
|
2024-08-30 19:02:48 -05:00
|
|
|
return;
|
|
|
|
|
}
|
2024-04-21 14:40:47 -05:00
|
|
|
struct timeval tv;
|
|
|
|
|
uint32_t secs = p.time;
|
|
|
|
|
|
|
|
|
|
tv.tv_sec = secs;
|
|
|
|
|
tv.tv_usec = 0;
|
2024-08-13 06:56:20 -05:00
|
|
|
|
2024-05-26 08:04:31 -04:00
|
|
|
perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv, forceUpdate);
|
2024-04-21 14:40:47 -05:00
|
|
|
}
|
|
|
|
|
|
2024-08-13 06:56:20 -05:00
|
|
|
bool PositionModule::hasQualityTimesource()
|
|
|
|
|
{
|
2024-08-17 17:35:05 -05:00
|
|
|
bool setFromPhoneOrNtpToday =
|
2024-09-23 08:58:14 -05:00
|
|
|
lastSetFromPhoneNtpOrGps == 0 ? false : Throttle::isWithinTimespanMs(lastSetFromPhoneNtpOrGps, SEC_PER_DAY * 1000UL);
|
2024-09-05 19:16:06 +08:00
|
|
|
#if MESHTASTIC_EXCLUDE_GPS
|
|
|
|
|
bool hasGpsOrRtc = (rtc_found.address != ScanI2C::ADDRESS_NONE.address);
|
|
|
|
|
#else
|
2024-11-20 07:52:39 -06:00
|
|
|
bool hasGpsOrRtc = hasGPS() || (rtc_found.address != ScanI2C::ADDRESS_NONE.address);
|
2024-09-05 19:16:06 +08:00
|
|
|
#endif
|
2024-08-13 06:56:20 -05:00
|
|
|
return hasGpsOrRtc || setFromPhoneOrNtpToday;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-20 07:52:39 -06:00
|
|
|
bool PositionModule::hasGPS()
|
|
|
|
|
{
|
|
|
|
|
#if MESHTASTIC_EXCLUDE_GPS
|
|
|
|
|
return false;
|
|
|
|
|
#else
|
|
|
|
|
return gps && gps->isConnected();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-25 16:24:24 +01:00
|
|
|
// Allocate a packet with our position data if we have one
|
|
|
|
|
meshtastic_MeshPacket *PositionModule::allocPositionPacket()
|
2020-12-03 16:48:44 +08:00
|
|
|
{
|
2024-02-22 12:32:01 -06:00
|
|
|
if (precision == 0) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_DEBUG("Skip location send because precision is set to 0!");
|
2023-08-28 03:28:04 -05:00
|
|
|
return nullptr;
|
2023-07-24 09:37:56 -05:00
|
|
|
}
|
|
|
|
|
|
2024-08-01 19:29:49 -05:00
|
|
|
meshtastic_NodeInfoLite *node = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
2021-01-04 09:59:53 +08:00
|
|
|
assert(node->has_position);
|
2021-02-14 11:37:32 +08:00
|
|
|
|
2021-10-24 00:10:36 +00:00
|
|
|
// configuration of POSITION packet
|
|
|
|
|
// consider making this a function argument?
|
2022-05-21 22:38:33 +02: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
|
2023-01-21 18:22:19 +01:00
|
|
|
meshtastic_Position p = meshtastic_Position_init_default; // Start with an empty structure
|
2023-10-09 18:33:04 -05:00
|
|
|
// if localPosition is totally empty, put our last saved position (lite) in there
|
2023-06-17 09:10:09 -05:00
|
|
|
if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) {
|
2024-03-21 09:06:37 -05:00
|
|
|
nodeDB->setLocalPosition(TypeConversions::ConvertToPosition(node->position));
|
2023-06-17 09:10:09 -05:00
|
|
|
}
|
|
|
|
|
localPosition.seq_number++;
|
2021-10-24 00:10:36 +00:00
|
|
|
|
2024-03-03 11:19:30 -06:00
|
|
|
if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_WARN("Skip position send because lat/lon are zero!");
|
2024-03-03 11:19:30 -06:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 00:10:36 +00:00
|
|
|
// lat/lon are unconditionally included - IF AVAILABLE!
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_DEBUG("Send location with precision %i", precision);
|
2024-02-25 21:27:32 -06:00
|
|
|
if (precision < 32 && precision > 0) {
|
2024-02-26 00:22:05 -06:00
|
|
|
p.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - precision));
|
|
|
|
|
p.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - precision));
|
2024-02-23 15:39:32 -06:00
|
|
|
|
2024-02-25 21:27:32 -06:00
|
|
|
// We want the imprecise position to be the middle of the possible location, not
|
2024-02-24 12:53:46 -06:00
|
|
|
p.latitude_i += (1 << (31 - precision));
|
|
|
|
|
p.longitude_i += (1 << (31 - precision));
|
2024-02-25 21:27:32 -06:00
|
|
|
} else {
|
|
|
|
|
p.latitude_i = localPosition.latitude_i;
|
|
|
|
|
p.longitude_i = localPosition.longitude_i;
|
2024-02-23 15:39:32 -06:00
|
|
|
}
|
2024-02-22 12:32:01 -06:00
|
|
|
p.precision_bits = precision;
|
2024-08-23 06:25:40 -05:00
|
|
|
p.has_latitude_i = true;
|
|
|
|
|
p.has_longitude_i = true;
|
2024-11-20 07:52:39 -06:00
|
|
|
// Always use NTP / GPS time if available
|
|
|
|
|
if (getValidTime(RTCQualityNTP) > 0) {
|
|
|
|
|
p.time = getValidTime(RTCQualityNTP);
|
|
|
|
|
} else if (rtc_found.address != ScanI2C::ADDRESS_NONE.address) {
|
|
|
|
|
LOG_INFO("Use RTC time for position");
|
|
|
|
|
p.time = getValidTime(RTCQualityDevice);
|
|
|
|
|
} else if (getRTCQuality() < RTCQualityNTP) {
|
|
|
|
|
LOG_INFO("Strip low RTCQuality (%d) time from position", getRTCQuality());
|
|
|
|
|
p.time = 0;
|
|
|
|
|
}
|
2021-10-24 00:10:36 +00:00
|
|
|
|
2024-08-30 19:02:48 -05:00
|
|
|
if (config.position.fixed_position) {
|
|
|
|
|
p.location_source = meshtastic_Position_LocSource_LOC_MANUAL;
|
2024-11-20 07:52:39 -06:00
|
|
|
} else {
|
|
|
|
|
p.location_source = localPosition.location_source;
|
2024-08-30 19:02:48 -05:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) {
|
2024-08-23 06:25:40 -05:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) {
|
2023-06-17 09:10:09 -05:00
|
|
|
p.altitude = localPosition.altitude;
|
2024-08-23 06:25:40 -05:00
|
|
|
p.has_altitude = true;
|
|
|
|
|
} else {
|
2023-06-17 09:10:09 -05:00
|
|
|
p.altitude_hae = localPosition.altitude_hae;
|
2024-08-23 06:25:40 -05:00
|
|
|
p.has_altitude_hae = true;
|
|
|
|
|
}
|
2022-09-12 09:37:21 +02:00
|
|
|
|
2024-08-23 06:25:40 -05:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION) {
|
2023-06-17 09:10:09 -05:00
|
|
|
p.altitude_geoidal_separation = localPosition.altitude_geoidal_separation;
|
2024-08-23 06:25:40 -05:00
|
|
|
p.has_altitude_geoidal_separation = true;
|
|
|
|
|
}
|
2021-10-24 00:10:36 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_DOP) {
|
|
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HVDOP) {
|
2023-06-17 09:10:09 -05:00
|
|
|
p.HDOP = localPosition.HDOP;
|
|
|
|
|
p.VDOP = localPosition.VDOP;
|
2021-10-24 00:10:36 +00:00
|
|
|
} else
|
2023-06-17 09:10:09 -05:00
|
|
|
p.PDOP = localPosition.PDOP;
|
2021-10-24 00:10:36 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW)
|
2023-06-17 09:10:09 -05:00
|
|
|
p.sats_in_view = localPosition.sats_in_view;
|
2021-10-24 00:10:36 +00:00
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_TIMESTAMP)
|
2023-06-17 09:10:09 -05:00
|
|
|
p.timestamp = localPosition.timestamp;
|
2021-10-24 00:10:36 +00:00
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SEQ_NO)
|
2023-06-17 09:10:09 -05:00
|
|
|
p.seq_number = localPosition.seq_number;
|
2022-09-27 14:03:02 +02:00
|
|
|
|
2024-08-23 06:25:40 -05:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING) {
|
2023-06-17 09:10:09 -05:00
|
|
|
p.ground_track = localPosition.ground_track;
|
2024-08-23 06:25:40 -05:00
|
|
|
p.has_ground_track = true;
|
|
|
|
|
}
|
2022-10-03 20:30:11 +02:00
|
|
|
|
2024-08-23 06:25:40 -05:00
|
|
|
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED) {
|
2023-06-17 09:10:09 -05:00
|
|
|
p.ground_speed = localPosition.ground_speed;
|
2024-08-23 06:25:40 -05:00
|
|
|
p.has_ground_speed = true;
|
|
|
|
|
}
|
2022-10-03 20:30:11 +02:00
|
|
|
|
2025-01-25 16:24:24 +01:00
|
|
|
LOG_INFO("Position packet: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i);
|
2022-08-07 15:08:46 -05:00
|
|
|
|
2025-07-14 11:12:26 +01:00
|
|
|
#ifndef MESHTASTIC_EXCLUDE_ATAK
|
2024-02-16 20:04:21 -06:00
|
|
|
// TAK Tracker devices should send their position in a TAK packet over the ATAK port
|
|
|
|
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
|
|
|
|
|
return allocAtakPli();
|
2025-07-14 11:12:26 +01:00
|
|
|
#endif
|
2024-02-16 20:04:21 -06:00
|
|
|
|
2021-02-14 11:37:32 +08:00
|
|
|
return allocDataProtobuf(p);
|
2020-12-07 10:18:11 +08:00
|
|
|
}
|
|
|
|
|
|
2025-01-25 16:24:24 +01:00
|
|
|
meshtastic_MeshPacket *PositionModule::allocReply()
|
|
|
|
|
{
|
2025-06-19 18:20:20 -05:00
|
|
|
if (config.device.role != meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND && lastSentReply &&
|
|
|
|
|
Throttle::isWithinTimespanMs(lastSentReply, 3 * 60 * 1000)) {
|
|
|
|
|
LOG_DEBUG("Skip Position reply since we sent a reply <3min ago");
|
2025-01-25 16:24:24 +01:00
|
|
|
ignoreRequest = true; // Mark it as ignored for MeshModule
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2025-06-19 18:20:20 -05:00
|
|
|
|
|
|
|
|
meshtastic_MeshPacket *reply = allocPositionPacket();
|
|
|
|
|
if (reply) {
|
|
|
|
|
lastSentReply = millis(); // Track when we sent this reply
|
|
|
|
|
}
|
|
|
|
|
return reply;
|
2025-01-25 16:24:24 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-16 20:04:21 -06:00
|
|
|
meshtastic_MeshPacket *PositionModule::allocAtakPli()
|
|
|
|
|
{
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Send TAK PLI packet");
|
2024-02-16 20:04:21 -06:00
|
|
|
meshtastic_MeshPacket *mp = allocDataPacket();
|
|
|
|
|
mp->decoded.portnum = meshtastic_PortNum_ATAK_PLUGIN;
|
|
|
|
|
|
|
|
|
|
meshtastic_TAKPacket takPacket = {.is_compressed = true,
|
|
|
|
|
.has_contact = true,
|
2024-09-12 22:42:10 +02:00
|
|
|
.contact = meshtastic_Contact_init_default,
|
2024-02-16 20:04:21 -06:00
|
|
|
.has_group = true,
|
|
|
|
|
.group = {meshtastic_MemberRole_TeamMember, meshtastic_Team_Cyan},
|
|
|
|
|
.has_status = true,
|
|
|
|
|
.status =
|
|
|
|
|
{
|
|
|
|
|
.battery = powerStatus->getBatteryChargePercent(),
|
|
|
|
|
},
|
|
|
|
|
.which_payload_variant = meshtastic_TAKPacket_pli_tag,
|
2024-09-12 22:42:10 +02:00
|
|
|
.payload_variant = {.pli = {
|
|
|
|
|
.latitude_i = localPosition.latitude_i,
|
|
|
|
|
.longitude_i = localPosition.longitude_i,
|
|
|
|
|
.altitude = localPosition.altitude_hae,
|
|
|
|
|
.speed = localPosition.ground_speed,
|
|
|
|
|
.course = static_cast<uint16_t>(localPosition.ground_track),
|
|
|
|
|
}}};
|
2024-02-16 20:04:21 -06:00
|
|
|
|
2024-07-23 06:16:53 -05:00
|
|
|
auto length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.device_callsign,
|
|
|
|
|
sizeof(takPacket.contact.device_callsign) - 1, USX_PSET_DFLT, NULL);
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("Uncompressed device_callsign '%s' - %d bytes", owner.long_name, strlen(owner.long_name));
|
|
|
|
|
LOG_DEBUG("Compressed device_callsign '%s' - %d bytes", takPacket.contact.device_callsign, length);
|
2024-07-23 06:16:53 -05:00
|
|
|
length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.callsign,
|
|
|
|
|
sizeof(takPacket.contact.callsign) - 1, USX_PSET_DFLT, NULL);
|
2024-02-16 20:04:21 -06:00
|
|
|
mp->decoded.payload.size =
|
|
|
|
|
pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), &meshtastic_TAKPacket_msg, &takPacket);
|
|
|
|
|
return mp;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-21 07:42:36 -05:00
|
|
|
void PositionModule::sendOurPosition()
|
|
|
|
|
{
|
|
|
|
|
bool requestReplies = currentGeneration != radioGeneration;
|
|
|
|
|
currentGeneration = radioGeneration;
|
|
|
|
|
|
|
|
|
|
// If we changed channels, ask everyone else for their latest info
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Send pos@%x:6 to mesh (wantReplies=%d)", localPosition.timestamp, requestReplies);
|
Unify the native display config between legacy display and MUI (#6838)
* Add missed include
* Another Warning fix
* Add another HAS_SCREEN
* Namespace fixes
* Removed depricated destination types and re-factored destination screen
* Get rid of Arduino Strings
* Clean up after Copilot
* SixthLine Def, Screen Rename
Added Sixth Line Definition Screen Rename, and Automatic Line Adjustment
* Consistency is hard - fixed "Sixth"
* System Frame Updates
Adjusted line construction to ensure we fit maximum content per screen.
* Fix up notifications
* Add a couple more ifdef HAS_SCREEN lines
* Add screen->isOverlayBannerShowing()
* Don't forget the invert!
* Adjust Nodelist Center Divider
Adjust Nodelist Center Divider
* Fix variable casting
* Fix entryText variable as empty before update to fix validation
* Altitude is int32_t
* Update PowerTelemetry to have correct data type
* Fix cppcheck warnings (#6945)
* Fix cppcheck warnings
* Adjust logic in Power.cpp for power sensor
---------
Co-authored-by: Jason P <applewiz@mac.com>
* More pixel wrangling so things line up NodeList edition
* Adjust NodeList alignments and plumb some background padding for a possible title fix
* Better alignment for banner notifications
* Move title into drawCommonHeader; initial screen tested
* Fonts make spacing items difficult
* Improved beeping booping and other buzzer based feedback (#6947)
* Improved beeping booping and other buzzer based feedback
* audible button feedback (#6949)
* Refactor
---------
Co-authored-by: todd-herbert <herbert.todd@gmail.com>
* Sandpapered the corners of the notification popup
* Finalize drawCommonHeader migration
* Update Title of Favorite Node Screens
* Update node metric alignment on LoRa screen
* Update the border for popups to separate it from background
* Update PaxcounterModule.cpp with CommonHeader
* Update WiFi screen with CommonHeader and related data reflow
* It was not, in fact, pointing up
* Fix build on wismeshtap
* T-deck trackball debounce
* Fix uptime on Device Focused page to actually detail
* Update Sys screen for new uptime, add label to Freq/Chan on LoRa
* Don't display DOP any longer, make Uptime consistent
* Revert Uptime change on Favorites, Apply to Device Focused
* Label the satelite number to avoid confusion
* Boop boop boop boop
* Correct GPS positioning and string consistency across strings for GPS
* Fix GPS text alignment
* Enable canned messages by default
* Don't wake screen on new nodes
* Cannedmessage list emote support added
* Fn+e emote picker for freetext screen
* Actually block CannedInput actions while display is shown
* Add selection menu to bannerOverlay
* Off by one
* Move to unified text layouts and spacing
* Still my Fav without an "e"
* Fully remove EVENT_NODEDB_UPDATED
* Simply LoRa screen
* Make some char pointers const to fix compilation on native targets
* Update drawCompassNorth to include radius
* Fix warning
* button thread cleanup
* Pull OneButton handling from PowerFSM and add MUI switch (#6973)
* Trunk
* Onebutton Menu Support
* Add temporary clock icon
* Add gps location to fsi
* Banner message state reset
* Cast to char to satisfy compiler
* Better fast handling of input during banner
* Fix warning
* Derp
* oops
* Update ref
* Wire buzzer_mode
* remove legacy string->print()
* Only init screen if one found
* Unsigned Char
* More buttonThread cleaning
* screen.cpp button handling cleanup
* The Great Event Rename of 2025
* Fix the Radiomaster
* Missed trackball type change
* Remove unused function
* Make ButtonThread an InputBroker
* Coffee hadn't kicked in yet
* Add clock icon for Navigation Bar
* Restore clock screen definition code - whoops
* ExternalNotifications now observe inputBroker
* Clock rework (#6992)
* Move Clock bits into ClockRenderer space
* Rework clock into all device navigation
* T-Watch Actually Builds Different
* Compile fix
---------
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
* Add AM/PM to Digital Clock
* Flip Seconds and AM/PM on Clock Display
* Tik-tok pixels are hard
* Fix builds on Thinknode M1
* Check for GPS and don't crash
* Don't endif til the end
* Rework the OneButton thread to be much less of a mess. (#6997)
* Rework the OneButton thread to be much less of a mess. And break lots of targets temporarily
* Update src/input/ButtonThread.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix GPS toggle
* Send the shutdown event, not just the kbchar
* Honor the back button in a notificaiton popup
* Draw the right size box for popup with options
* Try to un-break all the things
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* 24-hour Clock Should have leading zero, but not 12-hour
* Fixup some compile errors
* Add intRoutine to ButtonThread init, to get more responsive user button back
* Add Timezone picker
* Fix Warning
* Optionally set the initial selection for the chooser popup
* Make back buttons work in canned messages
* Drop the wrapper classes
* LonPressTime now configurable
* Clock Frame can not longer be blank; just add valid time
* Back buttons everywhere!
* Key Verification confirm banner
* Make Elecrow M* top button a back button
* Add settings saves
* EInk responsiveness fixes
* Linux Input Fixes
* Add Native Trackball/Joystick support, and move UserButton to Input
* No Flight Stick Mode
* Send input event
* Add Channel Utilization to Device Focused frame
* Don't shift screens when we draw new ones
* Add showOverlayBanner arguments to no-op
* trunk
* Default Native trackball to NC
* Fix crash in simulator mode
* Add longLong button press
* Get the args right
* Adjust Bluetooth Pairing Screen to account for bottom navigation.
* Trackball everywhere, and unPhone buttons
* Remap visionmaster secondary button to TB_UP
* Kill ScanAndSelect
* trunk
* No longer need the canned messages input filter
* All Canned All the time
* Fix stm32 compile error regarding inputBroker
* Unify tft lineheights (#7033)
* Create variable line heights based upon SCREEN_HEIGHT
* Refactor textPositions into method -> getTextPositions
* Update SharedUIDisplay.h
---------
Co-authored-by: Jason P <applewiz@mac.com>
* Adjust top distance for larger displays
* Adjust icon sizes for larger displays
* Fix Paxcounter compile errors after code updates
* Pixel wrangling to make larger screens fit better
* Alert frame has precedence over banner -- for now
* Unify on ALT_BUTTON
* Align AM/PM to the digit, not the segment on larger displays
* Move some global pin defines into configuration.h
* Scaffolding for BMM150 9-axis gyro
* Alt button behavior
* Don't add the blank GPS frames without HAS_GPS
* EVENT_NODEDB_UPDATED has been retired
* Clean out LOG_WARN messages from debugging
* Add dismiss message function
* Minor buttonThread cleanup
* Add BMM150 support
* Clean up last warning from dev
* Simplify bmm150 init return logic
* Add option to reply to messages
* Add minimal menu upon selecting home screen
* Move Messages to slot 2, rename GPS to Position, move variables nearer functional usage in Screen.cpp
* Properly dismiss message
* T-Deck Trackball press is not user button
* Add select on favorite frame to launch cannedMessage DM
* Minor wording change
* Less capital letters
* Fix empty message check, time isn't reliable
* drop dead code
* Make UIRenderer a static class instead of namespace
* Fix the select on favorite
* Check if message is empty early and then 'return'
* Add kb_found, and show the option to launch freetype if appropriate
* Ignore impossible touchscreen touches
* Auto scroll fix
* Move linebreak after "from" for banners to maximize screen usage.
* Center "No messages to show" on Message frame
* Start consolidating buzzer behavior
* Fixed signed / unsigned warning
* Cast second parameter of max() to make some targets happy
* Cast kbchar to (char) to make arduino string happy
* Shorten the notice of "No messages"
* Add buzzer mode chooser
* Add regionPicker to Lora icon
* Reduce line spacing and reorder Position screen to resolve overlapping issues
* Update message titles, fix GPS icons, add Back options
* Leftover boops
* Remove chirp
* Make the region selection dismissable when a region is already set
* Add read-aloud functionality on messages w/ esp8266sam
* "Last Heard" is a better label
* tweak the beep
* 5 options
* properly tear down freetext upon cancel
* de-convelute canned messages just a bit
* Correct height of Mail icon in navigation bar
* Remove unused warning
* Consolidate time methods into TimeFormatters
* Oops
* Change LoRa Picker Cancel to Back
* Tweak selection characters on Banner
* Message render not scrolling on 5th line
* More fixes for message scrolling
* Remove the safety next on text overflow - we found that root cause
* Add pin definitions to fix compilation for obscure target
* Don't let the touchscreen send unitialized kbchar values
* Make virtual KB just a bit quicker
* No more double tap, swipe!
* Left is left, and Right is right
* Update horizontal lightning bolt design
* Move from solid to dashed separator for Message Frame
* Single emote feature fix
* Manually sort overlapping elements for now
* Freetext and clearer choices
* Fix ESP32 InkHUD builds on the unify-tft branch (#7087)
* Remove BaseUI branding
* Capitalization is fun
* Revert Meshtastic Boot Frame Changes
* Add ANZ_433 LoRa region to picker
* Update settings.json
---------
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: todd-herbert <herbert.todd@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-21 06:36:04 -05:00
|
|
|
for (uint8_t channelNum = 0; channelNum < 8; channelNum++) {
|
|
|
|
|
if (channels.getByIndex(channelNum).settings.has_module_settings &&
|
|
|
|
|
channels.getByIndex(channelNum).settings.module_settings.position_precision != 0) {
|
|
|
|
|
sendOurPosition(NODENUM_BROADCAST, requestReplies, channelNum);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-21 07:42:36 -05:00
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:51:22 +02:00
|
|
|
void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t channel)
|
2020-12-07 10:18:11 +08:00
|
|
|
{
|
2021-02-11 17:39:53 +08:00
|
|
|
// cancel any not yet sent (now stale) position packets
|
2021-02-14 11:37:32 +08:00
|
|
|
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
2024-08-01 19:29:49 -05:00
|
|
|
service->cancelSending(prevPacketId);
|
2021-02-11 19:00:17 +08:00
|
|
|
|
2024-02-22 12:32:01 -06:00
|
|
|
// Set's the class precision value for this particular packet
|
2024-02-24 12:53:46 -06:00
|
|
|
if (channels.getByIndex(channel).settings.has_module_settings) {
|
|
|
|
|
precision = channels.getByIndex(channel).settings.module_settings.position_precision;
|
|
|
|
|
}
|
2024-02-22 12:32:01 -06:00
|
|
|
|
2025-01-25 16:24:24 +01:00
|
|
|
meshtastic_MeshPacket *p = allocPositionPacket();
|
2023-08-28 03:28:04 -05:00
|
|
|
if (p == nullptr) {
|
2025-01-25 16:24:24 +01:00
|
|
|
LOG_DEBUG("allocPositionPacket returned a nullptr");
|
2023-08-28 03:28:04 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-03 16:48:44 +08:00
|
|
|
p->to = dest;
|
2023-09-30 21:09:17 -05:00
|
|
|
p->decoded.want_response = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ? false : wantReplies;
|
2024-02-16 20:04:21 -06:00
|
|
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
|
|
|
|
|
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
|
2023-01-28 14:32:57 -06:00
|
|
|
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
|
|
|
|
|
else
|
|
|
|
|
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
2021-02-11 17:39:53 +08:00
|
|
|
prevPacketId = p->id;
|
2020-12-03 16:48:44 +08:00
|
|
|
|
2023-03-29 13:51:22 +02:00
|
|
|
if (channel > 0)
|
|
|
|
|
p->channel = channel;
|
|
|
|
|
|
2024-08-01 19:29:49 -05:00
|
|
|
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
2023-09-30 21:09:17 -05:00
|
|
|
|
2024-09-25 13:50:00 -05:00
|
|
|
if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER,
|
|
|
|
|
meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) &&
|
2024-02-16 20:04:21 -06:00
|
|
|
config.power.is_power_saving) {
|
2025-05-07 21:20:32 -05:00
|
|
|
meshtastic_ClientNotification *notification = clientNotificationPool.allocZeroed();
|
|
|
|
|
notification->level = meshtastic_LogRecord_Level_INFO;
|
|
|
|
|
notification->time = getValidTime(RTCQualityFromNet);
|
|
|
|
|
sprintf(notification->message, "Sending position and sleeping for %us interval in a moment",
|
|
|
|
|
Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs) /
|
|
|
|
|
1000U);
|
|
|
|
|
service->sendClientNotification(notification);
|
2023-09-30 21:09:17 -05:00
|
|
|
sleepOnNextExecution = true;
|
2025-05-07 21:20:32 -05:00
|
|
|
LOG_DEBUG("Start next execution in 5s, then sleep");
|
|
|
|
|
setIntervalFromNow(FIVE_SECONDS_MS);
|
2023-09-30 21:09:17 -05:00
|
|
|
}
|
2020-12-03 16:48:44 +08:00
|
|
|
}
|
2021-02-14 11:57:48 +08:00
|
|
|
|
2023-10-09 18:33:04 -05:00
|
|
|
#define RUNONCE_INTERVAL 5000;
|
|
|
|
|
|
2022-02-27 02:21:02 -08:00
|
|
|
int32_t PositionModule::runOnce()
|
2021-02-14 12:26:51 +08:00
|
|
|
{
|
2023-09-30 21:09:17 -05:00
|
|
|
if (sleepOnNextExecution == true) {
|
|
|
|
|
sleepOnNextExecution = false;
|
2024-03-17 08:18:30 -05:00
|
|
|
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs);
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_DEBUG("Sleep for %ims, then awaking to send position again", nightyNightMs);
|
2024-11-12 06:17:26 -06:00
|
|
|
doDeepSleep(nightyNightMs, false, false);
|
2023-09-30 21:09:17 -05:00
|
|
|
}
|
|
|
|
|
|
2024-03-21 09:06:37 -05:00
|
|
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
2024-01-22 20:13:27 -06:00
|
|
|
if (node == nullptr)
|
|
|
|
|
return RUNONCE_INTERVAL;
|
2021-11-28 19:41:34 -08:00
|
|
|
|
2021-02-14 11:57:48 +08:00
|
|
|
// We limit our GPS broadcasts to a max rate
|
|
|
|
|
uint32_t now = millis();
|
2024-07-13 05:59:19 -05:00
|
|
|
uint32_t intervalMs = Default::getConfiguredOrDefaultMsScaled(config.position.position_broadcast_secs,
|
|
|
|
|
default_broadcast_interval_secs, numOnlineNodes);
|
2023-03-27 14:09:22 -05:00
|
|
|
uint32_t msSinceLastSend = now - lastGpsSend;
|
2023-10-09 18:33:04 -05:00
|
|
|
// Only send packets if the channel util. is less than 25% utilized or we're a tracker with less than 40% utilized.
|
2024-02-16 20:04:21 -06:00
|
|
|
if (!airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
|
|
|
|
|
config.device.role != meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)) {
|
2023-10-09 18:33:04 -05:00
|
|
|
return RUNONCE_INTERVAL;
|
|
|
|
|
}
|
2021-10-26 12:41:44 +00:00
|
|
|
|
2023-03-27 14:09:22 -05:00
|
|
|
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
|
2024-11-26 14:00:10 -06:00
|
|
|
if (nodeDB->hasValidPosition(node)) {
|
2023-10-09 18:33:04 -05:00
|
|
|
lastGpsSend = now;
|
2021-02-14 11:57:48 +08:00
|
|
|
|
2023-10-09 18:33:04 -05:00
|
|
|
lastGpsLatitude = node->position.latitude_i;
|
|
|
|
|
lastGpsLongitude = node->position.longitude_i;
|
|
|
|
|
|
2024-04-21 07:42:36 -05:00
|
|
|
sendOurPosition();
|
2023-12-13 17:43:20 -06:00
|
|
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) {
|
|
|
|
|
sendLostAndFoundText();
|
|
|
|
|
}
|
2023-10-09 18:33:04 -05:00
|
|
|
}
|
|
|
|
|
} else if (config.position.position_broadcast_smart_enabled) {
|
2024-08-01 19:29:49 -05:00
|
|
|
const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
2023-10-09 18:33:04 -05:00
|
|
|
|
2024-11-26 14:00:10 -06:00
|
|
|
if (nodeDB->hasValidPosition(node2)) {
|
2023-10-09 18:33:04 -05:00
|
|
|
// The minimum time (in seconds) that would pass before we are able to send a new position packet.
|
|
|
|
|
|
|
|
|
|
auto smartPosition = getDistanceTraveledSinceLastSend(node->position);
|
2024-05-03 15:10:57 +02:00
|
|
|
msSinceLastSend = now - lastGpsSend;
|
2023-10-09 18:33:04 -05:00
|
|
|
|
2024-04-21 07:42:36 -05:00
|
|
|
if (smartPosition.hasTraveledOverThreshold &&
|
|
|
|
|
Throttle::execute(
|
|
|
|
|
&lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); },
|
2024-11-04 19:15:59 -06:00
|
|
|
[]() { LOG_DEBUG("Skip send smart broadcast due to time throttling"); })) {
|
2022-01-26 22:32:33 -08:00
|
|
|
|
2024-04-21 07:42:36 -05:00
|
|
|
LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, "
|
2024-10-14 06:11:43 +02:00
|
|
|
"minTimeInterval=%ims)",
|
2024-04-21 07:42:36 -05:00
|
|
|
localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold,
|
|
|
|
|
msSinceLastSend, minimumTimeThreshold);
|
2023-10-09 18:33:04 -05: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;
|
2021-11-26 19:12:00 -08:00
|
|
|
}
|
|
|
|
|
}
|
2021-02-14 11:57:48 +08:00
|
|
|
}
|
|
|
|
|
|
2023-10-09 18:33:04 -05:00
|
|
|
return RUNONCE_INTERVAL; // to save power only wake for our callback occasionally
|
2023-09-23 23:45:35 -05:00
|
|
|
}
|
|
|
|
|
|
2023-12-13 17:43:20 -06:00
|
|
|
void PositionModule::sendLostAndFoundText()
|
|
|
|
|
{
|
|
|
|
|
meshtastic_MeshPacket *p = allocDataPacket();
|
|
|
|
|
p->to = NODENUM_BROADCAST;
|
|
|
|
|
char *message = new char[60];
|
|
|
|
|
sprintf(message, "🚨I'm lost! Lat / Lon: %f, %f\a", (lastGpsLatitude * 1e-7), (lastGpsLongitude * 1e-7));
|
|
|
|
|
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
|
|
|
|
|
p->want_ack = false;
|
|
|
|
|
p->decoded.payload.size = strlen(message);
|
|
|
|
|
memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
|
|
|
|
|
|
2024-08-01 19:29:49 -05:00
|
|
|
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
2023-12-13 17:43:20 -06:00
|
|
|
delete[] message;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-27 10:32:35 -05:00
|
|
|
struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition)
|
|
|
|
|
{
|
|
|
|
|
// The minimum distance to travel before we are able to send a new position packet.
|
2024-03-17 08:18:30 -05:00
|
|
|
const uint32_t distanceTravelThreshold =
|
|
|
|
|
Default::getConfiguredOrDefault(config.position.broadcast_smart_minimum_distance, 100);
|
2023-09-27 10:32:35 -05:00
|
|
|
|
|
|
|
|
// Determine the distance in meters between two points on the globe
|
|
|
|
|
float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter(
|
|
|
|
|
lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7);
|
|
|
|
|
|
|
|
|
|
return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend),
|
|
|
|
|
.distanceThreshold = distanceTravelThreshold,
|
|
|
|
|
.hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold};
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-23 23:45:35 -05:00
|
|
|
void PositionModule::handleNewPosition()
|
|
|
|
|
{
|
2024-03-21 09:06:37 -05:00
|
|
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
2024-08-01 19:29:49 -05:00
|
|
|
const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
2023-09-23 23:45:35 -05:00
|
|
|
// We limit our GPS broadcasts to a max rate
|
2024-11-26 14:00:10 -06:00
|
|
|
if (nodeDB->hasValidPosition(node2)) {
|
2023-09-27 10:32:35 -05:00
|
|
|
auto smartPosition = getDistanceTraveledSinceLastSend(node->position);
|
2024-04-21 07:42:36 -05:00
|
|
|
uint32_t msSinceLastSend = millis() - lastGpsSend;
|
|
|
|
|
if (smartPosition.hasTraveledOverThreshold &&
|
|
|
|
|
Throttle::execute(
|
|
|
|
|
&lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); },
|
2024-11-04 19:15:59 -06:00
|
|
|
[]() { LOG_DEBUG("Skip send smart broadcast due to time throttling"); })) {
|
2024-04-21 07:42:36 -05:00
|
|
|
LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, "
|
2024-10-14 06:11:43 +02:00
|
|
|
"minTimeInterval=%ims)",
|
2024-04-21 07:42:36 -05:00
|
|
|
localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend,
|
|
|
|
|
minimumTimeThreshold);
|
2023-09-23 23:45:35 -05: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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-25 05:33:57 -06:00
|
|
|
}
|
|
|
|
|
|
2024-11-26 14:00:10 -06:00
|
|
|
#endif
|