From 7b110d6d9c82162c10a2985cbd9ff72832fce6f3 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Mon, 21 Apr 2025 21:03:58 +0200
Subject: [PATCH 1/8] [alpha-phi] Moved enums to dedicated folder

---
 classes/enums/CoeffType.m           | 7 +++++++
 classes/{bays => enums}/MotorType.m | 0
 2 files changed, 7 insertions(+)
 create mode 100644 classes/enums/CoeffType.m
 rename classes/{bays => enums}/MotorType.m (100%)

diff --git a/classes/enums/CoeffType.m b/classes/enums/CoeffType.m
new file mode 100644
index 0000000..85e051c
--- /dev/null
+++ b/classes/enums/CoeffType.m
@@ -0,0 +1,7 @@
+classdef CoeffType
+    enumeration
+        AlphaTotPhi
+        AlphaBeta
+    end
+end
+
diff --git a/classes/bays/MotorType.m b/classes/enums/MotorType.m
similarity index 100%
rename from classes/bays/MotorType.m
rename to classes/enums/MotorType.m
-- 
GitLab


From cae902b74bd55cde455880b0d101301de1cada57 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Mon, 21 Apr 2025 21:04:40 +0200
Subject: [PATCH 2/8] [alpha-phi] Implementing coefficient type enum and
 symmetry flag

---
 classes/misc/Coefficient.m | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index 02f721d..b41b341 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -59,10 +59,12 @@ classdef Coefficient < Structured
         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
+        type                        CoeffType   % Wheter based on alphaTot-phi or alpha-beta
+        symmetry            (1, 1)  logical     % Apply axial simmetry: valid for alphaTot-phi
+
+        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)
@@ -197,6 +199,14 @@ classdef Coefficient < Structured
         end
 
         function obj = set.state(obj, value)
+            if isfield(value, 'phis') && ~isempty(value.phis)
+                if ~isempty(value.betas), error('Cannot set both alpha, beta and phi'); end
+                obj.type = CoeffType.AlphaTotPhi;
+                obj.symmetry = (360/obj.STATE.phis(end) == obj.GEOMETRY.nPanel);
+            else
+                obj.type = CoeffType.AlphaBeta;
+            end
+
             obj.STATE   = value;
             obj.isReady = obj.checkProperties();
             obj         = obj.updateChad();
-- 
GitLab


From 6efc8ebd2113b58b28abf31cdd4de986ec897f24 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Tue, 22 Apr 2025 11:05:32 +0200
Subject: [PATCH 3/8] [alpha-phi] Improved checks

---
 classes/misc/Coefficient.m                   | 30 ++++++++++++++++----
 functions/miscellaneous/createDissileInput.m |  9 +++---
 2 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index b41b341..01e508e 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -192,6 +192,10 @@ classdef Coefficient < Structured
         end
 
         function obj = set.geometry(obj, value)
+            if ~isempty(obj.STATE) && isfield(obj.STATE, 'phis') && ~isempty(obj.STATE.phis)
+                obj.symmetry = (360/obj.STATE.phis(end) == value.nPanel);
+            end
+
             obj.GEOMETRY = value;
             obj.isReady  = obj.checkProperties();
             obj          = obj.updateChad();
@@ -200,9 +204,13 @@ classdef Coefficient < Structured
 
         function obj = set.state(obj, value)
             if isfield(value, 'phis') && ~isempty(value.phis)
-                if ~isempty(value.betas), error('Cannot set both alpha, beta and phi'); end
+                if ~isempty(value.betas)
+                    error('Cannot set both alpha, beta and phi'); 
+                end
+                if ~isempty(obj.GEOMETRY)
+                    obj.symmetry = (360/value.phis(end) == obj.GEOMETRY.nPanel);
+                end
                 obj.type = CoeffType.AlphaTotPhi;
-                obj.symmetry = (360/obj.STATE.phis(end) == obj.GEOMETRY.nPanel);
             else
                 obj.type = CoeffType.AlphaBeta;
             end
@@ -251,12 +259,17 @@ classdef Coefficient < Structured
 
             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};
