function spacecraft_attitude_control_HW2_2025()
    clc; clear; close all;
    format long;
    
    %% Initialize Parameters
    global params;
    params = initialize_parameters();
    
    %% Request 1: Disturbance Torques and Uncontrolled Motion
    fprintf('=== Request 1: Disturbance Torques ===\n');
    simulate_uncontrolled_motion();
    
    %% Request 2: Control System Design (now with PID)
    fprintf('\n=== Request 2: Control System Design ===\n');
    design_control_system();
    
    %% Request 3: Saturation Analysis with Thruster Failure
    fprintf('\n=== Request 3: Saturation Analysis ===\n');
    [t_sat, ww_sat] = analyze_saturation();
    
    %% Request 4: Desaturation Maneuver
    if ~isinf(t_sat)
        fprintf('\n=== Request 4: Desaturation Maneuver ===\n');
        perform_desaturation(t_sat, ww_sat);
    else
        fprintf('\nNo wheel saturation detected - skipping desaturation maneuver\n');
    end
    
    %% Save Results
    save_results();
end

%% Parameter Initialization
function p = initialize_parameters()
    p = struct();
    
    % Constants
    p.mu_moon = 4903e9;                  % Moon's GM [m³/s²]
    p.R_moon = 1737e3;                   % Moon radius [m]
    p.altitude = 8000e3;                 % Orbital altitude [m]
    p.a = p.R_moon + p.altitude;         % Semimajor axis [m]
    p.n = sqrt(p.mu_moon/p.a^3);         % Mean motion [rad/s]
    p.arcsec = pi/648000;                % 1 arcsec in radians
    
    % Spacecraft properties
    p.Ix = 21000; p.Iy = 20000; p.Iz = 22500; % [kg·m²]
    p.Iw = 0.11;                              % Wheel inertia [kg·m²]
    
    % Reaction wheel
    p.ww0 = 271*(2*pi/60);                   % Initial speed [rad/s]
    p.ww_max = 2000*(2*pi/60);               % Max speed [rad/s]
    p.Tw_max = 0.236;                        % Max torque [Nm]
    
    % Disturbance parameters
    p.A = 34;                                % Solar array area [m²]
    p.Cs = 0.8;                              % Reflectivity coefficient
    p.cp_offset = 0.6;                       % Center-of-pressure offset [m]
    p.Phi = 1371;                            % Solar flux [W/m²]
    p.c = 299792458;                         % Speed of light [m/s]
    p.thruster_failure_torque = 0.008;       % Additional disturbance [Nm]
    
    % Thrusters
    p.Ft = 3;                                % Thruster force [N]
    p.b = 1.25;                              % Moment arm [m]
    
    % Initial conditions
    p.theta0 = deg2rad(-29.2537);            % Initial pitch [rad]
    p.dtheta0 = deg2rad(-0.0039);            % Initial pitch rate [rad/s]
    p.X0 = [p.theta0; p.dtheta0; p.ww0; 0];  % State vector [θ, θ̇, ω_w, ∫θ]
    
    % Control parameters
    p.thruster_failure = false;
    p.desaturation_mode = false;
    p.control_active = false;
    
    % Electrical parameters
    p.Km = 0.118;                            % Motor constant [Nm/A]
    p.Kw = 0.003;                            % Back-EMF constant [V/rpm]
    p.R = 1.5;                               % Resistance [Ohm]
    p.Pmax = 18;                             % Max power [W]
    
    % ODE options
    p.options = odeset('RelTol', 1e-8, 'AbsTol', 1e-8);
end

