Analytical solution of 2BP

Basic concepts

  • Vis-Viva equation: It is a fundamental equation in astrodynamics that relates the speed of a satellite to its distance from the central body and the semi-major axis of its orbit. It is derived from the conservation of mechanical energy in an orbiting system. The total specific mechanical energy, \(\epsilon\), is the sum of the specific kinetic energy and specific potential energy:

\[ \epsilon = \frac{v^2}{2} - \frac{\mu}{r}, \]

where \(v\) is the orbital velocity, \(r\) is the distance from the central body, \(\mu\) is the standard gravitational parameter (\(\mu = G(M+m) \approx GM \approx 398600\ \text{km}^3/\text{s}^2\) for the Earth-Satellite system).

For any bound orbit, the total specific mechanical energy remains constant and can also be expressed in terms of the semi-major axis, \(a\):

\[ \epsilon = -\frac{\mu}{2a}. \]

Equating the two expressions for \(\epsilon\):

\[ \frac{v^2}{2} - \frac{\mu}{r} = -\frac{\mu}{2a}. \]

Rearranging to solve for \(v^2\):

\[ \boxed{ v^2 = \mu \left(\frac{2}{r} - \frac{1}{a}\right) } \]

  • Conic sections are the curves obtained by intersecting a plane with a double-napped cone. In orbital mechanics, they describe the possible paths of celestial bodies under the influence of gravity in the two-body problem. The four types of conic sections are:
  • Circle: A closed curve with eccentricity \(e = 0\); has negative specific orbital energy.
  • Ellipse: A closed curve with eccentricity \(0 < e < 1\) ; has negative specific orbital energy.
  • Parabola: An open curve with eccentricity \(e = 1\), representing a trajectory that just escapes the gravitational influence of the central body; has zero specific orbital energy.
  • Hyperbola: An open curve with eccentricity \(e > 1\), representing a trajectory that escapes the gravitational influence of the central body; has positive specific orbital energy.

Here is a python code that plots the orbits of different eccentricities with a common focus at the origin.

Code
# Converted from MATLAB code by Suyog Soman (MTech, AFM 2023-2025) with GPT-4o

import numpy as np
import matplotlib.pyplot as plt

# List of eccentricity values
e_values = [0, 0.7, 1, 2.5]

# Fixed periapsis distance
r_p = 1

plt.figure(figsize=(10, 10))
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

for i, e in enumerate(e_values):
    col = colors[i % len(colors)]
    # p is determined from the periapsis distance: r_p = p/(1+e)  =>  p = r_p*(1+e)
    p = r_p * (1 + e)
    
    if e < 1:
        # Elliptical orbit: full 0 to 2pi is valid.
        theta = np.linspace(0, 2*np.pi, 500)
        r = p / (1 + e * np.cos(theta))
    elif np.isclose(e, 1):
        # Parabolic orbit: avoid theta = pi where r blows up.
        theta = np.linspace(-np.pi+0.1, np.pi-0.1, 500)
        r = p / (1 + e * np.cos(theta))
    else:
        # Hyperbolic orbit: valid for theta where denominator is positive
        theta_inf = np.arccos(-1/e)
        theta = np.linspace(-theta_inf, theta_inf, 500)
        r = p / (1 + e * np.cos(theta))
    
    # Convert polar to Cartesian coordinates
    x = r * np.cos(theta)
    y = r * np.sin(theta)
    plt.plot(x, y, label=f"e={e}", color=col)

# Mark the common focus at the origin.
plt.plot(0, 0, 'ko', label='Focus')

plt.xlabel("x")
plt.ylabel("y")
plt.title("Orbits with Common Focus (rp = 1)")
plt.legend(loc='upper right')
plt.grid(True)
plt.axis('equal')
plt.xlim(-6, 6)
plt.ylim(-6, 6)
plt.show()

Classical Orbital Elements

Classical orbital elements are a set of six parameters that completely define the size, shape, orientation of an orbit, and the position of the satellite along the orbit. They are defined as:

  • Semi-major axis, \(a\): Defines the size of the orbit.
  • Eccentricity, \(e\): Describes the shape of the orbit (circular: \(e=0\), elliptical: \(0<e<1\), parabolic: \(e=1\), hyperbolic: \(e>1\)).
  • Inclination, \(i\): The tilt of the orbit with respect to the reference plane (usually the equatorial plane).
  • Right Ascension of the Ascending Node (RAAN), \(\Omega\): Specifies the horizontal orientation of the orbit by locating the ascending node.
  • Argument of Perigee, \(\omega\): Describes the orientation of the ellipse within the orbital plane.
  • True Anomaly, \(\nu\): Gives the satellite’s position along the orbit at a specific time.

