From 60cff1a7c361c4b597ebfa66cb72d7c6191401d7 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Fri, 18 Apr 2025 17:07:28 +0200
Subject: [PATCH 01/12] [coeff-transport] Implementing coefficient manager
 class

---
 classes/misc/Coefficient.m | 94 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 classes/misc/Coefficient.m

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
new file mode 100644
index 0000000..d5d60e2
--- /dev/null
+++ b/classes/misc/Coefficient.m
@@ -0,0 +1,94 @@
+classdef Coefficient
+    % Motor: Manages the coefficients associated with a rocket
+    %
+    %   Constructor:
+    %       - Coefficient: Creates an instance of the Coefficient class.
+    %           Loaded config: -
+    %           Loaded data: aeroCoefficients.mat
+    %           Arguments:
+    %               - filePath: char, path to coefficient file to load
+    %               - name: (optional) coefficient name. Used when dynamic
+    %               derivatives are also needed
+    %
+    %   Coefficients are retrieved from DM in the following format:
+    %       Coefficient matrix: double (15, nAlpha, nMach, nBeta, nAlt, nAbk, nXcg) 
+    %       Where the 15 coefficients are 
+    %           - CA        Axial force 
+    %           - CYB       Side force derivative wrt beta
+    %           - CY0       Side force
+    %           - CNA       Normal force derivative wrt alpha
+    %           - CN0       Normal force
+    %           - Cl        Rolling moment
+    %           - Clp       Rolling moment derivative wrt roll rate
+    %           - Cma       Pitching moment derivative wrt alpha
+    %           - Cm0       Pitching moment
+    %           - Cmad      Pitching moment derivative wrt alpha dot
+    %           - Cmq       Pitching moment derivative wrt pitch rate
+    %           - Cnb       Yawing moment derivative wrt beta
+    %           - Cn0       Yawing moment
+    %           - Cnr       Yawing moment derivative wrt yaw rate
+    %           - Cnp       Yawing moment derivative wrt roll rate
+    %
+    %   NOTE: When dynamic derivatives are not loaded, the matrix will be
+    %   6-D, as there will be no need to interpolate wrt to the xcg: moment
+    %   transport formulas will be used instead
+
+    properties
+        static          % Static coefficients: [CA, CY, CN, Cl, Cm, Cn]
+        dynamic         % Dynamic derivatives: [Clp, Cmad, Cmq, Cnr, Cnp]
+
+        finsCN          % Fins-only CN
+        geometry        % Reference geometry
+        state           % Flight envelope
+    end
+
+    properties(Access = private)
+        loadDynamic     % Whether to load dynamic derivatives. Adds XCG dependece
+    end
+    
+    methods
+        function obj = Coefficient(filePath, name)
+            arguments
+                filePath    (1, :)  char
+                name        (1, :)  char = 'generic'
+            end
+            
+            obj = obj.loadData(filePath, name);
+        end
+    end
+
+    methods(Access = private)
+        function obj = loadData(obj, filePath, name)
+            % Load the coefficients from the specified file
+            % filePath: char, path to coefficient file to load
+            % name: (optional) coefficient name. Used when dynamic
+            % derivatives are also needed
+            %
+            % NOTE: Coefficient alpha and beta derivatives are not loaded
+            %       as coefficients are already interpolated linearly
+            
+
+            if ~exist(filePath, 'file'), return; end
+            warning('off'); % Disable warning in case coefficients are not present
+            dataCoeffs = load(filePath, name);
+            warning('on');
+            
+            if ~isfield(dataCoeffs, name), return; end
+            dataCoeffs = dataCoeffs.(name);
+
+            obj.loadDynamic = ~strcmp(name, 'generic');
+            obj.finsCN = dataCoeffs.finsCN;
+            obj.geometry = dataCoeffs.geometry;
+            obj.state = dataCoeffs.state;
+            
+            % Load CA, CY, CN, Cl, Cm, Cn
+            obj.static = dataCoeffs.total([1, 3, 5, 6, 9, 13], :, :, :, :, :, :);
+
+            if obj.loadDynamic
+                % Load Clp, Cmad, Cmq, Cnr, Cnp
+                obj.dynamic = dataCoeffs.total([7, 10, 11, 14, 15], :, :, :, :, :, :);
+            end
+        end
+    end
+end
+
-- 
GitLab


From d8578d7fb9a56073eb4b3df1582193e3888d12ff Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Fri, 18 Apr 2025 22:36:19 +0200
Subject: [PATCH 02/12] [coeff-transport] Implemented coefficient transport
 system for xcg

---
 classes/misc/Coefficient.m | 155 +++++++++++++++++++++++++++++++++++--
 1 file changed, 148 insertions(+), 7 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index d5d60e2..a896b41 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -29,23 +29,46 @@ classdef Coefficient
     %           - Cnr       Yawing moment derivative wrt yaw rate
     %           - Cnp       Yawing moment derivative wrt roll rate
     %
+    %   Coefficients are then stored in the following format:
+    %       static:  double (6, nAlpha, nMach, nBeta, nAlt, nAbk)
+    %       Where the 6 coefficients are: 
+    %           - [CA, CY, CN, Cl, Cm, Cn]
+    %       dynamic: double (5, nAlpha, nMach, nBeta, nAlt, nAbk, nXcg)
+    %           - [Clp, Cmad, Cmq, Cnr, Cnp]
+    %
     %   NOTE: When dynamic derivatives are not loaded, the matrix will be
     %   6-D, as there will be no need to interpolate wrt to the xcg: moment
     %   transport formulas will be used instead
+    %
+    %   NOTE: Coefficients in an interpolants are stored on the last
+    %   dimension, instead of the first, to improve performance
+
+    properties(Dependent)
+        static                      double  % Static coefficients: [CA, CY, CN, Cl, Cm, Cn]
+        dynamic                     double  % Dynamic derivatives: [Clp, Cmad, Cmq, Cnr, Cnp]
+
+        geometry                    struct  % Reference geometry
+        state                       struct  % Flight envelope
+    end
 
     properties
-        static          % Static coefficients: [CA, CY, CN, Cl, Cm, Cn]
-        dynamic         % Dynamic derivatives: [Clp, Cmad, Cmq, Cnr, Cnp]
+        finsCN                      double  % Fins-only CN
+    end
 
-        finsCN          % Fins-only CN
-        geometry        % Reference geometry
-        state           % Flight envelope
+    properties(Access = private)
+        STATIC
+        DYNAMIC
+        GEOMETRY                    % Cached variable for geometry
+        STATE                       % Cached variable for state
     end
 
     properties(Access = private)
-        loadDynamic     % Whether to load dynamic derivatives. Adds XCG dependece
+        loadDynamic         (1, 1)  logical % Whether to load dynamic derivatives. Adds XCG dependece
+        
+        staticInterpolant   (1, 1)  griddedInterpolant
+        dynamicInterpolant  (1, 1)  griddedInterpolant
     end
-    
+
     methods
         function obj = Coefficient(filePath, name)
             arguments
@@ -55,6 +78,86 @@ classdef Coefficient
             
             obj = obj.loadData(filePath, name);
         end
+
+        function coefficients = get(obj, alpha, mach, beta, altitude, airbakes, xcg)
+            % GET: Retrieve aerodynamic coefficients for specified flight conditions
+            %
+            %   This method interpolates the aerodynamic coefficients based on
+            %   the provided flight conditions and adjusts them for the given
+            %   center of gravity (xcg).
+            %
+            %   Arguments:
+            %       - alpha     double, angle of attack             [rad]
+            %       - mach      double, Mach number                 [-]
+            %       - beta      double, sideslip angle              [rad]
+            %       - altitude  double, altitude                    [m]
+            %       - airbakes  double, airbrake deployment         [-]
+            %       - xcg       double, center of gravity position  [m]
+            %
+            %   Returns:
+            %       - coefficients: double (11, 1), aerodynamic coefficients
+            %           [CA, CY, CN, Cl, Cm, Cn, Clp, Cmad, Cmq, Cnr, Cnp]
+            %
+            %   NOTE: Static coefficients are interpolated and adjusted for
+            %   the specified xcg. Dynamic derivatives are included only if
+            %   loadDynamic is true.
+            
+            coefficients = zeros(11, 1);
+            coefficients(1:6) = obj.staticInterpolant(alpha, mach, beta, altitude, airbakes);
+            % Transporting static coefficients to new xcg
+
+            % C_B = C_A + d / c * [0; -CN; CY]_A <- NOTE: Non torna il meno su CN
+            d = xcg - obj.GEOMETRY.xcg(1);
+            l = obj.GEOMETRY.diameter;
+
+            CY = coefficients(2); CN = coefficients(3);
+            forceCoeffs = [0; CN; CY];
+            
+            coefficients(4:6) = coefficients(4:6) + d/l * forceCoeffs;
+
+            if ~obj.loadDynamic, return; end
+            coefficients(7:11) = obj.dynamicInterpolant(alpha, mach, beta, altitude, airbakes, xcg);
+        end
+    end
+
+    methods % Getters
+        function value = get.static(obj)
+            value = obj.STATIC;
+        end
+
+        function value = get.dynamic(obj)
+            value = obj.DYNAMIC;
+        end
+
+        function value = get.geometry(obj)
+            value = obj.GEOMETRY;
+        end
+
+        function value = get.state(obj)
+            value = obj.STATE;
+        end
+    end
+
+    methods % Setters
+        function obj = set.static(obj, value)
+            obj.STATIC = value(:, :, :, :, :, :, 1);
+            obj = obj.updateInterpolants();
+        end
+
+        function obj = set.dynamic(obj, value)
+            obj.DYNAMIC = value(:, :, :, :, :, :, :);
+            obj = obj.updateInterpolants();
+        end
+
+        function obj = set.geometry(obj, value)
+            obj.GEOMETRY = value;
+            obj = obj.updateInterpolants();
+        end
+
+        function obj = set.state(obj, value)
+            obj.STATE = value;
+            obj = obj.updateInterpolants();
+        end
     end
 
     methods(Access = private)