%% Request 1: Uncontrolled Motion
function simulate_uncontrolled_motion()
    global params;
    
    % Ensure control is disabled for uncontrolled simulation
    params.control_active = false;
    
    % Simulate uncontrolled dynamics for 1 hour
    [time, X] = ode113(@spacecraft_dynamics, [0 3600], params.X0, params.options);
    
    % Calculate disturbance torques
    T_grav = zeros(size(time));
    T_srp = zeros(size(time));
    for i = 1:length(time)
        theta = X(i,1);
        % Gravity gradient torque (φ=0 in this case)
        T_grav(i) = (3/2)*(params.mu_moon/params.a^3)*(params.Iz-params.Ix)*sin(2*theta);
        % Solar radiation pressure torque (Sun along X-axis)
        F_srp = (params.Phi/params.c)*params.A*(1+params.Cs);
        T_srp(i) = F_srp * params.cp_offset;
    end
    
    % Plot disturbance torques
    figure('Name', 'Disturbance Torques', 'Position', [100 100 900 700]);
    
    subplot(2,1,1);
    plot(time, T_grav*1e6, 'b', 'LineWidth', 2);
    title('Gravity Gradient Torque', 'FontSize', 14);
    grid on; box on;
    xlabel('Time [s]', 'FontSize', 12);
    ylabel('Torque [µNm]', 'FontSize', 12);
    set(gca, 'FontSize', 11);
    
    subplot(2,1,2);
    plot(time, T_srp*1e6, 'r', 'LineWidth', 2);
    title('Solar Radiation Pressure Torque', 'FontSize', 14);
    grid on; box on;
    xlabel('Time [s]', 'FontSize', 12);
    ylabel('Torque [µNm]', 'FontSize', 12);
    set(gca, 'FontSize', 11);
    
    % Plot uncontrolled attitude
    figure('Name', 'Uncontrolled Attitude', 'Position', [200 200 900 500]);
    plot(time, rad2deg(X(:,1)), 'LineWidth', 2, 'Color', [0 0.5 0]);
    title('Uncontrolled Pitch Angle', 'FontSize', 14);
    grid on; box on;
    xlabel('Time [s]', 'FontSize', 12);
    ylabel('Pitch Angle [deg]', 'FontSize', 12);
    set(gca, 'FontSize', 11);
end

%% Request 2: Control System Design (PID implementation)
function design_control_system()
    global params;
    
    % Tune PID controller
    wn = 0.05;       % Natural frequency [rad/s]
    zeta = 1.0;      % Critical damping
    params.kp = params.Iy * wn^2;
    params.kd = 2 * zeta * sqrt(params.Iy * params.kp);
    params.ki = 0.1;  % Small integral term to eliminate steady-state error
    
    % Enable control for simulation
    params.control_active = true;
    params.thruster_failure = false;
    params.desaturation_mode = false;
    
    % Simulate controlled system for initial acquisition (5 minutes)
    [time, X] = ode113(@spacecraft_dynamics, [0 300], params.X0, params.options);
    
    % Calculate control performance metrics
    theta = X(:,1);
    err = -theta;
    ww = X(:,3);
    
    % Calculate electrical parameters
    Tc = params.kp*err + params.kd*(-X(:,2)) + params.ki*X(:,4); % PID control torque
    iw = Tc/params.Km;                        % Current
    Vm = params.Kw*(ww*60/(2*pi)) + params.R*iw; % Voltage
    Pw = Vm.*iw;                              % Power
    
    final_error = abs(err(end));
    max_ww = max(abs(ww));
    max_err = max(abs(err));
    max_power = max(abs(Pw));
    
    fprintf('PID Controller Gains:\n');
    fprintf('kp = %.4f Nm/rad\n', params.kp);
    fprintf('ki = %.4f Nm/(rad·s)\n', params.ki);
    fprintf('kd = %.4f Nms/rad\n', params.kd);
    
    fprintf('\nControl Performance:\n');
    fprintf('Max pointing error: %.2f arcsec\n', max_err/params.arcsec);
    fprintf('Final pointing error: %.2f arcsec (requirement: <20 arcsec)\n',...
            final_error/params.arcsec);
    fprintf('Max wheel speed: %.1f rpm (limit: %.1f rpm)\n',...
            max_ww*60/(2*pi), params.ww_max*60/(2*pi));
    fprintf('Max power consumption: %.2f W (limit: %.1f W)\n\n',...
            max_power, params.Pmax);
    
    % Plot control performance
    figure('Name', 'Control Performance', 'Position', [100 100 1000 900]);
    
    subplot(3,1,1);
    plot(time, err/params.arcsec, 'LineWidth', 2, 'Color', [0.2 0.4 0.8]);
    yline(20, 'r--', '20 arcsec limit', 'LineWidth', 1.5);
    yline(-20, 'r--', 'LineWidth', 1.5);
    title('Pointing Error During Acquisition', 'FontSize', 14);
    grid on; box on;
    xlabel('Time [s]', 'FontSize', 12);
    ylabel('Error [arcsec]', 'FontSize', 12);
    legend('Pointing Error', 'Limits', 'Location', 'best');
    set(gca, 'FontSize', 11);
    
    subplot(3,1,2);
    plot(time, ww*60/(2*pi), 'LineWidth', 2, 'Color', [0.8 0.2 0.2]);
    yline(params.ww_max*60/(2*pi), 'r--', 'Max Speed', 'LineWidth', 1.5);
    yline(-params.ww_max*60/(2*pi), 'r--', 'LineWidth', 1.5);
    title('Reaction Wheel Speed', 'FontSize', 14);
    grid on; box on;
    xlabel('Time [s]', 'FontSize', 12);
    ylabel('Speed [rpm]', 'FontSize', 12);
    set(gca, 'FontSize', 11);
    
    subplot(3,1,3);
    plot(time, Pw, 'LineWidth', 2, 'Color', [0.5 0 0.5]);
    yline(params.Pmax, 'r--', 'Max Power', 'LineWidth', 1.5);
    title('Wheel Power Consumption', 'FontSize', 14);
    grid on; box on;
    xlabel('Time [s]', 'FontSize', 12);
    ylabel('Power [W]', 'FontSize', 12);
    set(gca, 'FontSize', 11);
