Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Battin's Method? #37

Open
hikari20XX opened this issue Nov 8, 2024 · 0 comments
Open

Support for Battin's Method? #37

hikari20XX opened this issue Nov 8, 2024 · 0 comments

Comments

@hikari20XX
Copy link

hikari20XX commented Nov 8, 2024

Hello!

I've been using this toolkit a lot, and wanted to contribute. Based off the code found here (https://github.com/Spacecraft-Code/Vallado/blob/master/Fortran/ASTIOD.FOR), I have an updated version of Battin's Method. There are some references to other modules within my specific project (shouldn't be crazy to replace them), but hopefully this could be useful to someone!

! Battin method
subroutine solve_battin(r1, r2, tof, theta, tau, soln)
use lambert_utils, only: wp, pi
use orbital_constants, only: mu_canonical
implicit none
real(wp), dimension(3), intent(in) :: r1, r2
real(wp), intent(in) :: tof, theta, tau
type(lambert_soln_type), intent(out) :: soln

! Local variables
real(wp) :: r1_mag, r2_mag, c, s
real(wp) :: RoR, eps, tan2w, rp
real(wp) :: l, m, x, xn, lim1, see_value, denom
real(wp) :: h1, h2, b_var, u, k_value, y_sqrt, y, dt_diff
real(wp) :: a, arg1, arg2, AlpE, BetE, DE, f, g, gdot
real(wp) :: delta_nu, chord
integer :: loops

! Initialize
soln%converged = .false.
soln%iterations = 0
soln%error = huge(1.0_wp)  ! Initialize error to a large number

! Calculate magnitudes and geometry
r1_mag = norm2_vec(r1)
r2_mag = norm2_vec(r2)
chord = norm2_vec(r2 - r1)
s = (r1_mag + r2_mag + chord) / 2.0_wp
RoR = r2_mag / r1_mag
eps = RoR - 1.0_wp
delta_nu = theta
tan2w = 0.25_wp * eps ** 2 / (sqrt(RoR) + RoR * (2.0_wp + sqrt(RoR)))
rp = sqrt(r1_mag * r2_mag) * ((cos(delta_nu / 4.0_wp)) ** 2 + tan2w)

if (delta_nu < pi) then
    l = ((sin(delta_nu / 4.0_wp)) ** 2 + tan2w) / (((sin(delta_nu / 4.0_wp)) ** 2) + tan2w + cos(delta_nu / 2.0_wp))
else
    l = (((cos(delta_nu / 4.0_wp)) ** 2) + tan2w - cos(delta_nu / 2.0_wp)) / (((cos(delta_nu / 4.0_wp)) ** 2) + tan2w)
endif

m = mu_canonical * tof ** 2 / (8.0_wp * rp ** 3)
x = 10.0_wp
xn = l
lim1 = sqrt(m / l)

loops = 0
do while ((abs(xn - x) >= TOL) .and. (loops <= MAX_ITER))
    x = xn
    see_value = see(x)
    denom = 1.0_wp / ((1.0_wp + 2.0_wp * x + l) * (4.0_wp * x + see_value * (3.0_wp + x)))
    h1 = (l + x) ** 2 * (1.0_wp + 3.0_wp * x + see_value) * denom
    h2 = m * (x - l + see_value) * denom

    ! Evaluate cubic
    b_var = 0.25_wp * 27.0_wp * h2 / ((1.0_wp + h1) ** 3)
    if (b_var < -1.0_wp) then
        xn = 1.0_wp - 2.0_wp * l
    else
        u = 0.5_wp * b_var / (1.0_wp + sqrt(1.0_wp + b_var))
        k_value = k_func(u)
        y_sqrt = sqrt(1.0_wp + b_var)
        y = ((1.0_wp + h1) / 3.0_wp) * (2.0_wp + y_sqrt / (1.0_wp + 2.0_wp * u * k_value ** 2))
        xn = sqrt(((1.0_wp - l) * 0.5_wp) ** 2 + m / (y ** 2)) - (1.0_wp + l) * 0.5_wp
    endif
    loops = loops + 1
end do

soln%iterations = loops
dt_diff = abs(xn - x)
soln%error = dt_diff  ! Set the convergence error

if (dt_diff < TOL) then
    soln%converged = .true.
    a = mu_canonical * tof ** 2 / (16.0_wp * rp ** 2 * xn * y ** 2)

    ! Determine orbit type
    if (a < -NEAR_ZERO) then
        ! Hyperbolic case
        arg1 = sqrt(s / (-2.0_wp * a))
        arg2 = sqrt((s - chord) / (-2.0_wp * a))
        AlpE = 2.0_wp * asinh(arg1)
        BetE = 2.0_wp * asinh(arg2)
        DE = AlpE - BetE
        f = 1.0_wp - (a / r1_mag) * (1.0_wp - cosh(DE))
        g = tof - sqrt(-a ** 3 / mu_canonical) * (sinh(DE) - DE)
        gdot = 1.0_wp - (a / r2_mag) * (1.0_wp - cosh(DE))
    else if (a > NEAR_ZERO) then
        ! Elliptical case
        arg1 = sqrt(s / (2.0_wp * a))
        arg2 = sqrt((s - chord) / (2.0_wp * a))
        AlpE = 2.0_wp * asin(arg1)
        BetE = 2.0_wp * asin(arg2)
        DE = AlpE - BetE
        f = 1.0_wp - (a / r1_mag) * (1.0_wp - cos(DE))
        g = tof - sqrt(a ** 3 / mu_canonical) * (DE - sin(DE))
        gdot = 1.0_wp - (a / r2_mag) * (1.0_wp - cos(DE))
    else
        ! Parabolic case (not handled)
        print *, 'Parabolic orbit encountered in solve_battin.'
        soln%converged = .false.
        return
    endif

    ! Compute velocities
    soln%v1 = (r2 - f * r1) / g
    soln%v2 = (gdot * r2 - r1) / g
    soln%a = a
else
    print *, 'solve_battin did not converge within tolerance.'
endif
end subroutine solve_battin

function see(v) result(see_val)
implicit none
real(wp), intent(in) :: v
real(wp) :: see_val
! Local variables
real(wp) :: c(0:20)
real(wp) :: term, termold, del, delold, sum1, eta, sqrt_opv
integer :: i

! Coefficients
c(0) = 0.2_wp
c(1) = 9.0_wp / 35.0_wp
c(2) = 16.0_wp / 63.0_wp
c(3) = 25.0_wp / 99.0_wp
c(4) = 36.0_wp / 143.0_wp
c(5) = 49.0_wp / 195.0_wp
c(6) = 64.0_wp / 255.0_wp
c(7) = 81.0_wp / 323.0_wp
c(8) = 100.0_wp / 399.0_wp
c(9) = 121.0_wp / 483.0_wp
c(10) = 144.0_wp / 575.0_wp
c(11) = 169.0_wp / 675.0_wp
c(12) = 196.0_wp / 783.0_wp
c(13) = 225.0_wp / 899.0_wp
c(14) = 256.0_wp / 1023.0_wp
c(15) = 289.0_wp / 1155.0_wp
c(16) = 324.0_wp / 1295.0_wp
c(17) = 361.0_wp / 1443.0_wp
c(18) = 400.0_wp / 1599.0_wp
c(19) = 441.0_wp / 1763.0_wp
c(20) = 484.0_wp / 1935.0_wp

sqrt_opv = sqrt(1.0_wp + v)
eta = v / ((1.0_wp + sqrt_opv) ** 2)

! Initialize recursion
delold = 1.0_wp
termold = c(0)
sum1 = termold
i = 1

do while ((i <= 20) .and. (abs(termold) > 1.0e-12_wp))
    del = 1.0_wp / (1.0_wp + c(i) * eta * delold)
    term = termold * (del - 1.0_wp)
    sum1 = sum1 + term
    i = i + 1
    delold = del
    termold = term
end do

see_val = (1.0_wp / (8.0_wp * (1.0_wp + sqrt_opv))) * (3.0_wp + sum1 / (1.0_wp + eta * sum1))
end function see

function k_func(v) result(k_value)
implicit none
real(wp), intent(in) :: v
real(wp) :: k_value
! Local variables
real(wp) :: d(0:20)
real(wp) :: del, delold, term, termold, sum1
integer :: i

! Coefficients
d(0) = 1.0_wp / 3.0_wp
d(1) = 4.0_wp / 27.0_wp
d(2) = 8.0_wp / 27.0_wp
d(3) = 2.0_wp / 9.0_wp
d(4) = 22.0_wp / 81.0_wp
d(5) = 208.0_wp / 891.0_wp
d(6) = 340.0_wp / 1287.0_wp
d(7) = 418.0_wp / 1755.0_wp
d(8) = 598.0_wp / 2295.0_wp
d(9) = 700.0_wp / 2907.0_wp
d(10) = 928.0_wp / 3591.0_wp
d(11) = 1054.0_wp / 4347.0_wp
d(12) = 1330.0_wp / 5175.0_wp
d(13) = 1480.0_wp / 6075.0_wp
d(14) = 1804.0_wp / 7047.0_wp
d(15) = 1978.0_wp / 8091.0_wp
d(16) = 2350.0_wp / 9207.0_wp
d(17) = 2548.0_wp / 10395.0_wp
d(18) = 2968.0_wp / 11655.0_wp
d(19) = 3190.0_wp / 12987.0_wp
d(20) = 3658.0_wp / 14391.0_wp

! Initialize recursion
sum1 = d(0)
delold = 1.0_wp
termold = d(0)
i = 1

do while ((i <= 20) .and. (abs(termold) > 1.0e-12_wp))
    del = 1.0_wp / (1.0_wp + d(i) * v * delold)
    term = termold * (del - 1.0_wp)
    sum1 = sum1 + term
    i = i + 1
    delold = del
    termold = term
end do

k_value = sum1
end function k_func

! Calculate tau parameter (Battin's formulation)
tau = sqrt(r1_mag * r2_mag * (1.0_wp + cos(theta))) / (r1_mag + r2_mag)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant