Embedded Software

Embedded Software

SCADE and STK – Satellite Attitude Control

    • SolutionSolution
      Participant



      Introduction

      Modern space missions demand highly accurate and reliable control systems, especially when it comes to satellite attitude control. This article explores how Ansys SCADE and Ansys STK (Systems Tool Kit) can be integrated to create a powerful simulation environment for satellite embedded software development, enabling real-time testing and validation of attitude control strategies.

      In this blog, we integrate STK and SCADE to simulate a satellite attitude control software on a host computer.

      Note: this article is written using SCADE Suite for embedded software design, but the integration would be similar with our new generation product, Ansys Scade One.

      Satellite Attitude Control

      Attitude control refers to the orientation of a spacecraft relative to an inertial frame of reference. Maintaining the correct attitude is vital for satellite operations, such as ensuring that antennas point towards Earth or solar panels face the Sun.



      Combining the power of SCADE and STK



      SCADE is a model-based development environment focused on safety-critical embedded software.

      It is used to develop, simulate, and generate code for critical systems, such as the satellite attitude controller.





      STK is used to simulate and visualize air and space missions, satellite orbits, and dynamic systems.

      It provides realistic physics-based modeling.



      The integration of SCADE and STK bridges model-based software design with high-fidelity mission simulation. When combined, these tools create a closed-loop simulation environment, where satellite dynamics and control logic interact in real time.

      Use case description

      In our scenario, a satellite is orbiting Earth with a defined initial state (angular speed and orientation) and a target state (target angular speed and orientation). While its orbit remains unchanged, we want the satellite’s orientation to be actively managed by a software controller.



      Simulated satellite with its initial attitude (grey) and target attitude (red)



      Simulated satellite trajectory and angular speeds

      Our SCADE Model is responsible for the following:

      • Reading inputs: Current and target angular speeds and orientations, expressed as quaternions and Euler angles.
      • Computing output: Torque needed to correct the satellite’s orientation, expressed as a three-dimensional vector. Note that the torque is just a vector. How it gets applied to the satellite (reaction wheels, thrusters…) is up to the STK model.

      Integration Workflow

      The integration between SCADE and STK involves several components:



      • A SCADE model, compiled into a Windows DLL.
      • A Python proxy, using the SCADE Python Wrapper, to exercise the model.
      • A manual Python script that maps STK parameters to SCADE inputs/outputs, and calls the SCADE root operator at each simulation step.
      • The STK engine to process attitude changes and send updates.

      On the STK side, the Attitude Simulator provides an interface to connect to MATLAB, VBScript, Python, or Perl scripts. The STK to Python interface is done through COM interaction.

      Note: The approach described is specific to attitude control plugin. Adapting it to other simulation domains within STK may require adjustments due to differing APIs.

      Requirements

      Running this integration requires:

      • An STK 12 installation (the Jupyter Notebook Plugin is optional but recommended).
      • A SCADE Suite Installation in version 2024 R2 or later.
      • A Python Interpreter in version 3.10 or later. One comes packaged with SCADE, under /contrib/Python310.
      • The SCADE Python Wrapper.

      SCADE Model and Python Wrapper

      The core logic of the SCADE model is a simple Proportional / Derivative controller, converting the measured and target data into a torque vector:



      Scade Attitude Controller logic

      To generate the Python Proxy module, we’ll use the same technique described in a previous blog.

      Once generated, the proxy contains the following:

      • A global variable called sensors with one read/write property per sensor in the model.
      • A class per root operator which defines:
        • One read/write property per input
        • One read-only property per output
        • Two functions to exercise the generated code:
          • call_reset(self) -> None
          • call_cycle(self, cycles: int = 1) -> None

      In white box simulation mode, the call_cycle signature becomes:

      call_cycle(self, cycles: int = 1, refresh: bool = True, debug: bool = False) -> None
      

      Integrating the wrapper with the Attitude Controller Plugin

      On the STK side, the attitude controller expects the registration of Python functions with a particular signature. We manually write these ‘glue’ functions between STK and the SCADE Python Proxy into an intermediary Python file.

      For this code, we rely on two sections of the STK documentation: Python functions and Attitude Simulator plugin points.

      The code consists of three main parts.

      First, a main function serves as the entry point called by STK. It takes a list argument, the first element of which is the calling mode. This mode is set to register once to indicate the initialization phase. Then, it is set to None to run simulation steps. Some STK interfaces may also set it to compute.

      def Attitude_Torque_Controller(argList):
          callMode = str(argList[0])
          if callMode in ['None','compute']:
              retVal = Attitude_Torque_Controller_compute(argList) # Run a SCADE cycle
          elif callMode == 'register':
              global Attitude_Torque_Controller_init
              Attitude_Torque_Controller_init = -1
              retVal = Attitude_Torque_Controller_register()
          else:
              retVal = []  # bad call, return empty list
          return retVal
      

      Next, a function to register inputs and outputs once during initialization:

      def Attitude_Torque_Controller_register():
          # Register arguments, as described in https://help.agi.com/stkdevkit/12.9.1/index.htm#../Subsystems/pluginScripts/Content/pythonFunction.htm
          arg1 = 'ArgumentType = Output ; Type = Parameter ; ArgumentName = Torque ; Name = Torque ; BasicType = Vector'
          arg2 = 'ArgumentType = Input ; ArgumentName = time ; Name = Epoch'
          arg3 = 'ArgumentType = Input ; ArgumentName = att ; Type = Attitude; Derivative = Yes '
          arg5 = 'ArgumentType = Input ; ArgumentName = q1r ; Name = q1; Type = Scalar; Source = Satellite/Reference'
          arg6 = 'ArgumentType = Input ; ArgumentName = q2r ; Name = q2; Type = Scalar; Source = Satellite/Reference'
          arg7 = 'ArgumentType = Input ; ArgumentName = q3r ; Name = q3; Type = Scalar; Source = Satellite/Reference'
          arg8 = 'ArgumentType = Input ; ArgumentName = q4r ; Name = q4; Type = Scalar; Source = Satellite/Reference'
          arg9 = 'ArgumentType = Input ; ArgumentName = angxr ; Name = angx; Type = Scalar; Source = Satellite/Reference'
          arg10 = 'ArgumentType = Input ; ArgumentName = angyr ; Name = angy; Type = Scalar; Source = Satellite/Reference'
          arg11 = 'ArgumentType = Input ; ArgumentName = angzr ; Name = angz; Type = Scalar; Source = Satellite/Reference'
          argList = [arg1, arg2, arg3, arg5, arg6, arg7, arg8, arg9, arg10, arg11]
      
          # Instantiate the SCADE model proxy
          # The cosim flag would start a simulator session in SCADE
          global adcs_op
          adcs_op = SCADE_ADCS(cosim=False)
      
          # Provide initial SCADE model inputs as constants
          adcs_op.ThetaX = 1000
          adcs_op.ThetaY = 1000
          adcs_op.ThetaZ = 1000
          adcs_op.SigmaQuaternion = 0
          adcs_op.SigmaOmega = 0.001
      
          return argList
      

      And finally, a compute function, running one cycle of the SCADE model and called by STK at each step of the simulation:

      def Attitude_Torque_Controller_compute(argList):
          global Attitude_Torque_Controller_init
          global Attitude_Torque_Controller_Inputs
      
          # Access data based on argument names
          if Attitude_Torque_Controller_init < 0:
              Attitude_Torque_Controller_init = 1
              Attitude_Torque_Controller_Inputs = g_PluginArrayInterfaceHash["Attitude_Torque_Controller_Inputs"]
      
          # Retrieve STK inputs
          q1r = argList[Attitude_Torque_Controller_Inputs['q1r']]
          q2r = argList[Attitude_Torque_Controller_Inputs['q2r']]
          q3r = argList[Attitude_Torque_Controller_Inputs['q3r']]
          q4r = argList[Attitude_Torque_Controller_Inputs['q4r']]
          angxr = argList[Attitude_Torque_Controller_Inputs['angxr']]
          angyr = argList[Attitude_Torque_Controller_Inputs['angyr']]
          angzr = argList[Attitude_Torque_Controller_Inputs['angzr']]
          att = np.array(argList[Attitude_Torque_Controller_Inputs['att']])
      
          # Pass inputs to the SCADE Model through the adcs object
          adcs_op.MeasuredQuaternion[0] = att[0]
          adcs_op.MeasuredQuaternion[1] = att[1]
          adcs_op.MeasuredQuaternion[2] = att[2]
          adcs_op.MeasuredQuaternion[3] = att[3]
      
          adcs_op.MeasuredAngularSpeed[0] = math.degrees(att[4])
          adcs_op.MeasuredAngularSpeed[1] = math.degrees(att[5])
          adcs_op.MeasuredAngularSpeed[2] = math.degrees(att[6])
      
          adcs_op.RefQuater[0] = q1r
          adcs_op.RefQuater[1] = q2r
          adcs_op.RefQuater[2] = q3r
          adcs_op.RefQuater[3] = q4r
          adcs_op.RefAngularSpeed[0] = math.degrees(angxr)
          adcs_op.RefAngularSpeed[1] = math.degrees(angyr)
          adcs_op.RefAngularSpeed[2] = math.degrees(angzr)
          
          # Call the SCADE model’s root operator
          # Set refresh to true to have the value refreshed in the UI
          # Set debug to true to pause the simulation after executing the cycle
          adcs_op.call_cycle(refresh=True,debug=True)
      
          # Retrieve and return the output
          output = adcs_op.OutputTorque
          retList = np.array([output[0], output[1], output[2]])
      
          return np.array([retList])
      

      Running and debugging the simulation

      Once the Python part is done, we can register the script from the STK IDE.

      The Attitude Simulator plugin is accessible from right click the Satellite > Satellite > Attitude Simulator…



      Accessing the Attitude Simulator

      From there, we click Configuration…:



      Attitude Simulator Configuration menu

      In Simulation, we click Add and select the Python file located under /Scripting/Attitude. We see it successfully appear:



      Once registered, we run the simulation with the plugin dialog’s Run… button:



      If co-simulation was set to true in the Python code, a SCADE Simulator Session starts, allowing to do a step by step debugging of the scenario. Otherwise, the simulation runs by itself.

      This video showcases all steps above, up to the execution of the simulation:

      Want to learn more?

      In this article, we have seen how to integrate SCADE and STK to simulate a satellite attitude control system.

      You may start a free trial of SCADE Suite (30 days) using this link, and of STK (21 days) using that link. If you’d like to know more about how Ansys SCADE and STK can improve your engineering workflows, you may contact us from the Ansys Embedded Software page or the Ansys Digital Mission Engineering page.

      In future blogs, we will dive into other use cases where SCADE and STK come together to solve real world engineering problems:

      • Enhancing the Satellite Attitude Controller using the FMI standard
      • Battery Monitoring for autonomous eVTOL vehicles
      • Thermal and attitude control simulations
      • UAV & fighter formation flight



      About the author



      Romain Andrieux (LinkedIn) is a Product Specialist Engineer at Ansys. He has been working on SCADE Ecosystem and the SCADE Support team development for 2 years.