diff --git a/Analisys_With_Features.m b/Analisys_With_Features.m new file mode 100644 index 0000000000000000000000000000000000000000..b62aae33bd596a16a68f63adc197eb9c8f23e93e --- /dev/null +++ b/Analisys_With_Features.m @@ -0,0 +1,78 @@ +clear +close all +clc + +%% Setup + + warning('off', 'all'); format longG; + set(0,'DefaultTextInterpreter','latex'); + set(0,'DefaultAxesFontSize', 16); + +% Mac + restoredefaultpath + addpath('../useful_functions/'); + addpath('../dynamical_model/'); + addpath(genpath('../mice/')); + addpath(genpath('../computer-vision/')); + addpath(genpath('../paper/')); + addpath(genpath('../generic_kernels/')); + addpath(genpath('../paper/MMX_Fcn_CovarianceAnalyses/')); + addpath(genpath('../paper/MMX_Product/MMX_BSP_Files_GravLib/')); + MMX_InitializeSPICE + cspice_furnsh(which('mar097.bsp')); + cspice_furnsh(which('MARPHOFRZ.txt')); + cspice_furnsh(which('MMX_QSO_049_2x2_826891269_828619269.bsp')); + cspice_furnsh(which('Phobos_826891269_828619269.bsp')); + + +%% Load observations and path for syntethic pictures + + load("YObs.mat"); + + +%% Initial conditions for the analysis + +% Model parameters + [par, units] = MMX_DefineNewModelParametersAndUnits; + +% Time of the analysis + data = '2026-03-16 00:00:00 (UTC)'; + data = cspice_str2et(data); + day = 86400; + par.et0 = data; + [Ph,par] = Phobos_States_NewModel(data,par); + +% Covariance analysis parameters + [par, units] = CovarianceAnalysisParameters(par, units); + par.sigma = 1e-10/(units.vsf*units.tsf); + par.sigmaPh = 0/(units.vsf*units.tsf); + +% Initial Phobos's state vector + Ph0 = Ph./units.sfVec2; + +% Initial MMX's State vector + MMX0 = cspice_spkezr('-34', data, 'MarsIAU', 'none', '499'); + MMX0 = MMX0./units.sfVec; + +% Analysis initial state vector + St0 = [MMX0; Ph0; par.I2; par.bias]; + + Est0.X = St0; + Est0.dx = zeros(size(St0,1),1); + Est0.P0 = par.P0; + Est0.t0 = data*units.tsf; + +%% Analysis + + par.alpha = 1; + par.beta = 2; + file_features = 'Principal_Phobos_Ogni10.mat'; + + [Est] = UKF_Tool(Est0,@Dynamics_MPHandMMX_Inertia,... + @Cov_Dynamics_Good, @Observables_model_with_Features,... + par.R,YObs,par,units,file_features); + + + Results(Est, YObs, par, units) + + diff --git a/CovarianceAnalysisParameters.m b/CovarianceAnalysisParameters.m index 3c31b036b214aabfa784a99a48866a18121ad01c..0e1d8dfff9bd97253f09e1275690d230974ff529 100644 --- a/CovarianceAnalysisParameters.m +++ b/CovarianceAnalysisParameters.m @@ -1,7 +1,7 @@ function [pars, units] = CovarianceAnalysisParameters(pars, units) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -% [pars, units] = New_MMX_CovarianceAnalysisParameters(pars, units) +% [pars, units] = CovarianceAnalysisParameters(pars, units) % % This function adds some units and parameters useful to the covariance % analysis @@ -16,6 +16,9 @@ function [pars, units] = CovarianceAnalysisParameters(pars, units) % pars.ObsNoise.range_rate Standard deviation of the relative velocity measure % pars.ObsNoise.lidar Standard deviation of the lidar measure % pars.ObsNoise.camera Standard deviation of the camera measure +% pars.ObsNoise.pixel Standard deviation of the pixel error +% pars.camera_intrinsic Camera intrinsic matrix +% pars.FOV Camera Filed Of View % pars.interval Seconds between observations % pars.Clm Vector with all the Clm coefficients % pars.Slm Vector with all the Slm coefficients @@ -99,7 +102,7 @@ units.CovDim = (repmat(units.Dim, 1, length(units.Dim)).*... % A-priori covariance pars.P0 = diag(([.3*ones(3,1); .3e-3*ones(3,1);... - 1e-1; 1e-4; 1e-5; 1e-7; 3e-1; 1e-3; 3e-1; 1e-3; 1e-2*ones(pars.nCoeff,1);... + 1e-1; 1e-4; 1e-5; 1e-7; 1; 1e-2; 1e-1; 1e-3; 1e-2*ones(pars.nCoeff,1);... 1; 1e-3; 1]./units.Dim).^2); end \ No newline at end of file diff --git a/Measurement_read.m b/Measurement_read.m new file mode 100644 index 0000000000000000000000000000000000000000..89729c3d88144cf8868e5b76362eae32e22fc3a2 --- /dev/null +++ b/Measurement_read.m @@ -0,0 +1,68 @@ +function [Yobs, R, Stat_ID_Range, Stat_ID_Rate, check_Lidar, Features_ID] = Measurement_read(Measures,O,units) + + Y_obs = []; + R = []; + Stat_ID_Range = []; + Stat_ID_Rate = []; + Features_ID = []; + + if ~isempty(Measures.Range) + which = ~isnan(Measures.Range); + count = sum(~isnan(Measures.Range)); + + if ~isnan(Measures.Range(1)) + Stat_ID_Range = [Stat_ID_Range; 1]; + end + + if ~isnan(Measures.Range(2)) + Stat_ID_Range = [Stat_ID_Range; 2]; + end + + if ~isnan(Measures.Range(3)) + Stat_ID_Range = [Stat_ID_Range; 3]; + end + + Ranges = Measures.Range(which); + Y_obs = Ranges./units.lsf; + R = ones(count,1)*O(1,1); + + end + + if ~isempty(Measures.Rate) + which = ~isnan(Measures.Rate); + count = sum(~isnan(Measures.Rate)); + + if ~isnan(Measures.Rate(1)) + Stat_ID_Rate = [Stat_ID_Rate; 1]; + end + + if ~isnan(Measures.Rate(2)) + Stat_ID_Rate = [Stat_ID_Rate; 2]; + end + + if ~isnan(Measures.Rate(3)) + Stat_ID_Rate = [Stat_ID_Rate; 3]; + end + + Rates = Measures.Rate(which); + Y_obs = [Y_obs; Rates./units.vsf]; + R = [R; ones(count,1)*O(2,2)]; + end + + if ~isempty(Measures.Lidar) + check_Lidar = 1; + Lidar = Measures.Lidar; + Y_obs = [Y_obs; Lidar./units.lsf]; + R = [R; O(3,3)]; + end + + Yobs.meas = Y_obs; + + if ~isempty(Measures.Features) + Features_ID = Measures.Features(end,:); + Yobs.cam = Measures.Features; + else + Yobs.cam = []; + end + +end \ No newline at end of file diff --git a/Observables_model_with_Features.m b/Observables_model_with_Features.m new file mode 100644 index 0000000000000000000000000000000000000000..0443873024ea9a28819cef1ec75ff45cfa236fbc --- /dev/null +++ b/Observables_model_with_Features.m @@ -0,0 +1,189 @@ +function [G, Rm] = Observables_model_with_Features(et, X, St_ID_Range, St_ID_Rate,... + Lidar_check, Feature_ID, pars, units, O, file_features) +%========================================================================== +% [G, R] = Observables_model_with_Features(et,Obs,X,St_ID_Range,St_ID_Rate, Feature_ID, par,units) +% +% Compute the Range, Range Rate, Lidar's and camera's measures +% +% INPUT: Description Units +% +% et - Time Epoch s +% +% stationID - Available Observables at the epoch t +% +% X - State Vector defined in the Phobos rotating frame +% at the time being considered (42x1) +% .MMX Position Vector (3x1) km +% .MMX Velocity Vector (3x1) km/s +% .Phobos Position Vector (3x1) km +% .Phobos Velocity Vector (3x1) km/s +% .Clm harmonics coefficients +% .Slm Hamronics coefficients +% .State Transition Matrix +% +% par - Parameters structure +% +% +% OUTPUT: +% +% G - G +% +% DO NOT FORGET TO USE RESHAPE LATER IN THE PROJECT +% +% Coupling: +% None +% +% Author: Edoardo Ciccarelli +% Date: 06/2022 +% +%========================================================================== + +% Dimensions of the problem + n = pars.d/2; + d = pars.d; + +% Output initialization + range_Camb = []; + range_rate_Camb = []; + range_Gold = []; + range_rate_Gold = []; + range_Mad = []; + range_rate_Mad = []; + lidar = []; + + Rm = []; + +% Unpacking of the state vector + X_MMX = X(1:d); + r_MMX = X_MMX(1:3); + v_MMX = X_MMX(4:6); + r_Phobos = pars.perifocal2MARSIAU*X(d+1)*[cos(X(d+3)); sin(X(d+3)); 0]; + + switch size(X,1) + case 17 + bias = pars.bias; + case 18 + bias = X(16:end); + case 20 + bias = X(18:end); + end + +% Definition of Mars position WRT Earth J2000 + [Mars, ~] = cspice_spkezr('499',et,'MARSIAU','none','399'); + Mars = Mars./units.sfVec; + + +% Which station is working +% Camberra's Position in the J2000 Earth-centered + [Camb, ~] = cspice_spkezr('DSS-45', et, 'MARSIAU', 'none', 'EARTH'); + Camb = Camb./units.sfVec; +% Goldstone's Position in the J2000 Earth-centered + [Gold, ~] = cspice_spkezr('DSS-24', et, 'MARSIAU', 'none', 'EARTH'); + Gold = Gold./units.sfVec; +% Madrid's Position in the J2000 Earth-centered + [Mad, ~] = cspice_spkezr('DSS-65', et, 'MARSIAU', 'none', 'EARTH'); + Mad = Mad./units.sfVec; + + if sum((St_ID_Range == 1)==1) +% Camberra antennas available + r = norm(Mars(1:n) + r_MMX - Camb(1:n)); + range_Camb = r + bias(1); + Rm = [Rm; O(1,1)]; + end + + if sum((St_ID_Rate == 1)==1) +% Camberra antennas available + R = norm(Mars(1:n) + r_MMX - Camb(1:n)); + r_dot = 1/R*sum((Mars(n+1:d) + v_MMX - Camb(n+1:d)).*(Mars(1:n) + r_MMX - Camb(1:n))); + range_rate_Camb = r_dot + bias(2); + Rm = [Rm; O(2,2)]; + end + + if sum((St_ID_Range == 2)==1) +% Goldstone antennas available + r = norm(Mars(1:n) + r_MMX - Gold(1:n)); + range_Gold = r + bias(1); + Rm = [Rm; O(1,1)]; + end + + if sum((St_ID_Rate == 2)==1) +% Goldstone antennas available + R = norm(Mars(1:n) + r_MMX - Gold(1:n)); + r_dot = 1/R*sum((Mars(n+1:d) + v_MMX - Gold(n+1:d)).*(Mars(1:n) + r_MMX - Gold(1:n))); + range_rate_Gold = r_dot + bias(2); + Rm = [Rm; O(2,2)]; + end + + if sum((St_ID_Range == 3)==1) +% Madrid antennas available + r = norm(Mars(1:n) + r_MMX - Mad(1:n)); + range_Mad = r + bias(1); + Rm = [Rm; O(1,1)]; + end + + if sum((St_ID_Rate == 3)==1) +% Camberra antennas available + R = norm(Mars(1:n) + r_MMX - Mad(1:n)); + r_dot = 1/R*sum((Mars(n+1:d) + v_MMX - Mad(n+1:d)).*(Mars(1:n) + r_MMX - Mad(1:n))); + range_rate_Mad = r_dot + bias(2); + Rm = [Rm; O(2,2)]; + end + + if Lidar_check + +% Lidar rispetto alla superficie + I = r_Phobos/X(7); + k = pars.perifocal2MARSIAU*[0; 0; 1]; + j = cross(k,I); + NO = [I,j,k]; + ON = NO'; + + Xsi = X(13); + BO = [cos(Xsi), sin(Xsi), 0; -sin(Xsi), cos(Xsi), 0; 0, 0, 1]; + + rsb = r_MMX-r_Phobos; + +% Posizione di MMX nel Phobos Body-fixed reference frame + r_bf = BO*ON*rsb; + + lat = asin(r_bf(3)/norm(r_bf)); + lon = atan2(r_bf(2), r_bf(1)); + +% Phobos radius as function of latitude and longitude + alpha = pars.Phobos.alpha; + beta = pars.Phobos.beta; + gamma = pars.Phobos.gamma; + + R_latlon = (alpha*beta*gamma)/sqrt(beta^2*gamma^2*cos(lon)^2 +... + gamma^2*alpha^2*sin(lon)^2*cos(lat)^2 + alpha^2*beta^2*sin(lon)^2*sin(lat)); + + lidar = norm(rsb) - R_latlon/units.lsf + bias(3); + + Rm = [Rm; O(3,3)]; + + end + + if ~isempty(Feature_ID) + +% There should be features + [Sun, ~] = cspice_spkezr('10',et,'MARSIAU','none','499'); + + R2Rot = [cos(X(9)), sin(X(9)), 0; -sin(X(9)), cos(X(9)), 0; 0, 0, 1]; + v_Phobos = pars.perifocal2MARSIAU*R2Rot'*([X(8); 0; 0] + ... + cross([0; 0; X(10)],[X(7)*cos(X(9)); X(7)*sin(X(9)); 0])); + Phobos = [r_Phobos.*units.lsf; v_Phobos.*units.vsf]; + + [~, Y_pix] = visible_features(X_MMX.*units.sfVec, Phobos, Sun, Xsi, file_features, pars, units); + else + Y_pix = []; + end + +% Ready to exit + G.meas = [range_Camb; range_rate_Camb; range_Gold; range_rate_Gold; ... + range_Mad; range_rate_Mad; lidar]; + G.cam = Y_pix; + + G.meas(isnan(G.meas)) = []; + + +end \ No newline at end of file diff --git a/Observations_Generation.m b/Observations_Generation.m index 708b8df21bcf4d8954f867a42c9b6740f6628845..eb51eb651df9b5e61cacad9aa58e59359948a63e 100644 --- a/Observations_Generation.m +++ b/Observations_Generation.m @@ -23,11 +23,12 @@ clc cspice_furnsh(which('MMX_QSO_049_2x2_826891269_828619269.bsp')); cspice_furnsh(which('Phobos_826891269_828619269.bsp')); +%% Initial conditions per la analisi - % Time of the analysis - data = '2026-03-21 00:00:00 (UTC)'; +% Time of the analysis + data = '2026-03-16 00:00:00 (UTC)'; data = cspice_str2et(data); - Ndays = 1; + Ndays = 4; t = Ndays*86400; date_end = data+t; @@ -37,18 +38,15 @@ clc % Covariance analysis parameters pars.et0 = data; [pars, units] = CovarianceAnalysisParameters(pars, units); - + file_features = 'Principal_Phobos_Ogni10.mat'; % Positin of my target bodies at that date Sun = cspice_spkezr('10', data, 'J2000', 'none', 'MARS'); MMX = cspice_spkezr('-34', data, 'J2000', 'none', 'MARS'); Phobos = cspice_spkezr('-401', data, 'J2000', 'none', 'MARS'); - Phi = deg2rad(0); - -% [Y_LOS, Y_pix] = visible_features(MMX, Phobos, Sun, Phi,... -% 'Principal_Phobos_Ogni10.mat', par, units); + + +%% Creazione lista osservabili - file_features = 'Principal_Phobos_Ogni2.mat'; YObs = Observations_with_features(data, date_end, file_features, pars, units); - -% Plotfeatures_Pic(YObs(685).Features, pars) + save('YObs'); \ No newline at end of file diff --git a/Observations_with_features.m b/Observations_with_features.m index a72a4cbbdc1143e96d264d15990aa01e2fc3ada6..9207438495f71310b915695b5bbe8b837a06ef50 100644 --- a/Observations_with_features.m +++ b/Observations_with_features.m @@ -1,20 +1,23 @@ -function YObs = Observations_with_features(date0, date_end, file_features, pars, units) +function YObs = Observations_with_features(date_0, date_end, file_features, pars, units) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -% dx = Mars_Phobos_Circular_FullState(t,x,pars) +% YObs = Observations_with_features(date0, date_end, file_features, pars, units) % -% Calculate the acceleration vector in the N-Body Problem with Mars and -% Phobos only and the derivative of the coefficients to be estimated +% Creation of the structure containing the timestamp, of the observables +% and the available observables to be processed % % Input: % -% orbit String with the name of the type of orbit -% date0 Date of the beginning of the analysis -% date_end Date of the end of the analysis +% date0 Date of the beginning of the analysis +% date_end Date of the end of the analysis +% file_features Name of the file containing the features positions in +% the body-fixed reference frame +% pars Struct with useful parameters +% units Struct with useful units % % Output: % -% YObs_Full Vector of observations and time of observations +% Obs Struct of observations and time of observations % % Author: E.Ciccarelli % @@ -36,21 +39,36 @@ function YObs = Observations_with_features(date0, date_end, file_features, pars, sigma_range_rate = pars.ObsNoise.range_rate*units.vsf; sigma_lidar = pars.ObsNoise.lidar*units.lsf; +% Minimum interval between two different observables of the same kind + min_interval = min([interval_Range; interval_Range_rate;... + interval_lidar; interval_camera]); + + +%% Needed to find the Phobos libration angle at different timestamps + + [Ph, pars] = Phobos_States_NewModel(date_0,pars); + Ph = Ph./units.sfVec2; + St0 = [Ph; reshape(eye(pars.d2),[pars.d2^2,1])]; + tspan = (0:min_interval:date_end-date_0)*units.tsf; + RelTol = 1e-13; + AbsTol = 1e-16; + opt = odeset('RelTol',RelTol,'AbsTol',AbsTol); + [~,X] = ode113(@(t,X) MP_CoupledLibration(t,X,pars),tspan,St0,opt); + t = (0:min_interval:date_end-date_0); + Phi_t = X(:,7); + %% Observations - et0 = date0; - et_end = date_end; - + et0 = date_0; + et_end = date_end; + +% Inizzializzazione struttura YObs = []; - min_interval = min([interval_Range; interval_Range_rate; interval_lidar; interval_camera]); - - for i = et0:min_interval:et_end - got_it = 0; - + got_it = 0; %-------------------------------------------------------------------------- % LIDAR OBSERVABLES WRT PHOBOS SURFACE @@ -63,15 +81,15 @@ function YObs = Observations_with_features(date0, date_end, file_features, pars, if (norm(MMX(1:3)-Phobos(1:3)) < LIDARlimit) && (rem((i-et0),interval_lidar)==0) rPh = Phobos(1:3); - [Ph, pars] = Phobos_States_NewModel(i,pars); - I = rPh/Ph(1); + I = rPh/norm(rPh); k = pars.perifocal2MARSIAU*[0; 0; 1]; j = cross(k,I); NO = [I,j,k]; ON = NO'; - BO = [cos(Ph(end-1)), sin(Ph(end-1)), 0; -sin(Ph(end-1)), cos(Ph(end-1)), 0; 0, 0, 1]; + Phi = Phi_t(t == (i-et0)); + BO = [cos(Phi), sin(Phi), 0; -sin(Phi), cos(Phi), 0; 0, 0, 1]; rsb = MMX(1:3)-rPh; @@ -189,8 +207,11 @@ function YObs = Observations_with_features(date0, date_end, file_features, pars, Sun = cspice_spkezr('10', i, 'J2000', 'none', 'MARS'); MMX = cspice_spkezr('-34', i, 'J2000', 'none', 'MARS'); Phobos = cspice_spkezr('-401', i, 'J2000', 'none', 'MARS'); - Phi = deg2rad(0); + +% What's the Phobos libration angle at that t? + Phi = Phi_t(t == (i-et0)); +% Identification of the features [~, Y_pix] = visible_features(MMX, Phobos, Sun, Phi,... file_features, pars, units); got_it = 1; @@ -208,12 +229,19 @@ function YObs = Observations_with_features(date0, date_end, file_features, pars, if (got_it == 1) Obs.t = i-et0; - DSN = [R_Cam; R_dot_Cam; R_Gold; R_dot_Gold; R_Mad; R_dot_Mad]; - if sum(~isnan(DSN))>0 - Obs.DSN = [R_Cam; R_dot_Cam; R_Gold; R_dot_Gold; R_Mad; R_dot_Mad]; + Range = [R_Cam; R_Gold; R_Mad]; + if sum(~isnan(Range))>0 + Obs.Range = [R_Cam; R_Gold; R_Mad]; else - Obs.DSN = []; + Obs.Range = []; end + Rate = [R_dot_Cam; R_dot_Gold; R_dot_Mad]; + if sum(~isnan(Rate))>0 + Obs.Rate = [R_dot_Cam; R_dot_Gold; R_dot_Mad]; + else + Obs.Rate = []; + end + Obs.Lidar = Lidar; Obs.Features= Y_pix; YObs = [YObs; Obs]; diff --git a/PlotGeometryAndLight.m b/PlotGeometryAndLight.m index 2fd3a9b566a02968e3676e4874767fc7c107127a..47f5b32cc8f5b2e4678dd73ec1010aeb8b011b43 100644 --- a/PlotGeometryAndLight.m +++ b/PlotGeometryAndLight.m @@ -15,11 +15,12 @@ function PlotGeometryAndLight(points, point_in_light, visible, r_sb_Ph, r_PhSun, R_CV = R_bCam2CV*T; - figure() + figure(1) + hold off + plot3(points(1,:), points(2,:), points(3,:),'.','Color','b') + hold on grid on axis equal - hold on - plot3(points(1,:), points(2,:), points(3,:),'.','Color','b') plot3(point_in_light(1,:), point_in_light(2,:), point_in_light(3,:), 'x','Color','g') if ~isempty(visible) plot3(visible(1,:), visible(2,:), visible(3,:), 'o','Color','r') @@ -39,5 +40,4 @@ function PlotGeometryAndLight(points, point_in_light, visible, r_sb_Ph, r_PhSun, -r_sb_Ph(1)/units.tsf,-r_sb_Ph(2)/units.tsf,-r_sb_Ph(3)/units.tsf,'LineWidth',1.5,'Color','r') plotCamera('location',r_sb_Ph,'orientation',R_CV,'Color','r') - end \ No newline at end of file diff --git a/Results.m b/Results.m new file mode 100644 index 0000000000000000000000000000000000000000..7c2fb7e34704db9bcb4e112c2cb0f85d2c8bc4d4 --- /dev/null +++ b/Results.m @@ -0,0 +1,330 @@ +function Results(Est, YObs_Full, pars, units) +%========================================================================== +% NewCov_ResultsPlot(Est, YObs_Full, pars) +% +% This function plots the results of the covariance analysis +% +% INPUT: Description Units +% +% Est - Estimation of: +% .X, States at final t +% .x, States' deviation vector at final t +% .P, Covariance matrix at final t +% .t, Time observation vector +% .err, Post-fit residuals +% .pre, Pre-fit residuals +% .y_t, Pre-fit residuals +% .X_t, States at different t +% .x_t, States deviation vector at different t +% .Phi_t, STM at different t +% .P_t, Covariance matrix at different t +% .Pbar_t, Covariance matrix time update at different t +% +% YObs_Full Vector of observations and time of observations +% +% pars - Structure containing problem's parameters +% +% +% Coupling: +% None +% +% Author: Edoardo Ciccarelli +% Date: 09/2021 +% +%========================================================================== + + set(0,'DefaultTextInterpreter','latex'); + set(0,'DefaultAxesFontSize', 16); + + t_obs = (0:100:YObs_Full(end).t); + +% Plot of the post-fit residuals + +% figure(1) +% subplot(1,2,1) +% plot(t_obs/3600,Est.pre(1,:),'x','Color','b') +% grid on; +% hold on; +% plot(t_obs/3600,Est.pre(3,:),'x','Color','b') +% plot(t_obs/3600,Est.pre(5,:),'x','Color','b') +% plot(t_obs/3600,3*pars.ObsNoise.range*units.lsf*ones(size(Est.pre(1,:),2)),'.-','Color','b') +% plot(t_obs/3600,-3*pars.ObsNoise.range*units.lsf*ones(size(Est.pre(1,:),2)),'.-','Color','b') +% plot(t_obs/3600,3*pars.ObsNoise.range*units.lsf*ones(size(Est.pre(3,:),2)),'.-','Color','b') +% plot(t_obs/3600,-3*pars.ObsNoise.range*units.lsf*ones(size(Est.pre(3,:),2)),'.-','Color','b') +% plot(t_obs/3600,3*pars.ObsNoise.range*units.lsf*ones(size(Est.pre(5,:),2)),'.-','Color','b') +% plot(t_obs/3600,-3*pars.ObsNoise.range*units.lsf*ones(size(Est.pre(5,:),2)),'.-','Color','b') +% xlabel('$[hour]$') +% ylabel('$[km]$') +% title('Range Pre-fit residual') +% figure(2) +% subplot(1,2,1) +% plot(t_obs/3600,Est.pre(2,:),'x','Color','r') +% grid on; +% hold on; +% plot(t_obs/3600,Est.pre(4,:),'x','Color','r') +% plot(t_obs/3600,Est.pre(6,:),'x','Color','r') +% plot(t_obs/3600,3*pars.ObsNoise.range_rate*units.vsf*ones(size(Est.pre(2,:),2)),'.-','Color','r') +% plot(t_obs/3600,-3*pars.ObsNoise.range_rate*units.vsf*ones(size(Est.pre(2,:),2)),'.-','Color','r') +% plot(t_obs/3600,3*pars.ObsNoise.range_rate*units.vsf*ones(size(Est.pre(4,:),2)),'.-','Color','r') +% plot(t_obs/3600,-3*pars.ObsNoise.range_rate*units.vsf*ones(size(Est.pre(4,:),2)),'.-','Color','r') +% plot(t_obs/3600,3*pars.ObsNoise.range_rate*units.vsf*ones(size(Est.pre(6,:),2)),'.-','Color','r') +% plot(t_obs/3600,-3*pars.ObsNoise.range_rate*units.vsf*ones(size(Est.pre(6,:),2)),'.-','Color','r') +% xlabel('$[hour]$') +% ylabel('$[km/s]$') +% title('RangeRate Pre-fit residual') +% figure(3) +% subplot(1,2,1) +% plot(t_obs/3600,Est.pre(7,:),'x','Color','g') +% grid on; +% hold on; +% plot(t_obs/3600,3*pars.ObsNoise.lidar*units.lsf*ones(size(Est.pre(7,:),2)),'.-','Color','g') +% plot(t_obs/3600,-3*pars.ObsNoise.lidar*units.lsf*ones(size(Est.pre(7,:),2)),'.-','Color','g') +% xlabel('$[hour]$') +% ylabel('$[km]$') +% title('Lidar Pre-fit residual') +% figure(4) +% subplot(1,2,1) +% plot(t_obs/3600,Est.pre(8,:),'x','Color','m') +% grid on; +% hold on; +% plot(t_obs/3600,3*pars.ObsNoise.camera*ones(size(Est.pre(8,:),2)),'.-','Color','m') +% plot(t_obs/3600,-3*pars.ObsNoise.camera*ones(size(Est.pre(8,:),2)),'.-','Color','m') +% xlabel('$[hour]$') +% ylabel('$[rad]$') +% title('Camera Pre-fit residual') +% figure(5) +% subplot(1,2,1) +% plot(t_obs/3600,Est.pre(9,:),'x','Color','k') +% grid on; +% hold on; +% plot(t_obs/3600,3*pars.ObsNoise.camera*ones(size(Est.pre(9,:),2)),'.-','Color','k') +% plot(t_obs/3600,-3*pars.ObsNoise.camera*ones(size(Est.pre(9,:),2)),'.-','Color','k') +% xlabel('$[hour]$') +% ylabel('$[rad]$') +% title('Camera Pre-fit residual') + + +% figure(1) +% subplot(1,2,2) +% histfit([Est.pre(1,:), Est.pre(3,:), Est.pre(5,:)]) +% xlabel('$[km]$') +% title('Range Pre-fit residual') +% figure(2) +% subplot(1,2,2) +% histfit([Est.pre(2,:),Est.pre(4,:), Est.pre(6,:)]) +% xlabel('$[km/s]$') +% title('Range Rate Pre-fit residual') +% figure(3) +% subplot(1,2,2) +% histfit(Est.pre(7,:)) +% xlabel('$[km]$') +% title('Lidar Pre-fit residual') +% figure(4) +% subplot(1,2,2) +% histfit(Est.pre(8,:)) +% xlabel('$[rad]$') +% title('Camera Pre-fit residual') +% figure(5) +% subplot(1,2,2) +% histfit(Est.pre(9,:)) +% xlabel('$[rad]$') +% title('Camera Pre-fit residual') + +% Square-Root of the MMX covariance error + + sqx = squeeze(Est.P_t(1,1,1:end-1)); + sqy = squeeze(Est.P_t(2,2,1:end-1)); + sqz = squeeze(Est.P_t(3,3,1:end-1)); + SQRT_X = 3*sqrt(sqx + sqy + sqz); + sqvx = squeeze(Est.P_t(4,4,1:end-1)); + sqvy = squeeze(Est.P_t(5,5,1:end-1)); + sqvz = squeeze(Est.P_t(6,6,1:end-1)); + SQRT_V = 3*sqrt(sqvx + sqvy + sqvz); + + +% 3sigma Envelopes + figure() + subplot(1,2,1) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(1,1,1:end-1)))),'Color','b','LineWidth',1) + hold on; + grid on; + semilogy(t_obs(1:end-1)/3600,3*squeeze(real(sqrt(Est.P_t(2,2,1:end-1)))),'Color','r','LineWidth',1) + semilogy(t_obs(1:end-1)/3600,3*squeeze(real(sqrt(Est.P_t(3,3,1:end-1)))),'Color','g','LineWidth',1) + semilogy(t_obs(1:end-1)/3600,SQRT_X,'Color','k','LineWidth',2) + xlabel('time $[hour]$') + ylabel('$[km]$') + title('MMX position vector $3\sigma$ envelopes','Interpreter','latex','FontSize',16) + legend('$3\sigma_{x}$','$3\sigma_{y}$','$3\sigma_{z}$','$3 RMS$','Interpreter','latex','FontSize',14) + subplot(1,2,2) + semilogy(t_obs(1:end-1)/3600,3*squeeze(real(sqrt(Est.P_t(4,4,1:end-1)))),'Color','b','LineWidth',1) + hold on; + grid on; + semilogy(t_obs(1:end-1)/3600,3*squeeze(real(sqrt(Est.P_t(5,5,1:end-1)))),'Color','r','LineWidth',1) + semilogy(t_obs(1:end-1)/3600,3*squeeze(real(sqrt(Est.P_t(6,6,1:end-1)))),'Color','g','LineWidth',1) + semilogy(t_obs(1:end-1)/3600,SQRT_V,'Color','k','LineWidth',2) + xlabel('time $[hour]$') + ylabel('$[km/s]$') + title('MMX velocity vector $3\sigma$ envelopes','Interpreter','latex','FontSize',16) + legend('$3\sigma_{\dot{x}}$','$3\sigma_{\dot{y}}$','$3\sigma_{\dot{z}}$','$3 RMS$','Interpreter','latex','FontSize',14) + + +% Phobos's states uncertainties evolution + +% % 3sigma Envelopes + + figure() + subplot(2,4,1) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(7,7,1:end-1)))),'Color','b','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$R_{Ph}$ [km]') + subplot(2,4,2) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(9,9,1:end-1)))),'Color','r','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$\theta_{Ph}$ [rad]') + subplot(2,4,3) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(11,11,1:end-1)))),'Color','g','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$\Phi_{M}$ [rad]') + subplot(2,4,4) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(13,13,1:end-1)))),'Color','m','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$\Phi_{Ph}$ [rad]') + + subplot(2,4,5) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(8,8,1:end-1)))),'Color','b','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$\dot{R}_{Ph}$ [km/s]') + subplot(2,4,6) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(10,10,1:end-1)))),'Color','r','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$\dot{\theta}_{Ph}$ [rad/s]') + subplot(2,4,7) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(12,12,1:end-1)))),'Color','g','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$\dot{\Phi}_{M}$ [rad/s]') + subplot(2,4,8) + semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(14,14,1:end-1)))),'Color','m','LineWidth',1) + grid on + xlabel('time [hour]') + ylabel('$\dot{\Phi}_{Ph}$ [rad/s]') + +% Harmonics 3sigma Envelopes + +% place0 = size(Est.X,1)-pars.nCoeff-pars.nBias; + corr_label = {'$x$','$y$','$z$','$\dot{x}$','$\dot{y}$','$\dot{z}$',... + '$R_{Ph}$','$\dot{R}_{Ph}$','$\theta_{Ph}$','$\dot{\theta}_{Ph}$',... + '$\Phi_{M}$','$\dot{\Phi}_{M}$','$\Phi_{Ph}$','$\dot{\Phi}_{Ph}$'}; + + +% Moments of inertia +% figure() +% subplot(1,3,1) +% semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(15,15,1:end-1)))),'LineWidth',1); +% grid on; +% hold on; + corr_label{end+1} = ['$I_{PhX}$']; +% xlabel('time $[hour]$') +% legend('$I_{PhX}$','Interpreter','latex','FontSize',14) +% subplot(1,3,2) +% semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(16,16,1:end-1)))),'LineWidth',1); +% grid on; +% hold on; + corr_label{end+1} = ['$I_{PhY}$']; +% xlabel('time $[hour]$') +% legend('$I_{PhY}$','Interpreter','latex','FontSize',14) +% subplot(1,3,3) +% semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(16,16,1:end-1)))),'LineWidth',1); +% grid on; +% hold on; + corr_label{end+1} = ['$I_{PhZ}$']; +% xlabel('time $[hour]$') +% legend('$I_{PhZ}$','Interpreter','latex','FontSize',14) + + +% Biases + + place0 = size(Est.X,1)-pars.nBias; + corr_label(end+1) = {'$\rho$'}; + corr_label(end+1) = {'$\dot{\rho}$'}; + corr_label(end+1) = {'$LIDAR_b$'}; + +% figure() +% subplot(1,pars.nBias,1) +% semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(place0+1,place0+1,1:end-1)))),'LineWidth',1,... +% 'DisplayName',('$\rho$'),'Color','b'); +% grid on; +% hold on; +% xlabel('time $[hour]$') +% ylabel('$[km]$') +% subplot(1,pars.nBias,2) +% semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(place0+2,place0+2,1:end-1)))),'LineWidth',1,... +% 'DisplayName',('$\dot{\rho}$'),'Color','r'); +% grid on; +% hold on; +% xlabel('time $[hour]$') +% ylabel('$[km/s]$') +% subplot(1,pars.nBias,3) +% semilogy(t_obs(1:end-1)/3600,3.*squeeze(real(sqrt(Est.P_t(place0+3,place0+3,1:end-1)))),'LineWidth',1,... +% 'DisplayName',('$LIDAR_b$'),'Color','g'); +% grid on; +% hold on; +% xlabel('time $[hour]$') +% ylabel('$[km]$') + + +% +% % Harmonics Coefficients +% + P_t_I2x = squeeze(Est.P_t(15,15,1:end-1)); + P_t_I2y = squeeze(Est.P_t(16,16,1:end-1)); + P_t_I2z = squeeze(Est.P_t(17,17,1:end-1)); + Cov_I2xI2y = squeeze(Est.P_t(15,16,1:end-1)); + Cov_I2yI2z = squeeze(Est.P_t(16,17,1:end-1)); + Cov_I2xI2z = squeeze(Est.P_t(15,17,1:end-1)); + + P_t_C_20 = .25*(P_t_I2y + P_t_I2x - 2*Cov_I2xI2y); + P_t_C_22 = .5*(4*P_t_I2z + P_t_I2y + P_t_I2x - 4*Cov_I2xI2z - 4*Cov_I2yI2z + 2*Cov_I2xI2y); + + figure() + subplot(1,2,1) + semilogy(t_obs(1:end-1)/3600,3.*real(sqrt(P_t_C_20)),'LineWidth',1,'Color','b'); + grid on; + hold on; + xlabel('time $[hour]$') + legend('$C_{20}$','Interpreter','latex','FontSize',14) + subplot(1,2,2) + semilogy(t_obs(1:end-1)/3600,3.*real(sqrt(P_t_C_22)),'LineWidth',1,'Color','r'); + grid on; + hold on; + xlabel('time $[hour]$') + legend('$C_{22}$','Interpreter','latex','FontSize',14) + + + +% Correlations coefficients + + [~,corr] = readCovMatrix(real(Est.P)); + + figure(); + imagesc(real(corr)) + title('Correlation Coefficients','FontSize',16); + set(gca,'FontSize',16); + colormap(hot); + colorbar; + set(gca,'TickLabelInterpreter','latex'); + set(gca,'XTick',(1:size(Est.X,1))); + set(gca,'XTickLabel',corr_label); + set(gca,'YTick',(1:size(Est.X,1))); + set(gca,'YTickLabel',corr_label); + axis square; + freezeColors; + + +end \ No newline at end of file diff --git a/Test_Features.m b/Test_Features.m new file mode 100644 index 0000000000000000000000000000000000000000..b56068001f8410d8e03490be4760a31ac14411dc --- /dev/null +++ b/Test_Features.m @@ -0,0 +1,53 @@ +clear +close all +clc + +%% Setup + + warning('off', 'all'); format longG; + set(0,'DefaultTextInterpreter','latex'); + set(0,'DefaultAxesFontSize', 16); + +% Mac + restoredefaultpath + addpath('../useful_functions/'); + addpath(genpath('../mice/')); + addpath(genpath('../computer-vision/')); + addpath(genpath('../paper/')); + addpath(genpath('../generic_kernels/')); + addpath(genpath('../paper/MMX_Fcn_CovarianceAnalyses/')); + addpath(genpath('../paper/MMX_Product/MMX_BSP_Files_GravLib/')); + MMX_InitializeSPICE + cspice_furnsh(which('mar097.bsp')); + cspice_furnsh(which('MARPHOFRZ.txt')); + cspice_furnsh(which('MMX_QSO_049_2x2_826891269_828619269.bsp')); + cspice_furnsh(which('Phobos_826891269_828619269.bsp')); + +%% Initial conditions per la analisi + +% Time of the analysis + data = '2026-03-21 00:00:00 (UTC)'; + data = cspice_str2et(data); + Ndays = 1; + t = Ndays*86400; + date_end = data+t; + +% Model parameters + [pars, units] = MMX_DefineNewModelParametersAndUnits; + +% Covariance analysis parameters + pars.et0 = data; + [pars, units] = CovarianceAnalysisParameters(pars, units); + file_features = 'Principal_Phobos_Ogni10.mat'; + + for i=data:100:date_end + +% Positin of my target bodies at that date + Sun = cspice_spkezr('10', i, 'J2000', 'none', 'MARS'); + MMX = cspice_spkezr('-34', i, 'J2000', 'none', 'MARS'); + Phobos = cspice_spkezr('-401', i, 'J2000', 'none', 'MARS'); + Phi = 0; + + [Y_LOS, Y_pix] = visible_features(MMX, Phobos, Sun, Phi, file_features, pars, units); + + end \ No newline at end of file diff --git a/UKF_Tool.m b/UKF_Tool.m new file mode 100644 index 0000000000000000000000000000000000000000..2decf5485612b81c8c723773739cddb56e140227 --- /dev/null +++ b/UKF_Tool.m @@ -0,0 +1,301 @@ +function [Est] = UKF_Tool(Est0, f, fsigma, G, O, Measures, pars, units, file_features) +%========================================================================== +% +% [Est] = UKF_Tool(Est0,f,G_dG,R,Measures,par) +% +% +% INPUT: Description Units +% +% Est0 - Apriori initial guess of +% .X, States +% .P0, Covariance matrix +% .t0, initial time +% +% @f - Dynamical model +% +% @G - Observation's model and partial derivatives wrt the states +% +% O - Weight matrix +% Nsigma - Number of sigma points +% Measures - Observations matrix: the first column contains observation +% times, then every column has a different type of observaion +% type +% par - Structure containing parameters required by @f and @G_dG +% units - Structure containing lenght and time units +% +% OUTPUT: +% +% Est - Estimation of: +% .X, States at final t +% .P, Covariance matrix at final t +% .t, Time observation vector +% .X_t, States at different t +% .P_t, Covariance matrix at different t +% .Pbar_t, Covariance matrix time update at different t +% +% +% Coupling: +% None +% +% Author: Edoardo Ciccarelli +% Date: 04/2021 +% +%========================================================================== + + +% Apriori initial guess, deviation and covariance matrix. + Xold = Est0.X; + Pold = Est0.P0; + +% Column of the measures time. + told = Measures(1).t; + +% The measures must be organized in colums depending on the type of +% measure; each row is reffered to a given measure's time. + obs = Measures; + + +% n,l and m are respectively the number of states, number of different +% types of measures and number of measures per each type. + n = size(Xold,1); + m = size(obs,1); + q = 3; + +% err is the vector of residuals, the others are needed to build the +% fitting residuals. + err = NaN(size(O,1),m); + prefit = NaN(size(O,1),m); + y_t = NaN(size(O,1),m); + X_t = zeros(n,size(Measures,1)); + x_t = zeros(n,size(Measures,1)); + P_t = zeros(n,n,m); + Pbar_t = zeros(n,n,m); + +% Initialization of the filter + k = 3 - n; + alpha = pars.alpha; + lambda = alpha^2*(n+k) - n; + gamma = sqrt(n+lambda); + +% Sigma points' weights + W0c = zeros(2*n, 1); + W0c(:) = 1./(2*(n+lambda)); + +% Reference orbit propagation + interval_Range = pars.interval_Range; + interval_Range_rate = pars.interval_Range_rate; + interval_lidar = pars.interval_lidar; + interval_camera = pars.interval_camera; + min_interval = min([interval_Range; interval_Range_rate; interval_lidar; interval_camera]); + + switch n + case 14 + St0 = [Xold; pars.IPhx_bar; pars.IPhy_bar; pars.IPhz_bar]; + case 17 + St0 = Xold(1:17); + case 18 + St0 = pars.X0_reference; + case 20 + St0 = Xold(1:17); + end + + tspan = (0:min_interval:Measures(end).t)*units.tsf; + RelTol = 1e-13; + AbsTol = 1e-16; + opt = odeset('RelTol',RelTol,'AbsTol',AbsTol,'event',@(t,X) landing_Phobos(t,X,pars,units)); + [t,X] = ode113(@(t,X) f(t,X,pars,units),tspan,St0,opt); + fprintf('\nOrbit propagation complete...'); + + +% For-loop on the observations + for i = 1:m + +% Look for the observation to process + if Measures(i).t == told + idx = 1; + switch n + case 14 + Xmean_bar = X(idx,1:size(Est0.X,1))'; + case 17 + Xmean_bar = X(idx,:)'; + case 18 + Xmean_bar = [X(idx,1:10)'; X(idx,13:end)'; 0; 0; 0]; + pars.PhiM = X(idx,11); + case 20 + Xmean_bar = [X(idx,:)'; 0; 0; 0]; + end + elseif Measures(i).t > told + tobs = Measures(i).t; + idx = find(round(t/units.tsf)==tobs); + switch n + case 14 + Xmean_bar = X(idx,1:size(Est0.X,1))'; + case 17 + Xmean_bar = X(idx,:)'; + case 18 + Xmean_bar = [X(idx,1:10)'; X(idx,13:end)'; 0; 0; 0]; + case 20 + Xmean_bar = [X(idx,:)'; 0; 0; 0]; + end + end + +% Useful quantities's initialization + et = Est0.t0/units.tsf + Measures(i).t; + +% TIME UPDATE +% Sigma points' definition + S0 = real(sqrtm(Pold)); + X0 = [(repmat(Xold,1,n)-gamma.*S0),... + (repmat(Xold,1,n)+gamma.*S0)]; + +% Need to know how many observations are available + [Y_obs, R, Stat_ID_Range, Stat_ID_Rate, check_Lidar, Features_ID] = Measurement_read(Measures(i),O,units); + + + if Measures(idx).t == told + +% The sigma point dont need to be proppagated + Xbar = X0; + + elseif Measures(idx).t > told + +% Propagation of the sigma points' trajectories + St0 = reshape(X0,[n*(2*n),1]); + tspan = (told:Measures(idx).t)*units.tsf; + opt = odeset('RelTol',1e-13,'AbsTol',1e-16,'event',@(t,X) landing_Phobos_UKF(t,X,pars,units)); + [~,X_sigma] = ode113(@(t,X) fsigma(t,X,pars,units),tspan,St0,opt); + Xbar = reshape(X_sigma(end,1:n*(2*n))',[n,(2*n)]); + + end + + fprintf('\nProcesso osservazione n. : %d', i); + +% Process noise if needed + deltaT = (Measures(idx).t-told)*units.tsf; + Q = pars.sigma^2*eye(3); + switch n + case 14 + Gamma = [eye(q)*deltaT^2/2; eye(q)*deltaT; zeros(n-6,q)]; + Q = Gamma*Q*Gamma' + [zeros(6,n); zeros(n-6,6), pars.sigmaPh^2*... + diag([deltaT^2/2, deltaT, deltaT^2/2, deltaT, deltaT^2/2, deltaT, deltaT^2/2, deltaT])]; + case 17 + Gamma = [eye(q)*deltaT^2/2; eye(q)*deltaT; zeros(n-6,q)]; + Q = Gamma*Q*Gamma' + [zeros(6,n); zeros(n-6,6), pars.sigmaPh^2*... + diag([deltaT^2/2, deltaT, deltaT^2/2, deltaT, deltaT^2/2, deltaT, deltaT^2/2, deltaT, zeros(1,n-14)])]; + case 18 + Gamma = [eye(q)*deltaT^2/2; eye(q)*deltaT; zeros(n-6,q)]; + Q = Gamma*Q*Gamma' + [zeros(6,n); zeros(n-6,6), pars.sigmaPh^2*... + diag([deltaT^2/2, deltaT, deltaT^2/2, deltaT, deltaT^2/2, deltaT, zeros(1,n-12)])]; + case 20 + Gamma = [eye(q)*deltaT^2/2; eye(q)*deltaT; zeros(n-6,q)]; + Q = Gamma*Q*Gamma' + [zeros(6,n); zeros(n-6,6), pars.sigmaPh^2*... + diag([deltaT^2/2, deltaT, deltaT^2/2, deltaT, deltaT^2/2, deltaT, deltaT^2/2, deltaT, zeros(1,n-14)])]; + end + +% Covariance matrix's time update + P_bar = Q; + + for j = 1:2*n + P_bar = P_bar + W0c(j)*((Xbar(:,j) - Xmean_bar) * (Xbar(:,j) - Xmean_bar)'); + end + +% Sigma point redefinition + S_bar = real(sqrtm(P_bar)); + X0 = [(repmat(Xmean_bar,1,n)-gamma.*S_bar),... + (repmat(Xmean_bar,1,n)+gamma.*S_bar)]; + + Y = zeros(size(Y_obs.meas,1),2*n); + Y_feat = cell(2*n); + + for j = 1:2*n +% G(j-th sigma point) + [Y_sigma, ~] = G(et, X0(:,j), Stat_ID_Range, Stat_ID_Rate,... + check_Lidar, Features_ID, pars, units, O, file_features); + Y(:,j) = Y_sigma.meas; + Y_feat{j} = Y_sigma.cam; + + if ~isempty(Y_feat{j}) + Features_ID = intersect(Features_ID, Y_feat{j}(3,:)); + end + + end + + Y_sigma = []; + + if ~isempty(Y_feat{1}) + for j = 1:2*n + features = features_list(Y_feat{j}, Features_ID); + Y_sigma = [Y_sigma, [Y(:,j); features]]; + end + else + Y_sigma = Y; + end + +% Mean predicted measurement + [Y_mean,R] = G(et, Xmean_bar, Stat_ID_Range, Stat_ID_Rate,... + check_Lidar, Features_ID, pars, units, O, file_features); + + features= features_list(Y_mean.cam, Features_ID); + Y_mean = [Y_mean.meas; features]; + + features= features_list(Y_obs.cam, Features_ID); + Y_obs = [Y_obs.meas; features]; + +% Innovation covariance and cross covariance + + R = [R; O(4,4)*ones(size(features,1),1)]; + Py = diag(R); + Pxy = zeros(n,size(Y_obs,1)); + + for j = 1:2*n + Py = Py + W0c(j)*(Y_sigma(:,j) - Y_mean)*(Y_sigma(:,j) - Y_mean)'; + Pxy = Pxy + W0c(j)*(X0(:,j) - Xmean_bar)*(Y_sigma(:,j) - Y_mean)'; + end + +% Kalman gain + K = Pxy/Py; + +% MEASUREMENT UPDATE + y = (Y_obs - Y_mean); + Xstar = Xmean_bar + K*y; + P = (P_bar - K*Py*K'); + +% Next iteration preparation + Xold = Xmean_bar; + told = Measures(idx).t; + Pold = P; + + +% err(IDX,i) = (Y_obs - G(et, Xmean_bar, Stat_ID_Range, Stat_ID_Rate, check_Lidar, Features_ID, par,units)'); +% prefit(IDX,i) = (Y_obs - G(et, Xmean_bar, Stat_ID_Range, Stat_ID_Rate, check_Lidar, Features_ID, par,units)'); +% y_t(IDX,i) = y; + + +% Storing the results + P_t(:,:,i) = abs(real(P)).*units.CovDim; + Pbar_t(:,:,i) = P_bar.*units.CovDim; + X_t(:,i) = Xold.*units.Dim; + x_t(:,i) = K*y.*units.Dim; + + + end + + fprintf('\nAnalysis complete!\n'); + + + +% Initial states' estimation, deviation and covariance matrix + Est.X = Xold.*units.Dim; + Est.P = P.*units.CovDim; + +% Residuals and fit-residuals at different observation times + Est.t = tobs; + Est.err = err; + Est.pre = prefit; + Est.y_t = y_t; + Est.X_t = X_t; + Est.x_t = x_t; + Est.P_t = P_t; + Est.Pbar_t = Pbar_t; + +end \ No newline at end of file diff --git a/YObs.mat b/YObs.mat new file mode 100644 index 0000000000000000000000000000000000000000..b26142e8117fa866eed8dc4cdec26702598bb5d7 Binary files /dev/null and b/YObs.mat differ diff --git a/features_list.m b/features_list.m new file mode 100644 index 0000000000000000000000000000000000000000..58abdefbc001590af0517bec3f8754cb8b731800 --- /dev/null +++ b/features_list.m @@ -0,0 +1,11 @@ +function features = features_list(Y_feat, Features_ID) + + features = []; + for k=1:size(Features_ID,2) + common_feat = Y_feat(1:2, Y_feat(3,:)==Features_ID(k)); + if ~isempty(common_feat) + features = [features; common_feat]; + end + end + +end \ No newline at end of file diff --git a/visible_features.m b/visible_features.m index 4c437f4a7a6d5fa243a6264358147ad0c5eab602..a63cd7ed576002e5a7e08156c272ce543f86b845 100644 --- a/visible_features.m +++ b/visible_features.m @@ -13,7 +13,8 @@ function [Y_LOS, Y_pix] = visible_features(MMX, Phobos, Sun, Phi, file_features, % % Output: % -% Y Vector of features' pixel coordinates in the picture +% Y_LOS Vector of features' Line Of Sight and features' ID +% Y_pix Vector of features' position in the Pic and features' ID % % Author: E.Ciccarelli % @@ -34,7 +35,7 @@ function [Y_LOS, Y_pix] = visible_features(MMX, Phobos, Sun, Phi, file_features, % Rotation from J200 to Phobos radial reference frame - i_feat = Phobos(1:3)/norm(Phobos(1:3)); + i_feat = Phobos(1:3)/norm(Phobos(1:3)); v = Phobos(4:6)/norm(Phobos(4:6)); k = cross(i_feat, v)/norm(cross(i_feat, v)); j = cross(k, i_feat)/norm(cross(k, i_feat));