-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDesktopGPS.cpp
More file actions
121 lines (102 loc) · 3.03 KB
/
Copy pathDesktopGPS.cpp
File metadata and controls
121 lines (102 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "DesktopGPS.h"
#include "DekiHttp.h"
#include "DekiLogSystem.h"
#include <cstdlib>
#include <cstring>
#include <ctime>
void DesktopGPS::Configure(const ModuleConfig&)
{
}
bool DesktopGPS::Initialize()
{
m_Cancel.store(false);
m_HasFix.store(false);
m_LastError.clear();
m_Thread = std::thread(&DesktopGPS::FetchLocation, this);
m_State = ModuleState::Initialized;
return true;
}
void DesktopGPS::Shutdown()
{
m_Cancel.store(true);
if (m_Thread.joinable())
m_Thread.join();
m_State = ModuleState::Uninitialized;
}
DekiGPSLocation DesktopGPS::Current() const
{
DekiGPSLocation loc;
loc.latitude = m_Lat.load();
loc.longitude = m_Lon.load();
return loc;
}
bool DesktopGPS::HasLiveFix() const
{
return m_HasFix.load();
}
int64_t DesktopGPS::CurrentUTCEpochSeconds() const
{
return (int64_t)std::time(nullptr);
}
bool DesktopGPS::HasUTC() const
{
// The desktop's system clock is the UTC source — std::time() is always UTC
// regardless of the OS timezone setting. Available for the lifetime of the process.
return true;
}
namespace
{
bool ParseDoubleAfter(const std::string& body, const char* key, double& out)
{
size_t pos = body.find(key);
if (pos == std::string::npos) return false;
pos += std::strlen(key);
while (pos < body.size() && (body[pos] == ' ' || body[pos] == ':' || body[pos] == '\t')) ++pos;
if (pos >= body.size()) return false;
const char* start = body.c_str() + pos;
char* end = nullptr;
double v = std::strtod(start, &end);
if (end == start) return false;
out = v;
return true;
}
bool ResponseIsSuccess(const std::string& body)
{
size_t pos = body.find("\"status\"");
if (pos == std::string::npos) return false;
pos = body.find("\"success\"", pos);
return pos != std::string::npos;
}
}
void DesktopGPS::FetchLocation()
{
DEKI_LOG_INFO("[deki-gps] DesktopGPS: querying ip-api.com for approximate location");
std::string body = DekiHttp::FetchUrl("http://ip-api.com/json/");
if (m_Cancel.load() || body.empty())
{
if (body.empty())
{
m_LastError = "DekiHttp returned empty body (no client registered or fetch failed)";
DEKI_LOG_WARNING("[deki-gps] DesktopGPS: %s", m_LastError.c_str());
}
return;
}
if (!ResponseIsSuccess(body))
{
m_LastError = "ip-api response status was not success";
DEKI_LOG_WARNING("[deki-gps] DesktopGPS: %s", m_LastError.c_str());
return;
}
double lat = 0.0;
double lon = 0.0;
if (!ParseDoubleAfter(body, "\"lat\"", lat) || !ParseDoubleAfter(body, "\"lon\"", lon))
{
m_LastError = "ip-api response missing lat/lon";
DEKI_LOG_WARNING("[deki-gps] DesktopGPS: %s", m_LastError.c_str());
return;
}
m_Lat.store(lat);
m_Lon.store(lon);
m_HasFix.store(true);
DEKI_LOG_INFO("[deki-gps] DesktopGPS: live fix acquired at %.4f, %.4f", lat, lon);
}