Source code for gfinder.ptr_pointing

"""PTR pointing module.
- extract PTR parameters from a given opporttunity definition.
- create a PTR block from these parameters.


Usage:

    - ptr_pointing = JUICE_Jupiter_Ring(fixed_offset_x_angle=12.4)
    - ptr_pointing.get_prm(start_time, stop_time, metadata='')

    or

    - ptr_pointing = PTR_Pointing(pointing_type='juice_jupiter_ring', fixed_offset_x_angle=12.4)
    - ptr_pointing.get_prm(start_time, stop_time, metadata='')

    or

    - ptr_pointing = PTR_Pointing(opportunition_definition=opportunition_definition)
    - ptr_pointing.get_prm(start_time, stop_time, metadata='')

"""

# from gfinder.opportunity import OpportunityDefinition
from ptr import Element, ObsBlock, PointingRequestMessage
from ptr.datetime.parser import dt, td


SLEW_START_DELAY = '3m'
"""Delay before spacecraft slew starts."""

SLEW_STOP_DELAY = '3m'
"""Delay after spacecraft slew stops."""

AGM_ORB_POLE_DIRS = {
    'JUPITER': 'JP2SC_orbPole',
    'CALLISTO': 'CA2SC_orbPole',
    'EUROPA': 'EU2SC_orbPole',
    'GANYMEDE': 'GA2SC_orbPole'
}
"""Mapping between target body name and AGM SC to orbital pole direction definition."""

AGM_NORTH_POLE_DIRS = {
    'JUPITER': 'JPNorthPole',
}
"""Mapping between target body name and AGM North Pole direction definition."""