@@ -89,6 +192,44 @@ classdef Coefficient
                 obj.dynamic = dataCoeffs.total([7, 10, 11, 14, 15], :, :, :, :, :, :);
             end
         end
+
+        function ready = checkProperties(obj)
+            % Check if STATIC, DYNAMIC, GEOMETRY, and STATE are non-empty
+            ready = ~isempty(obj.STATIC) && ...
+                    ~isempty(obj.GEOMETRY) && ~isempty(obj.STATE) && ...
+                    ~(isempty(obj.DYNAMIC) && obj.loadDynamic);
+        end
+
+        function obj = updateInterpolants(obj)
+            % Update static and dynamic interpolants
+            if ~obj.checkProperties(), return; end
+        
+            %% Retrieve flight conditions
+            alpha = obj.state.alphas*pi/180;                    % Converting to rad
+            mach = obj.state.machs;
+            beta = obj.state.betas*pi/180;                      % Converting to rad
+            altitude = obj.state.altitudes;
+            airbakes = obj.state.hprot/obj.state.hprot(end);    % Normalizing on height
+
+            %% Create interpolants
+            staticCoeffs = permute(obj.static, [2, 3, 4, 5, 6, 1]);
+
+            gridVecs = {alpha, mach, beta, altitude, airbakes};
+            obj.staticInterpolant = griddedInterpolant(gridVecs, staticCoeffs, 'linear', 'nearest');
+
+            if ~obj.loadDynamic, return; end
+
+            xcg = obj.geometry.xcg;
+            dynamicCoeffs = permute(obj.dynamic, [2, 3, 4, 5, 6, 7, 1]);
+
+            if xcg(2) < xcg(1) % Flip xcg in case of descending order
+                xcg = flip(xcg);
+                dynamicCoeffs = flip(dynamicCoeffs, 7);
+            end
+
+            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
+            obj.dynamicInterpolant = griddedInterpolant(gridVecs, dynamicCoeffs, 'linear', 'nearest');
+        end
     end
 end
 
-- 
GitLab


From 65d160b64ad75c8b5d38e20a741e38f7af3b4af1 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sat, 19 Apr 2025 20:28:32 +0200
Subject: [PATCH 03/12] [coeff-transport] Updated comment

---
 classes/misc/Coefficient.m | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index a896b41..f48b71d 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -1,5 +1,5 @@
 classdef Coefficient
-    % Motor: Manages the coefficients associated with a rocket
+    % Coefficient: Manages the coefficients associated with a rocket
     %
     %   Constructor:
     %       - Coefficient: Creates an instance of the Coefficient class.
@@ -56,10 +56,10 @@ classdef Coefficient
     end
 
     properties(Access = private)
-        STATIC
-        DYNAMIC
-        GEOMETRY                    % Cached variable for geometry
-        STATE                       % Cached variable for state
+        STATIC                              % Cached variable for static coefficients
+        DYNAMIC                             % Cached variable for dynamic derivatives
+        GEOMETRY                            % Cached variable for geometry
+        STATE                               % Cached variable for state
     end
 
     properties(Access = private)
-- 
GitLab


From 59db294dfd27fc2869f2050e524b4539664cef8e Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 10:45:11 +0200
Subject: [PATCH 04/12] [coeff-transport][Rocket] Added option to load dynamic
 derivatives

---
 classes/@Rocket/Rocket.m                      | 30 +++++++++++++++----
 .../config/rocketConfig.m                     |  5 ++++
 .../config/rocketConfig.m                     |  5 ++++
 .../config/rocketConfig.m                     |  5 ++++
 .../config/rocketConfig.m                     |  5 ++++
 .../config/rocketConfig.m                     |  6 ++++
 .../config/rocketConfig.m                     |  5 ++++
 .../config/rocketConfig.m                     |  5 ++++
 .../config/rocketConfig.m                     |  5 ++++
 .../config/rocketConfig.m                     |  9 ++++--
 .../config/rocketConfig.m                     |  5 ++++
 11 files changed, 78 insertions(+), 7 deletions(-)

diff --git a/classes/@Rocket/Rocket.m b/classes/@Rocket/Rocket.m
index a06283b..7e39e87 100644
--- a/classes/@Rocket/Rocket.m
+++ b/classes/@Rocket/Rocket.m
@@ -50,9 +50,11 @@ classdef Rocket < Config
     properties        
         pitot                       Pitot           % [-]       Pitot component
         parachutes                  cell            % [-]       (nParachutes, nStages) Parachutes onboard
+
+        dynamicDerivatives  (1, 1)  logical         % [-]       True if dynamic derivatives will be loaded
         
-        coefficients                struct          % [-]       Aerodynamic coefficients
-        coefficientsHighAOA         struct          % [-]       Aerodynamic coefficients at high angle of attack
+        coefficients                Coefficient     % [-]       Aerodynamic coefficients
+        coefficientsHighAOA         Coefficient     % [-]       Aerodynamic coefficients at high angle of attack
     end
 
     properties(Access = private)
@@ -138,9 +140,20 @@ classdef Rocket < Config
             obj.parachutes   = paraLoader.parachutes;
             
             if options.loadCoefficients
-                obj = obj.loadData();
+                if obj.dynamicDerivatives, coeffName = obj.motor.name;
+                else, coeffName = 'generic'; 
+                end
+
+                obj.coefficients = Coefficient( ...
+                    fullfile(mission.dataPath, 'aeroCoefficients.mat'), ...
+                    coeffName);
+
+                obj.coefficientsHighAOA = Coefficient( ...
+                    fullfile(mission.dataPath, 'aeroCoefficientsHighAOA.mat'), ...
+                    coeffName);
+
                 answer = '';
-                if isempty(obj.coefficients) || isempty(obj.coefficientsHighAOA)
+                if isempty(obj.coefficients.static) || isempty(obj.coefficientsHighAOA.static)
                     answer = questdlg(['Coefficient matrices not found. ' ...
                         'Do you want to create new matrices?']);
                 elseif options.checkGeometry && ~all(obj.checkGeometry())
@@ -153,7 +166,14 @@ classdef Rocket < Config
                         parserPath = fullfile(mission.msaPath, 'autoMatricesProtub');
                         addpath(genpath(parserPath));
                         mainAutoMatProtub(obj);
-                        obj = obj.loadData();
+
+                        obj.coefficients = Coefficient( ...
+                            fullfile(mission.dataPath, 'aeroCoefficients.mat'), ...
+                            coeffName);
+
+                        obj.coefficientsHighAOA = Coefficient( ...
+                            fullfile(mission.dataPath, 'aeroCoefficientsHighAOA.mat'), ...
+                            coeffName);
                     case 'Cancel'
                         error('Rocket creation aborted')
                     otherwise
diff --git a/missions/2021_Lynx_Portugal_October/config/rocketConfig.m b/missions/2021_Lynx_Portugal_October/config/rocketConfig.m
index 4df34a1..160c5b0 100644
--- a/missions/2021_Lynx_Portugal_October/config/rocketConfig.m
+++ b/missions/2021_Lynx_Portugal_October/config/rocketConfig.m
@@ -16,6 +16,11 @@ rocket.inertiaNoMotor = [0.0786; 12.972; 12.972];       % [kg*m^2] OVERRIDE iner
 rocket.xCgNoMotor = 1.6025;                                             % [m]      OVERRIDE xCg without motor
 rocket.lengthCenterNoMot = 1.7640;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2021_Lynx_Roccaraso_September/config/rocketConfig.m b/missions/2021_Lynx_Roccaraso_September/config/rocketConfig.m
index 83f6476..d0dc3df 100644
--- a/missions/2021_Lynx_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2021_Lynx_Roccaraso_September/config/rocketConfig.m
@@ -16,6 +16,11 @@ rocket.inertiaNoMotor = [0.06; 7.511; 7.512];       % [kg*m^2] OVERRIDE inertia
 rocket.xCgNoMotor = 1.1986;                                             % [m]      OVERRIDE xCg without motor
 rocket.lengthCenterNoMot = 1.7840;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2022_Pyxis_Portugal_October/config/rocketConfig.m b/missions/2022_Pyxis_Portugal_October/config/rocketConfig.m
