
Math & Physics Mathematical and physical problems of space flight and astronomy. 

Thread Tools 
07032018, 09:20 AM  #1 
Orbinaut

From BCI to BCBF and back again
This note sets out the transformations that Orbiter uses to convert points in its 'bodycentred inertial' (BCI)reference frame to its 'bodycentred, bodyfixed' reference frame. (The BCI reference frame is a generalisation of the Earthcentred inertial (ECI) reference frame applying to any gravitating body not just the Earth. Likewise, the BCBF reference frame is a generalisation of the Earthcentred Earthfixed (ECEF) reference frame again applying to any gravitating body.)
These transformations have a number of uses. In subsequent posts I will focus on using them to develop high fidelity numerical integration tools. In turn, I will use these integration tools to solve the targeting (rendezvous) problem. In this note, however, I will just define the transformations from the BCI to BCBF reference frame and its inverse. You should note that the standard Orbiter 2016 documentation includes a description of these forward and backward transformations. However, I find this description to be a little opaque. Hopefully, this note may serve to augment the Orbiter documentation. In this note, I will also include some explicit Python code that carries out the forward and backward transformations. A quick recap on BCI and BCBF reference frames To briefly summarise, the BCI reference frame is a reference frame whose origin lies at the centre of mass of the gravitating body but does not corotate with that gravitating body. The BCI reference frame is the natural reference frame in which to express the equations of motion of a satellite. Because the BCI reference frame is not an inertial reference frame, we need to add noninertial tidal forces to arising from other gravitating bodies to the equations of motion. For the Earth, it is common to add tidal contributions from the Moon and from the Sun. The BCBF reference frame is also centred on the gravitating body's centre of mass but its xyz axes rotate with the instantaneous rotation axes of the gravitating body. This means that for any point on the surface of the gravitating body has fixed coordinates in the BCBF reference frame. The BCBF reference frame is the natural reference frame in which to express nonspherical gravitational contributions arising from the J_2 (and possibly higher order) gravitational field term. Rotations included in the transformations In Orbiter, to transform a vector from the BCI reference frame to the BCBF reference frame, we need to model:
To do this, we need five angles:
In Orbiter, the two fixed angles used to define the orientation of the precession axis are called "PrecessionLAN" and "PrecessionObliquity" in the body's Orbiter configuration files. For example, from the .cfg file for Mars we have: Code:
PrecessionLAN = 4.005081124 PrecessionObliquity = 0.03224369545 where As you can see, these expressions are a function of 'LAN', 'LAN_MJD', (the precession period of the rotation axis about the precession axis) and 'mjd'. Now, 'mjd' is simply the Modified Julian Date for which you wish to calculate the BCI to BCBF transformation. is the number of days since the reference epoch given by 'LAN_MJD'. 'LAN', 'LAN_MJD' and are all constants defined in the body's Orbiter configuration file. For example, the .cfg for Mars we have: Code:
PrecessionPeriod = 63346652.48 LAN = 0.6210531483 LAN_MJD = 51544.5 Code:
Obliquity = 0.4397415938 This expression is a function of a two new parameters, and . is the sidereal rotation period of the body; and is the angle of rotation at the start of the epoch given by 'LAN_MJD' and in Orbiter known as the Sidereal rotation offset. Again, these two values are treated as constants in Orbiter and are defined in the body's configuration file. For example, the .cfg file for Mars has: Code:
SidRotOffset = 5.469523488 SidRotPeriod = 88642.66435 Code:
PrecessionLAN = 4.005081124 PrecessionObliquity = 0.03224369545 PrecessionPeriod = 63346652.48 LAN = 0.6210531483 LAN_MJD = 51544.5 Obliquity = 0.4397415938 SidRotOffset = 5.469523488 SidRotPeriod = 88642.66435 The BCI to BCBF transformation The general strategy for transforming a vector from the BCI reference frame to the BCBF reference frame is a three step process: * step 1: rotate the vector into a coordinate system that is aligned with the precession axis of the body. Two angles, the Precession LAN () and the Precession Obliquity (), are needed to do this. * step 2: rotate from a coordinate system aligned with the body's precession axis to a coordinate system aligned with the instantaneous rotation axis of the body. To do this we need to calculate the value  i.e, the number of days since the start of epoch. Then we can calculate the angle using the formula given above. The two angles and the obliquity are then used to make the transformation. * step 3: rotate the new vector about the rotation axis by an amount . The result is the transformed vector in the BCBF reference frame. Let's suppose that a vector is given to us in BCI coordinates. To carry out step 1, we need to define two rotation matrices: and Then, we apply and to the vector in succession  i.e., To apply the second step, we do apply two further rotations, and defined as follows: and Then, we apply and to the vector in succession  i.e., Finally, to apply the third step, we now rotate by a further angle as defined above: such that: The full BCI to BCBF transformation, then, is given by: Now, this transformation looks fairly formidable. But actually it can be encoded quite simply. In Python, for example, let's fix the eight required parameters of the transformation using the Mars .cfg file values: Code:
pLan = 4.005081124 # Precession LAN pObl = 0.03224369545 # Precession obliquity Tp = 63346652.48 # Precession period LAN = 0.6210531483 # LAN mjdL = 51544.5 # LAN MJD Ts = 88642.66435 # Sidereal rotation period eps = 0.4397415938 # Obliquity psi0 = 5.469523488 # Sidereal rotation offset Code:
import numpy as np def BCI2BCBF( X, mjd ): deld = mjd  mjdL tau = LAN + 2 * np.pi * deld / Tp psi = 2 * np.pi * 86400.0 / Ts * deld  2 * np.pi * np.cos(eps) / Tp * deld + psi0 x = np.empty(6) y = np.empty(6) z = np.empty(6) x[0] = X[0] y[0] = X[1] z[0] = X[2] c = np.cos(pLan) s = np.sin(pLan) x[1] = +x[0] * c + y[0] * s y[1] = x[0] * s + y[0] * c z[1] = +z[0] c = np.cos(pObl) s = np.sin(pObl) x[2] = +x[1] y[2] = +y[1] * c  z[1] * s z[2] = +y[1] * s + z[1] * c c = np.cos(tau) s = np.sin(tau) x[3] = +x[2] * c + y[2] * s y[3] = x[2] * s + y[2] * c z[3] = +z[2] c = np.cos(eps) s = np.sin(eps) x[4] = +x[3] y[4] = +y[3] * c  z[3] * s z[4] = +y[3] * s + z[3] * c c = np.cos(psi) s = np.sin(psi) x[5] = +x[4] * c + y[4] * s y[5] = x[4] * s + y[4] * c z[5] = +z[4] return np.array([x[5],y[5],z[5]]) The BCBF to BCI transformation Since the BCBF to BCI transformation is the inverse of the BCI to BCBF transformation, and since the transformation is simply defined as a sequence of elemental rotations, we can generate the inverse of the transformation by simply reversing the order in which we apply the elemental rotation operations and by changing the signs of all of the rotation angles. In other words, we have: So, the equivalent Python code for the inverse transformation from the BCBF reference frame to the BCI reference frame is: Code:
import numpy as np def BCBF2BCI( X, mjd ): deld = mjd  mjdL tau = LAN + 2 * np.pi * deld / Tp psi = 2 * np.pi * 86400.0 / Ts * deld  2 * np.pi * np.cos(eps) / Tp * deld + psi0 x = np.empty(6) y = np.empty(6) z = np.empty(6) x[5] = X[0] y[5] = X[1] z[5] = X[2] c = np.cos(psi) s = np.sin(psi) x[4] = +x[5] * c  y[5] * s y[4] = +x[5] * s + y[5] * c z[4] = +z[5] c = np.cos(eps) s = np.sin(eps) x[3] = +x[4] y[3] = +y[4] * c + z[5] * s z[3] = y[4] * s + z[5] * c c = np.cos(tau) s = np.sin(tau) x[2] = +x[3] * c  y[3] * s y[2] = +x[3] * s + y[3] * c z[2] = +z[3] c = np.cos(pObl) s = np.sin(pObl) x[1] = +x[2] y[1] = +y[2] * c + z[2] * s z[1] = y[2] * s + z[2] * c c = np.cos(pLan) s = np.sin(pLan) x[0] = +x[1] * c  y[1] * s y[0] = +x[1] * s + y[1] * c z[0] = +z[1] return np.array([x[0],y[0],z[0]]) An example As a quick example, let's apply these two transformation routines. Let's suppose that we are considering Marscentred reference frames. And let's suppose that we start with a vector in the Marscentred inertial reference frame: Although the transformation is not restricted to position vectors, we can think of this as a point in space 4000 km from the centre of Mars. We can map this point to the Marscentred Marsfixed reference frame by applying the Python transformation for MJD = 52644.5: Code:
X = np.array([4000000.0, 0.0, 0.0]) Y = BCBF2BCI(X, 52644.5) print(Y) These transformation rules are quite general in that they should apply to any body in Orbiter's Solar System since the planetary rotation and precession is consistently defined by the same bodydependent parameters. Simply by extracting the relevant eight constant parameters from the body configuration file, one can readily obtain the transformation rule appropriate for that body. Although the transformations have been written in Python (for my convenience!), it should be straightforward to translate these algorithms into C or Lua without much difficulty. A note on Orbiter's lefthanded coordinate system In the above definitions, I have assumed a conventional righthanded coordinate system. Orbiter, however, uses a lefthanded coordinate system. In my view, the best way of dealing with Orbiter's curious coordinate convention is to swap the 'y' and 'z' values of any state vector extracted from Orbiter. This converts from a lefthanded to a righthanded coordinate system. Then, carry out whatever conventional operations are needed in the righthanded coordinate system. When completed, swap the 'y' and 'z' values again thereby transforming the solution back to Orbiter's lefthanded coordinate system. These values can then be exported back into Orbiter. In the above example, the transformed vector was given by: These are the values in the conventional righthanded coordinate system. So, to convert back to Orbiter's lefthanded convention, we need to swap the 'y' and 'z; values. Thus, the Orbiter compatible result is the vector: These results should agree with Orbiter's estimation of the same transformation nighon exactly. Next steps In a subsequent post, I want to use these transformations to build a straightforward (but high fidelity) numerical integration scheme. 

Thread Tools  


Quick Links  Need Help? 