The figure below illustrates the definitions of some of these these elements in the context of an elliptical orbit.

Classical Orbital Elements

Figure 5.1: Classical Orbital Elements (By Lasunncty at the English Wikipedia, CC BY-SA 3.0, Link)

Eccentricity \(e\) describes how “stretched” the ellipse is. The inclination \(i\) is the angle between the orbital plane and the reference plane (the equatorial plane). The RAAN or the longitude of ascending node \(\Omega\) is the angle from a reference direction (the vernal equinox) to the ascending node, where the satellite crosses from south to north of the reference plane. The argument of perigee \(\omega\) is the angle from the ascending node to the point of closest approach (perigee) in the orbit. The true anomaly \(\nu\) is the angle between the direction of perigee and the current position of the satellite.

These elements form the basis for analyzing and predicting the motion of bodies in a two-body (Keplerian) system.

Orbit Equation:

The orbit equation, valid for all conic sections, relates the radial distance \(r\) to the true anomaly \(\theta\), the specific angular momentum \(h\), and the standard gravitational parameter \(\mu\): \[ \boxed{ r = \frac{h^2 / \mu}{1 + e \cos\theta} } \]

Converting Between ECI Coordinates and Classical Orbital Elements

The conversion between the Earth-Centered Inertial (ECI) frame (given by position vector \(\mathbf{r}\) and velocity vector \(\mathbf{v}\)) and classical orbital elements involves several steps:

From ECI to Classical Orbital Elements

Given: - r: Position vector in an inertial (ECI) frame
- v: Velocity vector in the same frame
- \(\mu\): Gravitational parameter

Follow these steps:

  1. Compute the Magnitudes: \[ r = \|\mathbf{r}\| \]

\[ v = \|\mathbf{v}\| \]

  1. Specific Angular Momentum: \[ \mathbf{h} = \mathbf{r} \times \mathbf{v} \]

\[ h = \|\mathbf{h}\| \]

  1. Eccentricity Vector: \[ \mathbf{e} = \frac{1}{\mu} \Bigl[(\mathbf{v} \times \mathbf{h}) - \mu\,\frac{\mathbf{r}}{r}\Bigr] \]

\[ e = \|\mathbf{e}\| \]

  1. Specific Orbital Energy: \[ \epsilon = \frac{v^2}{2} - \frac{\mu}{r} \]

  2. Semi-major Axis: \[ a = -\frac{\mu}{2\epsilon} \]

  3. Inclination: \[ i = \arccos\left(\frac{h_z}{h}\right) \] where \(h_z\) is the z-component of \(\mathbf{h}\).

  4. Node Vector: \[ \mathbf{n} = \mathbf{k} \times \mathbf{h} \]

where \(\mathbf{k}\) is the unit vector along the Z-axis.

\[ n = \|\mathbf{n}\| \]

  1. Right Ascension of the Ascending Node (RAAN, \(\Omega\)): \[ \Omega = \arccos\left(\frac{n_x}{n}\right) \]

If \(n_y < 0\), then set \(\Omega = 2\pi - \Omega\).

  1. Argument of Perigee (\(\omega\)): \[ \omega = \arccos\left(\frac{\mathbf{n} \cdot \mathbf{e}}{n\,e}\right) \]

If \(e_z < 0\), then set \(\omega = 2\pi - \omega\).

  1. True Anomaly (\(\nu\)): \[ \nu = \arccos\left(\frac{\mathbf{e} \cdot \mathbf{r}}{e\,r}\right) \]

If \(\mathbf{r} \cdot \mathbf{v} < 0\), then set \(\nu = 2\pi - \nu\).

Here is a python code that converts the ECI coordinates to classical orbital elements.

Code
# Converted from MATLAB code by Suyog Soman (MTech, AFM 2023-2025) with GPT-4o

import numpy as np