index 8cdcf2b..2f949bf 100644
--- a/missions/2022_Pyxis_Portugal_October/config/rocketConfig.m
+++ b/missions/2022_Pyxis_Portugal_October/config/rocketConfig.m
@@ -16,6 +16,11 @@ rocket.inertiaNoMotor = [0.06303; 9.62497; 9.62524];       % [kg*m^2] OVERRIDE i
 rocket.xCgNoMotor = 1.2239;                                             % [m]      OVERRIDE xCg without motor
 rocket.lengthCenterNoMot = 1.4470;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2022_Pyxis_Roccaraso_September/config/rocketConfig.m b/missions/2022_Pyxis_Roccaraso_September/config/rocketConfig.m
index bec3d20..ee3a3c7 100644
--- a/missions/2022_Pyxis_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2022_Pyxis_Roccaraso_September/config/rocketConfig.m
@@ -18,6 +18,11 @@ rocket.xCgNoMotor = 1.2957;                                             % [m]
 % settings.Lcenter -  motors(iMotor).L/1000
 rocket.lengthCenterNoMot = 1.61;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2023_Gemini_Portugal_October/config/rocketConfig.m b/missions/2023_Gemini_Portugal_October/config/rocketConfig.m
index 610966a..401c415 100644
--- a/missions/2023_Gemini_Portugal_October/config/rocketConfig.m
+++ b/missions/2023_Gemini_Portugal_October/config/rocketConfig.m
@@ -15,6 +15,12 @@ rocket.inertiaNoMotor = [0.06535397; 17.21019828; 17.21056483];       % [kg*m^2]
 rocket.xCgNoMotor = 1.254;                                             % [m]      OVERRIDE xCg without motor
 rocket.lengthCenterNoMot = 1.517;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2023_Gemini_Roccaraso_September/config/rocketConfig.m b/missions/2023_Gemini_Roccaraso_September/config/rocketConfig.m
index 600065a..fc2715b 100644
--- a/missions/2023_Gemini_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2023_Gemini_Roccaraso_September/config/rocketConfig.m
@@ -15,6 +15,11 @@ rocket.inertiaNoMotor = [0.06535397; 12.07664659; 12.07701314];       % [kg*m^2]
 rocket.xCgNoMotor = 1.149;                                             % [m]      OVERRIDE xCg without motor
 rocket.lengthCenterNoMot = 1.517;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2024_Lyra_Portugal_October/config/rocketConfig.m b/missions/2024_Lyra_Portugal_October/config/rocketConfig.m
index 2369c08..f87640c 100644
--- a/missions/2024_Lyra_Portugal_October/config/rocketConfig.m
+++ b/missions/2024_Lyra_Portugal_October/config/rocketConfig.m
@@ -9,6 +9,11 @@ rocket.inertiaNoMotor = [];                                             % [kg*m^
 rocket.xCgNoMotor = [];                                                 % [m]      OVERRIDE xCg without motor
 rocket.lengthCenterNoMot = [];                                          % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2024_Lyra_Roccaraso_September/config/rocketConfig.m b/missions/2024_Lyra_Roccaraso_September/config/rocketConfig.m
index b2ca5cb..2939db0 100644
--- a/missions/2024_Lyra_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2024_Lyra_Roccaraso_September/config/rocketConfig.m
@@ -15,6 +15,11 @@ rocket.lengthCenterNoMot = [];                                          % [m]
 % rocket.xCgNoMotor = 1.28;                                             % [m]      OVERRIDE xCg without motor
 % rocket.lengthCenterNoMot = 1.778;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
diff --git a/missions/2025_Orion_Portugal_October/config/rocketConfig.m b/missions/2025_Orion_Portugal_October/config/rocketConfig.m
index c879160..6a13ec2 100644
--- a/missions/2025_Orion_Portugal_October/config/rocketConfig.m
+++ b/missions/2025_Orion_Portugal_October/config/rocketConfig.m
@@ -3,12 +3,17 @@
 %% ROCKET - OVERRIDES BAYS CONFIG
 rocket = Rocket();
 
-rocket.diameter = 0.15;                                                 % [m]      Rocket diameter
+rocket.diameter = 0.15;                                                   % [m]      Rocket diameter
 % rocket.massNoMotor = [];                                                % [kg]     OVERRIDE mass without motor
 % rocket.inertiaNoMotor = [];                                             % [kg*m^2] OVERRIDE inertia without motor - body axes reference
 % rocket.xcgNoMotor = [];                                                 % [m]      OVERRIDE xCg without motor
 % rocket.lengthCenterNoMot = [];                                          % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PRF - Includes Parafoil + Nose
 parafoil = Parafoil();
 
@@ -69,7 +74,7 @@ airbrakes.servoTau = 0.0374588;
 %% MOTOR
 motor = Motor();
 
-motor.name = 'HRE_ARM_OPT_3_Update';
+motor.name = 'HRE_ARM_EuRoC_2024';
 
 motor.cutoffTime = [];                                               % [s] OVERRIDE Cutoff time
 motor.ignitionTransient = 0.3;                                       % [s] Ignition transient
diff --git a/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m b/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m
index dfc372f..c4e1ffc 100644
--- a/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m
@@ -15,6 +15,11 @@ rocket.lengthCenterNoMot = [];                                          % [m]
 % rocket.xCgNoMotor = 1.28;                                             % [m]      OVERRIDE xCg without motor
 % rocket.lengthCenterNoMot = 1.778;                                     % [m]      OVERRIDE Center length - no nose, no motor
 
+% If dynamic derivatives are loaded, coefficient will depend on rocket xcg
+%   When false, coefficients are saved with current motor's name
+%   When true,  coefficients are saved as 'generic'
+rocket.dynamicDerivatives = false;                                    % [-]      True if dynamic derivatives will be loaded
+
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
 
-- 
GitLab


From 1d8b632e76a3c93cca2c7595d6ccce97930e548c Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 10:46:01 +0200
Subject: [PATCH 05/12] [coeff-transpot][Coefficient] Handles singleton
 dimension cases and improved performance

---
 classes/misc/Coefficient.m | 108 +++++++++++++++++++++++++------------
 1 file changed, 75 insertions(+), 33 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index f48b71d..a230fbf 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -55,6 +55,11 @@ classdef Coefficient
         finsCN                      double  % Fins-only CN
     end
 
+    properties(SetAccess = private)
+        isReady             (1, 1)  logical % Whether all coefficients are loaded and of the right size
+        isDynamic           (1, 1)  logical % Whether to load dynamic derivatives. Adds XCG dependece
+    end
+
     properties(Access = private)
         STATIC                              % Cached variable for static coefficients
         DYNAMIC                             % Cached variable for dynamic derivatives
@@ -63,8 +68,6 @@ classdef Coefficient
     end
 
     properties(Access = private)
-        loadDynamic         (1, 1)  logical % Whether to load dynamic derivatives. Adds XCG dependece
-        
         staticInterpolant   (1, 1)  griddedInterpolant
         dynamicInterpolant  (1, 1)  griddedInterpolant
     end
@@ -77,6 +80,8 @@ classdef Coefficient
             end
             
             obj = obj.loadData(filePath, name);
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
 
         function coefficients = get(obj, alpha, mach, beta, altitude, airbakes, xcg)
@@ -102,6 +107,8 @@ classdef Coefficient
             %   the specified xcg. Dynamic derivatives are included only if
             %   loadDynamic is true.
             
+            if ~obj.isReady, error('Canno interpolate coefficients: check that all dimensions match'); end
+
             coefficients = zeros(11, 1);
             coefficients(1:6) = obj.staticInterpolant(alpha, mach, beta, altitude, airbakes);
             % Transporting static coefficients to new xcg
@@ -115,7 +122,7 @@ classdef Coefficient
             
             coefficients(4:6) = coefficients(4:6) + d/l * forceCoeffs;
 
-            if ~obj.loadDynamic, return; end
+            if ~obj.isDynamic, return; end
             coefficients(7:11) = obj.dynamicInterpolant(alpha, mach, beta, altitude, airbakes, xcg);
         end
     end
@@ -140,23 +147,27 @@ classdef Coefficient
 
     methods % Setters
         function obj = set.static(obj, value)
-            obj.STATIC = value(:, :, :, :, :, :, 1);
-            obj = obj.updateInterpolants();
+            obj.STATIC  = value(:, :, :, :, :, :, 1);
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
 
         function obj = set.dynamic(obj, value)
             obj.DYNAMIC = value(:, :, :, :, :, :, :);
-            obj = obj.updateInterpolants();
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
 
         function obj = set.geometry(obj, value)
             obj.GEOMETRY = value;
-            obj = obj.updateInterpolants();
+            obj.isReady  = obj.checkProperties();
+            obj          = obj.updateInterpolants();
         end
 
         function obj = set.state(obj, value)
-            obj.STATE = value;
-            obj = obj.updateInterpolants();
+            obj.STATE   = value;
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
     end
 
@@ -179,17 +190,17 @@ classdef Coefficient
             if ~isfield(dataCoeffs, name), return; end
             dataCoeffs = dataCoeffs.(name);
 
-            obj.loadDynamic = ~strcmp(name, 'generic');
+            obj.isDynamic = ~strcmp(name, 'generic');
             obj.finsCN = dataCoeffs.finsCN;
-            obj.geometry = dataCoeffs.geometry;
-            obj.state = dataCoeffs.state;
+            obj.GEOMETRY = dataCoeffs.geometry;
+            obj.STATE = dataCoeffs.state;
             
             % Load CA, CY, CN, Cl, Cm, Cn
-            obj.static = dataCoeffs.total([1, 3, 5, 6, 9, 13], :, :, :, :, :, :);
+            obj.STATIC = dataCoeffs.total([1, 3, 5, 6, 9, 13], :, :, :, :, :, :);
 
-            if obj.loadDynamic
+            if obj.isDynamic
                 % Load Clp, Cmad, Cmq, Cnr, Cnp
-                obj.dynamic = dataCoeffs.total([7, 10, 11, 14, 15], :, :, :, :, :, :);
+                obj.DYNAMIC = dataCoeffs.total([7, 10, 11, 14, 15], :, :, :, :, :, :);
             end
         end
 
@@ -197,39 +208,70 @@ classdef Coefficient
             % Check if STATIC, DYNAMIC, GEOMETRY, and STATE are non-empty
             ready = ~isempty(obj.STATIC) && ...
                     ~isempty(obj.GEOMETRY) && ~isempty(obj.STATE) && ...
-                    ~(isempty(obj.DYNAMIC) && obj.loadDynamic);
+                    ~(isempty(obj.DYNAMIC) && obj.isDynamic);
+
+            if ~ready, return; end
+
+            alpha = obj.STATE.alphas;
+            mach = obj.STATE.machs;
+            beta = obj.STATE.betas;
+            altitude = obj.STATE.altitudes;
+            airbakes = obj.STATE.hprot;
+            xcg = obj.GEOMETRY.xcg;
+ 
+            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
+            dims = cellfun(@(x) length(x), gridVecs);
+            dims(dims == 0) = 1;                                            % Empty case is the same as scalar case
+
+            staticDimsReady = all(size(obj.STATIC, 2:6) == dims(1:end-1));
+            dynamicDimsReady = true;
+
+            if obj.isDynamic
+                dynamicDimsReady = all(size(obj.DYNAMIC, 2:7) == dims);
+            end
+            
+            ready = ready && staticDimsReady && dynamicDimsReady;
         end
 
         function obj = updateInterpolants(obj)
             % Update static and dynamic interpolants
-            if ~obj.checkProperties(), return; end
+            if ~obj.isReady, return; end
         
             %% Retrieve flight conditions
-            alpha = obj.state.alphas*pi/180;                    % Converting to rad
-            mach = obj.state.machs;
-            beta = obj.state.betas*pi/180;                      % Converting to rad
-            altitude = obj.state.altitudes;
-            airbakes = obj.state.hprot/obj.state.hprot(end);    % Normalizing on height
+            alpha = obj.STATE.alphas*pi/180;                                % Converting to rad
+            mach = obj.STATE.machs;
+            beta = obj.STATE.betas*pi/180;                                  % Converting to rad
+            altitude = obj.STATE.altitudes;
+            xcg = obj.GEOMETRY.xcg;
+
+            if isempty(obj.STATE.hprot), airbakes = 0;
+                else, airbakes = obj.STATE.hprot/obj.STATE.hprot(end);      % Normalizing on height
+            end
+
+            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
+
+            % Find singleton dims (last dimension is coefficients and will not be of length 1)
+            singletonDims = cellfun(@(x) isscalar(x) || isempty(x), gridVecs);
+            gridVecs(singletonDims) = deal({[0, 1]});
 
             %% Create interpolants
-            staticCoeffs = permute(obj.static, [2, 3, 4, 5, 6, 1]);
+            staticCoeffs = permute(obj.STATIC, [2, 3, 4, 5, 6, 1]);
+            staticCoeffs = repmat(staticCoeffs, singletonDims(1:end-1) + 1); % Exclude xcg for static coefficients
 
-            gridVecs = {alpha, mach, beta, altitude, airbakes};
-            obj.staticInterpolant = griddedInterpolant(gridVecs, staticCoeffs, 'linear', 'nearest');
+            obj.staticInterpolant = griddedInterpolant(gridVecs(1:end-1), staticCoeffs, 'linear', 'nearest');
 
-            if ~obj.loadDynamic, return; end
+            if ~obj.isDynamic, return; end
 
-            xcg = obj.geometry.xcg;
-            dynamicCoeffs = permute(obj.dynamic, [2, 3, 4, 5, 6, 7, 1]);
+            %% Handle dynamic derivatives
+            dynamicCoeffs = permute(obj.DYNAMIC, [2, 3, 4, 5, 6, 7, 1]);
 
-            if xcg(2) < xcg(1) % Flip xcg in case of descending order
-                xcg = flip(xcg);
+            % Flip xcg in case of descending order
+            if ~isscalar(xcg) && xcg(2) < xcg(1)
+                gridVecs{end} = flip(xcg);
                 dynamicCoeffs = flip(dynamicCoeffs, 7);
             end
 
-            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
             obj.dynamicInterpolant = griddedInterpolant(gridVecs, dynamicCoeffs, 'linear', 'nearest');
         end
     end
-end
-
+end
\ No newline at end of file
-- 
GitLab


From b426f4da8e8bf3ac0811f6ef3cc30f73f0bb734d Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 12:16:17 +0200
Subject: [PATCH 06/12] [coeff-transport] Added summary for different
 coefficients

---
 classes/@Rocket/Rocket.m        |  14 +++-
 classes/@Rocket/checkGeometry.m | 136 ++++++++++++++++++--------------
 2 files changed, 85 insertions(+), 65 deletions(-)

diff --git a/classes/@Rocket/Rocket.m b/classes/@Rocket/Rocket.m
index 7e39e87..b904ff4 100644
--- a/classes/@Rocket/Rocket.m
+++ b/classes/@Rocket/Rocket.m
@@ -153,12 +153,18 @@ classdef Rocket < Config
                     coeffName);
 
                 answer = '';
