Relative position vector in orbital reference frame

Hi everybody,

Suppose I have two SpacecraftStates with TimeStampedPVCoordinates expressed in the same inertial reference frame and with equal AbsoluteDates. How can I get the relative position vector of the second one with respect to the first one, expressed in an orbital reference frame, like LVLH?

One idea was to project the difference vector (computed in the inertial reference frame) onto the orbital reference frame (say LVLH) of the first one, but I don’t know how to do it.

Thank you,
Alfredo

One simple way to do it would be to convert one of the spacecraft state by overriding its attitude part so attitude is LVLH, then use the SpacecraftState.toTransform() method to get a Transform that would transform the coordinates of the other spacecraft state to LVLH.

It would be something along these lines:

  LofOffset lvlh = new LofOffset(s1.getFrame(), LOFType.LVLH);
  SpacecraftState converted =
                  new SpacecraftState(s1.getOrbit(),
                                      lvlh.getAttitude(s1.getOrbit(), s1.getDate(), s1.getFrame()));
  TimeStampedPVCoordinates pv2 = converted.
                                 toTransform().
                                 transformPVCoordinates(s2.getPVCoordinates(s1.getFrame()));
 
1 Like

Thank you Luc! Very elegant solution.

I was coding the following method in Python, that includes also relative velocity vector:

def states_compare(self, other):
        drR = []
        drT = []
        drN = []
        rng = []
        dvR = []
        dvT = []
        dvN = []
        dv = []
        for s0, s1 in zip(self.ss, other.ss):
            pv0 = s0.getPVCoordinates()
            pv1 = s1.getPVCoordinates()
            if pv0.date.isEqualTo(pv1.date):
                # pos/vel of reference spacecraft
                p0 = pv0.position     # 3D Vector
                v0 = pv0.velocity
                # range and range/rate
                rng.append(pv0.position.distance(pv1.position))
                dv.append(pv0.velocity.distance(pv1.velocity))
                # delta position/velocity in ECI
                drECI = pv1.position.subtract(pv0.position)
                dvECI = pv1.velocity.subtract(pv0.velocity)
                # orbital frame
                R = p0.normalize()
                N = p0.crossProduct(v0).normalize().negate()
                T = R.crossProduct(N)
                # position
                drR.append(drECI.dotProduct(R)) # [m]
                drT.append(drECI.dotProduct(T)) # [m]
                drN.append(drECI.dotProduct(N)) # [m]
                # velocity
                dvR.append(dvECI.dotProduct(R)) # [m/s]
                dvT.append(dvECI.dotProduct(T)) # [m/s]
                dvN.append(dvECI.dotProduct(N)) # [m/s]
       return drR, drT, drN, rng, dvR, dvT, dvN, dv

I have defined the N direction as the negative orbital angular momentum. Is it possible to do the same in your formulation? (using your example, I get the same results when comparing two SpacecrafStates, but the N component has opposite sign)

How could I include also relative velocity vector with your solution?

Thank you very much,
Alfredo

I think you could use LofOffset with offsets and not the default constructor I proposed that set all three angular offsets to 0. If you want to rotate by π around the X axis, then you can use:

  LofOffset rotatedLvlh = new LofOffset(s1.getFrame(), LOFType.LVLH,
                                        RotationOrder.XYZ, FastMath.PI, 0.0, 0.0);
1 Like

Thanks Luc.

How can I get the relative velocity from your solution?

The pv2 object has zero velocity, while the relative velocity between s1 and s2 is not zero:

 s1:  {2022-10-15T02:00:00.000, P(-6454699.905721267, 2376050.8660066323, -4.6566128730773926E-10), V(338.7783365789778, 920.3138402722359, 7549.175771532052), A(7.90681113683953, -2.910590069785613, 5.704209190602858E-16)}
 s2:  {2022-10-15T02:00:00.000, P(-6454155.842376496, 2377498.5245195837, 11904.578679591883), V(351.24636790341765, 915.7226198062323, 7549.164273482744), A(7.906144675783803, -2.9123634074496, -0.014582746937583848)}
pv2:  {2022-10-15T02:00:00.000, P(-10.4759990706998, 12004.607610634012, -9.640643838793039E-11), V(1.2256862191861728E-13, -9.84611697729676E-13, 5.825548811307503E-14), A(-4.674915434063609E-15, -2.626402260772392E-16, 2.4806452214018442E-16)}

Thank you very much,
Alfredo

It’s a fun one!

It seems your two spacecrafts are tightly bound together as if it was a rigid pair. Perhaps they follow each other on the same orbit, one being about 12km in front of the other one?

What we observe here is that the relative velocity of one satellite with respect to the other one expressed in inertial frame is exactly compensated by the rotation rate of the local orbital frame. This means that as seen from one satellite, the other one is always in the same position (they form a rigid pair, at least at this time). I doubt this is by chance and guess the pair as been configured as is on purpose.

When we compute the transform from inertial frame to local orbital frame, we take into account the frame position and velocity, the frame orientation, but also the frame rotation rate, so when we convert the coordinates of a moving point that act in fact as if it was a rigid body, we get zero relative velocity.
This is the same thing that we observe when we convert the position-velocity of a perfectly geostationary satellite in Earth frame: despite the satellite has a non-zero velocity in inertial frame, it has zero velocity in Earth frame because it does not move with respect to Earth frame.

The fact that Transform.transformPVCoordinates() applies velocity composition ensures that the result has consistent derivatives, i.e. that the derivative of transformed position is the transformed velocity (which is logical if you consider the geostationary satellite in Earth frame: its position is fixed in Earth frame, so its velocity is zero).

So what is really the velocity you want? There can be several choices, and several ways to achieve them.

If you want the inertial velocity just rotated to project it into local orbital frame but explicitly ignoring all velocity composition (i.e. ignoring both frame velocity and frame rotation rate), then the proper way is to not call transformPVCoordinates but call transformPosition for the position vector and transformVector for the velocity vector as it just applies the rotation part. In this case, you will get the following velocity: (-13.2865009782; -7612.5965756968; -0)

If you want something very special like taking into account the frame velocity but not the frame rotation rate, then you could dissect the transform and rebuild it without the rotation rate, as follows:

Transform rebuilt = new Transform(transform.getDate(),
                                  new Transform(transform.getDate(),
                                                transform.getCartesian()),
                                  new Transform(transform.getDate(),
                                                new AngularCoordinates(transform.getRotation(),
                                                                       Vector3D.ZERO)))

Then you will get the following velocity: (-13.286500978196113, 0.011594662351892993, -5.773159728050814E-14).

However, I really am not sure about the physical meaning of the second option. Taking just some of the derivatives but not others seems weird to me.

Also note that in both cases, as long as you ignore at least some derivatives in the transforms, the derivative of the transformed position is not the transformed velocity: they are not related anymore.

Thanks Luc!

Yes, they are two spacecrafts in the same orbit, separated 12 km Along Track, and this was configured on purpose to simulate a formation.

You are right that the relative velocity is zero, if we take into account the frame velocity and frame rotation rate of the second spacecraft. That is the relative velocity I’m interested in, so the toTransform.transformPVCoordinates() method is what I need. There is no reason to ignore velocity composition.

Thank you very much for your great support!

Alfredo