#include "tracking_widget.h" #include "common/tracking/tle.h" #include "common/utils.h" #include "logger.h" namespace satdump { void TrackingWidget::backend_run() { time_t last_rot_update = 0; while (backend_should_run) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); if (!has_tle) continue; tle_update_mutex.lock(); if (horizons_mode) { if (getTime() > last_horizons_fetch_time + 3600) { loadHorizons(); updateNextPass(); } if (horizons_data.size() > 0) { double timed = getTime(); int iter = 0; for (int i = 0; i < horizons_data.size(); i++) if (horizons_data[i].timestamp < timed) iter = i; if (getTime() > next_los_time) updateNextPass(); current_az = horizons_data[iter].az; current_el = horizons_data[iter].el; } } else { if (satellite_object != nullptr) { predict_orbit(satellite_object, &satellite_orbit, predict_to_julian_double(getTime())); predict_observe_orbit(observer_station, &satellite_orbit, &observation_pos); if (getTime() > next_los_time) updateNextPass(); current_az = observation_pos.azimuth * RAD_TO_DEG; current_el = observation_pos.elevation * RAD_TO_DEG; } } // Update if (backend_needs_update) { logger->trace("Updating elements..."); if (horizons_mode) { loadHorizons(); updateNextPass(); } else { if (satellite_object != nullptr) predict_destroy_orbital_elements(satellite_object); auto &tle = general_tle_registry[current_satellite]; satellite_object = predict_parse_tle(tle.line1.c_str(), tle.line2.c_str()); updateNextPass(); } backend_needs_update = false; } tle_update_mutex.unlock(); // TODO : MOVE THIS OUT OF THERE!!!! if (rotctld_client.is_connected() && time(0) - last_rot_update > 1) { updateRotator(); last_rot_update = time(0); } } } void TrackingWidget::updateNextPass() { logger->trace("Update pass trajectory..."); upcoming_pass_points.clear(); next_aos_time = 0; next_los_time = 0; if (horizons_mode) { if (horizons_data.size() == 0) return; upcoming_passes_mtx.lock(); double timed = getTime(); int iter = 0; for (int i = 0; i < horizons_data.size(); i++) if (horizons_data[i].timestamp < timed) iter = i; if (horizons_data[iter].el > 0) // Already got AOS { next_aos_time = timed; for (int i = iter - 1; i >= 0; i--) // Attempt to find previous AOS { if (horizons_data[i].el <= 0) { next_aos_time = horizons_data[i].timestamp; next_aos_az = horizons_data[i].az; next_aos_el = horizons_data[i].el; break; } } for (int i = iter; i < horizons_data.size(); i++) // Find LOS { if (horizons_data[i].el <= 0) { next_los_time = horizons_data[i].timestamp; break; } } } else { int aos_iter = 0; for (int i = iter; i < horizons_data.size(); i++) // Find AOS { if (horizons_data[i].el > 0) { next_aos_time = horizons_data[i].timestamp; next_aos_az = horizons_data[i].az; next_aos_el = horizons_data[i].el; aos_iter = i; break; } } if (next_aos_time != 0) { for (int i = aos_iter; i < horizons_data.size(); i++) // Find LOS { if (horizons_data[i].el <= 0) { next_los_time = horizons_data[i].timestamp; break; } } } } if (next_aos_time != 0 && next_los_time != 0) { double time_step = abs(next_los_time - next_aos_time) / 50.0; for (double ctime = next_aos_time; ctime <= next_los_time; ctime += time_step) { int iter = 0; for (int i = 0; i < horizons_data.size(); i++) if (horizons_data[i].timestamp < ctime) iter = i; upcoming_pass_points.push_back({horizons_data[iter].az, horizons_data[iter].el}); } } upcoming_passes_mtx.unlock(); } else { if (predict_is_geosynchronous(satellite_object)) return; upcoming_passes_mtx.lock(); // Get next LOS predict_observation next_aos, next_los; next_aos = next_los = predict_next_los(observer_station, satellite_object, predict_to_julian_double(getTime())); // Calculate the AOS before that LOS next_aos_time = next_los_time = predict_from_julian(next_los.time); do { next_aos = predict_next_aos(observer_station, satellite_object, predict_to_julian_double(next_aos_time)); next_aos_time -= 10; } while (predict_from_julian(next_aos.time) >= next_los_time); next_los_time = predict_from_julian(next_los.time); next_aos_time = predict_from_julian(next_aos.time); next_aos_az = next_aos.azimuth * RAD_TO_DEG; next_aos_el = next_aos.elevation * RAD_TO_DEG; // Calculate a few points during the pass predict_position satellite_orbit2; predict_observation observation_pos2; double time_step = abs(next_los_time - next_aos_time) / 50.0; for (double ctime = next_aos_time; ctime <= next_los_time; ctime += time_step) { predict_orbit(satellite_object, &satellite_orbit2, predict_to_julian_double(ctime)); predict_observe_orbit(observer_station, &satellite_orbit2, &observation_pos2); upcoming_pass_points.push_back({observation_pos2.azimuth * RAD_TO_DEG, observation_pos2.elevation * RAD_TO_DEG}); } upcoming_passes_mtx.unlock(); } } void TrackingWidget::loadHorizons() { logger->trace("Pulling Horizons data..."); double curr_time = getTime(); double start_time = curr_time - 24 * 3600; double stop_time = curr_time + 24 * 3600; std::string cmd = (std::string) "https://ssd.jpl.nasa.gov/api/horizons.api?format=text" + "&OBJ_DATA=NO" + "&MAKE_EPHEM=YES" + "&COMMAND=" + horizons_ids[current_horizons] + "&CAL_FORMAT=JD" + "&EPHEM_TYPE=OBSERVER" + "&CENTER='coord@399'" + "&COORD_TYPE=GEODETIC" + "&SITE_COORD='" + (qth_lon >= 0 ? "+" : "") + std::to_string(qth_lon) + "," + (qth_lat >= 0 ? "+" : "") + std::to_string(qth_lat) + "," + std::to_string(qth_alt / 1e3) + "'" + "&START_TIME='JD " + std::to_string((start_time / 86400.0) + 2440587.5) + "'" + "&STOP_TIME='JD " + std::to_string((stop_time / 86400.0) + 2440587.5) + "'" + "&STEP_SIZE='8640'" + // 86400 "&QUANTITIES='4,20'"; std::string req_result; int err = perform_http_request(cmd, req_result); if (err != 0) { logger->error("Could not fetch data from Horizons!"); return; } last_horizons_fetch_time = curr_time; std::istringstream req_results(req_result); std::string line; horizons_data.clear(); bool fount_soe = false; bool fount_eoe = false; while (getline(req_results, line)) { if (!fount_soe) { if (line.find("$$SOE") != std::string::npos) fount_soe = true; continue; } if (fount_eoe) { continue; } else { if (line.find("$$EOE") != std::string::npos) fount_eoe = true; } double julian_time = 0; double az = 0; double el = 0; double delta = 0; double deldot = 0; if (sscanf(line.c_str(), "%lf%*s %lf %lf %lf %lf %lf", &julian_time, &az, &el, &delta, &deldot) == 5 || sscanf(line.c_str(), "%lf %lf %lf %lf %lf %lf", &julian_time, &az, &el, &delta, &deldot) == 5) { double ctime = (julian_time - 2440587.5) * 86400.0; // logger->info("%s %f %f", timestamp_to_string(ctime).c_str(), az, el); horizons_data.push_back({ctime, (float)az, (float)el}); } } logger->trace("Done pulling Horizons data..."); } void TrackingWidget::updateRotator() { // logger->info("Rot update!"); if (rotctld_client.get_pos(¤t_rotator_az, ¤t_rotator_el)) logger->error("Error getting rotator position!"); if (rotator_engaged) { if (rotator_tracking) { if (current_el > 0) { current_req_rotator_az = current_az; current_req_rotator_el = current_el; } else { current_req_rotator_az = next_aos_az; current_req_rotator_el = next_aos_el; } } if (current_req_rotator_el < 0) current_req_rotator_el = 0; if (rotctld_client.set_pos(current_req_rotator_az, current_req_rotator_el)) logger->error("Error setting rotator position %f %f!", current_req_rotator_az, current_req_rotator_el); } } }