+                
                 if isempty(obj.coefficients.static) || isempty(obj.coefficientsHighAOA.static)
                     answer = questdlg(['Coefficient matrices not found. ' ...
                         'Do you want to create new matrices?']);
-                elseif options.checkGeometry && ~all(obj.checkGeometry())
-                    answer = questdlg(['Coefficient matrices differ from rocket geometry. ' ...
-                        'Do you want to create new matrices?']);
+                elseif options.checkGeometry
+                    [check, summary] = obj.checkGeometry();
+
+                    if ~all(check)
+                        disp(summary);
+                        answer = questdlg(['Coefficient matrices differ from rocket geometry. ' ...
+                            'Do you want to create new matrices?']);
+                    end
                 end
                 
                 switch answer
@@ -184,7 +190,7 @@ classdef Rocket < Config
 
     methods
         [coeffsValues, angle0] = interpCoeffs(obj, t, alpha, mach, beta, alt, c)
-        checks = checkGeometry(obj)
+        [checks, summary] = checkGeometry(obj)
         img = plot(obj)
     end
 
diff --git a/classes/@Rocket/checkGeometry.m b/classes/@Rocket/checkGeometry.m
index e61422f..6608a6d 100644
--- a/classes/@Rocket/checkGeometry.m
+++ b/classes/@Rocket/checkGeometry.m
@@ -1,11 +1,16 @@
-function checks = checkGeometry(obj)
+function [checks, summary] = checkGeometry(obj)
     % checkGeometry     - This methods checks if the rocket geometry
     %                     is consistent with the geometry of the
     %                     aerodynamic matrices
     %
     % OUTPUTS:
     %                   - checks (n fields of geometry, 1): boolean value of the geometry checks
-    
+
+    names = ["Initial xcg"; "Final xcg"; "Ogive type"; "Diameter"; "Nose Length"; 
+        "Nose p Mod"; "Nose c Mod"; "Center Length"; "Fins root chord"; 
+        "Fins free chord"; "Fins height"; "Fins XLE"; "Fins number"; 
+        "Boat Length"; "Boat final diameter"; "Boat type"];
+
     xCgRocket = round([ ...
         obj.xcg(1); ...
         obj.xcg(end) ...
@@ -15,69 +20,78 @@ function checks = checkGeometry(obj)
         obj.coefficients.geometry.xcg(1);
         obj.coefficients.geometry.xcg(end);
         ], 3);
+
+    noseTypeCheck = strcmp(obj.coefficients.geometry.ogType, obj.parafoil.noseType);
+
+    geometryRocket = round([
+        obj.diameter;
+        obj.parafoil.noseLength;
+        0; 0; % Placeholders for nose p and c mod
+        obj.lengthCenter;
+        obj.rear.finsRootChord;
+        obj.rear.finsFreeChord;
+        obj.rear.finsHeight;
+        obj.rear.finsDeltaXFreeChord;
+        obj.rear.nPanel;
+        obj.rear.boatLength;
+        obj.rear.boatFinalDiameter;
+        ], 3);
     