end

%% Request 3: Saturation Analysis
function [t_sat, ww_sat] = analyze_saturation()
    global params;
    
    % Simulate nominal operation for 1 hour
    params.control_active = true;
    params.thruster_failure = false;
    [time1, X1] = ode113(@spacecraft_dynamics, [0 3600], params.X0, params.options);
    
    % Simulate with thruster failure for extended period
    params.thruster_failure = true;
    [time2, X2] = ode113(@spacecraft_dynamics, [3600 18000], X1(end,:), params.options);
    
    % Combine results
    time = [time1; time2];
    X = [X1; X2];
    ww = X(:,3);
    
    % Find saturation point (after failure)
    post_failure = time > 3600;
    sat_idx = find(abs(ww(post_failure)) >= 0.95*params.ww_max, 1);
    
    if isempty(sat_idx)
        t_sat = Inf;
        ww_sat = 0;
        fprintf('Wheel does not saturate within 5 hours after failure\n');
    else
        t_sat = time(sat_idx + sum(~post_failure)) - 3600;
        ww_sat = ww(sat_idx + sum(~post_failure));
        fprintf('Wheel saturates at t = %.1f s after failure (%.1f%% of max speed)\n',...
                t_sat, 100*abs(ww_sat)/params.ww_max);
    end
    
    % Plot wheel speed history
    figure('Name', 'Wheel Saturation Analysis', 'Position', [200 200 1000 600]);
    plot(time-3600, ww*60/(2*pi), 'LineWidth', 2, 'Color', [0.1 0.6 0.3]);
    xline(0, 'k--', 'Thruster Failure', 'LineWidth', 1.5);
    yline(params.ww_max*60/(2*pi), 'r--', 'Max Speed', 'LineWidth', 1.5);
    yline(-params.ww_max*60/(2*pi), 'r--', 'LineWidth', 1.5);
    title('Wheel Speed History After Thruster Failure', 'FontSize', 14);
    grid on; box on;
    xlabel('Time After Failure [s]', 'FontSize', 12);
    ylabel('Wheel Speed [rpm]', 'FontSize', 12);
    legend('Wheel Speed', 'Failure Time', 'Speed Limits', 'Location', 'best');
    set(gca, 'FontSize', 11);
end