[docs]def list_pointing_types(): print('Available PTR pointing types, and associated required pointing parameters:') for ptr_pointing_type in PTR_POINTINGS: # print(ptr_pointing_type) ptr_pointing = PTRPointing(ptr_pointing_type=ptr_pointing_type, mandatory=False) # print(f' - {ptr_pointing}') s = f' - {ptr_pointing_type:<25}: ' for parameter in ptr_pointing.parameters.keys(): s += f'{parameter} = {ptr_pointing.parameters[parameter]}, ' parameters_str = s[:-2] print(f'{parameters_str}') print()
[docs]def PTRPointing(ptr_pointing_type='', opportunity_definition=None, **kwargs): """Function serving as AbstractPTRPointing object factory. """ if opportunity_definition: ptr_pointing_type = opportunity_definition.ptr_pointing_type # check that format is valid and return corresponding AbstractPTRPointing object. if ptr_pointing_type in PTR_POINTINGS.keys(): PTRPointingClass = PTR_POINTINGS[ptr_pointing_type] ptr_pointing = PTRPointingClass(opportunity_definition=opportunity_definition, **kwargs) return ptr_pointing else: raise ValueError(f'Invalid PTR pointing type: {ptr_pointing_type}. Allowed format are: {list(PTR_POINTINGS.keys())}')
[docs]class AbstractPTRPointing: def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): if opportunity_definition: params = self.derive_parameters(opportunity_definition) self.set_parameters(params, mandatory=mandatory) else: self.set_parameters(kwargs, mandatory=mandatory) # self.pointing_type = self.__class__.__name__ def __repr__(self): s = f"{self.__class__.__name__}: " for parameter in self.parameters.keys(): s += f'{parameter} = {self.parameters[parameter]}, ' return s[:-2]
[docs] def set_parameters(self, parameters, mandatory=True): for required_parameter in self.parameters: if required_parameter in parameters.keys(): setattr(self, required_parameter, parameters[required_parameter]) else: if mandatory: raise ValueError(f'Could not set {required_parameter}.')
@property def parameters(self): """PTR pointing required parameters. """ return self.__dict__
[docs] def derive_parameters(self, opportunity_definition): return {}
@property def attitude(self): return Element('attitude')
[docs] def get_prm(self, start_time, stop_time, metadata=''): return PointingRequestMessage( ObsBlock( start_time, stop_time, self.attitude, metadata=metadata ) )
[docs]class JUICE_Jupiter_Ring(AbstractPTRPointing): """JUICE_Jupiter_Ring. DEPRECATED, use JUICE_Target_NPL instead. <attitude ref="track"> <boresight ref="SC_Zaxis"/> <target ref="Jupiter"/> <offsetRefAxis ref="SC_Xaxis"/> <offsetAngles ref="fixed"> <xAngle units="deg">{{ fixed_offset_x_angle }}</xAngle> <yAngle units="deg">0.0</yAngle> </offsetAngles> <phaseAngle ref="align"> <SCAxis frame="SC"> <x>0</x> <y>1</y> <z>0</z> </SCAxis> <inertialAxis ref="JPNorthPole"/> </phaseAngle> </attitude> """ def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs) # set required pointing parameters self.fixed_offset_x_angle = 0.0 super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs) # if opportunity_definition: # params = self.derive_parameters(opportunity_definition) # self.set_parameters(params, mandatory=mandatory) # else: # self.set_parameters(kwargs, mandatory=mandatory)
[docs] def derive_parameters(self, opportunity_definition): # opportunity definition -> PTR pointing parameters sc_frame_def = opportunity_definition.get_geometry_definition('Simulated_SC_Frame') y_start = sc_frame_def['parameters']['offset_rotations']['parameters']['y_start'] fixed_offset_x_angle = -y_start return { 'fixed_offset_x_angle': fixed_offset_x_angle }
@property def attitude(self): return Element( 'attitude', Element('boresight', ref='SC_Zaxis'), Element('target', ref='Jupiter'), Element('offsetRefAxis', ref='SC_Xaxis'), Element( 'offsetAngles', Element('xAngle', self.fixed_offset_x_angle, units='deg'), Element('yAngle', 0.0, units='deg'), ref='fixed' ), Element( 'phaseAngle', Element('SCAxis', {'x': 0, 'y': 1, 'z': 0}, frame='SC'), Element('inertialAxis', ref='JPNorthPole'), ref='align' ), ref='track' )
[docs]class JUICE_Target_NOA(AbstractPTRPointing): """JUICE_Target_NOA. Template: <attitude ref="track"> <boresight ref="SC_Zaxis"/> <target ref="{{ target }}"/> <offsetRefAxis ref="SC_Xaxis"/> <offsetAngles ref="fixed"> <xAngle units="deg">{{ x_offset_angle }} </xAngle> <yAngle units="deg">{{ y_offset_angle }}</yAngle> </offsetAngles> <phaseAngle ref="align"> <SCAxis frame="SC"> <x>1</x> <y>0</y> <z>0</z> </SCAxis> <inertialAxis ref="{{ inertial_axis }}"/> </phaseAngle> </attitude> """ def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs) # set required pointing parameters self.target = '' self.fixed_offset_x_angle = 0.0 self.fixed_offset_y_angle = 0.0 # self.boresight = '' # "MAJIS_Zaxis" # self.secondary_axis = '' # "MAJIS_Yaxis" super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs)
[docs] def derive_parameters(self, opportunity_definition): # opportunity definition -> PTR pointing parameters target = opportunity_definition.target.upper() sc_frame_def = opportunity_definition.get_geometry_definition('Simulated_SC_Frame') # print(sc_frame_def) try: x_start = sc_frame_def['parameters']['offset_rotations']['parameters']['x_start'] except Exception as e: x_start = 0.0 print(f'MOS `x_start` parameter not found, set to {x_start}.') try: y_start = sc_frame_def['parameters']['offset_rotations']['parameters']['y_start'] except Exception as e: y_start = 0.0 print(f'MOS `y_start` parameter not found, set to {y_start}.') fixed_offset_x_angle = y_start fixed_offset_y_angle = -x_start return { 'target': target, 'fixed_offset_x_angle': fixed_offset_x_angle, 'fixed_offset_y_angle': fixed_offset_y_angle }
@property def attitude(self): sc_axis = [1, 0, 0] offset_ref_axis = [1, 0, 0] inertial_axis = '' if self.target in AGM_ORB_POLE_DIRS.keys(): inertial_axis = AGM_ORB_POLE_DIRS[self.target] else: raise Exception(f'No orbital pole direction vector defined for {self.target}.') # Temporary patch to be removed when "MAJIS_Yaxis" definition has been added by JUICE SOC. # if self.secondary_axis == 'MAJIS_Yaxis': # sc_axis = [-0.01425291, 0.99988351, 0.00546049] # majis_axis as defined in JUICE FK on March 2023 # offset_ref_axis = [ 0.99988928, 0.01422921, 0.00435425] return Element( 'attitude', Element('boresight', ref='SC_Zaxis'), Element('target', ref=self.target), Element('offsetRefAxis', {'x': offset_ref_axis[0], 'y': offset_ref_axis[1], 'z': offset_ref_axis[2]}, frame='SC'), # Element('offsetRefAxis', ref='SC_Xaxis'), Element( 'offsetAngles', Element('xAngle', self.fixed_offset_x_angle, units='deg'), Element('yAngle', self.fixed_offset_y_angle, units='deg'), ref='fixed' ), Element( 'phaseAngle', Element('SCAxis', {'x': sc_axis[0], 'y': sc_axis[1], 'z': sc_axis[2]}, frame='SC'), Element('inertialAxis', ref=inertial_axis), ref='align' ), ref='track' )
[docs]class JUICE_Target_NPL(AbstractPTRPointing): """JUICE_Target_NPL. Template: <attitude ref="track"> <boresight ref="SC_Zaxis"/> <target ref="{{ target }}"/> <offsetRefAxis ref="SC_Xaxis"/> <offsetAngles ref="fixed"> <xAngle units="deg">{{ fixed_offset_x_angle }}</xAngle> <yAngle units="deg">0.0</yAngle> </offsetAngles> <phaseAngle ref="align"> <SCAxis frame="SC"> <x>0</x> <y>1</y> <z>0</z> </SCAxis> <inertialAxis ref="JPNorthPole"/> </phaseAngle> </attitude> """ def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs) # set required pointing parameters self.target = '' self.fixed_offset_x_angle = 0.0 self.fixed_offset_y_angle = 0.0 self.boresight = '' # "MAJIS_Zaxis" self.secondary_axis = '' # "MAJIS_Yaxis" super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs)
[docs] def derive_parameters(self, opportunity_definition): # opportunity definition -> PTR pointing parameters target = opportunity_definition.target sc_frame_def = opportunity_definition.get_geometry_definition('Simulated_SC_Frame') # print(sc_frame_def) try: x_start = sc_frame_def['parameters']['offset_rotations']['parameters']['x_start'] except Exception as e: x_start = 0.0 print(f'MOS `x_start` parameter not found, set to {x_start}.') try: y_start = sc_frame_def['parameters']['offset_rotations']['parameters']['y_start'] except Exception as e: y_start = 0.0 print(f'MOS `y_start` parameter not found, set to {y_start}.') fixed_offset_x_angle = y_start fixed_offset_y_angle = -x_start boresight = opportunity_definition.boresight if boresight == 'Z': boresight = 'SC_Zaxis' # TODO: temporary patch to be removed when JUICE SOC has changed naming from "JUICE_MAJIS_BASE" to "MAJIS_Zaxis" if boresight == 'MAJIS_Zaxis': boresight = 'JUICE_MAJIS_BASE' secondary_axis = opportunity_definition.secondary_axis return { 'target': target, 'fixed_offset_x_angle': fixed_offset_x_angle, 'fixed_offset_y_angle': fixed_offset_y_angle, 'boresight': boresight, 'secondary_axis': secondary_axis }
@property def attitude(self): # TODO: Temporary patch to be removed when "MAJIS_Yaxis" definition has been added by SC sc_axis = [0, 1, 0] offset_ref_axis = [1, 0, 0] if self.secondary_axis == 'MAJIS_Yaxis': sc_axis = [-0.01425291, 0.99988351, 0.00546049] # majis_axis as defined in JUICE FK on March 2023 offset_ref_axis = [ 0.99988928, 0.01422921, 0.00435425] inertial_axis = '' if self.target in AGM_NORTH_POLE_DIRS.keys(): inertial_axis = AGM_NORTH_POLE_DIRS[self.target] else: raise Exception(f'No SC to North Pole direction defined for {self.target}.') return Element( 'attitude', Element('boresight', ref=self.boresight), # eg: 'SC_Zaxis' or 'JUICE_MAJIS_BASE' Element('target', ref=self.target), Element('offsetRefAxis', {'x': offset_ref_axis[0], 'y': offset_ref_axis[1], 'z': offset_ref_axis[2]}, frame='SC'), # Element('offsetRefAxis', ref='SC_Xaxis'), Element( 'offsetAngles', Element('xAngle', self.fixed_offset_x_angle, units='deg'), Element('yAngle', self.fixed_offset_y_angle, units='deg'), ref='fixed' ), Element( 'phaseAngle', Element('SCAxis', {'x': sc_axis[0], 'y': sc_axis[1], 'z': sc_axis[2]}, frame='SC'), # Element('SCAxis', {'x': 0, 'y': 1, 'z': 0}, frame='SC'), Element('inertialAxis', ref=inertial_axis), ref='align' ), ref='track' )
[docs]class JUICE_Target_NPL_Slew(AbstractPTRPointing): """JUICE_Target_NPL_Slew. Note: - "scan" refers to spacecraft slew. Hypothesis: - The "scan" startTime element indicates the actual requested MAJIS observation start time. - A minimum time delay of 120/200s [TBD] is required between "OBS" block and the "scan" start time, as well as between "OBS" block stop time . Therefore, the "OBS" block startTime element Template: <block ref="OBS"> <startTime> {{ start_time }} </startTime> <endTime> {{ stop_time }} </endTime> <attitude ref="track"> <boresight ref="SC_Zaxis"/> <target ref="{{ target }}"/> <offsetRefAxis ref="SC_Xaxis"/> <offsetAngles ref="scan"> <startTime>{{ scan_start_time }}</startTime> <numberOfLines>1</numberOfLines> <numberOfScansPerLine>1</numberOfScansPerLine> <xStart units="deg">{{ x_start }}</xStart> <yStart units="deg">{{ y_start }}</yStart> <scanSpeed units="deg/sec">{{ scan_speed }}</scanSpeed> <borderSlewTime units="sec">0.0</borderSlewTime> <lineAxis>y</lineAxis> </offsetAngles> <phaseAngle ref="align"> <SCAxis frame="SC"> <x>0</x> <y>1</y> <z>0</z> </SCAxis> <inertialAxis ref="JPNorthPole"/> </phaseAngle> </attitude> </block> """ def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs) # set required pointing parameters self.scan_start_time = '' self.target = '' self.x_start = 0.0 # deg self.y_start = 0.0 # deg self.scan_delta = 0.0 # deg self.scan_speed = 0.0 # deg/s super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs)
[docs] def derive_parameters(self, opportunity_definition): # opportunity definition -> PTR pointing parameters """ Example of input opportunity definition "offset_rotations": "offset_rotations": { "name": "SC_Slew_Angles", "parameters": { "y_start": 0.0, "x_start": -4.0, "x_stop": 4.0, "x_rate": 0.034482758620689655 } } """ # retrieve target target = opportunity_definition.target # retrieve Simulated_SC_Frame parameters sc_frame_def = opportunity_definition.get_geometry_definition('Simulated_SC_Frame') try: x_start_rot = sc_frame_def['parameters']['offset_rotations']['parameters']['x_start'] except Exception as e: x_start_rot = 0.0 print(f'MOS `x_start` parameter not found, set to {x_start_rot}.') try: x_stop_rot = sc_frame_def['parameters']['offset_rotations']['parameters']['x_stop'] except Exception as e: x_stop_rot = 0.0 print(f'MOS `x_stop` parameter not found, set to {x_stop_rot}.') try: x_rate = abs(sc_frame_def['parameters']['offset_rotations']['parameters']['x_rate']) except Exception as e: x_rate = 0.0 print(f'MOS `x_rate` parameter not found, set to {x_rate}.') try: y_start_rot = sc_frame_def['parameters']['offset_rotations']['parameters']['y_start'] except Exception as e: y_start_rot = 0.0 print(f'MOS `y_start` parameter not found, set to {y_start_rot}.') # derive PTR pointing parameters x_start = y_start_rot y_start = -x_start_rot y_stop = -x_stop_rot scan_delta = y_stop - y_start return { 'scan_start_time': '', 'target': target, 'x_start': x_start, 'y_start': y_start, 'scan_delta': scan_delta, 'scan_speed': x_rate }
@property def attitude(self): inertial_axis = '' if self.target in AGM_NORTH_POLE_DIRS.keys(): inertial_axis = AGM_NORTH_POLE_DIRS[self.target] else: raise Exception(f'No SC to North Pole direction defined for {self.target}.') return Element( 'attitude', Element('boresight', ref='SC_Zaxis'), Element('target', ref=self.target), Element('offsetRefAxis', ref='SC_Xaxis'), Element( 'offsetAngles', Element('startTime', self.scan_start_time), Element('numberOfLines', 1), Element('numberOfScansPerLine', 1), Element('xStart', self.x_start, units='deg'), Element('yStart', self.y_start, units='deg'), Element('scanDelta', self.scan_delta, units='deg'), Element('scanSpeed', self.scan_speed, units='deg/sec'), # Element('borderSlewTime', 0.0, units='sec'), ref='scan' ), Element( 'phaseAngle', Element('SCAxis', {'x': 0, 'y': 1, 'z': 0}, frame='SC'), Element('inertialAxis', ref=inertial_axis), ref='align' ), ref='track' )
[docs] def get_prm(self, start_time, stop_time, metadata=''): """Returns PRM XML for JUICE_Target_NPL_Slew pointing type. Note: This child method is required to update pointing parameters that can not be retrieved from an opportunity definition object. In this case: the `scan_start_time` parameter. It also to adjust input start and stop time to accommodate for AGM validation rules, eg: "tranquilisation" time. """ # update scan start time to the actually requested observation start time. self.scan_start_time = dt(start_time).isoformat() # update start and stop to be used in "OBS" block so as to include a time delay of 2 minutes before and after # the scan start_time = dt(start_time) - td(SLEW_START_DELAY) stop_time = dt(stop_time) + td(SLEW_STOP_DELAY) # return PRM return super().get_prm(start_time, stop_time, metadata=metadata)
[docs]class JUICE_Target_NPO(AbstractPTRPointing): """JUICE_Target_NPO. <attitude ref="track"> <boresight ref="SC_Zaxis"/> <target ref="{{ target }}"/> <offsetRefAxis ref="SC_Xaxis"/> <offsetAngles ref="fixed"> <xAngle units="deg">{{ fixed_offset_x_angle }}</xAngle> <yAngle units="deg">{{ fixed_offset_y_angle }}</yAngle> </offsetAngles> <phaseAngle ref="powerOptimised"> <yDir>false</yDir> </phaseAngle> </attitude> """ def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): # set required pointing parameters self.target = '' self.fixed_offset_x_angle = 0.0 self.fixed_offset_y_angle = 0.0 super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs)
[docs] def derive_parameters(self, opportunity_definition): # opportunity definition -> PTR pointing parameters target = opportunity_definition.target sc_frame_def = opportunity_definition.get_geometry_definition('Simulated_SC_Frame') x_start = sc_frame_def['parameters']['offset_rotations']['parameters']['x_start'] y_start = sc_frame_def['parameters']['offset_rotations']['parameters']['y_start'] fixed_offset_x_angle = y_start fixed_offset_y_angle = -x_start return { 'target': target, 'fixed_offset_x_angle': fixed_offset_x_angle, 'fixed_offset_y_angle': fixed_offset_y_angle }
@property def attitude(self): # return Element('attitude') return Element( 'attitude', Element('boresight', ref='SC_Zaxis'), Element('target', ref=self.target), Element('offsetRefAxis', ref='SC_Xaxis'), Element( 'offsetAngles', Element('xAngle', self.fixed_offset_x_angle, units='deg'), Element('yAngle', self.fixed_offset_y_angle, units='deg'), ref='fixed' ), Element('phaseAngle', {'yDir': False}, ref='powerOptimised'), ref='track' )
[docs]class JUICE_Target_NPO_Slew(AbstractPTRPointing): """JUICE_Target_NPO_Slew. Note: - "scan" refers to spacecraft slew. Hypothesis: - The "scan" startTime element indicates the actual requested MAJIS observation start time. - A minimum time delay of 120/200s [TBD] is required between "OBS" block and the "scan" start time, as well as between "OBS" block stop time . Therefore, the "OBS" block startTime element PTR block template: <block ref="OBS"> <startTime> {{ start_time }} </startTime> <endTime> {{ stop_time }} </endTime> <attitude ref="track"> <boresight ref="SC_Zaxis"/> <target ref="{{ target }}"/> <offsetRefAxis ref="SC_Xaxis"/> <offsetAngles ref="scan"> <startTime>{{ scan_start_time }}</startTime> <numberOfLines>1</numberOfLines> <numberOfScansPerLine>1</numberOfScansPerLine> <xStart units="deg">{{ x_start }}</xStart> <yStart units="deg">{{ y_start }}</yStart> <scanSpeed units="deg/sec">{{ scan_speed }}</scanSpeed> <borderSlewTime units="sec">0.0</borderSlewTime> <lineAxis>y</lineAxis> </offsetAngles> <phaseAngle ref="powerOptimised"> <yDir>false</yDir> </phaseAngle> </attitude> </block> """ def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs) # set required pointing parameters self.scan_start_time = '' self.target = '' self.x_start = 0.0 # deg self.y_start = 0.0 # deg self.scan_delta = 0.0 # deg self.scan_speed = 0.0 # deg/s super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs)
[docs] def derive_parameters(self, opportunity_definition): # opportunity definition -> PTR pointing parameters """ Example of input opportunity definition "offset_rotations": "offset_rotations": { "name": "SC_Slew_Angles", "parameters": { "x_start": -4.0, "x_stop": 4.0, "x_rate": 0.034482758620689655 } } """ target = opportunity_definition.target sc_frame_def = opportunity_definition.get_geometry_definition('Simulated_SC_Frame') x_start_rot = sc_frame_def['parameters']['offset_rotations']['parameters']['x_start'] x_stop_rot = sc_frame_def['parameters']['offset_rotations']['parameters']['x_stop'] x_rate = abs(sc_frame_def['parameters']['offset_rotations']['parameters']['x_rate']) x_start = 0.0 # TODO: update based on available SC_Slew_Angles parameters y_start = -x_start_rot y_stop = -x_stop_rot scan_delta = y_stop - y_start return { 'scan_start_time': '', 'target': target, 'x_start': x_start, 'y_start': y_start, 'scan_delta': scan_delta, 'scan_speed': x_rate }
@property def attitude(self): return Element( 'attitude', Element('boresight', ref='SC_Zaxis'), Element('target', ref=self.target), Element('offsetRefAxis', ref='SC_Xaxis'), Element( 'offsetAngles', Element('startTime', self.scan_start_time), Element('numberOfLines', 1), Element('numberOfScansPerLine', 1), Element('xStart', self.x_start, units='deg'), Element('yStart', self.y_start, units='deg'), Element('scanDelta', self.scan_delta, units='deg'), Element('scanSpeed', self.scan_speed, units='deg/sec'), ref='scan' ), Element('phaseAngle', {'yDir': False}, ref='powerOptimised'), ref='track' )
[docs] def get_prm(self, start_time, stop_time, metadata=''): """Returns PRM XML for JUICE_Target_NPL_Slew pointing type. Note: This child method is required to update pointing parameters that can not be retrieved from an opportunity definition object. In this case: the `scan_start_time` parameter. It also to adjust input start and stop time to accommodate for AGM validation rules, eg: "tranquilisation" time. """ # update scan start time to the actually requested observation start time. self.scan_start_time = dt(start_time).isoformat() # update start and stop to be used in "OBS" block so as to include a time delay of 2 minutes before and after # the scan start_time = dt(start_time) - td(SLEW_START_DELAY) stop_time = dt(stop_time) + td(SLEW_STOP_DELAY) # return PRM return super().get_prm(start_time, stop_time, metadata='')
[docs]class JUICE_Target_Limb(AbstractPTRPointing): """JUICE_Target_Limb. NOT IMPLEMENTED. Example: <block ref="OBS"> <startTime> 2032-01-11T12:31:40 </startTime> <endTime> 2032-01-11T12:56:00 </endTime> <attitude ref="limb"> <boresight ref="SC_Zaxis" /> <targetDir ref="rotate"> <axis ref="CA2Sun" /> <rotationAxis ref="SC2CA" /> <rotationAngle units="deg"> 40 </rotationAngle> </targetDir> <height units="km"> 0 </height> <surface ref="Callisto" /> <phaseAngle ref="align"> <SCAxis frame="SC"> <x> 0 </x> <y> -1 </y> <z> 0 </z> </SCAxis> <inertialAxis ref="rotate"> <axis ref="CA2Sun" /> <rotationAxis ref="SC2CA" /> <rotationAngle units="deg"> 40 </rotationAngle> </inertialAxis> </phaseAngle> </attitude> </block> """ def __init__(self, opportunity_definition=None, mandatory=True, **kwargs): super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs) raise NotImplementedError('JUICE_Target_Limb PTR pointing not implemented yet.') # set required pointing parameters self.target = '' self.fixed_offset_x_angle = 0.0 self.fixed_offset_y_angle = 0.0 self.boresight = '' # "MAJIS_Zaxis" self.secondary_axis = '' # "MAJIS_Yaxis" super().__init__(opportunity_definition=opportunity_definition, mandatory=mandatory, **kwargs)
# def derive_parameters(self, opportunity_definition): # opportunity definition -> PTR pointing parameters # target = opportunity_definition.target # sc_frame_def = opportunity_definition.get_geometry_definition('Simulated_SC_Frame') # # print(sc_frame_def) # try: # x_start = sc_frame_def['parameters']['offset_rotations']['parameters']['x_start'] # except Exception as e: # x_start = 0.0 # print(f'MOS `x_start` parameter not found, set to {x_start}.') # try: # y_start = sc_frame_def['parameters']['offset_rotations']['parameters']['y_start'] # except Exception as e: # y_start = 0.0 # print(f'MOS `y_start` parameter not found, set to {y_start}.') # # fixed_offset_x_angle = y_start # fixed_offset_y_angle = -x_start # boresight = opportunity_definition.boresight # if boresight == 'Z': # boresight = 'SC_Zaxis' # # TODO: temporary patch to be removed when JUICE SOC has changed naming from "JUICE_MAJIS_BASE" to "MAJIS_Zaxis" # if boresight == 'MAJIS_Zaxis': # boresight = 'JUICE_MAJIS_BASE' # # secondary_axis = opportunity_definition.secondary_axis # return { # 'target': target, # 'fixed_offset_x_angle': fixed_offset_x_angle, # 'fixed_offset_y_angle': fixed_offset_y_angle, # 'boresight': boresight, # 'secondary_axis': secondary_axis # } # # @property # def attitude(self): # # TODO: Temporary patch to be removed when "MAJIS_Yaxis" definition has been added by SC # sc_axis = [0, 1, 0] # offset_ref_axis = [1, 0, 0] # if self.secondary_axis == 'MAJIS_Yaxis': # sc_axis = [-0.01425291, 0.99988351, 0.00546049] # majis_axis as defined in JUICE FK on March 2023 # offset_ref_axis = [ 0.99988928, 0.01422921, 0.00435425] # # inertial_axis = '' # if self.target in AGM_NORTH_POLE_DIRS.keys(): # inertial_axis = AGM_NORTH_POLE_DIRS[self.target] # else: # raise Exception(f'No SC to North Pole direction defined for {self.target}.') # # return Element( # 'attitude', # Element('boresight', ref=self.boresight), # eg: 'SC_Zaxis' or 'JUICE_MAJIS_BASE' # Element('target', ref=self.target), # Element('offsetRefAxis', {'x': offset_ref_axis[0], 'y': offset_ref_axis[1], 'z': offset_ref_axis[2]}, frame='SC'), # # Element('offsetRefAxis', ref='SC_Xaxis'), # Element( # 'offsetAngles', # Element('xAngle', self.fixed_offset_x_angle, units='deg'), # Element('yAngle', self.fixed_offset_y_angle, units='deg'), # ref='fixed' # ), # Element( # 'phaseAngle', # Element('SCAxis', {'x': sc_axis[0], 'y': sc_axis[1], 'z': sc_axis[2]}, frame='SC'), # # Element('SCAxis', {'x': 0, 'y': 1, 'z': 0}, frame='SC'), # Element('inertialAxis', ref=inertial_axis), # ref='align' # ), # ref='track' # ) PTR_POINTINGS = { 'juice_target_noa': JUICE_Target_NOA, 'juice_target_npo': JUICE_Target_NPO, 'juice_target_npo_slew': JUICE_Target_NPO_Slew, 'juice_target_npl': JUICE_Target_NPL, 'juice_target_npl_slew': JUICE_Target_NPL_Slew, 'juice_jupiter_ring': JUICE_Jupiter_Ring, 'juice_target_limb': JUICE_Target_Limb }