def cart2class(r, v):
    """
    Converts Cartesian coordinates to classical orbital elements.
    
    Parameters:
        r : array_like
            Position vector (3,1) in kilometers.
        v : array_like
            Velocity vector (3,1) in km/s.
            
    Returns:
        a     : float
                Semi-major axis [km]
        e     : float
                Eccentricity
        i     : float
                Inclination [rad]
        RAAN  : float
                Right ascension of ascending node [rad]
        omega : float
                Argument of periapsis [rad]
        theta : float
                True anomaly [rad]
    """
    mu = 398600  # Earth's gravitational parameter [km^3/s^2]

    # Ensure inputs are numpy arrays
    r = np.array(r)
    v = np.array(v)
    
    # Angular momentum vector and its magnitude
    h = np.cross(r, v)
    h_norm = np.linalg.norm(h)
    
    r_norm = np.linalg.norm(r)
    v_norm = np.linalg.norm(v)
    
    # Eccentricity vector and magnitude
    e_vec = (1/mu) * ((v_norm**2 - mu/r_norm)*r - np.dot(r, v)*v)
    e = np.linalg.norm(e_vec)
    
    # Specific orbital energy
    energy = v_norm**2 / 2 - mu / r_norm
    # For non-parabolic orbits, compute semi-major axis
    if abs(e - 1) > 1e-8:  # tolerance for float comparison
        a = -mu / (2 * energy)
    else:
        a = np.inf  # parabolic orbit
    
    # Inclination
    i_val = np.arccos(h[2] / h_norm)
    
    # Node vector (pointing towards ascending node)
    K = np.array([0, 0, 1])
    N = np.cross(K, h)
    N_norm = np.linalg.norm(N)
    
    # Right Ascension of the Ascending Node (RAAN)
    if N_norm > 1e-8:
        RAAN = np.arccos(N[0] / N_norm)
        if N[1] < 0:
            RAAN = 2 * np.pi - RAAN
    else:
        RAAN = 0
    
    # Argument of periapsis
    if N_norm > 1e-8 and e > 1e-8:
        omega = np.arccos(np.dot(N, e_vec) / (N_norm * e))
        if e_vec[2] < 0:
            omega = 2 * np.pi - omega
    else:
        omega = 0
    
    # True anomaly
    if e > 1e-8:
        theta = np.arccos(np.dot(e_vec, r) / (e * r_norm))
    else:
        theta = 0
    if np.dot(r, v) < 0:
        theta = 2 * np.pi - theta
    
    return a, e, i_val, RAAN, omega, theta

# Example usage
if __name__ == "__main__":
    
    # Position vector (km)
    r_example = [7000, 500, 500]
    # Velocity vector (km/s)
    v_example = [0, 7.546, 1]

    a, e, i, RAAN, omega, theta = cart2class(r_example, v_example)

    print("Semi-major axis (a):", a, "km")
    print("Eccentricity (e):", e)
    print("Inclination (i):", np.degrees(i), "degrees")
    print("RAAN:", np.degrees(RAAN), "degrees")
    print("Argument of Periapsis (omega):", np.degrees(omega), "degrees")
    print("True Anomaly (theta):", np.degrees(theta), "degrees")
Semi-major axis (a): 7199.239655216658 km
Eccentricity (e): 0.08294103697605933
Inclination (i): 8.32282494084567 degrees
RAAN: 334.94055273017824 degrees
Argument of Periapsis (omega): 310.678594628741 degrees
True Anomaly (theta): 78.72522050823235 degrees

From Classical Orbital Elements to ECI

Given the classical orbital elements: - \(a\): Semi-major axis
- \(e\): Eccentricity
- \(i\): Inclination
- \(\Omega\): RAAN
- \(\omega\): Argument of perigee
- \(\nu\): True anomaly

and the gravitational parameter \(\mu\):

  1. Compute the Semi-latus Rectum: \[ p = a(1 - e^2) \]

  2. Compute the Radial Distance: \[ r = \frac{p}{1 + e\cos\nu} \]

  3. Determine the Position and Velocity in the Perifocal Frame:

  • Position Vector: \[ \mathbf{r}_{\text{pf}} = \begin{bmatrix} r\cos\nu \\ r\sin\nu \\ 0 \end{bmatrix} \]

  • Velocity Vector: \[ \mathbf{v}_{\text{pf}} = \begin{bmatrix} -\sqrt{\frac{\mu}{p}}\sin\nu \\ \sqrt{\frac{\mu}{p}}(e+\cos\nu) \\ 0 \end{bmatrix} \]

  1. Transform to the Inertial (ECI) Frame:

