Pointing to another satellite in yaw-steering

Hello,

I would like to setup an attitude provider where the Z axis of the SC is pointed towards another PVCoordinatesProvider (typically, the propagator of another SC), and where the remaining degree of freedom is calculated with yaw-steering towards sun

here is my code (python). The only way I found to create an attitude provider pointing towards a PVCoordinatesProvider is through CelestialBodyPointed

    targetSc_pointing = CelestialBodyPointed(FramesFactory.getEME2000(), targetSc.prop,
                                Vector3D.PLUS_K,# phasing dir in EME2000
                                Vector3D.PLUS_K, # SC vector to be pointed
                                Vector3D.PLUS_I) # phasing dir in SC frame
    # with yaw steering
    sat_r_pointed_YS = YawSteering(FramesFactory.getEME2000(), targetSc_pointing,
                                   CelestialBodyFactory.getSun(), Vector3D.PLUS_I)

the problem I have is that the YawSteering line throws an InvalidArgs error, probably because the expected type of underlying attitude provider is GroundPointing as per the docs, while CelestialBodyPointed is not inheriting from GroundPointing but directly from AttitudeProvider

Do I miss something ?
thanks for your help

Side note: I checked the targetSc_pointing attitude provider is functional.

Update:
I tried to define a new subclass YawSteering2 of PythonAttitudeProvider to remove the hard-coded requirement of GroundPointing in YawSteering java code, for underlying attitude provider.

I created the python equivalent of YawSteering by looking at the java part (src/main/java/org/orekit/attitudes/YawSteering.java · release-11.2 · Orekit / Orekit · GitLab)

class YawSteering2(PythonAttitudeProvider):
PLUS_Z = PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);

def __init__(self, inertialFrame, pointingLaw, sun, phasingAxis):
    self.sun = sun        
    self.pointingLaw = pointingLaw
    self.inertialFrame = inertialFrame
    self.phasingNormal = PVCoordinates(Vector3D.crossProduct(Vector3D.PLUS_K, phasingAxis).normalize(),
                                           Vector3D.ZERO,
                                           Vector3D.ZERO);
    super(YawSteering2, self).__init__()

    
def getUnderlyingAttitudeProvider(self)->AttitudeProvider :
    return self.pointingLaw

def getTargetPV(self, pvProv, date, frame)-> TimeStampedPVCoordinates :
    return self.sun.getPVCoordinates(date, self.inertialFrame)

def getBaseState(self, pvProv, date, frame)-> Attitude:
    return self.pointingLaw.getAttitude(pvProv, date, frame)

def getAttitude(self, pVCoordinatesProvider, absoluteDate, frame) -> Attitude:

    base = self.getBaseState(pVCoordinatesProvider, absoluteDate, frame)
    sunDirection = PVCoordinates(pVCoordinatesProvider.getPVCoordinates(absoluteDate, frame),
                  self.sun.getPVCoordinates(absoluteDate, frame))
    sunNormal = PVCoordinates.crossProduct(self.PLUS_Z, base.getOrientation().applyTo(sunDirection))
    compensation = TimeStampedAngularCoordinates(absoluteDate, self.PLUS_Z, sunNormal.normalize(),
                                              self.PLUS_Z, self.phasingNormal,
                                              1.0e-9)
    return Attitude(frame, compensation.addOffset(base.getOrientation()))

But I got an error at runtime, which I don’t understand:

super: <class ‘JavaError’>, >
Java stacktrace:
java.lang.RuntimeException: InvalidArgsError
at org.orekit.attitudes.PythonAttitudeProvider.getAttitude(Native Method)

i’m completely lost. I looked through orekit_python_artifacts/test at version-11.2 · petrushy/orekit_python_artifacts · GitHub to find some tests using this but with no success.

Hi @vianneyl,

I’m not sure but maybe you miss the definition of the “field” version of the getAttitude method ?
See here for the functions you need to overload when implementing an AttitudeProvider in Python.

hello @MaximeJ

thanks for your answer. I already overloaded the getAttitude method which has the field* attributes (see below, I had not copy pasted the code for the sake of clarity in the original post), but it does not change the result. Actually I overloaded more than the functions that are present in the definition of PythonAttitudeProvider, not just getAttitude functions. I also tried to remove all the code of these functions and just return an empty Attitude/FieldAttitude object, but it did not help

def getAttitude(self, fieldPVCoordinatesProvider, fieldAbsoluteDate, frame) -> FieldAttitude:
        field = fieldAbsoluteDate.getField()
        zero = FieldVector3D.getZero(field)
        plusZ = FieldPVCoordinates(FieldVector3D.getPlusK(field), zero, zero)
        
        base = self.getBaseState(fieldPVCoordinatesProvider, fieldAbsoluteDate, frame)
        
        sunDirection = FieldPVCoordinates(fieldPVCoordinatesProvider.getPVCoordinates(fieldAbsoluteDate, frame),
                      FieldPVCoordinates(self.sun.getPVCoordinates(fieldAbsoluteDate, frame)))
        
        sunNormal = plusZ.crossProduct(base.getOrientation().applyTo(sunDirection))
        
        compensation = TimeStampedFieldAngularCoordinates(fieldAbsoluteDate, plusZ, sunNormal.normalize(),
                                                  plusZ, FieldPVCoordinates(field,self.phasingNormal),
                                                  1.0e-9)
        
        return FieldAttitude(frame, compensation.addOffset(base.getOrientation()))

