Last active
June 13, 2025 08:36
-
-
Save pzarzycki/6a6b31f933e5aa59730293c15ec0bb2f to your computer and use it in GitHub Desktop.
Flight profile 2.5D
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Revised version of FlightProfile with time in minutes | |
#include <cmath> | |
#include <vector> | |
#include <iostream> | |
#include <algorithm> | |
struct FlightState { | |
double t; // time in minutes | |
double altitude; // meters | |
double speed; // km/h | |
double downrange; // meters | |
}; | |
struct Airport { | |
double lat; | |
double lon; | |
double elev; | |
}; | |
class FlightProfile { | |
public: | |
FlightProfile(Airport dep, Airport dest, double total_time_min) | |
: dep(dep), dest(dest), total_time_min(total_time_min), time_step(10.0) { | |
compute_total_distance(); | |
compute_profile(); | |
} | |
FlightState get_state_at_time(double t) const { | |
if (t <= 0) return profile.front(); | |
if (t >= total_time_min) { | |
if (profile.size() < 2) return profile.back(); | |
const FlightState& p1 = profile[profile.size() - 2]; | |
const FlightState& p2 = profile.back(); | |
double f = (total_time_min - p1.t) / (p2.t - p1.t); | |
return FlightState{ | |
total_time_min, | |
interpolate(f, 0, 1, p1.altitude, p2.altitude), | |
interpolate(f, 0, 1, p1.speed, p2.speed), | |
interpolate(f, 0, 1, p1.downrange, p2.downrange) | |
}; | |
} | |
size_t idx = static_cast<size_t>(t / time_step); | |
const FlightState& p1 = profile[idx]; | |
const FlightState& p2 = profile[idx + 1]; | |
double f = (t - p1.t) / (p2.t - p1.t); | |
return FlightState{ | |
t, | |
interpolate(f, 0, 1, p1.altitude, p2.altitude), | |
interpolate(f, 0, 1, p1.speed, p2.speed), | |
interpolate(f, 0, 1, p1.downrange, p2.downrange) | |
}; | |
} | |
const std::vector<FlightState>& get_profile() const { return profile; } | |
double get_total_distance() const { return total_distance_m; } | |
private: | |
Airport dep, dest; | |
double total_time_min; // minutes | |
double time_step; // minutes | |
double total_distance_m; | |
std::vector<FlightState> profile; | |
const double alt_cruise = 11000.0; | |
const double speed_cruise = 900.0; // km/h | |
const double takeoff_frac = 0.03; | |
const double climb_frac = 0.17; | |
const double cruise_frac = 0.60; | |
const double descent_frac = 0.15; | |
const double approach_frac = 0.05; | |
static double interpolate(double x, double x0, double x1, double y0, double y1) { | |
return y0 + (y1 - y0) * ((x - x0) / (x1 - x0)); | |
} | |
static double haversine(double lat1, double lon1, double lat2, double lon2) { | |
const double R = 6371.0; // km | |
double dlat = to_rad(lat2 - lat1); | |
double dlon = to_rad(lon2 - lon1); | |
lat1 = to_rad(lat1); | |
lat2 = to_rad(lat2); | |
double a = sin(dlat/2)*sin(dlat/2) + cos(lat1)*cos(lat2)*sin(dlon/2)*sin(dlon/2); | |
double c = 2 * atan2(sqrt(a), sqrt(1 - a)); | |
return R * c; | |
} | |
static double to_rad(double deg) { return deg * M_PI / 180.0; } | |
void compute_total_distance() { | |
double km = haversine(dep.lat, dep.lon, dest.lat, dest.lon); | |
total_distance_m = km * 1000.0; | |
} | |
void compute_profile() { | |
size_t n = static_cast<size_t>(std::ceil(total_time_min / time_step)) + 1; | |
std::vector<double> nominal_speeds(n); | |
for (size_t i = 0; i < n; ++i) { | |
double t = i * time_step; | |
double t_frac = t / total_time_min; | |
if (t_frac < takeoff_frac) { | |
double phase_t = t_frac / takeoff_frac; | |
nominal_speeds[i] = interpolate(phase_t, 0, 1, 0, 100.0); | |
} else if (t_frac < takeoff_frac + climb_frac) { | |
double phase_t = (t_frac - takeoff_frac) / climb_frac; | |
nominal_speeds[i] = interpolate(phase_t, 0, 1, 100.0, speed_cruise); | |
} else if (t_frac < takeoff_frac + climb_frac + cruise_frac) { | |
nominal_speeds[i] = speed_cruise; | |
} else if (t_frac < takeoff_frac + climb_frac + cruise_frac + descent_frac) { | |
double phase_t = (t_frac - (takeoff_frac + climb_frac + cruise_frac)) / descent_frac; | |
nominal_speeds[i] = interpolate(phase_t, 0, 1, speed_cruise, 200.0); | |
} else { | |
double phase_t = (t_frac - (takeoff_frac + climb_frac + cruise_frac + descent_frac)) / approach_frac; | |
nominal_speeds[i] = interpolate(phase_t, 0, 1, 200.0, 0); | |
} | |
} | |
double total_nominal_distance = 0; | |
for (double v : nominal_speeds) total_nominal_distance += (v * time_step / 60.0) * 1000.0; // convert to meters | |
double scale = total_distance_m / total_nominal_distance; | |
double downrange = 0; | |
profile.reserve(n); | |
for (size_t i = 0; i < n; ++i) { | |
double t = i * time_step; | |
double t_frac = t / total_time_min; | |
double speed = nominal_speeds[i] * scale; | |
double altitude = 0; | |
if (t_frac < takeoff_frac) { | |
double phase_t = t_frac / takeoff_frac; | |
altitude = interpolate(phase_t, 0, 1, dep.elev, dep.elev + 300); | |
} else if (t_frac < takeoff_frac + climb_frac) { | |
double phase_t = (t_frac - takeoff_frac) / climb_frac; | |
altitude = interpolate(phase_t, 0, 1, dep.elev + 300, alt_cruise); | |
} else if (t_frac < takeoff_frac + climb_frac + cruise_frac) { | |
altitude = alt_cruise; | |
} else if (t_frac < takeoff_frac + climb_frac + cruise_frac + descent_frac) { | |
double phase_t = (t_frac - (takeoff_frac + climb_frac + cruise_frac)) / descent_frac; | |
altitude = interpolate(phase_t, 0, 1, alt_cruise, dest.elev + 300); | |
} else { | |
double phase_t = (t_frac - (takeoff_frac + climb_frac + cruise_frac + descent_frac)) / approach_frac; | |
altitude = interpolate(phase_t, 0, 1, dest.elev + 300, dest.elev); | |
} | |
profile.push_back({t, altitude, speed, downrange}); | |
downrange += (speed * time_step / 60.0) * 1000.0; | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment