23#include <nlohmann/json.hpp>
41 const std::lock_guard<std::mutex> lock(m_threadMutex);
44 LOG_INFO(
"ISS Tracker: Already tracking, skipping");
52 const std::lock_guard<std::mutex> callbackLock(m_callbackMutex);
53 m_callback = callback;
58 m_trackingThread = std::jthread([
this](
const std::stop_token& stopToken) { TrackingLoop(stopToken); });
60 LOG_INFO(
"ISS Tracker: Started tracking");
64 const std::lock_guard<std::mutex> lock(m_threadMutex);
71 m_trackingThread.request_stop();
74 LOG_INFO(
"ISS Tracker: Stopped tracking");
82 const std::lock_guard<std::mutex> lock(m_dataMutex);
83 return m_currentPosition;
87 const std::lock_guard<std::mutex> lock(m_dataMutex);
91 latitudes.reserve(m_positionHistory.size());
92 longitudes.reserve(m_positionHistory.size());
94 for (
const auto& pos : m_positionHistory) {
96 latitudes.push_back(pos.latitude);
97 longitudes.push_back(pos.longitude);
103 return FetchPositionImpl();
106void ISSTracker::TrackingLoop(
const std::stop_token& stopToken) {
107 while (!stopToken.stop_requested()) {
112 if (stopToken.stop_requested()) {
113 LOG_INFO(
"ISS Tracker: Stop requested, discarding fetched data");
117 if (position.
valid) {
120 const std::lock_guard<std::mutex> lock(m_dataMutex);
121 m_currentPosition = position;
122 AddToHistory(position);
126 std::function<void(
const ISSPosition&)> callback;
128 const std::lock_guard<std::mutex> lock(m_callbackMutex);
129 callback = m_callback;
135 }
catch (
const std::exception& e) {
136 LOG_ERROR(
"ISS Tracker: Callback threw exception: {}", e.what());
138 LOG_ERROR(
"ISS Tracker: Callback threw unknown exception");
142 LOG_INFO(
"ISS Tracker: Position updated - Lat: {}, Long: {}, Alt: {} km, Vel: {} km/h",
145 }
catch (
const std::exception& e) {
146 LOG_ERROR(
"ISS Tracker: Error fetching position: {}", e.what());
148 LOG_ERROR(
"ISS Tracker: Unknown error fetching position");
152 auto start = std::chrono::steady_clock::now();
153 while (!stopToken.stop_requested()) {
154 auto now = std::chrono::steady_clock::now();
155 auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - start).count();
159 std::this_thread::sleep_for(std::chrono::milliseconds(100));
163 LOG_INFO(
"ISS Tracker: Tracking loop exited");
166ISSPosition ISSTracker::FetchPositionImpl() {
167 ISSPosition position;
168 position.
valid =
false;
171 const std::string jsonResponse = FetchJSON(ISS_API_URL);
172 if (jsonResponse.empty()) {
173 LOG_ERROR(
"ISS Tracker: Empty response from server");
177 position = ParseJSON(jsonResponse);
178 }
catch (
const std::bad_alloc& e) {
179 LOG_ERROR(
"ISS Tracker: Memory allocation failed: {}", e.what());
180 }
catch (
const std::exception& e) {
181 LOG_ERROR(
"ISS Tracker: Fetch failed: {}", e.what());
183 LOG_ERROR(
"ISS Tracker: Unknown error during fetch");
191size_t WriteCallback(
void* contents,
size_t size,
size_t nmemb,
void* userp) {
192 static_cast<std::string*
>(userp)->append(
static_cast<char*
>(contents), size * nmemb);
197std::string ISSTracker::FetchJSON(
const std::string& url) {
200 CURL* curl = curl_easy_init();
201 if (curl ==
nullptr) {
202 LOG_ERROR(
"ISS Tracker: Failed to initialize CURL");
206 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
207 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
208 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
209 curl_easy_setopt(curl, CURLOPT_USERAGENT,
"MetaImGUI-ISSTracker/1.0");
210 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
211 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
212 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
213 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
215 const CURLcode res = curl_easy_perform(curl);
217 if (res != CURLE_OK) {
218 LOG_ERROR(
"ISS Tracker: Request failed: {}", curl_easy_strerror(res));
222 curl_easy_cleanup(curl);
227ISSPosition ISSTracker::ParseJSON(
const std::string& jsonResponse) {
228 ISSPosition position;
229 position.valid =
false;
232 auto json = nlohmann::json::parse(jsonResponse);
235 if (
json.contains(
"latitude") &&
json.contains(
"longitude")) {
236 position.latitude =
json[
"latitude"].get<
double>();
237 position.longitude =
json[
"longitude"].get<
double>();
240 if (
json.contains(
"altitude")) {
241 position.altitude =
json[
"altitude"].get<
double>();
243 if (
json.contains(
"velocity")) {
244 position.velocity =
json[
"velocity"].get<
double>();
246 if (
json.contains(
"timestamp")) {
247 position.timestamp =
json[
"timestamp"].get<
long>();
250 position.valid =
true;
252 LOG_ERROR(
"ISS Tracker: Missing required fields in JSON response");
254 }
catch (
const nlohmann::json::parse_error& e) {
255 LOG_ERROR(
"ISS Tracker: JSON parse error: {}", e.what());
256 }
catch (
const nlohmann::json::type_error& e) {
257 LOG_ERROR(
"ISS Tracker: JSON type error: {}", e.what());
258 }
catch (
const std::exception& e) {
259 LOG_ERROR(
"ISS Tracker: Error parsing JSON: {}", e.what());
265void ISSTracker::AddToHistory(
const ISSPosition& position) {
268 if (!position.valid) {
273 m_positionHistory.push_back(position);
276 while (m_positionHistory.size() > m_maxHistorySize) {
277 m_positionHistory.pop_front();