+            if obj.type == CoeffType.AlphaBeta
+                theta = obj.STATE.betas;                             % Converting to rad
+            else
+                theta = obj.STATE.phis;                              % Converting to rad
+            end
+
+            gridVecs = {alpha, mach, theta, altitude, airbakes, xcg};
             dims = cellfun(@(x) length(x), gridVecs);
             dims(dims == 0) = 1;                                            % Empty case is the same as scalar case
 
@@ -270,15 +283,20 @@ classdef Coefficient < Structured
             %% 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;
             xcg = obj.GEOMETRY.xcg;
 
             if isempty(obj.STATE.hprot), airbakes = 0;
                 else, airbakes = obj.STATE.hprot/obj.STATE.hprot(end);      % Normalizing on height
             end
+            
+            if obj.type == CoeffType.AlphaBeta
+                theta = obj.STATE.betas*pi/180;                             % Converting to rad
+            else
+                theta = obj.STATE.phis*pi/180;                              % Converting to rad
+            end
 
-            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
+            gridVecs = {alpha, mach, theta, 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);
diff --git a/functions/miscellaneous/createDissileInput.m b/functions/miscellaneous/createDissileInput.m
index 39bc2b0..1c3f5ee 100644
--- a/functions/miscellaneous/createDissileInput.m
+++ b/functions/miscellaneous/createDissileInput.m
@@ -49,12 +49,12 @@ if not(any(vars.alpha == 1))
     error('vars.alpha does not contains 1');
 end
 
-if not(any(vars.alpha == -1))
-    error('vars.alpha does not contains -1');
+if isempty(vars.phi) && not(any(vars.alpha == -1))
+    error('vars.alpha does not contain -1 in alpha-beta configuration');
 end
 
-if not(isequal(vars.alpha, -fliplr(vars.alpha)))
-    error('vars.alpha is not symmetric');
+if isempty(vars.phi) && not(isequal(vars.alpha, -fliplr(vars.alpha)))
+    error('vars.alpha is not symmetric in alpha-beta configuration');
 end
 
 % if any(vars.abk > 1)
@@ -69,6 +69,7 @@ input.fltcon.about = 'Flight conditions quantities';
 input.fltcon.MACH = vars.mach;
 input.fltcon.ALPHA = vars.alpha;
 input.fltcon.BETA = vars.beta;
+input.fltcon.PHI = vars.phi;
 input.fltcon.ALT = vars.alt;
 
 %% REFQ
-- 
GitLab


From b1293f6c2a187cb6a29837668ecc05b8b85e717a Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sat, 26 Apr 2025 22:48:52 +0200
Subject: [PATCH 4/8] [alpha-phi][Coefficient] Implemented coefficient
 transport

---
 classes/misc/Coefficient.m | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index 01e508e..14ab717 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -93,7 +93,7 @@ classdef Coefficient < Structured
             obj         = obj.updateInterpolants();
         end
 
-        function coefficients = get(obj, alpha, mach, beta, altitude, airbakes, xcg)
+        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
@@ -118,10 +118,31 @@ classdef Coefficient < Structured
             
             if ~obj.isReady, error('Cannot interpolate coefficients: check that all dimensions match'); end
 
+            % Interpolating coefficients
             coefficients = zeros(11, 1);
-            coefficients(1:6) = obj.staticInterpolant(alpha, mach, beta, altitude, airbakes);
-            % Transporting static coefficients to new xcg
+            if obj.type == CoeffType.AlphaBeta
+                coefficients(1:6) = obj.staticInterpolant(alpha, mach, beta, altitude, airbakes);
+            else
+                [alphaTot, phi] = getAlphaPhi(alpha, beta);
+                phi = wrapTo2Pi(phi);
+
+                finAngle = 2*pi / obj.GEOMETRY.nPanel;                           % Angle between two fin panels
+                n = floor(phi / finAngle);
+                deltaPhi = n*finAngle;  
+                psi = phi - deltaPhi;                                            % Angle wrapped to finAngle
+                
+                coefficients(1:6) = obj.staticInterpolant(alphaTot, mach, psi, altitude, airbakes); % Get coeffs in limited range
+
+                if n > 1 % If necessary, perform rotation on different frame
+                    R = [cos(deltaPhi), -sin(deltaPhi);
+                        sin(deltaPhi), cos(deltaPhi)];
+    
+                    coefficients([2, 3]) = R  * coefficients([2, 3]);
+                    coefficients([5, 6]) = R' * coefficients([5, 6]);
+                end
+            end
 