Do you know if there is somewhere a test that shows how to implement this kind of stuff ?
When I had to create a subclass of PythonEventDetector I had tests in the above mentioned link, that helped me, but theyr does not contain tests related to Attitude.

Hi @vianneyl,

Ok sorry for the bother I thought it could be that.

Not to my knowledge no, but I’m not an expert in the Python wrapper.
I know that there is at least one for sub-classing the EventDetector as you mentioned but I’m not aware of similar example for the AttitudeProvider.
Sorry…
Maybe @petrus.hyvonen would know ?

I also defined the getAttitude_FFF function, but with no help.

deleted previous message, as actually it is not solving the issue ^^

@petrus.hyvonen @luc would you have some test file in Python showing creation of a class using PythonAttitudeProviderModifier ?

In a nutshell I want to perform yaw-steering using CelestialBodyPointed as an underlying provider (so as I can point Z axis towards another SC, while managing remaining degree of freedom to maximize sun illumination), but the current implementation does not allow it, because type of underlying provider for YawSteering is restricted to GroundPointing, while CelestialBodyPointed is not of that type (its type is AttitudeProvider)

I thus created a python class YawSteering2 overloading required functions using PythonAttitudeProviderModifier, but I get a run time error (invalid args on getAttitude), and I don’t know why.

Here is the code of the class:

class YawSteering2(PythonAttitudeProviderModifier):
    PLUS_Z = PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO);
    
    def __init__(self, inertialFrame, groundPointingLaw, sun, phasingAxis):
        self.sun = sun        
        self.groundPointingLaw = groundPointingLaw
        self.inertialFrame = inertialFrame
        self.phasingNormal = PVCoordinates(Vector3D.crossProduct(Vector3D.PLUS_K, phasingAxis).normalize(),
                                               Vector3D.ZERO,
                                               Vector3D.ZERO);
        super(YawSteering2, self).__init__()

        
    def getUnderlyingAttitudeProvider(self)->AttitudeProvider :
        return self.groundPointingLaw
    
    def getAttitude(self, pVCoordinatesProvider, absoluteDate, frame) -> Attitude:
        base = self.getBaseState(pVCoordinatesProvider, absoluteDate, frame)
        sunDirection = PVCoordinates(pVCoordinatesProvider.getPVCoordinates(absoluteDate, frame),
                      self.sun.getPVCoordinates(absoluteDate, frame))
        sunNormal = PVCoordinates.crossProduct(self.PLUS_Z, base.getOrientation().applyTo(sunDirection))
        compensation = TimeStampedAngularCoordinates(absoluteDate, self.PLUS_Z, sunNormal.normalize(),
                                                  self.PLUS_Z, self.phasingNormal,
                                                  1.0e-9)
        return Attitude(frame, compensation.addOffset(base.getOrientation()))
     

    def getAttitude_FFF(self, fieldPVCoordinatesProvider, fieldAbsoluteDate, frame) -> FieldAttitude:
        field = fieldAbsoluteDate.getField()
        zero = FieldVector3D.getZero(field)
        plusZ = FieldPVCoordinates(FieldVector3D.getPlusK(field), zero, zero)
        
        base = self.getBaseState(fieldPVCoordinatesProvider, fieldAbsoluteDate, frame)
        
        sunDirection = FieldPVCoordinates(fieldPVCoordinatesProvider.getPVCoordinates(fieldAbsoluteDate, frame),
                      FieldPVCoordinates(self.sun.getPVCoordinates(fieldAbsoluteDate, frame)))
        
        sunNormal = plusZ.crossProduct(base.getOrientation().applyTo(sunDirection))
        
        compensation = TimeStampedFieldAngularCoordinates(fieldAbsoluteDate, plusZ, sunNormal.normalize(),
                                                  plusZ, FieldPVCoordinates(field,self.phasingNormal),
                                                  1.0e-9)
        
        return FieldAttitude(frame, compensation.addOffset(base.getOrientation()))
    
    def finalize(self):
        pass

and how it is used (later on it is integrated in an AttitudeSequence):

        # attitude provider pointing towards targetSc
        targetSc_pointing = CelestialBodyPointed(FramesFactory.getEME2000(), targetSc.prop,
                                    Vector3D.PLUS_K,# phasing dir in EME2000
                                    Vector3D.PLUS_K, # SC vector to be pointed
                                    Vector3D.PLUS_I) # phasing dir in SC frame
        
        # with yaw steering (not working currently using CelestialBodyPointed as underlying provider)
        sat_r_pointed_YS = YawSteering2(FramesFactory.getEME2000(), targetSc_pointing,
                                       CelestialBodyFactory.getSun(), Vector3D.PLUS_I)

the runtime error occurs during propagation (I’m using NumericalPropagator):

<super: <class 'JavaError'>, <JavaError object>>
    Java stacktrace:
java.lang.RuntimeException: AttributeError
	at org.orekit.attitudes.PythonAttitudeProviderModifier.getAttitude(Native Method)
	at org.orekit.attitudes.AttitudesSequence.getAttitude(AttitudesSequence.java:292)
	at

I checked it was properly working with the ‘normal’ YawSteering and using a GroundPointing-type underlying provider (such as BodyCenterPointing).

I also checked that my underlying provider CelestialBodyPointed was properly working when used in standalone way (i.e. not through YawSteering)

Finally I got it to work. Actually the error was not on the declaration of fct but inside the fct… and the stack trace stops at the method level, it does not go into it !

Now I know it !

Good to know you got it to work @vianneyl !