Enjo
Here (below) is a revised version of the code that includes the two-variable root-finding solution. This code is valid for a wide range of spacecraft with widely varying thrust characteristics approaching periapsis on an elliptical or hyperbolic trajectory and wishing to enter a circular orbit by executing a retrograde burn. (I have not yet considered circularisation at apoapsis, but the code would need some minor changes to address that case.)
The calculation of the duration of the burn and the time (before periapsis) to execute the orbit insertion burn is iterative. This means that the calculation needs a reasonable initial guess in order to converge quickly. I propose that BTC's current estimates provide that initial guess. In particular, the two quantities of interest are:
1. the number of seconds [MATH]\delta t[/MATH] before periapsis when the orbit insertion burn starts; and
2. the duration, [MATH]t_m[/MATH] of the orbit insertion burn.
First, you need to convert [MATH]\delta t[/MATH] into an orbital radius by solving for the eccentric anomaly, if the approach orbit is elliptical:
[MATH]\delta t=\sqrt{\frac{a^3}{\mu }}\, (E-e\, \sin E)[/MATH]
and the following if the approach orbit is hyperbolic:
[MATH]\delta t=\sqrt{\frac{-a^3}{\mu }}\, (e\, \sinh E - E)[/MATH]
Second, you need to calculate the initial vale of [MATH]R[/MATH]:
[MATH]R = a (1- e\, \cos E)[/MATH]
if the approach orbit is elliptical; and
[MATH]R = a (1 - e\, \cosh E )[/MATH]
if the approach orbit is hyperbolic.
The resulting value of [MATH]R[/MATH] and [MATH]t_m[/MATH] are the initial values for the orbit insertion algorithm below.
In most cases, convergence should be rapid - but, as with all things involving non-linear root-finding, there are no guarantees. At the moment, the algorithm has few checks to test for convergence. Enhancing the 'robustness' of the algorithm may be worthwhile thing to do at some later stage.
The outputs of the algorithm are:
1. the time in seconds prior to periapsis when the orbit insertion burn should start. This is a 'refined' estimate of BTC's initial guess.
2. the duration of the orbit insertion burn in seconds. Again, this is refinement of BTCs estimate of the same.
3. the radius of the final circular orbit. This is an additional piece of information that BTC does not currently provide which, in effect, assumes that the radius of the final orbit will be the same as the orbital periapsis. For short-duration burn, this is almost true, but for low thrust, long duration orbit insertion burns there can be a significant drop in the final orbital radius. What you choose to do with this additional information is up to you, but I think it a value that should be reported by BTC.)
The algorithm also needs to link-up to relevant Orbiter parameters:
1. the value of the current semi-major axis, [MATH]a[/MATH];
2. the value of the current orbital eccentricity, [MATH]e[/MATH];
3. the value of 'GM' for the central gravitating body, [MATH]\mu[/MATH];
4. the current mass of the spacecraft, [MATH]m_0[/MATH];
5. the mass flow rate for the engines chosen to execute the retrograde burn, [MATH]\gamma[/MATH]; and
6. the effective exhaust velocity of the combustion gases of those engines [MATH]v_e[/MATH].
To obtain these values, you will of course need to interrogate Orbiter's system.
For the specific example given in the code below (which considers a Shuttle A on approach to perilune aiming to use its main engines to enter into a circular orbit. The algorithm reports that:
a. the duration of the orbit insertion burn is: 19.595 seconds;
b. the orbit insertion burn should commence 10.277 seconds before periapsis; and
c. the radius of the final circular orbit will be 3380913.4 metres.
In comparison, BTC reports that the duration of the orbit insertion burn is: 19.591 seconds; and the insertion burn should commence 9.831 seconds before periapsis. Here, the algorithm opts for a slightly longer burn time because the final orbital radius is very slightly lower than that assumed by BTC. The material difference between the two solutions is that the algorithm estimates that the burn should commence 0.45 seconds earlier than that calculated by BTC. For fast, high-thrust orbit insertion burns such as this the differences in solutions are not particularly material. But for low-thrust orbit insertion burns, the differences are much greater.
At this stage, to hook this up to BTC, is there anything else that you need?
Code:
// main.c
// A procedure to use 'automatic differentiation' to allow fast computation of the derivtaives
// to arbitrarily high order - first used in celestial mechanics problems
// For further details see, for example, http://www.maia.ub.edu/~angel/taylor/taylor.pdf
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// parameters used to define the initial orbit of the spacecraft
#define MU 4.9048695e12 // the 'GM' parameter for the central gravitating body (m^3 s^-2)
#define SMAJ 84781532.99 // the semi-major axis of the spacecraft's current orbit (m)
#define ECC 0.960122 // the orbital eccentricity of the spacecraft's current orbit (n/a)
// parameters used to define the initial state of the spacecraft
#define M0 87200.0 // the spacecraft's initial mass (kg)
#define GAMMA 64.507 // the fuel mass fow rate (kg/s)
#define VE 33000.0 // the effective velocity of engine exhaust gases (m/s)
// parameters used to define numerical integration order and step-size
#define DELTAT 20.0 // the integration step-size (s)
#define N 20 // the order of the Taylor series expansion of the integration
int taylorIntegation( double er[N+1], // time derivatives of the radial component of the orbital eccentricity vector
double et[N+1], // time derivatives of the transbsrese component of the orbital eccentricity vector
double th[N+1], // time derivatives of the angular component of the spacecraft's position
double h [N+1], // time derivatives of the spacecrafts specific angular momentum
double f [N+1] // time derivatives of the spacecraft's acceleration
) {
// declare the working arrays
double u01[N], u02[N], u05[N], u06[N], u07[N], u08[N], u09[N], u10[N], u11[N], u12[N], u13[N], u14[N], u15[N];
double v01[N], v02[N];
double w01[N], w02[N], w04[N], w05[N], w06[N], w07[N], w08[N], w09[N], w10[N];
// declare miscellaeous
double alpha;
// the integration step - building Taylor series expansions up to 20th order.
for ( int n = 0; n < N; n++) {
u01[n] = er[n];
u02[n] = et[n];
v01[n] = f [n];
w01[n] = h [n];
// calculate f'(t) - (the derivatives of the spacecraft's thrust function)
v02[n] = 0;
for (int j = 0; j <= n; j++) { // f(t)^2
v02[n] += v01[j] * v01[n-j];
}
v02[n] = v02[n] / VE; // f(t)^2 / ve
f[n+1] = v02[n] / (n+1); // f'(t) = f(t)^2 / ve
// calculate h'(t) and higher order derivatives
w02[n] = 0;
for (int j = 0; j <= n; j++) { // h(t)^2
w02[n] += w01[j] * w01[n-j];
}
w04[n] = 0;
for (int j = 0; j <= n; j++) { // f(t) * h(t)^2
w04[n] += w02[j] * v01[n-j];
}
w05[n] = u01[n];
if (n == 0) { // 1 + e_r(t)
w05[n] += 1;
}
w06[n] = 0;
for (int j = 0; j <= n; j++) { // (1 + e_r(t))^2
w06[n] += w05[j] * w05[n-j];
}
w07[n] = 0;
for (int j = 0; j <= n; j++) { // e_t(t)^2
w07[n] += u02[j] * u02[n-j];
}
w08[n] = w06[n] + w07[n]; // A = (1 + e_r(t))^2 + e_t(t)^2
alpha = -0.5;
if (n == 0) {
w09[n] = pow( w08[0], alpha ); // A^-1/2
} else {
w09[n] = 0;
for (int j = 0; j <= n-1; j++) {
w09[n] += (n * alpha - j * (1 + alpha)) * w08[n-j] * w09[j];
}
w09[n] = w09[n] / n / w08[0];
}
w10[n] = 0;
for (int j = 0; j <= n; j++) {
w10[n] += w09[j] * w04[n-j];
}
w10[n] = - w10[n] / MU; // - mu^-1 * f(t) * h(t)^2 * A^-1/2
h[n+1] = w10[n] / (n+1);
// calculate theta'(t) and higher order derivatives
alpha = -3.0;
if (n == 0) {
u05[n] = pow( w01[0], alpha ); // h(t)^-3
} else {
u05[n] = 0;
for (int j = 0; j <= n-1; j++) {
u05[n] += (n * alpha - j * (1 + alpha)) * w01[n-j] * u05[j];
}
u05[n] = u05[n] / n / w01[0];
}
u06[n] = 0;
for (int j = 0; j <= n; j++) {
u06[n] += u05[j] * w06[n-j];
}
u06[n] = u06[n] * MU * MU; // mu^2 * (1 + e_r(t))^2 * h(t)^-3
th[n+1] = u06[n] / (n+1); // th'(t) = u06 / (n+1)
// calculate e_r'(t) and higher order derivatives
u07[n] = 0;
for (int j = 0; j <= n; j++) {
u07[n] += u06[j] * u02[n-j]; // mu^2 * (1 + e_r(t))^2 * h(t)^-3 * e_t(t)
}
u08[n] = 0;
for (int j = 0; j <= n; j++) { // - mu^-1 * f(t) * h(t)^2 * A^-1/2 * (1 + e_r(t))
u08[n] += w10[j] * w05[n-j];
}
alpha = -1.0;
if (n == 0) {
u09[n] = pow( w01[0], alpha ); // h(t)^-1
} else {
u09[n] = 0;
for (int j = 0; j <= n-1; j++) {
u09[n] += (n * alpha - j * (1 + alpha)) * w01[n-j] * u09[j];
}
u09[n] = u09[n] / n / w01[0];
}
u10[n] = 0;
for (int j = 0; j <= n; j++) { // - mu^-1 * f(t) * h(t) * A^-1/2 * (1 + e_r(t))
u10[n] += u09[j] * u08[n-j];
}
u11[n] = + u07[n] + 2 * u10[n]; // mu^2*(1+e_r(t))^2*h(t)^-3*e_t(t) - 2*mu^-1*f(t)*h(t)*A^-1/2*(1 + e_r(t))
er[n+1] = u11[n] / (n+1); // e_r'(t) = u11 / (n+1)
//calculate e_t'(t) and higher order derivatives
u12[n] = 0;
for (int j = 0; j <= n; j++) { // mu^2 * (1 + e_r(t))^2 * h(t)^-3 * e_r(t)
u12[n] += u06[j] * u01[n-j];
}
u13[n] = 0;
for (int j = 0; j <= n; j++) {
u13[n] += w10[j] * u02[n-j]; // - mu^-1 * f(t) * h(t)^2 * A^-1/2 * e_t(t)
}
u14[n] = 0;
for (int j = 0; j <= n; j++) { // - mu^-1 * f(t) * h(t) * A^-1/2 * e_t(t)
u14[n] += u13[j] * u09[n-j];
}
u15[n] = - u12[n] + 2 * u14[n]; // - mu^2*(1 + e_r(t))^2*h(t)^-3*e_r(t) - 2*mu^-1*f(t)*h(t)*A^-1/2*e_t(t)
et[n+1] = u15[n] / (n+1); // e_t'(t) = u15 / (n+1)
}
return 0;
}
int updateInitialValues( double er[N+1], // time derivatives of the radial component of the orbital eccentricity vector
double et[N+1], // time derivatives of the transbsrese component of the orbital eccentricity vector
double th[N+1], // time derivatives of the angular component of the spacecraft's position
double h [N+1], // time derivatives of the spacecrafts specific angular momentum
double f [N+1], // time derivatives of the spacecraft's acceleration
double dt // the time step
) {
double temp;
temp = 0; for (int j = N; j >=0; j--) {temp = (temp * dt + er[j]);}; er[0] = temp;
temp = 0; for (int j = N; j >=0; j--) {temp = (temp * dt + et[j]);}; et[0] = temp;
temp = 0; for (int j = N; j >=0; j--) {temp = (temp * dt + th[j]);}; th[0] = temp;
temp = 0; for (int j = N; j >=0; j--) {temp = (temp * dt + f [j]);}; f [0] = temp;
temp = 0; for (int j = N; j >=0; j--) {temp = (temp * dt + h [j]);}; h [0] = temp;
return 0;
}
int f ( double R, // the orbital radius at the start of the orbit insertion burn
double tm, // the duration of the orbit insertion burn
double e[4], // the radial and transverse components of the eccentricity vector at the end of the orbital inserion burn
double g[3]
) {
// declare result arrays - normalised Taylor series derivatives up to and including 10th order
double er[N+1]; // the component of the eccentricity vector in the radial direction
double et[N+1]; // the component of the eccentricity vector in the 'theta' direction
double th[N+1]; // the spatial angular coordinate of the spacecraft
double h [N+1]; // the specific angular momentum of the spacecraft
double f [N+1]; // the spacecraft's thrust function g * ve / (m0 - g * t)
// initalise the values to start the integration processteration process
er[0] = SMAJ * (1 - ECC * ECC) / R - 1;
et[0] = sqrt( (1 - ECC * ECC) * (SMAJ * ECC + SMAJ - R) * (SMAJ * ECC - SMAJ + R)) / R;
th[0] = 0.0;
h [0] = sqrt(MU * SMAJ * (1 - ECC * ECC));
f [0] = GAMMA * VE / M0;
// declare miscellanous quantities
int status;
double t, temp;
t = tm;
// carry out the integration step nine times - with each integration step spanning 1000 seconds
while (t >= DELTAT) {
status = taylorIntegation (er, et, th, h, f);
status = updateInitialValues (er, et, th, h, f, DELTAT);
t -= DELTAT;
}
status = taylorIntegation (er, et, th, h, f);
status = updateInitialValues (er, et, th, h, f, t);
// calculate the components of the eccentricity vector at the end of the orbit insertion burn
e[0] = er[0];
e[1] = et[0];
// calculate the firs derivatives with respect to time of the eccentricity vector at the end of the orbit insertion burn
temp = 0; for (int j = N; j >=1; j--) {temp = (temp * t + j * er[j]);}; e[2] = temp;
temp = 0; for (int j = N; j >=1; j--) {temp = (temp * t + j * et[j]);}; e[3] = temp;
g[0] = th[0];
g[1] = f [0];
g[2] = h [0];
return 0;
}
int main(int argc, const char * argv[]) {
// INPUTS
double r = 3380938.0; // the orbital radius at the start of the orbit insertion burn
double tm = 19.586; // the duration of the orbit insertion burn
double dr = 1.0; // the small distance displacement for calculating derivatives of the final eccentricity vector with resepct to r;
// OUTPUTS
double e[4]; // the radial and transverse components of the eccentricity vector at the end of the orbital insertion burn
// e[0] holds the radial component of the eccentricity vector
// e[1] holds the transverse component of the eccentricity vector
// e[2] holds the derivative of the radial component with respect to time
// e[3] holds the derivative of the transverse component with respect to time
double g[3]; // a vector to hold other useful information to be used once the Newton-Raphson iteration has been completed
// g[0] holds the the angle variable after orbit insertion
// g[1] holds the spacecraft acceleration due to thrust immediatel prior to the end of the insertion burn
// g[2] holds the spacecraft's specific angular momentum
double f0[2]; // a 2-vector declaration to hold the values of the orbital eccentricity vector at the current N-R iteration
double jac[2][2]; // a 2 x 2 array declaration to hold the Jacobian
double det; // the determinant of the Jacobian
int flag = 1;
// perform the Newton-Raphson (N-R) root-finding algorithm
int status;
while ((sqrt(f0[0] * f0[0] + f0[1] * f0[1]) > 1.e-8) || flag == 1) {
flag = 0;
status = f(r + dr, tm, e, g);
jac[0][0] = e[0];
jac[1][0] = e[1];
status = f(r - dr, tm, e, g);
jac[0][0]-= e[0]; jac[0][0] = jac[0][0] / dr / 2;
jac[1][0]-= e[1]; jac[1][0] = jac[1][0] / dr / 2;
status = f(r, tm, e, g);
jac[0][1] = e[2];
jac[1][1] = e[3];
f0[0] = e[0];
f0[1] = e[1];
det = jac[0][0] * jac[1][1] - jac[0][1] * jac[1][0];
r = r - (+ jac[1][1] * f0[0] - jac[0][1] * f0[1]) / det;
if (r <= SMAJ * (1 - ECC)) {
r = SMAJ * (1 - ECC) + 100;
}
if (if (r >= SMAJ * (1 + ECC) && a > 0) {) {
r = SMAJ * (1 + ECC) - 100;
}
tm = tm - (- jac[1][0] * f0[0] + jac[0][0] * f0[1]) / det;
}
status = f(r, tm, e, g);
f0[0] = e[0];
f0[1] = e[1];
// print the solution
// 'r' is the orbital radius of the spacecraft at the start of the orbit insertion burn (this can be converted to a time measure.)
// 'tm' is the duration (seconds) of the orbit insertion burn
// 'ecc' is the final orbital eccentricity at the end of the orbit insertion burn. This should be zero for a circular orbit
// 'r_c' is the orbital radius at the end of the burn (metres). For entry into a circular orbit, it also the radius of the final circular orbit.
// 'tbp' is the time before (current) periapsis when the orbit insertion burn should start.
printf("r %2.10f\n", r );
printf("tm %2.10f\n", tm );
printf("\n");
printf("ecc %2.10f\n", sqrt(f0[0] * f0[0] + f0[1] * f0[1]) );
printf("r_c %2.10f\n", g[2] * g[2] / MU / (1 + f0[0]));
printf("tbp %2.10f\n", sqrt(SMAJ/MU)*(sqrt(SMAJ*SMAJ*(-1 + ECC*ECC) + 2*SMAJ*r - r*r) - 2*SMAJ*atan(sqrt((SMAJ*(-1 + ECC) + r)/(SMAJ + SMAJ*ECC - r)))));
printf("\n");
return 0;
}