%% Request 4: Desaturation Maneuver
function perform_desaturation(t_sat, ww_sat)
    global params;
    
    % Find initial state at saturation
    params.control_active = true;
    params.thruster_failure = true;
    params.desaturation_mode = false;
    [time1, X1] = ode113(@spacecraft_dynamics, [0 3600+t_sat], params.X0, params.options);
    X0 = X1(end,:)';
    
    % Perform desaturation
    params.desaturation_mode = true;
    desat_duration = 60; % Initial guess [s]
    [time2, X2] = ode113(@spacecraft_dynamics, [0 desat_duration], X0, params.options);
    
    % Calculate electrical parameters during desaturation
    Tc = zeros(size(time2));
    for i = 1:length(time2)
        ww = X2(i,3);
        if ww > 0.8*params.ww_max
            Tc(i) = -params.Tw_max;
            Tt = -2 * params.Ft * params.b; % Use both thrusters
        elseif ww < -0.8*params.ww_max
            Tc(i) = params.Tw_max;
            Tt = 2 * params.Ft * params.b; % Use both thrusters
        else
            Tc(i) = 0;
            Tt = 0;
        end
    end
    iw = Tc/params.Km;
    Vm = params.Kw*(X2(:,3)*60/(2*pi)) + params.R*iw;
    Pw = Vm.*iw;
    
    % Combine results
    time = [time1; time1(end)+time2];
    X = [X1; X2];
    
    % Analyze results
    theta = X2(:,1);
    ww = X2(:,3);
    recovery_idx = find(abs(ww) <= 0.1*params.ww_max, 1);
    max_pointing_err = max(abs(rad2deg(theta)));
    
    if isempty(recovery_idx)
        fprintf('Desaturation failed to return wheel to safe range\n');
    else
        fprintf('Desaturation successful in %.1f seconds\n', time2(recovery_idx));
    end
    fprintf('Max pointing error: %.3f deg (requirement: <1 deg)\n', max_pointing_err);
    fprintf('Max power during desaturation: %.2f W (limit: %.1f W)\n', max(abs(Pw)), params.Pmax);
    
    % Plot desaturation performance
    figure('Name', 'Desaturation Performance', 'Position', [100 100 1200 900]);
    
    subplot(3,1,1);
    plot(time-3600-t_sat, rad2deg(X(:,1)), 'LineWidth', 2, 'Color', [0.7 0.3 0.1]);
    yline(1, 'r--', '1° Error Limit', 'LineWidth', 1.5);
    yline(-1, 'r--', 'LineWidth', 1.5);
    title('Pointing Error During Desaturation', 'FontSize', 14);
    grid on; box on;
    xlabel('Time After Saturation [s]', 'FontSize', 12);
    ylabel('Error [deg]', 'FontSize', 12);
    legend('Pointing Error', 'Error Limits', 'Location', 'best');
    set(gca, 'FontSize', 11);
    
    subplot(3,1,2);
    plot(time-3600-t_sat, X(:,3)*60/(2*pi), 'LineWidth', 2, 'Color', [0.1 0.5 0.7]);
    yline([params.ww_max, -params.ww_max]*60/(2*pi), 'r--', 'Speed Limits', 'LineWidth', 1.5);
    yline([0.1*params.ww_max, -0.1*params.ww_max]*60/(2*pi), 'g--', 'Safe Range', 'LineWidth', 1.5);
    title('Wheel Speed During Desaturation', 'FontSize', 14);
    grid on; box on;
    xlabel('Time After Saturation [s]', 'FontSize', 12);
    ylabel('Speed [rpm]', 'FontSize', 12);
    set(gca, 'FontSize', 11);
    
    subplot(3,1,3);
    plot(time2, Pw, 'LineWidth', 2, 'Color', [0.6 0.2 0.6]);
    yline(params.Pmax, 'r--', 'Max Power', 'LineWidth', 1.5);
    title('Power Consumption During Desaturation', 'FontSize', 14);
    grid on; box on;
    xlabel('Time [s]', 'FontSize', 12);
    ylabel('Power [W]', 'FontSize', 12);
    set(gca, 'FontSize', 11);
end

%% Core Dynamics Function (updated for PID)
function dX = spacecraft_dynamics(t, X)
    global params;
    theta = X(1); dtheta = X(2); ww = X(3); err_int = X(4);
    
    % Calculate disturbance torques
    T_grav = (3/2)*(params.mu_moon/params.a^3)*(params.Iz-params.Ix)*sin(2*theta);
    F_srp = (params.Phi/params.c)*params.A*(1+params.Cs);
    T_srp = F_srp * params.cp_offset;
    T_dist = T_grav + T_srp;
    
    % Additional disturbance if thruster failed
    if params.thruster_failure && t > 3600
        T_dist = T_dist + params.thruster_failure_torque;
    end
    
    % Control logic
    if params.control_active
        if params.desaturation_mode
            % Desaturation control logic
            if ww > 0.8*params.ww_max
                Tc = -params.Tw_max;
                Tt = -2 * params.Ft * params.b; % Use both thrusters
            elseif ww < -0.8*params.ww_max
                Tc = params.Tw_max;
                Tt = 2 * params.Ft * params.b; % Use both thrusters
            else
                Tc = 0;
                Tt = 0;
            end
        else
            % PID control with anti-windup
            err = -theta;
            derr = -dtheta;
            Tc = params.kp*err + params.kd*derr + params.ki*err_int;
            
            % Anti-windup protection
            if abs(Tc) > params.Tw_max
                Tc = sign(Tc) * params.Tw_max;
            end
            Tt = 0;
        end
    else
        % No control
        Tc = 0;
        Tt = 0;
    end
    
    % State derivatives
    dX = zeros(4,1);
    dX(1) = dtheta;
    dX(2) = (T_dist + Tc + Tt) / params.Iy;
    dX(3) = -Tc / params.Iw;
    dX(4) = -theta; % Integral of error (for PID control)
end

%% Utility Function
function save_results()
    if ~exist('Plots', 'dir')
        mkdir('Plots');
    end
    
    figs = findobj('Type', 'figure');
    for i = 1:length(figs)
        fig = figs(i);
        name = get(fig, 'Name');
        if isempty(name)
            name = sprintf('Figure_%d', fig.Number);
        end
        saveas(fig, fullfile('Plots', [strrep(name, ' ', '_') '.png']));
        saveas(fig, fullfile('Plots', [strrep(name, ' ', '_') '.fig']));
    end
end