The conversion uses three successive rotations via the rotation matrices:

  • Rotation about the Z-axis by \(-\Omega\): \[ R_3(-\Omega) = \begin{bmatrix} \cos\Omega & \sin\Omega & 0 \\ -\sin\Omega & \cos\Omega & 0 \\ 0 & 0 & 1 \end{bmatrix} \]

  • Rotation about the X-axis by \(-i\): \[ R_1(-i) = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos i & \sin i \\ 0 & -\sin i & \cos i \end{bmatrix} \]

  • Rotation about the Z-axis by \(-\omega\): \[ R_3(-\omega) = \begin{bmatrix} \cos\omega & \sin\omega & 0 \\ -\sin\omega & \cos\omega & 0 \\ 0 & 0 & 1 \end{bmatrix} \]

The overall rotation matrix is: \[ R = R_3(-\Omega)\,R_1(-i)\,R_3(-\omega) \]

Then, the inertial position and velocity vectors are: \[ \mathbf{r} = R\,\mathbf{r}_{\text{pf}} \] \[ \mathbf{v} = R\,\mathbf{v}_{\text{pf}} \]

Here is a python code that converts the classical orbital elements to ECI coordinates.

Code
# Converted from MATLAB code by Suyog Soman (MTech, AFM 2023-2025) with GPT-4o

import numpy as np

def class2cart(a, e, i, RAAN, omega, theta):
    """
    Converts classical orbital elements to Cartesian coordinates (ECI frame).

    Parameters:
        a     : float
                Semi-major axis [km]
        e     : float
                Eccentricity
        i     : float
                Inclination [rad]
        RAAN  : float
                Right ascension of ascending node [rad]
        omega : float
                Argument of periapsis [rad]
        theta : float
                True anomaly [rad]

    Returns:
        r : numpy array (3,)
            Position vector in km
        v : numpy array (3,)
            Velocity vector in km/s
    """
    mu = 398600  # Gravitational parameter of Earth [km^3/s^2]

    # Distance from the central body in the perifocal frame
    r_pqw = (a * (1 - e**2)) / (1 + e * np.cos(theta)) * np.array([np.cos(theta), np.sin(theta), 0])
    
    # Velocity in perifocal frame
    v_pqw = np.sqrt(mu / (a * (1 - e**2))) * np.array([-np.sin(theta), e + np.cos(theta), 0])
    
    # Rotation matrix from perifocal to ECI:
    # 1. Rotation about z-axis by RAAN
    R3_W = np.array([
        [np.cos(RAAN), -np.sin(RAAN), 0],
        [np.sin(RAAN),  np.cos(RAAN), 0],
        [0,             0,            1]
    ])
    
    # 2. Rotation about x-axis by inclination (i)
    R1_i = np.array([
        [1, 0,           0],
        [0, np.cos(i), -np.sin(i)],
        [0, np.sin(i),  np.cos(i)]
    ])
    
    # 3. Rotation about z-axis by argument of periapsis (omega)
    R3_w = np.array([
        [np.cos(omega), -np.sin(omega), 0],
        [np.sin(omega),  np.cos(omega), 0],
        [0,              0,             1]
    ])
    
    # Complete rotation matrix from perifocal to ECI frame
    R = R3_W @ R1_i @ R3_w  # Using the "@" operator for matrix multiplication
    
    # Transform position and velocity vectors to the ECI frame
    r = R @ r_pqw
    v = R @ v_pqw

    return r, v

# Example usage:
if __name__ == "__main__":
    # Provided orbital elements
    a = 7199.239655216658        # Semi-major axis [km]
    e = 0.08294103697605933       # Eccentricity
    i_deg = 8.32282494084567       # Inclination in degrees
    RAAN_deg = 334.94055273017824  # RAAN in degrees
    omega_deg = 310.678594628741   # Argument of periapsis in degrees
    theta_deg = 78.72522050823235  # True anomaly in degrees

    # Convert angles from degrees to radians
    i = np.deg2rad(i_deg)
    RAAN = np.deg2rad(RAAN_deg)
    omega = np.deg2rad(omega_deg)
    theta = np.deg2rad(theta_deg)

    # Compute the Cartesian coordinates
    r, v = class2cart(a, e, i, RAAN, omega, theta)

    print("Position vector (r) [km]:", r)
    print("Velocity vector (v) [km/s]:", v)
