Skip to content

Instantly share code, notes, and snippets.

@pzarzycki
Last active June 13, 2025 08:36
Show Gist options
  • Save pzarzycki/6a6b31f933e5aa59730293c15ec0bb2f to your computer and use it in GitHub Desktop.
Save pzarzycki/6a6b31f933e5aa59730293c15ec0bb2f to your computer and use it in GitHub Desktop.
Flight profile 2.5D
// 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