{"id":199106,"date":"2026-07-16T16:12:20","date_gmt":"2026-07-16T16:12:20","guid":{"rendered":"https:\/\/innovationspace.ansys.com\/knowledge\/?post_type=topic&#038;p=199106"},"modified":"2026-07-17T08:29:23","modified_gmt":"2026-07-17T08:29:23","slug":"from-equations-to-execution-modeling-and-integrating-a-satellite-controller-with-scade-one","status":"publish","type":"topic","link":"https:\/\/innovationspace.ansys.com\/knowledge\/forums\/topic\/from-equations-to-execution-modeling-and-integrating-a-satellite-controller-with-scade-one\/","title":{"rendered":"From Equations to Execution: Modeling and Integrating a Satellite Controller with Scade One"},"content":{"rendered":"<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-banner.jpeg\" style=\"max-height: 700px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<h3  id=\"INTRODUCTION\">Introduction<\/h3>\n<p>This blog demonstrates how a simple satellite position controller can be designed in <a href=\"https:\/\/www.ansys.com\/products\/embedded-software\/ansys-scade-one\">Scade One<\/a><strong> <\/strong>and integrated<strong> <\/strong>into a closed-loop system implemented in Python. It expands on a <a href=\"https:\/\/innovationspace.ansys.com\/knowledge\/forums\/topic\/scade-and-stk-satellite-attitude-control\/\">previously published article<\/a>, highlighting how the SCADE and Scade One set of tools naturally fits the specific space challenges.<\/p>\n<p>Starting from a classical state-space formulation with a Linear Quadratic (LQ) controller, the theory is progressively translated into a fully executable model.<\/p>\n<p>In this article, you will also learn how to work with vectors and matrices using operators from the Scade One<strong> <\/strong>standard libraries, enabling a structured and reusable implementation of linear algebra operations.<\/p>\n<h3  id=\"CONTROLLER-INTEGRATION-WORKFLOW\">Controller Integration Workflow<\/h3>\n<p>The purpose of the article is to demonstrate a complete integration workflow: from controller synthesis in Python to executable model design in Scade One, and back to Python for system-level validation (see Figure 1). The behavior of the generated controller code (and, consequently, of the Python proxy) is identical to that of the initial control model designed in Python. Validation is performed through comparison between the satellite system with embedded Scade One controller and a pure Python implementation.<\/p>\n<p>The integration workflow consists of the following steps:<\/p>\n<ul>\n<li>Compute the LQ controller gain in Python for a linearized satellite model.<\/li>\n<li>Simulate the continuous-time satellite system (plant model + LQ controller) in Python.<\/li>\n<li>Design and validate the corresponding discrete-time satellite system (plant model + LQ controller) in Scade One.<\/li>\n<li>Generate code for the controller in Scade One.<\/li>\n<li>Wrap the generated code into a Python proxy using the Python wrapper.<\/li>\n<li>Integrate the Python proxy (Scade One controller) with a discretized satellite plant model within a Python script, for discrete-time simulation.<\/li>\n<li>Compare simulation results between the satellite system with embedded Scade One controller and the pure Python implementation.<\/li>\n<\/ul>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-integration-workflow.png\" style=\"max-height: 700px !important\" \/><br \/>\n    <em>Figure 1: Scade One controller integration workflow<\/em>\n<\/p>\n<h3  id=\"SATELLITE-CONTROL-MODEL-THEORY\">Satellite Control Model: Theory<\/h3>\n<p>In a simplified scenario, a satellite in orbit ideally follows a stable trajectory defined by gravitational forces (see Figure 2), and the Earth is the only influencing body and the orbit remains constant.<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-orbit.png\" style=\"max-height: 366px !important\" \/><br \/>\n    <em>Figure 2: Satellite moving on a circular orbit around the Earth<\/em>\n<\/p>\n<p>In practice, disturbances such as atmospheric drag constantly affect the satellite trajectory.<\/p>\n<p>The following linearized satellite model captures radial and angular dynamics around a nominal circular orbit:<\/p>\n<p>$$\\dot{x}(t)=Ax(t)+Bu\\left(t\\right)$$<\/p>\n<p>$$y(t)=Cx(t)=Ix(t)$$<\/p>\n<p>The state vector $x(t)$ (here, equal to the output vector $y(t)$) is:<\/p>\n<p>$$x\\left(t\\right)={\\left[x_{1},x_{2},x_{3},x_{4}\\right]}^{T}={\\left[r,\\ \\theta ,\\ \\dot{r},\\ \\dot{\\theta }\\right]}^{T}$$ <\/p>\n<p>Where $(r,\u03b8)$ are polar coordinates (orbit radius and angular position), $\\dot{r}$ is a radial velocity, $\\omega =\\dot{\\theta }$ is an angular velocity. ${\\left[u_{1},u_{2}\\right]}^{T}={\\left[u_{r},u_{\\theta }\\right]}^{T}$ is an input (control) vector. $A$ and $B$ matrices are as follows:<\/p>\n<p>$$\\left(\\begin{gathered}{\\dot{x}}_{1} \\\\ {\\dot{x}}_{2} \\\\ {\\dot{x}}_{3} \\\\ {\\dot{x}}_{4}\\end{gathered}\\right)=\\begin{pmatrix}0 &amp; 0 &amp; 1 &amp; 0 \\\\ 0 &amp; 0 &amp; 0 &amp; 1 \\\\ A_{31} &amp; A_{32} &amp; A_{33} &amp; A_{34} \\\\ 0 &amp; 0 &amp; A_{43} &amp; A_{44}\\end{pmatrix}\\left(\\begin{gathered}x_{1} \\\\ x_{2} \\\\ x_{3} \\\\ x_{4}\\end{gathered}\\right)+\\begin{pmatrix}0 &amp; 0 \\\\ 0 &amp; 0\\end{pmatrix}\\binom{u_{1}}{\\ u_{2}}$$<\/p>\n<p>With <\/p>\n<p>$A_{31}={\\omega }_{0}^{2}+\\frac{2GM}{r_{0}^{3}}=3{\\omega }_{0}^{2}$<\/p>\n<p>$ A_{32}=0$<\/p>\n<p>$A_{33}=\\frac{\\rho AC_{D}}{2m}r_{0}{\\omega }_{0}$<\/p>\n<p>$A_{34}=-2r_{0}{\\omega }_{0}$<\/p>\n<p>$A_{41}=0$<\/p>\n<p>$ A_{42}=0$<\/p>\n<p>$A_{43}=\\frac{2{\\omega }_{0}}{r_{0}}$<\/p>\n<p>$A_{44}=\\frac{\\rho AC_{D}}{2m}r_{0}{\\omega }_{0}$<\/p>\n<p>Where $\\rho$ &#8211; air density, $A$ &#8211; satellite reference area, $C_{D}$ &#8211; drag coefficient, $m$ &#8211; satellite mass, $r_{0}$ &#8211; distance from the satellite position to the center of the Earth at steady state, ${\\omega }_{0}=\\sqrt \\frac{GM}{r_{0}^{3}}$ &#8211; satellite angular velocity at steady state.<\/p>\n<p>The objective of the controller is to maintain the satellite on a reference trajectory despite disturbances. The LQ controller minimizes a quadratic cost function balancing state deviation and control effort: <\/p>\n<p>$$J=\\int (\\delta x^{T}Q\\delta x+u^{T}Ru)dt$$<\/p>\n<ul>\n<li>Increasing $Q$ reduces state energy.<\/li>\n<li>Increasing $R$ reduces control activity.<\/li>\n<\/ul>\n<p>The optimal control law is computed by solving algebraic Riccati equation and is applied as follows:<\/p>\n<p>$$u=-K*\\delta x=-K*\\delta y$$<\/p>\n<p>The diagram from Figure 3 represents a linearized satellite model in combination with the LQ controller, in discrete-time domain, with an approximation of integrator:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-control-model.png\" style=\"max-height: 400px !important\" \/><br \/>\n    <em>Figure 3: Linearized satellite model in combination with the LQ controller in discrete-time domain<\/em>\n<\/p>\n<h3  id=\"CONTINUOUS-TIME-CONTROL-DESIGN-AND-VALIDATION-IN-PYTHON\">Continuous-time Control Design and Validation in Python<\/h3>\n<p>The closed-loop satellite system with continuous-time LQ controller is implemented and simulated using the Python Control Systems library. Its behavior is then compared with that of the discrete controller exported from Scade One. The full implementation of the satellite system is available in the accompanying Jupyter notebook (see Next Steps).<\/p>\n<h4  id=\"SATELLITE-MODEL\">Satellite Model<\/h4>\n<p>Let&#8217;s start with the import of some libraries:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">import<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">numpy<\/span><span style=\"color: #BBB\"> <\/span><span style=\"font-weight: bold\">as<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">np<\/span>\r\n<span style=\"font-weight: bold\">import<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">matplotlib.pyplot<\/span><span style=\"color: #BBB\"> <\/span><span style=\"font-weight: bold\">as<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">plt<\/span>\r\n<span style=\"font-weight: bold\">import<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">control<\/span><span style=\"color: #BBB\"> <\/span><span style=\"font-weight: bold\">as<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">ct<\/span>\r\n\r\n<span style=\"font-weight: bold\">from<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">scipy<\/span><span style=\"color: #BBB\"> <\/span><span style=\"font-weight: bold\">import<\/span> linalg\r\n<span style=\"font-weight: bold\">from<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">scipy.integrate<\/span><span style=\"color: #BBB\"> <\/span><span style=\"font-weight: bold\">import<\/span> odeint\r\n<span style=\"font-weight: bold\">from<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">scipy.signal<\/span><span style=\"color: #BBB\"> <\/span><span style=\"font-weight: bold\">import<\/span> StateSpace, lsim\r\n<\/pre>\n<\/div>\n<p>We will define satellite parameters to setup our plant model. Let&#8217;s take a satellite model with the following parameters:  $r_{0}=R_{E}+h = 6371 + 729 = 7100 km$,  $\\rho= 3.1 \\times 10^{-13} kg\/m^3$,  $C_{D} = 2.2$,  $m = 1000 kg$,  $A = 2 m^2$:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"color: #998;font-style: italic\"># Earth and orbital parameters<\/span>\r\nG <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">6.6743e-11<\/span>\r\nM <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">5.9722e24<\/span>\r\nmu <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">3.986e14<\/span>  <span style=\"color: #998;font-style: italic\"># Earth gravitational parameter mu = G * M [m^3\/s^2]<\/span>\r\naltitude <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">729e3<\/span>  <span style=\"color: #998;font-style: italic\"># Orbital altitude [m]<\/span>\r\nr0 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">6.371e6<\/span> <span style=\"font-weight: bold\">+<\/span> altitude  <span style=\"color: #998;font-style: italic\"># Orbital radius [m]<\/span>\r\nomega0 <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>sqrt(mu <span style=\"font-weight: bold\">\/<\/span> r0 <span style=\"font-weight: bold\">**<\/span> <span style=\"color: #099\">3<\/span>)  <span style=\"color: #998;font-style: italic\"># Orbital angular velocity [rad\/s]<\/span>\r\nT_orbit <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">2<\/span> <span style=\"font-weight: bold\">*<\/span> np<span style=\"font-weight: bold\">.<\/span>pi <span style=\"font-weight: bold\">\/<\/span> omega0  <span style=\"color: #998;font-style: italic\"># Orbital period [s]<\/span>\r\n\r\n<span style=\"color: #998;font-style: italic\"># Satellite parameters<\/span>\r\nmass <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">1000<\/span>  <span style=\"color: #998;font-style: italic\"># Satellite mass [kg]<\/span>\r\ndrag_coeff <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">2.2<\/span>  <span style=\"color: #998;font-style: italic\"># Drag coefficient<\/span>\r\narea <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">2<\/span> <span style=\"color: #998;font-style: italic\"># Cross-sectional area [m^2]<\/span>\r\n\r\n<span style=\"color: #998;font-style: italic\"># Atmospheric parameters<\/span>\r\nrho <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">3.1e-13<\/span> <span style=\"color: #998;font-style: italic\"># 1.225e-12  # Atmospheric density at altitude 500 [kg\/m^3]<\/span>\r\n\r\n<span style=\"color: #998;font-style: italic\"># A33, A44<\/span>\r\nk_drag <span style=\"font-weight: bold\">=<\/span> <span style=\"font-weight: bold\">-<\/span>(rho <span style=\"font-weight: bold\">*<\/span> drag_coeff <span style=\"font-weight: bold\">*<\/span> area <span style=\"font-weight: bold\">*<\/span> r0 <span style=\"font-weight: bold\">*<\/span> omega0) <span style=\"font-weight: bold\">\/<\/span> (<span style=\"color: #099\">2<\/span> <span style=\"font-weight: bold\">*<\/span> mass)\r\n<\/pre>\n<\/div>\n<p>The state space matrices are defined as follows:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>A <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array([\r\n    [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">0<\/span>],\r\n    [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>],\r\n    [<span style=\"color: #099\">3<\/span> <span style=\"font-weight: bold\">*<\/span> omega0 <span style=\"font-weight: bold\">**<\/span> <span style=\"color: #099\">2<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"font-weight: bold\">-<\/span>k_drag, <span style=\"font-weight: bold\">-<\/span><span style=\"color: #099\">2<\/span> <span style=\"font-weight: bold\">*<\/span> r0 <span style=\"font-weight: bold\">*<\/span> omega0],\r\n    [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">2<\/span> <span style=\"font-weight: bold\">*<\/span> omega0 <span style=\"font-weight: bold\">\/<\/span> r0, <span style=\"font-weight: bold\">-<\/span>k_drag]\r\n])\r\nB <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array([\r\n    [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>],\r\n    [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>],\r\n    [<span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">0<\/span>],\r\n    [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span> ]\r\n])\r\n\r\nC <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array([[<span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>],[<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">0<\/span>],[<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>]]);\r\nD <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array([[<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>]]);\r\n<\/pre>\n<\/div>\n<h4  id=\"LQ-CONTROLLER\">LQ Controller<\/h4>\n<p>Let&#8217;s compute LQ controller (<em>K<\/em> matrix) by state feedback with some arbitrary choice of weights Q and R:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"color: #998;font-style: italic\"># Q and R matrices<\/span>\r\nQ <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>diag(np<span style=\"font-weight: bold\">.<\/span>asarray([<span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">1<\/span>]))\r\nR <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>diag(np<span style=\"font-weight: bold\">.<\/span>asarray([<span style=\"color: #099\">10<\/span>, <span style=\"color: #099\">10<\/span>]))\r\n\r\n<span style=\"color: #998;font-style: italic\"># Solve Riccati equation<\/span>\r\nP <span style=\"font-weight: bold\">=<\/span> linalg<span style=\"font-weight: bold\">.<\/span>solve_continuous_are(A, B, Q, R)\r\nK <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>linalg<span style=\"font-weight: bold\">.<\/span>inv(R) <span style=\"font-weight: bold\">@<\/span> B<span style=\"font-weight: bold\">.<\/span>T <span style=\"font-weight: bold\">@<\/span> P\r\n<\/pre>\n<\/div>\n<p>Now we put the plant with LQ controller (K matrix) in a closed-loop form:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>Acl <span style=\"font-weight: bold\">=<\/span> A <span style=\"font-weight: bold\">-<\/span> B <span style=\"font-weight: bold\">@<\/span> K <span style=\"font-weight: bold\">@<\/span> C;\r\nBcl <span style=\"font-weight: bold\">=<\/span> B <span style=\"font-weight: bold\">@<\/span> K;\r\nDcl <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array([[<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>]]);\r\n\r\nclosed_loop <span style=\"font-weight: bold\">=<\/span> ct<span style=\"font-weight: bold\">.<\/span>ss(Acl, Bcl, C, Dcl);\r\n<\/pre>\n<\/div>\n<h4  id=\"VALIDATION\">Validation<\/h4>\n<p>The resulting controller is validated by simulating the continuous-time closed-loop system. This step establishes a reference behavior that will be used for comparison throughout the workflow.<\/p>\n<p>We will setup timeline, initial conditions and input reference signals to run the simulation. The satellite control system already contains a disturbance &#8211; atmospheric drag. Let&#8217;s see how the satellite performs reference tracking. We will set a particular form input to the satellite model. The orbit <em>r<\/em> is a fixed to:<\/p>\n<ul>\n<li>$7.1\u00b7{10}^{6}$ meters at step 0. <\/li>\n<li>$7.1\u00b7{10}^{6}+{10}^{3}$ meters at step 100. <\/li>\n<li>$7.1\u00b7{10}^{6}+{1.5\u00b710}^{3}$ meters at step 1000. <\/li>\n<\/ul>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>dt <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">0.01<\/span>;\r\nt <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>linspace(<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">20<\/span>, <span style=\"color: #099\">2000<\/span>);\r\n\r\n<span style=\"color: #998;font-style: italic\"># Initial condition<\/span>\r\nmu <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">3.986e14<\/span>  <span style=\"color: #998;font-style: italic\"># Earth gravitational parameter mu = G * M [m^3\/s^2]<\/span>\r\naltitude <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">729e3<\/span>  <span style=\"color: #998;font-style: italic\"># Orbital altitude [m]<\/span>\r\nr0 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">6.371e6<\/span> <span style=\"font-weight: bold\">+<\/span> altitude  <span style=\"color: #998;font-style: italic\"># Orbital radius [m]<\/span>\r\nomega0 <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>sqrt(mu <span style=\"font-weight: bold\">\/<\/span> r0 <span style=\"font-weight: bold\">**<\/span> <span style=\"color: #099\">3<\/span>)  <span style=\"color: #998;font-style: italic\"># Orbital angular velocity [rad\/s]<\/span>\r\ntheta <span style=\"font-weight: bold\">=<\/span>  t <span style=\"font-weight: bold\">*<\/span> omega0; \r\n\r\n<span style=\"color: #998;font-style: italic\"># Reference input <\/span>\r\nr <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>zeros((<span style=\"color: #999\">len<\/span>(t), <span style=\"color: #099\">4<\/span>)); \r\nr[:, <span style=\"color: #099\">0<\/span>] <span style=\"font-weight: bold\">=<\/span> r0;        <span style=\"color: #998;font-style: italic\"># constant on input 1<\/span>\r\nr[:, <span style=\"color: #099\">1<\/span>] <span style=\"font-weight: bold\">=<\/span> theta;       \r\nr[:, <span style=\"color: #099\">2<\/span>] <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">0<\/span>;    \r\nr[:, <span style=\"color: #099\">3<\/span>] <span style=\"font-weight: bold\">=<\/span> omega0;\r\n\r\n<span style=\"color: #998;font-style: italic\"># Step function<\/span>\r\nstart1 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">100<\/span><span style=\"font-weight: bold\">*<\/span>dt;     <span style=\"color: #998;font-style: italic\"># starting time (cycles)<\/span>\r\namplitude1 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">1000<\/span>\r\nstart2 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">1000<\/span><span style=\"font-weight: bold\">*<\/span>dt;\r\namplitude2 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">500<\/span>\r\noffset <span style=\"font-weight: bold\">=<\/span> r0\r\ndist1 <span style=\"font-weight: bold\">=<\/span> offset <span style=\"font-weight: bold\">+<\/span> amplitude1 <span style=\"font-weight: bold\">*<\/span> (t <span style=\"font-weight: bold\">&gt;=<\/span> start1)\r\ndist2 <span style=\"font-weight: bold\">=<\/span>  amplitude2 <span style=\"font-weight: bold\">*<\/span> (t <span style=\"font-weight: bold\">&gt;=<\/span> start2)\r\ndistX <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>zeros((<span style=\"color: #999\">len<\/span>(t), <span style=\"color: #099\">4<\/span>))\r\nr[:,<span style=\"color: #099\">0<\/span>] <span style=\"font-weight: bold\">=<\/span> dist1 <span style=\"font-weight: bold\">+<\/span> dist2;\r\n\r\nx0 <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array([r0, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, omega0]);   <span style=\"color: #998;font-style: italic\"># initial state<\/span>\r\n<\/pre>\n<\/div>\n<p>Now we can run the closed-loop system and check performances for reference tracking:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>t, y, x <span style=\"font-weight: bold\">=<\/span> lsim((Acl,Bcl,C,Dcl), U<span style=\"font-weight: bold\">=<\/span>r, T<span style=\"font-weight: bold\">=<\/span>t, X0<span style=\"font-weight: bold\">=<\/span>x0);\r\nerror_ct <span style=\"font-weight: bold\">=<\/span> r <span style=\"font-weight: bold\">-<\/span> y;\r\nt_scade_one <span style=\"font-weight: bold\">=<\/span> t<span style=\"font-weight: bold\">\/<\/span>dt; \r\n\r\nplt<span style=\"font-weight: bold\">.<\/span>figure(<span style=\"color: #099\">1<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>plot(t_scade_one, error_ct[:, <span style=\"color: #099\">0<\/span>])\r\nplt<span style=\"font-weight: bold\">.<\/span>title(<span style=\"color: #B84\">&quot;Position error&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>xlabel(<span style=\"color: #B84\">&quot;Time [s]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>ylabel(<span style=\"color: #B84\">&quot;[m]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>grid()\r\nplt<span style=\"font-weight: bold\">.<\/span>show()\r\n\r\nplt<span style=\"font-weight: bold\">.<\/span>figure(<span style=\"color: #099\">2<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>plot(t_scade_one, y[:, <span style=\"color: #099\">0<\/span>])\r\nplt<span style=\"font-weight: bold\">.<\/span>plot(t_scade_one, r[:, <span style=\"color: #099\">0<\/span>])\r\nplt<span style=\"font-weight: bold\">.<\/span>title(<span style=\"color: #B84\">&quot;Reference and output position&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>xlabel(<span style=\"color: #B84\">&quot;Time [s]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>ylabel(<span style=\"color: #B84\">&quot;[m]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>grid()\r\nplt<span style=\"font-weight: bold\">.<\/span>show()\r\n<\/pre>\n<\/div>\n<p>The simulation traces (see Figure 4) show an error of the satellite position tracking: at execution step 2000 its steady state value is equal to -0.141 m. This is expected: the LQ controller does not include integral action, meaning it cannot completely eliminate constant disturbances. Despite this, the error remains very small (below 0.001%), demonstrating the effectiveness of the controller<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-ref-and-output-continuous.svg\" style=\"max-height: 500px !important\" \/><br \/>\n    <em>Figure 4: Reference and output satellite orbital position (a) and error of the satellite orbital position (b), (continuous-time simulation in Python)<\/em>\n<\/p>\n<h3  id=\"DISCRETE-TIME-CONTROL-DESIGN-AND-VALIDATION-IN-SCADE-ONE\">Discrete-time Control Design and Validation in Scade One<\/h3>\n<p>To prepare for code generation, the satellite dynamics and controller are implemented in Scade One using Swan constructs.<\/p>\n<p>To show the details of the closed-loop system implementation, let&#8217;s review how core linear algebra operations can be expressed in Swan.<\/p>\n<h4  id=\"LQ-CONTROLLER-IMPLEMENTATION\">LQ Controller Implementation<\/h4>\n<p>The arrays <code>A<\/code>, <code>B<\/code> and <code>K<\/code> are defined as follows:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>const A: float64<span style=\"font-weight: bold\">^<\/span><span style=\"color: #099\">4<\/span><span style=\"font-weight: bold\">^<\/span><span style=\"color: #099\">4<\/span> <span style=\"font-weight: bold\">=<\/span> [[<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">0<\/span>], \r\n                        [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>], \r\n                        [<span style=\"color: #099\">3.34e-6<\/span>,<span style=\"color: #099\">0<\/span>, <span style=\"font-weight: bold\">-<\/span><span style=\"color: #099\">2.55e-11<\/span>,<span style=\"color: #099\">1.5e4<\/span>], \r\n                        [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>, <span style=\"font-weight: bold\">-<\/span><span style=\"color: #099\">2.97e-10<\/span>, <span style=\"font-weight: bold\">-<\/span><span style=\"color: #099\">2.55e-11<\/span>]];\r\nconst B: float64<span style=\"font-weight: bold\">^<\/span><span style=\"color: #099\">2<\/span><span style=\"font-weight: bold\">^<\/span><span style=\"color: #099\">4<\/span> <span style=\"font-weight: bold\">=<\/span> [[<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], \r\n                        [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">0<\/span>], \r\n                        [<span style=\"color: #099\">1<\/span>, <span style=\"color: #099\">0<\/span>], \r\n                        [<span style=\"color: #099\">0<\/span>, <span style=\"color: #099\">1<\/span>]];\r\nconst K: float64<span style=\"font-weight: bold\">^<\/span><span style=\"color: #099\">4<\/span><span style=\"font-weight: bold\">^<\/span><span style=\"color: #099\">2<\/span> <span style=\"font-weight: bold\">=<\/span> [[<span style=\"color: #099\">2.072e-03<\/span>, <span style=\"font-weight: bold\">-<\/span><span style=\"color: #099\">3.16e-1<\/span>, <span style=\"color: #099\">2.12e-3<\/span>, <span style=\"color: #099\">3.23e-1<\/span>], \r\n                       [<span style=\"color: #099\">3.16e-01<\/span>, <span style=\"color: #099\">2.072e-03<\/span>, <span style=\"color: #099\">3.23e-01<\/span>, <span style=\"color: #099\">9.83e+01<\/span>]];\r\n<\/pre>\n<\/div>\n<p>In the definitions above, <code>float64^M^N<\/code> denotes the type on an array of size <code>N<\/code> where each cell is an array of size <code>M<\/code> of float64 values. From a mathematical point of view, the index <code>M<\/code> corresponds to the number of columns, and the index <code>N<\/code> to the number of rows. The example above follows the convention that matrices are organized by rows; however, there is no prescriptive standard necessitating such an arrangement.<\/p>\n<p>Scade One provides a substantial collection of standard libraries, covering a wide spectrum of typical use cases. Rather than reimplementing existing functionality, you are encouraged to leverage these components whenever possible, to benefit from proven, reusable, and standardized solutions.<\/p>\n<p>Numerous vector and matrix computations are provided by the <em>Vector<\/em> and <em>Matrix<\/em> libraries included in the Scade One installation. Some of them are presented in the article <a href=\"https:\/\/innovationspace.ansys.com\/knowledge\/forums\/topic\/matrix-operators-using-scade-ones-forward-block\/\">Matrix operators using Scade One&#8217;s forward block<\/a>. These operators are mostly based on the <code>forward<\/code> and <code>map<\/code> constructs, allowing iterations over arrays. All operators in the <em>Vector<\/em> and <em>Matrix<\/em> libraries are polymorphic, meaning that they can support any numeric type. The operators listed below are part of the satellite model: <\/p>\n<ul>\n<li><em>Scale<\/em> from <em>Vector<\/em> library &#8211; vector-scalar multiplication<\/li>\n<li><em>Add<\/em> and <em>Sub<\/em> from <em>Vector<\/em> library &#8211; vector addition and subtraction<\/li>\n<li><em>MatVectMult<\/em> from <em>Matrix<\/em> library &#8211; matrix-vector multiplication<\/li>\n<\/ul>\n<p>The implementation of the LQ controller is shown in Figure 5. It computes the difference (or error) between the reference satellite orbit parameters <code>yRef<\/code> and the model output parameters <code>y<\/code>, and multiplies this difference by the LQ matrix <code>K<\/code>.<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-lq-controller.png\" style=\"max-height: 350px !important\" \/><br \/>\n    <em>Figure 5: LQ controller<\/em>\n<\/p>\n<h4  id=\"INTEGRAL-OF-AN-ARRAY\">Integral of an Array<\/h4>\n<p>In the <em>Satellite<\/em> model, you need to compute an integral of four independent states (flows) that will vary with time, $y=x(t)={\\left[x_{1},x_{2},x_{3},x_{4}\\right]}^{T}={[r,\\ \\theta ,\\ \\dot{r},\\ \\dot{\\theta }]}^{T}$.<\/p>\n<p>To compute an array integral, the <code>map<\/code> iterator is combined with a nested integrator, see Figure 6:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-array-integrator.png\" style=\"max-height: 400px !important\" \/><br \/>\n    <em>Figure 6: Integral of an array using <code>map<\/code> operator<\/em>\n<\/p>\n<p>The constant <code>N<\/code> is defined as follows: &#8216;<code>const N: int32 = 4<\/code>&#8216;. The <code>map<\/code> construct allows to process the array elements independently computing an integral for each flow (<em>IntegratorBwd<\/em> is replicated <code>N=4<\/code> times in parallel).<\/p>\n<h4  id=\"COMPUTATION-OF-THE-REFERENCE-SATELLITE-PARAMETERS-WITH-ARRAY-CONSTRUCTOR\">Computation of the Reference Satellite Parameters with Array Constructor<\/h4>\n<p>The reference input array in the satellite model (see Figure 4) is given by the following expression: $yRef={[r_{0},\\ \\theta ,\\ 0,\\ {\\dot{\\theta }}_{0}]}^{T}$, where <\/p>\n<ul>\n<li>The constant orbital position $r_{0}$ is considered, thus the radial velocity $\\dot{r}$ is zero.<\/li>\n<li>The satellite angular position $\\theta$ is defined by the equation $\\theta =\\int_{0}^{DT(k-1)} \\dot{\\theta }\\ dt$.<\/li>\n<\/ul>\n<p>To compute the discrete-time integral of the angular speed $\\dot{\\theta}$, you can use the backward Euler integrator approximation:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>const DT: float64 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">0.01<\/span>;\r\n\r\nnode IntegratorBwd (u: float64; yInit: float64)\r\nreturns (y: float64)\r\n{\r\n    let y <span style=\"font-weight: bold\">=<\/span> (yInit pre y) <span style=\"font-weight: bold\">+<\/span> DT <span style=\"font-weight: bold\">*<\/span> u;\r\n}\r\n<\/pre>\n<\/div>\n<p>$$y\\left(k\\right)=y\\left(k-1\\right)+DT\\ast u\\left(k\\right)$$<\/p>\n<p>The reference input array<em> <\/em><code>yRef<\/code> can be designed in Scade One as follows (see Figure 7):<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-yref-from-theta-dot0.png\" style=\"max-height: 500px !important\" \/><br \/>\n    <em>Figure 7: Computation of the reference satellite parameters <code>yRef<\/code> from the constant angular speed  ${\\dot{\\boldsymbol{\\theta }}}_{0}$<\/em>\n<\/p>\n<p>The <strong>array<\/strong> <strong>constructor<\/strong> operator &#8216;<code>[ _ ]<\/code>&#8216; allows to create the <code>yRef<\/code> array from constant orbital position  $r_{0}$, constant angular speed ${\\dot{\\theta }}_{0}$, and computed angular position  $\\theta$.<\/p>\n<h4  id=\"CLOSED-LOOP-SATELLITE-SYSTEM-WITH-LQ-CONTROLLER\">Closed-loop Satellite System with LQ Controller<\/h4>\n<p>The implementation of the full closed-loop system (a test harness) from Figure 3 in Scade One is shown in Figure 8:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-satellite-with-control.png\" style=\"max-height: 650px !important\" \/><br \/>\n    <em>Figure 8: Implementation of the satellite control system with LQ controller in Scade One<\/em>\n<\/p>\n<p>To properly initialize the model in Figure 4, the following initial parameters of the satellite orbit are used: &#8216;<code>[r0, 0, 0, thetaDot0]<\/code>&#8216;, where:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>const r0: float64 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">7100000<\/span>;\r\nconst thetaDot0: float64 <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #099\">0.0010553<\/span>;\r\n<\/pre>\n<\/div>\n<p>Compared to the theoretical diagram in Figure 3, this model introduces two additional operators <code>pre<\/code> in the feedback loops. The <code>pre<\/code> operator denotes a unit delay. <\/p>\n<p>A Swan program represents a system of equations in which each variable or flow is defined once and only once by a fixed rule, ensuring a unique solution. To guarantee causality, a flow must not depend on itself, either directly or indirectly. Consequently, when a feedback loop is required in a model, the <code>pre<\/code> operator is mandatory to break instantaneous dependencies and preserve causality.<\/p>\n<\/p>\n<h4  id=\"VALIDATION\">Validation<\/h4>\n<p>The test harness for the satellite control system in Figure 8 allows to check reference tracking, and set the orbit $r$ using two step functions from the <em>Sources<\/em> library, with following parameters: <\/p>\n<ul>\n<li>Step function 1: starting time &#8216;<code>cycles = 100<\/code>&#8216;, &#8216;<code>amplitude = 1000<\/code>&#8216;, &#8216;<code>offset = r0 = 7.1e+06<\/code>&#8216;.<\/li>\n<li>Step function 2: starting time &#8216;<code>cycles = 1000<\/code>&#8216;, &#8216;<code>amplitude = 500<\/code>&#8216;, &#8216;<code>offset = 0<\/code>&#8216;.<\/li>\n<\/ul>\n<p>The simulations results for sampling period <em>DT <\/em>= 0.01 and 2000 cycles are shown in Figure 9:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-ref-and-output-scade-one.png\" style=\"max-height: 500px !important\" \/><br \/>\n    <em>Figure 9: Reference and output satellite orbital position (a) and error of the satellite orbital position (b) (discrete-time simulation in Scade One)<\/em>\n<\/p>\n<p>The simulation reveals a small steady-state error, at execution step 2000 it is equal to -0.161. <\/p>\n<h4  id=\"CODE-GENERATION-IN-SCADE-ONE\">Code Generation in Scade One<\/h4>\n<p>Now, you can create in <strong>Scade One<\/strong> a code generation job for the satellite control model (LQ controller). Set the job name as <em>CodeGenerationJobController<\/em>, select the LQ controller as root operator in the job properties and launch the job (optionally), see Figure 10.<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-code-generation-job.png\" style=\"max-height: 300px !important\" \/><br \/>\n    <em>Figure 10: Code generation job for LQ controller<\/em>\n<\/p>\n<h3  id=\"INTEGRATION-AND-VALIDATION-OF-THE-SCADE-ONE-CONTROLLER-IN-PYTHON\">Integration and Validation of the Scade One Controller in Python<\/h3>\n<p>The controller code generated by Scade One is wrapped into a Python proxy using the provided Python wrapper service. This proxy is then integrated into a discrete-time simulation of the satellite plant in Python.<\/p>\n<h4  id=\"PYTHON-WRAPPER-AND-PYTHON-PROXY\">Python Wrapper and Python Proxy<\/h4>\n<p>First we&#8217;ll create a Python proxy for the Scade One controller, using the Python wrapper service (the link for the complete script is in <em><\/em><em><\/em>Next Steps<em><\/em>):<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>gen <span style=\"font-weight: bold\">=<\/span> PythonWrapper(project<span style=\"font-weight: bold\">=<\/span>prj, job<span style=\"font-weight: bold\">=<\/span>job_name, output<span style=\"font-weight: bold\">=<\/span>out_name, target_dir<span style=\"font-weight: bold\">=<\/span>target_dir)\r\ngen<span style=\"font-weight: bold\">.<\/span>generate()\r\n<\/pre>\n<\/div>\n<p>Once the script has been executed, a generated folder is available in the current location containing the wrapped code. Two important files are generated in the target folder: <em>satellite_control_wrapper.dll<\/em> and <em>satellite_control_wrapper.py<\/em>. <\/p>\n<p>The <em>satellite_control_wrapper.py<\/em> file uses ctypes to wrap the controller operator in a class that exposes its inputs (<code>y<\/code> and <code>yRef<\/code>), its output (<code>u<\/code>), and functions to run it.<\/p>\n<p>The next step is to import the generated Python proxy and integrate it into the satellite control system in Python:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">from<\/span><span style=\"color: #BBB\"> <\/span><span style=\"color: #555\">satellite_control_wrapper<\/span><span style=\"color: #BBB\"> <\/span><span style=\"font-weight: bold\">import<\/span> satellite_control_wrapper\r\n<span style=\"color: #998;font-style: italic\"># Create an instance of a controller main operator<\/span>\r\nsatellite_controller <span style=\"font-weight: bold\">=<\/span> satellite_control_wrapper<span style=\"font-weight: bold\">.<\/span>LQController_Control()\r\n<\/pre>\n<\/div>\n<h4  id=\"DISCRETIZATION-OF-THE-SATELLITE-MODEL\">Discretization of the Satellite Model<\/h4>\n<p>Why discretization of the continuous-time satellite model is required to enable simulation:<\/p>\n<ul>\n<li>Scade One operates in a discrete-time execution model <\/li>\n<li>The continuous satellite plant must therefore be discretized <\/li>\n<\/ul>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>plant_d <span style=\"font-weight: bold\">=<\/span> ct<span style=\"font-weight: bold\">.<\/span>c2d(plant, dt)\r\nA <span style=\"font-weight: bold\">=<\/span> plant_d<span style=\"font-weight: bold\">.<\/span>A\r\nB <span style=\"font-weight: bold\">=<\/span> plant_d<span style=\"font-weight: bold\">.<\/span>B\r\nC <span style=\"font-weight: bold\">=<\/span> plant_d<span style=\"font-weight: bold\">.<\/span>C\r\nD <span style=\"font-weight: bold\">=<\/span> plant_d<span style=\"font-weight: bold\">.<\/span>D\r\n<\/pre>\n<\/div>\n<p>Next, some storage variables are prepared to perform the simulation:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"color: #998;font-style: italic\"># Initial state<\/span>\r\nx_dt <span style=\"font-weight: bold\">=<\/span> x0<span style=\"font-weight: bold\">.<\/span>copy()\r\n\r\n<span style=\"color: #998;font-style: italic\"># Storage<\/span>\r\ny_hist <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>zeros((<span style=\"color: #999\">len<\/span>(t), <span style=\"color: #099\">4<\/span>))\r\nu_hist <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>zeros((<span style=\"color: #999\">len<\/span>(t), <span style=\"color: #099\">2<\/span>))\r\nerror_hist <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>zeros((<span style=\"color: #999\">len<\/span>(t), <span style=\"color: #099\">4<\/span>))\r\n<\/pre>\n<\/div>\n<p>Once this is done, the discrete-time system output can be computed:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">for<\/span> i, timer <span style=\"font-weight: bold\">in<\/span> <span style=\"color: #999\">enumerate<\/span>(t):\r\n   \r\n    y_dt <span style=\"font-weight: bold\">=<\/span> C <span style=\"font-weight: bold\">@<\/span> x_dt \r\n    y_hist[i, :] <span style=\"font-weight: bold\">=<\/span> y_dt\r\n\r\n    <span style=\"color: #998;font-style: italic\"># --- Error ---<\/span>\r\n    error <span style=\"font-weight: bold\">=<\/span> r[i, :] <span style=\"font-weight: bold\">-<\/span> y_dt\r\n    error_hist[i, :] <span style=\"font-weight: bold\">=<\/span> error   <span style=\"color: #998;font-style: italic\"># store<\/span>\r\n   \r\n    input1 <span style=\"font-weight: bold\">=<\/span> r[i, :]\r\n    input2 <span style=\"font-weight: bold\">=<\/span> y_hist[i,:]\r\n    <span style=\"color: #998;font-style: italic\"># --- Fill inputs (ctypes arrays) ---<\/span>\r\n    <span style=\"font-weight: bold\">for<\/span> k <span style=\"font-weight: bold\">in<\/span> <span style=\"color: #999\">range<\/span>(<span style=\"color: #099\">4<\/span>):\r\n        satellite_controller<span style=\"font-weight: bold\">.<\/span>inputs<span style=\"font-weight: bold\">.<\/span>_yRef[k] <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #999\">float<\/span>(input1[k])\r\n        satellite_controller<span style=\"font-weight: bold\">.<\/span>inputs<span style=\"font-weight: bold\">.<\/span>_y[k] <span style=\"font-weight: bold\">=<\/span> <span style=\"color: #999\">float<\/span>(input2[k])\r\n\r\n    <span style=\"color: #998;font-style: italic\"># --- Call the SCADE operator ---<\/span>\r\n    satellite_controller<span style=\"font-weight: bold\">.<\/span>cycle_fct(\r\n        satellite_controller<span style=\"font-weight: bold\">.<\/span>inputs<span style=\"font-weight: bold\">.<\/span>_yRef,\r\n        satellite_controller<span style=\"font-weight: bold\">.<\/span>inputs<span style=\"font-weight: bold\">.<\/span>_y,\r\n        satellite_controller<span style=\"font-weight: bold\">.<\/span>outputs<span style=\"font-weight: bold\">.<\/span>_u,\r\n    )\r\n\r\n    <span style=\"color: #998;font-style: italic\"># --- Extract outputs properly ---<\/span>\r\n    u <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array([satellite_controller<span style=\"font-weight: bold\">.<\/span>outputs<span style=\"font-weight: bold\">.<\/span>_u[i] <span style=\"font-weight: bold\">for<\/span> i <span style=\"font-weight: bold\">in<\/span> <span style=\"color: #999\">range<\/span>(<span style=\"color: #099\">2<\/span>)])\r\n    u <span style=\"font-weight: bold\">=<\/span> np<span style=\"font-weight: bold\">.<\/span>array(u)\r\n\r\n    <span style=\"color: #998;font-style: italic\"># store results<\/span>\r\n    u_hist[i, :] <span style=\"font-weight: bold\">=<\/span> u \r\n\r\n    <span style=\"color: #998;font-style: italic\"># --- Plant update ---<\/span>\r\n    x_dt <span style=\"font-weight: bold\">=<\/span> A <span style=\"font-weight: bold\">@<\/span> x_dt <span style=\"font-weight: bold\">+<\/span> B <span style=\"font-weight: bold\">@<\/span> u\r\n<\/pre>\n<\/div>\n<p>To plot the simulation results:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>plt<span style=\"font-weight: bold\">.<\/span>figure(<span style=\"color: #099\">1<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>plot(t_scade_one, error_hist[:, <span style=\"color: #099\">0<\/span>])\r\nplt<span style=\"font-weight: bold\">.<\/span>title(<span style=\"color: #B84\">&quot;Position error&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>xlabel(<span style=\"color: #B84\">&quot;Time [s]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>ylabel(<span style=\"color: #B84\">&quot;[m]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>grid()\r\nplt<span style=\"font-weight: bold\">.<\/span>show()\r\n\r\nplt<span style=\"font-weight: bold\">.<\/span>figure(<span style=\"color: #099\">2<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>plot(t_scade_one, y_hist[:, <span style=\"color: #099\">0<\/span>])\r\nplt<span style=\"font-weight: bold\">.<\/span>plot(t_scade_one, r[:, <span style=\"color: #099\">0<\/span>])\r\nplt<span style=\"font-weight: bold\">.<\/span>title(<span style=\"color: #B84\">&quot;Reference and output position&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>xlabel(<span style=\"color: #B84\">&quot;Time [s]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>ylabel(<span style=\"color: #B84\">&quot;[m]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>grid()\r\nplt<span style=\"font-weight: bold\">.<\/span>show()\r\n<\/pre>\n<\/div>\n<p>The simulation results of the closed-loop system with the imported Scade One controller are shown in Figure 11:<\/p>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-ref-and-output.svg\" style=\"max-height: 500px !important\" \/><br \/>\n    <em>Figure 11: Reference and output satellite orbital position (a) and error of the satellite orbital position (b) in Python<\/em>\n<\/p>\n<p>The steady-state (or static) error of the satellite position at execution step 2000 is equal to -0.141.<\/p>\n<p>The difference in tracking errors is then plotted (see Figure 12):<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>plt<span style=\"font-weight: bold\">.<\/span>figure(<span style=\"color: #099\">1<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>plot(t_scade_one, error_hist[:, <span style=\"color: #099\">0<\/span>]<span style=\"font-weight: bold\">-<\/span>error_ct[:, <span style=\"color: #099\">0<\/span>])\r\nplt<span style=\"font-weight: bold\">.<\/span>title(<span style=\"color: #B84\">&quot;Position error difference&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>xlabel(<span style=\"color: #B84\">&quot;Time [s]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>ylabel(<span style=\"color: #B84\">&quot;[m]&quot;<\/span>)\r\nplt<span style=\"font-weight: bold\">.<\/span>grid()\r\nplt<span style=\"font-weight: bold\">.<\/span>show()\r\n<\/pre>\n<\/div>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-tracking-error-difference.svg\" style=\"max-height: 500px !important\" \/><br \/>\n    <em>Figure 12: The difference in tracking errors between the system using the continuous-time controller and the discrete-time controller imported from Scade One<\/em>\n<\/p>\n<p>The results obtained with the continuous-time controller and the discrete-time controller exported from Scade One show a high level of agreement, demonstrating the accuracy of the integrated model. This comparison highlights the consistency between the two modeling approaches.<\/p>\n<h3  id=\"NEXT-STEPS\">Next Steps<\/h3>\n<p>You can download the example model and notebook from this blog <a href=\"https:\/\/github.com\/ansys\/scadeone-examples\/releases\/latest\/download\/2026-07-15-satellite-controller.zip\">here on GitHub<\/a>. The archive contains everything you need to run the example:<\/p>\n<ul>\n<li>Scade One control model (sources)<\/li>\n<li>Python model (notebook)<\/li>\n<\/ul>\n<p>You can extend this Swan model by reusing or combining existing operators (such as <code>map<\/code>, <code>forward<\/code>, and standard library components) to implement additional computations.<\/p>\n<p>To go further, explore the Scade One standard libraries to replace or enrich parts of your own models to accelerate development. <\/p>\n<p>Share your thoughts, questions, or improvements on the <a href=\"https:\/\/innovationspace.ansys.com\/forum\/forums\/forum\/discuss-simulation\/embedded-software\/\">Ansys community forum<\/a> and see how others are using Scade One for control applications.<\/p>\n<h3  id=\"ABOUT-THE-AUTHOR\">About the author<\/h3>\n<table style=\"max-width: 1000px;border: none !important\">\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px;border: none !important\">\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2026\/07\/scade-072-author.png\" style=\"max-height: 150px !important\" \/><br \/>\n                <em><\/em>\n<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px;border: none !important\">\n<p><strong>Elena Ivanova<\/strong> (<a href=\"https:\/\/www.linkedin.com\/in\/elenaivanova0513\/\">LinkedIn<\/a>) is a Senior R&amp;D Documentation Specialist. She joined Ansys in 2024 and works on the Scade One product. She has a PhD in Control Theory and about 10 years of experience in automotive and aeronautics.<\/p>\n<\/td>\n<\/tr>\n<\/table>\n","protected":false},"template":"","class_list":["post-199106","topic","type-topic","status-publish","hentry","topic-tag-arrays","topic-tag-forward","topic-tag-lq-control","topic-tag-map","topic-tag-orbit","topic-tag-pyscadeone","topic-tag-python","topic-tag-python-proxy","topic-tag-python-wrapper","topic-tag-satellite","topic-tag-scade-one","topic-tag-space","topic-tag-state-space","topic-tag-swan"],"aioseo_notices":[],"acf":[],"custom_fields":[{"0":{"_edit_lock":["1784279987:1769"],"_edit_last":["1769"],"_aioseo_title":[null],"_aioseo_description":[null],"_aioseo_keywords":["a:0:{}"],"_aioseo_og_title":[null],"_aioseo_og_description":[null],"_aioseo_og_article_section":[""],"_aioseo_og_article_tags":["a:0:{}"],"_aioseo_twitter_title":[null],"_aioseo_twitter_description":[null],"filter_by_optics_product":["Lumerical"],"_filter_by_optics_product":["field_64fb192ba3121"],"application_name":[""],"_application_name":["field_64a80903c8e15"],"family":[""],"_family":["field_64a809229a857"],"siebel_km_number":[""],"_siebel_km_number":["field_63ecbffce60db"],"salesforce_km_number":[""],"_salesforce_km_number":["field_63ecc018e60dc"],"km_published_date":[""],"_km_published_date":["field_64c77704499dd"],"product_version":[""],"_product_version":["field_64c776cb4fd2e"],"_bbp_forum_id":["27825"],"_bbp_topic_id":["199111"],"_bbp_author_ip":["87.190.19.71"],"_bbp_last_reply_id":["0"],"_bbp_last_active_id":["199107"],"_bbp_last_active_time":["2026-07-16 16:12:20"],"_bbp_reply_count":["0"],"_bbp_reply_count_hidden":["0"],"_bbp_voice_count":["0"],"_btv_view_count":["68"],"_bbp_likes_count":["1"]},"test":"solution"}],"_links":{"self":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/199106","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics"}],"about":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/types\/topic"}],"version-history":[{"count":5,"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/199106\/revisions"}],"predecessor-version":[{"id":199111,"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/199106\/revisions\/199111"}],"wp:attachment":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/media?parent=199106"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}