+            % 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;
@@ -132,6 +153,9 @@ classdef Coefficient < Structured
             coefficients(4:6) = coefficients(4:6) + d/l * forceCoeffs;
 
             if ~obj.isDynamic, return; end
+            if obj.type == CoeffType.AlphaTotPhi
+                error('Alpha-Phi coefficients do not support dynamic derivatives yet')
+            end
             coefficients(7:11) = obj.dynamicInterpolant(alpha, mach, beta, altitude, airbakes, xcg);
         end
 
-- 
GitLab


From ba45c68e00961cd4a520fd0d00585dcc485f6d86 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 27 Apr 2025 00:28:43 +0200
Subject: [PATCH 5/8] [alpha-phi][Rocket] Added option to not load highAOA
 matrices

---
 classes/@Rocket/Rocket.m                         | 16 ++++++++++------
 .../config/rocketConfig.m                        |  1 +
 .../config/rocketConfig.m                        |  1 +
 .../config/rocketConfig.m                        |  1 +
 .../config/rocketConfig.m                        |  1 +
 .../config/rocketConfig.m                        |  2 +-
 .../config/rocketConfig.m                        |  1 +
 .../config/rocketConfig.m                        |  1 +
 .../config/rocketConfig.m                        |  1 +
 .../config/rocketConfig.m                        |  3 ++-
 10 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/classes/@Rocket/Rocket.m b/classes/@Rocket/Rocket.m
index a7796d4..f050235 100644
--- a/classes/@Rocket/Rocket.m
+++ b/classes/@Rocket/Rocket.m
@@ -52,7 +52,8 @@ classdef Rocket < Config
         parachutes                  Para            % [-]       (nParachutes, nStages) Parachutes onboard
 
         dynamicDerivatives  (1, 1)  logical         % [-]       True if dynamic derivatives will be loaded
-        
+        highAOACoefficients (1, 1)  logical         % [-]       True if highAOACoefficients are loaded
+
         coefficients                Coefficient     % [-]       Aerodynamic coefficients
         coefficientsHighAOA         Coefficient     % [-]       Aerodynamic coefficients at high angle of attack
     end
@@ -149,13 +150,16 @@ classdef Rocket < Config
                     fullfile(mission.dataPath, 'aeroCoefficients.mat'), ...
                     coeffName);
 
-                obj.coefficientsHighAOA = Coefficient( ...
-                    fullfile(mission.dataPath, 'aeroCoefficientsHighAOA.mat'), ...
-                    coeffName);
+                if obj.highAOACoefficients
+                    obj.coefficientsHighAOA = Coefficient( ...
+                        fullfile(mission.dataPath, 'aeroCoefficientsHighAOA.mat'), ...
+                        coeffName);
+                end
 
                 answer = '';
                 
-                if isempty(obj.coefficients.static) || isempty(obj.coefficientsHighAOA.static)
+                if isempty(obj.coefficients.static) || ...
+                        (obj.highAOACoefficients && isempty(obj.coefficientsHighAOA.static))
                     answer = questdlg(['Coefficient matrices not found. ' ...
                         'Do you want to create new matrices?']);
                 elseif options.checkGeometry
@@ -194,7 +198,7 @@ classdef Rocket < Config
                         parserPath = fullfile(mission.msaPath, 'autoMatricesProtub');
                         addpath(genpath(parserPath));
                         [obj.coefficients, obj.coefficientsHighAOA] = ...
-                            mainAutoMatProtub(obj);
+                            mainAutoMatProtub(obj, 'computeHighAOA', obj.highAOACoefficients);
                     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 160c5b0..cb26401 100644