Position vector (r) [km]: [7000.  500.  500.]
Velocity vector (v) [km/s]: [-3.77475828e-15  7.54600000e+00  1.00000000e+00]

Analytical Solution of the Two-Body Problem

The classical two-body problem, governed by Kepler’s laws, has analytical solution. Two approaches are commonly used:

Classical Kepler’s Method for Elliptical Orbits

For elliptical orbits (where the eccentricity \(0 < e < 1\)), Kepler’s Equation relates the mean anomaly \(M\) to the eccentric anomaly \(E\):

\[ M = E - e \sin E. \]

The following steps outline the process to propagate an orbit over a given time interval:

1. Compute the Mean Anomaly

The mean motion \(n\) is computed from the semi-major axis \(a\) and the gravitational parameter \(\mu\):

\[ n = \sqrt{\frac{\mu}{a^3}}. \]

Given an initial true anomaly \(\theta_0\), we first compute the initial eccentric anomaly \(E_0\) using the relationship:

\[ E_0 = 2 \arctan\left(\sqrt{\frac{1-e}{1+e}} \tan\left(\frac{\theta_0}{2}\right)\right). \]

If \(E_0\) is negative, it is adjusted by adding \(2\pi\). The initial mean anomaly \(M_0\) is then obtained by:

\[ M_0 = E_0 - e \sin E_0. \]

Over a time interval \(\Delta t\) (denoted as dt in the code), the final mean anomaly \(M_f\) is:

\[ M_f = M_0 + n \Delta t. \]

2. Solve Kepler’s Equation Using Newton-Raphson method

To find the eccentric anomaly \(E_f\) corresponding to \(M_f\), the iterative Newton-Raphson method is applied. The update formula is:

\[ E_{n+1} = E_n - \frac{E_n - e \sin E_n - M_f}{1 - e \cos E_n}. \]

The iteration continues until the change is less than a specified tolerance.

3. Convert to the True Anomaly

Once \(E_f\) is determined, the final true anomaly \(\theta_f\) (or \(\nu\)) is computed by:

\[ \tan\left(\frac{\theta_f}{2}\right) = \sqrt{\frac{1+e}{1-e}} \tan\left(\frac{E_f}{2}\right). \]

Again, if the resulting true anomaly is negative, it is adjusted by adding \(2\pi\).

4. Update the Orbital State

In the two-body problem, the other orbital elements remain unchanged during the propagation. The final orbital elements include the semi-major axis \(a\), eccentricity \(e\), inclination \(i\), right ascension of the ascending node (RAAN), argument of periapsis \(\omega\), and the computed true anomaly \(\theta_f\).

Code
# Converted from MATLAB code by Suyog Soman (MTech, AFM 2023-2025) with GPT-4o

import numpy as np

def propagate_orbit(a, e, i, RAAN, omega, theta0, dt):
    """
    Propagates the orbit using Kepler's classical method of analytical orbit propagation.
    
    Parameters:
        a     : float
                Semi-major axis [km]
        e     : float
                Eccentricity
        i     : float
                Inclination [rad]
        RAAN  : float
                Right Ascension of the Ascending Node [rad]
        omega : float
                Argument of periapsis [rad]
        theta0: float
                Initial true anomaly [rad]
        dt    : float
                Time elapsed [s]
    
    Returns:
        a_final, e_final, i_final, RAAN_final, omega_final, theta_final : tuple of floats
            Final orbital elements.
    """
    mu = 398600.0  # Earth's gravitational parameter [km^3/s^2]
    
    # Mean motion (n) [rad/s]
    n = np.sqrt(mu/(a**3))
    
    # Compute the initial eccentric anomaly (E0) from theta0
    E0 = 2 * np.arctan( np.sqrt((1 - e)/(1 + e)) * np.tan(theta0/2) )
    if E0 < 0:
        E0 = E0 + 2*np.pi
    
    # Compute the initial mean anomaly (M0)
    M0 = E0 - e*np.sin(E0)
    
    # Compute the final mean anomaly (Mf)
    Mf = M0 + n*dt
    
    # Solve Kepler's equation: Ef - e*sin(Ef) = Mf using Newton-Raphson iteration
    Ef = Mf  # initial guess
    tol = 1e-8
    error = 1.0
    while np.abs(error) > tol:
        error = (Ef - e*np.sin(Ef) - Mf) / (1 - e*np.cos(Ef))
        Ef = Ef - error
    
    # Compute the final true anomaly from Ef
    theta_final = 2 * np.arctan( np.sqrt((1+e)/(1-e)) * np.tan(Ef/2) )
    if theta_final < 0:
        theta_final = theta_final + 2*np.pi
    
    # Other orbital elements remain unchanged in a two-body problem.
    return a, e, i, RAAN, omega, theta_final