-    if (obj.parafoil.noseCMod & obj.parafoil.nosePMod) == 0 % KARMAN ogive case, no modified p and c coefficients
-        geometryRocket = round([
-            obj.diameter;
-            obj.parafoil.noseLength;
-            obj.lengthCenter;
-            obj.rear.finsRootChord;
-            obj.rear.finsFreeChord;
-            obj.rear.finsHeight;
-            obj.rear.finsDeltaXFreeChord;
-            obj.rear.nPanel;
-            obj.rear.boatLength;
-            obj.rear.boatFinalDiameter;
-            ], 3);
-        
-        geometryTest = round([
-            obj.coefficients.geometry.diameter;
-            obj.coefficients.geometry.lNose;
-            obj.coefficients.geometry.lCenter;
-            obj.coefficients.geometry.chord1;
-            obj.coefficients.geometry.chord2;
-            obj.coefficients.geometry.height;
-            obj.coefficients.geometry.deltaXLE;
-            obj.coefficients.geometry.nPanel;
-            obj.coefficients.geometry.boatL;
-            obj.coefficients.geometry.boatD;
-            ], 3);
-    else % MHAAK ogive case, modified p and c coefficients
-        geometryRocket = round([
-            obj.diameter;
-            obj.parafoil.noseLength;
-            obj.lengthCenter;
+    geometryTest = round([
+        obj.coefficients.geometry.diameter;
+        obj.coefficients.geometry.lNose;
+        0; 0; % Placeholders for nose p and c mod
+        obj.coefficients.geometry.lCenter;
+        obj.coefficients.geometry.chord1;
+        obj.coefficients.geometry.chord2;
+        obj.coefficients.geometry.height;
+        obj.coefficients.geometry.deltaXLE;
+        obj.coefficients.geometry.nPanel;
+        obj.coefficients.geometry.boatL;
+        obj.coefficients.geometry.boatD;
+        ], 3);
+
+    if (obj.parafoil.noseCMod && obj.parafoil.nosePMod)
+        geometryRocket([3, 4]) = round([            ...
             obj.parafoil.noseCMod;
-            obj.parafoil.nosePMod;
-            obj.rear.finsRootChord;
-            obj.rear.finsFreeChord;
-            obj.rear.finsHeight;
-            obj.rear.finsDeltaXFreeChord;
-            obj.rear.nPanel;
-            obj.rear.boatLength;
-            obj.rear.boatFinalDiameter;
-            ], 3);
-        
-        geometryTest = round([
-            obj.coefficients.geometry.diameter;
-            obj.coefficients.geometry.lNose;
-            obj.coefficients.geometry.lCenter;
-            obj.coefficients.geometry.cMod;
-            obj.coefficients.geometry.pMod;
-            obj.coefficients.geometry.chord1;
-            obj.coefficients.geometry.chord2;
-            obj.coefficients.geometry.height;
-            obj.coefficients.geometry.deltaXLE;
-            obj.coefficients.geometry.nPanel;
-            obj.coefficients.geometry.boatL;
-            obj.coefficients.geometry.boatD;
-            ], 3);
+            obj.parafoil.nosePMod;], 4);
+
+        geometryTest([3, 4]) = round([            ...
+            obj.parafoil.noseCMod;
+            obj.parafoil.nosePMod;], 4);
     end
-    
+
+    boatTypeCheck = strcmp(obj.coefficients.geometry.boatType, obj.rear.boatType);
+
     checks = [
-        xCgRocket == xCgTest;
-        strcmp(obj.coefficients.geometry.ogType, obj.parafoil.noseType);
+        ~obj.dynamicDerivatives | (xCgRocket == xCgTest);
+        noseTypeCheck;
         geometryRocket == geometryTest;
-        strcmp(obj.coefficients.geometry.boatType, obj.rear.boatType)
+        boatTypeCheck;
         ];
+
+    if nargout == 2
+        % Summarize data and leave placeholders 
+        ogiveType = ["CONE", "OGIVE", "POWER", "HAACK", "KARMAN", "MHAACK"];
+        boatType  = ["CONE", "OGIVE"];
+
+        % Convert text to DATCOM standard. !TODO: Si farebbe meglio con un
+        % enum
+        boatRocket = find(strcmp(obj.rear.boatType, boatType), 1, 'first') - 1;
+        boatTest = find(strcmp(obj.coefficients.geometry.boatType, boatType), 1, 'first') - 1;
+
+        ogRocket = find(strcmp(obj.parafoil.noseType, ogiveType), 1, 'first') - 1;
+        ogTest = find(strcmp(obj.coefficients.geometry.ogType, ogiveType), 1, 'first') - 1;
+
+        rocketData = [xCgRocket; ogRocket; geometryRocket; boatRocket];
+        testData = [xCgTest; ogTest; geometryTest; boatTest];
+        checkNames = names(~checks);
+
+        if isempty(checkNames)
+            summary = table;
+        else
+            summary = table(rocketData(~checks), testData(~checks), ...
+                'RowNames', checkNames, 'VariableNames', ["Rocket", "Coefficients"]);
+        end
+    end
 end
\ No newline at end of file
-- 
GitLab


From eb8921893427d12d3e4761c710e6615ed7e4e639 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 15:19:39 +0200
Subject: [PATCH 07/12] [coeff-transpot] Added total coefficients

---
 classes/misc/Coefficient.m | 70 +++++++++++++++++---------------------
 1 file changed, 31 insertions(+), 39 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index a230fbf..4af9ad6 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -10,7 +10,7 @@ classdef Coefficient
     %               - name: (optional) coefficient name. Used when dynamic
     %               derivatives are also needed
     %
-    %   Coefficients are retrieved from DM in the following format:
+    %   Coefficients are retrieved from DATCOM in the following format:
     %       Coefficient matrix: double (15, nAlpha, nMach, nBeta, nAlt, nAbk, nXcg) 
     %       Where the 15 coefficients are 
     %           - CA        Axial force 
@@ -44,9 +44,7 @@ classdef Coefficient
     %   dimension, instead of the first, to improve performance
 
     properties(Dependent)
-        static                      double  % Static coefficients: [CA, CY, CN, Cl, Cm, Cn]
-        dynamic                     double  % Dynamic derivatives: [Clp, Cmad, Cmq, Cnr, Cnp]
-
+        total                       double  % Coefficients stored in DATCOM format
         geometry                    struct  % Reference geometry
         state                       struct  % Flight envelope
     end
@@ -55,14 +53,19 @@ classdef Coefficient
         finsCN                      double  % Fins-only CN
     end
 
+    properties(Dependent, SetAccess = private)
+        static                      double  % Static coefficients: [CA, CY, CN, Cl, Cm, Cn]
+        dynamic                     double  % Dynamic derivatives: [Clp, Cmad, Cmq, Cnr, Cnp]
+    end
+
+
     properties(SetAccess = private)
         isReady             (1, 1)  logical % Whether all coefficients are loaded and of the right size
         isDynamic           (1, 1)  logical % Whether to load dynamic derivatives. Adds XCG dependece
     end
 
     properties(Access = private)
-        STATIC                              % Cached variable for static coefficients
-        DYNAMIC                             % Cached variable for dynamic derivatives
+        TOTAL                               % Cached variable for total
         GEOMETRY                            % Cached variable for geometry
         STATE                               % Cached variable for state
     end
@@ -75,9 +78,11 @@ classdef Coefficient
     methods
         function obj = Coefficient(filePath, name)
             arguments
-                filePath    (1, :)  char
+                filePath    (1, :)  char = ''
                 name        (1, :)  char = 'generic'
             end
+
+            if isempty(filePath), return; end
             
             obj = obj.loadData(filePath, name);
             obj.isReady = obj.checkProperties();
@@ -128,12 +133,17 @@ classdef Coefficient
     end
 
     methods % Getters
+        function value = get.total(obj)
+            value = obj.TOTAL;
+        end
+
         function value = get.static(obj)
-            value = obj.STATIC;
+            value = obj.TOTAL([1, 3, 5, 6, 9, 13], :, :, :, :, :, 1);
         end
 
         function value = get.dynamic(obj)
-            value = obj.DYNAMIC;
+            if ~obj.isDynamic, value = []; return; end
+            value = obj.TOTAL([7, 10, 11, 14, 15], :, :, :, :, :, :);
         end
 
         function value = get.geometry(obj)
@@ -146,16 +156,11 @@ classdef Coefficient
     end
 
     methods % Setters
-        function obj = set.static(obj, value)
-            obj.STATIC  = value(:, :, :, :, :, :, 1);
-            obj.isReady = obj.checkProperties();
-            obj         = obj.updateInterpolants();
-        end
-
-        function obj = set.dynamic(obj, value)
-            obj.DYNAMIC = value(:, :, :, :, :, :, :);
-            obj.isReady = obj.checkProperties();
-            obj         = obj.updateInterpolants();
+        function obj = set.total(obj, value)
+            obj.TOTAL = value;
+            obj.isDynamic   = size(value, 7) ~= 1;
+            obj.isReady     = obj.checkProperties();
+            obj             = obj.updateInterpolants();
         end
 
         function obj = set.geometry(obj, value)
