├── LICENSE ├── MATLAB ├── AtlasV411.m ├── AtlasV531.m ├── AtlasV532.m ├── CSEroutine.m ├── SpaceShuttle.m ├── SpaceShuttleThrustProfile.m ├── approxFromCurve.m ├── calculateAirDensity.m ├── createLaunchSite.m ├── dbg.m ├── dbgIntegrals.m ├── degtorad.m ├── earth_sphere.license ├── earth_sphere.m ├── easyCSE.m ├── evSLS.m ├── flightManager.m ├── flightSim2D.m ├── flightSim3D.m ├── getAngleFromFrame.m ├── getMaxValue.m ├── getOrbital.m ├── getOrbitalElements.m ├── getThrust.m ├── initSimulation.m ├── launchTargeting.m ├── linearFit.m ├── planeError.m ├── poweredExplicitGuidance.m ├── resultsToInit.m ├── rodrigues.m ├── telemetry.m ├── testAtlas.m ├── testOSIRIS.m ├── testShuttle.m ├── tightfig.license ├── tightfig.m ├── trajectory.m ├── unifiedPoweredFlightGuidance.m ├── unit.m └── vehicleTools.m ├── README.md └── docs ├── readme.md ├── simulation.md ├── tutorial.md └── upfg.md /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noiredd/PEGAS-MATLAB/26dba6a1b1385a794e7bc44259d6effcb2dfc5ff/LICENSE -------------------------------------------------------------------------------- /MATLAB/AtlasV411.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noiredd/PEGAS-MATLAB/26dba6a1b1385a794e7bc44259d6effcb2dfc5ff/MATLAB/AtlasV411.m -------------------------------------------------------------------------------- /MATLAB/AtlasV531.m: -------------------------------------------------------------------------------- 1 | clearvars vehicle 2 | %Atlas V 531 3 | % 3x Aerojet SRB + 1x RD-180 4 | % RD-180 (Common Core Booster) 5 | % RL-10C-1 (Centaur) 6 | %Data taken from: 7 | % http://spaceflight101.com/spacerockets/atlas-v-531/ 8 | % http://www.spacelaunchreport.com/atlas5.html#components 9 | % http://www.astronautix.com/a/atlasv.html (when in doubt) 10 | if ~exist('payload', 'var') 11 | payload = 14000; %certified max to LEO = 15575kg 12 | end 13 | scaleTo = 97; 14 | SpaceShuttleThrustProfile %use time-scaled STS SRB thrust profile 15 | 16 | %SRBs + RD-180 17 | stage_m0 = (21054+284089+285) + ... %CCB + fuel + interstage 18 | 3 * 46697 + ... %SRBs 19 | (2243+20830) + 4379 + ... %Centaur + long PLF 20 | payload; 21 | stage_engines(1) = struct('mode', 1,... 22 | 'isp0', 338,... 23 | 'isp1', 311,... 24 | 'flow', 3827000/(311*g0),... 25 | 'data', [0.470 1.000]); %RD-180 26 | stage_engines(2) = struct('mode', 2,...%%%%%%%%%%%%%%%%%%%%%% 27 | 'isp0', 275,... 28 | 'isp1', 245,... 29 | 'flow', 3*1688400/(245*g0),... 30 | 'data', thrustProfile); %3 SRBs 31 | stage_time = 94; 32 | stage_area = pi*(5.4/2)^2; 33 | stage_drag = [ 0.0 0.08; 34 | 250 0.08; 35 | 343 0.80; 36 | 999 0.50; 37 | 9999 0.40; ]; %not supported by any real data! 38 | stage = struct('MODE', 1,... 39 | 'm0', stage_m0,... 40 | 'engines', stage_engines,... 41 | 'maxT', stage_time,... 42 | 'gLim', 0,... 43 | 'area', stage_area,... 44 | 'drag', stage_drag); 45 | vehicle(1) = stage; 46 | 47 | stage_m0 = (21054+284089+285) + ... %CCB + fuel + interstage 48 | (2243+20830) + 4379 + ... %Centaur + long PLF 49 | payload; 50 | stage_engines(2) = []; 51 | stage.engines(2) = []; 52 | stage_m0 = stage_m0 - vehicleTools('mass', stage, 1, stage_time); 53 | stage_time = vehicleTools('tgo', stage, 1, 284089) - 94; 54 | stage_area = pi*(5.4/2)^2; 55 | stage = struct('MODE', 1,... 56 | 'm0', stage_m0,... 57 | 'engines', stage_engines,... 58 | 'maxT', stage_time,... 59 | 'gLim', 0,... 60 | 'area', stage_area,... 61 | 'drag', stage_drag); 62 | vehicle(2) = stage; 63 | 64 | stage_m0 = 2243+20830 + payload; %Centaur + payload 65 | stage_engines(1) = struct('mode', 1,... 66 | 'isp0', 449.7,... 67 | 'isp1', 449.7,... 68 | 'flow', 101800/(449.7*g0),... 69 | 'data', [1.000 1.000]); %RL-10C-1 70 | stage_area = pi*(3.05/2)^2; 71 | stage = struct('MODE', 1,... 72 | 'm0', stage_m0,... 73 | 'engines', stage_engines,... 74 | 'maxT', 0,... 75 | 'gLim', 0,... 76 | 'area', stage_area,... 77 | 'drag', stage_drag); 78 | stage.maxT = vehicleTools('tgo', stage, 1, 20830); 79 | vehicle(3) = stage; 80 | 81 | clearvars payload 82 | clearvars scaleTo thrustProfile 83 | clearvars stage_m0 stage_time stage_area stage_drag stage_engines stage -------------------------------------------------------------------------------- /MATLAB/AtlasV532.m: -------------------------------------------------------------------------------- 1 | clearvars vehicle 2 | %Atlas V 532 3 | % 3x Aerojet SRB + 1x RD-180 4 | % RD-180 (Common Core Booster) 5 | % 2x RL-10C-1 (Centaur) 6 | %Data taken from: 7 | % http://spaceflight101.com/spacerockets/atlas-v-531/ 8 | % https://en.wikipedia.org/wiki/Centaur_(rocket_stage) 9 | % http://www.astronautix.com/a/atlasv.html (when in doubt) 10 | payload = 17250; %certified max to LEO = 17250kg 11 | scaleTo = 97; 12 | SpaceShuttleThrustProfile %use time-scaled STS SRB thrust profile 13 | 14 | %SRBs + RD-180 15 | stage_m0 = (21054+284089+285) + ... %CCB + fuel + interstage 16 | 3 * 46697 + ... %SRBs 17 | (2462+20830) + 4379 + ... %Centaur + long PLF 18 | payload; 19 | stage_engines(1) = struct('mode', 1,... 20 | 'isp0', 338,... 21 | 'isp1', 311,... 22 | 'flow', 3827000/(311*g0),... 23 | 'data', [0.470 1.000]); %RD-180 24 | stage_engines(2) = struct('mode', 2,...%%%%%%%%%%%%%%%%%%%%%% 25 | 'isp0', 275,... 26 | 'isp1', 245,... 27 | 'flow', 3*1688400/(245*g0),... 28 | 'data', thrustProfile); %3 SRBs 29 | stage_time = 94; 30 | stage_area = pi*(5.4/2)^2; 31 | stage_drag = [ 0.0 0.08; 32 | 250 0.08; 33 | 343 0.80; 34 | 999 0.50; 35 | 9999 0.40; ]; %not supported by any real data! 36 | stage = struct('MODE', 1,... 37 | 'm0', stage_m0,... 38 | 'engines', stage_engines,... 39 | 'maxT', stage_time,... 40 | 'gLim', 0,... 41 | 'area', stage_area,... 42 | 'drag', stage_drag); 43 | vehicle(1) = stage; 44 | 45 | stage_m0 = (21054+284089+285) + ... %CCB + fuel + interstage 46 | (2462+20830) + 4379 + ... %Centaur + long PLF 47 | payload; 48 | stage_engines(2) = []; 49 | stage.engines(2) = []; 50 | stage_m0 = stage_m0 - vehicleTools('mass', stage, 1, stage_time); 51 | stage_time = vehicleTools('tgo', stage, 1, 284089) - 94; 52 | stage_area = pi*(5.4/2)^2; 53 | stage = struct('MODE', 1,... 54 | 'm0', stage_m0,... 55 | 'engines', stage_engines,... 56 | 'maxT', stage_time,... 57 | 'gLim', 0,... 58 | 'area', stage_area,... 59 | 'drag', stage_drag); 60 | vehicle(2) = stage; 61 | 62 | stage_m0 = 2462+20830 + payload; %Centaur + payload 63 | stage_engines(1) = struct('mode', 1,... 64 | 'isp0', 449.7,... 65 | 'isp1', 449.7,... 66 | 'flow', 198400/(449.7*g0),... 67 | 'data', [1.000 1.000]); %2x RL-10C-1 68 | stage_area = pi*(3.05/2)^2; 69 | stage = struct('MODE', 1,... 70 | 'm0', stage_m0,... 71 | 'engines', stage_engines,... 72 | 'maxT', 0,... 73 | 'gLim', 0,... 74 | 'area', stage_area,... 75 | 'drag', stage_drag); 76 | stage.maxT = vehicleTools('tgo', stage, 1, 20830); 77 | vehicle(3) = stage; 78 | 79 | clearvars payload 80 | clearvars scaleTo thrustProfile 81 | clearvars stage_m0 stage_time stage_area stage_drag stage_engines stage -------------------------------------------------------------------------------- /MATLAB/CSEroutine.m: -------------------------------------------------------------------------------- 1 | function [r, v, last] = CSEroutine(r0, v0, dt, last) 2 | %[r, v, cser] = CSEROUTINE(r0, v0, time, cser) 3 | %Implementation of Conic State Extrapolation Routine as described by 4 | %Shepperd and Robertson in Space Shuttle GN&C Equation Document 25. 5 | %Starting from the given vehicle state, extrapolates its state into the 6 | %future assuming only central gravity field with no other forces present. 7 | %This implementation has been simulation-proven to give correct results! 8 | % 9 | %REQUIRES 10 | % mu Global variable, standard gravity parameter of the body; 11 | % gravity constant * mass of the body (kg). 12 | % 13 | %INPUT 14 | % r0 Initial position relative to center of the Earth, XYZ 15 | % vector (m). 16 | % v0 Initial velocity, XYZ vector (m/s). 17 | % time Time of extrapolation - finds state so far into the future 18 | % (seconds). 19 | % cser Results of previous calculation (needs to be a correct 20 | % struct, can be filled with zeros if this is the first call). 21 | % 22 | %OUTPUT 23 | % r Future position (units like r0). 24 | % v Future velocity (units like v0). 25 | % cser Updated cser struct. 26 | 27 | %Reference on cser struct: 28 | % dtcp delta tc prim; previous converged value of transfer 29 | % time interval 30 | % xcp xc prim, previous converged value of x corresponding to tc 31 | % A, D, E Un function values 32 | 33 | if last.dtcp==0 34 | dtcp = dt; 35 | else 36 | dtcp = last.dtcp; 37 | end 38 | xcp = last.xcp; 39 | x = xcp; %independent variable for Kepler iteration scheme 40 | A = last.A; 41 | D = last.D; 42 | E = last.E; 43 | %Program constants 44 | kmax = 10; %U1 series iterator maximum value 45 | imax = 10; %Kepler iterator maximum values 46 | global mu; 47 | 48 | %5.1 ROUTINE 49 | %PLATE 5-2 (p24) 50 | if dt>=0 51 | f0 = 1; 52 | else 53 | f0 = -1; 54 | end 55 | 56 | n = 0; 57 | r0m = norm(r0); 58 | 59 | f1 = f0*sqrt(r0m/mu); 60 | f2 = 1/f1; 61 | f3 = f2/r0m; 62 | f4 = f1*r0m; 63 | f5 = f0/sqrt(r0m); 64 | f6 = f0*sqrt(r0m); 65 | 66 | ir0 = r0/r0m; 67 | v0s = f1*v0; %v0 vector with the silly dash on top 68 | sigma0s = dot(ir0,v0s); %sigma0 with the silly dash 69 | b0 = dot(v0s,v0s)-1; 70 | alphas = 1-b0; %alpha with the silly dash 71 | 72 | %PLATE 5-3 (p25) 73 | xguess = f5*x; 74 | xlast = f5*xcp; 75 | xmin = 0; 76 | dts = f3*dt; %delta t with the silly dash 77 | dtlast = f3*dtcp; %delta tlast with the silly dash 78 | dtmin = 0; %delta tmin with the silly dash 79 | 80 | %assuming sqrt(alphas) is never smaller than epsilon alpha 81 | %means orbit is not parabolic (why would it be?) 82 | 83 | xmax = 2*pi/sqrt(abs(alphas)); 84 | 85 | if alphas>0 86 | dtmax = xmax/alphas; 87 | xP = xmax; %xP with the silly dash 88 | Ps = dtmax; %P with the silly dash 89 | while dts>=Ps 90 | n = n+1; 91 | dts = dts-Ps; 92 | dtlast = dtlast-Ps; 93 | xguess = xguess-xP; 94 | xlast = xlast-xP; 95 | end 96 | else 97 | [dtmax, ~, ~, ~] = KTTI(xmax, sigma0s, alphas, kmax); 98 | if dtmax=xguess || xguess>=xmax 110 | xguess = 0.5*(xmin+xmax); 111 | end 112 | 113 | [dtguess, ~, ~, ~] = KTTI(xguess, sigma0s, alphas, kmax); 114 | 115 | if dts0). However, in the same path variable 135 | %n is never incremented above 0, so we can restate: 136 | if n>0 137 | xc = f6*(xguess+n*xP); 138 | dtc = f4*(dtguess+n*Ps); 139 | else 140 | xc = f6*xguess; 141 | dtc = f4*dtguess; 142 | end 143 | %Store converged values for the next run 144 | last.dtcp = dtc; 145 | last.xcp = xc; 146 | last.A = A; 147 | last.D = D; 148 | last.E = E; 149 | 150 | %Extrapolated State Vector (ROUTINE 5.3.6, PLATE 5-16 (p38) 151 | %Implemented inline for simplicity 152 | F = 1 - 2*A; 153 | Gs = 2*(D*E + sigma0s*A); %G with the silly dash 154 | Fts = -2*b4*D*E; %Ft with the silly dash 155 | Gt = 1 - 2*b4*A; 156 | 157 | r = r0m*(F*ir0 + Gs*v0s); 158 | v = f2*(Fts*ir0 + Gt*v0s); 159 | end 160 | 161 | function [t, A, D, E] = KTTI(xarg, s0s, a, kmax) 162 | %5.3.1 ROUTINE - Kepler Transfer Time Interval 163 | %PLATE 5-9 (p31) 164 | u1 = USS(xarg, a, kmax); 165 | 166 | zs = 2*u1; %z with the silly dash 167 | E = 1 - 0.5*a*zs^2; 168 | w = sqrt( max(0.5+E/2,0) ); %added safety check against sqrt from negative value 169 | D = w*zs; 170 | A = D^2; 171 | B = 2*(E+s0s*D); 172 | 173 | Q = QCF(w); 174 | 175 | t = D*(B+A*Q); 176 | end 177 | 178 | function [u1] = USS(xarg, a, kmax) 179 | %5.3.2 ROUTINE - U1 Series Summation 180 | %PLATE 5-10 (p32) 181 | du1 = xarg/4; 182 | u1 = du1; 183 | f7 = -a*du1^2; 184 | k=3; 185 | while k2 219 | j=j-1; 220 | b=y/(1+(j-1)/(j+2)*(1-b)); 221 | end 222 | 223 | Q = 1/w^2 * (1 + (2-b/2) / (3*w*(w+1))); 224 | end 225 | 226 | function [xguess, dtguess, A, D, E] = KIL(imax, dts, xguess, dtguess, xmin, dtmin, xmax, dtmax, s0s, a, kmax, A, D, E) 227 | %5.3.4 ROUTINE - Kepler Iteration Loop 228 | %input skips convergence criteria and "max representable scalar" 229 | %input adds previous values of A, D & E 230 | %PLATE 5-13 (p35) 231 | i = 1; 232 | while i=xmax 280 | dxs = (xguess-xmax)*(dterror/dtmaxp); 281 | end 282 | xmin = xguess; 283 | dtmin = dtguess; 284 | end 285 | end 286 | end -------------------------------------------------------------------------------- /MATLAB/SpaceShuttle.m: -------------------------------------------------------------------------------- 1 | clearvars vehicle 2 | %Full model of the Space Shuttle, 4 stages: 3 | % SSME + SRB 4 | % constant-thrust SSME mode 5 | % acceleration-limiting SSME mode 6 | % OMS 7 | %Data taken from: 8 | % http://www.braeunig.us/space/specs/shuttle.htm 9 | % http://www.braeunig.us/space/specs/orbiter.htm 10 | % http://www.astronautix.com/s/srb.html 11 | % https://en.wikipedia.org/wiki/Space_Shuttle_orbiter#Shuttle_Orbiter_Specifications_.28OV-105.29 12 | orbiter = 79135; %OV-105 Endeavour, full OMS/RCS fuel tanks 13 | if ~exist('payload', 'var') 14 | payload = 25000; %max for Endeavour 15 | end 16 | SpaceShuttleThrustProfile 17 | 18 | %SRB+SSME 19 | stage_m0 = 2*587000 + 765000 + orbiter + payload; %launch mass [kg] | SRBs + ET (35t structure, 730t fuel) + orbiter + payload 20 | stage_engines(1) = struct('mode', 1,... 21 | 'isp0', 452,... 22 | 'isp1', 366,... 23 | 'flow', 1462.7,... 24 | 'data', [0.670 1.045]); %3 SSMEs 25 | stage_engines(2) = struct('mode', 2,... 26 | 'isp0', 269,... 27 | 'isp1', 237,... 28 | 'flow', 2*13500000/(242*g0),... %approx. peak thrust from STS-107 mission plots 29 | 'data', thrustProfile); %2 SRBs 30 | stage_time = 124; %SRB burn time [s] 31 | stage_area = 2*10.8 + 55.4 + 15.2; %cross section [m2] 32 | stage_drag = [ 0.0 0.08; 33 | 250 0.08; 34 | 343 1.20; 35 | 999 0.50; 36 | 9999 0.40; ]; %drag curve - not supported by any real data! 37 | stage = struct('MODE', 1,... %constant thrust 38 | 'm0', stage_m0,... 39 | 'engines', stage_engines,... 40 | 'maxT', stage_time,... 41 | 'gLim', 0,... 42 | 'area', stage_area,... 43 | 'drag', stage_drag); 44 | vehicle(1) = stage; 45 | 46 | %SSME const-thrust 47 | stage_m0 = 765000 + orbiter + payload; 48 | stage.engines(2) = []; %SRBs were jettisoned 49 | stage_engines(2) = []; 50 | stage_m0 = stage_m0 - vehicleTools('mass', stage, 1, stage.maxT);%SSMEs burned some of the fuel in ET away 51 | stage_time = 320; %acceleration exceeds 3G after that time 52 | stage_area = 55.4 + 15.2; 53 | stage = struct('MODE', 1,... 54 | 'm0', stage_m0,... 55 | 'engines', stage_engines,... 56 | 'maxT', stage_time,... 57 | 'gLim', 0,... 58 | 'area', stage_area,... 59 | 'drag', stage_drag); 60 | vehicle(2) = stage; 61 | 62 | %SSME g-limited 63 | stage_m0 = stage_m0 - vehicleTools('mass', stage, 1, stage.maxT); 64 | stage_fuel = 730000 - vehicleTools('mass', stage, 1, 124+320); %fuel left after previous stages 65 | stage = struct('MODE', 2,... %constant acceleration 66 | 'm0', stage_m0,... 67 | 'engines', stage_engines,... 68 | 'maxT', 0,... 69 | 'gLim', 3,... %acceleration limit of 3 Gs 70 | 'area', stage_area,... 71 | 'drag', stage_drag); 72 | stage.maxT = vehicleTools('tgo', stage, 2, [stage_fuel stage.gLim]); 73 | vehicle(3) = stage; 74 | 75 | %OMS 76 | stage_m0 = orbiter + payload; 77 | stage_area = 15.2; 78 | stage_engines(1) = struct('mode', 1,... 79 | 'isp0', 313,... 80 | 'isp1', 313,... 81 | 'flow', 2*26700/(313*g0),... 82 | 'data', thrustProfile); 83 | stage = struct('MODE', 1,... 84 | 'm0', stage_m0,... 85 | 'engines', stage_engines,... 86 | 'maxT', 0,... 87 | 'gLim', 0,... 88 | 'area', stage_area,... 89 | 'drag', stage_drag); 90 | stage.maxT = vehicleTools('tgo', stage, 1, 8174+13486); %mass of MMH and N2O4 in OMS/RCS pods 91 | vehicle(4) = stage; 92 | 93 | clearvars orbiter payload thrustProfile 94 | clearvars stage_m0 stage_time stage_area stage_drag stage_fuel stage_engines stage -------------------------------------------------------------------------------- /MATLAB/SpaceShuttleThrustProfile.m: -------------------------------------------------------------------------------- 1 | %STS SRB thrust profile 2 | %Eye-balled from https://en.wikipedia.org/wiki/Space_Shuttle_Solid_Rocket_Booster#Ignition 3 | thrustProfile = [0 0.920; 4 | 10 0.987; 5 | 21 1.000; 6 | 27 0.918; 7 | 30 0.895; 8 | 34 0.852; 9 | 40 0.803; 10 | 42 0.787; 11 | 50 0.738; 12 | 52 0.738; 13 | 60 0.777; 14 | 63 0.787; 15 | 70 0.820; 16 | 77 0.830; 17 | 80 0.813; 18 | 84 0.787; 19 | 89 0.721; 20 | 95 0.689; 21 | 100 0.633; 22 | 104 0.590; 23 | 106 0.564; 24 | 110 0.541; 25 | 112 0.459; 26 | 115 0.262; 27 | 120 0.098; 28 | 124 0.033; 29 | 127 0.000]; 30 | if exist('scaleTo','var') 31 | scale_ = scaleTo / max(thrustProfile(:,1)); 32 | for i=1:length(thrustProfile) 33 | thrustProfile(i,1) = thrustProfile(i,1) * scale_; 34 | end 35 | end 36 | clearvars scale_ -------------------------------------------------------------------------------- /MATLAB/approxFromCurve.m: -------------------------------------------------------------------------------- 1 | function [y] = approxFromCurve(x, curve) 2 | %y = APPROXFROMCURVE(x, curve) 3 | %Interpolates a point on a curve given by list of control points. 4 | %If the input is between two points of the curve, output will be 5 | %calculated from the linear function fitted to those points. If the input 6 | %is out of range, extreme values will be output. 7 | % 8 | %INPUT 9 | % x Argument 10 | % curve Array of 2D (XY) points of shape (n,2) 11 | % 12 | %OUTPUT 13 | % y Linear approximation for the given argument. 14 | 15 | [n,~] = size(curve); 16 | 17 | %If the input is outside the given range, output the extreme value. 18 | if (x>curve(n,1)) 19 | y = curve(n,2); 20 | return; 21 | elseif (x x 29 | break; 30 | end; 31 | end; 32 | %Calculate a linear function coefficients between those points from the 33 | %given rearrangement: 34 | % m*x1+b = y1 35 | % m*x2+b = y2 36 | % y1-m*x1 = y2-m*x2 37 | % m(x2-x1) = y2-y1 38 | % m = (y2-y1)/(x2-x1) 39 | % b = y2 - m*x2 40 | m = ( curve(i,2)-curve(i-1,2) ) / ( curve(i,1)-curve(i-1,1) ); 41 | b = curve(i,2) - m*curve(i,1); 42 | 43 | y = m*x+b; -------------------------------------------------------------------------------- /MATLAB/calculateAirDensity.m: -------------------------------------------------------------------------------- 1 | function [density] = calculateAirDensity(pressure, temperature) 2 | %d = CALCULATEAIRDENSITY(pressure, temp) 3 | %Calculates air density from ideal gas law. 4 | % 5 | %INPUT 6 | % pressure Air pressure in Pascals 7 | % temp Air temperature in Kelvins 8 | % 9 | %OUTPUT 10 | % density Air density in kg/m^2 11 | 12 | R = 286.9; %specific gas constant for air [J/(kg*K)] 13 | density = pressure/(R*temperature); -------------------------------------------------------------------------------- /MATLAB/createLaunchSite.m: -------------------------------------------------------------------------------- 1 | function [ls] = createLaunchSite(name) 2 | %site = CREATELAUNCHSITE(name) 3 | %Outputs a properly formatted init struct from a given string representing 4 | %a launch site name. 5 | % 6 | %INPUT 7 | % name Short name of a launch site. Supported choices: 8 | % full name short name 9 | % ------------------------------------------------ 10 | % Kennedy Space Center KSC, Kennedy 11 | % Vandenberg Air Force Base Vandenberg 12 | % Guiana Space Centre Kourou 13 | % Baikonur Cosmodrome Baikonur 14 | % Plesetsk Cosmodrome Plesetsk 15 | % Uchinoura Space Center Uchinoura 16 | % RAAF Woomera Test Range Woomera 17 | % Jiuquan Satellite Launch Center Jiuquan 18 | % 19 | %OUTPUT 20 | % site Init struct of type 0 containing longitude, latitude and 21 | % altitude of a selected launch site. 22 | switch name 23 | case 'KSC' 24 | ls = struct('type', 0, 'lat', 28.52406, 'lon', -80.65085, 'alt', 0); 25 | case 'Kennedy' 26 | ls = createLaunchSite('KSC'); 27 | case 'Vandenberg' 28 | ls = struct('type', 0, 'lat', 34.75083, 'lon', -120.49778, 'alt', 0); 29 | case 'Kourou' 30 | ls = struct('type', 0, 'lat', 5.15972, 'lon', -52.65028, 'alt', 0); 31 | case 'Baikonur' 32 | ls = struct('type', 0, 'lat', 45.91194, 'lon', 63.31028, 'alt', 92); 33 | case 'Plesetsk' 34 | ls = struct('type', 0, 'lat', 62.92556, 'lon', 40.57778, 'alt', 131); 35 | case 'Uchinoura' 36 | ls = struct('type', 0, 'lat', 31.25194, 'lon', 131.08914, 'alt', 0); 37 | case 'Woomera' 38 | ls = struct('type', 0, 'lat', -30.94907, 'lon', 136.53418, 'alt', 137); 39 | case 'Jiuquan' 40 | ls = struct('type', 0, 'lat', 41.11803, 'lon', 100.46330, 'alt', 1045); 41 | otherwise 42 | ls = createLaunchSite('KSC'); 43 | fprintf('Unknown launch site - returning KSC.'); 44 | end -------------------------------------------------------------------------------- /MATLAB/dbg.m: -------------------------------------------------------------------------------- 1 | figure(9); clf; 2 | stage = STS.powered(3); 3 | N = stage.Plots.DEBUG.THIS; 4 | K = 5; 5 | t = stage.Plots.DEBUG.time(1:N); 6 | 7 | a = stage.Plots.DEBUG.L(1:N,:); 8 | b = stage.Plots.DEBUG.J(1:N,:); 9 | c = stage.Plots.DEBUG.S(1:N,:); 10 | 11 | show = 1; %how many items to show? 1 or 2 12 | dims = 1; %1 shows linear plot (of norm, if passed a 4D item), 3 shows 3D plot 13 | step = 0; %show dots for plot points (yes or not) 14 | 15 | s = size(a); 16 | if dims==3 && s(2)<3 17 | dims = 1; 18 | end; 19 | if dims==3 20 | axis vis3d; 21 | hold on; 22 | plot3(a(:,1), a(:,2), a(:,3), 'b'); 23 | if step 24 | scatter3(a(2:K-1,1), a(2:K-1,2), a(2:K-1,3), 'b'); 25 | scatter3(a((K+1):(N-5),1), a((K+1):(N-5),2), a((K+1):(N-5),3), 'b'); 26 | end; 27 | scatter3(a(1,1), a(1,2), a(1,3), 'm'); 28 | scatter3(a(K,1), a(K,2), a(K,3), 'k'); 29 | if show>1 30 | plot3(b(:,1), b(:,2), b(:,3), 'g'); 31 | if step 32 | scatter3(b(2:K-1,1), b(2:K-1,2), b(2:K-1,3), 'g'); 33 | scatter3(b((K+1):(N-5),1), b((K+1):(N-5),2), b((K+1):(N-5),3), 'g'); 34 | end; 35 | scatter3(b(1,1), b(1,2), b(1,3), 'm'); 36 | scatter3(b(K,1), b(K,2), b(K,3), 'k'); 37 | end; 38 | if show>2 39 | plot3(c(:,1), c(:,2), c(:,3), 'r'); 40 | if step 41 | scatter3(c(2:K-1,1), c(2:K-1,2), c(2:K-1,3), 'r'); 42 | scatter3(c((K+1):(N-5),1), c((K+1):(N-5),2), c((K+1):(N-5),3), 'r'); 43 | end; 44 | scatter3(c(1,1), c(1,2), c(1,3), 'm'); 45 | scatter3(c(K,1), c(K,2), c(K,3), 'k'); 46 | end; 47 | hold off; 48 | else 49 | hold on; 50 | s = size(a); 51 | if s(2)>3 52 | adim=4; 53 | else 54 | adim=1; 55 | end; 56 | s = size(b); 57 | if s(2)>3 58 | bdim=4; 59 | else 60 | bdim=1; 61 | end; 62 | s = size(c); 63 | if s(2)>3 64 | cdim=4; 65 | else 66 | cdim=1; 67 | end; 68 | plot(t, a(:,adim), 'b'); 69 | if step 70 | scatter(t(1), a(1,adim), 'm'); 71 | scatter(t(2:K-1), a(2:K-1,adim), 'b'); 72 | scatter(t((K+1):(N-5)), a((K+1):(N-5),adim), 'b'); 73 | end; 74 | scatter(t(K), a(K,adim), 'k'); 75 | if show>1 76 | plot(t, b(:,bdim), 'g'); 77 | if step 78 | scatter(t(1), b(1,bdim), 'm'); 79 | scatter(t(2:K-1), b(2:K-1,bdim), 'g'); 80 | scatter(t((K+1):(N-5)), b((K+1):(N-5),bdim), 'g'); 81 | end; 82 | scatter(t(K), b(K,bdim), 'k'); 83 | end; 84 | if show>2 85 | plot(t, c(:,cdim), 'r'); 86 | if step 87 | scatter(t(1), c(1,cdim), 'm'); 88 | scatter(t(2:K-1), c(2:K-1,cdim), 'r'); 89 | scatter(t((K+1):(N-5)), c((K+1):(N-5),cdim), 'r'); 90 | end; 91 | scatter(t(K), c(K,cdim), 'k'); 92 | end; 93 | hold off; 94 | end; 95 | 96 | clearvars N K t a b c show dims s adim bdim cdim stage step -------------------------------------------------------------------------------- /MATLAB/dbgIntegrals.m: -------------------------------------------------------------------------------- 1 | function [] = dbgIntegrals(stages, fid) 2 | %DBGINTEGRALS(results, figure) 3 | %Plots UPFG thrust integrals from up to 4 given stages. 4 | % 5 | %INPUT 6 | % results Array of struct of type results (as output by flightSim3D). 7 | % Each must contain a field 'Plots' being a struct with a 8 | % field 'DEBUG'. 9 | % figure ID of a figure in which to plot. 10 | % 11 | %OUTPUT 12 | % (none) 13 | 14 | figure(fid), clf; 15 | colors = ['b', 'r', 'g', 'y']; 16 | 17 | %Plot of the last stage ends abruptly (time is left unset at zero). 18 | %This has to be fixed, else plots get weird. 19 | maxes = [0]; 20 | for i=1:length(stages) 21 | A = stages(i).Plots.DEBUG.time; 22 | maxes(i) = find(A==max(A(:))); 23 | end 24 | 25 | subplot(2,4,1); hold on; 26 | for i=1:length(stages) 27 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.L(2:maxes(i)), colors(i)); 28 | end 29 | title('L integral'); 30 | hold off; 31 | 32 | subplot(2,4,2); hold on; 33 | for i=1:length(stages) 34 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.J(2:maxes(i)), colors(i)); 35 | end 36 | title('J integral'); 37 | hold off; 38 | 39 | subplot(2,4,3); hold on; 40 | for i=1:length(stages) 41 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.S(2:maxes(i)), colors(i)); 42 | end 43 | title('S integral'); 44 | hold off; 45 | 46 | subplot(2,4,5); hold on; 47 | for i=1:length(stages) 48 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.Q(2:maxes(i)), colors(i)); 49 | end 50 | title('Q integral'); 51 | hold off; 52 | 53 | subplot(2,4,6); hold on; 54 | for i=1:length(stages) 55 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.P(2:maxes(i)), colors(i)); 56 | end 57 | title('P integral'); 58 | hold off; 59 | 60 | subplot(2,4,7); hold on; 61 | for i=1:length(stages) 62 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.H(2:maxes(i)), colors(i)); 63 | end 64 | title('H integral'); 65 | 66 | subplot(2,4,4); hold on; 67 | for i=1:length(stages) 68 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.vgo2(2:maxes(i),4), colors(i)); 69 | end 70 | title('Vgo (2)'); 71 | 72 | subplot(2,4,8); hold on; 73 | for i=1:length(stages) 74 | plot(stages(i).Plots.DEBUG.time(2:maxes(i)), stages(i).Plots.DEBUG.tgo(2:maxes(i)), colors(i)); 75 | end 76 | title('Tgo'); 77 | hold off; -------------------------------------------------------------------------------- /MATLAB/degtorad.m: -------------------------------------------------------------------------------- 1 | function radians = degtorad(degrees) 2 | %radians = DEGTORAD(degrees) 3 | %Converts angle in degrees into radians. Useful to users who do not own the 4 | %Mapping Toolbox. 5 | % 6 | %INPUT 7 | % degrees Angle in degrees. 8 | % 9 | %OUTPUT 10 | % radians The same angle in radians. 11 | 12 | radians = degrees * pi/180; -------------------------------------------------------------------------------- /MATLAB/earth_sphere.license: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, The MathWorks, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | * Neither the name of the The MathWorks, Inc. nor the names 14 | of its contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MATLAB/earth_sphere.m: -------------------------------------------------------------------------------- 1 | function [xx,yy,zz] = earth_sphere(varargin) 2 | %EARTH_SPHERE Generate an earth-sized sphere. 3 | % [X,Y,Z] = EARTH_SPHERE(N) generates three (N+1)-by-(N+1) 4 | % matrices so that SURFACE(X,Y,Z) produces a sphere equal to 5 | % the radius of the earth in kilometers. The continents will be 6 | % displayed. 7 | % 8 | % [X,Y,Z] = EARTH_SPHERE uses N = 50. 9 | % 10 | % EARTH_SPHERE(N) and just EARTH_SPHERE graph the earth as a 11 | % SURFACE and do not return anything. 12 | % 13 | % EARTH_SPHERE(N,'mile') graphs the earth with miles as the unit rather 14 | % than kilometers. Other valid inputs are 'ft' 'm' 'nm' 'miles' and 'AU' 15 | % for feet, meters, nautical miles, miles, and astronomical units 16 | % respectively. 17 | % 18 | % EARTH_SPHERE(AX,...) plots into AX instead of GCA. 19 | % 20 | % Examples: 21 | % earth_sphere('nm') produces an earth-sized sphere in nautical miles 22 | % 23 | % earth_sphere(10,'AU') produces 10 point mesh of the Earth in 24 | % astronomical units 25 | % 26 | % h1 = gca; 27 | % earth_sphere(h1,'mile') 28 | % hold on 29 | % plot3(x,y,z) 30 | % produces the Earth in miles on axis h1 and plots a trajectory from 31 | % variables x, y, and z 32 | 33 | % Clay M. Thompson 4-24-1991, CBM 8-21-92. 34 | % Will Campbell, 3-30-2010 35 | % Copyright 1984-2010 The MathWorks, Inc. 36 | 37 | %% Input Handling 38 | [cax,args,nargs] = axescheck(varargin{:}); % Parse possible Axes input 39 | error(nargchk(0,2,nargs)); % Ensure there are a valid number of inputs 40 | 41 | % Handle remaining inputs. 42 | % Should have 0 or 1 string input, 0 or 1 numeric input 43 | j = 0; 44 | k = 0; 45 | n = 50; % default value 46 | units = 'km'; % default value 47 | for i = 1:nargs 48 | if ischar(args{i}) 49 | units = args{i}; 50 | j = j+1; 51 | elseif isnumeric(args{i}) 52 | n = args{i}; 53 | k = k+1; 54 | end 55 | end 56 | 57 | if j > 1 || k > 1 58 | error('Invalid input types') 59 | end 60 | 61 | %% Calculations 62 | 63 | % Scale factors 64 | Scale = {'km' 'm' 'mile' 'miles' 'nm' 'au' 'ft'; 65 | 1 1000 0.621371192237334 0.621371192237334 0.539956803455724 6.6845871226706e-009 3280.839895}; 66 | 67 | % Identify which scale to use 68 | try 69 | myscale = 6378.1363*Scale{2,strcmpi(Scale(1,:),units)}; 70 | catch %#ok<*CTCH> 71 | error('Invalid units requested. Please use m, km, ft, mile, miles, nm, or AU') 72 | end 73 | 74 | % -pi <= theta <= pi is a row vector. 75 | % -pi/2 <= phi <= pi/2 is a column vector. 76 | theta = (-n:2:n)/n*pi; 77 | phi = (-n:2:n)'/n*pi/2; 78 | cosphi = cos(phi); cosphi(1) = 0; cosphi(n+1) = 0; 79 | sintheta = sin(theta); sintheta(1) = 0; sintheta(n+1) = 0; 80 | 81 | x = myscale*cosphi*cos(theta); 82 | y = myscale*cosphi*sintheta; 83 | z = myscale*sin(phi)*ones(1,n+1); 84 | 85 | %% Plotting 86 | 87 | if nargout == 0 88 | cax = newplot(cax); 89 | 90 | % Load and define topographic data 91 | load('topo.mat','topo','topomap1'); 92 | 93 | % Rotate data to be consistent with the Earth-Centered-Earth-Fixed 94 | % coordinate conventions. X axis goes through the prime meridian. 95 | % http://en.wikipedia.org/wiki/Geodetic_system#Earth_Centred_Earth_Fixed_.28ECEF_or_ECF.29_coordinates 96 | % 97 | % Note that if you plot orbit trajectories in the Earth-Centered- 98 | % Inertial, the orientation of the contintents will be misleading. 99 | topo2 = [topo(:,181:360) topo(:,1:180)]; %#ok 100 | 101 | % Define surface settings 102 | props.FaceColor= 'texture'; 103 | props.EdgeColor = 'none'; 104 | props.FaceLighting = 'phong'; 105 | props.Cdata = topo2; 106 | 107 | % Create the sphere with Earth topography and adjust colormap 108 | surface(x,y,z,props,'parent',cax) 109 | colormap(topomap1) 110 | 111 | % Replace the calls to surface and colormap with these lines if you do 112 | % not want the Earth's topography displayed. 113 | % surf(x,y,z,'parent',cax) 114 | % shading flat 115 | % colormap gray 116 | 117 | % Refine figure 118 | axis equal 119 | xlabel(['X [' units ']']) 120 | ylabel(['Y [' units ']']) 121 | zlabel(['Z [' units ']']) 122 | view(127.5,30) 123 | else 124 | xx = x; yy = y; zz = z; 125 | end 126 | -------------------------------------------------------------------------------- /MATLAB/easyCSE.m: -------------------------------------------------------------------------------- 1 | function [r, v, last] = easyCSE(r0, v0, t, last) 2 | %[r, v, cser] = EASYCSE(r0, v0, time, cser) 3 | %Naive gravity integration, legacy code from the times where CSE had 4 | %issues. This works exactly the same (except 'cser' is unused). 5 | %See also CSEROUTINE. 6 | 7 | global mu; 8 | dt = 0.5; 9 | N = ceil(t/dt); 10 | %keep some precision minimum 11 | if N<150 12 | N = 150; 13 | dt = t/N; 14 | end 15 | 16 | v=v0; 17 | r=r0; 18 | for i=1:N 19 | v = v - dt*mu*r/norm(r)^3; 20 | r = r + dt*v; 21 | end -------------------------------------------------------------------------------- /MATLAB/evSLS.m: -------------------------------------------------------------------------------- 1 | %experimental vehicle creation model 2 | clearvars vehicle 3 | payload = 50000; 4 | %first stage SLS: core with 2 boosters 5 | stage_m0 = 1464000+1094000+141300+payload+8000; %launch mass [kg] SLS: boosters, core, EUS, payload, 8t fairing 6 | stage_thrust = 32300000+4*1860000; %thrust ASL [N] SLS: boosters+core 7 | stage_isp1 = 258.4; %ISP ASL [s] SLS: combined boosters+core 8 | stage_isp0 = 289.9; %ISP VAC [s] SLS: combined boosters+core assuming 2330kN core 9 | stage_time = 122; %burn time [s] SLS: fixed 10 | stage_area = 88; %cross section [m2] 11 | stage_drag = [ 0.0 0.08; 12 | 250 0.08; 13 | 343 1.00; 14 | 999 0.50; 15 | 9999 0.40; ]; %drag curve, randomly interpolated from maccolo's notes 16 | stage_dm = stage_thrust/(stage_isp1*g0); %mass flow rate [kg/s] 17 | stage = struct('m0', stage_m0,... 18 | 'dm', stage_dm,... 19 | 'i0', stage_isp0,... 20 | 'i1', stage_isp1,... 21 | 'mt', stage_time,... 22 | 'ra', stage_area,... 23 | 'dc', stage_drag); 24 | vehicle(1) = stage; 25 | %second stage: core, EUS, payload 26 | stage_m0 = 1094000-253000+141300+payload+8000; %core, EUS, payload, fairing 27 | stage_thrust = 4*1860000; 28 | stage_isp1 = 366; 29 | stage_isp0 = 453; 30 | stage_time = 347; %calculated manually 31 | stage_area = 57; 32 | stage_drag = [999 0.5; 9999 0.4; ]; %it will mostly run in VAC anyway 33 | stage_dm = stage_thrust/(stage_isp1*g0); 34 | stage = struct('m0', stage_m0,... 35 | 'dm', stage_dm,... 36 | 'i0', stage_isp0,... 37 | 'i1', stage_isp1,... 38 | 'mt', stage_time,... 39 | 'ra', stage_area,... 40 | 'dc', stage_drag); 41 | vehicle(2) = stage; 42 | %third stage: EUS 43 | stage_m0 = 141300+payload; %EUS + payload 44 | stage_thrust = 444000; 45 | stage_isp0 = 462; 46 | stage_isp1 = 462; 47 | stage_time = 1280; %no this is not a mistake. lol 48 | stage_area = 57; 49 | stage_drag = [999 0.5; 9999 0.4; ]; %copied over from s2, won't matter anyway 50 | stage_dm = stage_thrust/(stage_isp1*g0); 51 | stage = struct('m0', stage_m0,... 52 | 'dm', stage_dm,... 53 | 'i0', stage_isp0,... 54 | 'i1', stage_isp1,... 55 | 'mt', stage_time,... 56 | 'ra', stage_area,... 57 | 'dc', stage_drag); 58 | vehicle(3) = stage; 59 | clearvars payload stage_m0 stage_thrust stage_isp0 stage_isp1 stage_time stage_area stage_drag stage_dm stage -------------------------------------------------------------------------------- /MATLAB/flightManager.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noiredd/PEGAS-MATLAB/26dba6a1b1385a794e7bc44259d6effcb2dfc5ff/MATLAB/flightManager.m -------------------------------------------------------------------------------- /MATLAB/flightSim2D.m: -------------------------------------------------------------------------------- 1 | function [results] = flightSim2D(vehicle, initial, control, dt) 2 | %results = FLIGHTSIM2D(vehicle, initial, control, dt) 3 | %OBSOLETE 2DoF flight simulation. Left in repository for future purposes of 4 | %atmospheric ascent optimalization (2DoF is potentially faster than 3DoF). 5 | %Vehicle is modelled as a point mass with drag. Simulation is located in a 6 | %rotating frame of reference, so a centrifugal force appears. Uses OBSOLETE 7 | %vehicle structure (expects to receive only one stage of an array, does not 8 | %understand new engine struct). Atmosphere is modelled from RO data, but 9 | %only direct drag effects are modelled (no AoA or lift). 10 | % 11 | %REQUIRES 12 | % mu Global variable, standard gravity parameter of the body; 13 | % gravity constant * mass of the body (kg). 14 | % g0 Global variable, standard gravity acceleration (m/s). 15 | % R Global variable, radius of the body (m). 16 | % atmpressure Global variable, atmospheric pressure as a function of 17 | % altitude; array of size (n,2), altitude (kilometres above 18 | % sea level) in first column, pressure (atmospheres) in second. 19 | % atmtemperature Global variable, atmospheric temperature as a function 20 | % of altitude; array of size (n,2), altitude (kilometres above 21 | % sea level) in first column, temperature (Kelvins) in second. 22 | % 23 | %INPUT 24 | % vehicle Single struct of the obsolete vehicle type. 25 | % initial Struct of initial conditions type. 26 | % control Struct defining method of controlling the current stage. 27 | % Supports gravity turn, pitch program or PEG (type==3). 28 | % dt Simulation precision in seconds. 29 | % 30 | %OUTPUT 31 | % results Struct containing packed results of the simulation, from 32 | % short summary to orbital parameters and plots. 33 | % NO GUARANTEE THAT THOSE WILL BE COMPATIBLE WITH THE REST OF 34 | % THE PROGRAM. 35 | 36 | %declare globals 37 | global mu; global g0; global R; 38 | global atmpressure; global atmtemperature; 39 | 40 | %unpack vehicle data 41 | m = vehicle.m0; 42 | isp0 = vehicle.i0; 43 | isp1 = vehicle.i1; 44 | dm = vehicle.dm; 45 | maxT = vehicle.mt; 46 | engT = vehicle.et; 47 | area = vehicle.ra; 48 | dragcurve = vehicle.dc; 49 | 50 | %define control type 51 | if control.type == 0 52 | %type 0 = natural gravity turn simulation 53 | gtiP = control.p; %initial pitchover angle for gravity turn 54 | gtiV = control.v; %velocity at which the pitchover begins 55 | GT = 0; %gravity turn status flag: 0 - not begun yet; 1 - equaling to flight angle; 2 - match flight angle 56 | elseif control.type == 1 57 | %type 1 = pitch program control 58 | prog = control.program; 59 | elseif control.type == 2 60 | %type 2 = powered explicit guidance 61 | target = control.target*1000+R; %target orbit altitude 62 | ct = control.major; %length of the major loop 63 | lc = 0; %time since last PEG cycle 64 | ENG = 1; %engine state flag: 65 | %0 - fuel deprived; 66 | %1 - running; 67 | %2 - cut as scheduled by PEG 68 | elseif control.type == 3 69 | %type 3 = coast phase (unguided free flight) 70 | %strongly recommended using initial.type==1 71 | engT = 0; 72 | maxT = control.length; 73 | end 74 | 75 | %SIMULATION SETUP 76 | %simulate engine ignition effects 77 | m = m - engT*dm; %rocket is burning fuel while bolted to the launchpad for engT seconds before it's released 78 | maxT = maxT - engT; %this results in loss of propellant mass and hence reduction of maximum burn time 79 | N = floor(maxT/dt)+1;%simulation steps 80 | t = zeros(1,N); %simulation time 81 | Ca = 0; %centrifugal acceleration [m/s^2] (we use a cartesian non rotating frame of reference) 82 | F = zeros(1,N); %thrust [N] 83 | acc = zeros(1,N); %acceleration due to thrust [m/s^s] 84 | q = zeros(1,N); %dynamic pressure q [Pa] 85 | D = zeros(1,N); %drag [N] 86 | Ga = 0; %current gravity acceleration [m/s^2] 87 | vair = 0; %relative air velocity (subtracted for aero calculations) [m/s] 88 | vx = zeros(1,N); %horizontal (tangential) velocity (orbital) [m/s] 89 | vy = zeros(1,N); %vertical (radial) velocity (altitude change) [m/s] 90 | angleS = zeros(1,N);%velocity vector direction log [deg] (0 - straight up, 90 - due east) 91 | angleO = zeros(1,N);%angleS is related to surface (air) (undefined on launchpad), angleO is orbital (90 on launchpad) 92 | pitch = zeros(1,N); %vehicle orientation log [deg] (pitch commands) (0 - straight up, 90 - due east) 93 | vg = 0; %gravity losses (integrated) [m/s] 94 | vd = 0; %aerodynamic losses (integrated) [m/s] 95 | alt = zeros(1,N); %altitude (integral) [m] (measured from the surface) 96 | rad = zeros(1,N); %radial distance from launch point (eg. geographical longitude, for a GTO launch) [deg] 97 | 98 | %initialize simulation 99 | if initial.type==0 %launch from static position 100 | alt(1) = initial.alt; 101 | rad(1) = initial.lon; 102 | vair = (2*pi*R/24/3600)*cosd(initial.lat); %gained from Earth's rotational motion 103 | vx(1) = vair; 104 | angleS(1) = 0; 105 | angleO(1) = 90; 106 | elseif initial.type==1 %vehicle already in flight 107 | t(1) = initial.t; 108 | alt(1) = initial.alt; 109 | rad(1) = initial.rad; 110 | vx(1) = initial.vx; 111 | vy(1) = initial.vy; 112 | vair = initial.wind; 113 | angleS(1) = asind((vx(1)-vair) / sqrt((vx(1)-vair)^2+vy(1)^2)); 114 | angleO(1) = asind(vx(1) / sqrt(vx(1)^2+vy(1)^2)); 115 | end 116 | 117 | %PEG setup 118 | if control.type==2 119 | dbg = zeros(6,N); %debug log (A, B, C, sum, sum acos, T) 120 | %get effective exhaust velocity... 121 | p = approxFromCurve(alt(1)/1000, atmpressure); 122 | isp = (isp1-isp0)*p+isp0; 123 | ve = isp*g0; 124 | acc(1) = ve*dm/m; %...to find initial acceleration 125 | [A, B, C, T] = poweredExplicitGuidance(... 126 | 0,... 127 | alt(1)+R, vx(1), vy(1), target,... 128 | acc(1), ve,... 129 | 0, 0, maxT); 130 | pitch(1) = acosd(A + C); 131 | end 132 | 133 | %MAIN LOOP 134 | for i=2:N 135 | %PITCH CONTROL SECTION 136 | if control.type == 0 137 | %natural, lock-prograde gravity turn 138 | if vy(i-1) >= gtiV && GT == 0 139 | GT = 1; 140 | elseif angleS(i-1) > gtiP && GT == 1 141 | GT = 2; 142 | end; 143 | if GT == 0 144 | pitch(i) = 0; 145 | elseif GT == 1 146 | pitch(i) = min(pitch(i-1)+dt, gtiP); %hardcoded 1deg/s change (to simulate real, not instantaneous pitchover) 147 | else 148 | pitch(i) = angleS(i-1); 149 | end; 150 | elseif control.type == 1 151 | %pitch program control 152 | pitch(i) = approxFromCurve(t(i-1), prog); 153 | elseif control.type == 2 154 | %PEG pitch control 155 | %check if there's still fuel 156 | if (t(i)-t(1) > maxT && ENG > 0) 157 | ENG = 0; %engine ran out of fuel 158 | break; %exit the main simulation loop 159 | end; 160 | %check how long ago was the last PEG cycle 161 | if (lc < ct) 162 | %if not too long ago - increment 163 | lc = lc + dt; 164 | else 165 | %run PEG 166 | [A, B, C, T] = poweredExplicitGuidance(... 167 | lc,... 168 | alt(i-1)+R, vx(i-1), vy(i-1), target,... 169 | acc(i-1), isp*g0,...%use previously calculated acceleration and Isp 170 | A, B, T); %passing old T instead of T-dt IS CORRECT 171 | lc = 0; %think about not resetting this one if PEG skipped AB recalculation 172 | end; 173 | temp = A - B*lc + C; 174 | %PEG debug logs 175 | dbg(1,i) = A; 176 | dbg(2,i) = B; 177 | dbg(3,i) = C; 178 | dbg(4,i) = temp; 179 | dbg(5,i) = acosd( min(1, max(-1, temp)) ); %clamp to arcus cosinus domain (ideally should never be needed) 180 | dbg(6,i) = T-lc; 181 | %PEG-scheduled cutoff 182 | if (T-lc < dt && ENG == 1) 183 | ENG = 2; 184 | break; 185 | end; 186 | %pitch control (clamped same as debug entry 5) 187 | pitch(i) = acosd( min(1, max(-1, temp)) ); 188 | end 189 | 190 | %PHYSICS SIMULATION PART 191 | %get isp 192 | p = approxFromCurve(alt(i-1)/1000, atmpressure); %get pressure 193 | isp = (isp1-isp0)*p+isp0; 194 | %calculate thrust and acceleration 195 | if control.type==3 196 | %set thrust to zero for unpowered flight 197 | F(i) = 0; 198 | else 199 | F(i) = isp*g0*dm; 200 | end; 201 | acc(i) = F(i)/m; 202 | %new velocity 203 | dv = acc(i) * dt; 204 | vx(i) = vx(i-1) + dv*sind(pitch(i-1)); 205 | vy(i) = vy(i-1) + dv*cosd(pitch(i-1)); 206 | %centrifugal and gravitational acceleration 207 | Ca = vx(i-1)^2/(alt(i-1)+R); 208 | Ga = mu/(alt(i-1)+R)^2; 209 | vy(i) = vy(i) + (Ca - Ga) * dt; 210 | vg = vg + Ga * dt;%integrate gravity losses 211 | %aerodynamic losses (simplest way: no lift or AoA effects) 212 | %velocity related to air (so we subtract what we gained with 213 | %Earth's rotation because the air around us is rotating too) 214 | airv = vy(i-1)^2 + ((vx(i-1))-vair)^2; 215 | %we retain squared value for dynamic pressure calculation, for 216 | %drag coefficient we do separate ad-hoc root 217 | cd = approxFromCurve(sqrt(airv), dragcurve); 218 | temp = approxFromCurve(alt(i-1)/1000, atmtemperature)+273.15; 219 | dens = calculateAirDensity(p*101325, temp); 220 | q(i) = 0.5*dens*airv; %dynamic pressure 221 | D(i) = area*cd*q(i); %aerodynamic drag force (original formula: 0.5*A*cd*dens*airv^2) 222 | dv = D(i)/m * dt; %reuse an old variable, this time for a velocity decrement due to drag 223 | vx(i) = vx(i) - dv*sind(angleS(i-1)); 224 | vy(i) = vy(i) - dv*cosd(angleS(i-1)); 225 | vd = vd + dv; %integrate aerodynamic losses 226 | %velocity vector angles 227 | angleS(i) = asind((vx(i)-vair) / sqrt((vx(i)-vair)^2+vy(i)^2)); %surface-related 228 | angleO(i) = asind(vx(i) / sqrt(vx(i)^2+vy(i)^2)); %orbit-related 229 | %integrate altitude and radial distance 230 | alt(i) = alt(i-1) + vy(i)*dt; 231 | rad(i) = rad(i-1) + atan2d(vx(i)*dt, alt(i)+R); 232 | %update mass and time 233 | m = m - dm*dt; 234 | t(i) = t(i-1) + dt; 235 | %legacy crash check 236 | if alt(i) < 0 237 | break; 238 | end; 239 | end 240 | %prepare summary 241 | plots = struct('t', t(1:i-1),... 242 | 'F', F(1:i-1),... 243 | 'a', acc(1:i-1),... 244 | 'q', q(1:i-1),... 245 | 'D', D(1:i-1),... 246 | 'vx', vx(1:i-1),... 247 | 'vy', vy(1:i-1),... 248 | 'angle_ps', angleS(1:i-1),... 249 | 'angle_po', angleO(1:i-1),... 250 | 'pitch', pitch(1:i-1),... 251 | 'alt', alt(1:i-1),... 252 | 'rad', rad(1:i-1)); 253 | %add debug data if created 254 | if exist('dbg', 'var')==1 255 | plots().DEBUG = dbg; 256 | end; 257 | results = struct('Altitude', alt(i-1),... 258 | 'Apoapsis', 0, 'Periapsis', 0,... 259 | 'Eccentricity', 0,... 260 | 'Velocity', sqrt(vx(i-1)^2+vy(i-1)^2),... 261 | 'VelocityX', vx(i-1),... 262 | 'VelocityY', vy(i-1),... 263 | 'maxQv', 0, 'maxQt', 0,... 264 | 'Angle_srf', angleS(i-1),... 265 | 'Angle_obt', angleO(i-1),... 266 | 'LostGravity', vg,... 267 | 'LostDrag', vd,... 268 | 'LostTotal', vg+vd,... 269 | 'BurnTimeLeft',maxT-t(i-1)+t(1),... 270 | 'Plots', plots); 271 | [results.Apoapsis,results.Periapsis,results.Eccentricity] = getOrbital(vx(i-1), vy(i-1), alt(i-1)+R); %returns apoapsis and periapsis 272 | [results.maxQt, results.maxQv] = getMaxValue(q); %returns maxQ's occurence (index in time table) and value 273 | results.maxQt = t(results.maxQt); %format to seconds -------------------------------------------------------------------------------- /MATLAB/getAngleFromFrame.m: -------------------------------------------------------------------------------- 1 | function [angle] = getAngleFromFrame(vector, frame, type) 2 | %angle = GETANGLEFROMFRAME(vector, frame, type) 3 | %Calculates pitch or yaw angle of a vector in a given reference frame. 4 | % 5 | %INPUT 6 | % vector XYZ vector 7 | % frame 3x3 matrix of unit basis vectors given row-wise in the 8 | % following order: 9 | % local "up" (direction of zero pitch) 10 | % local "north" (direction of 90 degree yaw) 11 | % local "east" (direction of zero yaw) 12 | % type string, either 'pitch' or 'yaw' 13 | % 14 | %OUTPUT 15 | % angle requested angle in degrees 16 | 17 | vector = unit(vector); 18 | if strcmp(type, 'pitch') 19 | angle = safeAcosd(dot(vector, frame(1,:))); 20 | elseif strcmp(type, 'yaw') 21 | inplane = vector - frame(1,:)*dot(vector, frame(1,:)); 22 | inplane = unit(inplane); 23 | angle = safeAcosd(dot(inplane, frame(3,:))); 24 | %correct for direction of the angle 25 | tangential = cross(frame(1,:), frame(3,:)); 26 | if dot(inplane, tangential) < 0 27 | angle = -angle; 28 | end; 29 | else 30 | disp('Unknown parameter (getAngleFromFrame).\n'); 31 | angle = 0; 32 | end 33 | if abs(imag(angle))>0 34 | disp('-'); 35 | end 36 | end 37 | 38 | function [a] = safeAcosd(angle) 39 | angle = min(angle, 1); 40 | angle = max(angle,-1); 41 | a = acosd(angle); 42 | end -------------------------------------------------------------------------------- /MATLAB/getMaxValue.m: -------------------------------------------------------------------------------- 1 | function [max_i, max_val] = getMaxValue(array) 2 | %[index, value] = GETMAXVALUE(array) 3 | %Finds (the first occurence of) maximum value in a 1D array. 4 | % 5 | %INPUT 6 | % array 1D array (there is no safety check on that) 7 | % 8 | %OUTPUT 9 | % index Index of the maximum element. 10 | % value Value of the maximum element. 11 | 12 | max_val = max(array); 13 | idx = 0; 14 | for i=1:length(array) 15 | if array(i)==max_val 16 | idx = i; 17 | break; 18 | end; 19 | end; 20 | max_i = idx; -------------------------------------------------------------------------------- /MATLAB/getOrbital.m: -------------------------------------------------------------------------------- 1 | function [Ap, Pe, Ecc] = getOrbital(vx, vy, alt) 2 | %[Ap, Pe, Ecc] = GETORBITAL(vx, vy, alt) 3 | %Calculates orbital elements of a 2D orbit. Currently unused feature. 4 | % 5 | %REQUIRES 6 | % mu Global variable, standard gravity parameter of the body; 7 | % gravity constant * mass of the body (kg). 8 | % R Global variable, radius of the body (m). 9 | % 10 | %INPUT 11 | % vx Horizontal velocity (m/s). 12 | % vy Vertical velocity (m/s). 13 | % alt Altitude from Earth's center (m). 14 | % 15 | %OUTPUT 16 | % Ap Apoapsis from the body's surface (km). 17 | % Pe Periapsis from the body's surface (km). 18 | % Ecc Eccentricity 19 | 20 | global mu; global R; 21 | %at any given point on the ellipse: v = sqrt( mu*( 2/r-1/a ) ) 22 | v = sqrt(vx^2 + vy^2); 23 | %a - semimajor axis: 1/a = 2/r - v^2/mu 24 | sma = 1/( 2/alt - v^2/mu ); 25 | %specific orbital energy 26 | soe = -mu/(2*sma); 27 | %from orbital eccentricity equations: e = sqrt( 1 + 2*soe*h^2/mu^2 ) 28 | %h - specific relative angular momentum: h = r*v 29 | srh = norm(cross([alt 0 0], [vy; vx; 0])); 30 | Ecc = sqrt(1 + 2*soe*srh^2 / mu^2); 31 | %apsi distances are given: r_a/p = a*( 1 +/- e ) 32 | Ap = sma*(1+Ecc); %raw value 33 | Ap = (Ap-R)/1000; %return in [km] 34 | Pe = sma*(1-Ecc); 35 | Pe = (Pe-R)/1000; 36 | end -------------------------------------------------------------------------------- /MATLAB/getOrbitalElements.m: -------------------------------------------------------------------------------- 1 | function [ap, pe, sma, ecc, inc, lan, aop, tan] = getOrbitalElements(r, v) 2 | %[ap, pe, sma, ecc, inc, lan, aop, tan] = GETORBITALELEMENTS(r, v) 3 | %Calculates complete set of Keplerian elements of a 3D orbit given by 4 | %position and velocity vectors. Equations taken from: 5 | %https://en.wikibooks.org/wiki/Astrodynamics/Classical_Orbit_Elements 6 | %http://space.stackexchange.com/a/1919 7 | % 8 | %REQUIRES 9 | % mu Global variable, standard gravity parameter of the body; 10 | % gravity constant * mass of the body (kg). 11 | % R Global variable, radius of the body (m). 12 | % 13 | %INPUT 14 | % r Position XYZ vector relative to the center of reference 15 | % frame (m). 16 | % v Velocity XYZ vector (m/s). 17 | % 18 | %OUTPUT 19 | % ap Apoapsis from the body's surface (km). 20 | % pe Periapsis from the body's surface (km). 21 | % sma Semi-major axis (m). 22 | % ecc Eccentricity 23 | % inc Inclination (deg). 24 | % lan Longitude of ascending node (deg). 25 | % aop Argument of periapsis (deg). 26 | % tan True anomaly (deg). 27 | 28 | global mu; global R; 29 | %angular momentum 30 | h = cross(r, v); 31 | %ascending node 32 | n = cross([0 0 1], h); 33 | %specific mechanical energy 34 | E = norm(v)^2/2-mu/norm(r); 35 | %semi-major axis 36 | sma = -0.5*mu/E; 37 | %eccentricity vector 38 | e = (norm(v)^2/mu - 1/norm(r))*r - dot(r, v)/mu*v; 39 | ecc = norm(e); 40 | %inclination 41 | inc = acosd(dot([0 0 1], h)/norm(h)); 42 | %longitude of the ascending node 43 | lan = acosd(dot([1 0 0], n)/norm(n)); 44 | if n(2)<0 45 | lan = 360-lan; 46 | end; 47 | %argument of periapsis 48 | aop = acosd(dot(n, e)/(norm(n)*norm(e))); 49 | if e(3)<0 50 | aop = 360-aop; 51 | end; 52 | %true anomaly 53 | tan = acosd(dot(e, r)/(norm(e)*norm(r))); 54 | if dot(r,v)<0 55 | tan = 360-tan; 56 | end; 57 | %Ap, Pe 58 | ap = (sma*(1+ecc)-R)/1000; 59 | pe = (sma*(1-ecc)-R)/1000; 60 | end -------------------------------------------------------------------------------- /MATLAB/getThrust.m: -------------------------------------------------------------------------------- 1 | function [F, dm, isp] = getThrust(engines, pressure, time) 2 | %[F, dm, Isp] = GETTHRUST(engines, pressure, time) 3 | %Calculates thrust and closely related parameters for the given set of 4 | %engines at given pressure. Supports engines with constant thrust (eg. 5 | %liquid) and those with thrust profile (eg. solid rocket motors). 6 | % 7 | %REQUIRES 8 | % g0 Global variable, standard gravity acceleration (m/s). 9 | % 10 | %INPUT 11 | % engines Array of struct of type engine. 12 | % pressure Atmospheric pressure (Pascals). 13 | % time Time since ignition of the engines (seconds). 14 | % 15 | %OUTPUT 16 | % F Combined thrust (Newtons). 17 | % dm Combined mass flow rate (kg/s). 18 | % Isp Combined specific impulse (seconds). 19 | 20 | global g0; 21 | n = length(engines); 22 | p = pressure; 23 | t = time; 24 | F = 0; 25 | dm = 0; 26 | for i=1:n 27 | isp1 = engines(i).isp1; 28 | isp0 = engines(i).isp0; 29 | isp = (isp1-isp0)*p+isp0; 30 | if engines(i).mode==1 31 | dm_ = engines(i).flow; 32 | elseif engines(i).mode==2 33 | dm_ = approxFromCurve(t, engines(i).data) * engines(i).flow; 34 | end 35 | dm = dm + dm_; 36 | F = F + isp*dm_*g0; 37 | end 38 | isp = F/(dm*g0); 39 | end -------------------------------------------------------------------------------- /MATLAB/initSimulation.m: -------------------------------------------------------------------------------- 1 | %initSimulation.m 2 | %Initialization module. Cleans up some vehicle-related variables, sets up 3 | %orbiting body (Earth) parameters and atmosphere curves (data from 4 | %RealSolarSystem\RSSKopernicus.cfg). 5 | 6 | global g0; %std gravity asl [m/s2] 7 | g0 = 9.80665; 8 | 9 | global mu; %std gravity param for Earth [m3/s2] 10 | mu = 398600441800000; 11 | 12 | global R; %Earth radius [m] 13 | R = 6371000; 14 | 15 | global period; %Earth rotation period [s] 16 | period = 24*3600; 17 | 18 | global convergenceCriterion; %for UPFG 19 | convergenceCriterion = 0.01; 20 | 21 | %Thrust profiles are done somewhat globally, basing on STS SRB profile. 22 | %Generator function checks for scaleTo variable to automatically scale the 23 | %profile in time if we want to make a faster or slower burning SRB. 24 | %Once set, this variable might affect other vehicles, so best to delete it. 25 | clearvars scaleTo 26 | 27 | %Payloads are done in a similar way, so... 28 | clearvars payload 29 | 30 | %altitude [km] vs pressure [atm] 31 | if ~exist('atmpressure', 'var') 32 | global atmpressure; 33 | atmpressure = [ 0.0 1.0 ; 34 | 1.5512810696042834 0.8293033334108524 ; 35 | 3.049490512617914 0.6877017510446373 ; 36 | 4.496387604836978 0.5702444163147664 ; 37 | 5.893677091578151 0.47282126627696414 ; 38 | 7.243010623061255 0.3920205567378669 ; 39 | 8.545988169576226 0.3250104789672165 ; 40 | 9.804159415369073 0.26944079326066694 ; 41 | 11.01902513031035 0.2233611050921582 ; 42 | 11.84014685339708 0.19632505252876972 ; 43 | 12.661479569463696 0.17256150402323664 ; 44 | 13.483023359760763 0.1516743473174663 ; 45 | 14.304778305580532 0.1333154167235328 ; 46 | 15.12674448825698 0.1171786895168694 ; 47 | 15.948921989165822 0.10299518481855771 ; 48 | 16.771310889724543 0.09052847993471885 ; 49 | 17.593911271392436 0.07957076941364262 ; 50 | 18.416723215670608 0.06993940112805067 ; 51 | 19.239746804102023 0.06147383164159355 ; 52 | 20.062982118274434 0.05403295010784879 ; 53 | 20.848089497565947 0.04778939820639058 ; 54 | 21.63620936661535 0.042267349078245586 ; 55 | 22.42735394519108 0.037383419911108245 ; 56 | 23.221535507546527 0.03306386499304308 ; 57 | 24.018766382699184 0.029243461811422816 ; 58 | 24.8190589547115 0.025864525910496747 ; 59 | 25.622425662973434 0.022876039623080074 ; 60 | 26.42887900248672 0.02023288151260161 ; 61 | 27.238431524150865 0.017895144883579222 ; 62 | 28.05109583505088 0.01582753506445577 ; 63 | 28.86688459874678 0.013998836357006461 ; 64 | 29.685810535564812 0.012381440599191202 ; 65 | 30.50788642289051 0.010950930219291178 ; 66 | 31.333125095463508 0.009685709482500008 ; 67 | 32.16153944567676 0.00856667835929167 ; 68 | 32.938227246035915 0.007641143253424154 ; 69 | 33.72239861830024 0.006815621618893492 ; 70 | 34.514127125411996 0.00607930403304661 ; 71 | 35.31348708126911 0.005422549546182163 ; 72 | 36.12055355892001 0.004836759353067881 ; 73 | 36.93540239885774 0.004314264124281222 ; 74 | 37.758110217414796 0.0038482235201537335 ; 75 | 38.588754415260034 0.0034325365698691804 ; 76 | 39.42741318599914 0.003061761740754522 ; 77 | 40.274165524880075 0.002731045649879268 ; 78 | 41.12909123760493 0.0024360594834088893 ; 79 | 41.992270949249814 0.0021729422902310026 ; 80 | 42.86378611329412 0.0019382504065130982 ; 81 | 43.74371902076092 0.0017289123482408188 ; 82 | 44.63215280946986 0.001542188580481708 ; 83 | 45.52917147340433 0.0013756356360598086 ; 84 | 46.434859872194416 0.0012270741133522903 ; 85 | 47.34930374071258 0.0010945601337771134 ; 86 | 48.364384412817486 0.0009647618356676518 ; 87 | 49.379785586778326 0.0008503557210569973 ; 88 | 50.39550741421541 0.0007495164938529389 ; 89 | 51.41155004684329 0.0006606353132858362 ; 90 | 52.60233679755664 0.0005693036016959514 ; 91 | 53.77919611346216 0.0004905938576126548 ; 92 | 54.94228410298658 0.0004227624006014844 ; 93 | 56.091755284696234 0.00036430633500513854 ; 94 | 57.22776259968469 0.0003139303145738763 ; 95 | 58.35045742395875 0.0002705178923931361 ; 96 | 59.45998958082039 0.00023310682382008942 ; 97 | 60.556507353241216 0.00020086777728288698 ; 98 | 61.64015749622714 0.00017308598293538365 ; 99 | 62.711085249170246 0.00014914541394851294 ; 100 | 63.76943434818539 0.00012851515108212665 ; 101 | 64.815347038429 0.00011073762934697615 ; 102 | 65.84896408639746 0.00009541850709542147 ; 103 | 66.8704247922031 0.00008221793368536839 ; 104 | 67.87986700182476 0.00007084302273310274 ; 105 | 68.8774271193317 0.00006104136458678227 ; 106 | 69.86324011907763 0.000052595434599546184; 107 | 70.8374395578637 0.00004531777356493782 ; 108 | 71.80015758707647 0.000039046833733439246; 109 | 72.68865461815959 0.00003398575432010344 ; 110 | 73.57022854353788 0.000029580547160393628; 111 | 74.44493098881618 0.000025746233592392944; 112 | 75.31281323119997 0.000022408843008637936; 113 | 76.17392620126307 0.00001950398719402563 ; 114 | 77.02832048471646 0.00001697561926004407 ; 115 | 77.87604632417766 0.000014774953279356489; 116 | 78.71715362094046 0.00001285952381730844 ; 117 | 79.551691936745 0.000011192367249356912; 118 | 80.37971049554787 9.741309097476518e-6 ; 119 | 81.20125818529202 8.478343659349097e-6 ; 120 | 82.01638355967633 7.379093980827345e-6 ; 121 | 82.82513483992473 6.422341768933253e-6 ; 122 | 83.62755991655452 5.589618189260209e-6 ; 123 | 84.42370635114384 4.864847663982511e-6 ; 124 | 85.2136213780981 4.234037807288761e-6 ; 125 | 85.99735190641914 3.6850095235747807e-6 ; 126 | 86.77126507788661 3.2092787757787186e-6 ; 127 | 87.53914411678717 2.794954292752292e-6 ; 128 | 88.30103431720043 2.4341112574484694e-6 ; 129 | 89.05698066083089 2.119847408523293e-6 ; 130 | 89.80702781872314 1.846151123042689e-6 ; 131 | 90.55122015297522 1.6077865140798708e-6 ; 132 | 91.28960171845002 1.4001933490614182e-6 ; 133 | 92.00550873461046 1.2333118599412074e-6 ; 134 | 92.72423273172485 1.0863213945314536e-6 ; 135 | 93.44578535382313 9.568509390520772e-7 ; 136 | 94.17017829735393 8.428121226896023e-7 ; 137 | 94.89742331145317 7.423655223488129e-7 ; 138 | 95.62753219821417 6.538909846164284e-7 ; 139 | 96.3605168129594 5.759614859695728e-7 ; 140 | 97.09638906451391 5.073201093718896e-7 ; 141 | 97.83516091548039 4.46859765699811e-7 ; 142 | 98.57684438251582 3.936053327437919e-7 ; 143 | 99.32145153660994 3.466979235488415e-7 ; 144 | 100.0689945033653 3.053810302255262e-7 ; 145 | 100.81948546327908 2.6898831963163175e-7 ; 146 | 101.57293665202647 2.3693288398456065e-7 ; 147 | 102.32936036074604 2.0869777294549137e-7 ; 148 | 103.08876893632655 1.838276543975248e-7 ; 149 | 103.85117478169572 1.6192146935538855e-7 ; 150 | 104.61659035611073 1.426259624876361e-7 ; 151 | 105.38502817545033 1.2562998386281498e-7 ; 152 | 106.15650081250897 1.1065946997674138e-7 ; 153 | 106.9310208972926 9.747302307980794e-8 ; 154 | 107.70860111731623 8.585801747804816e-8 ; 155 | 108.4892542179035 7.562716998532403e-8 ; 156 | 109.27299300248785 6.661551919379164e-8 ; 157 | 110.05983033291581 5.867776482658846e-8 ; 158 | 110.84977912975185 5.168592424694919e-8 ; 159 | 111.64285237258542 4.552726831549305e-8 ; 160 | 112.43906310033962 4.010250329483534e-8 ; 161 | 113.23842441158197 3.532416947066346e-8 ; 162 | 114.04094946483697 3.1115230655123596e-8 ; 163 | 114.84665147890068 2.740783181815693e-8 ; 164 | 115.65554373315715 2.4142204805057678e-8 ; 165 | 116.46763956789694 2.1265704487747348e-8 ; 166 | 117.28295238463754 1.8731959801643352e-8 ; 167 | 118.10149564644574 1.6500125973500722e-8 ; 168 | 118.92328287826211 1.4534225878138631e-8 ; 169 | 119.74832766722746 1.2802569899865155e-8 ; 170 | 120.57664366301124 1.1277244940900727e-8 ; 171 | 121.40824457814219 9.933664334603154e-9 ; 172 | 122.2431441883408 8.750171403809505e-9 ; 173 | 123.08135633285417 7.707690270003333e-9 ; 174 | 123.92289491479256 6.7894182812367484e-9 ; 175 | 124.7677739014685 5.980555098096755e-9 ; 176 | 125.61600732473762 5.268064068329123e-9 ; 177 | 126.46760928134195 4.640462041568822e-9 ; 178 | 127.32259393325515 4.087634234370453e-9 ; 179 | 128.18097550803 3.6006711597960495e-9 ; 180 | 129.0427682991481 3.171724991706807e-9 ; 181 | 129.90798666637178 2.7938830473793276e-9 ; 182 | 130 0.0 ; ]; 183 | end 184 | % altitude [km] vs temperature [C] 185 | if ~exist('atmtemperature', 'var') 186 | global atmtemperature; 187 | atmtemperature = [ 0.0 15.0 ; 188 | 0.5873532661402939 11.182555706237338 ; 189 | 1.1708835840211225 7.3906551726050225 ; 190 | 1.7505894467842473 3.624294347296768 ; 191 | 2.326469355432374 -0.11653078269370098; 192 | 2.8985218188122817 -3.8318241914215605 ; 193 | 3.4667453535972417 -7.521589813835249 ; 194 | 4.031138484268703 -11.185831545616793 ; 195 | 4.591699743097215 -14.824553243017135 ; 196 | 5.148427670122567 -18.437758722686567 ; 197 | 5.701320813133119 -22.02545176150059 ; 198 | 6.250377727644286 -25.587636096380322 ; 199 | 6.795596976876166 -29.124315424107493 ; 200 | 7.336977131730267 -32.63549340113431 ; 201 | 7.874516770765299 -36.121173643387124 ; 202 | 8.408214480172017 -39.581359726064505 ; 203 | 8.93806885374706 -43.016055183428875 ; 204 | 9.464078492865767 -46.42526350859214 ; 205 | 9.986242006453917 -49.80898815329434 ; 206 | 10.504558010958375 -53.167232527675424 ; 207 | 11.01902513031035 -56.5 ; 208 | 20.062982118274434 -56.5 ; 209 | 20.98084654897413 -55.5880202565871 ; 210 | 21.9008389620091 -54.67418896100597 ; 211 | 22.8229605287171 -53.75850666193023 ; 212 | 23.747212423371924 -52.84097390917117 ; 213 | 24.673595823186716 -51.92159125367721 ; 214 | 25.602111908317323 -51.000359247533595 ; 215 | 26.53276186186562 -50.07727844396172 ; 216 | 27.46554686988287 -49.15234939731894 ; 217 | 28.400468121373088 -48.22557266309792 ; 218 | 29.337526808296396 -47.296948797926234 ; 219 | 30.27672412557241 -46.36647835956603 ; 220 | 31.218061271083617 -45.4341619069134 ; 221 | 32.16153944567676 -44.5 ; 222 | 32.82294660742017 -42.666806255917464 ; 223 | 33.48710277233055 -40.82637421834721 ; 224 | 34.154009078612766 -38.97870535550513 ; 225 | 34.823666669121195 -37.12380114207147 ; 226 | 35.496076691365815 -35.26166305918406 ; 227 | 36.17124029751821 -33.39229259443138 ; 228 | 36.84915864441759 -31.5156912418461 ; 229 | 37.529832893576796 -29.63186050189853 ; 230 | 38.213264211188296 -27.74080188148986 ; 231 | 38.899453768130186 -25.842516893946026 ; 232 | 39.58840273997215 -23.93700705901128 ; 233 | 40.28011230698143 -22.024273902841713 ; 234 | 40.97458365412876 -20.104318957999368 ; 235 | 41.67181797109436 -18.177143763445997 ; 236 | 42.37181645227385 -16.242749864536734 ; 237 | 43.07458029678417 -14.301138813014575 ; 238 | 43.78011070846955 -12.352312167004072 ; 239 | 44.48840889590738 -10.396271491005507 ; 240 | 45.19947607241417 -8.43301835588943 ; 241 | 45.91331345605142 -6.4625543388903 ; 242 | 46.629922269631564 -4.484881023601417 ; 243 | 47.34930374071258 -2.5 ; 244 | 51.41155004684329 -2.5 ; 245 | 52.15213108353106 -4.540325693286491 ; 246 | 52.89004297885337 -6.5728299133947985 ; 247 | 53.625284454668574 -8.597514369303667 ; 248 | 54.35785423699868 -10.614380762784776 ; 249 | 55.08775105603217 -12.623430788399048 ; 250 | 55.81497364612668 -14.62466613349386 ; 251 | 56.53952074581175 -16.618088478199184 ; 252 | 57.26139109779142 -18.60369949542431 ; 253 | 57.98058344894684 -20.58150085085441 ; 254 | 58.69709655033888 -22.55149420294663 ; 255 | 59.41092915721056 -24.51368120292659 ; 256 | 60.122080028989615 -26.46806349478453 ; 257 | 60.83054792929086 -28.414642715271185 ; 258 | 61.536331625918585 -30.353420493894077 ; 259 | 62.2394298908689 -32.284398452913365 ; 260 | 62.93984150033201 -34.20757820733763 ; 261 | 63.637565234694456 -36.12296136491932 ; 262 | 64.33259987854133 -38.03054952615099 ; 263 | 65.02494422065837 -39.930344284260286 ; 264 | 65.71459705403412 -41.82234722520562 ; 265 | 66.40155717586185 -43.706559927671464 ; 266 | 67.0858233875417 -45.58298396306344 ; 267 | 67.76739449468249 -47.45162089550365 ; 268 | 68.44626930710369 -49.31247228182542 ; 269 | 69.12244663883719 -51.165539671568524 ; 270 | 69.79592530812911 -53.010824606973586 ; 271 | 70.46670413744148 -54.848328622976965 ; 272 | 71.13478195345398 -56.6780532472053 ; 273 | 71.80015758707647 -58.5 ; 274 | 72.61330146122056 -60.09004159019099 ; 275 | 73.4235813878142 -61.67408380840237 ; 276 | 74.2309958642791 -63.252128092591676 ; 277 | 75.0355433928183 -64.82417587481251 ; 278 | 75.8372224804201 -66.39022858121263 ; 279 | 76.636031638862 -67.9502876320322 ; 280 | 77.4319693847145 -69.50435444160118 ; 281 | 78.225034239345 -71.05243041833751 ; 282 | 79.01522472892147 -72.59451696474463 ; 283 | 79.80253938441622 -74.13061547740938 ; 284 | 80.5869767416096 -75.66072734699961 ; 285 | 81.3685353410936 -77.1848539582615 ; 286 | 82.14721372827546 -78.70299669001744 ; 287 | 82.92301045338111 -80.21515691516328 ; 288 | 83.69592407145879 -81.72133600066562 ; 289 | 84.46595314238239 -83.22153530755938 ; 290 | 85.23309623085487 -84.71575619094494 ; 291 | 85.99735190641914 -86.20400000000001 ; 292 | 91.28960171845002 -86.20400000000001 ; 293 | 92.12832386178835 -85.38891267215413 ; 294 | 92.96903608064213 -84.5721036221104 ; 295 | 93.81173937133609 -83.7535733082683 ; 296 | 94.65643473272092 -82.93332219002036 ; 297 | 95.50312316617602 -82.11135072775184 ; 298 | 96.35180567561207 -81.28765938284019 ; 299 | 97.20248326747372 -80.46224861765484 ; 300 | 98.05515695074233 -79.6351188955567 ; 301 | 98.90982773693857 -78.80627068089782 ; 302 | 99.76649664012515 -77.97570443902097 ; 303 | 100.62516467690959 -77.14342063625926 ; 304 | 101.48583286644673 -76.30941973993583 ; 305 | 102.34850223044162 -75.47370221836343 ; 306 | 103.21317379315218 -74.6362685408439 ; 307 | 104.0798485813919 -73.7971191776681 ; 308 | 104.94852762453256 -72.95625460011516 ; 309 | 105.81921195450698 -72.11367528045238 ; 310 | 106.69190260581182 -71.26938169193477 ; 311 | 107.56660061551021 -70.42337430880454 ; 312 | 108.44330702323458 -69.57565360629096 ; 313 | 109.3220228711894 -68.72622006060976 ; 314 | 110.20274920415396 -67.87507414896282 ; 315 | 111.08548706948508 -67.02221634953798 ; 316 | 111.97023751712 -66.16764714150833 ; 317 | 112.85700159957906 -65.31136700503203 ; 318 | 113.74578037196856 -64.45337642125193 ; 319 | 114.6365748919835 -63.59367587229514 ; 320 | 115.52938621991044 -62.73226584127275 ; 321 | 116.42421541863025 -61.86914681227927 ; 322 | 117.32106355362102 -61.00431927039244 ; 323 | 118.21993169296077 -60.13778370167276 ; 324 | 119.12082090733038 -59.26954059316307 ; 325 | 120.02373227001637 -58.399590432888374 ; 326 | 120.92866685691372 -57.527933709855205 ; 327 | 121.83562574652883 -56.65457091405142 ; 328 | 122.74461001998229 -55.77950253644579 ; 329 | 123.6556207610117 -54.90272906898761 ; 330 | 124.56865905597468 -54.0242510046063 ; 331 | 125.4837259938516 -53.14406883721105 ; 332 | 126.40082266624862 -52.262183061690564 ; 333 | 127.3199501674004 -51.37859417391243 ; 334 | 128.24110959417317 -50.49330267072298 ; 335 | 129.16430204606752 -49.606309049946844 ; 336 | 130.08952862522133 -48.717613810386524 ; 337 | 131.0167904364128 -47.827217451822094 ; 338 | 131.94608858706317 -46.93512047501079 ; 339 | 132.87742418723982 -46.041323381686595 ; 340 | 133.81079834965917 -45.14582667455994 ; 341 | 134.7462121896896 -44.24863085731738 ; 342 | 135.68366682535438 -43.349736434621036 ; 343 | 136.62316337733475 -42.44914391210841 ; 344 | 137.5647029689727 -41.54685379639193 ; 345 | 138.50828672627412 -40.642866595058564 ; 346 | 139.4539157779117 -39.737182816669474 ; 347 | 140.40159125522788 -38.82980297075966 ; 348 | 141.351314292238 -37.92072756783756 ; 349 | 142.303086025633 -37.00995711938464 ; 350 | 143.25690759478286 -36.09749213785528 ; 351 | 144.21278014173924 -35.183333136675856 ; 352 | 145.1707048112387 -34.26748063024499 ; 353 | 146.1306827507056 -33.349935133932775 ; 354 | 147.09271511025534 -32.430697164080556 ; 355 | 148.0568030426972 -31.509767238000506 ; 356 | 149.0229477035375 -30.587145873975317 ; 357 | 149.9911502509826 -29.662833591257765 ; 358 | 150.96141184594208 -28.736830910070353 ; 359 | 151.9337336520317 -27.809138351604986 ; 360 | 152.9081168355765 -26.879756438022582 ; 361 | 153.8845625656139 -25.948685692452557 ; 362 | 154.86307201389687 -25.015926638992795 ; 363 | 155.84364635489695 -24.081479802708913 ; 364 | 156.82628676580737 -23.14534570963403 ; 365 | 157.8109944265462 -22.207524886768567 ; 366 | 158.79777051975938 -21.26801786207949 ; 367 | 159.7866162308241 -20.326825164500406 ; 368 | 160.77753274785167 -19.383947323930784 ; 369 | 161.7705212616907 -18.43938487123586 ; 370 | 162.76558296593052 -17.49313833824604 ; 371 | 163.762719056904 -16.545208257756826 ; 372 | 164.76193073369092 -15.59559516352806 ; 373 | 165.76321919812113 -14.64429959028405 ; 374 | 166.76658565477774 -13.691322073712627 ; 375 | 167.77203131100023 -12.73666315046512 ; 376 | 168.77955737688774 -11.780323358156124 ; 377 | 169.78916506530223 -10.82230323536271 ; 378 | 170.8008555918717 -9.862603321624306 ; 379 | 171.81463017499357 -8.90122415744247 ; 380 | 172.83049003583758 -7.938166284280101 ; 381 | 173.84843639834935 -6.973430244561541 ; 382 | 174.8684704892535 -6.007016581671678 ; 383 | 175.89059353805683 -5.038925839956221 ; 384 | 176.91480677705178 -4.069158564720738 ; 385 | 177.9411114413195 -3.097715302230654 ; 386 | 178.96950876873325 -2.124596599710628 ; 387 | 179.99999999996163 -1.1498030053443244 ; ]; 388 | end -------------------------------------------------------------------------------- /MATLAB/launchTargeting.m: -------------------------------------------------------------------------------- 1 | function [lan, azimuth, target] = launchTargeting(launchSite, altitude, opposingApsis, inclination, deltaLAN) 2 | %[lan, azimuth, target] = LAUNCHTARGETING(site, altitude, apsis, 3 | % inclination, slip) 4 | %Creates a UPFG-compatible target structure as well as a launch azimuth for 5 | %first stage guidance from a given launch site and definition of a target 6 | %orbit. 7 | %Assumes the insertion will occur in one apsis of the target orbit. Handles 8 | %LAN specially - since the Earth does not rotate in the simulation, (it is 9 | %assumed that at launch (beginning of any simulation) the 0 degree meridian 10 | %points in a reference direction - ie. a zero-LAN orbit's ascending node 11 | %passes right over it at this time) arbitrarily choosing the target LAN 12 | %makes no sense. Instead, an assumption is made that the launch occurs some 13 | %time before the launch site rotates under the target orbit. This time can 14 | %be specified by the deltaLAN param. This function will return the LAN of a 15 | %targeted orbit, so in order to simulate a launch into a plane with any 16 | %desired LAN, a simple adjustment of time to launch is possible: one simply 17 | %needs to subtract the returned LAN from their desired LAN and convert this 18 | %to seconds (ie. calculate how long will it take until the Earth rotates by 19 | %this amount). 20 | % 21 | %REQUIRES 22 | % mu Global variable, standard gravity parameter of the body; 23 | % gravity constant * mass of the body (kg). 24 | % R Global variable, radius of the body (m). 25 | % period Global variable, period of Earth's rotation (seconds). 26 | % 27 | %INPUT 28 | % site Struct defining the launch site. 29 | % altitude Desired cutoff altitude (km above sea level). Becomes one 30 | % apsis of the target orbit. 31 | % apsis Opposing apsis of the target orbit (km above sea level). 32 | % inclinationDesired inclination of the target orbit in degrees. 33 | % deltaLAN Lift-off will occur when the launch site is that many 34 | % degrees before rotating right under the target orbit. 35 | % Zero means launch right when this happens. Negative values 36 | % mean launch is late, after the target orbit passed over the 37 | % launch site. Values of 1.5-2.5 work pretty good, depending 38 | % on vehicle. Too little or too much can break UPFG. 39 | % 40 | %OUTPUT 41 | % lan LAN of the orbit passing (degrees). 42 | % azimuth Launch azimuth corrected for the velocity bonus from 43 | % Earth's rotation (degrees from earth to north, CCW). 44 | % target Target struct, compatible with UPFG. 45 | 46 | global mu; global R; global period; 47 | 48 | %Calculation of the launch azimuth. Math largely based on: 49 | %http://www.orbiterwiki.org/wiki/Launch_Azimuth 50 | if inclination x0(i) & x <= x0(i + 1)); 42 | 43 | if isempty(j{i}) 44 | error('Insufficient amount of data points'); 45 | end 46 | 47 | end 48 | % compute the matrices corresponding to the 49 | % system of equations 50 | A = zeros(numberOfParameters, numberOfParameters); 51 | B = zeros(numberOfParameters, 1); 52 | for i = 1:numberOfParameters 53 | if i ~= 1 54 | % first sum 55 | A(i, i-1) = A(i, i-1) - ... 56 | sum((x(j{i-1}) - x0(i-1)) .* (x(j{i-1}) - x0(i))) / (x0(i) - x0(i-1)) .^ 2; 57 | A(i, i) = A(i, i) + ... 58 | sum((x(j{i-1}) - x0(i-1)) .^ 2) / (x0(i) - x0(i-1)) .^ 2; 59 | 60 | B(i) = B(i) + ... 61 | (sum(x(j{i-1}) .* y(j{i-1})) - x0(i-1) * sum(y(j{i-1}))) / (x0(i) - x0(i-1)); 62 | end 63 | if i ~= numberOfParameters 64 | % second sum 65 | A(i, i) = A(i, i) + ... 66 | sum((x(j{i}) - x0(i+1)) .^ 2) / (x0(i+1) - x0(i)) .^ 2; 67 | A(i, i+1) = A(i, i+1) - ... 68 | sum((x(j{i}) - x0(i)) .* (x(j{i}) - x0(i+1))) / (x0(i+1) - x0(i)) .^ 2; 69 | 70 | B(i) = B(i) + ... 71 | (-sum(x(j{i}) .* y(j{i})) + x0(i+1) * sum(y(j{i}))) / (x0(i+1) - x0(i)); 72 | end 73 | end 74 | % find the parameters 75 | p = A^-1 * B; 76 | 77 | % return 78 | fit = [x0;p']; -------------------------------------------------------------------------------- /MATLAB/planeError.m: -------------------------------------------------------------------------------- 1 | function [error] = planeError(results, target) 2 | %angle = PLANEERROR(results, target) 3 | %Computes angle between target orbital plane and actually achieved plane. 4 | % 5 | %INPUT 6 | % results Results struct as output by flightManager (NOT flightSim3D). 7 | % target Target struct as output by launchTargeting. 8 | % 9 | %OUTPUT 10 | % angle Angle between the two orbital planes. 11 | % 12 | %See also FLIGHTMANAGER, LAUNCHTARGETING. 13 | inc = results.powered(results.n).Orbit.INC; 14 | lan = results.powered(results.n).Orbit.LAN; 15 | 16 | Rx=[1,0,0;0,cosd(inc),-sind(inc);0,sind(inc),cosd(inc)]; 17 | Rz=[cosd(lan),-sind(lan),0;sind(lan),cosd(lan),0;0,0,1]; 18 | reached = (Rz*Rx*[0,0,-1]')'; 19 | error = acosd(dot(target.normal, reached)); -------------------------------------------------------------------------------- /MATLAB/poweredExplicitGuidance.m: -------------------------------------------------------------------------------- 1 | function [A, B, C, T] = poweredExplicitGuidance(cycle, alt, vt, vr, tgt, acc, ve, oldA, oldB, oldT) 2 | %[A, B, C, T] = POWEREDEXPLICITGUIDANCE(cycle, alt, vt, vr, target, 3 | % acc, ve, oldA, oldB, oldT) 4 | %LEGACY implementation of Powered Explicit Guidance as described by Teren 5 | %in Explicit Guidance Equations for Multistage Boost Trajectories. 6 | %http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19660006073.pdf 7 | %http://www.orbiterwiki.org/wiki/Powered_Explicit_Guidance 8 | %Only a single-stage capability is implemented with no yaw control. 9 | %If time to cutoff is less than 7.5 seconds, will cease solving the matrix 10 | %for A and B and return old values (only updating T). 11 | % 12 | %REQUIRES 13 | % mu Global variable, standard gravity parameter of the body; 14 | % gravity constant * mass of the body (kg). 15 | % 16 | %INPUT 17 | % cycle Length of the major cycle (time between PEG calls) (s). 18 | % alt Current altitude relative to body center (m). 19 | % vt Tangential (horizontal) velocity (m/s). 20 | % vr Radial (vertical) velocity (m/s). 21 | % tgt Target altitude relative to body center (m). 22 | % acc Current vehicle acceleration (m/s^2). 23 | % ve Effective exhaust velocity (Isp*g0) (m/s). 24 | % oldA Previous value of A. 25 | % oldB Previous value of B. If oldA==oldB==0, those will be 26 | % estimated from oldT (this needs to be a "reasonable guess" 27 | % in this case). Otherwise (ie. if the algorithm is converged 28 | % or converging), previously obtained values need to be passed. 29 | % oldT Previous value of T (s). 30 | % 31 | %OUTPUT 32 | % A Steering constant, cosine of pitch angle right now. 33 | % B Steering constant corresponding to pitch rate, ie. pitch 34 | % after t seconds from when these values were calculated 35 | % should equal acosd(A+B*t). 36 | % C Portion of vehicle acceleration used to counteract gravity 37 | % and centrifugal force (unitless). 38 | % T Time to go (until the end of burn) (s). 39 | 40 | global mu; 41 | tau = ve / acc; 42 | if oldA==0 && oldB==0 43 | %calculate A and B (PEG.pdf, page 22) 44 | b0 = -ve*log(1-oldT/tau); 45 | b1 = b0*tau - ve*oldT; 46 | c0 = b0*oldT - b1; 47 | c1 = c0*tau - ve*oldT^2/2; 48 | matA = [b0 b1; 49 | c0 c1]; 50 | matB = [-vr; 51 | tgt - alt - vr*oldT]; 52 | matX = linsolve(matA, matB); 53 | oldA = matX(1); 54 | oldB = matX(2); 55 | end; 56 | 57 | %current and target angular momentum, change in momentum 58 | angM = norm(cross([alt 0 0], [vr; vt; 0])); 59 | tgtV = sqrt(mu/tgt); 60 | tgtM = norm(cross([tgt 0 0], [0; tgtV; 0])); 61 | dMom = tgtM - angM; 62 | 63 | %based on old ABT (or freshly estimated values) calculate steering constant series f_r (eq 22) 64 | C = (mu/tgt^2 - tgtV^2/tgt) / (acc / (1-oldT/tau)); %target values 65 | f_r_T = oldA + oldB*oldT + C; 66 | C = (mu/alt^2 - vt^2/alt) / acc; %current values 67 | f_r = oldA + C; 68 | f_r_dot = (f_r_T - f_r) / oldT; 69 | 70 | %based on that series calculate steering constant series f_theta (eq 25) 71 | %f_h terms omitted because we're not doing yaw steering (yet?) 72 | f_theta = 1 - f_r^2/2; 73 | f_theta_dot = -f_r*f_r_dot; 74 | f_theta_2dot = -f_r_dot^2/2; 75 | 76 | %based on that series and old-but-updated T (old - delta T) calculate delta-v (eq 36) 77 | avgR = (alt + tgt)/2; 78 | deltav = dMom/avgR + ve*(oldT-cycle)*(f_theta_dot+f_theta_2dot*tau) + f_theta_2dot*ve*(oldT-cycle)^2/2; 79 | deltav = deltav / (f_theta + f_theta_dot*tau + f_theta_2dot*tau^2); 80 | 81 | %based on that delta-v calculate the new T (eq 37b) 82 | T = tau*(1-exp(-deltav/ve)); 83 | 84 | %solve matrix for new steering constants AB 85 | %however, don't do it if T is too small 86 | if (T>=7.5) 87 | b0 = -ve*log(1-T/tau); 88 | b1 = b0*tau - ve*T; 89 | c0 = b0*T - b1; 90 | c1 = c0*tau - ve*T^2/2; 91 | matA = [b0 b1; 92 | c0 c1]; 93 | matB = [-vr; 94 | tgt - alt - vr*T]; 95 | matX = linsolve(matA, matB); 96 | A = matX(1); 97 | B = matX(2); 98 | else 99 | %in such case, just live with the old values 100 | A = oldA; 101 | B = oldB; 102 | end; 103 | end -------------------------------------------------------------------------------- /MATLAB/resultsToInit.m: -------------------------------------------------------------------------------- 1 | function [init] = resultsToInit(results) 2 | %init = RESULTSTOINIT(results) 3 | %Converts a results struct into an initialization struct, allowing easily 4 | %continuing a flight. 5 | % 6 | %INPUT 7 | % results Results struct as returned by flightSim3D. 8 | % 9 | %OUTPUT 10 | % init Init struct of type 1. 11 | 12 | n = length(results.Plots.t); 13 | init = struct('type', 1,... 14 | 't', results.Plots.t(n),... 15 | 'r', results.Plots.r(n,:),... 16 | 'v', results.Plots.v(n,:)); 17 | %Handle UPFG state passing. Copy final state from results, create a 18 | %dummy state otherwise. 19 | if isfield(results, 'UPFG') 20 | init.upfg = results.UPFG; 21 | else 22 | init.upfg = struct(); 23 | end; 24 | end -------------------------------------------------------------------------------- /MATLAB/rodrigues.m: -------------------------------------------------------------------------------- 1 | function [rotated] = rodrigues(vector, axis, angle) 2 | %[rotated] = RODRIGUES(vector, axis, angle) 3 | %Rotates a given vector about a given axis by a given angle using Rodrigues 4 | %rotation formula: 5 | %https://en.wikipedia.org/wiki/Rodrigues'_rotation_formula 6 | % 7 | %INPUT 8 | % vector XYZ vector to be rotated. 9 | % axis XYZ vector representing the axis of rotation (will be 10 | % normalized so its magnitude does not matter). 11 | % angle Angle of rotation (degrees). 12 | % 13 | %OUTPUT 14 | % rotated Rotated XYZ vector. 15 | 16 | axis = unit(axis); 17 | rotated = vector*cosd(angle); 18 | rotated = rotated + cross(axis, vector)*sind(angle); 19 | rotated = rotated + axis*dot(axis,vector)*(1-cosd(angle)); -------------------------------------------------------------------------------- /MATLAB/telemetry.m: -------------------------------------------------------------------------------- 1 | function [] = telemetry(powered, coast, fid) 2 | %TELEMETRY(powered, coast, figure) 3 | %Plots basic flight data in 6 subplots in a given figure: 4 | % pitch and yaw commands with flight path angle (orbital and surface) 5 | % altitude 6 | % vertical velocity 7 | % acceleration 8 | % downrange distance 9 | % horizontal velocity 10 | %Has some basic support for legacy results from flightSim2D. 11 | % 12 | %REQUIRES 13 | % g0 Global variable, standard gravity acceleration (m/s). 14 | % R Global variable, radius of the body (m). 15 | % 16 | %INPUT 17 | % powered Array of struct of type results, as output by flightSim3D, 18 | % corresponding to powered phases. 19 | % coast Array of struct of type results, as output by flightSim3D, 20 | % corresponding to coast phases. 21 | % Best to just extract the respective fields from a combined 22 | % results struct as output by flightManager. 23 | % figure ID of a figure in which to plot. 24 | % 25 | %OUTPUT 26 | % (none) 27 | % 28 | %See also FLIGHTSIM3D, FLIGHTMANAGER. 29 | 30 | global R; 31 | global g0; 32 | %if no coast periods are to be shown, don't distinguish 33 | if isempty(coast) 34 | c = 'b'; 35 | else 36 | c = 'r'; 37 | end; 38 | figure(fid), clf; 39 | %pitch&flight angle 40 | subplot(2,3,1); hold on; 41 | for i=1:length(powered) 42 | x = powered(i).Plots; 43 | plot(x.t, x.pitch, 'b'); 44 | plot(x.t, x.yaw, 'y'); 45 | plot(x.t, x.angle_ps, 'g'); 46 | plot(x.t, x.angle_po, 'g'); 47 | end; 48 | for i=1:length(coast) 49 | x = coast(i).Plots; 50 | %plot(x.t, x.pitch, 'b'); 51 | plot(x.t, x.angle_ps, 'g'); 52 | plot(x.t, x.angle_po, 'g'); 53 | end; 54 | title('Pitch and flight angles'); 55 | xlabel('Time [s]'); 56 | ylabel('Angle [deg]'); 57 | hold off; 58 | %altitude - HANDLE LEGACY RESULTS (flightSim2D.m) 59 | subplot(2,3,2); hold on; 60 | for i=1:length(powered) 61 | x = powered(i).Plots; 62 | %legacy plot has no 'rmag' field but an 'alt' one 63 | if isfield(x, 'alt') 64 | plot(x.t, x.alt/1000, c); 65 | else 66 | plot(x.t, (x.rmag-R)/1000, c); 67 | end; 68 | end; 69 | for i=1:length(coast) 70 | %coast results expected to only come from 3D simulation 71 | x = coast(i).Plots; 72 | plot(x.t, (x.rmag-R)/1000, 'b'); 73 | end; 74 | title('Altitude'); 75 | xlabel('Time [s]'); 76 | ylabel('Altitude ASL [km]'); 77 | hold off; 78 | %velocity Y 79 | subplot(2,3,3); hold on; 80 | for i=1:length(powered) 81 | x = powered(i).Plots; 82 | plot(x.t, x.vy, c); 83 | end; 84 | for i=1:length(coast) 85 | x = coast(i).Plots; 86 | plot(x.t, x.vy, 'b'); 87 | end; 88 | title('Vertical velocity'); 89 | xlabel('Time [s]'); 90 | ylabel('Velocity [m/s]'); 91 | hold off; 92 | %acceleration 93 | subplot(2,3,4); hold on; 94 | for i=1:length(powered) 95 | x = powered(i).Plots; 96 | plot(x.t, x.a/g0, 'b'); 97 | end; 98 | for i=1:length(coast) 99 | x = coast(i).Plots; 100 | plot(x.t, x.a/g0, 'b'); 101 | end; 102 | title('Acceleration'); 103 | xlabel('Time [s]'); 104 | ylabel('Acceleration [g]'); 105 | hold off; 106 | %downrange distance - HANDLE LEGACY RESULTS (flightSim2D.m) 107 | %For legacy results will only display results from first powered phase. 108 | subplot(2,3,5); hold on; 109 | if isfield(powered(1).Plots, 'rad') 110 | %this returns true for legacy structure 111 | plot(powered(1).Plots.t, (powered(1).Plots.rad-powered(1).Plots.rad(1))*(2*pi*R/360000), c); 112 | else 113 | %normal handling for pure 3D results 114 | zero = powered(1).Plots.r(1,:); 115 | zero = zero/norm(zero); 116 | for i=1:length(powered) 117 | x = powered(i).Plots; 118 | dd = zeros(length(x.r(:,1)),1); 119 | for j=1:length(x.r(:,1)) 120 | %Calculates angular distance from launch site, then length 121 | %of the arc on the surface of the Earth. Therefore this is 122 | %NOT the total length of the burn arc! 123 | dd(j) = dot(zero,x.r(j,:))/(1*x.rmag(j)); 124 | dd(j) = acosd( min(max(dd(j),-1),1) ); 125 | dd(j) = 2*pi*R*dd(j)/360; 126 | end; 127 | plot(x.t, dd/1000, c); 128 | end; 129 | for i=1:length(coast) 130 | x = coast(i).Plots; 131 | dd = zeros(length(x.r(:,1)),1); 132 | for j=1:length(x.r(:,1)) 133 | dd(j) = dot(zero,x.r(j,:))/(1*x.rmag(j)); 134 | dd(j) = acosd( min(max(dd(j),-1),1) ); 135 | dd(j) = 2*pi*R*dd(j)/360; 136 | end; 137 | plot(x.t, dd/1000, 'b'); 138 | end; 139 | end; 140 | title('Downrange'); 141 | xlabel('Time [s]'); 142 | ylabel('Distance [km]'); 143 | hold off; 144 | %velocity T - HANDLE LEGACY RESULTS (flightSim2D.m) 145 | subplot(2,3,6); hold on; 146 | for i=1:length(powered) 147 | x = powered(i).Plots; 148 | if isfield(x, 'vx') 149 | %legacy results have no 'vt' field, but a 'vx' one 150 | plot(x.t, x.vx, c); 151 | else 152 | plot(x.t, x.vt, c); 153 | end; 154 | end; 155 | for i=1:length(coast) 156 | x = coast(i).Plots; 157 | plot(x.t, x.vt, 'b'); 158 | end; 159 | title('Horizontal velocity'); 160 | xlabel('Time [s]'); 161 | ylabel('Velocity [m/s]'); 162 | hold off; 163 | %remove excessive margins 164 | tightfig; 165 | end -------------------------------------------------------------------------------- /MATLAB/testAtlas.m: -------------------------------------------------------------------------------- 1 | initSimulation 2 | 3 | AtlasV531 4 | 5 | site = createLaunchSite('Plesetsk'); 6 | 7 | alt = 200; 8 | inc = 90; 9 | [lan, azm, target] = launchTargeting(site, alt, alt, inc, 2.0); 10 | 11 | stage1 = struct('type', 0, 'pitch', 10, 'velocity', 50, 'azimuth', azm); 12 | 13 | AV5 = flightManager(vehicle, site, target, 0.2, stage1, 2, 5, []); 14 | 15 | telemetry(AV5.powered, AV5.coast, 1); 16 | dbgIntegrals(AV5.powered(2:AV5.n), 2); 17 | trajectory(AV5.powered, AV5.coast, target, 2, 1, 3); 18 | 19 | clearvars site alt inc lan azm target stage1 -------------------------------------------------------------------------------- /MATLAB/testOSIRIS.m: -------------------------------------------------------------------------------- 1 | initSimulation 2 | 3 | payload = 2110; 4 | AtlasV411 5 | 6 | site = createLaunchSite('Kennedy'); 7 | 8 | periapsis = 156; 9 | apoapsis = 181; 10 | inclination = 28.5; 11 | [lan, azm, target] = launchTargeting(site, periapsis, apoapsis, inclination, 2.0); 12 | 13 | stage1 = struct('type', 0, 'pitch', 8, 'velocity', 45, 'azimuth', 1); 14 | 15 | AV4 = flightManager(vehicle, site, target, 0.2, stage1, 2, [0 0 10], [139 4000; 267 2127]); 16 | 17 | telemetry(AV4.powered, AV4.coast, 1); 18 | dbgIntegrals(AV4.powered(2:AV4.n), 2); 19 | trajectory(AV4.powered, AV4.coast, target, 2, 1, 3); 20 | 21 | clearvars site periapsis apoapsis inclination lan azm target stage1 -------------------------------------------------------------------------------- /MATLAB/testShuttle.m: -------------------------------------------------------------------------------- 1 | initSimulation 2 | 3 | SpaceShuttle 4 | 5 | site = createLaunchSite('Kennedy'); 6 | 7 | periapsis = 100; 8 | apoapsis = 250; 9 | inclination = 51.65; 10 | [lan, azm, target] = launchTargeting(site, periapsis, apoapsis, inclination, 2.0); 11 | 12 | stage1 = struct('type', 0, 'pitch', 8.5, 'velocity', 50, 'azimuth', azm); 13 | 14 | STS = flightManager(vehicle, site, target, 0.2, stage1, 2, 0, []); 15 | 16 | telemetry(STS.powered, STS.coast, 1); 17 | dbgIntegrals(STS.powered(2:STS.n), 2); 18 | trajectory(STS.powered, STS.coast, target, 2, 1, 3); 19 | 20 | clearvars site periapsis apoapsis inclination lan azm target stage1 -------------------------------------------------------------------------------- /MATLAB/tightfig.license: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Richard Crozier 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MATLAB/tightfig.m: -------------------------------------------------------------------------------- 1 | function hfig = tightfig(hfig) 2 | % tightfig: Alters a figure so that it has the minimum size necessary to 3 | % enclose all axes in the figure without excess space around them. 4 | % 5 | % Note that tightfig will expand the figure to completely encompass all 6 | % axes if necessary. If any 3D axes are present which have been zoomed, 7 | % tightfig will produce an error, as these cannot easily be dealt with. 8 | % 9 | % hfig - handle to figure, if not supplied, the current figure will be used 10 | % instead. 11 | 12 | if nargin == 0 13 | hfig = gcf; 14 | end 15 | 16 | % There can be an issue with tightfig when the user has been modifying 17 | % the contnts manually, the code below is an attempt to resolve this, 18 | % but it has not yet been satisfactorily fixed 19 | % origwindowstyle = get(hfig, 'WindowStyle'); 20 | set(hfig, 'WindowStyle', 'normal'); 21 | 22 | % 1 point is 0.3528 mm for future use 23 | 24 | % get all the axes handles note this will also fetch legends and 25 | % colorbars as well 26 | hax = findall(hfig, 'type', 'axes'); 27 | 28 | % get the original axes units, so we can change and reset these again 29 | % later 30 | origaxunits = get(hax, 'Units'); 31 | 32 | % change the axes units to cm 33 | set(hax, 'Units', 'centimeters'); 34 | 35 | % get various position parameters of the axes 36 | if numel(hax) > 1 37 | % fsize = cell2mat(get(hax, 'FontSize')); 38 | ti = cell2mat(get(hax,'TightInset')); 39 | pos = cell2mat(get(hax, 'Position')); 40 | else 41 | % fsize = get(hax, 'FontSize'); 42 | ti = get(hax,'TightInset'); 43 | pos = get(hax, 'Position'); 44 | end 45 | 46 | % ensure very tiny border so outer box always appears 47 | ti(ti < 0.1) = 0.15; 48 | 49 | % we will check if any 3d axes are zoomed, to do this we will check if 50 | % they are not being viewed in any of the 2d directions 51 | views2d = [0,90; 0,0; 90,0]; 52 | 53 | for i = 1:numel(hax) 54 | 55 | set(hax(i), 'LooseInset', ti(i,:)); 56 | % set(hax(i), 'LooseInset', [0,0,0,0]); 57 | 58 | % get the current viewing angle of the axes 59 | [az,el] = view(hax(i)); 60 | 61 | % determine if the axes are zoomed 62 | iszoomed = strcmp(get(hax(i), 'CameraViewAngleMode'), 'manual'); 63 | 64 | % test if we are viewing in 2d mode or a 3d view 65 | is2d = all(bsxfun(@eq, [az,el], views2d), 2); 66 | 67 | if iszoomed && ~any(is2d) 68 | error('TIGHTFIG:haszoomed3d', 'Cannot make figures containing zoomed 3D axes tight.') 69 | end 70 | 71 | end 72 | 73 | % we will move all the axes down and to the left by the amount 74 | % necessary to just show the bottom and leftmost axes and labels etc. 75 | moveleft = min(pos(:,1) - ti(:,1)); 76 | 77 | movedown = min(pos(:,2) - ti(:,2)); 78 | 79 | % we will also alter the height and width of the figure to just 80 | % encompass the topmost and rightmost axes and lables 81 | figwidth = max(pos(:,1) + pos(:,3) + ti(:,3) - moveleft); 82 | 83 | figheight = max(pos(:,2) + pos(:,4) + ti(:,4) - movedown); 84 | 85 | % move all the axes 86 | for i = 1:numel(hax) 87 | 88 | set(hax(i), 'Position', [pos(i,1:2) - [moveleft,movedown], pos(i,3:4)]); 89 | 90 | end 91 | 92 | origfigunits = get(hfig, 'Units'); 93 | 94 | set(hfig, 'Units', 'centimeters'); 95 | 96 | % change the size of the figure 97 | figpos = get(hfig, 'Position'); 98 | 99 | set(hfig, 'Position', [figpos(1), figpos(2), figwidth, figheight]); 100 | 101 | % change the size of the paper 102 | set(hfig, 'PaperUnits','centimeters'); 103 | set(hfig, 'PaperSize', [figwidth, figheight]); 104 | set(hfig, 'PaperPositionMode', 'manual'); 105 | set(hfig, 'PaperPosition',[0 0 figwidth figheight]); 106 | 107 | % reset to original units for axes and figure 108 | if ~iscell(origaxunits) 109 | origaxunits = {origaxunits}; 110 | end 111 | 112 | for i = 1:numel(hax) 113 | set(hax(i), 'Units', origaxunits{i}); 114 | end 115 | 116 | set(hfig, 'Units', origfigunits); 117 | 118 | % set(hfig, 'WindowStyle', origwindowstyle); 119 | 120 | end -------------------------------------------------------------------------------- /MATLAB/trajectory.m: -------------------------------------------------------------------------------- 1 | function [] = trajectory(powered, coast, target, earth, vectors, fid) 2 | %TRAJECTORY(powered, coast, target, earth, vectors, figure) 3 | %Displays a 3D plot of flight trajectories (distinguishing powered flight 4 | %and coast periods). Optionally renders the Earth for reference and some 5 | %reference direction vectors. 6 | % 7 | %REQUIRES 8 | % R Global variable, radius of the body (m). 9 | % 10 | %INPUT 11 | % powered Array of struct of type results, as output by flightSim3D, 12 | % corresponding to powered phases. 13 | % coast Array of struct of type results, as output by flightSim3D, 14 | % corresponding to coast phases. 15 | % Best to just extract the respective fields from a combined 16 | % results struct as output by flightManager. 17 | % target UPFG target struct as generated by launchTargeting. 18 | % earth Flag controlling rendering the Earth sphere: 19 | % 0 - no Earth 20 | % 1 - only upper hemisphere (wireframe) 21 | % 2 - render whole Earth (using earth_sphere) 22 | % vectors Flag controlling rendering reference vectors of a local RNC 23 | % frame: 24 | % 0 - no vectors 25 | % 1 - vectors only at the end of the last powered phase 26 | % 2 - vectors at ends of each powered phase 27 | % figure ID of a figure in which to plot. 28 | % 29 | %OUTPUT 30 | % (none) 31 | % 32 | %See also LAUNCHTARGETING, FLIGHTSIM3D, FLIGHTMANAGER, EARTH_SPHERE. 33 | 34 | global R; 35 | figure(fid); clf; hold on; 36 | %color config 37 | cEarth = 'g'; %the planet 38 | cPower = 'r'; %powered ascent trajectories 39 | cCoast = 'b'; %coast trajectories 40 | cTrack = 'k'; %ground tracks 41 | %vectors defined, unfortunately, in their function 42 | cTarget = 'm'; %target orbit 43 | %vector size (no matter if desired or not) depending on Earth presence 44 | if earth==0 45 | %for no Earth, this will suffice 46 | scale = 100000; 47 | else 48 | %if we render the planet too, will need to make vectors a bit bigger 49 | scale = 3000000; 50 | end; 51 | %render Earth? 52 | if earth==1 53 | [sx,sy,sz]=sphere(20); 54 | if earth<2 55 | sx=sx(11:end,:); 56 | sy=sy(11:end,:); 57 | sz=sz(11:end,:); 58 | end; 59 | plot3(R*sx,R*sy,R*sz,cEarth); 60 | scatter3(0,0,0,cEarth); 61 | elseif earth==2 62 | earth_sphere('m'); 63 | cTrack = 'y'; %ground tracks will be easier visible in yellow 64 | end; 65 | %display powered paths 66 | for i=1:length(powered) 67 | x = powered(i).Plots; 68 | plot3(x.r(:,1), x.r(:,2), x.r(:,3), cPower); 69 | end; 70 | for i=1:length(coast) 71 | x = coast(i).Plots; 72 | plot3(x.r(:,1), x.r(:,2), x.r(:,3), cCoast); 73 | end; 74 | %display ground tracks for all 75 | for i=1:length(powered) 76 | x = powered(i).Plots; 77 | gt = zeros(length(x.r(:,1)),3); 78 | for j=1:length(x.r(:,1)) 79 | gt(j,:) = R*x.r(j,:)/x.rmag(j); 80 | end; 81 | plot3(gt(:,1), gt(:,2), gt(:,3), cTrack); 82 | end; 83 | for i=1:length(coast) 84 | x = coast(i).Plots; 85 | gt = zeros(length(x.r(:,1)),3); 86 | for j=1:length(x.r(:,1)) 87 | gt(j,:) = R*x.r(j,:)/x.rmag(j); 88 | end; 89 | plot3(gt(:,1), gt(:,2), gt(:,3), cTrack); 90 | end; 91 | %optionally, vectors 92 | if vectors>0 93 | if vectors>1 94 | for i=1:length(powered) 95 | x = powered(i).Plots; 96 | n = length(x.r(:,1)); 97 | dirs(x.r(n,:), x.v(n,:), scale); 98 | end; 99 | for i=1:length(coast) 100 | x = coast(i).Plots; 101 | n = length(x.r(:,1)); 102 | dirs(x.r(n,:), x.v(n,:), scale); 103 | end; 104 | else 105 | x = powered(length(powered)).Plots; 106 | n = length(x.r(:,1)); 107 | dirs(x.r(n,:), x.v(n,:), scale); 108 | end; 109 | end; 110 | %optionally, target orbit ground track 111 | if isfield(target, 'normal') && earth>0 112 | %normal vector first 113 | t = zeros(2,3); 114 | t(2,:) = target.normal*R*1.25; 115 | plot3(t(:,1), t(:,2), t(:,3), cTarget); 116 | %then a nice target ground track 117 | t = 1:1:360; 118 | tt = zeros(length(t), 3); 119 | %it's a little hard cause we have to rework the matrices... 120 | %http://math.stackexchange.com/a/476311/380921 121 | n = target.normal; 122 | a = [0 0 1]; 123 | v = cross(a,n); 124 | s = norm(v); 125 | c = dot(a,n); 126 | vx = [0 -v(3) v(2); v(3) 0 -v(1); -v(2) v(1) 0]; 127 | Rot = [1 0 0; 0 1 0; 0 0 1]; 128 | Rot = Rot + vx + ((1-c)/s^2) * vx*vx; 129 | for i=1:length(tt) 130 | tt(i,:) = (R+100)*(Rot*[sind(t(i));cosd(t(i));0])'; 131 | end; 132 | plot3(tt(:,1), tt(:,2), tt(:,3), cTarget); 133 | end; 134 | axis equal; %otherwise the whole plot looks just silly 135 | hold off; 136 | end 137 | 138 | %gets and renders direction vectors 139 | function [] = dirs(r, v, scale) 140 | rnc = getCircumFrame(r, v); 141 | t = zeros(2,3); 142 | t(1,:) = r; 143 | t(2,:) = r + scale*rnc(1,:); 144 | plot3(t(:,1), t(:,2), t(:,3), 'c'); 145 | t(2,:) = r + scale*rnc(2,:); 146 | plot3(t(:,1), t(:,2), t(:,3), 'c'); 147 | t(2,:) = r + scale*rnc(3,:); 148 | plot3(t(:,1), t(:,2), t(:,3), 'c'); 149 | end 150 | 151 | %FUNCTION PURELY COPIED FROM flightSim3D.m 152 | %constructs a local reference frame in style of PEG coordinate base 153 | function [f] = getCircumFrame(r, v) 154 | %pass current position under r (1x3) 155 | %current velocity under v (1x3) 156 | radial = unit(r); %Up direction (radial away from Earth) 157 | normal = unit(cross(r, v)); %Normal direction (perpendicular to orbital plane) 158 | circum = cross(normal, radial); %Circumferential direction (tangential to sphere, in motion plane) 159 | f = zeros(3,3); 160 | %return a left(?)-handed coordinate system base 161 | f(1,:) = radial; 162 | f(2,:) = normal; 163 | f(3,:) = circum; 164 | end -------------------------------------------------------------------------------- /MATLAB/unifiedPoweredFlightGuidance.m: -------------------------------------------------------------------------------- 1 | function [current, guidance, debug] = unifiedPoweredFlightGuidance(vehicle, target, state, previous) 2 | %[current, guidance, debug] = UNIFIEDPOWEREDFLIGHTGUIDANCE(vehicle, 3 | % target, state, previous) 4 | %Implementation of Unified Powered Flight Guidance in Standard Ascent Mode 5 | %as described by Brand, Brown and Higgins in Space Shuttle GN&C Equation 6 | %Document 24. 7 | % 8 | %REQUIRES 9 | % g0 Global variable, standard gravity acceleration. 10 | % 11 | %INPUT 12 | % vehicle (Array of) struct defining vehicle performance stage by 13 | % stage. First element of the array should be the currently 14 | % flown stage. 15 | % target Struct defining desired insertion conditions. 16 | % state Struct defining current vehicle physical state. 17 | % previous UPFG internal struct containing results of previous iteration. 18 | % 19 | %OUTPUT 20 | % current UPFG internal struct containing results of this iteration. 21 | % guidance Struct containing calculated guidance parameters. 22 | % debug Struct containing raw values of produced vectors and scalars. 23 | 24 | %"BLOCK 0" 25 | global g0; global convergenceCriterion; 26 | gamma = target.angle; 27 | iy = target.normal; 28 | rdval = target.radius; 29 | vdval = target.velocity; 30 | t = state.time; 31 | m = state.mass; 32 | r = state.radius; 33 | v = state.velocity; 34 | cser = previous.cser; 35 | rbias = previous.rbias; 36 | rd = previous.rd; 37 | rgrav = previous.rgrav; 38 | tp = previous.time; 39 | vprev = previous.v; 40 | vgo = previous.vgo; 41 | 42 | %BLOCK 1 43 | n = length(vehicle); %total number of stages 44 | SM = ones(n,1); %thrust mode (1=const thrust, 2=const acc) 45 | aL = zeros(n,1); %acceleration limit for const acceleration mode 46 | md = zeros(n,1); %mass flow rate 47 | ve = zeros(n,1); %effective exhaust velocity 48 | fT = zeros(n,1); %thrust 49 | aT = zeros(n,1); %acceleration at the beginning of stage 50 | tu = zeros(n,1); %"time to burn as if the whole stage was fuel" 51 | tb = zeros(n,1); %remaining burn time of each stage 52 | 53 | for i=1:n 54 | SM(i) = vehicle(i).MODE; 55 | aL(i) = vehicle(i).gLim * g0; 56 | [fT(i), md(i), ve(i)] = getThrust(vehicle(i).engines, 0, 0); 57 | ve(i) = ve(i) * g0; 58 | aT(i) = fT(i) / vehicle(i).m0; 59 | tu(i) = ve(i) / aT(i); 60 | tb(i) = vehicle(i).maxT; 61 | end 62 | 63 | %BLOCK 2 64 | %We need to store dt in order to keep track on maneuver time. 65 | dt = t-tp; 66 | %In the paper, this block assumes the only known thing about vehicle's 67 | %state change since the last iteration is vector of velocity change 68 | %(delta-v_sensed) and time interval (delta t). In this implementation 69 | %however we assume the state is perfectly known and hence the call to 70 | %Powered Flight Navigation Routine is not necessary. 71 | %However, we still decrement vgo here! 72 | dvsensed = v - vprev; 73 | vgo = vgo - dvsensed; vgo1 = vgo; 74 | %Calculation of 'remaining time to burn in stage k' (which here means 75 | %current stage) is done differently than in the paper. There, t_b,k is 76 | %decremented by dt every iteration; here this variable is not 77 | %persistent, but re-read from vehicle data at each iteration, and 78 | %instead we remember the current burn time tb, so we simply subtract 79 | %this time from original time-to-burn, obtaining the remaining time. 80 | tb(1) = tb(1) - previous.tb; 81 | 82 | %BLOCK 3 83 | %Current vehicle parameters have already been obtained in block 1, the 84 | %only thing different is a_T,k which should be calculated from current 85 | %mass instead of initial, and subsequently tu,k. 86 | %This is done according to theory on pages 33-34 and block diagrams on 87 | %56-57, although with a small change: original document for the Space 88 | %Shuttle assumed that standard ascent will be finalized with a 89 | %predetermined OMS burn (Orbiter's SSMEs burning fuel from ET will only 90 | %take it so far, then the ET is jettisoned and vehicle coasts for a 91 | %predetermined amount of time (tc), after which the last burn phase 92 | %circularizes). Therefore, active guidance did not calculate the 93 | %integral Li(n). Stages 1..n-2 were assumed to burn out completely 94 | %(hence the logarithmic expression for them), and stage n-1 has burn 95 | %time set accordingly, so the total delta-v expended equals vgo. 96 | %In this application however, there is no such thing as a predetermined 97 | %final stage velocity. Therefore, stages n-1 are assumed to burn out 98 | %completely, and the last one is adjusted, so it burns out only as long 99 | %as needed. 100 | if SM(1)==1 101 | aT(1) = fT(1) / m; 102 | elseif SM(1)==2 103 | aT(1) = aL(1); 104 | end; 105 | tu(1) = ve(1) / aT(1); 106 | L = 0; 107 | Li = zeros(n,1); 108 | for i=1:n-1 109 | if SM(i)==1 110 | Li(i) = ve(i)*log(tu(i) / (tu(i)-tb(i))); 111 | elseif SM(i)==2 112 | Li(i) = aL(i)*tb(i); 113 | end; 114 | L = L + Li(i); 115 | %If we have more stages than we need to get to orbit, redo the 116 | %whole calculation but skip the last stage. 117 | if L>norm(vgo) 118 | [current, guidance, debug] = unifiedPoweredFlightGuidance(vehicle(1:n-1), target, state, previous); 119 | return; 120 | end 121 | end 122 | Li(n) = norm(vgo) - L; 123 | %Now for each stage its remaining time of burn is calculated (tbi) and 124 | %in the same pass accumulated into a total time-to-go of the maneuver. 125 | tgoi = zeros(n,1); 126 | for i=1:n 127 | if SM(i)==1 128 | tb(i) = tu(i)*(1-exp(-Li(i)/ve(i))); 129 | elseif SM(i)==2 130 | tb(i) = Li(i) / aL(i); 131 | end; 132 | if i==1 133 | tgoi(i) = tb(i); 134 | else 135 | tgoi(i) = tgoi(i-1) + tb(i); 136 | end 137 | end 138 | L1 = Li(1); 139 | tgo = tgoi(n); 140 | 141 | %BLOCK 4 142 | L = 0; 143 | J = 0; Ji = zeros(n,1); 144 | S = 0; Si = zeros(n,1); 145 | Q = 0; Qi = zeros(n,1); 146 | H = 0; 147 | P = 0; Pi = zeros(n,1); 148 | %Major loop of the whole block, almost exactly as in the block diagrams. 149 | for i=1:n 150 | %Variable tgoi1 represents t_go,i-1 only is determined in a safe 151 | %way (as to not exceed the array). 152 | if i==1 153 | tgoi1 = 0; 154 | else 155 | tgoi1 = tgoi(i-1); 156 | end 157 | 158 | %Constant thrust vs constant acceleration mode 159 | if SM(i)==1 160 | Ji(i) = tu(i)*Li(i) - ve(i)*tb(i); 161 | Si(i) = -Ji(i) + tb(i)*Li(i); 162 | Qi(i) = Si(i)*(tu(i)+tgoi1) - (1/2)*ve(i)*tb(i)^2; 163 | Pi(i) = Qi(i)*(tu(i)+tgoi1) - (1/2)*ve(i)*tb(i)^2 * ((1/3)*tb(i)+tgoi1); 164 | elseif SM(i)==2 165 | Ji(i) = 0.5*Li(i)*tb(i); 166 | Si(i) = Ji(i); 167 | Qi(i) = Si(i)*((1/3)*tb(i) + tgoi1); 168 | Pi(i) = (1/6)*Si(i)*(tgoi(i)^2 + 2*tgoi(i)*tgoi1 + 3*tgoi1^2); 169 | end 170 | 171 | %Common for both modes 172 | Ji(i) = Ji(i) + Li(i)*tgoi1; 173 | Si(i) = Si(i) + L*tb(i); 174 | Qi(i) = Qi(i) + J*tb(i); 175 | Pi(i) = Pi(i) + H*tb(i); 176 | 177 | %No coast period before the last stage. 178 | 179 | L = L + Li(i); 180 | J = J + Ji(i); 181 | S = S + Si(i); 182 | Q = Q + Qi(i); 183 | P = P + Pi(i); 184 | H = J*tgoi(i) - Q; 185 | end 186 | 187 | %BLOCK 5 188 | lambda = unit(vgo); 189 | rgrav1 = rgrav; 190 | if previous.tgo~=0 191 | rgrav = (tgo/previous.tgo)^2 * rgrav; 192 | end; 193 | rgo = rd - (r + v*tgo + rgrav); rgo1 = rgo; 194 | iz = unit(cross(rd,iy)); iz1 = iz; 195 | rgoxy = rgo - dot(iz,rgo)*iz; 196 | rgoz = (S - dot(lambda,rgoxy)) / dot(lambda,iz); 197 | rgo = rgoxy + rgoz*iz + rbias; 198 | lambdade = Q - S*J/L; 199 | lambdadot = (rgo - S*lambda) / lambdade; 200 | iF = unit(lambda - lambdadot*J/L); 201 | phi = acos(dot(iF,lambda)); 202 | phidot = -phi*L/J; 203 | vthrust = (L - 0.5*L*phi^2 - J*phi*phidot - 0.5*H*phidot^2)*lambda; 204 | %vthrust = vthrust - (L*phi + J*phidot)*unit(lambdadot); %effectively does nothing 205 | rthrust = (S - 0.5*S*phi^2 - Q*phi*phidot - 0.5*P*phidot^2)*lambda; 206 | rthrust = rthrust - (S*phi + Q*phidot)*unit(lambdadot); 207 | vbias = vgo-vthrust; 208 | rbias = rgo-rthrust; 209 | 210 | %BLOCK 6 - original document does not contain any implementation 211 | %TODO - pitch and yaw RATES 212 | UP = unit(r); 213 | EAST = unit(cross([0,0,1], UP)); 214 | frame = [UP;[0,0,1];EAST]; 215 | pitch = getAngleFromFrame(iF, frame, 'pitch'); 216 | yaw = getAngleFromFrame(iF, frame, 'yaw'); 217 | 218 | %BLOCK 7 - this calls the Conic State Extrapolation Routine 219 | rc1 = r - 0.1*rthrust - (1/30)*vthrust*tgo; 220 | vc1 = v + 1.2*rthrust/tgo - 0.1*vthrust; 221 | [rc2, vc2, cser] = CSEroutine(rc1, vc1, tgo, cser); 222 | %[rc2, vc2, cser] = easyCSE(rc1, vc1, tgo, cser); 223 | vgrav = vc2 - vc1; 224 | rgrav = rc2 - rc1 - vc1*tgo; 225 | 226 | %BLOCK 8 227 | rp = r + v*tgo + rgrav + rthrust; 228 | rp = rp - dot(rp,iy)*iy; 229 | rd = rdval*unit(rp); 230 | ix = unit(rd); 231 | iz = cross(ix,iy); 232 | vd = vdval*([ix;iy;iz]'*[sind(gamma);0;cosd(gamma)])'; 233 | vgop = vd - v - vgrav + vbias; 234 | dvgo = 0.0*(vgop-vgo); %big values (0.8+) cause bananas; standard ascent uses 0 (?) 235 | vgo = vgop + dvgo; 236 | 237 | %RETURN 238 | current = previous; 239 | current.cser = cser; 240 | current.rbias = rbias; 241 | current.rd = rd; 242 | current.rgrav = rgrav; 243 | current.tb = current.tb + dt; 244 | current.time = t; 245 | current.tgo = tgo; 246 | current.v = v; 247 | current.vgo = vgo; 248 | 249 | guidance = struct('pitch', pitch, 'yaw', yaw, 'pitchdot', 0, 'yawdot', 0, 'tgo', tgo); 250 | 251 | debug = struct('time', t,... 252 | 'r', r,... 253 | 'v', v,... 254 | 'm', m,... 255 | 'dvsensed', dvsensed,... 256 | 'vgo1', vgo1,... 257 | 'L1', L1,... 258 | 'tgo', tgo,... 259 | 'L', L,... 260 | 'J', J,... 261 | 'S', S,... 262 | 'Q', Q,... 263 | 'P', P,... 264 | 'H', H,... 265 | 'lambda', lambda,... 266 | 'rgrav1', rgrav1,... 267 | 'rgo1', rgo1,... 268 | 'iz1', iz1,... 269 | 'rgoxy', rgoxy,... 270 | 'rgoz', rgoz,... 271 | 'rgo2', rgo,... 272 | 'lambdade', lambdade,... 273 | 'lambdadot', lambdadot,... 274 | 'iF', iF,... 275 | 'phi', phi,... 276 | 'phidot', phidot,... 277 | 'vthrust', vthrust,... 278 | 'rthrust', rthrust,... 279 | 'vbias', vbias,... 280 | 'rbias', rbias,... 281 | 'pitch', pitch,... 282 | 'EAST', EAST,... 283 | 'yaw', yaw,... 284 | 'rc1', rc1,... 285 | 'vc1', vc1,... 286 | 'rc2', rc2,... 287 | 'vc2', vc2,... 288 | 'cser', cser,... 289 | 'vgrav', vgrav,... 290 | 'rgrav2', rgrav,... 291 | 'rp', rp,... 292 | 'rd', rd,... 293 | 'ix', ix,... 294 | 'iz2', iz,... 295 | 'vd', vd,... 296 | 'vgop', vgop,... 297 | 'dvgo', dvgo,... 298 | 'vgo2', vgo,... 299 | 'diverge', 0); 300 | end -------------------------------------------------------------------------------- /MATLAB/unit.m: -------------------------------------------------------------------------------- 1 | function [v] = unit(vector) 2 | %u = UNIT(v) 3 | %Returns a normalized vector. If zero vector is passed, returns this vector. 4 | % 5 | %INPUT 6 | % v Any valid XYZ vector. 7 | % 8 | %OUTPUT 9 | % u Vector of the same direction as v but of magnitude 1. 10 | 11 | if norm(vector)==0 12 | v = vector; 13 | else 14 | v = vector/norm(vector); 15 | end; -------------------------------------------------------------------------------- /MATLAB/vehicleTools.m: -------------------------------------------------------------------------------- 1 | function [result] = vehicleTools(task, stage, mode, input) 2 | %VEHICLETOOLS calculate often-used parameters for a given stage and mode 3 | %(constant thrust of constant acceleration). Exact input depends on type of 4 | %call. Constant acceleration mode can only be used for single-engine stages 5 | %(collapse multiple engines with the same parameters into one) that are 6 | %throttleable (ie. no thrust profile allowed for constant acceleration). 7 | %The function will not check for violation of throttle limits (will warn if 8 | %they are exceeded though). Constant acceleration assumes vacuum conditions 9 | %for Isp calculation. 10 | % 11 | %COMMON INPUT 12 | % stage Struct of type vehicle stage (ie. an element of a vehicle 13 | % array). 14 | % mode Mode to be used; allowed choices: 15 | % 1 - constant thrust 16 | % 2 - constant acceleration 17 | % 18 | % 19 | %time = VEHICLETOOLS('tgo', stage, mode, fuel) 20 | %Calculates maximum burn time for a stage with given set of engines and 21 | %total mass of fuel. 22 | % 23 | %INPUT 24 | % fuel If mode==1: Mass of fuel to be burned (kg). 25 | % If mode==2: Array of length 2, containing the following: 26 | % fuel(1) = mass of fuel to be burned (kg). 27 | % fuel(2) = acceleration limit (G). 28 | % 29 | %OUTPUT 30 | % time Time it takes to burn the given amount of fuel. 31 | % 32 | % 33 | %mass = VEHICLETOOLS('mass', stage, mode, time) 34 | %Calculates mass of fuel burned by a stage with given set of engines over 35 | %some given time. 36 | % 37 | %INPUT 38 | % time If mode==1: Time of burn (s). 39 | % If mode==2: Array of length 2, containing the following: 40 | % time(1) = time of burn (s). 41 | % time(2) = acceleration limit (G). 42 | %OUTPUT 43 | % mass Mass of fuel burned over the given time. 44 | 45 | result = 0; 46 | switch task 47 | case 'tgo' 48 | if mode==1 %constant-thrust 49 | dm = 0; 50 | for i=1:length(stage.engines) 51 | if stage.engines(i).mode~=1 52 | fprintf('vehicleTools couldnt calculate tgo for the given stage. One of the engines is in profiled or unknown mode.\n'); 53 | return 54 | end 55 | dm = dm + stage.engines(i).flow; 56 | end 57 | result = input / dm; 58 | elseif mode==2 %constant-acceleration 59 | if length(stage.engines)~=1 60 | fprintf('vehicleTools couldnt calculate tgo for the given stage. Only single-engined stages can run in constant-acceleration mode.\n'); 61 | return 62 | end 63 | m0 = stage.m0; 64 | isp = stage.engines(1).isp0; 65 | result = isp / input(2) * log( m0/(m0-input(1)) ); 66 | %check for violation of engine throttling limits 67 | maxFlow = input(2) * m0 / isp; 68 | minFlow = input(2) * (m0-input(1)) / isp; 69 | nominalFlow = stage.engines(1).flow; 70 | minThrottle = stage.engines(1).data(1); 71 | maxThrottle = stage.engines(1).data(2); 72 | if maxFlow > maxThrottle*nominalFlow || minFlow > maxThrottle*nominalFlow 73 | fprintf('vehicleTools found a violation of upper throttle limit! Time calculated by this call will likely underestimate true burn time.\n'); 74 | end 75 | if minFlow < minThrottle*nominalFlow || maxFlow < minThrottle*nominalFlow 76 | fprintf('vehicleTools found a violation of lower throttle limit! Time calculated by this call will likely overestimate true burn time.\n'); 77 | end 78 | else 79 | fprintf('vehicleTools couldnt calculate tgo for the given stage. Unknown throttle mode.\n'); 80 | return 81 | end 82 | case 'mass' 83 | if mode==1 %constant-thrust 84 | dm = 0; 85 | for i=1:length(stage.engines) 86 | if stage.engines(i).mode~=1 87 | fprintf('vehicleTools couldnt calculate burned mass for the given stage. One of the engines is in profiled or unknown mode.\n'); 88 | return 89 | end 90 | dm = dm + stage.engines(i).flow; 91 | end 92 | result = input * dm; 93 | elseif mode==2 %constant-acceleration 94 | if length(stage.engines)~=1 95 | fprintf('vehicleTools couldnt calculate burned mass for the given stage. Only single-engined stages can run in constant-acceleration mode.\n'); 96 | return 97 | end 98 | m0 = stage.m0; 99 | isp = stage.engines(1).isp0; 100 | alim = input(2); 101 | t = input(1); 102 | result = m0 - m0 * exp( -t / (isp/alim) ); 103 | else 104 | fprintf('vehicleTools couldnt calculate burned mass for the given stage. Unknown throttle mode.\n'); 105 | return 106 | end 107 | otherwise 108 | fprintf('vehicleTools failed to understand the task.\n'); 109 | return 110 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## PEGAS-MATLAB 2 | *Powered Explicit Guidance Ascent System* is a simple physics simulator based on [Kerbal Space Program](https://kerbalspaceprogram.com/) together with an implementation of two real-word rocket guidance algorithms: previously [Powered Explicit Guidance](http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19660006073.pdf), more recently [Unified Powered Flight Guidance](https://ntrs.nasa.gov/search.jsp?R=19740004402) - as used in the **Space Shuttle** GN&C computer. 3 | 4 | [PEGAS](https://github.com/Noiredd/PEGAS) started as an autopilot script for the game, but soon - due to its unique implementation of PEG and then UPFG - evolved into a complex piece of software that needed an entire simulation environment to develop and debug. 5 | This infrastructure code was written in MATLAB and originally PEGAS repository held both that prototype code and the game script. 6 | Since the prototype code grew somewhat beyond its original purpose, and at the present state it is a general testing platform for possibly many other projects, it was separated from the game code and placed in the following repository. 7 | 8 | ### Credits 9 | 10 | MATLAB `linearFit.m` function contains pieces written originally by [Andrey Rubshtein](http://stackoverflow.com/users/817452/andrey-rubshtein) and [Nikolai Golovchenko](http://golovchenko.org). 11 | `flightPlots.m` uses [Richard Crozier](http://www.mathworks.com/matlabcentral/profile/authors/1590682-richard-crozier)'s [`tightfit`](http://www.mathworks.com/matlabcentral/fileexchange/34055-tightfig) (see MATLAB\tightfit.license for attached BSD license). 12 | I also made use of [Will Campbell](https://www.mathworks.com/matlabcentral/profile/authors/1050816-will-campbell)'s [`earth_sphere`](https://www.mathworks.com/matlabcentral/fileexchange/27123-earth-sized-sphere-with-topography) code (see MATLAB\earth_sphere.license for attached BSD license). 13 | Thank you for your great work! 14 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | ## PEGAS documentation 2 | **PEGAS** consists of two main elements: a MATLAB prototype (with a test environment) and the kOS code (*currently obsolete, to-be-work-in-progress*). 3 | In the following section you will find a brief documentation for the MATLAB part of the project, specifically the UPFG implementation with some explanation of the algorithm itself, and the simulation environment. 4 | Hopefully this will allow a properly equipped user (one obviously needs a MATLAB license to use this software) to run their own simulations. 5 | 6 | ### [Unified Powered Flight Guidance](upfg.md) 7 | This part focuses on the algorithm itself, stating the guidance problem and explaining the solution. 8 | Some math background might be necessary to understand the concepts used here. 9 | 10 | ### [PEGAS MATLAB implementation](simulation.md) 11 | PEGAS includes a simulation environment to test the guidance algorithm, including a physics simulation, vehicle setup and various debugging/visualisation tools. 12 | This section covers the framework from the concept and physics point of view. 13 | Potentially it is too detailed than necessary in order to run your own mission - you might not need to know how all the internal structures are laid out - however, reading *introduction*, *flow*, *physics* and *vehicle* is recommended. 14 | 15 | ### [Tutorial: run my own mission](tutorial.md) 16 | A brief walkthrough on running a simulation from scratch, on an example Space Shuttle mission. 17 | 18 | ### Example missions 19 | Demonstration of PEGAS capabilities and limitations. 20 | -------------------------------------------------------------------------------- /docs/simulation.md: -------------------------------------------------------------------------------- 1 | ## PEGAS MATLAB implementation 2 | This article is meant as an introduction to MATLAB part of **PEGAS** (from this point on just "PEGAS"). 3 | The basic concepts of the program are outlined here. 4 | For details, see help (and code) for each function in the repository (`help flightsim3d` etc.). 5 | 6 | ### Introduction 7 | PEGAS is a spaceflight **launch simulation** environment, in this aspect at least as detailed as Kerbal Space Program (KSP). 8 | In its typical usage scenario (see `testShuttle.m` for example), a launch from an launch site on Earth into some target orbit is simulated. 9 | The code is however flexible, so with a bit more tweaking any kind of on-orbit maneuver is possible (within the rather tight limits of physics - see [physics](#physics) and forget about complete moonshot simulations). 10 | 11 | There exist several conceptual differences between a simulation in PEGAS and, for example, a KSP or Orbiter mission. 12 | Primarily, a "mission" in PEGAS is not a continuous flight, in which a vehicle undergoes changes (staging). 13 | Instead, it is divided into small pieces (which will be referred to as "simulations"), in each of which an invariable vehicle flies from some initial condition for some limited period of time under some scheme of control. 14 | By this design, each stage of a vehicle must be flown in an individual simulation, and the final condition of each of them becomes the initial condition for the subsequent. 15 | For this reason, each stage of a vehicle must be a complete, self-sufficient description of a vehicle. 16 | Additionally, a "stage" does not necessarily have to be a physical stage - shift of a control scheme (eg. from an atmosphere-optimized method to precise orbit targetting algorithm) also must be handled by staging. 17 | Another consequence of this setup is that guidance (control) is closely tied with the physical design of the vehicle - control algorithm changes can only be made when the vehicle stages. 18 | So if such a change is required during an active burn, an artificial staging event has to be created (which requires a redefinition of a vehicle itself). 19 | 20 | Another crucial difference is a physics setting, which is stripped down to the necessary bones. 21 | At all times, only one body is simulated: the vehicle. 22 | It is subject to three forces: gravity, thrust and atmospheric drag. 23 | Gravity follows the SoI concept of KSP, ie. only the parent body can have gravitational effect on the vehicle, but the actual body does not really exist. 24 | Nor do any other bodies (not even "on rails" like in KSP) - effectively any interactions like crashes or SoI changes cannot be simulated. 25 | 26 | Thrust follows several possible logic paths (engines can be throttlable or not, thrust can be constant or adjusted for acceleration limit, or even follow a custom function). 27 | Any changes in thrust mode are to be simulated by staging: for example engaging a 3G acceleration limit in a Space Shuttle (occured before SSME MECO) is a new logical stage. 28 | 29 | ### Flow 30 | Most generally, the program starts with initialization (creation of the environment, vehicle, setting up initial and target conditions), then *a series* of simulations, finally the visualization of results. 31 | Basic scenario assumes a multistage vehicle being launched into some target *condition* (usually but not necessarily an orbit). 32 | In this setup, the first stage is flown with a separate control scheme, ie. either a pitch program (if available) or naive, gravity turn (pitch over and hold prograde). Further stages are guided with [Unified Powered Flight Guidance](upfg.md) onto a given target, until that target is reached or vehicle runs out of fuel. 33 | 34 | The smallest unit is a single simulation, handled by single call to `flightSim3D`, corresponding to a single physical or logical stage of a vehicle. 35 | In a "basic scenario" a `flightManager` function calls it for each stage, automatically handling the final/initial conditions conversion. 36 | Stages, as already mentioned, correspond to different states of the vehicle, be it physical (jettisoning high-thrust atmospheric engine in favor of a vacuum-optimized engine, dropping strap-on boosters) or logical (switching from constant thrust to constant acceleration mode, changing atmospheric guidance algorithm to orbital targetting). 37 | Minor events - like fairing separation, jettison of an interstage ring or a launch abort tower - can be simulated as stages, but there exists a hack-ish but simpler way to handle them (see `help flightSim3D`, section "INPUT", entry "jettison"). 38 | 39 | A simulation requires 3 components: 40 | * vehicle definition, 41 | * initial conditions, 42 | * method of control. 43 | 44 | Vehicle is in general an array of structures corresponding to successive stages. 45 | Each stage must be a logically self-sufficient vehicle. 46 | For a 3-stage example: stage 1 will be a complete vehicle as standing on the launch pad, stage 2 will be a vehicle state after jettisoning the first stage (both stages 2 and 3), stage 3 is the final stage alone. 47 | 48 | Initial conditions is a structure defining a vehicle state at the beginning of simulation. 49 | There are two possible versions: stationary condition on a launch pad, defined by latitude and longitude of the site, and in-flight condition, defined by position and velocity vectors. 50 | `flightManager` makes use of `resultsToInit` to extract final vehicle state and convert it into an initial conditions for the next stage. 51 | 52 | Method of control is a structure that defines the way a vehicle is steered during the flight. There are 4 main options: 53 | * naive gravity turn - this simulates the simplest and safest approach to atmospheric flight, ie. pitching over at a given time by a given angle and holding prograde until MECO (allows choosing a constant launch azimuth), 54 | * preprogrammed pitch - vehicle is passively guided using a pitch vs time table with a launch azimuth held constant - this is similar to how Saturn V realized its first stage flight (note that there is no tool to create this pitch program - so use at your own risk), 55 | * [Unified Powered Flight Guidance](upfg.md) - general guidance algorithm as used on the actual Space Shuttle GN&C computer - this requires a definition of a target orbit (generated by `launchTargeting`) and a complete knowledge of the vehicle (provided automatically by `flightSim3D`) and actively, iteratively generates pitch and yaw commands to guide the vehicle onto the target, 56 | * coast phase - this setting disables thrust (and mass flow), making the vehicle freely coast for a specified period of time. 57 | 58 | For most of those required structures there exist generator functions that create compatible `struct`s (see end of this document). 59 | 60 | ### Physics 61 | PEGAS is a 3 degrees of freedom (3DoF) simulation, ie. vehicles are modelled as point masses with certain dynamics. 62 | Frame of reference is stationary, its center coincident with the center of the body the vehicle is launched from (it can be easily configured to be anything else than Earth). 63 | The Earth's rotation is partially implemented, ie. the vehicle does receive the initial velocity from this rotation, but the rotation of the actual body is not simulated (because the body does not actually exist). 64 | Since the simulation keeps track on both absolute and surface-relative velocity, this does not affect the dynamics of the vehicle (i.e. atmospheric velocity is properly handled). 65 | However, since Earth never rotates, the vehicle's ground track will not be entirely realistic. 66 | Additionally, longitude of ascending node is affected: attainable LAN heavily depends on the choice of launch site (in real world or KSP also on time-of-launch). 67 | This is however countered by simulating a time-of-launch adjustment as a difference between the target LAN and LAN of the orbit passing right over the launch site is calculated (`help launchTargeting`). 68 | 69 | Gravity model is identical with KSP - there is only one body directly acting on the vehicle. 70 | But in fact, there are no other bodies simulated at all (aside from the vehicle), so more complex missions are not possible without excessive changes. 71 | Gravity is a simple, Newtonian inverse-square model with no account for Earth oblateness (body is only described by its radius and gravity parameter). 72 | Atmosphere (air pressure and temperature) is derived from Realism Overhaul data files, sampled as a function of altitude. 73 | Air density is calculated from the ideal gas law. 74 | 75 | Vehicle engines are simulated similarly to ones in KSP, ie. they are defined by mass flow and Isp (surface level and vacuum), although the technicalities behind them are vastly different (read the next section). 76 | Isp changes with atmospheric pressure, creating a realistic thrust behavior. 77 | Solid rocket motors with different thrust dynamics are also possible. 78 | 79 | ### Vehicle 80 | Vehicle is an array of structures corresponding to stages. 81 | Each stage is modelled as a self-sufficient, independent "logical vehicle". 82 | Parameters of separate stages are not interdependent - this makes it technically possible to launch an entire Saturn V on top of a WAC Corporal (it is up to the user to define their vehicles realistically). 83 | Basic layout of a stage struct is as follows: 84 | 85 | Field | Unit | Meaning 86 | --- | --- | --- 87 | MODE | (int) | Thrust mode: **1** - constant thrust, **2** - constant acceleration. 88 | m0 | kg | Initial mass of the vehicle. 89 | maxT | s | Maximum time of burn of this stage. 90 | gLim | G | Acceleration limit. *Ignored if MODE==1.* 91 | area | m^2 | Area of active cross-section for drag calculation. 92 | drag | m/2, - | Drag coefficient `Cd` as a function of velocity, given by (n,2) array: velocity in first column, drag coefficient corresponding to this velocity in second column. 93 | engines | (struct) | Array of structures of type engine, defining all actively thrusting engines in this stage. 94 | 95 | One thing to notice in this description is lack of notion of "fuel". 96 | Vehicle does indeed burn its mass, but instead of limiting its capabilities by amount of fuel, it needs to be converted directly into time. 97 | There exists a function to easily do this (and other conversions of this kind), see `help vehicleTools`. 98 | 99 | Another limitation concerns engines. 100 | The structure is organized in the following way: 101 | 102 | Field | Unit | Meaning 103 | --- | --- | --- 104 | mode | (int) | Throttle mode: **1** - free throttle, **2** - throttle program. 105 | isp0 | s | Vacuum specific impulse (Isp). 106 | isp1 | s | Surface level specific impulse (Isp). 107 | flow | kg/s | Nominal mass flow rate at 100% throttle. 108 | data | - | If **mode==1**: throttle limits as an array of length 2 (minimum throttle as first element, maximum as second), given as fraction of nominal throttle (ie. value of 1.0 corresponds to 100% thrust). 109 | data | s, - | If **mode==2**: throttle as a function of time, given by (n,2) array: time in the first column, throttle setting corresponding to this time in the second column (as fraction of nominal, see above). 110 | 111 | *Mode* distinguishes between two engine types: 112 | * *free throttle*, where the **program is in control** of the throttle (example engine types that would be simulated this way: liquid fuel engines in constant acceleration mode, non-throttleable engines) 113 | * *throttle program*, in which throttle setting is a **predetermined function** of time (specifically designed for solid rocket motors, but can fulfill a variety of other needs, for instance preprogrammed reduction of thrust during max-q). 114 | 115 | *Data* means different things depending on chosen mode and should be declared with care. 116 | 117 | There exists a relation between engine::mode and stage::MODE. 118 | If a vehicle is in a constant thrust mode, each of its free-throttled engines is being run at 100% throttle (nominal mass flow), and each of its programmed-throttle engines follows its program. 119 | *This ignores throttle limits, so if for some reason an engine has both throttle limits above or below 1.0 (ie. prohibiting a 100% thrust), these will have no effect.* 120 | In a constant-acceleration mode, an engine will be throttled to abide by the acceleration limit (this *will* obey the throttle limits, so if a smaller than possible thrust is requested, the engined will only throttle so low, effectively ignoring the acceleration limit). 121 | However, due to the simplicity of a function that calculates vehicle thrust (`getThrust`), a constant-acceleration stage must only have one single engine. 122 | For this reason, if multiple engines of the same type are to be used, they should be collapsed into a single engine with proportionally larger nominal mass flow rate. 123 | Naturally, this engine must not be following a throttle program (ie. has to be a free-throttled engine). 124 | 125 | ### Structs and flags 126 | This section lists layouts of all structures used by PEGAS. 127 | Attention must be paid that all structures meant to be stacked into arrays (eg. vehicle, engine) have the exact same set of fields. 128 | *Origin* stands for center of the reference frame (center of the Earth). 129 | 130 | 131 | #### Initial 132 | Initial physical state of the vehicle. 133 | 134 | Field | Unit | Meaning 135 | --- | --- | --- 136 | type | (int) | Type of initialization: **0** - launch site, **1** - vehicle continues motion 137 | lon | deg | Longitude. *Ignored if type == 1.* 138 | lat | deg | Latitude. *Ignored if type == 1.* 139 | alt | m | Altitude above sea level. *Ignored if type == 1.* 140 | t | s | Mission elapsed time. *Ignored if type == 0.* 141 | r | m | Vehicle position relative to origin (XYZ vector). *Ignored if type == 0.* 142 | v | m/s | Vehicle velocity (XYZ vector). *Ignored if type == 0.* 143 | 144 | Generators: 145 | `createLaunchSite` - creates a type 0 initial struct (launch site), 146 | `resultsToInit` - converts a [results](#results) struct to a type 1 initial struct. 147 | Typical functions that use it: 148 | `flightSim3D`. 149 | 150 | #### Target 151 | Desired insertion conditions for UPFG. 152 | 153 | Field | Unit | Meaning 154 | --- | --- | --- 155 | radius | m | Altitude from origin 156 | velocity | m/s | Velocity vector magnitude 157 | angle | deg | Flight path angle measured from the horizon 158 | normal | - | Unit vector (XYZ) normal and negative\* to the desired orbital plane 159 | 160 | \* - *for a prograde equatorial orbit this vector should point south, that's how UPFG was written*. 161 | 162 | Generators: 163 | `launchTargeting`. 164 | Typical functions that use it: 165 | `flightSim3D` - only passes it down to `unifiedPoweredFlightGuidance`. 166 | 167 | #### Control 168 | Method of controlling a vehicle stage. 169 | 170 | Field | Unit | Meaning 171 | --- | --- | --- 172 | type | (int) | Type of control: **0** - pitch over and hold prograde, **1** - programmed pitch, **2** - obsolete, **3** - UPFG, **5** - uncontrolled coast phase 173 | pitch | deg | Initial pitchover angle.\* *Ignored if type != 0.* 174 | velocity | m/s | Pitchover velocity.\* *Ignored if type != 0.* 175 | azimuth | deg | Launch azimuth (measured from east to north, counterclockwise). *Ignored if type > 1.* 176 | program | s, deg | Pitch program as a function of time, given by (n,2) array: time in the first column, pitch angle corresponding to this time in the second column. *Ignored if type != 1.* 177 | target | struct | Struct of type target. *Ignored if type != 3.* 178 | major | s | Length of the UPFG cycle (ie. UPFG routine is called every this often). *Ignored if type != 3.* 179 | length | s | Length of the coast phase. *Ignored if type != 5.* 180 | 181 | \* - *will go straight up until reaches given velocity, then starts pitching over 1 degree/s towards the given azimuth; when reaches the given pitchover angle will hold waiting for the AoA to decrease to zero; then will hold prograde until MECO.* 182 | 183 | Generators: 184 | `flightManager` - will create *coast* (type 5) and *UPFG* (type 3) structs. For now the users has to create type 0 or 1 structs. 185 | Typical functions that use it: 186 | `flightSim3D` - one of primary requirements. 187 | 188 | #### State 189 | Current vehicle physical state for UPFG. 190 | 191 | Field | Unit | Meaning 192 | --- | --- | --- 193 | time | s | Mission elapsed time 194 | mass | kg | Total vehicle mass 195 | radius | m | Vehicle position relative to origin (XYZ vector) 196 | velocity | m/s | Vehicle velocity (XYZ vector) 197 | 198 | Only `unifiedPoweredFlightGuidance` uses it, while `flightSim3D` creates it when necessary. 199 | 200 | #### UPFG internal 201 | Current internal state of the UPFG algorithm. 202 | 203 | Field | Unit | Meaning 204 | --- | --- | --- 205 | cser | struct | Struct of type CSER (defined below) 206 | rbias | m | XYZ vector, see definition of `Rbias` in [upfg.md](upfg.md) 207 | rd | m | XYZ vector, see definition of `Rd` in [upfg.md](upfg.md) 208 | rgrav | m | XYZ vector, see definition of `Rgrav` in [upfg.md](upfg.md) 209 | tb | s | Elapsed time of this burn phase 210 | time | s | Mission elapsed time 211 | tgo | s | Calculated time-to-go 212 | v | m/s | Vehicle velocity (XYZ vector) 213 | vgo | m/s | Calculated velocity-to-go, see [upfg.md](upfg.md) 214 | 215 | Only `unifiedPoweredFlightGuidance` uses and returns it, but `flightSim3D` creates it for the first time. 216 | 217 | #### CSER 218 | Current logical state of the CSE routine, see [CSEroutine.m](../MATLAB/CSEroutine.m). 219 | 220 | Field | Unit | Meaning 221 | --- | --- | --- 222 | dtcp | s | Converged value of transfer time interval 223 | xcp | - | Converged value of *x* corresponding to the time interval 224 | A | - | `Un` function parameter 225 | D | - | `Un` function parameter 226 | E | - | `Un` function parameter 227 | 228 | Only `CSEroutine` uses and returns it, it is a part of *UPFG internal* struct and as such it comes through `unifiedPoweredFlightGuidance`, and `flightSim3D` creates it for the first time along with it. 229 | 230 | #### Guidance 231 | Guidance parameters calculated by UPFG. 232 | 233 | Field | Unit | Meaning 234 | --- | --- | --- 235 | pitch | deg | Pitch angle measured from the local *up* direction (0 = straight up) 236 | pitchdot | deg/s | Rate of change of pitch angle 237 | yaw | deg | Yaw angle measured from the local *east* direction towards local *north* (0 = straight east, -90 = towards the south pole) 238 | yawdot | deg/s | Rate of change of yaw angle 239 | tgo | s | Time-to-go (cutoff) 240 | 241 | Generators: 242 | `unifiedPoweredFlightGuidance` - primary output of the function. 243 | Typical functions that use it: 244 | `flightSim3D` - uses it internally to guide the vehicle during the simulation. 245 | 246 | #### Results 247 | Nested structure returned by `flightSim3D`. Fields in the structure root as well as *Orbit* field contain simulation summary (final conditions). *Plots* contain complete logs of simulation variables as functions of simulation time (iteration). 248 | 249 | Field | Subfield | Unit | Meaning 250 | --- | --- | --- | --- 251 | Altitude | - | km | Altitude above sea level. 252 | Apoapsis | - | km | Orbit apoapsis above sea level. 253 | Periapsis | - | km | Orbit periapsis above sea level. 254 | Velocity | - | m/s | Velocity magnitude. 255 | VelocityY | - | m/s | Vertical velocity component magnitude. 256 | VelocityT | - | m/s | Tangential velocity component magnitude. 257 | maxQv | - | Pa | Maximum dynamic pressure encountered. 258 | maxQt | - | s | Time at which maximum dynamic pressure was encountered. 259 | LostGravity | - | m/s | Velocity lost due to gravity. Makes most sense for stages flying straight up - for upper stages making long burn arcs this might get a little weird. 260 | LostDrag | - | m/s | Velocity lost due to atmospheric drag. 261 | LostTotal | - | m/s | Combined velocity losses. 262 | BurnTimeLeft | - | s | Fuel left in this stage's tank after cut off, measured by remaining burn time. Larger number for the same mission = better efficiency. 263 | Orbit | SMA | m | Semi-major axis of the orbit (due to the math behind this, this and other orbital parameters will be calculated with no regard to whether the vehicle actually is in orbit or not.) 264 | . | ECC | - | Eccentricity. 265 | . | INC | deg | Inclination. 266 | . | LAN | deg | Longitude of ascending node. 267 | . | AOP | deg | Argument of periapsis. 268 | . | TAN | deg | True anomaly. 269 | Plots | t | s | Mission elapsed time in each simulation step, (n,1) array. 270 | . | r | m | Vehicle position in each simulation step, (n,3) array. 271 | . | rmag | m | Vehicle position magnitude (distance from origin), (n,1) array. 272 | . | v | m/s | Vehicle velocity, (n,3) array. 273 | . | vy | m/s | Vehicle vertical velocity magnitude, (n,1) array. 274 | . | vt | m/s | Vehicle tangential velocity magnitude, (n,1) array. 275 | . | vmag | m/s | Vehicle total velocity magnitude, (n,1) array. 276 | . | F | N | Vehicle thrust magnitude, (n,1) array. 277 | . | a | m/s^2| Vehicle acceleration magnitude, (n,1) array. 278 | . | q | Pa | Dynamic pressure, (n,1) array. 279 | . | pitch | deg | Pitch command log, (n,1) array. 280 | . | yaw | deg | Yaw command log, (n,1) array. 281 | . | vair | m/s | Vehicle surface-relative velocity, (n,3) array. 282 | . | vairmag | m/s | Vehicle surface-relative velocity magni tude, (n,1) array. 283 | . | angle_ps | deg | Surface-relative flight path angle (angle to "up"). 284 | . | angle_ys | deg | Surface-relative flight "yaw" (angle from "east" as measured towards "north"). 285 | . | angle_po | deg | Absolute (orbital) flight path angle. 286 | . | angle_yo | deg | Absolute (orbital) flight "yaw" angle. 287 | . | DEBUG | (struct) | Debug data from UPFG calls. Too long to document exactly. 288 | ENG | - | - | Flag informing of state of the engine at end of stage: **-1** - most likely error, **0** - engine fuel deprived, **1** - engine still running, **2** - cutoff as scheduled by UPFG, **3** - emergency cutoff (UPFG most likely went crazy). 289 | 290 | Generators: 291 | `flightSim3D` - returns this normally. 292 | Typical functions that use it: 293 | `telemetry`, `trajectory` - use it indirectly. 294 | 295 | #### Flight 296 | Packed flight results of multiple stages. 297 | 298 | Field | Type | Meaning 299 | --- | --- | --- 300 | powered | results struct | Results of all powered phases in order. 301 | coast | results struct | Results of all coast phases in order. Information on order of coast phases in relation to powered phases is not directly available (.Plots.t encodes it). 302 | n | int | Index of the last powered phase. 303 | 304 | Generators: 305 | `flightManager` - returns this normally. 306 | Typical functions that use it: 307 | `telemetry`, `trajectory` - ingest this struct directly, extracting *results* struct within themselves. 308 | -------------------------------------------------------------------------------- /docs/tutorial.md: -------------------------------------------------------------------------------- 1 | ## PEGAS launch tutorial 2 | The bare essence of what you need to do to simulate your launch. 3 | This assumes you know what kind of mission are you going to fly (where from, where to) and you collected all parameters of the vehicle you are going to use. 4 | We will be assuming a Space Shuttle mission from KSC to a parking orbit in ISS plane. 5 | The atmospheric part of ascent will follow a *pitch over and hold prograde* mode, switching to UPFG upon jettisoning the SRBs. 6 | Since the mission is manned, the acceleration will be limited to 3G. 7 | This creates 3 stages: SSME+SRB atmospheric ascent, SSME full thrust mode, SSME in constant acceleration mode. 8 | 9 | ### Required information 10 | * Launch site. `createLaunchSite` offers some limited choice of ready-to-use sites, but if yours is not there, you will need to know its **latitude**, **longitude** and **altitude ASL**. 11 | * Target orbit, particularly **apoapsis**, **periapsis** and **inclination**. If you needed to target LAN too, `launchTargeting` will help you calculate when do you need to launch (see `help` on it, read on "lan" returned value). 12 | * Vehicle information, stage by stage. For each you will need to find/calculate its **initial mass**, **maximum burn time** (time equivalent of fuel in the tanks), **cross-section area**, **drag coefficient** as function of velocity, and the following about each of its engines: **sea level** and **vacuum Isp**, nominal **mass flow**, **throttle limits** ([1.0 1.0] for non-throttleable engines) and for specialized applications (SRB) also **thrust profile**. 13 | See `SpaceShuttleThrustProfile` for an example SRB thrust profile. 14 | 15 | ### Overview 16 | There are a few steps you typically go through when launching a vehicle. 17 | File `testShuttle.m` is an example of such typical launch, on the example of a Space Shuttle mission. 18 | To illustrate those typical steps, it is structured into 8 sections: 19 | * initialization: `initSimulation` creates the world, setting up global variables like Earth radius, its rotation period, atmosphere etc., 20 | * construction of the vehicle (`SpaceShuttle`), 21 | * launch site definition by loading a predefined site using `createLaunchSite`, 22 | * target orbit definition and creation of a UPFG target, 23 | * atmospheric guidance definition (`stage1`, in this example it is a simple pitch-over-and-hold-prograde), 24 | * launch using `flightManager`, 25 | * results visualization, 26 | * clearing the environment from temporary variables. 27 | 28 | ### Creating the vehicle 29 | File `SpaceShuttle.m` contains a stage-by-stage definition of a Space Shuttle Endeavour. 30 | It starts with some technical bits: first it removes an existing vehicle (but you are free to have several vehicles in your workspace under different names), then sets variables corresponding to the mass of the orbiter itself and payload. 31 | Finally it loads a reference SRB thrust profile from `SpaceShuttleThrustProfile.m`. 32 | Then each stage is defined separately in the following order: 33 | * launch pad configuration (Orbiter with payload, External Tank, two Solid Rocket Boosters), 34 | * Orbiter with payload + External Tank, main engines in full thrust mode, 35 | * Orbiter with payload + External Tank, acceleration-limited mode, 36 | * Orbiter with payload only, OMS engines in constant thrust. 37 | 38 | Let's review it stage by stage, and, since each is broken up into lines for clarity, line by line. First, the complete launch stack: 39 | `stage_m0 = 2*587000 + 765000 + orbiter + payload;` simply adds together total masses of all individual components 40 | `stage_engines(1) = struct(`... creates an array of engines, initializing its first element - the 3 SSMEs; see [struct specification](simulation.md#vehicle) for details 41 | `stage_engines(2) = struct(`... adds another engine, this is 2 SRBs (notice how its thrust profile is passed in `data` field) 42 | `stage_time = 124;` defines for how long can this stage burn 43 | `stage_area = 2*10.8 + 55.4 + 15.2;` is an estimate of the stack's cross-section area for aerodynamic drag calculation 44 | `stage_drag = [`... is a VERY rough estimate (to the point of being unrealistic) of the Shuttle's drag coefficient Cd (as function of velocity) - essentially, we want a little drag at first, then some more in the transonic region (`343 1.20;`), and then less and less (similarly to how FAR analysis curves look like) 45 | `stage = struct(`... combines all the variables we just set into a struct, as per [specification](simulation.md#vehicle); we set `MODE` to 1 for constant thrust, so the `gLim` setting does not matter 46 | `vehicle(1) = stage;` packs the struct into an array, probably could be done within one line like with the engines. 47 | 48 | The next stage begins after the SRB jettison. 49 | Its initial mass is therefore smaller by the mass of SRBs and *all the fuel the SSMEs consumed by now*. 50 | First, let's bring the initial mass of all the components, just without the SRBs: 51 | `stage_m0 = 765000 + orbiter + payload;` 52 | We will use a neat tool to get the fuel mass decrement, the `vehicleTools`, but first we need to set up some variables. 53 | Since `vehicleTools` requires a configured stage struct to work, we need to give it one, but *bearing in mind that we are only interested in the SSME fuel consumption*. 54 | `stage.engines(2) = [];` gets rid of SRBs in our existing (first) `stage` so that we can use `vehicleTools` 55 | `stage_engines(2) = [];` does the same with the engines struct that we will put in the new stage (second) 56 | `stage_m0 = stage_m0 - vehicleTools('mass', stage, 1, stage.maxT);` deduces mass of fuel burned by the SSMEs (do `help vehicleTools` to see how it works) 57 | `stage_time = 320;` is a somewhat magical piece of code - it sets the stage burn time so that at the end of this stage the acceleration at SSME full thrust will be equal to 3G; it could of course be calculated in the following steps: 58 | * use `getThrust` to find the nominal thrust of the stage 59 | * rearrange the formula `force = mass * acceleration` to find mass of the vehicle, at which this thrust will produce your desired acceleration 60 | * subtract this mass from current `stage_m0` to find how much fuel the vehicle needs to burn until that happens 61 | * use `vehicleTools` to find how long will it take to burn that much fuel 62 | 63 | The result comes pretty close to 320 seconds. 64 | `stage_area = 55.4 + 15.2;` recalculates the cross-section area without the SRBs 65 | `stage = struct(`... combines all those into a new struct 66 | `vehicle(2) = stage;` packs them into the array. 67 | 68 | Next stage begins when the vehicle switches into a constant-acceleration mode with a 3G limit. 69 | We burned some more fuel, so the first thing we do is deduce our initial mass by that: 70 | `stage_m0 = stage_m0 - vehicleTools('mass', stage, 1, stage.maxT);` 71 | Then we need to find out how much more fuel do we have left. 72 | This was not mentioned anywhere earlier, but the Shuttle begins its flight with about 730 tons of hydrogen and oxygen in the ET: 73 | `stage_fuel = 730000 - vehicleTools('mass', stage, 1, 124+320);` since we know how long did the previous stages last, we simply calculate how much fuel was burned during that time 74 | `stage = struct(`... now we pack the values, largely basing on the previously compiled stage, into a new struct; we set `MODE` to 2, meaning constant-acceleration, and `gLim` to 3 for a functional acceleration limit. 75 | Now we can call `vehicleTools` again, this time to calculate for how long exactly can this stage burn: 76 | `stage.maxT = vehicleTools('tgo', stage, 2, [stage_fuel stage.gLim]);` 77 | We needed to have a functional stage struct in order to do that, now we just updated it with one last value (`maxT`). 78 | 79 | Last stage should be self-explanatory at this point. 80 | The only bits worth noticing is the inline calculation of engine mass flow (`'flow', 2*26700/(313*g0),...`) and stage burn time calculation deferred again until after the struct was built. 81 | Line `vehicle(4) = stage;` ends creation of the vehicle structure. 82 | Nothing is returned since the script works directly on the workspace - so at this point the vehicle is ready to run. 83 | The last two lines remove all the temporary variables, **along with the** `payload` **variable** that we might have supplied before the script was ran. 84 | This is on purpose, but might cause some confusion. 85 | 86 | ### Defining the target 87 | Since we want to use UPFG for vehicle control, we need to supply it with a proper [target structure](simulation.md#target). 88 | The `launchTargeting` function requires a launch site definition and the following 4 values: 89 | * periapsis, or actually the final altitude - in kilometers above sea level, 90 | * apoapsis, or actually the opposing apsis - the same, 91 | * inclination - in degrees, 92 | * angle to launch - because the simulation physics are how they are, the launch always targets an orbit passing directly over the launch site *minus this amount* (see `help launchTargeting` for details). 93 | 94 | This function returns LAN of the targeted orbit (if you want to control it, simply imagine adjusting the time of launch), launch azimuth for first stage guidance and the UPFG target struct. 95 | 96 | ### Running the simulation 97 | Now that we have a vehicle, launch site, target orbit and the first stage guidance, and the simulation is initialized, we can proceed with the launch. 98 | This is as simple as calling `flightManager` with said arguments. 99 | Here we can control a variety of other things, such as simulation precision (here in time steps of 0.2s), time between UPFG cycles (2 seconds) and coast phase lengths (here no coast phases but see `help flightManager` how it should be done). 100 | Additionally we can control minor staging events that we did not put into the vehicle definition. 101 | Those only allow simple mass deductions at specified time, without any other impact on the vehicle - see `help flightSim3D` for details. 102 | 103 | This function returns [packed simulation results](simulation.md#flight), distinguishing between powered flight and coast phases. 104 | For each phase there is a separate struct, containing its result summary as well as history of the simulation in form of a time series. 105 | Detailed list of all elements of this struct can be found [here](simulation.md#results). 106 | 107 | ### Visualizing the results 108 | Those results can be now fed to one of three functions for display. 109 | `telemetry()` draws plots of simulation history in the form of a following table: 110 | 111 | pitch & yaw steering | altitude | vertical velocity 112 | :---: | :---: | :---: 113 | **acceleration** | **downrange distance** | **horizontal velocity** 114 | 115 | `trajectory()` draws a 3D plot of the vehicle's powered trajectory. 116 | It has options for rendering an Earth under the plot, as well as some reference vectors. Best to just see how it works and then read its help. 117 | Finally there is `dbgIntegrals()` - this is only a debug function for UPFG and unless the algorithm malfunctions you will not need it (if your pitch&yaw plots look weird or your vehicle crashes, it's most likely it). 118 | -------------------------------------------------------------------------------- /docs/upfg.md: -------------------------------------------------------------------------------- 1 | ## Unified Powered Flight Guidance 2 | 3 | ### Introduction 4 | There are 3 essential problems with launching a rocket into orbit: determining when should it lift off, how should it travel through the atmosphere, and finally: how to make sure it reaches a precisely defined target orbit. 5 | In this article we will focus on the last part, that is exoatmospheric guidance of a rocket vehicle, explaining one of the most general and well documented algorithms: Unified Powered Flight Guidance. 6 | It was designed specially for the Space Shuttle orbiter, allowing it to carry out tightly constrained missions (most notably the Hubble Space Telescope service missions or ISS construction - both requiring precise rendesvous targetting) while providing a wide range of abort capabilities. 7 | 8 | The following assumptions will be made (following the implicit assumptions of the original paper): vehicle is already in motion, high enough that aerodynamic drag can be neglected; only gravity and thrust affect it; there are no coast periods between stages. 9 | The task is to steer the vehicle from its initial state (position, velocity) into a target state (orbit of given inclination, longitude of ascending node, semi-major axis, eccentricity and potentially also argument of periapsis) using only its engines. 10 | In other words, we need to find pitch and yaw angles throughout the whole ascent, as well as determine the moment of engine cutoff. 11 | Additionally, we want to do that in an *optimal* way - making sure as little fuel as possible gets wasted in any of the stages. 12 | 13 | ### Difficulties 14 | For start, let's look into the forces that act upon the vehicle. 15 | Thrust, although (let's assume for now) constant, causes a non-linear acceleration, because in order to generate it the vehicle must lose mass. 16 | The math is easy thanks to [Konstantin Tsiolkovsky](https://en.wikipedia.org/wiki/Tsiolkovsky_rocket_equation) who developed solutions over a hundred years ago. 17 | Then there is gravity, which is also pretty easy (at least in the 2-body problem) as all the math has been known for 3 centuries. 18 | The problem arises when we try to integrate the trajectory (that is, predict the future state) of a body in the presence of **both** these forces. 19 | If we rewrite the equations so that accelerations replace forces, we get thrust as a function of time (inverse linear) and gravity as a function of position (inverse square). 20 | What we want is closed-form solutions: velocity (first integral of acceleration) and position (second integral) as functions of time. 21 | Unfortunately, the equation we've just written cannot be generally solved this way, which means we cannot explicitly calculate the final state of a thrusting vehicle using a simple formula. 22 | Difficulty number one: how do predict where the vehicle will be in the future, given some thrust vector and gravity? 23 | 24 | Second major problem comes from the way we define our initial and target states. 25 | At the beginning the vehicle's state is simplest described using 2 vectors in Cartesian (XYZ) coordinates: position and velocity. 26 | But our target state is an orbit, not a single XYZ point - we would like to use [Keplerian elements](https://en.wikipedia.org/wiki/Orbital_elements) for that. 27 | After all we do not really care *where exactly* will the vehicle be, as long as it's on the right ellipse in the right plane. 28 | And if there is no particular point in space, there's also no particular velocity vector we need to achieve (it changes depending on position on the ellipse). 29 | Difficulty number two: how to formulate the math if we don't have a common coordinate frames for input and output? 30 | 31 | By the way, how will our answer even look like? 32 | Let's forget the integration problem for a moment (we can always employ some numeric method to find a solution, or compute it with brute force) and assume we *can* predict the future state of a vehicle. 33 | We know how the gravity will change (always points towards the center of a coordinate frame), and how the thrust magnitude will change, but what about thrust direction vector? 34 | Obviously, we are not interested only in pitch & yaw angles *right here, right now* - we have to somehow express them in a way that allows them to change, and in a way that allows us to integrate this change. 35 | Most logical assumption would be to parametrize them as functions of time (as we are looking for all other solutions as independent functions of time), but then how do we choose those functions? 36 | This is difficulty number three. 37 | 38 | Finally, even if we formulated the whole math apparatus to let us predict the future state of a thrusting vehicle in a gravity field, we would only have a set of equations that inputs current vehicle state, gravity parameters and thrust functions (pitch & yaw and magnitude) and outputs a new state in some time from "now". 39 | We need something that works the other way around: inputs current and desired states and outputs pitch & yaw functions. 40 | Final difficulty: how do we reverse those equations, so they yield what we need? 41 | 42 | Additionally, it's worth mentioning that we cannot use a half naive, half brute-force approach of precalculating a trajectory on an external (potentially very powerful) computer and then just have a vehicle follow it. 43 | Even the slightest deviations from that ideal path will accumulate on our way, along with errors from the unpredictable elements of the system: imprecise thrust vector control, thrust magnitude variations, turbulent air flow, imperfect gravity model - all can only be approximated, not predicted exactly. 44 | Thus, our guidance system must generate commands on-the-fly, basing on measured vehicle state. 45 | That imposes another difficulty: the system must be simple, robust and work as fast as possible. 46 | 47 | ### Solution 48 | ##### Coordinate frame 49 | We will tackle the second difficulty by deciding on a non-rotating Earth-centric XYZ coordinate frame: X and Y axes will lie in the equatorial plane (X aligned with the 0° meridian at some given time), Z will point north. 50 | It's easy to convert spherical coordinates (longitude, latitude & altitude) into XYZ - we just have to remember that the Earth constantly rotates, thus the launch site coordinates will also be continuously changing. 51 | Converting Keplerian coordinates of the target orbit into XYZ is more difficult. 52 | Since it is not a single, unique point in position&velocity space but rather a set of points, we can only express the desired state in terms of constraints. 53 | We will write them in the following form: 54 | * plane - defined by desired inclination and longitude of ascending node, 55 | * altitude - desired radius at MECO, 56 | * velocity - magnitude of the desired velocity vector, 57 | * flight path angle - angle between desired velocity vector and local horizontal. 58 | 59 | The first constraint can be easily and uniquely written in XYZ form as a vector normal to the target plane (we can use Euler formulas to calculate it knowing INC and LAN angles), explicitly describing where the orbit will be and what will be the direction of motion. 60 | Next three describe what the orbit will look like: knowing altitude (radius) and corresponding velocity and angle, we can draw an ellipse in a unique way. 61 | There's just one thing we cannot define: how will the orbit be rotated within the target plane (or, if we set the flight path angle to zero, assuming we burn out in perigee, what's our argument of periapsis). 62 | If the rocket takes longer to reach orbit, it'll cover more downrange distance from the launch site, and hence the AoP will be larger (than for a rocket getting to the same orbit faster from the same launch site). 63 | 64 | ##### Linear Tangent Guidance 65 | Now we can start formulating the math. 66 | This section is meant more as an intuitive explanation than a mathematically rigorous paper, so we will not write any equations here (these can be found below in the [literature](#literature) section). 67 | 68 | As mentioned in the first paragraph on difficulties, there are two forces acting on the vehicle: gravity and thrust. 69 | We model gravity with Newton's inverse square function of position and thrust acceleration with Tsiolkovsky's rocket equation. 70 | As mentioned in the third paragraph, we also need to model *thrust direction* somehow - preferrably with a simple function of time (allowing easy integration) that inputs some parameters (allowing control). 71 | The idea is to have a complete equation of motion with several independent, free variables to solve for, that we can then use to steer the vehicle. 72 | One of those variables we get for free: it's the time-to-MECO, which we can always choose (calculate) differently and get different end results. 73 | A reader with basic understanding of control theory will realize that we need as many control variables as the number of constraints we want to satisfy. 74 | 75 | The simplest choice would be a constant function, corresponding to a constant attitude throughout the whole maneuver (KSP players know this well, as it's the primary way of executing on-orbit maneuvers). 76 | Such a function would have only two parameters: pitch and yaw - possibly enough for short duration on-orbit burns, but not for ascent guidance. 77 | In this case adopting a linear function seems like a natural next choice. 78 | Linear Tangent Guidance is almost exactly that: it assumes that the *tangent* of pitch (or yaw) is a linear function of time; we could write `pitch=atan(At+B)` (and similarly for yaw). 79 | 80 | Parametrizing steering with Linear Tangent function has one large advantage: if we assumed a constant gravity function (that is, gave up the inverse square relation and replace it with an average vector, as if for a flat Earth), the equation of motion becomes directly integrable. 81 | There is a more general version of LTG called Bilinear Tangent Guidance, in which the tangent of each angle is a *ratio* of two linear functions (`pitch=atan((At+B)/(Ct+D))`). 82 | Both these parametrizations are optimal depending on the approximations made (citation needed) and provide closed-form integrals with constant gravity. 83 | Obviously, the flat Earth approximation introduces a potentially large error, which is the more of a problem the longer the burn takes. 84 | Whitcombe (see [literature](#literature)) says however, that there exist "fixes" for that approximation. 85 | Indeed, UPFG also contains an ingenious fix that allows it to benefit from integration simplicity of (a somewhat upgraded) LTG while getting rid of the gross approximation of constant gravity. 86 | This makes it an algorithm of choice even for long-duration maneuvers like Shuttle deorbit burn. 87 | 88 | ##### Predictor & corrector 89 | Integrating forward (current state + thrust vector => future state) is easy thanks to LTG, integrating backwards (current state + future state => thrust vector) is more difficult but also possible. 90 | We however do not have any future state, only constraints on it, which makes the problem harder. 91 | This is why UPFG does not attempt to solve it in a single shot - instead, it uses an iterative predictor-corrector scheme. 92 | That means, for a known current state (position, velocity) some guidance (pitch, yaw, angle rates) is calculated and some final state corresponding to it is *predicted* (it is not necessarily a desired state). Then this final state is *corrected* to meet the constraints, so that the next prediction is closer to desired. 93 | An important notion here is that we're optimizing two things simultaneously: first, we're looking for a future state that will meet the constraints (desired state); second, we're looking for a guidance that will make the vehicle reach this state from the current one. 94 | 95 | Some terminology will be introduced now: 96 | * `V` and `R`: current vehicle state (velocity and position), 97 | * `Vd` and `Rd`: desired velocity and position - we want those to meet the constraints set above, 98 | * `Vgrav` and `Rgrav`: changes to vehicle state over the burn trajectory purely due to gravity - purely means: as if the vehicle was coasting, 99 | * `Vgo` and `Rgo`: velocity and position that *need* to be gained due to thrust alone - **required** effect of thrust, 100 | * `Vthrust` and `Rthrust`: velocity and distance that *will be* gained due to thrust alone given some steering constants - **predicted** effect of thrust, 101 | * `Vbias` and `Rbias`: error (*bias*) terms between required (`Vgo`, `Rgo`) and predicted velocity/position change (`Vthrust`, `Rthrust`) - these measure how good our guidance is, 102 | * `Tgo`: time to the end of maneuver - important thing to mention is that in each call, the algorithm assumes the time is zero; hence, `Tgo` should be continuously and stably decreasing, and when it reaches zero thrust should be cut off. 103 | 104 | UPFG has several different modes of operation: one for standard ascent to orbit (`Smode=1`), two alternative ascent modes (reference trajectory and Lambert), two abort options and three for on-orbit maneuvers. In here we will focus on the standard ascent mode. 105 | 106 | The algorithm is structured in 9 functionally separate "blocks". 107 | The first one (**initialization**) is called exactly once, as it sets up the algorithm with initial values of all variables. 108 | Among those are the `Rd` and `Vd` - exactly how are they chosen is hard to tell (the document does not elaborate on that), and we will get to that later. 109 | Second block (**update**) reads accelerometers and provides the current state `R`, `V` - something we wouldn't have to do in a simplified simulation where all those are directly readable from the simulation memory (like PEGAS or KSP). 110 | It also decreases `Vgo` by velocity gained since the last iteration. 111 | 112 | Block 3 (**time-to-go**) calculates `Tgo` for this updated `Vgo` using the rocket equation and knowledge of vehicle parameters in each stage. 113 | In the original paper it is quite complicated, as the implementation contains some pieces specific to the Space Shuttle ascent mode (eg. it accounts for the coast period between SSME cutoff and OMS ignition). 114 | Block 4 (**integrals of thrust**) calculates *thrust integrals* - these describe vehicle capabilities in form of scalar integrals and their *moments*: for example `L` corresponds to remaining delta-v and `S` is "delta-r" (same concept but for distance); `J` will stand for the first moment of velocity, providing some information *on distribution of velocity increment in time* (citation needed). 115 | The equations in this block are arranged as they are to optimize memory usage - many variables are reused in computation of successive ones. 116 | 117 | Block 5 (**turning rate**) is where things start to get really interesting. 118 | The main goal here is to calculate the thrust vector and its turning rate and predict its immediate effects. 119 | First, `Rgo` is calculated between current and desired states, taking into account the gravity effect vector (`Rgrav`) as well as previous position error (`Rbias`). 120 | Thrust vector and its turning rate are then explicitly calculated from LTG relationships, using `Vgo`. 121 | Next, change to position and *velocity* due to this thrust vector - `Rthrust` and `Vthrust` - is calculated using integrals from the previous block. 122 | Finally, a check is made how well this trajectory matches the required one: ideally `Rgo` should equal `Rthrust` (which would mean: if this thrust vector and turning rate are maintained, the change of position due to it will exactly cover the difference between the current and desired, countering the gravity pull on the way; the same should be true for velocity). 123 | Difference of those values is stored as an error (`Rbias`, `Vbias`) for the next iteration. 124 | 125 | Block 7 (**gravity effects**) integrates the effects of a central gravity field on the thrust trajectory. 126 | The idea is to find displacement and velocity change (`Rgrav`, `Vgrav`) between the current state and the future state (in `Tgo` seconds from "now"), knowing the state change due to thrust alone. 127 | Was there no thrust and the vehicle was freely moving in a gravity field, an exact calculation would be possible - UPFG uses the *Conic State Extrapolation* routine to determine the final state. 128 | However, it is still problematic to incorporate gravity into a powered trajectory without approximating it with a constant vector; a different, more sophisticated but more accurate approximation is made. 129 | In this approach, the actual powered trajectory is replaced with an approximate coast trajectory. 130 | By changing the initial conditions in a special way (using thrust effect integrals `Rthrust`, `Vthrust`) we can generate a coast trajectory that minimizes the average position error between it and the actual powered one. 131 | The modified current state, `Rc1` and `Vc1` is calculated, and then the CSE routine used to integrate trajectory, resulting in final state: `Rc2` and `Vc2`. 132 | Difference between `Rc2` and `Rc1` is then a gravity effect integral `Rgrav` for the approximated powered trajectory; the same follows for velocity. 133 | CSE itself is moderately precise (there exists a more accurate but slower routine, the *Precision State Extrapolation Routine*), but here it works with an approximated trajectory, which naturally results in some error. 134 | For this reason, gravity calculation must be periodically updated to prevent the error from accumulating. 135 | 136 | Block 8 (**velocity-to-be-gained**) corrects the desired states (`Vd`, `Rd`), forcing them to meet the constraints. 137 | First the actual cutoff position is predicted (`Rp` - "p" for prediction) simply by summing already obtained vectors: current state (`R` and `V` times `Tgo`), gravity effects, thrust effects. 138 | Then, to obtain a new desired state `Rd`, the magnitude of this vector is corrected to meet the desired altitude - note how this will result in a `Rbias` term change in block 5 in the next iteration. 139 | Finally, an entirely new desired velocity `Vd` is calculated at this position: all remaining constraints are satisfied in this step (normal vector, velocity magnitude, flight path angle). 140 | *Important notice: for some reason, the target plane normal vector* `iY` *in UPFG is oriented opposite to the orbital angular velocity vector.* 141 | `Vgo` is then updated as a difference between this desired velocity and the current one, keeping in mind that the gravity will also affect the vehicle's motion (`Vgrav`) and propagating a remaining velocity error (`Vbias`) for the next iteration. 142 | 143 | ##### Convergence 144 | If we wrote all the equations into simplified relations using only symbols defined in the terminology section (skipping the intermediate variables used in the actual formulation), we'd find that everything is a function of everything else. 145 | Block 5 uses position terms (current, desired, gravity effects integral, error term) to generate guidance, which it then uses to predict thrust effects integrals (both velocity and position terms); these are finally used to calculate new errors. 146 | Block 7 updates gravity integrals estimation over the new trajectory. 147 | Block 8 uses position terms (current, new gravity integral, new thrust effect prediction) and constraints to generate a new desired state. 148 | Then it uses velocity terms (current, freshly generated desired one, gravity integral, error term) to calculate a new velocity to go that will be used in the next call to block 5. 149 | 150 | When the algorithm is first called, the error between predicted and desired states can be very large and thus the resulting steering constants are not very reliable. 151 | The original document refers to this phase as a *prethrust mode* - each call (=*iteration*) the errors should decrease and the guidance approach a good one. 152 | After several iterations the predicted state should closely match the desired - when that happens we say that the algorithm has *converged*, and the vehicle can safely start following the calculated guidance. 153 | From then on the algorithm is in *active guidance mode* - each next call only corrects for any errors (eg. from gravity calculation). 154 | 155 | Last remaining problem: if everything is a function of everything and the algorithm somehow "converges" on the "right" guidance and predictions... where do we start from? 156 | Block 5 uses desired position `Rd` which is not known until block 8, and without `Rd` there is no `Vd` without which there can be no `Vgo` which is used as early as block 2. 157 | Unfortunately the original paper does not provide any information on how to determine the initial value of `Rd`. 158 | PEGAS implementation contains a custom logic to generate an initial guess on this value. 159 | It follows from an (empirically confirmed) assumption that if this guess is reasonable enough, UPFG will be able to correct for the guess error and converge on a true value. 160 | The reasoning behind the guess is the fact that the vehicle is already in motion in some direction - so it is only possible that the cutoff position will be somewhere that way. 161 | Just how far ahead (in terms of downrange distance) is a matter of vehicle performance: for example, low-thrust upper stages can make very long burn arcs. 162 | PEGAS generates the initial guess by rotating the initial position vector in the direction of motion by an arbitrary angle and correcting the resulting vector so that it meets plane and altitude constraints. 163 | Then `Vd` is calculated for this position following the equation in block 8; `Vgo` is initialized very simply as `Vd` minus initial `V`. 164 | It turns out that these values result in UPFG convergence within 2-3 iterations (for a reference Space Shuttle mission, convergence criterion: change in `Tgo` between iterations less than 1%). 165 | 166 | ### Epilogue 167 | Two of the blocks were not mentioned in the article: 6 and 9. 168 | Skipped on purpose, they are not used in guidance and could be omitted altogether in a very simplified simulation; the original document labels them both as *To Be Determined*. 169 | Block 6 (**steering commands**) would generate thrust vector *commands* - that is, convert the thrust direction vector `iF` and thrust turning rate vector into pitch and yaw angles and rates (perhaps also engine gimbal commands?). 170 | This block is implemented in PEGAS using simple vector transformations (pitch is the angle between `iF` and local UP vector `unit(R)`, yaw is the angle between planar component of `iF` and local EAST); angle rates are not calculated. 171 | Block 9 (**throttle commands**) would generate throttle setting and engine on/off commands. 172 | Standard ascent mode uses either a constant-thrust mode (throttle being set to max) or a constant-acceleration mode, where the throttle is gradually reduced in order to enforce a G-limit on the vehicle. 173 | Other modes (eg. ascent to reference trajectory) might use active throttling to provide an additional degree of control freedom. 174 | Additionally, this block would issue an engine-off command basing on its characteristics, probably to allow a precisely timed shutdown accounting for thrust tail-off and valve response times. 175 | 176 | The above should walk the reader through the UPFG algorithm and provide a general understanding of the concept. 177 | For a more in-depth understanding, *iterating* over the original document, preferrably with a pencil and paper, is recommended. 178 | 179 | 180 | ### Literature 181 | * D. W. Whitcombe [Present and Advanced Guidance Techniques](http://www.dtic.mil/docs/citations/AD0754923) - "present" refers to 1972, but this is a decent entry-level survey of guidance techniques with many references 182 | * T. J. Brand, D. W. Brown, J. P. Higgins [Unified Powered Flight Guidance](https://ntrs.nasa.gov/search.jsp?R=19740004402) - original formulation of UPFG with equations, block diagrams and drawings 183 | * F. Teren [Explicit Guidance Equations for Multistage Boost Trajectories](https://ntrs.nasa.gov/search.jsp?R=19660006073) - simpler, earlier algorithm: Powered Explicit Guidance 184 | * Orbiter Wiki [Powered Explicit Guidance](http://www.orbiterwiki.org/wiki/Powered_Explicit_Guidance) - this article serves as a bridge between tough mathematics of the above paper and understanding of PEG, but does not concern yaw control 185 | --------------------------------------------------------------------------------