# Example usage:
if __name__ == "__main__":
    # Define initial orbital elements
    a = 7200.0              # Semi-major axis in km
    e = 0.08             # Eccentricity
    i = np.deg2rad(8.0)     # Inclination in radians
    RAAN = np.deg2rad(335.0) # RAAN in radians
    omega = np.deg2rad(310.0)  # Argument of periapsis in radians
    theta0 = np.deg2rad(80.0) # Initial true anomaly in radians
    dt = 3600                           # Propagation time in seconds (e.g., 1 hour)
    
    # Propagate the orbit
    a_final, e_final, i_final, RAAN_final, omega_final, theta_final = propagate_orbit(a, e, i, RAAN, omega, theta0, dt)
    
    # Display final orbital elements
    print("Final Orbital Elements:")
    print("Semi-major axis (a):", a_final, "km")
    print("Eccentricity (e):", e_final)
    print("Inclination (i):", np.rad2deg(i_final), "degrees")
    print("RAAN:", np.rad2deg(RAAN_final), "degrees")
    print("Argument of Periapsis (omega):", np.rad2deg(omega_final), "degrees")
    print("True Anomaly (theta):", np.rad2deg(theta_final), "degrees")
Final Orbital Elements:
Semi-major axis (a): 7200.0 km
Eccentricity (e): 0.08
Inclination (i): 8.0 degrees
RAAN: 335.0 degrees
Argument of Periapsis (omega): 310.0 degrees
True Anomaly (theta): 275.15750711200366 degrees

Universal Variable Formulation

To extend the analytical solution to all conic sections—including circular, elliptical, parabolic, and hyperbolic trajectories — the universal variable formulation is used. This method introduces a single variable (often denoted by \(x\)) to solve the Kepler problem regardless of the orbit type.

Key Components

  • Stumpff Functions:
    The Stumpff functions, \(C(z)\) and \(S(z)\), where \(z = \alpha x^2\) (with \(\alpha = 1/a\) for elliptical orbits and appropriately defined for parabolic/hyperbolic cases), help handle the various orbital regimes within one framework.

  • Universal Kepler’s Equation: \[ x = \sqrt{\mu}\,\Delta t + \left[1 - z\,S(z)\right] \frac{\mathbf{r}_0\cdot\mathbf{v}_0}{\sqrt{\mu}}\,x^2 + r_0\,C(z)\,x^3, \]

where \(\Delta t\) is the time of flight and \(r_0 = |\mathbf{r}_0|\).

Algorithmic Process

  1. Initial Setup:
  • Start with the initial state vectors \(\mathbf{r}_0\) and \(\mathbf{v}_0\), and compute the classical orbital elements.
  1. Iterative Solution for \(x\):
  • Use an iterative method (such as Newton-Raphson) to solve the universal Kepler’s Equation for the variable \(x\).
  1. Compute Lagrange Coefficients:
  • With \(x\) determined, compute the Lagrange coefficients \(f\), \(g\), \(\dot{f}\), and \(\dot{g}\) using: \[ \mathbf{r}(t) = f\,\mathbf{r}_0 + g\,\mathbf{v}_0, \]

\[ \mathbf{v}(t) = \dot{f}\,\mathbf{r}_0 + \dot{g}\,\mathbf{v}_0. \]

  • These coefficients are functions of \(x\), the Stumpff functions, and the gravitational parameter \(\mu\).
  1. Update State Vectors:
  • Calculate the new position and velocity at time \(t = t_0 + \Delta t\).