@@ -190,25 +195,19 @@ classdef Coefficient
             if ~isfield(dataCoeffs, name), return; end
             dataCoeffs = dataCoeffs.(name);
 
-            obj.isDynamic = ~strcmp(name, 'generic');
             obj.finsCN = dataCoeffs.finsCN;
             obj.GEOMETRY = dataCoeffs.geometry;
             obj.STATE = dataCoeffs.state;
             
-            % Load CA, CY, CN, Cl, Cm, Cn
-            obj.STATIC = dataCoeffs.total([1, 3, 5, 6, 9, 13], :, :, :, :, :, :);
-
-            if obj.isDynamic
-                % Load Clp, Cmad, Cmq, Cnr, Cnp
-                obj.DYNAMIC = dataCoeffs.total([7, 10, 11, 14, 15], :, :, :, :, :, :);
-            end
+            % Load coefficients
+            obj.total = dataCoeffs.total;
         end
 
         function ready = checkProperties(obj)
             % Check if STATIC, DYNAMIC, GEOMETRY, and STATE are non-empty
-            ready = ~isempty(obj.STATIC) && ...
+            ready = ~isempty(obj.static) && ...
                     ~isempty(obj.GEOMETRY) && ~isempty(obj.STATE) && ...
-                    ~(isempty(obj.DYNAMIC) && obj.isDynamic);
+                    ~(isempty(obj.dynamic) && obj.isDynamic);
 
             if ~ready, return; end
 
@@ -223,14 +222,7 @@ classdef Coefficient
             dims = cellfun(@(x) length(x), gridVecs);
             dims(dims == 0) = 1;                                            % Empty case is the same as scalar case
 
-            staticDimsReady = all(size(obj.STATIC, 2:6) == dims(1:end-1));
-            dynamicDimsReady = true;
-
-            if obj.isDynamic
-                dynamicDimsReady = all(size(obj.DYNAMIC, 2:7) == dims);
-            end
-            
-            ready = ready && staticDimsReady && dynamicDimsReady;
+            ready = all(size(obj.total, 2:7) == dims);
         end
 
         function obj = updateInterpolants(obj)
@@ -255,7 +247,7 @@ classdef Coefficient
             gridVecs(singletonDims) = deal({[0, 1]});
 
             %% Create interpolants
-            staticCoeffs = permute(obj.STATIC, [2, 3, 4, 5, 6, 1]);
+            staticCoeffs = permute(obj.static, [2, 3, 4, 5, 6, 1]);
             staticCoeffs = repmat(staticCoeffs, singletonDims(1:end-1) + 1); % Exclude xcg for static coefficients
 
             obj.staticInterpolant = griddedInterpolant(gridVecs(1:end-1), staticCoeffs, 'linear', 'nearest');
@@ -263,7 +255,7 @@ classdef Coefficient
             if ~obj.isDynamic, return; end
 
             %% Handle dynamic derivatives
-            dynamicCoeffs = permute(obj.DYNAMIC, [2, 3, 4, 5, 6, 7, 1]);
+            dynamicCoeffs = permute(obj.dynamic, [2, 3, 4, 5, 6, 7, 1]);
 
             % Flip xcg in case of descending order
             if ~isscalar(xcg) && xcg(2) < xcg(1)
-- 
GitLab


From 089ef921e07c9330a856353678779d19c3023a81 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 16:04:06 +0200
Subject: [PATCH 08/12] [coeff-transport][ballistic] Implemented new get method
 for coefficients and removed XCPlon and XCPlat

---
 functions/odeFunctions/ballistic.m | 66 +++++-------------------------
 1 file changed, 11 insertions(+), 55 deletions(-)

diff --git a/functions/odeFunctions/ballistic.m b/functions/odeFunctions/ballistic.m
index 0e16abd..d08b306 100644
--- a/functions/odeFunctions/ballistic.m
+++ b/functions/odeFunctions/ballistic.m
@@ -96,11 +96,13 @@ if theta < tb
     m = interpLinear(rocket.motor.time, rocket.mass, theta);
     T = interpLinear(rocket.motor.time, rocket.motor.thrust, theta);
     Pe = interpLinear(rocket.motor.time, rocket.motor.pe, theta);
+    xcg = interpLinear(rocket.motor.time, rocket.xcg, theta);
     T = T + rocket.motor.ae*(Pe - P);
 
     I = interpLinear(rocket.motor.time, rocket.inertia, theta);
     Idot = interpLinear(rocket.motor.time, rocket.inertiaDot, theta);
 else                                                                        % for theta >= tb the flight condition is the empty one (no interpolation needed)
+    xcg = rocket.xcg(end);
     m = rocket.cutoffMass;
     T = 0;
 
@@ -143,53 +145,20 @@ betaOut = beta;
 
 %% INTERPOLATE AERODYNAMIC COEFFICIENTS:
 
-if abs(alpha)>25*pi/180 || abs(beta)>25*pi/180
-    coeffsValues = interpN( rocket.coefficientsHighAOA.total,...
-        {rocket.coefficientsHighAOA.state.alphas, rocket.coefficientsHighAOA.state.machs, ...
-        rocket.coefficientsHighAOA.state.betas, rocket.coefficientsHighAOA.state.altitudes}, ...
-        [alpha, mach, beta, absoluteAltitude]);
-    angle0 = [alpha beta];
+if abs(alpha) > rocket.coefficients.state.alphas(end)*pi/180 || ...
+        abs(beta) > rocket.coefficients.state.betas(end)*pi/180
+    coeffsValues = rocket.coefficientsHighAOA.get(alpha, mach, beta, absoluteAltitude, extension, xcg);
 else
-    %[coeffsValues, angle0] = rocket.interpCoeffs(theta, alpha, mach, beta, absoluteAltitude, opening);
-    [coeffsValues, angle0] = interpCoeffs(rocket.coefficients, theta, rocket.motor.cutoffTime, ...
-        alpha, mach, beta, absoluteAltitude, extension);
+    coeffsValues = rocket.coefficients.get(alpha, mach, beta, absoluteAltitude, extension, xcg);
 end
 
 % Retrieve Coefficients
-CA = coeffsValues(1); CYB = coeffsValues(2); CY0 = coeffsValues(3);
-CNA = coeffsValues(4); CN0 = coeffsValues(5); Cl = coeffsValues(6);
-Clp = coeffsValues(7); Cma = coeffsValues(8); Cm0 = coeffsValues(9);
-Cmad = coeffsValues(10); Cmq = coeffsValues(11); Cnb = coeffsValues(12);
-Cn0 = coeffsValues(13); Cnr = coeffsValues(14); Cnp = coeffsValues(15);
-
-% Clb = coeffsValues(16);
-% XCP_value = coeffsValues(16);
-
-% compute CN,CY,Cm,Cn (linearized with respect to alpha and beta):
-alpha0 = angle0(1); beta0 = angle0(2);
-
-CN = (CN0 + CNA*(alpha - alpha0));
-CY = (CY0 + CYB*(beta - beta0));
-Cm = (Cm0 + Cma*(alpha - alpha0));
-Cn = (Cn0 + Cnb*(beta - beta0));
-
-% XCPlon = Cm/CN;
-if abs(alpha) <= pi/180
-    XCPlon = Cma/CNA;
-else
-    XCPlon = Cm/CN;
-end
-
-% XCPlat = Cn/CY;
-if abs(beta) <= pi/180
-    XCPlat = Cnb/CYB;
-else
-    XCPlat = Cn/CY;
-end
+CA = coeffsValues(1); CY = coeffsValues(2); CN = coeffsValues(3);
+Cl = coeffsValues(4); Cm = coeffsValues(5); Cn = coeffsValues(6);
+Clp = coeffsValues(7); Cmad = coeffsValues(8); Cmq = coeffsValues(9);
+Cnr = coeffsValues(10); Cnp = coeffsValues(11);
 
-% if Cn == 0 && CY == 0
-%     XCPlat = -5;
-% end
+XCPtot = NaN;
 
 %% RAMP / FREE FLIGHT
 if altitude < environment.effectiveRampAltitude                             % No torque on the launchpad
@@ -209,14 +178,10 @@ if altitude < environment.effectiveRampAltitude                             % No
     betaOut = NaN;
     fY = 0;
     fZ = 0;
-    XCPlon = NaN;
-    XCPlat = NaN;
 
     if T < Fg                                                               % No velocity untill T = Fg
         du = 0;
     end
-
-    XCPtot = NaN;
 else
     %% FORCES
     % first computed in the body-frame reference system
@@ -255,8 +220,6 @@ else
     CMaTot = cos(phi)*Cm - sin(phi)*Cn;
     if CFaTot ~= 0
         XCPtot = CMaTot/CFaTot;
-    else
-        XCPtot = XCPlon;
     end
 
     dAngle = 0;
