#include "tracking_widget.h" #include "common/utils.h" #include "logger.h" #include "imgui/imgui.h" #include "imgui/imgui_stdlib.h" #include "main_ui.h" #include "core/module.h" #include "core/style.h" namespace satdump { void TrackingWidget::renderPolarPlot() { int d_pplot_size = ImGui::GetWindowContentRegionMax().x; ImDrawList *draw_list = ImGui::GetWindowDrawList(); draw_list->AddRectFilled(ImGui::GetCursorScreenPos(), ImVec2(ImGui::GetCursorScreenPos().x + d_pplot_size, ImGui::GetCursorScreenPos().y + d_pplot_size), light_theme ? ImColor(255, 255, 255, 255) : ImColor::HSV(0, 0, 0)); float radius = 0.45; float radius1 = d_pplot_size * radius * (3.0 / 9.0); float radius2 = d_pplot_size * radius * (6.0 / 9.0); float radius3 = d_pplot_size * radius * (9.0 / 9.0); draw_list->AddCircle({ImGui::GetCursorScreenPos().x + (d_pplot_size / 2), ImGui::GetCursorScreenPos().y + (d_pplot_size / 2)}, radius1, ImColor(0, 255, 0, 255), 0, 2); draw_list->AddCircle({ImGui::GetCursorScreenPos().x + (d_pplot_size / 2), ImGui::GetCursorScreenPos().y + (d_pplot_size / 2)}, radius2, ImColor(0, 255, 0, 255), 0, 2); draw_list->AddCircle({ImGui::GetCursorScreenPos().x + (d_pplot_size / 2), ImGui::GetCursorScreenPos().y + (d_pplot_size / 2)}, radius3, ImColor(0, 255, 0, 255), 0, 2); draw_list->AddLine({ImGui::GetCursorScreenPos().x + (d_pplot_size / 2), ImGui::GetCursorScreenPos().y}, {ImGui::GetCursorScreenPos().x + (d_pplot_size / 2), ImGui::GetCursorScreenPos().y + d_pplot_size}, ImColor(0, 255, 0, 255), 2); draw_list->AddLine({ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y + (d_pplot_size / 2)}, {ImGui::GetCursorScreenPos().x + d_pplot_size, ImGui::GetCursorScreenPos().y + (d_pplot_size / 2)}, ImColor(0, 255, 0, 255), 2); if (upcoming_pass_points.size() > 1) { upcoming_passes_mtx.lock(); for (int i = 0; i < (int)upcoming_pass_points.size() - 1; i++) { auto &p1 = upcoming_pass_points[i]; auto &p2 = upcoming_pass_points[i + 1]; float point_x1, point_x2, point_y1, point_y2; point_x1 = point_x2 = ImGui::GetCursorScreenPos().x + (d_pplot_size / 2); point_y1 = point_y2 = ImGui::GetCursorScreenPos().y + (d_pplot_size / 2); point_x1 += sin(p1.first * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - p1.second) / 90.0); point_y1 -= cos(p1.first * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - p1.second) / 90.0); point_x2 += sin(p2.first * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - p2.second) / 90.0); point_y2 -= cos(p2.first * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - p2.second) / 90.0); draw_list->AddLine({point_x1, point_y1}, {point_x2, point_y2}, ImColor(255, 165, 0, 255), 2.0); } upcoming_passes_mtx.unlock(); } if (current_el > 0) { float point_x = ImGui::GetCursorScreenPos().x + (d_pplot_size / 2); float point_y = ImGui::GetCursorScreenPos().y + (d_pplot_size / 2); point_x += sin(current_az * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - current_el) / 90.0); point_y -= cos(current_az * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - current_el) / 90.0); draw_list->AddCircleFilled({point_x, point_y}, 5 * ui_scale, ImColor(255, 0, 0, 255)); } if (rotator_handler->is_connected()) { { float point_x = ImGui::GetCursorScreenPos().x + (d_pplot_size / 2); float point_y = ImGui::GetCursorScreenPos().y + (d_pplot_size / 2); point_x += sin(current_rotator_az * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - current_rotator_el) / 90.0); point_y -= cos(current_rotator_az * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - current_rotator_el) / 90.0); draw_list->AddCircle({point_x, point_y}, 9 * ui_scale, ImColor(0, 237, 255, 255), 0, 2.0); } if (rotator_engaged) { float point_x = ImGui::GetCursorScreenPos().x + (d_pplot_size / 2); float point_y = ImGui::GetCursorScreenPos().y + (d_pplot_size / 2); point_x += sin(current_req_rotator_az * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - current_req_rotator_el) / 90.0); point_y -= cos(current_req_rotator_az * DEG_TO_RAD) * d_pplot_size * radius * ((90.0 - current_req_rotator_el) / 90.0); draw_list->AddLine({point_x - 5 * ui_scale, point_y}, {point_x - 12 * ui_scale, point_y}, ImColor(0, 237, 255, 255), 2.0); draw_list->AddLine({point_x + 5 * ui_scale, point_y}, {point_x + 12 * ui_scale, point_y}, ImColor(0, 237, 255, 255), 2.0); draw_list->AddLine({point_x, point_y - 5 * ui_scale}, {point_x, point_y - 12 * ui_scale}, ImColor(0, 237, 255, 255), 2.0); draw_list->AddLine({point_x, point_y + 5 * ui_scale}, {point_x, point_y + 12 * ui_scale}, ImColor(0, 237, 255, 255), 2.0); } } #if 1 if (next_aos_time != 0 && next_los_time != 0) { double timeOffset = 0, ctime = getTime(); if (next_aos_time > ctime) timeOffset = next_aos_time - ctime; else timeOffset = next_los_time - ctime; int hours = timeOffset / 3600; int minutes = fmod(timeOffset / 60, 60); int seconds = fmod(timeOffset, 60); std::string time_dis = (hours < 10 ? "0" : "") + std::to_string(hours) + ":" + (minutes < 10 ? "0" : "") + std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds); ImVec2 cur = ImGui::GetCursorPos(); ImVec2 curs = ImGui::GetCursorScreenPos(); ImVec2 size = ImGui::CalcTextSize((horizons_mode ? horizonsoptions[current_horizons].second.c_str() : satoptions[current_satellite]).c_str()); draw_list->AddRectFilled(curs, ImVec2(curs.x + size.x + 2 * ImGui::GetStyle().FramePadding.x, curs.y + size.y), ImColor(0, 0, 0, 180)); ImGui::TextColored(ImColor(0, 255, 0, 255), "%s", horizons_mode ? horizonsoptions[current_horizons].second.c_str() : satoptions[current_satellite].c_str()); curs = ImGui::GetCursorScreenPos(); char buff[9]; snprintf(buff, sizeof(buff), "Az: %.1f", current_az); size = ImGui::CalcTextSize(buff); draw_list->AddRectFilled(curs, ImVec2(curs.x + size.x + 2 * ImGui::GetStyle().FramePadding.x, curs.y + size.y), ImColor(0, 0, 0, 180)); ImGui::TextColored(ImColor(0, 255, 0, 255), "Az: %.1f", current_az); curs = ImGui::GetCursorScreenPos(); snprintf(buff, sizeof(buff), "El: %.1f", current_el); size = ImGui::CalcTextSize(buff); draw_list->AddRectFilled(curs, ImVec2(curs.x + size.x + 2 * ImGui::GetStyle().FramePadding.x, curs.y + size.y), ImColor(0, 0, 0, 180)); ImGui::TextColored(ImColor(0, 255, 0, 255), "El: %.1f", current_el); if (next_aos_time > ctime) size = ImGui::CalcTextSize(("AOS in" + time_dis).c_str()); else size = ImGui::CalcTextSize(("LOS in" + time_dis).c_str()); ImGui::SetCursorPosY(cur.y + d_pplot_size - 20 * ui_scale); curs = ImGui::GetCursorScreenPos(); draw_list->AddRectFilled(curs, ImVec2(curs.x + size.x + 2 * ImGui::GetStyle().FramePadding.x, curs.y + size.y), ImColor(0, 0, 0, 180)); ImGui::TextColored(ImColor(0, 255, 0, 255), "%s in %s", next_aos_time > ctime ? "AOS" : "LOS", time_dis.c_str()); ImGui::SetCursorPos(cur); } #endif ImGui::Dummy(ImVec2(d_pplot_size + 3 * ui_scale, d_pplot_size + 3 * ui_scale)); } void TrackingWidget::renderSelectionMenu() { bool update_global = false; if (backend_needs_update) style::beginDisabled(); if (ImGui::BeginTable("##trackingradiotable", 2, ImGuiTableFlags_None)) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); if (ImGui::RadioButton("Satellites", !horizons_mode)) { horizons_mode = false; update_global = true; } ImGui::TableSetColumnIndex(1); if (ImGui::RadioButton("Horizons", horizons_mode)) { if (horizonsoptions.size() == 1) horizonsoptions = pullHorizonsList(); horizons_mode = true; update_global = true; } ImGui::EndTable(); } ImGui::SetNextItemWidth(ImGui::GetWindowContentRegionMax().x); if (horizons_mode) { if (ImGui::BeginCombo("###horizonsselectcombo", horizonsoptions[current_horizons].second.c_str())) { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::InputTextWithHint("##horizonssatellitetracking", u8"\uf422 Search", &horizonssearchstr); for (int i = 0; i < (int)horizonsoptions.size(); i++) { bool show = true; if (horizonssearchstr.size() != 0) show = isStringPresent(horizonsoptions[i].second, horizonssearchstr); if (show) { if (ImGui::Selectable(horizonsoptions[i].second.c_str(), i == current_horizons)) { current_horizons = i; update_global = true; } } } ImGui::EndCombo(); } } else { if (ImGui::BeginCombo("###satelliteselectcombo", satoptions[current_satellite].c_str())) { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::InputTextWithHint("##searchsatellitetracking", u8"\uf422 Search", &satsearchstr); for (int i = 0; i < (int)satoptions.size(); i++) { bool show = true; if (satsearchstr.size() != 0) show = isStringPresent(satoptions[i], satsearchstr); if (show) { if (ImGui::Selectable(satoptions[i].c_str(), i == current_satellite)) { current_satellite = i; update_global = true; } } } ImGui::EndCombo(); } } if (backend_needs_update) style::endDisabled(); if (update_global) backend_needs_update = true; } void TrackingWidget::renderObjectStatus() { if (ImGui::BeginTable("##trackingwidgettable", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("Azimuth"); ImGui::TableSetColumnIndex(1); ImGui::Text("%.3f", current_az); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("Elevation"); ImGui::TableSetColumnIndex(1); ImGui::Text("%.3f", current_el); if (next_aos_time != 0 && next_los_time != 0) { double timeOffset = 0, ctime = getTime(); if (next_aos_time > ctime) timeOffset = next_aos_time - ctime; else timeOffset = next_los_time - ctime; int hours = timeOffset / 3600; int minutes = fmod(timeOffset / 60, 60); int seconds = fmod(timeOffset, 60); std::string time_dis = (hours < 10 ? "0" : "") + std::to_string(hours) + ":" + (minutes < 10 ? "0" : "") + std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("Next Event"); ImGui::TableSetColumnIndex(1); ImGui::Text("%s", time_dis.c_str()); } if (!horizons_mode && satellite_object != nullptr) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("Azimuth Rate"); ImGui::TableSetColumnIndex(1); ImGui::Text("%.2f", observation_pos.azimuth_rate * RAD_TO_DEG); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("Elevation Rate"); ImGui::TableSetColumnIndex(1); ImGui::Text("%.2f", observation_pos.elevation_rate * RAD_TO_DEG); } ImGui::EndTable(); } } }