diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m index d5d60e26454780c8ce58c19b276fb9d6bba31cea..a896b4132ddf6c37a3b8eedf7915accc5099bb17 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