function load_motor_to_workspace()
%LOAD_MOTOR_TO_WORKSPACE  Carica la mappa motore nel base workspace.
%
%  Popola tre variabili che servono al blocco 2-D Lookup Table dentro
%  swarm_phase3.slx:
%
%    mot_n_bp     [1 x N_n]      breakpoint giri motore [RPM]
%    mot_alpha_bp [1 x N_alpha]  breakpoint % acceleratore (0-100)
%    mot_T_table  [N_n x N_alpha] coppia [Nm]
%
%  STRATEGIA (tre fonti, in ordine di preferenza):
%   1. Se 'motor_map.mat' esiste con le 3 variabili gia' pronte → load
%   2. Se 'motor_map.m' esiste come script che carica le tabelle in base
%      WS → run e prelievo
%   3. Altrimenti generazione sintetica (placeholder ragionevole) — utile
%      per testare la pipeline finche' non hai la mappa vera del tuo
%      motore. Cambialo non appena hai i dati veri.
%
%  USO:
%    load_motor_to_workspace();
%    % poi load_system('swarm_phase3') / sim('swarm_phase3')

    src_used = '';

    %% Tentativo 1: motor_map.mat
    if exist('motor_map.mat', 'file')
        S = load('motor_map.mat');
        if all(isfield(S, {'mot_n_bp', 'mot_alpha_bp', 'mot_T_table'}))
            assignin('base', 'mot_n_bp',     S.mot_n_bp(:).');
            assignin('base', 'mot_alpha_bp', S.mot_alpha_bp(:).');
            assignin('base', 'mot_T_table',  S.mot_T_table);
            src_used = 'motor_map.mat';
        end
    end

    %% Tentativo 2: motor_map.m come script
    if isempty(src_used) && exist('motor_map.m', 'file')
        try
            evalin('base', 'motor_map');
            ok = evalin('base', ...
                'exist(''mot_n_bp'',''var'') && exist(''mot_alpha_bp'',''var'') && exist(''mot_T_table'',''var'')');
            if ok
                src_used = 'motor_map.m (script)';
            end
        catch ME
            warning('load_motor_to_workspace:scriptFail', ...
                'motor_map.m fallito: %s', ME.message);
        end
    end

    %% Tentativo 3: generazione sintetica
    if isempty(src_used)
        warning('load_motor_to_workspace:synthetic', ...
            ['Mappa motore non trovata, uso placeholder sintetico.\n', ...
             '   Sostituisci appena hai il file vero.']);

        % Curva di coppia tipica di un motore elettrico:
        %   - coppia massima costante fino a ~base speed
        %   - decadimento iperbolico (potenza costante) oltre
        n_bp     = linspace(0, 9000, 91);                  % RPM
        alpha_bp = linspace(0, 100, 11);                   % %
        T_max    = 350;                                    % Nm
        n_base   = 3000;
        T_curve  = T_max * min(1, n_base ./ max(n_bp, 1));

        T_table  = zeros(numel(n_bp), numel(alpha_bp));
        for k = 1:numel(alpha_bp)
            T_table(:, k) = (alpha_bp(k) / 100) * T_curve;
        end

        assignin('base', 'mot_n_bp',     n_bp);
        assignin('base', 'mot_alpha_bp', alpha_bp);
        assignin('base', 'mot_T_table',  T_table);
        src_used = 'sintetica';
    end
%% ------------------------------------------------------------------------
%  Generazione mappa inversa: (rpm, T_req) -> alpha %
%  ------------------------------------------------------------------------
%  Mappa diretta:
%       T_motor = f(n_motor, alpha)
%
%  Mappa inversa:
%       alpha_trim = f_inv(n_motor, T_motor_req)
%
%  Variabili generate:
%       mot_Treq_bp           [1 x N_Treq] breakpoint coppia richiesta [Nm]
%       mot_alpha_inv_table   [N_n x N_Treq] alpha [%]
%       mot_Tmax_bp           [1 x N_n] coppia max disponibile a ogni rpm [Nm]

n_bp_v     = evalin('base', 'mot_n_bp');
alpha_bp_v = evalin('base', 'mot_alpha_bp');
T_table_v  = evalin('base', 'mot_T_table');

% Forza orientamento corretto
n_bp_v     = n_bp_v(:).';
alpha_bp_v = alpha_bp_v(:).';

% Controllo dimensioni tabella diretta
if size(T_table_v, 1) ~= numel(n_bp_v) || size(T_table_v, 2) ~= numel(alpha_bp_v)
    error('load_motor_to_workspace:badMotorMapSize', ...
        ['Dimensioni mot_T_table non coerenti.\n', ...
         'Atteso: [%d x %d], trovato: [%d x %d].'], ...
         numel(n_bp_v), numel(alpha_bp_v), size(T_table_v,1), size(T_table_v,2));
end

% Coppia massima disponibile a ogni rpm.
% Se alpha_bp è ordinato crescente 0..100, la colonna finale è alpha max.
[~, idx_alpha_max] = max(alpha_bp_v);
mot_Tmax_bp = T_table_v(:, idx_alpha_max).';

% Breakpoint globale di coppia richiesta.
% Uso un asse comune per tutti gli rpm.
T_max_global = max(T_table_v(:));
N_Treq = 201;
mot_Treq_bp = linspace(0, T_max_global, N_Treq);

mot_alpha_inv_table = zeros(numel(n_bp_v), numel(mot_Treq_bp));

for i = 1:numel(n_bp_v)

    % Curva a rpm fissato: T vs alpha
    T_vs_alpha = T_table_v(i, :);
    alpha_vec  = alpha_bp_v;

    % Protezione: rimuovi NaN/Inf
    valid = isfinite(T_vs_alpha) & isfinite(alpha_vec);
    T_vs_alpha = T_vs_alpha(valid);
    alpha_vec  = alpha_vec(valid);

    % Ordina per coppia crescente
    [T_sorted, idx_sort] = sort(T_vs_alpha);
    alpha_sorted = alpha_vec(idx_sort);

    % Rimuovi duplicati di coppia, necessari per interp1 inversa.
    % Se due alpha danno stessa coppia, tengo l'alpha più basso.
    [T_unique, idx_unique] = unique(T_sorted, 'stable');
    alpha_unique = alpha_sorted(idx_unique);

    % Se la curva è degenerata, fallback alpha = 0
    if numel(T_unique) < 2 || max(T_unique) <= 0
        mot_alpha_inv_table(i, :) = 0;
        continue;
    end

    % Interp inversa: T_req -> alpha
    alpha_inv = interp1(T_unique, alpha_unique, mot_Treq_bp, ...
                        'linear', 'extrap');

    % Saturazione:
    % - se T_req < Tmin -> alpha minimo
    % - se T_req > Tmax disponibile a quel rpm -> alpha massimo
    alpha_inv(mot_Treq_bp <= min(T_unique)) = min(alpha_unique);
    alpha_inv(mot_Treq_bp >= max(T_unique)) = max(alpha_unique);

    % Saturazione finale ai limiti alpha originali
    alpha_inv = min(max(alpha_inv, min(alpha_bp_v)), max(alpha_bp_v));

    mot_alpha_inv_table(i, :) = alpha_inv;
end

% Salva nel base workspace
assignin('base', 'mot_Treq_bp',         mot_Treq_bp);
assignin('base', 'mot_alpha_inv_table', mot_alpha_inv_table);
assignin('base', 'mot_Tmax_bp',         mot_Tmax_bp);
    %% Aggiorna anche il Model Workspace se il modello e' caricato
    MDL = 'swarm_phase3';
    if bdIsLoaded(MDL)
        mws = get_param(MDL, 'ModelWorkspace');
        mws.assignin('mot_n_bp',     evalin('base', 'mot_n_bp'));
        mws.assignin('mot_alpha_bp', evalin('base', 'mot_alpha_bp'));
        mws.assignin('mot_T_table',  evalin('base', 'mot_T_table'));
        %mappa inversa
        mws.assignin('mot_Treq_bp',         evalin('base', 'mot_Treq_bp'));
        mws.assignin('mot_alpha_inv_table', evalin('base', 'mot_alpha_inv_table'));
        mws.assignin('mot_Tmax_bp',         evalin('base', 'mot_Tmax_bp'));
    end

    n_bp_v     = evalin('base', 'mot_n_bp');
    alpha_bp_v = evalin('base', 'mot_alpha_bp');
    T_table_v  = evalin('base', 'mot_T_table');

    fprintf('[motor_map] caricata da: %s\n', src_used);
    fprintf('   n_bp:     [%d punti]   range %.0f - %.0f RPM\n', ...
        numel(n_bp_v), min(n_bp_v), max(n_bp_v));
    fprintf('   alpha_bp: [%d punti]   range %.0f - %.0f %%\n', ...
        numel(alpha_bp_v), min(alpha_bp_v), max(alpha_bp_v));
    fprintf('   T_table:  size %dx%d   range %.1f - %.1f Nm\n\n', ...
        size(T_table_v, 1), size(T_table_v, 2), ...
        min(T_table_v(:)), max(T_table_v(:)));
end