@@ -331,19 +294,12 @@ if nargout == 2 || ~isempty(wrapper)
     parout.accelerometer.body_acc = ([-fX+T, fY, -fZ]')/m;
 
     parout.coeff.CA = CA;
-    parout.coeff.CYB = CYB;
-    parout.coeff.CNA = CNA;
     parout.coeff.Cl = Cl;
     parout.coeff.Clp = Clp;
-    %parout.coeff.Clb = Clb;
-    parout.coeff.Cma = Cma;
     parout.coeff.Cmad = Cmad;
     parout.coeff.Cmq = Cmq;
-    parout.coeff.Cnb = Cnb;
     parout.coeff.Cnr = Cnr;
     parout.coeff.Cnp = Cnp;
-    parout.coeff.XCPlon = XCPlon;
-    parout.coeff.XCPlat = XCPlat;
     parout.coeff.XCPtot = XCPtot;
 
     parout.uncertanty = [];
-- 
GitLab


From 90b1689a2f35c9e5cca62ca2bd755bf79e28391e Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 21:17:15 +0200
Subject: [PATCH 09/12] [coeff-transport][Wind] Added internal
 griddedInterpolant to wind

---
 classes/misc/Wind.m | 116 ++++++++++++++++++++++++++------------------
 1 file changed, 69 insertions(+), 47 deletions(-)

diff --git a/classes/misc/Wind.m b/classes/misc/Wind.m
index f3eb9a9..f3d6707 100644
--- a/classes/misc/Wind.m
+++ b/classes/misc/Wind.m
@@ -9,6 +9,7 @@ classdef Wind < Config
 %               - mission: Mission, mission object
 %               - varIn: (optional) config source. Alternative to config.m
 %               file
+
     properties(Dependent)
         altitudes               (1, :)
 
@@ -25,6 +26,9 @@ classdef Wind < Config
     end
     
     properties(Access = private)
+        isReady                 (1, 1)  logical                   % true if all properties are set
+        dataInerpolant          (1, 1)  griddedInterpolant      % [-] Interpolates [magnitude, azimuth, elevation]
+
         ALTITUDES
         
         MAGNITUDE_DISTRIBUTION  = ''
@@ -41,77 +45,71 @@ classdef Wind < Config
         elevation
         magnitude
     end
-    
-    properties(Access = private)
-        checks                  (1, 3)  logical                 % true if #[magnitude, azimuth, elevation] == #altitudes
-    end
 
     properties(Constant, Access = protected)
         configName = 'windConfig.m'
         variableName = 'windCustom'
     end
 
-    methods % Getters / Setters
-        function obj = set.altitudes(obj, value)
-            if length(value) ~= length(obj.ALTITUDES)
-                obj.ALTITUDES = value;
-                obj.magnitude = obj.updateDistribution(obj.MAGNITUDE_DISTRIBUTION, obj.MAGNITUDE_PARAMETERS);
-                obj.azimuth   = obj.updateDistribution(obj.AZIMUTH_DISTRIBUTION, obj.AZIMUTH_PARAMETERS);
-                obj.elevation = obj.updateDistribution(obj.ELEVATION_DISTRIBUTION, obj.ELEVATION_PARAMETERS);
-            else
-                obj.ALTITUDES = value;
-            end
+    methods % Getters
+        function value = get.altitudes(obj), value = obj.ALTITUDES; end
+
+        function value = get.magnitudeDistribution(obj), value = obj.MAGNITUDE_DISTRIBUTION; end
+        function value = get.azimuthDistribution(obj), value = obj.AZIMUTH_DISTRIBUTION; end
+        function value = get.elevationDistribution(obj), value = obj.ELEVATION_DISTRIBUTION; end
+            
+        function value = get.magnitudeParameters(obj), value = obj.MAGNITUDE_PARAMETERS; end
+        function value = get.azimuthParameters(obj), value = obj.AZIMUTH_PARAMETERS; end
+        function value = get.elevationParameters(obj), value = obj.ELEVATION_PARAMETERS; end
+    end
 
-            obj.checks(1) = length(obj.magnitude) == length(obj.ALTITUDES);
-            obj.checks(2) = length(obj.azimuth) == length(obj.ALTITUDES);
-            obj.checks(3) = length(obj.elevation) == length(obj.ALTITUDES);
+    methods % Setters
+        function obj = set.altitudes(obj, value)
+            obj.ALTITUDES = value;
+            obj = obj.updateAll();
         end
 
         function obj = set.magnitudeDistribution(obj, value)
             obj.MAGNITUDE_DISTRIBUTION = value;
+            obj.isReady = obj.checkProperties();
             obj.magnitude = obj.updateDistribution(value, obj.MAGNITUDE_PARAMETERS);
-            obj.checks(1) = length(obj.magnitude) == length(obj.ALTITUDES); 
+            obj = obj.updateInterpolant();
         end
 
         function obj = set.magnitudeParameters(obj, value)
             obj.MAGNITUDE_PARAMETERS = value;
+            obj.isReady = obj.checkProperties();
             obj.magnitude = obj.updateDistribution(obj.MAGNITUDE_DISTRIBUTION, value);
-            obj.checks(1) = length(obj.magnitude) == length(obj.ALTITUDES);
+            obj = obj.updateInterpolant();
         end
 
         function obj = set.azimuthDistribution(obj, value)
             obj.AZIMUTH_DISTRIBUTION = value;
+            obj.isReady = obj.checkProperties();
             obj.azimuth = obj.updateDistribution(value, obj.AZIMUTH_PARAMETERS);
-            obj.checks(2) = length(obj.azimuth) == length(obj.ALTITUDES); 
+            obj = obj.updateInterpolant();
         end
 
         function obj = set.azimuthParameters(obj, value)
             obj.AZIMUTH_PARAMETERS = value;
+            obj.isReady = obj.checkProperties();
             obj.azimuth = obj.updateDistribution(obj.AZIMUTH_DISTRIBUTION, value);
-            obj.checks(2) = length(obj.azimuth) == length(obj.ALTITUDES); 
+            obj = obj.updateInterpolant();
         end
 
         function obj = set.elevationDistribution(obj, value)
             obj.ELEVATION_DISTRIBUTION = value;
+            obj.isReady = obj.checkProperties();
             obj.elevation = obj.updateDistribution(value, obj.ELEVATION_PARAMETERS);
-            obj.checks(3) = length(obj.elevation) == length(obj.ALTITUDES); 
+            obj = obj.updateInterpolant();
         end
 
         function obj = set.elevationParameters(obj, value)
             obj.ELEVATION_PARAMETERS = value;
+            obj.isReady = obj.checkProperties();
             obj.elevation = obj.updateDistribution(obj.ELEVATION_DISTRIBUTION, value);
-            obj.checks(3) = length(obj.elevation) == length(obj.ALTITUDES); 
+            obj = obj.updateInterpolant();
         end
-
-        function value = get.altitudes(obj), value = obj.ALTITUDES; end
-
-        function value = get.magnitudeDistribution(obj), value = obj.MAGNITUDE_DISTRIBUTION; end
-        function value = get.azimuthDistribution(obj), value = obj.AZIMUTH_DISTRIBUTION; end
-        function value = get.elevationDistribution(obj), value = obj.ELEVATION_DISTRIBUTION; end
-            
-        function value = get.magnitudeParameters(obj), value = obj.MAGNITUDE_PARAMETERS; end
-        function value = get.azimuthParameters(obj), value = obj.AZIMUTH_PARAMETERS; end
-        function value = get.elevationParameters(obj), value = obj.ELEVATION_PARAMETERS; end
     end
 
     methods
@@ -123,18 +121,8 @@ classdef Wind < Config
             obj@Config(mission, varIn);
         end
 
-        function obj = updateAll(obj)
-            obj.magnitude = obj.updateDistribution(obj.MAGNITUDE_DISTRIBUTION, obj.MAGNITUDE_PARAMETERS);
-            obj.azimuth   = obj.updateDistribution(obj.AZIMUTH_DISTRIBUTION, obj.AZIMUTH_PARAMETERS);
-            obj.elevation = obj.updateDistribution(obj.ELEVATION_DISTRIBUTION, obj.ELEVATION_PARAMETERS);
-
-            obj.checks(1) = length(obj.magnitude) == length(obj.ALTITUDES);
-            obj.checks(2) = length(obj.azimuth) == length(obj.ALTITUDES);
-            obj.checks(3) = length(obj.elevation) == length(obj.ALTITUDES);
-        end
-
         function [uw, vw, ww] = getVels(obj, z)
-            if ~all(obj.checks)
+            if ~obj.isReady
                 error(['Parameters and distributions must be the same ' ...
                     'size as altitudes or scalar']);
             end
@@ -144,18 +132,52 @@ classdef Wind < Config
                 vw = round( - obj.magnitude * sin(obj.azimuth) * cos(obj.elevation), 6);
                 ww = round( - obj.magnitude * (-sin(obj.elevation)), 6);
             else
-                mag = interpLinear(obj.altitudes, obj.magnitude, z, true);
-                az = interpLinear(obj.altitudes, obj.azimuth, z, true);
-                el = interpLinear(obj.altitudes, obj.elevation, z, true);
+                data = obj.dataInerpolant(z);
+                mag = data(1); az = data(2); el = data(3);
 
                 uw = round( - mag * cos(az) * cos(el), 6);
                 vw = round( - mag * sin(az) * cos(el), 6);
                 ww = round( - mag * (-sin(el)), 6);
             end
         end
+
+        function obj = updateAll(obj)
+            obj.magnitude = obj.updateDistribution(obj.MAGNITUDE_DISTRIBUTION, obj.MAGNITUDE_PARAMETERS);
+            obj.azimuth   = obj.updateDistribution(obj.AZIMUTH_DISTRIBUTION, obj.AZIMUTH_PARAMETERS);
+            obj.elevation = obj.updateDistribution(obj.ELEVATION_DISTRIBUTION, obj.ELEVATION_PARAMETERS);
+
+            obj.isReady = obj.checkProperties();
+            obj = obj.updateInterpolant();
+        end
+
+        function obj = updateInterpolant(obj)
+            % UPDATEINTERPOLANT: Interpolates [magnitude; azimuth; elevation] based on altitude
+            %
+            %   Returns:
+            %       - interpolant: griddedInterpolant for [magnitude; azimuth; elevation]
+
+            if ~obj.isReady, return; end
+            if isscalar(obj.ALTITUDES), return; end
+
+            % Prepare data for interpolation
+            data = [obj.magnitude; obj.azimuth; obj.elevation];
+            gridVec = obj.ALTITUDES;
+
+            % Create gridded interpolant
+            obj.dataInerpolant = griddedInterpolant(gridVec, data', 'linear', 'nearest');
+        end
     end
 
     methods(Access = private)
+        function ready = checkProperties(obj)
+            % Check if STATIC, DYNAMIC, GEOMETRY, and STATE are non-empty
+
+            ready = ...
+                length(obj.magnitude) == length(obj.ALTITUDES) && ...
+                length(obj.azimuth) == length(obj.ALTITUDES) && ...     
+                length(obj.elevation) == length(obj.ALTITUDES);
+        end
+
         function vec = updateDistribution(obj, dist, parameters)
             s = length(obj.ALTITUDES);
             vec = [];
-- 
GitLab


From 1e412e1072b2aea9426eec55022bdc932f591868 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 21:36:38 +0200
Subject: [PATCH 10/12] [coeff-transport] Improved coefficient generation

---
 classes/@Rocket/Rocket.m   | 11 ++---------
 classes/misc/Coefficient.m |  5 +++--
 2 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/classes/@Rocket/Rocket.m b/classes/@Rocket/Rocket.m
index b904ff4..019b137 100644
--- a/classes/@Rocket/Rocket.m
+++ b/classes/@Rocket/Rocket.m
@@ -171,15 +171,8 @@ classdef Rocket < Config
                     case 'Yes'
                         parserPath = fullfile(mission.msaPath, 'autoMatricesProtub');
                         addpath(genpath(parserPath));
-                        mainAutoMatProtub(obj);
-
-                        obj.coefficients = Coefficient( ...
-                            fullfile(mission.dataPath, 'aeroCoefficients.mat'), ...
-                            coeffName);
-
-                        obj.coefficientsHighAOA = Coefficient( ...
-                            fullfile(mission.dataPath, 'aeroCoefficientsHighAOA.mat'), ...
-                            coeffName);
+                        [obj.coefficients, obj.coefficientsHighAOA] = ...
+                            mainAutoMatProtub(obj);
                     case 'Cancel'
                         error('Rocket creation aborted')
                     otherwise
diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index 4af9ad6..f7eb51b 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -112,7 +112,7 @@ classdef Coefficient
             %   the specified xcg. Dynamic derivatives are included only if
             %   loadDynamic is true.
             
-            if ~obj.isReady, error('Canno interpolate coefficients: check that all dimensions match'); end
+            if ~obj.isReady, error('Cannot interpolate coefficients: check that all dimensions match'); end
 
             coefficients = zeros(11, 1);
             coefficients(1:6) = obj.staticInterpolant(alpha, mach, beta, altitude, airbakes);
@@ -138,11 +138,12 @@ classdef Coefficient
         end
 
         function value = get.static(obj)
+            if isempty(obj.TOTAL), value = []; return; end
             value = obj.TOTAL([1, 3, 5, 6, 9, 13], :, :, :, :, :, 1);
         end
 
         function value = get.dynamic(obj)
-            if ~obj.isDynamic, value = []; return; end
+            if ~obj.isDynamic || isempty(obj.TOTAL), value = []; return; end
             value = obj.TOTAL([7, 10, 11, 14, 15], :, :, :, :, :, :);
         end
 
-- 
GitLab


From 7237a4fc0a55268a969b260ec02a989f81a245d0 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Mon, 21 Apr 2025 12:31:14 +0200
Subject: [PATCH 11/12] [coeff-transport][Mission] Added prp, gnc and dissile
 path to mission

---
 classes/Mission.m | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/classes/Mission.m b/classes/Mission.m
index 12cb70f..749a695 100644
--- a/classes/Mission.m
+++ b/classes/Mission.m
@@ -23,6 +23,9 @@ classdef Mission < Config
         configPath  char
         dataPath    char
         msaPath     char
+        prpPath     char
+        gncPath     char
+        dissilePath char
     end
 
     properties(Access = private)
@@ -98,6 +101,12 @@ classdef Mission < Config
             obj.dataPath = fullfile(obj.currentPath, obj.name, 'data');
             obj.msaPath = trimPath( ...
                 fullfile(obj.currentPath, '..', '..', '..', 'msa-toolkit'));
+            obj.prpPath = trimPath( ...
+                fullfile(obj.currentPath, '..', '..', '..', 'prp-toolkit'));
+            obj.gncPath = trimPath( ...
+                fullfile(obj.currentPath, '..', '..', '..', 'matlab-simulator'));
+            obj.dissilePath = trimPath( ...
+                fullfile(obj.currentPath, '..', '..', '..', 'dissilematcom'));
         end
 
         function updateMatlabPath(obj)
-- 
GitLab


From a2e17ddf31f82791e2036148792a3c3ef94b809a Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Mon, 21 Apr 2025 12:31:51 +0200
Subject: [PATCH 12/12] [coeff-transport] Applied suggestions for MR

---
 classes/@Rocket/Rocket.m        | 27 +++++++++++++++++++++++++--
 classes/@Rocket/checkGeometry.m |  4 ++--
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/classes/@Rocket/Rocket.m b/classes/@Rocket/Rocket.m
index 019b137..825e836 100644
--- a/classes/@Rocket/Rocket.m
+++ b/classes/@Rocket/Rocket.m
@@ -140,8 +140,10 @@ classdef Rocket < Config
             obj.parachutes   = paraLoader.parachutes;
             
             if options.loadCoefficients
-                if obj.dynamicDerivatives, coeffName = obj.motor.name;
-                else, coeffName = 'generic'; 
+                if obj.dynamicDerivatives
+                    coeffName = obj.motor.name;
+                else
+                    coeffName = 'generic'; 
                 end
 
                 obj.coefficients = Coefficient( ...
@@ -169,6 +171,27 @@ classdef Rocket < Config
                 
                 switch answer
                     case 'Yes'
+                        % Check for required tools
+                        isMsa       = exist(mission.msaPath, "file");
+                        isDissile   = exist(mission.dissilePath, "file");
+
+                        if ~all([isMsa, isDissile])
+                            msg = 'The following toolkits are required to update coefficients:';
+
+                            if ~isMsa
+                                msg = sprintf(...
+                                    '%s\n - <a href="https://git.skywarder.eu/afd/msa/msa-toolkit">msa-toolkit</a>:\n\t In %s\n', ...
+                                    msg, mission.msaPath); 
+                            end
+
+                            if ~isDissile
+                                msg = sprintf('%s\n - <a href="https://git.skywarder.eu/afd/msa/dissilematcom">dissilematcom</a>:\n\t In %s\n', ...
+                                    msg, mission.dissilePath); 
+                            end
+                            error(msg);
+                        end
+
+                        % Start coefficient creation
                         parserPath = fullfile(mission.msaPath, 'autoMatricesProtub');
                         addpath(genpath(parserPath));
                         [obj.coefficients, obj.coefficientsHighAOA] = ...
diff --git a/classes/@Rocket/checkGeometry.m b/classes/@Rocket/checkGeometry.m
index 6608a6d..d07a5cf 100644
--- a/classes/@Rocket/checkGeometry.m
+++ b/classes/@Rocket/checkGeometry.m
@@ -75,8 +75,8 @@ function [checks, summary] = checkGeometry(obj)
         ogiveType = ["CONE", "OGIVE", "POWER", "HAACK", "KARMAN", "MHAACK"];
         boatType  = ["CONE", "OGIVE"];
 
-        % Convert text to DATCOM standard. !TODO: Si farebbe meglio con un
-        % enum
+        % Convert text to DATCOM standard. !TODO: Would be more robust if
+        % enums were used
         boatRocket = find(strcmp(obj.rear.boatType, boatType), 1, 'first') - 1;
         boatTest = find(strcmp(obj.coefficients.geometry.boatType, boatType), 1, 'first') - 1;
 
-- 
GitLab