--- a/missions/2021_Lynx_Portugal_October/config/rocketConfig.m
+++ b/missions/2021_Lynx_Portugal_October/config/rocketConfig.m
@@ -20,6 +20,7 @@ rocket.lengthCenterNoMot = 1.7640;                                     % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 d0dc3df..0851f94 100644
--- a/missions/2021_Lynx_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2021_Lynx_Roccaraso_September/config/rocketConfig.m
@@ -20,6 +20,7 @@ rocket.lengthCenterNoMot = 1.7840;                                     % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 2f949bf..17d63ca 100644
--- a/missions/2022_Pyxis_Portugal_October/config/rocketConfig.m
+++ b/missions/2022_Pyxis_Portugal_October/config/rocketConfig.m
@@ -20,6 +20,7 @@ rocket.lengthCenterNoMot = 1.4470;                                     % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 ee3a3c7..5b7406a 100644
--- a/missions/2022_Pyxis_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2022_Pyxis_Roccaraso_September/config/rocketConfig.m
@@ -22,6 +22,7 @@ rocket.lengthCenterNoMot = 1.61;                                     % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 401c415..f9d6d4c 100644
--- a/missions/2023_Gemini_Portugal_October/config/rocketConfig.m
+++ b/missions/2023_Gemini_Portugal_October/config/rocketConfig.m
@@ -19,7 +19,7 @@ rocket.lengthCenterNoMot = 1.517;                                     % [m]
 %   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
-
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 fc2715b..cadbc7e 100644
--- a/missions/2023_Gemini_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2023_Gemini_Roccaraso_September/config/rocketConfig.m
@@ -19,6 +19,7 @@ rocket.lengthCenterNoMot = 1.517;                                     % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 f87640c..1d90d15 100644
--- a/missions/2024_Lyra_Portugal_October/config/rocketConfig.m
+++ b/missions/2024_Lyra_Portugal_October/config/rocketConfig.m
@@ -13,6 +13,7 @@ rocket.lengthCenterNoMot = [];                                          % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 2939db0..133be74 100644
--- a/missions/2024_Lyra_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2024_Lyra_Roccaraso_September/config/rocketConfig.m
@@ -19,6 +19,7 @@ rocket.lengthCenterNoMot = [];                                          % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA 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 e73d2a2..6e2c6c6 100644
--- a/missions/2025_Orion_Portugal_October/config/rocketConfig.m
+++ b/missions/2025_Orion_Portugal_October/config/rocketConfig.m
@@ -12,7 +12,8 @@ rocket.diameter = 0.15;                                                   % [m]
 % 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
+rocket.dynamicDerivatives = false;                                      % [-]      True if dynamic derivatives will be loaded
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA will be loaded
 
 %% PRF - Includes Parafoil + Nose
 parafoil = Parafoil();
-- 
GitLab


From 84fc310802e9a6bc13b6a642335fac70fb4bd1d6 Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 27 Apr 2025 00:29:28 +0200
Subject: [PATCH 6/8] [alpha-phi] Updated aerodynamic matrices

---
 .../2025_Orion_Portugal_October/data/aeroCoefficients.mat     | 4 ++--
 .../data/aeroCoefficientsHighAOA.mat                          | 4 ++--
 missions/2025_Orion_Roccaraso_September/config/rocketConfig.m | 1 +
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat b/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat
index 1df1179..3aa85db 100644
--- a/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat
+++ b/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:fb0ccbfea51866683e3cdaf1b7aa21c42ca3d387575aaa40df3694557c009fd4
-size 14789031
+oid sha256:f4ad48c4d016c33811361e5823e3fb0c87ae64679ff148293e2fcbb48c52f056
+size 300753532
diff --git a/missions/2025_Orion_Portugal_October/data/aeroCoefficientsHighAOA.mat b/missions/2025_Orion_Portugal_October/data/aeroCoefficientsHighAOA.mat
index 965f46e..00d9ce5 100644
--- a/missions/2025_Orion_Portugal_October/data/aeroCoefficientsHighAOA.mat
+++ b/missions/2025_Orion_Portugal_October/data/aeroCoefficientsHighAOA.mat
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f6f664625606e3fdfcbf0de0d8a45b8f49a43be69d8a879104f1315d4d9f5e53
-size 5776386
+oid sha256:1659d1dc6b19ea13c3cdd75b8f9627d42123c4cb6dff535d93618247a17ab9d1
+size 46224320
diff --git a/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m b/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m
index c4e1ffc..e320c15 100644
--- a/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m
+++ b/missions/2025_Orion_Roccaraso_September/config/rocketConfig.m
@@ -19,6 +19,7 @@ rocket.lengthCenterNoMot = [];                                          % [m]
 %   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
