satdump/src-core/projection/projection.cpp

185 lines
6.7 KiB
C++
Raw Permalink Normal View History

2025-01-09 13:35:23 +01:00
#include "projection.h"
#include "logger.h"
#include "common/geodetic/vincentys_calculations.h" // TODOREWORK MOVE OUT
2025-05-27 18:42:18 +01:00
#include "projection/standard/proj_json.h"
2025-01-09 13:35:23 +01:00
#include "raytrace/gcp_compute.h"
namespace satdump
{
2025-05-27 18:42:18 +01:00
namespace projection
2025-01-09 13:35:23 +01:00
{
2025-04-15 19:06:12 +01:00
Projection::Projection() {}
2025-01-09 13:35:23 +01:00
Projection::~Projection()
{
if (fwd_type == PROJ_STANDARD || inv_type == PROJ_STANDARD)
::proj::projection_free(&std_proj);
}
bool Projection::init(bool fwd, bool inv)
{
///////////////////////////////////////////////////////////
// We need image width/height
///////////////////////////////////////////////////////////
if (d_cfg.contains("width"))
width = d_cfg["width"];
else
throw satdump_exception("Image width must be present!");
if (d_cfg.contains("height"))
height = d_cfg["height"];
else
throw satdump_exception("Image height must be present!");
///////////////////////////////////////////////////////////
// Get channel pre-transform, if present
///////////////////////////////////////////////////////////
if (d_cfg.contains("transform"))
transform = d_cfg["transform"];
else
transform.init_none();
2025-04-28 19:43:01 +02:00
///////////////////////////////////////////////////////////
// Get optional second channel pre-transform, if present
///////////////////////////////////////////////////////////
if (d_cfg.contains("transform2"))
{
has_2nd_transform = true;
transform2 = d_cfg["transform2"];
}
2025-01-09 13:35:23 +01:00
///////////////////////////////////////////////////////////
// Attempt to setup a standard projection first
///////////////////////////////////////////////////////////
try
{
std_proj = d_cfg;
if (!::proj::projection_setup(&std_proj))
{
fwd_type = inv_type = PROJ_STANDARD;
2025-03-13 11:22:37 +01:00
if (d_cfg.contains("proj_timestamp"))
{
2025-05-24 20:59:22 +02:00
// logger->warn("Using projection timestamps for timestamp feedback. May be inacurate!"); // Disabled usually as it triggers... Everytime the proj is init
2025-03-13 11:22:37 +01:00
proj_timestamp = d_cfg["proj_timestamp"];
}
2025-01-09 13:35:23 +01:00
return true;
}
}
catch (std::exception &)
{
logger->trace("Not a standard projection!");
}
///////////////////////////////////////////////////////////
2025-02-03 07:48:52 +01:00
// If this didn't work, we can attempt a raytraced proj
2025-01-09 13:35:23 +01:00
///////////////////////////////////////////////////////////
try
{
raytracer = get_satellite_raytracer(d_cfg);
if (raytracer)
{
fwd_type = PROJ_INVALID;
inv_type = PROJ_RAYTRACER;
if (fwd)
{
logger->critical("Forward on raytrace is imperfect!");
auto gcps = compute_gcps(d_cfg);
2025-01-17 18:12:23 +01:00
tps_fwd = std::make_shared<satdump::proj::LatLonTpsProjHelper>(gcps, 1, 0);
2025-01-09 13:35:23 +01:00
fwd_type = PROJ_THINPLATESPLINE;
}
}
return true;
}
2025-01-17 18:12:23 +01:00
catch (std::exception &e)
2025-01-09 13:35:23 +01:00
{
2025-01-17 18:12:23 +01:00
logger->trace("Not a raytraced projection! : %s", e.what());
2025-01-09 13:35:23 +01:00
}
///////////////////////////////////////////////////////////
// And finally, the special case of simple GCPs
///////////////////////////////////////////////////////////
if (d_cfg["type"] == "normal_gcps")
logger->critical("GCPs ALONE SUPPORT TBD!");
return false;
}
bool Projection::forward(geodetic::geodetic_coords_t pos, double &x, double &y, bool except)
2025-01-09 13:35:23 +01:00
{
if (fwd_type == PROJ_STANDARD)
{
pos.toDegs(); // TODOREWORK?
if (::proj::projection_perform_fwd(&std_proj, pos.lon, pos.lat, &x, &y))
return 1;
}
else if (fwd_type == PROJ_THINPLATESPLINE)
{
// Perform TPS
2025-01-17 18:12:23 +01:00
tps_fwd->forward(pos, x, y);
2025-01-09 13:35:23 +01:00
return 0; // We do NOT want to run the ChannelTransform in reverse, TPS takes care of it already!
}
else
{
if (except)
throw satdump_exception("Invalid forward projection type! " + d_cfg["type"].get<std::string>());
else
return 1;
2025-01-09 13:35:23 +01:00
}
// Run channel transform, might do nothing if no transform is needed
transform.reverse(&x, &y);
2025-04-28 19:43:01 +02:00
if (has_2nd_transform)
transform2.reverse(&x, &y);
2025-01-09 13:35:23 +01:00
return 0;
}
bool Projection::inverse(double x, double y, geodetic::geodetic_coords_t &pos, double *otime, bool except)
2025-01-09 13:35:23 +01:00
{
// Run channel transform, might do nothing if no transform is needed
2025-04-28 19:43:01 +02:00
if (has_2nd_transform)
transform2.forward(&x, &y);
2025-01-09 13:35:23 +01:00
transform.forward(&x, &y);
if (inv_type == PROJ_STANDARD)
{
pos.toDegs(); // TODOREWORK?
2025-03-13 11:22:37 +01:00
if (otime != nullptr)
*otime = proj_timestamp;
2025-01-09 13:35:23 +01:00
return ::proj::projection_perform_inv(&std_proj, x, y, &pos.lon, &pos.lat);
}
else if (inv_type == PROJ_RAYTRACER)
{
2025-01-12 20:25:45 +01:00
return raytracer->get_position(x, y, pos, otime);
2025-01-09 13:35:23 +01:00
}
else
{
if (except)
throw satdump_exception("Invalid inverse projection type! " + d_cfg["type"].get<std::string>());
else
return 1;
2025-01-09 13:35:23 +01:00
}
}
void Projection::to_json(nlohmann::json &j) const
{
j = d_cfg;
2025-01-22 20:08:00 +01:00
if (height != -1)
j["height"] = height;
if (width != -1)
j["width"] = width;
2025-01-09 13:35:23 +01:00
}
void Projection::from_json(const nlohmann::json &j)
{
d_cfg = j; // TODOREWORK de-init?
2025-01-22 20:08:00 +01:00
height = j.contains("height") ? j["height"].get<int>() : -1;
width = j.contains("width") ? j["width"].get<int>() : -1;
2025-01-09 13:35:23 +01:00
fwd_type = PROJ_INVALID;
inv_type = PROJ_INVALID;
2025-09-04 01:49:14 +02:00
init(0, 0);
2025-01-09 13:35:23 +01:00
}
} // namespace projection
2025-04-15 19:06:12 +01:00
} // namespace satdump