+rocket.highAOACoefficients = false;                                     % [-]      True if separate matrix for high AOA will be loaded
 
 %% PLD - Includes Parafoil + Nose
 parafoil = Parafoil();
-- 
GitLab


From 2f5958ac270dae5b45547995421233a82bfbbad5 Mon Sep 17 00:00:00 2001
From: Lorenzo Bettonte <lorenzo.bettonte@skywarder.edu>
Date: Sun, 18 May 2025 22:41:23 +0200
Subject: [PATCH 7/8] [alpha-phi] fixed a bug where n would in the wrong range
 (in Coefficient's class)  and Handle AoA matrix usage in alphaTotPhi cases
 (in ballistic)

---
 classes/misc/Coefficient.m         |  2 +-
 functions/odeFunctions/ballistic.m | 13 ++++++++-----
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index 14ab717..4b6d685 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -133,7 +133,7 @@ classdef Coefficient < Structured
                 
                 coefficients(1:6) = obj.staticInterpolant(alphaTot, mach, psi, altitude, airbakes); % Get coeffs in limited range
 
-                if n > 1 % If necessary, perform rotation on different frame
+                if n > 0 % If necessary, perform rotation on different frame
                     R = [cos(deltaPhi), -sin(deltaPhi);
                         sin(deltaPhi), cos(deltaPhi)];
     
diff --git a/functions/odeFunctions/ballistic.m b/functions/odeFunctions/ballistic.m
index d08b306..fed5600 100644
--- a/functions/odeFunctions/ballistic.m
+++ b/functions/odeFunctions/ballistic.m
@@ -134,7 +134,7 @@ end
 if not(abs(ur) < 1e-9 || velsNorm < 1e-9)
     alpha = atan(wr/ur);
     beta = atan(vr/ur);                                                     % beta = asin(vr/V_norm) is the classical notation, Datcom uses this one though.
-    % alpha_tot = atan(sqrt(wr^2 + vr^2)/ur);   % datcom 97' definition
+    alphaTot = atan(sqrt(wr^2 + vr^2)/ur);   % datcom 97' definition
 else
     alpha = 0;
     beta = 0;
@@ -144,14 +144,17 @@ alphaOut = alpha;
 betaOut = beta;
 
 %% INTERPOLATE AERODYNAMIC COEFFICIENTS:
-
-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);
+if rocket.coefficients.type == CoeffType.AlphaBeta
+    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);
+    end
+elseif abs(alphaTot) > rocket.coefficients.state.alphas(end)*pi/180
+        coeffsValues = rocket.coefficientsHighAOA.get(alpha, mach, beta, absoluteAltitude, extension, xcg);
 else
     coeffsValues = rocket.coefficients.get(alpha, mach, beta, absoluteAltitude, extension, xcg);
 end
 
+
 % Retrieve Coefficients
 CA = coeffsValues(1); CY = coeffsValues(2); CN = coeffsValues(3);
 Cl = coeffsValues(4); Cm = coeffsValues(5); Cn = coeffsValues(6);
-- 
GitLab


From 7b9fa1e8d56c9b239181122f55996d47bce19f63 Mon Sep 17 00:00:00 2001
From: Lorenzo Bettonte <lorenzo.bettonte@skywarder.edu>
Date: Tue, 24 Jun 2025 12:45:39 +0200
Subject: [PATCH 8/8] [alpha-phi] created new coefficient's matrices and
 removed a comment

---
 classes/misc/Coefficient.asv                  | 322 ++++++++++++++++++
 classes/misc/Coefficient.m                    |   2 +-
 .../data/aeroCoefficients.mat                 |   4 +-
 3 files changed, 325 insertions(+), 3 deletions(-)
 create mode 100644 classes/misc/Coefficient.asv

diff --git a/classes/misc/Coefficient.asv b/classes/misc/Coefficient.asv
new file mode 100644
index 0000000..924dc07
--- /dev/null
+++ b/classes/misc/Coefficient.asv
@@ -0,0 +1,322 @@
+classdef Coefficient
+    % Coefficient: 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 DATCOM 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
+    %
+    %   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)
+        total                       double  % Coefficients stored in DATCOM format
+        geometry                    struct  % Reference geometry
+        state                       struct  % Flight envelope
+    end
+
+    properties
+        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)
+        type                        CoeffType   % Wheter based on alphaTot-phi or alpha-beta
+        symmetry            (1, 1)  logical     % Apply axial simmetry: valid for alphaTot-phi
+
+        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)
+        TOTAL                               % Cached variable for total
+        GEOMETRY                            % Cached variable for geometry
+        STATE                               % Cached variable for state
+    end
+
+    properties(Access = private)
+        staticInterpolant   (1, 1)  griddedInterpolant
+        dynamicInterpolant  (1, 1)  griddedInterpolant
+    end
+
+    methods
+        function obj = Coefficient(filePath, name)
+            arguments
+                filePath    (1, :)  char = ''
+                name        (1, :)  char = 'generic'
+            end
+
+            if isempty(filePath), return; end
+            
+            obj = obj.loadData(filePath, name);
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
+        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.
+            
+            if ~obj.isReady, error('Cannot interpolate coefficients: check that all dimensions match'); end
+
+            % Interpolating coefficients
+            coefficients = zeros(11, 1);
+            if obj.type == CoeffType.AlphaBeta
+                coefficients(1:6) = obj.staticInterpolant(alpha, mach, beta, altitude, airbakes);
+            else
+                [alphaTot, phi] = getAlphaPhi(alpha, beta);
+                phi = wrapTo2Pi(phi);
+
+                finAngle = 2*pi / obj.GEOMETRY.nPanel;                           % Angle between two fin panels
+                n = floor(phi / finAngle);
+                deltaPhi = n*finAngle;  
+                psi = phi - deltaPhi;                                            % Angle wrapped to finAngle
+                
+                coefficients(1:6) = obj.staticInterpolant(alphaTot, mach, psi, altitude, airbakes); % Get coeffs in limited range
+
+                if n > 0 % If necessary, perform rotation on different frame
+                    R = [cos(deltaPhi), -sin(deltaPhi);
+                        sin(deltaPhi), cos(deltaPhi)];
+    
+                    coefficients([2, 3]) = R  * coefficients([2, 3]);
+                    coefficients([5, 6]) = R' * coefficients([5, 6]);
+                end
+            end
+
+            % Transporting static coefficients to new xcg
+            % C_B = C_A + d / c * [0; -CN; CY]_A 
+            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.isDynamic, return; end
+            if obj.type == CoeffType.AlphaTotPhi
+                error('Alpha-Phi coefficients do not support dynamic derivatives yet')
+            end
+            coefficients(7:11) = obj.dynamicInterpolant(alpha, mach, beta, altitude, airbakes, xcg);
+        end
+    end
+
+    methods % Getters
+        function value = get.total(obj)
+            value = obj.TOTAL;
+        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 || isempty(obj.TOTAL), value = []; return; end
+            value = obj.TOTAL([7, 10, 11, 14, 15], :, :, :, :, :, :);
+        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.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)
+            if ~isempty(obj.STATE) && isfield(obj.STATE, 'phis') && ~isempty(obj.STATE.phis)
+                obj.symmetry = (360/obj.STATE.phis(end) == value.nPanel);
+            end
+
+            obj.GEOMETRY = value;
+            obj.isReady  = obj.checkProperties();
+            obj          = obj.updateInterpolants();
+        end
+
+        function obj = set.state(obj, value)
+            if isfield(value, 'phis') && ~isempty(value.phis)
+                if ~isempty(value.betas)
+                    error('Cannot set both alpha, beta and phi'); 
+                end
+                if ~isempty(obj.GEOMETRY)
+                    obj.symmetry = (360/value.phis(end) == obj.GEOMETRY.nPanel);
+                end
+                obj.type = CoeffType.AlphaTotPhi;
+            else
+                obj.type = CoeffType.AlphaBeta;
+            end
+
+            obj.STATE   = value;
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
+        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.finsCN = dataCoeffs.finsCN;
+            obj.GEOMETRY = dataCoeffs.geometry;
+            obj.state = dataCoeffs.state;
+            
+            % 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) && ...
+                    ~isempty(obj.GEOMETRY) && ~isempty(obj.STATE) && ...
+                    ~(isempty(obj.dynamic) && obj.isDynamic);
+
+            if ~ready, return; end
+
+            alpha = obj.STATE.alphas;
+            mach = obj.STATE.machs;
+            altitude = obj.STATE.altitudes;
+            airbakes = obj.STATE.hprot;
+            xcg = obj.GEOMETRY.xcg;
+ 
+            if obj.type == CoeffType.AlphaBeta
+                theta = obj.STATE.betas;                             % Converting to rad
+            else
+                theta = obj.STATE.phis;                              % Converting to rad
+            end
+
+            gridVecs = {alpha, mach, theta, altitude, airbakes, xcg};
+            dims = cellfun(@(x) length(x), gridVecs);
+            dims(dims == 0) = 1;                                            % Empty case is the same as scalar case
+
+            ready = all(size(obj.total, 2:7) == dims);
+        end
+
+        function obj = updateInterpolants(obj)
+            % Update static and dynamic interpolants
+            if ~obj.isReady, return; end
+        
+            %% Retrieve flight conditions
+            alpha = obj.STATE.alphas*pi/180;                                % Converting to rad
+            mach = obj.STATE.machs;
+            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
+            
+            if obj.type == CoeffType.AlphaBeta
+                theta = obj.STATE.betas*pi/180;                             % Converting to rad
+            else
+                theta = obj.STATE.phis*pi/180;                              % Converting to rad
+            end
+
+            gridVecs = {alpha, mach, theta, 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 = repmat(staticCoeffs, singletonDims(1:end-1) + 1); % Exclude xcg for static coefficients
+
+            obj.staticInterpolant = griddedInterpolant(gridVecs(1:end-1), staticCoeffs, 'linear', 'nearest');
+
+            if ~obj.isDynamic, return; end
+
+            %% Handle dynamic derivatives
+            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)
+                gridVecs{end} = flip(xcg);
+                dynamicCoeffs = flip(dynamicCoeffs, 7);
+            end
+
+            obj.dynamicInterpolant = griddedInterpolant(gridVecs, dynamicCoeffs, 'linear', 'nearest');
+        end
+    end
+end
\ No newline at end of file
diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index 4b6d685..2573c6d 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -143,7 +143,7 @@ classdef Coefficient < Structured
             end
 
             % Transporting static coefficients to new xcg
-            % C_B = C_A + d / c * [0; -CN; CY]_A <- NOTE: Non torna il meno su CN
+            % C_B = C_A + d / c * [0; -CN; CY]_A 
             d = xcg - obj.GEOMETRY.xcg(1);
             l = obj.GEOMETRY.diameter;
 
diff --git a/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat b/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat
index 3aa85db..e7019e8 100644
--- a/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat
+++ b/missions/2025_Orion_Portugal_October/data/aeroCoefficients.mat
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f4ad48c4d016c33811361e5823e3fb0c87ae64679ff148293e2fcbb48c52f056
-size 300753532
+oid sha256:f544fdb75b3dcc7180915ef4b0263969a17444b7609cb7e8947fc482d5cffa14
+size 300751768
-- 
GitLab