diff --git a/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.pdf b/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.pdf index eb0b689ab0..488062b065 100644 Binary files a/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.pdf and b/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.pdf differ diff --git a/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.tex b/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.tex index 32f5fc768f..6286e096bb 100644 --- a/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.tex +++ b/Doc/Project Apollo - NASSP/RTCC/ApolloRTCCMFD.tex @@ -263,12 +263,56 @@ \subsubsection{Buttons} \textbf{CLC:} Calculate the maneuver.\\ \textbf{MPT:} Create finite maneuver from impulsive burn.\\ \textbf{BCK:} Go back to the main menu.\\ - +\newpage \subsection{TLI Planning} +\subsubsection{Introduction} +On a typical lunar mission all the targeting needed for TLI has been loaded into the Saturn computer (LVDC) before launch. For a dispersed Earth orbit or an alternative mission the RTCC has the capability to target a new TLI or an alternative second S-IVB burn. The options available in the TLI processor are + +\begin{enumerate} + \item S-IVB hypersurface solution. Evaluates the preloaded targeting in the LVDC + \item Integrated free-return trajectory to the nominal lunar flyby conditions + \item Specified total Delta V + \item Specified apogee altitude + \item Integrated non-free return trajectory to the nominal nodal position at the Moon +\end{enumerate} -To be determined.\\ +\subsubsection{Inputs} +\textbf{IU:} Select the vessel that is used for the uplink of the updated TLI targeting. In non-MPT mode also used as the vessel from which the state vector and weights are taken for the TLI processor calculation.\\ +\textbf{MPT:} Select CSM or LM mission plan table (MPT mode only).\\ +\textbf{VEC:} Select the state vector used in the calculation. Normally the anchor vector of the CSM or LM ephemeris is used, but any vector in the useable vector table can be specified (MPT mode only).\\ +\textbf{Opp:} Select the TLI opportunity. For option 1 this is the input to choose when the TLI is done and for all options this changes certain parameters associated with one of the opportunities in the LVDC, mostly the time of S-IVB mixture ratio shift from ignition.\\ +\textbf{Mode:} Choose the TLI processor calculation mode, see list above.\\ +\textbf{TIG:} Time of ignition. Not used for option 1. Used as a very rough initial guess in option 2 and 5, that only has to be accurate to a full orbit. In options 3 and 4 this is the exact time of ignition used.\\ +\textbf{DV:} Delta V of the maneuver (option 3 only).\\ +\textbf{APO:} Apogee altitude to be achieved by the maneuver (option 4 only).\\ +\textbf{Window:} Pacific or Atlantic TLI window, for the lunar mission options (2 and 5) this is used to generate the initial guess.\\ +\textbf{SFP:} For the lunar mission options (2 and 5) the target parameters are taken from the Skeleton Flight Plan table, which has two sets of data. See the Midcourse Correction Processor for more details about the SFP.\\ + +\textbf{CLC:} Calculate solution.\\ +\textbf{MPT:} Transfer maneuver to MPT.\\ +\textbf{UPL:} Uplink solution to IU.\\ \newpage +\subsubsection{Display} +\begin{figure}[hp] + \centering + \includegraphics{./ApolloRTCCMFDFiles/TLIProcessorDisplayExample.png} + \caption{TLI Planning Display} + \label{fig:TLIProcessorDisplayExample} +\end{figure} + +\textbf{Display Parameters}\\ + +\textbf{GET RP:} Ground Elapsed Time of Restart Preparations (TB6)\\ +\textbf{GET TIG:} Ground Elapsed Time of main engine ignition\\ +\textbf{INCL:} Inclination at cutoff\\ +\textbf{DESC:} Longitude of the descending node at cutoff\\ +\textbf{ECC:} Eccentricity at cutoff\\ +\textbf{C3:} Orbital energy at cutoff\\ +\textbf{ALPHA:} Angle from perigee of cutoff ellipse to descending node of cutoff ellipse\\ +\textbf{TA:} True anomaly at cutoff\\ +\textbf{BT:} Burn time of maneuver\\ +\newpage \subsection{Midcourse Correction Processor} \subsubsection{Introduction} diff --git a/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.cpp b/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.cpp index 173606bccc..358b1fecfd 100644 --- a/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.cpp @@ -6107,17 +6107,96 @@ void RTCC::PMDLDPP(const LDPPOptions &opt, const LDPPResults &res, LunarDescentP table.PD_ThetaIgn = opt.theta_D*DEG; } -void RTCC::TranslunarInjectionProcessor(EphemerisData sv, PLAWDTOutput WeightsTable) +void RTCC::TranslunarInjectionProcessor(bool mpt, EphemerisData *sv, PLAWDTOutput *WeightsTable) { TLIMEDQuantities medquant; TLMCCMissionConstants mccconst; TLIOutputData out; + if (mpt) + { + //Get state vector + if (PZTLIPLN.VectorType == "ANC") + { + if (PZTLIPLN.mpt == RTCC_MPT_CSM) + { + medquant.state = EZANCHR1.AnchorVectors[9].Vector; + } + else + { + medquant.state = EZANCHR3.AnchorVectors[9].Vector; + } + } + else + { + int val = GetStateVectorTableEntry(PZTLIPLN.VectorType, PZTLIPLN.mpt); + if (val < 0) + { + PMXSPT("PMMEPP", 300); + return; + } + + medquant.state = BZUSEVEC.data[val].Vector; + } + + if (medquant.state.GMT == 0.0) + { + PMXSPT("PMMEPP", 300); + return; + } + + //Get weight + PLAWDTInput inp; + PLAWDTOutput outp; + + inp.T_UP = medquant.state.GMT; + inp.TableCode = PZTLIPLN.mpt; + inp.VentingOpt = true; + + PLAWDT(inp, outp); + + medquant.WeightsTable = outp; + } + else + { + medquant.state = *sv; + medquant.WeightsTable = *WeightsTable; + } + + medquant.mpt = PZTLIPLN.mpt; medquant.Mode = PZTLIPLN.Mode; - medquant.state = sv; - medquant.WeightsTable = WeightsTable; medquant.h_ap = PZTLIPLN.h_ap*1852.0; + medquant.dv_available = PZTLIPLN.dv_available*0.3048; medquant.GMT_TIG = GMTfromGET(PZTLIPLN.GET_TLI); + medquant.IPOA = PZTLIPLN.IsPacficWindow ? 1 : 2; + medquant.Opportunity = PZTLIPLN.Opportunity; + + if (PZTLIPLN.Mode == 2 || PZTLIPLN.Mode == 5) + { + TLMCCDataTable *tab = &PZSFPTAB.blocks[PZMCCPLN.SFPBlockNum - 1]; + + //Error return if SFP is not loaded + if (tab->GMTTimeFlag == 0.0) + { + PMXSPT("PMMEPP", 302); + return; + } + + if (PZTLIPLN.Mode == 2) + { + //Free return + medquant.h_PC = tab->h_pc1; + medquant.lat_PC = tab->lat_pc1; + } + else + { + //Non-free return + medquant.h_PC = tab->h_nd; + medquant.lat_PC = tab->lat_nd; + medquant.lng_node = tab->lng_nd; + medquant.GMT_node = tab->GMT_nd; + } + } mccconst.delta = PZTLIPLN.DELTA; mccconst.sigma = PZTLIPLN.SIGMA; @@ -6127,15 +6206,33 @@ void RTCC::TranslunarInjectionProcessor(EphemerisData sv, PLAWDTOutput WeightsTa tli.Init(medquant, mccconst, GetGMTBase()); tli.Main(out); - if (out.ErrorIndicator) return; + if (out.ErrorIndicator) + { + RTCCONLINEMON.IntBuffer[0] = out.ErrorIndicator; + PMXSPT("PMMEPP", 301); + return; + } + //Output table PZTTLIPL.DataIndicator = 1; PZTTLIPL.elem = out.uplink_data; + //MPT interface table + PZTPDXFR.DataIndicator = true; + PZTPDXFR.T_RP = out.uplink_data.GMT_TIG - SystemParameters.MDVSTP.DTIG; + PZTPDXFR.i = out.uplink_data.Inclination; + PZTPDXFR.theta_N = out.uplink_data.theta_N; + PZTPDXFR.e = out.uplink_data.e; + PZTPDXFR.C3 = out.uplink_data.C3; + PZTPDXFR.alpha_D = out.uplink_data.alpha_D; + PZTPDXFR.f = out.uplink_data.f; + PZTPDXFR.Opportunity = PZTLIPLN.Opportunity; + //Display data PZTPDDIS.GET_TIG = GETfromGMT(out.uplink_data.GMT_TIG); PZTPDDIS.GET_TB6 = PZTPDDIS.GET_TIG - SystemParameters.MDVSTP.DTIG; PZTPDDIS.dv_TLI = out.dv_TLI / 0.3048; + PZTPDDIS.T_b = out.sv_TLI_cut.GMT - out.sv_TLI_ign.GMT; } void RTCC::TranslunarMidcourseCorrectionProcessor(EphemerisData sv0, double CSMmass, double LMmass) @@ -6147,6 +6244,16 @@ void RTCC::TranslunarMidcourseCorrectionProcessor(EphemerisData sv0, double CSMm datatab = PZSFPTAB.blocks[PZMCCPLN.SFPBlockNum - 1]; + //TBD: Some old scenarios have a filled SFP, but GMTTimeFlag is zero + /* + //Error return if SFP is not loaded + if (datatab.GMTTimeFlag == 0.0) + { + PMXSPT("PMMLCP", 302); + return; + } + */ + medquant.Mode = PZMCCPLN.Mode; medquant.Config = PZMCCPLN.Config; medquant.T_MCC = GMTfromGET(PZMCCPLN.MidcourseGET); @@ -6281,14 +6388,36 @@ void RTCC::TLI_PAD(const TLIPADOpt &opt, TLIPAD &pad) in.mpt = &mpt; in.PresentGMT = 0.0; //Is this ok? in.PrevMan = NULL; - in.QUEID = 37; //Direct input S-IVB TLI maneuver + if (opt.StudyAid) + { + in.QUEID = 36; //Transfer from TLI planning study aid + + if (PZTPDXFR.DataIndicator == false) + { + //Error + return; + } + in.T_RP = PZTPDXFR.T_RP; + in.i = PZTPDXFR.i; + in.theta_N = PZTPDXFR.theta_N; + in.e = PZTPDXFR.e; + in.C3 = PZTPDXFR.C3; + in.alpha_D = PZTPDXFR.alpha_D; + in.f = PZTPDXFR.f; + in.InjOpp = PZTPDXFR.Opportunity; + } + else + { + in.QUEID = 37; //Direct input S-IVB TLI maneuver + in.T_RP = -1.0; + } + in.R = opt.sv0.R; in.ReplaceCode = 0; in.StartTimeLimit = 0.0; in.Table = RTCC_MPT_CSM; in.ThrusterCode = RTCC_ENGINETYPE_SIVB_MAIN; in.TVC = RTCC_MPT_CSM; - in.T_RP = -1.0; in.V = opt.sv0.V; int err = PMMSPT(in); @@ -6316,32 +6445,40 @@ void RTCC::TLI_PAD(const TLIPADOpt &opt, TLIPAD &pad) EMMENI(emmeniin); sv_TH = emmeniin.sv_cutoff; - //Continue processing, when the MPT uses PMMSPT with QUEID = 37 then it returns the threshold time first, before it continues processing with QUEID = 39 - in.QUEID = 39; - //Input state vector at threshold, MPT would input a state vector from the ephemeris at the threshold time - in.R = sv_TH.R; - in.V = sv_TH.V; - in.GMT = sv_TH.GMT; - //T_RP reset to zero so that the actual time of restart preparations is calculated - in.T_RP = 0.0; - err = PMMSPT(in); + EphemerisData sv_TB6; + if (opt.StudyAid == false) + { + //Continue processing, when the MPT uses PMMSPT with QUEID = 37 then it returns the threshold time first, before it continues processing with QUEID = 39 + in.QUEID = 39; + //Input state vector at threshold, MPT would input a state vector from the ephemeris at the threshold time + in.R = sv_TH.R; + in.V = sv_TH.V; + in.GMT = sv_TH.GMT; + //T_RP reset to zero so that the actual time of restart preparations is calculated + in.T_RP = 0.0; + err = PMMSPT(in); - if (err) return; + if (err) return; - //Take SV to TB6 - dt = man.GMTMAN - opt.sv0.GMT; - if (dt >= 0) - { - emmeniin.IsForwardIntegration = 1.0; + //Take SV to TB6 + dt = man.GMTMAN - opt.sv0.GMT; + if (dt >= 0) + { + emmeniin.IsForwardIntegration = 1.0; + } + else + { + emmeniin.IsForwardIntegration = -1.0; + } + emmeniin.MaxIntegTime = abs(dt); + emmeniin.AnchorVector = opt.sv0; + EMMENI(emmeniin); + sv_TB6 = emmeniin.sv_cutoff; } else { - emmeniin.IsForwardIntegration = -1.0; + sv_TB6 = sv_TH; } - emmeniin.MaxIntegTime = abs(dt); - emmeniin.AnchorVector = opt.sv0; - EMMENI(emmeniin); - EphemerisData sv_TB6 = emmeniin.sv_cutoff; //Update mass plain.CSMArea = 0.0; @@ -8358,7 +8495,7 @@ int RTCC::PMMSPT(PMMSPTInput &in) VECTOR3 TargetVector, T_P, R, V, P, S, P_dot, S_dot, Hbar, H_apo, W, Sbar_1, Cbar_1, Omega_X, Omega_Y, Omega_Z; double T_TH, alpha_TSS, alpha_TS, beta, T_RP, f, R_N, KP0, KY0, T3_apo, T_ST, tau3R, T2, Vex2, Mdot2, DV_BR, tau2N, lambda, dt_ig, T, dt4, dt4_apo; double t_D, cos_sigma, C3, e_N, RA, DEC, theta_E, RR, SINB, COSB, COSATS, t, p, r, vv, C1, C2, p_dot, h, w, du_apo, c, theta, du, cos_psi, sin_psi; - double i, X1, X2, theta_N, P_N, T_M, alpha_D_apo, P_RP, Y_RP, T3, tau3, A_Z, Azo, Azs, i_P, theta_N_P; + double i, X1, X2, theta_N, P_N, T_M, alpha_D_apo, P_RP, Y_RP, T3, tau3, A_Z, Azo, Azs, i_P, theta_N_P, K5, R_T, V_T, gamma_T, G_T; int J, OrigTLIInd, CurTLIInd, LD, counter, GRP15, KX, err, n; unsigned ORER_out; @@ -8421,20 +8558,27 @@ int RTCC::PMMSPT(PMMSPTInput &in) } if (in.QUEID == 34) { - //TBD: load data from study aid. Or not? + //Study Aid goto RTCC_PMMSPT_3_2; } T_RP = in.T_RP; - if (T_RP > 0) + //Were target parameters input? + if (in.e >= 0.0) { - OrigTLIInd = -in.InjOpp; + OrigTLIInd = 0; } else { - RTCC_PMMSPT_3_2: - OrigTLIInd = in.InjOpp; + if (T_RP > 0) + { + OrigTLIInd = -in.InjOpp; + } + else + { + RTCC_PMMSPT_3_2: + OrigTLIInd = in.InjOpp; + } } - CurTLIInd = OrigTLIInd; J = in.InjOpp; RTCC_PMMSPT_4_1: @@ -8470,10 +8614,12 @@ int RTCC::PMMSPT(PMMSPTInput &in) if (OrigTLIInd > 0) { + //Opportunity only input goto RTCC_PMMSPT_6_3; } else if (OrigTLIInd == 0) { + //Opportunity, T_RP and target params input goto RTCC_PMMSPT_8_2; } @@ -8566,6 +8712,15 @@ int RTCC::PMMSPT(PMMSPTInput &in) } t_D = GetGMTLO()*3600.0 - tlitab->T_LO; + if (OrigTLIInd == 0) + { + goto RTCC_PMMSPT_15_1; + } + else if (OrigTLIInd > 0) + { + //TBD: Move R, V at T_TH into output table + } + GRP15 = PCMSP2(tlitab, J, t_D, cos_sigma, C3, e_N, RA, DEC); if (GRP15) { @@ -8580,8 +8735,10 @@ int RTCC::PMMSPT(PMMSPTInput &in) RR = dotp(R, R); COSB = cos(beta); SINB = sin(beta); + //T_RP input? if (OrigTLIInd < 0) { + //Yes goto RTCC_PMMSPT_13_2; } alpha_TS = alpha_TSS + tlitab->K_a1*dt4_apo + tlitab->K_a2*dt4_apo*dt4_apo; @@ -8664,16 +8821,37 @@ int RTCC::PMMSPT(PMMSPTInput &in) P_N = (OrbMech::mu_Earth / C3)*(e_N*e_N - 1.0); T_M = P_N / (1.0 - e_N * cos_sigma); alpha_D_apo = acos(cos_psi) + atan2(dotp(Sbar_1, crossp(Cbar_1, Omega_Y)), dotp(S, crossp(Cbar_1, Omega_Y))); - in.CurMan->dV_inertial.x = i; - in.CurMan->dV_inertial.y = theta_N; - in.CurMan->dV_inertial.z = e_N; - in.CurMan->dV_LVLH.x = C3; - in.CurMan->dV_LVLH.y = alpha_D_apo; - in.CurMan->dV_LVLH.z = f; + //Store i, theta_N, alpha_D_apo, f, R_N, T_M in MPT block + in.CurMan->Inclination = i; + in.CurMan->theta_N = theta_N; + in.CurMan->Eccentricity = e_N; + in.CurMan->C3 = C3; + in.CurMan->alpha_D = alpha_D_apo; + in.CurMan->f = f; in.CurMan->Word67d = R_N; in.CurMan->Word69 = T_M; goto RTCC_PMMSPT_15_2; -//RTCC_PMMSPT_15_1: +RTCC_PMMSPT_15_1: + //Store input i, theta_N, e, C3, alpha_D, f in MPT block + in.CurMan->Inclination = in.i; + in.CurMan->theta_N = in.theta_N; + in.CurMan->Eccentricity = in.e; + in.CurMan->C3 = in.C3; + in.CurMan->alpha_D = in.alpha_D; + in.CurMan->f = in.f; + P_N = (OrbMech::mu_Earth / in.C3)*(in.e*in.e - 1.0); + K5 = sqrt(OrbMech::mu_Earth / P_N); + R_T = P_N / (1.0 + in.e*cos(in.f)); + V_T = K5 * sqrt(1.0 + 2.0*in.e*cos(in.f) + in.e*in.e); + gamma_T = atan(in.e*sin(in.f) / (1.0 + in.e*cos(in.f))); + G_T = -OrbMech::mu_Earth / pow(R_T, 2); + //Store P, K5, R_T, V_T, gamma_T, G_T in MPT block + in.CurMan->Word67d = P_N; + in.CurMan->Word68 = K5; + in.CurMan->Word69 = R_T; + in.CurMan->Word70 = V_T; + in.CurMan->Word71 = gamma_T; + in.CurMan->Word72 = G_T; RTCC_PMMSPT_15_2: P_RP = KP0 + SystemParameters.MDVSTP.KP1 * dt4_apo + SystemParameters.MDVSTP.KP2 * dt4_apo*dt4_apo; Y_RP = KP0 + SystemParameters.MDVSTP.KY1 * dt4_apo + SystemParameters.MDVSTP.KY2 * dt4_apo*dt4_apo; @@ -8732,7 +8910,9 @@ int RTCC::PMMSPT(PMMSPTInput &in) EPH = mul(OrbMech::tmat(A), M); BB = _M(cos(theta_N_P), 0, sin(theta_N_P), sin(theta_N_P)*sin(i_P), cos(i_P), -cos(theta_N_P)*sin(i_P), -sin(theta_N_P)*cos(i_P), sin(i_P), cos(theta_N_P)*cos(i_P)); GG = mul(BB, A); - B = _M(cos(theta_N), 0, sin(theta_N), sin(theta_N)*sin(i), cos(i), -cos(theta_N)*sin(i), -sin(theta_N)*cos(i), sin(i), cos(theta_N)*cos(i)); + B = _M(cos(in.CurMan->theta_N), 0, sin(in.CurMan->theta_N), + sin(in.CurMan->theta_N)*sin(in.CurMan->Inclination), cos(in.CurMan->Inclination), -cos(in.CurMan->theta_N)*sin(in.CurMan->Inclination), + -sin(in.CurMan->theta_N)*cos(in.CurMan->Inclination), sin(in.CurMan->Inclination), cos(in.CurMan->theta_N)*cos(in.CurMan->Inclination)); G = mul(B, A); in.CurMan->GMTI = T_RP + SystemParameters.MDVSTP.DTIG; @@ -14067,6 +14247,16 @@ void RTCC::PMXSPT(std::string source, int n) case 201: message.push_back(RTCCONLINEMON.TextBuffer[0]); break; + case 300: + message.push_back("VECTOR NOT AVAILABLE"); + break; + case 301: + sprintf_s(Buffer, "COMPUTATION FAILED WITH ERROR CODE %d", RTCCONLINEMON.IntBuffer[0]); + message.push_back(Buffer); + break; + case 302: + message.push_back("SKELETON FLIGHT PLAN TABLE NOT AVAILABLE"); + break; default: return; } @@ -19097,14 +19287,30 @@ void RTCC::PMSVCT(int QUEID, int L, StateVectorTableEntry sv0) //Page 7 //DTREAD MPT Header RTCC_PMSVCT_8: - bool tli = false; for (unsigned i = 0;i < mpt->mantable.size();i++) { if (mpt->mantable[i].AttitudeCode == RTCC_ATTITUDE_SIVB_IGM || mpt->mantable[i].AttitudeCode == RTCC_ATTITUDE_PGNS_DESCENT) { if (mpt->mantable[i].GMTMAN > sv0.Vector.GMT) { - if (mpt->mantable[i].FrozenManeuverInd == false) + //Iterable? + int temp; + if (mpt->mantable[i].FrozenManeuverInd) + { + temp = false; + } + else + { + if (mpt->mantable[i].AttitudeCode == RTCC_ATTITUDE_SIVB_IGM) + { + temp = mpt->mantable[i].Word78i[1] != 0; + } + else + { + temp = true; + } + } + if (temp) { //EMSMISS and PMMSPT for TLI/PDI EMSMISSInputTable intab; @@ -22296,7 +22502,7 @@ int RTCC::PMMXFR(int id, void *data) //Initialize flags working_man = 1; //Direct inputs (RTE, MPT, TLI) - if (id == 32 || id == 33 || id == 37) + if (id == 32 || id == 33 || id == 36 || id == 37) { PMMXFRDirectInput *inp = static_cast(data); std::string purpose; @@ -22402,24 +22608,42 @@ int RTCC::PMMXFR(int id, void *data) BPIND = inp->BurnParameterNumber; } //TLI - else if (id == 37) + else if (id == 36 || id == 37) { purpose = "TL"; - PMMSPTInput tliin; - - tliin.QUEID = id; - tliin.PresentGMT = RTCCPresentTimeGMT(); - tliin.ReplaceCode = 0; - tliin.InjOpp = inp->BurnParameterNumber; - tliin.Table = inp->TableCode; + if (id == 36) + { + //TLI study aid - if (err = PMMSPT(tliin)) + //Does it exist? + if (PZTPDXFR.DataIndicator == false) + { + //No + PMXSPT("PMMSPT", 38); + return 38; + } + inp->GMTI = PZTPDXFR.T_RP; + } + else { - PMXSPT("PMMSPT", err); - return 1; + PMMSPTInput tliin; + + tliin.QUEID = id; + tliin.PresentGMT = RTCCPresentTimeGMT(); + tliin.ReplaceCode = 0; + tliin.InjOpp = inp->BurnParameterNumber; + tliin.Table = inp->TableCode; + + if (err = PMMSPT(tliin)) + { + PMXSPT("PMMSPT", err); + return 1; + } + + inp->GMTI = tliin.T_RP; } - inp->GMTI = tliin.T_RP; + inp->ThrusterCode = RTCC_ENGINETYPE_SIVB_MAIN; inp->AttitudeCode = RTCC_ATTITUDE_SIVB_IGM; inp->ConfigurationChangeIndicator = RTCC_CONFIGCHANGE_NONE; @@ -22502,7 +22726,6 @@ int RTCC::PMMXFR(int id, void *data) in.CurMan = &man; in.EndTimeLimit = UpperLimit; in.GMT = sv.GMT; - in.InjOpp = inp->BurnParameterNumber; in.mpt = mpt; in.PresentGMT = RTCCPresentTimeGMT(); if (mpt->mantable.size() == 0) @@ -22513,14 +22736,34 @@ int RTCC::PMMXFR(int id, void *data) { in.PrevMan = &mpt->mantable.back(); } - in.QUEID = 39; + if (id == 36) + { + //Study aid + in.QUEID = 36; + in.T_RP = PZTPDXFR.T_RP; + in.i = PZTPDXFR.i; + in.theta_N = PZTPDXFR.theta_N; + in.e = PZTPDXFR.e; + in.C3 = PZTPDXFR.C3; + in.alpha_D = PZTPDXFR.alpha_D; + in.f = PZTPDXFR.f; + in.InjOpp = PZTPDXFR.Opportunity; + } + else + { + //Direct transfer + in.QUEID = 39; + in.T_RP = 0.0; + in.InjOpp = inp->BurnParameterNumber; + } + in.R = sv.R; in.ReplaceCode = inp->ReplaceCode; in.StartTimeLimit = LowerLimit; in.Table = inp->TableCode; in.ThrusterCode = RTCC_ENGINETYPE_SIVB_MAIN; in.TVC = RTCC_MANVEHICLE_SIVB; - in.T_RP = 0.0; + in.V = sv.V; err = PMMSPT(in); @@ -30786,6 +31029,29 @@ int RTCC::PMMMED(std::string med, std::vector data) void *vPtr = &inp; return PMMXFR(32, vPtr); } + //TLI Study Aid Transfer + else if (med == "75") + { + //Item 1: Vehicle + rtcc::AddTextMEDItem(opt, 1, MHGVNM.tab); + //Item 2: Replace option + rtcc::AddIntegerMEDItem(opt, 2, true, true, 0, 15, 0); + + int err = rtcc::GenericMEDProcessing(opt, data, out); + if (err) + { + //param = out.errorItem; + return 0; + } + + PMMXFRDirectInput inp; + + inp.TableCode = out.Values[0].i == 0 ? RTCC_MPT_CSM : RTCC_MPT_LM; + inp.ReplaceCode = out.Values[1].i; + + void *vPtr = &inp; + return PMMXFR(36, vPtr); + } //LOI and MCC Transfer else if (med == "78") { @@ -36043,6 +36309,25 @@ int RTCC::BMSVPSVectorFetch(const std::string &vecid, EphemerisData &sv_out) return 1; } +int RTCC::GetStateVectorTableEntry(std::string VectorType, int mpt) +{ + int val; + + if (VectorType == "CMC") val = 0; + else if (VectorType == "LGC") val = 1; + else if (VectorType == "AGS") val = 2; + else if (VectorType == "IU") val = 3; + else if (VectorType == "HSR") val = 4; + else if (VectorType == "DC") val = 5; + else return -1; + + if (mpt == RTCC_MPT_LM) + { + val += 6; + } + return val; +} + void RTCC::BMSVPS(int queid, int PBIID) { //0 = Vector control queue diff --git a/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.h b/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.h index 3dce69d8de..8d05785fd7 100644 --- a/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.h +++ b/Orbitersdk/samples/ProjectApollo/src_launch/rtcc.h @@ -572,6 +572,7 @@ struct TLIPADOpt VECTOR3 SeparationAttitude; //LVLH IMU angles double ConfigMass; int InjOpp; //Injection opportunity (1 or 2) + bool StudyAid = false; //false = Planned TLI, true = from TLI processor }; struct P27Opt @@ -1363,11 +1364,20 @@ struct MPTManeuver //Word 59, DPS scale factor (0 to 1) double DPSScaleFactor; //Word 61-63 - //For ascent maneuver this is: R_D, Y_D - VECTOR3 dV_inertial; //In RTCC coordinates - //Word 64-66, In P30 coordinates - //For ascent maneuver this is: R_D_dot, Y_D_dot, Z_D_dot - VECTOR3 dV_LVLH; + union + { + VECTOR3 dV_inertial; //In RTCC coordinates + struct { double R_D, Y_D; }; //Ascent + struct { double Inclination, theta_N, Eccentricity; }; //TLI + }; + //Word 64-66 + union + { + VECTOR3 dV_LVLH; //In P30 coordinates + struct { double R_D_dot, Y_D_dot, Z_D_dot; }; //Ascent + struct { double C3, alpha_D, f; }; //TLI + }; + //Word 67 union { //ExtDVCoordInd and @@ -1702,13 +1712,23 @@ struct PMMSPTInput unsigned ReplaceCode; int InjOpp; //Word 12 (Time of ignition for TLI confirmation, negative if not input) - double T_RP = -1; + double T_RP = -1.; //Word 13 int ThrusterCode; int AttitudeMode; - //Word 14-19 + //Word 14 std::string StationID; - //Targeting Parameters + double i; //Inclination of target orbit + //Word 15, Longitude of descending node of target orbit + double theta_N; + //Word 16, Eccentricity of target conic (negative of targeting parameters not input) + double e = -1.; + //Word 17, equals (-mu/a), where a is semi-major axis of target conic + double C3; + //Word 18, true anomaly of the descending node of the target conic + double alpha_D; + //Word 19, estimated true anomaly at cutoff + double f; //Word 20 (DT of burn for maneuver confirmation, negative if not input) double dt; //Word 29, configuration change indicator @@ -2470,7 +2490,9 @@ class RTCC { MATRIX3 REFSMMATCalc(REFSMMATOpt *opt); void EntryTargeting(EntryOpt *opt, EntryResults *res);//VECTOR3 &dV_LVLH, double &P30TIG, double &latitude, double &longitude, double &GET05G, double &RTGO, double &VIO, double &ReA, int &precision); void BlockDataProcessor(EarthEntryOpt *opt, EntryResults *res); - void TranslunarInjectionProcessor(EphemerisData sv, PLAWDTOutput WeightsTable); + //RTCC module name PMMEPP + void TranslunarInjectionProcessor(bool mpt, EphemerisData *sv = NULL, PLAWDTOutput *WeightsTable = NULL); + //RTCC module name PMMLCP void TranslunarMidcourseCorrectionProcessor(EphemerisData sv0, double CSMmass, double LMmass); int LunarDescentPlanningProcessor(EphemerisData sv, double W_LM); bool GeneralManeuverProcessor(GMPOpt *opt, VECTOR3 &dV_i, double &P30TIG); @@ -2794,6 +2816,7 @@ class RTCC { //Vector Panel Summary Control void BMSVPS(int queid, int PBIID); int BMSVPSVectorFetch(const std::string &vecid, EphemerisData &sv_out); + int GetStateVectorTableEntry(std::string VectorType, int mpt); // DIGITAL COMMAND SYSTEM (C) @@ -3770,12 +3793,22 @@ class RTCC { struct TLIPlanningTable { //INPUT - //4 = E-type mission ellipse + // + int mpt = 1; + //Vector type. CMC, LGC, AGS, IU, HSR, DC or ANC + std::string VectorType = "ANC"; + //2 = free-return, 3 = hybrid ellipse, 4 = E-type mission ellipse, 5 = non-free return int Mode = 4; - //TLI ignition for mode 4 + //TLI opportunity (1-2) + int Opportunity = 1; + //TLI ignition for mode 4, estimated TIG for mode 5 double GET_TLI = 0.0; //Apogee height for mode 4, nautical miles double h_ap = 5000.0; + //Launch window + bool IsPacficWindow = true; + //Available DV, for mode 3, feet per second + double dv_available = 5000.0; //CONSTANTS - THESE SHOULD BE SYSTEM PARAMETERS double DELTA = 0.0; @@ -3794,6 +3827,20 @@ class RTCC { double lng_ign = 0.0; } PZTPDDIS; + struct TLIPlanningMPTInterfaceDataTable + { + bool DataIndicator = false; //false = nothing here, true = good data + + double T_RP; + double i; + double theta_N; + double e; + double C3; + double alpha_D; + double f; + int Opportunity; //1-2 + } PZTPDXFR; + struct TLIPlanningOutputTable { int DataIndicator = 0; //0 = None, 1 = 7 parameters, 2 = 10 parameters diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp index 4a22799f35..55f6f92376 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.cpp @@ -677,6 +677,7 @@ ARCore::ARCore(VESSEL* v, AR_GCore* gcin) manpad_ullage_opt = true; ManPADMPT = 1; ManPADMPTManeuver = 1; + TLIPAD_StudyAid = false; mapupdate.LOSGET = 0.0; mapupdate.AOSGET = 0.0; @@ -3252,6 +3253,7 @@ int ARCore::subThread() opt.REFSMMAT= GC->rtcc->EZJGMTX1.data[0].REFSMMAT; opt.SeparationAttitude = lvdc->XLunarAttitude; opt.sv0 = GC->rtcc->StateVectorCalcEphem(GC->rtcc->pCSM); + opt.StudyAid = TLIPAD_StudyAid; GC->rtcc->TLI_PAD(opt, GC->tlipad); @@ -3515,16 +3517,23 @@ int ARCore::subThread() EphemerisData state; PLAWDTOutput WeightsTable; - if (iuvessel == NULL) + if (GC->MissionPlanningActive) { - Result = DONE; - break; + GC->rtcc->TranslunarInjectionProcessor(true); } + else + { + if (iuvessel == NULL) + { + Result = DONE; + break; + } - state = GC->rtcc->StateVectorCalcEphem(iuvessel); - WeightsTable = GC->rtcc->GetWeightsTable(iuvessel, true, false); + state = GC->rtcc->StateVectorCalcEphem(iuvessel); + WeightsTable = GC->rtcc->GetWeightsTable(iuvessel, true, false); - GC->rtcc->TranslunarInjectionProcessor(state, WeightsTable); + GC->rtcc->TranslunarInjectionProcessor(false, &state, &WeightsTable); + } Result = DONE; } diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.h b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.h index 9f8ae2a0f8..229866c3e0 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.h +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ARCore.h @@ -305,6 +305,7 @@ class ARCore { bool manpad_ullage_opt; //true = 4 jets, false = 2 jets int ManPADMPT; //1 = CSM, 3 = LEM int ManPADMPTManeuver; //1-15 + bool TLIPAD_StudyAid; //False = nominal TLI, true = study aid //MAP UPDATE PAGE AP10MAPUPDATE mapupdate; diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.cpp index c65ca3c1bf..b510d89afb 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.cpp @@ -1129,6 +1129,8 @@ void ApolloRTCCMFD::menuMidcourseTradeoffPage() void ApolloRTCCMFD::menuTLIPlanningPage() { + marker = 0; + markermax = 7; SelectPage(79); } @@ -6005,7 +6007,14 @@ void ApolloRTCCMFD::menuCycleTwoImpulseOption() void ApolloRTCCMFD::menuSwitchHeadsUp() { - G->HeadsUp = !G->HeadsUp; + if (G->manpadopt == 3) + { + G->TLIPAD_StudyAid = !G->TLIPAD_StudyAid; + } + else + { + G->HeadsUp = !G->HeadsUp; + } } void ApolloRTCCMFD::menuCalcManPAD() @@ -7209,19 +7218,53 @@ void ApolloRTCCMFD::menuTLIProcessorCalc() G->TLIProcessorCalc(); } -void ApolloRTCCMFD::menuTLIProcessorMode() -{ - -} - -void ApolloRTCCMFD::menuTLIProcessorGET() -{ - GenericGETInput(&GC->rtcc->PZTLIPLN.GET_TLI, "Input time of ignition or threshold time. Format HH:MM:SS:"); -} - -void ApolloRTCCMFD::menuTLIEllipseApogee() +void ApolloRTCCMFD::menuSetTLIProcessorInput() { - GenericDoubleInput(&GC->rtcc->PZTLIPLN.h_ap, "Input height of apogee (2700 to 7000 NM):"); + switch (marker) + { + case 0: + set_IUVessel(); + break; + case 1: + GC->rtcc->PZTLIPLN.mpt = 4 - GC->rtcc->PZTLIPLN.mpt; + break; + case 2: + GenericStringInput(&GC->rtcc->PZTLIPLN.VectorType, "Vector type (CMC, LGC, AGS, IU, HSR, DC, ANC):"); + break; + case 3: + GC->rtcc->PZTLIPLN.Opportunity = 3 - GC->rtcc->PZTLIPLN.Opportunity; + break; + case 4: + if (GC->rtcc->PZTLIPLN.Mode < 5) + { + GC->rtcc->PZTLIPLN.Mode++; + } + else + { + GC->rtcc->PZTLIPLN.Mode = 1; + } + break; + case 5: + GenericGETInput(&GC->rtcc->PZTLIPLN.GET_TLI, "Input time of ignition or threshold time. Format HH:MM:SS:"); + break; + case 6: + if (GC->rtcc->PZTLIPLN.Mode == 3) + { + GenericDoubleInput(&GC->rtcc->PZTLIPLN.dv_available, "Available Delta V for TLI (0 to 10000 ft/s):"); + } + else if (GC->rtcc->PZTLIPLN.Mode == 4) + { + GenericDoubleInput(&GC->rtcc->PZTLIPLN.h_ap, "Input height of apogee (2700 to 7000 NM):"); + } + else + { + GC->rtcc->PZTLIPLN.IsPacficWindow = !GC->rtcc->PZTLIPLN.IsPacficWindow; + } + break; + case 7: + menuCycleTLCCCSFPBlockNumber(); + break; + } } void ApolloRTCCMFD::menuLunarLiftoffCalc() @@ -9817,6 +9860,11 @@ void ApolloRTCCMFD::CycleEnableCalculation() EnableCalculation = !EnableCalculation; } +void ApolloRTCCMFD::SetMEDInputPageM75() +{ + SetMEDInputPage("M75"); +} + void ApolloRTCCMFD::SetMEDInputPageP13() { SetMEDInputPage("P13"); @@ -9915,6 +9963,13 @@ void ApolloRTCCMFD::SetMEDInputPage(std::string med) MEDInputData.display = 100; } + else if (med == "M75") + { + AddMEDInputTitle(MEDInputData, "M75", "Transfer of TLI maneuver from study aid"); + + AddMEDInput(MEDInputData.table, "VEH:", "Vehicle (CSM or LEM):", "CSM", ""); + AddMEDInput(MEDInputData.table, "REP:", "Replace code (1-15 or 0 if no replacement):", "0", ""); + } else if (med == "P13") { AddMEDInputTitle(MEDInputData, "P13", "Enter Vector in Spherical Coordinates"); diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.h b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.h index 39c6336faf..980e8e28c4 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.h +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD.h @@ -416,10 +416,8 @@ class ApolloRTCCMFD: public MFD2 { void menuTerrainModelCalc(); void set_TLand(double time); void menuTLCCCalc(); + void menuSetTLIProcessorInput(); void menuTLIProcessorCalc(); - void menuTLIProcessorMode(); - void menuTLIProcessorGET(); - void menuTLIEllipseApogee(); void menuNavCheckPADCalc(); void menuSetNavCheckGET(); void menuLAPCalc(); @@ -864,6 +862,7 @@ class ApolloRTCCMFD: public MFD2 { void CycleCSMOrLMSelection(); void CycleEnableCalculation(); + void SetMEDInputPageM75(); void SetMEDInputPageP13(); void SetMEDInputPageP14(); void SetMEDInputPage(std::string med); diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD_Display.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD_Display.cpp index 6cc1dc2c33..554985fa04 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD_Display.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApolloRTCCMFD_Display.cpp @@ -156,11 +156,7 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) skp->Text(6 * W / 8, 4 * H / 14, Buffer, strlen(Buffer)); } break; - } - - //Old - if (screen == 2) - { + case 2: skp->SetTextAlign(oapi::Sketchpad::CENTER); skp->Text(4 * W / 8, 2 * H / 32, "TWO IMPULSE MULTIPLE SOLUTION (MSK 0063)", 40); @@ -218,11 +214,11 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) skp->Text(49 * W / 64, 11 * H / 32, "YAW", 3); skp->Text(28 * W / 32, 11 * H / 32, "PITCH", 5); } - + skp->Text(60 * W / 64, 11 * H / 32, "L", 1); skp->Text(63 * W / 64, 11 * H / 32, "C", 1); - - for (int i = 0;i < GC->rtcc->TwoImpMultDispBuffer.Solutions;i++) + + for (int i = 0; i < GC->rtcc->TwoImpMultDispBuffer.Solutions; i++) { sprintf(Buffer, "%.2lf", GC->rtcc->TwoImpMultDispBuffer.data[i].DELV1); skp->Text(5 * W / 32, (12 + i) * H / 32, Buffer, strlen(Buffer)); @@ -246,15 +242,14 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) sprintf(Buffer, "%.2lf", GC->rtcc->TwoImpMultDispBuffer.data[i].PITCH2); skp->Text(57 * W / 64, (12 + i) * H / 32, Buffer, strlen(Buffer)); } - + sprintf(Buffer, "%c", GC->rtcc->TwoImpMultDispBuffer.data[i].L); skp->Text(60 * W / 64, (12 + i) * H / 32, Buffer, strlen(Buffer)); sprintf(Buffer, "%d", GC->rtcc->TwoImpMultDispBuffer.data[i].C); skp->Text(63 * W / 64, (12 + i) * H / 32, Buffer, strlen(Buffer)); } - } - else if (screen == 3) - { + break; + case 3: skp->Text(6 * W / 8, (int)(0.5 * H / 14), "Coelliptic", 10); skp->Text(1 * W / 16, 2 * H / 14, "SPQ Initialization", 18); @@ -371,8 +366,11 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) skp->Text(6 * W / 8, 17 * H / 21, Buffer, strlen(Buffer)); AGC_Display(Buffer, G->SPQDeltaV.z / 0.3048); skp->Text(6 * W / 8, 18 * H / 21, Buffer, strlen(Buffer)); + break; } - else if (screen == 4) + + //Old + if (screen == 4) { skp->Text(4 * W / 8, (int)(0.5 * H / 14), "General Purpose Maneuver", 24); @@ -1255,6 +1253,15 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) { skp->Text(4 * W / 8, (int)(0.5 * H / 14), "TLI PAD", 7); + if (G->TLIPAD_StudyAid) + { + skp->Text(1 * W / 16, 6 * H / 14, "TLI Processor", 13); + } + else + { + skp->Text(1 * W / 16, 6 * H / 14, "Nominal", 7); + } + GET_Display(Buffer, GC->tlipad.TB6P); sprintf(Buffer, "%s TB6p", Buffer); skp->Text(3 * W / 8, 3 * H / 20, Buffer, strlen(Buffer)); @@ -7094,6 +7101,9 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) { skp->Text(2 * W / 8, 1 * H / 14, "TLI PLANNING DISPLAY (MSK 0080)", 31); + skp->Text(1 * W / 44, (marker + 3) * H / 22, "*", 1); + + skp->Text(1 * W / 22, 3 * H / 22, "IU:", 3); if (G->iuvessel == NULL) { sprintf_s(Buffer, 127, "No IU!"); @@ -7102,22 +7112,120 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) { sprintf_s(Buffer, 127, G->iuvessel->GetName()); } - skp->Text(1 * W / 16, 2 * H / 14, Buffer, strlen(Buffer)); + skp->Text(5 * W / 22, 3 * H / 22, Buffer, strlen(Buffer)); - if (GC->rtcc->PZTLIPLN.Mode == 4) + if (GC->MissionPlanningActive) { - skp->Text(1 * W / 16, 4 * H / 14, "Ellipse", 7); + skp->Text(1 * W / 22, 4 * H / 22, "MPT:", 4); + if (GC->rtcc->PZTLIPLN.mpt == RTCC_MPT_CSM) + { + sprintf(Buffer, "CSM"); + } + else + { + sprintf(Buffer, "LEM"); + } + skp->Text(5 * W / 22, 4 * H / 22, Buffer, strlen(Buffer)); + + skp->Text(1 * W / 22, 5 * H / 22, "Vec:", 4); + skp->Text(5 * W / 22, 5 * H / 22, GC->rtcc->PZTLIPLN.VectorType.c_str(), GC->rtcc->PZTLIPLN.VectorType.size()); } - else + + skp->Text(1 * W / 22, 6 * H / 22, "Opp:", 4); + sprintf(Buffer, "%d", GC->rtcc->PZTLIPLN.Opportunity); + skp->Text(5 * W / 22, 6 * H / 22, Buffer, strlen(Buffer)); + + skp->Text(1 * W / 22, 7 * H / 22, "Mode:", 5); + switch (GC->rtcc->PZTLIPLN.Mode) + { + case 2: + skp->Text(5 * W / 22, 7 * H / 22, "Free Return", 11); + break; + case 3: + skp->Text(5 * W / 22, 7 * H / 22, "Hybrid Ellipse", 14); + break; + case 4: + skp->Text(5 * W / 22, 7 * H / 22, "Specified Apogee", 16); + break; + case 5: + skp->Text(5 * W / 22, 7 * H / 22, "Non-Free Return", 15); + break; + default: + skp->Text(5 * W / 22, 7 * H / 22, "Hypersurface", 12); + break; + } + + if (GC->rtcc->PZTLIPLN.Mode != 1) { - skp->Text(1 * W / 16, 4 * H / 14, "TBD", 3); + skp->Text(1 * W / 22, 8 * H / 22, "TIG:", 4); + GET_Display(Buffer, GC->rtcc->PZTLIPLN.GET_TLI, false); + skp->Text(5 * W / 22, 8 * H / 22, Buffer, strlen(Buffer)); } - - GET_Display(Buffer, GC->rtcc->PZTLIPLN.GET_TLI, false); - skp->Text(1 * W / 16, 6 * H / 14, Buffer, strlen(Buffer)); - sprintf_s(Buffer, "%.0lf NM", GC->rtcc->PZTLIPLN.h_ap); - skp->Text(1 * W / 16, 8 * H / 14, Buffer, strlen(Buffer)); + if (GC->rtcc->PZTLIPLN.Mode == 3) + { + skp->Text(1 * W / 22, 9 * H / 22, "DV:", 3); + sprintf_s(Buffer, "%.0lf ft/s", GC->rtcc->PZTLIPLN.dv_available); + skp->Text(5 * W / 22, 9 * H / 22, Buffer, strlen(Buffer)); + } + else if (GC->rtcc->PZTLIPLN.Mode == 4) + { + skp->Text(1 * W / 22, 9 * H / 22, "APO:", 4); + + sprintf_s(Buffer, "%.0lf NM", GC->rtcc->PZTLIPLN.h_ap); + skp->Text(5 * W / 22, 9 * H / 22, Buffer, strlen(Buffer)); + } + else if (GC->rtcc->PZTLIPLN.Mode == 2 || GC->rtcc->PZTLIPLN.Mode == 5) + { + skp->Text(1 * W / 22, 9 * H / 22, "Window:", 7); + + if (GC->rtcc->PZTLIPLN.IsPacficWindow) + { + skp->Text(5 * W / 22, 9 * H / 22, "Pacific", 14); + } + else + { + skp->Text(5 * W / 22, 9 * H / 22, "Atlantic", 15); + } + + skp->Text(1 * W / 22, 10 * H / 22, "SFP:", 4); + if (GC->rtcc->PZMCCPLN.SFPBlockNum == 1) + { + sprintf(Buffer, "1 (Preflight)"); + } + else + { + sprintf(Buffer, "2 (Nominal)"); + } + skp->Text(5 * W / 22, 10 * H / 22, Buffer, strlen(Buffer)); + + if (GC->rtcc->PZTLIPLN.Mode == 2) + { + skp->Text(5 * W / 22, 12 * H / 22, "Pericynthion:", 13); + + sprintf_s(Buffer, "Lat %.3lf", GC->rtcc->PZSFPTAB.blocks[GC->rtcc->PZMCCPLN.SFPBlockNum - 1].lat_pc1 * DEG); + skp->Text(5 * W / 22, 13 * H / 22, Buffer, strlen(Buffer)); + + sprintf_s(Buffer, "Height %.3lf", GC->rtcc->PZSFPTAB.blocks[GC->rtcc->PZMCCPLN.SFPBlockNum - 1].h_pc1 / 1852.0); + skp->Text(5 * W / 22, 14 * H / 22, Buffer, strlen(Buffer)); + } + else + { + skp->Text(5 * W / 22, 12 * H / 22, "Node:", 5); + + GET_Display2(Buffer, GC->rtcc->PZSFPTAB.blocks[GC->rtcc->PZMCCPLN.SFPBlockNum - 1].GMT_nd); + skp->Text(5 * W / 22, 13 * H / 22, Buffer, strlen(Buffer)); + + sprintf_s(Buffer, "Lat %.3lf", GC->rtcc->PZSFPTAB.blocks[GC->rtcc->PZMCCPLN.SFPBlockNum - 1].lat_nd * DEG); + skp->Text(5 * W / 22, 14 * H / 22, Buffer, strlen(Buffer)); + + sprintf_s(Buffer, "Lng %.3lf", GC->rtcc->PZSFPTAB.blocks[GC->rtcc->PZMCCPLN.SFPBlockNum - 1].lng_nd * DEG); + skp->Text(5 * W / 22, 15 * H / 22, Buffer, strlen(Buffer)); + + sprintf_s(Buffer, "Height %.3lf", GC->rtcc->PZSFPTAB.blocks[GC->rtcc->PZMCCPLN.SFPBlockNum - 1].h_nd / 1852.0); + skp->Text(5 * W / 22, 16 * H / 22, Buffer, strlen(Buffer)); + } + } skp->Text(9 * W / 16, 4 * H / 14, "GET RP", 6); skp->Text(9 * W / 16, 5 * H / 14, "GET TIG", 7); @@ -7128,6 +7236,7 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) skp->Text(9 * W / 16, 10 * H / 14, "ALPHA", 5); skp->Text(9 * W / 16, 11 * H / 14, "TA", 2); skp->Text(9 * W / 16, 12 * H / 14, "DV", 2); + skp->Text(9 * W / 16, 13 * H / 14, "BT", 2); if (GC->rtcc->PZTTLIPL.DataIndicator == 1) { @@ -7151,8 +7260,16 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) skp->Text(15 * W / 16, 11 * H / 14, Buffer, strlen(Buffer)); sprintf(Buffer, "%.1lf", GC->rtcc->PZTPDDIS.dv_TLI); skp->Text(15 * W / 16, 12 * H / 14, Buffer, strlen(Buffer)); + + double secs; + int hh, mm; + OrbMech::SStoHHMMSS(GC->rtcc->PZTPDDIS.T_b, hh, mm, secs); + sprintf(Buffer, "%d:%02.0f", mm, secs); + skp->Text(15 * W / 16, 13 * H / 14, Buffer, strlen(Buffer)); } + skp->SetTextAlign(oapi::Sketchpad::LEFT); + switch (G->iuUplinkResult) { case 1: @@ -7171,7 +7288,7 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) sprintf(Buffer, "No Uplink"); break; } - skp->Text(13 * W / 32, 30 * H / 32, Buffer, strlen(Buffer)); + skp->Text(5 * W / 32, 30 * H / 32, Buffer, strlen(Buffer)); } else if (screen == 80) { @@ -10635,6 +10752,7 @@ bool ApolloRTCCMFD::Update(oapi::Sketchpad *skp) sprintf(Buffer, "MGA: %+.2lf°", G->IMUParkingAngles.z * DEG); skp->Text(10 * W / 16, 8 * H / 14, Buffer, strlen(Buffer)); } + return true; } diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApollomfdButtons.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApollomfdButtons.cpp index b2c676ffec..6ba95de605 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApollomfdButtons.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/ApollomfdButtons.cpp @@ -2692,34 +2692,34 @@ ApolloRTCCMFDButtons::ApolloRTCCMFDButtons() static const MFDBUTTONMENU mnu79[] = { - { "IU vessel", 0, 'L' }, - { "Mode", 0, 'G' }, - { "Time of ignition", 0, 'Q' }, - { "Apogee height", 0, 'V' }, + { "Set input", 0, 'S' }, + { "Previous Item", 0, 'P' }, + { "Next Item", 0, 'N' }, + { "", 0, ' ' }, { "", 0, ' ' }, { "", 0, ' ' }, { "Calculate solution", 0, 'C' }, { "", 0, ' ' }, { "", 0, ' ' }, - { "", 0, ' ' }, - { "", 0, ' ' }, + { "Transfer to MPT", 0, 'S' }, + { "Uplink to LVDC", 0, 'E' }, { "Back to menu", 0, 'B' }, }; RegisterPage(mnu79, sizeof(mnu79) / sizeof(MFDBUTTONMENU)); - RegisterFunction("IU", OAPI_KEY_L, &ApolloRTCCMFD::set_IUVessel); - RegisterFunction("MOD", OAPI_KEY_G, &ApolloRTCCMFD::menuTLIProcessorMode); - RegisterFunction("TIG", OAPI_KEY_Q, &ApolloRTCCMFD::menuTLIProcessorGET); - RegisterFunction("APO", OAPI_KEY_V, &ApolloRTCCMFD::menuTLIEllipseApogee); - RegisterFunction("", OAPI_KEY_H, &ApolloRTCCMFD::menuVoid); - RegisterFunction("", OAPI_KEY_Q, &ApolloRTCCMFD::menuVoid); + RegisterFunction("SET", OAPI_KEY_S, &ApolloRTCCMFD::menuSetTLIProcessorInput); + RegisterFunction("<<", OAPI_KEY_P, &ApolloRTCCMFD::menuCycleMarkerDown); + RegisterFunction(">>", OAPI_KEY_N, &ApolloRTCCMFD::menuCycleMarkerUp); + RegisterFunction("", OAPI_KEY_R, &ApolloRTCCMFD::menuVoid); + RegisterFunction("", OAPI_KEY_D, &ApolloRTCCMFD::menuVoid); + RegisterFunction("", OAPI_KEY_A, &ApolloRTCCMFD::menuVoid); RegisterFunction("CLC", OAPI_KEY_C, &ApolloRTCCMFD::menuTLIProcessorCalc); RegisterFunction("", OAPI_KEY_A, &ApolloRTCCMFD::menuVoid); RegisterFunction("", OAPI_KEY_P, &ApolloRTCCMFD::menuVoid); - RegisterFunction("", OAPI_KEY_S, &ApolloRTCCMFD::menuVoid); + RegisterFunction("MPT", OAPI_KEY_S, &ApolloRTCCMFD::SetMEDInputPageM75); RegisterFunction("UPL", OAPI_KEY_E, &ApolloRTCCMFD::menuSLVTLITargetingUplink); RegisterFunction("BCK", OAPI_KEY_B, &ApolloRTCCMFD::menuTranslunarPage); diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.cpp index 15bce39614..3ce5743982 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.cpp @@ -5408,6 +5408,32 @@ double EMXINGElevSlope(VECTOR3 R, VECTOR3 V, VECTOR3 R_S, int body) return (dotp(rho_dot, N) + dotp(rho_apo, N_dot))*length(rho); } +double LongitudeConversion(double lng, double T, double w_E, double lng_0, bool inertial_to_geographic) +{ + //For ECI to ECEF conversion + + //lng: inertial or ECEF longitude + //T: Current time + //w_E: Body rotation rate + //lng_0: hour angle at T = 0 + //inertial_to_geographic: conversion direction + + double K; + if (inertial_to_geographic) + { + K = -1.0; + } + else + { + K = 1.0; + } + + lng = lng + K*(lng_0 + w_E * T); + + normalizeAngle(lng, false); + return lng; +} + CELEMENTS KeplerToEquinoctial(CELEMENTS kep) { CELEMENTS aeq; diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.h b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.h index 44e446421b..38657d4e58 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.h +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/OrbMech.h @@ -395,6 +395,7 @@ namespace OrbMech { void EMXINGElev(VECTOR3 R, VECTOR3 R_S, VECTOR3 &N, VECTOR3 &rho, double &sinang); //RTCC EMXING support routine, calculates elevation slope function double EMXINGElevSlope(VECTOR3 R, VECTOR3 V, VECTOR3 R_S, int body); + double LongitudeConversion(double lng, double T, double w_E, double lng_0, bool inertial_to_geographic); //AEG CELEMENTS LyddaneMeanToOsculating(CELEMENTS arr, int body); diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIGuidanceSim.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIGuidanceSim.cpp index c0e9757a77..c94a5fd08a 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIGuidanceSim.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIGuidanceSim.cpp @@ -675,6 +675,7 @@ void TLIGuidanceSim::PCMGN() { goto PMMSIU_PCMGN_20; } + //Target update calculations RN = P; E = RMAG * (E - 1.0) / RN + 1.0; TEMP1 = E * E; @@ -701,6 +702,7 @@ void TLIGuidanceSim::PCMGN() DDLT = T - TLAST; goto PMMSIU_PCMGN_2A; PMMSIU_PCMGN_1B: + //Target update calculations ALPHD = ALPHD - acos(TEMP4); TEMP2 = E * cos(F); RT = P / (1.0 + TEMP2); diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.cpp index a271cc93e2..2b232e09fa 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.cpp @@ -25,6 +25,653 @@ See http://nassp.sourceforge.net/license/ for more details. #include "rtcc.h" #include "TLIProcessor.h" +TLIFirstGuess::TLIFirstGuess(RTCC *r) : RTCCModule(r), +WE(0.262517142) +{ + +} + +void TLIFirstGuess::Calculate(const TLIFirstGuessInputs& in) +{ + inp = in; + + CIST(in.BHA + inp.Hour * WE); + if (CO1) return; + + double TOIDIS = -outp.T1; + UPDATE(TOIDIS); + outp.DEL = DEL; +} + +void TLIFirstGuess::CIST(double RAGBT) +{ + double ORBPER, B1, RNVBT, TOPCY, C4, W, RQDFT, A2M, PT1, CTIO, T1; + double AZ2M, B2M, C2, RA2, A2V, A3V, AZ2V, BETA, AFNTMH, CL, Z, ALFA, A4, AZ4, B4, RA4, CARC, WTT, WTTER; + double DUMCT, CTTPG, RTOPCY, DTOPCY, T4, T7, T8; + int NUMIT, IEX, IAI0, IOS, IOQ; + double BADA2M, DUM1, DUM2, PCARC, CCARC, TOLCOR; + + DUM1 = 0.0; + + WV = sqrt(19.9094165 / pow(inp.R1 / 3443.93359, 3)); + ORBPER = PI2 / WV; + //Angle from ascending node to longitude of launch along on the earth's equator, in radians + B1 = atan2(sin(inp.C1) * sin(inp.AZ1), cos(inp.AZ1)); + //Argument of the input parking orbit state vector past its ascending node on the earth's equator, in radians + A1 = atan2(sin(inp.C1), cos(inp.C1) * cos(inp.AZ1)); + //Calculate inclination of the earth parking orbit to the earth's equator, in radians + FIV = abs(atan(sin(inp.C1) / (cos(inp.C1) * sin(B1)))); + //Ascending node of the VOP for insertion at base time + RNVBT = RAGBT + inp.FLO1 - B1; + RNVBT = HELP(RNVBT); + + if (inp.debug) + { + + } + + TOPCY = ORBPER * (inp.ORBNUM - 1.0) + 40.0; + CO1 = 0; + PERCYN(TOPCY); + if (CO1) return; + + FIVTL = 0; + C4 = 0; + R4 = inp.R1 + 12; + W = ENERGY(inp.IPERT, C4); + RQDFT = FLYTYM(inp.IPERT); + + A2M = A6 + 1.33 / DEG; + A2M = HELP(A2M); + PT1 = 0; + NUMIT = 0; + IEX = 0; + IAI0 = 1; + IOS = 1; + + do + { + NUMIT = NUMIT + 1; + GEOARG(FIM, A2M, RNM, AZ2M, B2M, C2, RA2); + + if (abs(C2) >= FIV) + { + //Inaccessible node logic + if (IEX == 0) + { + IEX = 1; + } + C2 = FIV * OrbMech::sign(C2); + BADA2M = A2M; + IOQ = 1; + //fprintf("INACCESSIBLE NODE ON MOP CALLED FOR.\n"); + if (IEX == 3) + { + //fprintf("NECESSARY NODE ON MOP IS INACCESSIBLE. NO CONVERGENCE.\n"); + return; + } + else if (IEX == 1) + { + if (C2 < 0) + { + IOQ = 2; + } + + IEX = 2; + //fprintf("NODE PLACED AT BEGINNING OF INACESSIBLE REGION ON MOP.\n"); + } + else + { + if (C2 > 0) + { + IOQ = 2; + } + IEX = 3; + //fprintf("NODE PLACED AT END OF INACESSIBLE REGION ON MOP.\n"); + } + + GEOLAT(FIM, C2, DUM1, IOQ, A2M, B2M, AZ2M, DUM2); + GEOARG(FIM, A2M, RNM, AZ2M, B2M, DUM1, RA2); + double TCOR = A2M - BADA2M; + TCOR = HELP(TCOR); + TOPCY = TOPCY + TCOR / WM; + PERCYN(TOPCY); + FIVTL = AZ2M - PI05; + RQDFT = FLYTYM(inp.IPERT); + W = ENERGY(inp.IPERT, C2); + } + + GEOLAT(FIV, C2, RA2, inp.IPOA, A2V, A3V, AZ2V, RNV); + FIVTL = AZ2M - AZ2V; + + SUBB(inp.IPERT, BETA, AFNTMH); + CL = SUBCL(inp.IPERT); + TLIMP(W); + + Z = atan(sin(BETA) * cos(FIVTL) / cos(BETA)); + ALFA = pow(cos(BETA) / (cos(Z) * cos(CL)), 2) - 1.0; + if (ALFA < 0) + { + ALFA = 0; + } + ALFA = atan(sqrt(ALFA)) + Z; + A4 = A2V + ALFA; + A4 = HELP(A4); + GEOARG(FIV, A4, RNV, AZ4, B4, C4, RA4); + + CARC = A4 - A1 - APS; + CARC = HELP(CARC); + if (CARC < 0 && inp.ORBNUM != 0.0) + { + CARC = CARC + PI2; + } + if (NUMIT == 1) + { + PCARC = CARC; + } + + if (abs(CARC - PCARC) >= PI) + { + //Orbit coast time excursion logic + if (IAI0 == 1) + { + IAI0 = 2; + CCARC = CARC; + //fprintf("ORBIT COAST ARC EXCURSION. ARC WILL BE CONSTRAINED TO WITHIN PI OF NEXT VALUE.\n"); + } + else + { + double SDAIO = 1.0; + if (CARC > CCARC) + { + SDAIO = -1.0; + } + CARC = CARC + SDAIO * PI2; + } + } + PCARC = CARC; + if (inp.ORBNUM != 0.0) + { + CTIO = ORBPER * (CARC / (PI2)+inp.ORBNUM - 1.0); + } + else + { + CTIO = ORBPER * (CARC / (PI2)); + } + T1 = RNV - RNVBT; + T1 = HELP(T1); + T1 = T1 / WE; + if (NUMIT == 1) + { + PT1 = T1; + } + + if (abs(T1 - PT1) >= 12.0) + { + //Time of orbit state vector excursion logic + if (IOS == 1) + { + IOS = 2; + TOLCOR = PI2 / WE * OrbMech::sign(T1); + //fprintf("CHANGE IN INSERTION TIME EXCEEDS 12 HOURS. SIGN WILL BE CONSTRAINED TO THAT OF NEXT VALUE.\n"); + } + else + { + T1 = T1 + TOLCOR; + } + } + + PT1 = T1; + DUMCT = APS / WV; + CTTPG = CTIO + DUMCT; + RTOPCY = T1 + CTIO + DUMCT + RQDFT; + DTOPCY = RTOPCY - TOPCY; + + T8 = T1 + CTIO; + T4 = T8 + DUMCT; + T7 = T4 + FTOCO; + + if (abs(DTOPCY) >= inp.TOL && NUMIT < inp.MAXIT) + { + TOPCY = RTOPCY; + PERCYN(TOPCY); + if (CO1) return; + W = ENERGY(inp.IPERT, C4); + RQDFT = FLYTYM(inp.IPERT); + A2M = A6 - BETA; + A2M = HELP(A2M); + } + + } while (abs(DTOPCY) >= inp.TOL && NUMIT < inp.MAXIT); + + double A3, AZ3, B3, C3, RA3, S; + + A3 = A2V + AFNTMH; + A3 = HELP(A3); + GEOARG(FIV, A3, RNV, AZ3, B3, C3, RA3); + S = A4 - A3; + S = HELP(S); + MH.x = cos(C3) * cos(RA3); + MH.y = cos(C3) * sin(RA3); + MH.z = sin(C3); + + WTT = W * 2.0; + WTTER = WTT * pow(3600.0 / (6076.11549 * 3443.93359), 2); + + outp.T1 = T1; + outp.CTIO = CTIO; + outp.C3 = WTTER; + outp.S = S; + outp.TOPCY = TOPCY; +} + +void TLIFirstGuess::UPDATE(double TOIDIS) +{ + VECTOR3 VEC1, UMH, HV, POV, DUM; + double PSI, DOT, HOMSQ, HOM, DISRNV, AZ1, B1, C1, RA1, ARC; + + PSI = WM * TOIDIS * ((WV - WE * cos(FIV)) / (WV - WM * cos(FIVTL))); + DOT = dotp(MH, HM); + HOMSQ = dotp(HM, HM); + HOM = sqrt(HOMSQ); + VEC1 = crossp(HM, MH); + + UMH = HM * DOT / HOMSQ + VEC1 * sin(PSI) / HOM + crossp(VEC1, HM) * cos(PSI) / HOMSQ; + + DISRNV = RNV + WE * TOIDIS; + HV = _V(sin(FIV) * sin(DISRNV), -sin(FIV) * cos(DISRNV), cos(FIV)); + + DUM = crossp(HV, UMH); + DEL = ANGLE(HV, UMH, DUM); + DEL = PI05 - DEL; + + GEOARG(FIV, A1, DISRNV, AZ1, B1, C1, RA1); + POV = _V(cos(C1) * cos(RA1), cos(C1) * sin(RA1), sin(C1)); + ARC = ANGLE(POV, DUM, HV); + ARC = ARC - PI05; + ARC = HELP(ARC); +} + +void TLIFirstGuess::GetOutput(TLIFirstGuessOutputs& out) +{ + out = outp; +} + +void TLIFirstGuess::TLIMP(double W) +{ + double U, UE, FKMPNM, FPKM, RORB, C3, CDIF, ETA, DV; + double VPGSQ, VPG, HSQ, H, P, A, E, B, R7, G7D, COEF; + + U = 0.23167004e13; + //Gravitational constant of the earth, in km^3/sec^2 + UE = 398603.2; + //Conversion factor km/NM + FKMPNM = 1.852; + //Conversion factor, ft/km + FPKM = 3280.8399; + //Radius of circular earth parking orbit, in kilometers + RORB = inp.R1 * FKMPNM; + //Trajectory energy, in (km/sec)^2 + C3 = 2.0 * W / (FPKM * FPKM); + //Difference between the energies of the circular parking orbit and the trajectory, in (km/sec)^2 + CDIF = UE / RORB + C3; + //Empirical equations + //True anomaly of TLI cutoff, in radians + ETA = atan((2.1397405 - RORB / 5750.0) * (1.0143460 / inp.TTW - 0.02) / (260.73592 / CDIF + 1.6338479)); + //Angle from beginning of coplanar TLI to perigee, in radians (equals alpha plus sigma) + APS = atan((2.4185082 - RORB / 4620.0) * (1.0365969 / inp.TTW - 0.051020408) / (254.80898 / CDIF + 2.1846192)); + //Perigee radius + R4 = inp.R1 + (-0.15664127 * pow(CDIF, 3) + 34.56894 * pow(CDIF, 2) - 253.71417 * CDIF) / (RORB * pow(inp.TTW, 2) * FKMPNM); + //Characteristic velocity of coplanar TLI maneuver, in ft/sec + DV = (sqrt(CDIF + UE / RORB) - sqrt(UE / RORB) + (pow(CDIF - 11.61, 2) * (2.7022098 - RORB / 3850.0) * (2.0264543e-6 / inp.TTW + 3.632748e-8)) / inp.TTW) * FPKM; + + VPGSQ = 2.0 * (W + U / R4); + VPG = sqrt(VPGSQ); + HSQ = pow(R4, 2) * VPGSQ; + H = R4 * VPG; + P = HSQ / U; + A = -U / (2.0 * W); + E = 1.0 - R4 / A; + B = sqrt(abs(A * P)); + R7 = P / (1.0 + E * cos(ETA)); + G7D = atan(E * R7 / P * sin(ETA)) * DEG; + COEF = 6076.11549 / 3600.0 * A / H; + //Flight time on trajectory of TLI cutoff past perigee, in hours + FTOCO = COEF * (2.0 * B * atan(R4 / B * tan(ETA / 2.0)) - E * R7 * sin(ETA)); +} + +double TLIFirstGuess::SUBCL(int IPERT) const +{ + double SECL, OBCL, CL; + + if (IPERT <= 1) + { + SECL = 0; + } + else + { + SECL = (0.11 - 8e-7 * EMR) * sin(2.0 * SMOPL + 0.611) - 0.01; + } + if (IPERT == 0 || IPERT == 2) + { + OBCL = 0; + } + else + { + OBCL = 1.6457056e-2 * sin(2.0 * AM + 0.166); + } + CL = 6.3814106 - (1.1030239e-5) * EMR + (5.3567533e-3) * EMRDOT + 6950.0 / (inp.RPC + 652.0) + 0.0114 * abs(inp.YPC) + 0.01892 * inp.YPC * FIVTL - cos(FIVTL) * (0.055 * inp.XPC - 1.35) - + pow(0.2457 - 4.5e-7 * EMR, 2) * (888.88889 + 1.0 / (5.0625 * inp.XPC / (9.828e5 - 1.8 * EMR) - 0.001125)) + 0.055 * inp.XPC + SECL + OBCL; + CL = CL / DEG; + return CL; +} + +void TLIFirstGuess::SUBB(int IPERT, double& BETA, double& AFNTMH) const +{ + double XC, YC, PLUS, YT, S, Z, COTH, SITH; + + XC = (1.93 - 0.037 * inp.XPC) / DEG; + YC = (-OrbMech::sign(FIVTL) * (0.6 - 0.02 * inp.XPC) - 0.035 * inp.YPC) / DEG; + if (IPERT == 0) + { + PLUS = 0; + } + else if (IPERT == 1) + { + PLUS = 0.0317 * cos(AM - 18.0 / DEG); + } + else + { + PLUS = (0.0285 + 0.0115 * cos(3.85 * DS)) * cos(AM - (10.0 + 5.0 * cos(3.85 * DS)) / DEG); + } + YT = (PLUS - inp.YPC * (0.015165 - 0.000201 * inp.XPC)) / DEG; + if (abs(YT) < abs(FIVTL)) + { + S = (-sin(YT - YC) - cos(FIVTL) * sin(YC)) / (sin(FIVTL) * cos(YC)); + Z = -OrbMech::sign(S) * atan(1 / sqrt(1 / (S * S) - 1)); + } + else + { + YT = OrbMech::sign(YT) * abs(FIVTL); + S = -1.0; + if (FIVTL * YT < 0) + { + S = 1.0; + } + Z = -OrbMech::sign(S) * 90.0 / DEG; + //fprintf("WARNING, TANGENCY SUEFACE PROBLEM. YT REDEFINED AS SIGN(ABS(FIVTL),YT)\n"); + } + BETA = Z - XC; + COTH = sin(FIVTL) * cos(Z); + SITH = sqrt(1.0 - COTH * COTH); + AFNTMH = Z / cos(FIVTL) + (YC - YT) / (SITH / COTH); +} + +void TLIFirstGuess::GEOLAT(double FI, double C, double RA, int I, double& A, double& B, double& AZ, double& RN) const +{ + if (C == FI) + { + A = PI05; + B = PI05; + AZ = PI05; + } + else if (C == -FI) + { + A = -PI05; + B = -PI05; + AZ = PI05; + } + else if (C == 0.0) + { + if (I == 1) + { + A = 0; + B = 0; + AZ = PI05 - FI; + } + else + { + A = PI; + B = PI; + AZ = PI05 + FI; + } + } + else + { + double CSCA = sin(FI) / sin(C); + A = atan(1 / sqrt(CSCA * CSCA - 1.0)); + if (I == 2) + { + A = PI - A; + } + if (C < 0) + { + A = -A; + } + B = atan2(sin(A) * cos(FI), cos(A)); + AZ = atan2(cos(FI), sin(FI) * cos(A)); + } + + RN = RA - B; + RN = HELP(RN); +} + +void TLIFirstGuess::GEOARG(double FI, double A, double RN, double &AZ, double &B, double &C, double &RA) const +{ + if (FI == 0.0) + { + AZ = PI05; + B = A; + C = 0; + } + else if (FI == PI) + { + AZ = -PI05; + B = -A; + C = 0; + } + else if (FI == PI05) + { + if (abs(A) < PI05) + { + AZ = 0; + B = 0; + C = A; + } + else if (A >= PI05) + { + AZ = PI; + B = PI; + C = PI - A; + } + else + { + AZ = PI; + B = PI; + C = -A - PI; + } + } + else if (A == PI05) + { + AZ = PI05; + B = PI05; + C = FI; + } + else if (A == -PI05) + { + AZ = PI05; + B = -PI05; + C = -FI; + } + else if (A == 0.0) + { + AZ = PI05 - FI; + B = 0; + C = 0; + } + else if (A == PI) + { + AZ = PI05 + FI; + B = PI; + C = 0; + } + else + { + AZ = atan2(cos(FI), sin(FI) * cos(A)); + B = atan2(sin(A) * cos(FI), cos(A)); + C = atan(sin(FI) * sin(B) / cos(FI)); + } + RA = RN + B; + RA = HELP(RA); +} + +double TLIFirstGuess::ENERGY(int IPERT, double C4) const +{ + double SEW, EOBW, PHI, FI, XGN, YGN, COA, A, COB, B, F, W; + + //SEW = Effect of solar gravitation + if (IPERT <= 1) + { + SEW = 0; + } + else + { + SEW = 3.9e4 * sin(2.0 * SMOPL - 1.657) - 1.3e4; + } + + //EOBW = Effect of Earth oblateness + if (IPERT == 0 || IPERT == 2) + { + EOBW = 0; + } + else + { + EOBW = (5.97 - 2.67 * cos(C4)) * cos(2.55 * C4) * 1e5; + } + PHI = (30.0 + inp.XPC * 2.0 / 3.0) / DEG; + PHI = sin(PHI) / cos(PHI); + FI = 1.0 / (EMR - 29000.0); + XGN = -68.0 + EMR / 10000.0 + 0.625 * inp.XPC; + YGN = -sin(FIVTL) * (9.6 - 0.16 * (XGN + 48.0)) / DEG; + XGN = XGN / DEG; + COA = cos(XGN) * cos(YGN); + A = atan(sqrt(1 / (COA * COA) - 1.0)); + COB = sin(YGN) * sin(inp.YPC / DEG) + cos(YGN) * cos(inp.YPC / DEG) * cos(inp.XPC / DEG - XGN); + B = atan(sqrt(1 / (COB * COB) - 1)); + F = (B - A) * DEG; + W = -2714728.4 - 1.4002127e12 * FI + EMRDOT * (3070.6244 + FI * 2.1470226e6 + EMRDOT * 1.1495569) + 750.0 * R4 + (1.0 - cos(FIVTL)) * (2.2 * EMR - 6.8e4) + + F * (-75150.0 - 0.05 * EMR - 8.75 * EMRDOT) - (4050.0 + 364500.0 / (F - 90.0)) * (2.25 * EMRDOT - 0.01 * EMR + 7070.0) + + (8.6016e6) * (PHI*PHI) * (1.0 / ((inp.RPC - 1015.0) / (2217.025 * PHI) + 1.0) - 1.0) + SEW + EOBW; + return W; +} + +double TLIFirstGuess::FLYTYM(int IPERT) const +{ + double SGFT, XGN, YGN, COA, A, COB, B, F, RQDFT; + + if (IPERT <= 1) + { + SGFT = 0; + } + else + { + SGFT = 2e-7 * EMR + cos(0.1745 - 2.0 * SMOPL) * (EMR * 9.6e-7 - EMRDOT * 7.5e-5 - 0.01554) - 0.028; + } + XGN = -68.0 + EMR / 10000.0 + 0.625 * inp.XPC; + YGN = -sin(FIVTL) * (9.6 - 0.16 * (XGN + 48)) / DEG; + XGN = XGN / DEG; + COA = cos(XGN) * cos(YGN); + A = atan(sqrt(1 / (COA * COA) - 1)); + COB = sin(YGN) * sin(inp.YPC / DEG) + cos(YGN) * cos(inp.YPC / DEG) * cos(inp.XPC / DEG - XGN); + B = atan(sqrt(1 / (COB * COB) - 1)); + F = (B - A) * DEG; + RQDFT = (-23.480035 + EMR * 4.455251e-4 - EMRDOT * (EMR * 1.6723713e-7 - 1.5007662e-2) + (cos(FIVTL) - 1.0) * (EMR * 2.4e-5 - 3.96) - + pow(0.012 - 3.3e-6 * EMR + 5e-4 * EMRDOT, 2) * (425.53191 + 1.0 / (F * 2.209e-5 / (0.048 - EMR * 1.32e-5 + EMRDOT * 0.002) + - 0.00235)) + SGFT) * pow(inp.RPC / 1015, 0.17); + return RQDFT; +} + +void TLIFirstGuess::PERCYN(double T) +{ + VECTOR3 RM, VM, RS, SCHM, IV; + double RAM, RA6, DECM, C6, XHM, YHM, ZHM, B6, AZ6; + double RAS, AS, SMOPD; + + const double C = 3443.93358; + + bool IERR = pRTCC->PLEFEM(1, T + inp.Hour, 0, &RM, &VM, &RS, NULL); + if (IERR) + { + CO1 = 2; + return; + } + //Convert from m and m/s to Er and Er/hr + RM /= 6378165.0; + VM /= 6378165.0 / 3600.0; + RS /= 6378165.0; + + EMR = length(RM) * C; + EMRDOT = dotp(RM, VM) / length(RM) * C; + + RAM = atan2(RM.y, RM.x); + RA6 = RAM + PI; + RA6 = HELP(RA6); + DECM = atan2(RM.z, sqrt(RM.x * RM.x + RM.y * RM.y)); + C6 = -DECM; + HM = crossp(RM, VM); + XHM = HM.x; + YHM = HM.y; + ZHM = HM.z; + WM = length(HM) / (pow(length(RM), 2)); + FIM = atan(sqrt(XHM * XHM + YHM * YHM) / ZHM); + RNM = atan2(XHM, -YHM); + B6 = RA6 - RNM; + B6 = HELP(B6); + A6 = atan2(sin(B6), cos(B6) * cos(FIM)); + AM = A6 + PI; + AM = HELP(AM); + AZ6 = atan2(abs(sin(B6)), cos(B6) * abs(sin(C6))); + RAS = atan2(RS.y, RS.x); + DS = atan2(RS.z, sqrt(RS.x * RS.x + RS.y * RS.y)); + AS = atan(sqrt(pow(length(RS), 2) / (RS.x * RS.x) - 1.0)); + if (RS.x < 0) AS = PI - AS; + if (RS.z < 0) AS = -AS; + SCHM = crossp(RS, HM); + IV = crossp(RM, SCHM); + SMOPL = atan2(length(IV), dotp(RM, SCHM)); + if (dotp(IV, HM) < 0) + { + SMOPL = -SMOPL; + } + SMOPL += PI05; + SMOPL = HELP(SMOPL); + SMOPD = PI05 - atan2(length(SCHM), dotp(RS, HM)); +} + +double TLIFirstGuess::HELP(double X) const +{ + while (X > PI) X -= PI2; + while (X <= -PI)X += PI2; + return X; +} + +double TLIFirstGuess::ANGLE(VECTOR3 VEC1, VECTOR3 VEC2, VECTOR3 VEC3) const +{ + VECTOR3 H; + double DUM, XR, H4; + + H = crossp(VEC1, VEC2); + H4 = length(H); + DUM = dotp(VEC1, VEC2); + XR = atan2(H4, DUM); + DUM = dotp(H, VEC3); + if (DUM < 0) + { + XR = -XR; + } + return XR; +} + TLIProcessor::TLIProcessor(RTCC *r) : TLTrajectoryComputers(r) { @@ -41,51 +688,198 @@ void TLIProcessor::Main(TLIOutputData &out) { ErrorIndicator = 0; - if (MEDQuantities.Mode == 4) + //Get time of mixture ratio shift + TLITargetingParametersTable *tlitab = NULL; + int LD; + + LD = pRTCC->GZGENCSN.RefDayOfYear; + + for (int counter = 0; counter < 10; counter++) + { + if (LD == pRTCC->PZSTARGP.data[counter].Day) + { + tlitab = &pRTCC->PZSTARGP.data[counter]; + break; + } + } + + if (tlitab == NULL) + { + //Error + out.ErrorIndicator = 5; + return; + } + T_MRS_SIVB = tlitab->T2[MEDQuantities.Opportunity - 1] / 3600.0; + + switch (MEDQuantities.Mode) { + case 1: Option1(); + break; + case 2: + Option2_5(true); + break; + case 3: + Option3(); + break; + case 4: + Option4(); + break; + case 5: + Option2_5(false); + break; + default: + out.ErrorIndicator = 1; + return; } out.ErrorIndicator = ErrorIndicator; if (out.ErrorIndicator) return; //Convert to LVDC parameters - if (MEDQuantities.Mode == 4) + + //Convert to ECT at GRR + + EphemerisData2 sv, sv_ECT; + + sv.R = outarray.sv_tli_cut.R; + sv.V = outarray.sv_tli_cut.V; + sv.GMT = pRTCC->SystemParameters.MCGRIC*3600.0; + pRTCC->ELVCNV(sv, 0, 1, sv_ECT); + + OELEMENTS coe = OrbMech::coe_from_sv(sv_ECT.R, sv_ECT.V, mu_E); + coe.h = dotp(sv_ECT.V, sv_ECT.V) - 2.0*mu_E / length(sv_ECT.R); + + OELEMENTS coe2 = LVTAR(coe, pRTCC->SystemParameters.MCLGRA, pRTCC->SystemParameters.MCERTS*pRTCC->SystemParameters.MCGRIC); + + out.uplink_data.Inclination = coe2.i; + out.uplink_data.theta_N = coe2.RA; + out.uplink_data.e = coe2.e; + out.uplink_data.C3 = coe2.h; + out.uplink_data.alpha_D = coe2.w; + out.uplink_data.f = coe2.TA; + out.uplink_data.GMT_TIG = outarray.sv_tli_ign.GMT; + + out.dv_TLI = outarray.dv_TLI; + out.sv_TLI_ign = outarray.sv_tli_ign; + out.sv_TLI_cut = outarray.sv_tli_cut; +} + +void TLIProcessor::Option1() +{ + PMMSPTInput in; + + in.QUEID = 34; + in.GMT = MEDQuantities.state.GMT; + in.R = MEDQuantities.state.R; + in.V = MEDQuantities.state.V; + in.Table = MEDQuantities.mpt; + in.InjOpp = MEDQuantities.Opportunity; + in.mpt = NULL; + in.CurMan = NULL; + + int err = pRTCC->PMMSPT(in); + + if (err) { - //7 parameters + ErrorIndicator = 1; + return; + } + //Data is now in PZTTLIPL + + //Propagate state to TIG + EMSMISSInputTable emsin; + emsin.AnchorVector.R = pRTCC->PZTTLIPL.R; + emsin.AnchorVector.V = pRTCC->PZTTLIPL.V; + emsin.AnchorVector.GMT = pRTCC->PZTTLIPL.TB6; + emsin.AnchorVector.RBI = BODY_EARTH; - //Convert to ECT at GRR + emsin.MaxIntegTime = pRTCC->PZTTLIPL.TIG - emsin.AnchorVector.GMT; + emsin.VehicleCode = MEDQuantities.mpt; + emsin.useInputWeights = true; + emsin.WeightsTable = &MEDQuantities.WeightsTable; + + pRTCC->EMSMISS(&emsin); + + if (emsin.NIAuxOutputTable.ErrorCode) + { + ErrorIndicator = 1; + return; + } + + std::vector var, arr; + var.resize(20); + arr.resize(20); + + var[4] = pRTCC->PZTTLIPL.C3 / pow(R_E / 3600.0, 2); //C3 in Er^2/hr^2 + var[5] = 0.0; //dt_EPO + + //Calculate delta + VECTOR3 N_I; + N_I = unit(crossp(pRTCC->PZTTLIPL.R, pRTCC->PZTTLIPL.V)); + var[6] = asin(dotp(pRTCC->PZTTLIPL.T, N_I)); + + void *constPtr; - EphemerisData2 sv, sv_ECT; + outarray.TLIIndicator = true; + outarray.EllipticalCaseIndicator = false; + outarray.TLIOnlyIndicator = true; - sv.R = outarray.sv_tli_cut.R; - sv.V = outarray.sv_tli_cut.V; - sv.GMT = pRTCC->SystemParameters.MCGRIC*3600.0; - pRTCC->ELVCNV(sv, 0, 1, sv_ECT); + outarray.sv0.R = pRTCC->PZTTLIPL.R; + outarray.sv0.V = pRTCC->PZTTLIPL.V; + outarray.sv0.GMT = pRTCC->PZTTLIPL.TIG; + outarray.sv0.RBI = BODY_EARTH; + outarray.M_i = MEDQuantities.WeightsTable.ConfigWeight; + outarray.sigma_TLI = pRTCC->PZTTLIPL.sigma; + constPtr = &outarray; - OELEMENTS coe = OrbMech::coe_from_sv(sv_ECT.R, sv_ECT.V, mu_E); - coe.h = dotp(sv_ECT.V, sv_ECT.V) - 2.0*mu_E / length(sv_ECT.R); + IntegratedTrajectoryComputer(var, constPtr, arr, false); +} - OELEMENTS coe2 = LVTAR(coe, pRTCC->SystemParameters.MCLGRA, pRTCC->SystemParameters.MCERTS*pRTCC->SystemParameters.MCGRIC); +//Hybrid ellipse +void TLIProcessor::Option3() +{ + //Propagate state to TIG + EMSMISSInputTable in; - out.uplink_data.Inclination = coe2.i; - out.uplink_data.theta_N = coe2.RA; - out.uplink_data.e = coe2.e; - out.uplink_data.C3 = coe2.h; - out.uplink_data.alpha_D = coe2.w; - out.uplink_data.f = coe2.TA; - out.uplink_data.GMT_TIG = outarray.sv_tli_ign.GMT; + in.AnchorVector = MEDQuantities.state; + in.MaxIntegTime = MEDQuantities.GMT_TIG - MEDQuantities.state.GMT; + if (in.MaxIntegTime < 0) + { + in.MaxIntegTime = abs(in.MaxIntegTime); + in.IsForwardIntegration = false; } - else + in.VehicleCode = RTCC_MPT_CSM; + in.useInputWeights = true; + in.WeightsTable = &MEDQuantities.WeightsTable; + + pRTCC->EMSMISS(&in); + + if (in.NIAuxOutputTable.ErrorCode) { - //10 parameters + ErrorIndicator = 1; + return; } - out.dv_TLI = outarray.dv_TLI; + outarray.sv0 = in.NIAuxOutputTable.sv_cutoff; + outarray.M_i = in.NIAuxOutputTable.CutoffWeight; + + //Calculate initial guess for C3 + double C3_guess, R_I; + + R_I = length(outarray.sv0.R); + C3_guess = pow(MEDQuantities.dv_available, 2) - OrbMech::mu_Earth / R_I + 2.0*MEDQuantities.dv_available*sqrt(OrbMech::mu_Earth / R_I); + + bool err = HybridMission(C3_guess, MEDQuantities.dv_available); + if (err) + { + ErrorIndicator = 2; + return; + } } -void TLIProcessor::Option1() +void TLIProcessor::Option4() { //Propagate state to TIG EMSMISSInputTable in; @@ -134,6 +928,139 @@ void TLIProcessor::Option1() } } +void TLIProcessor::Option2_5(bool freereturn) +{ + //Initial guess + TLIFirstGuessInputs fgin; + TLIFirstGuessOutputs fgout; + TLIFirstGuess cist(pRTCC); + + //Step 1: propagate state to estimated TIG + EMSMISSInputTable in; + + in.AnchorVector = MEDQuantities.state; + in.MaxIntegTime = MEDQuantities.GMT_TIG - MEDQuantities.state.GMT; + if (in.MaxIntegTime < 0) + { + in.MaxIntegTime = abs(in.MaxIntegTime); + in.IsForwardIntegration = false; + } + in.VehicleCode = RTCC_MPT_CSM; + in.useInputWeights = true; + in.WeightsTable = &MEDQuantities.WeightsTable; + + pRTCC->EMSMISS(&in); + + if (in.NIAuxOutputTable.ErrorCode) + { + ErrorIndicator = 1; + return; + } + + outarray.sv0 = in.NIAuxOutputTable.sv_cutoff; + outarray.M_i = in.NIAuxOutputTable.CutoffWeight; + outarray.rad_lls = OrbMech::R_Moon; + + double rmag, vmag, rtasc, decl, fpav, az; + OrbMech::rv_from_adbar(outarray.sv0.R, outarray.sv0.V, rmag, vmag, rtasc, decl, fpav, az); + + //Step 2: First guess logic + fgin.BHA = pRTCC->SystemParameters.MCLAMD; + fgin.Hour = outarray.sv0.GMT / 3600.0; + fgin.C1 = decl; + fgin.FLO1 = OrbMech::LongitudeConversion(rtasc, outarray.sv0.GMT, OrbMech::w_Earth, pRTCC->SystemParameters.MCLAMD, true); + fgin.AZ1 = az; + fgin.R1 = rmag / 1852.0; + fgin.ORBNUM = 0.0; + fgin.IPOA = MEDQuantities.IPOA; + fgin.IPERT = 3; + fgin.XPC = 0.0; + fgin.YPC = 0.0; + fgin.RPC = (OrbMech::R_Moon + MEDQuantities.h_PC) / 1852.0; + + cist.Calculate(fgin); + int err = cist.GetError(); + if (err) + { + return; + } + cist.GetOutput(fgout); + + //Step 3: Converge trajectory + double GMT_node, lat_nd, lng_nd; + if (freereturn) + { + GMT_node = outarray.sv0.GMT + fgout.TOPCY*3600.0; + lat_nd = MEDQuantities.lat_PC; + lng_nd = PI; + } + else + { + GMT_node = MEDQuantities.GMT_node; + lat_nd = MEDQuantities.lat_PC; + lng_nd = MEDQuantities.lng_node; + } + err = IntegratedTLIToNode(fgout.C3, fgout.CTIO, fgout.DEL / 4.0, fgout.S, GMT_node, lat_nd, lng_nd); + if (err) + { + ErrorIndicator = 2; + return; + } + + if (freereturn) + { + err = IntegratedTLIFlyby(outarray.C3_TLI, outarray.dt_EPO / 3600.0, outarray.delta_TLI, fgout.S, MEDQuantities.lat_PC); + if (err) + { + ErrorIndicator = 2; + return; + } + } +} + +bool TLIProcessor::HybridMission(double C3_guess, double dv_TLI) +{ + double C3_guess_ER = C3_guess / pow(R_E / 3600.0, 2); + + void *constPtr; + outarray.TLIIndicator = true; + outarray.TLIOnlyIndicator = true; + //EllipticalCaseIndicator is used to decide between the nominal vs. alternate mission TLI polynomials + if (C3_guess_ER < -5.0) + { + outarray.EllipticalCaseIndicator = true; + } + else + { + outarray.EllipticalCaseIndicator = false; + } + outarray.sigma_TLI = MissionConstants.sigma; + constPtr = &outarray; + + bool IntegratedTrajectoryComputerPointer(void *data, std::vector &var, void *varPtr, std::vector& arr, bool mode); + bool(*fptr)(void *, std::vector&, void*, std::vector&, bool) = &IntegratedTrajectoryComputerPointer; + + GenIterator::GeneralizedIteratorBlock block; + + block.IndVarSwitch[4] = true; + + block.IndVarGuess[4] = C3_guess_ER; + block.IndVarGuess[5] = 0.0; //dt_EPO + block.IndVarGuess[6] = MissionConstants.delta; + + block.IndVarStep[4] = pow(2, -23); + block.IndVarWeight[4] = 4.0; + + block.DepVarSwitch[9] = true; + block.DepVarLowerLimit[9] = dv_TLI - 1.0*0.3048; + block.DepVarUpperLimit[9] = dv_TLI + 1.0*0.3048; + block.DepVarClass[9] = 1; + + std::vector result; + std::vector y_vals; + return GenIterator::GeneralizedIterator(fptr, block, constPtr, (void*)this, result, y_vals); +} + bool TLIProcessor::ConicTLIIEllipse(double C3_guess, double h_ap) { void *constPtr; @@ -201,6 +1128,124 @@ bool TLIProcessor::IntegratedTLIIEllipse(double C3_guess_ER, double h_ap) return GenIterator::GeneralizedIterator(fptr, block, constPtr, (void*)this, result, y_vals); } +bool TLIProcessor::IntegratedTLIToNode(double C3_guess_ER, double T_c_hrs, double delta, double sigma, double GMT_nd, double lat_nd, double lng_nd) +{ + void *constPtr; + outarray.TLIIndicator = true; + outarray.EllipticalCaseIndicator = false; + outarray.sigma_TLI = sigma; + outarray.NodeStopIndicator = true; + outarray.GMT_nd = GMT_nd; + + constPtr = &outarray; + + bool IntegratedTrajectoryComputerPointer(void *data, std::vector &var, void *varPtr, std::vector& arr, bool mode); + bool(*fptr)(void *, std::vector&, void*, std::vector&, bool) = &IntegratedTrajectoryComputerPointer; + + GenIterator::GeneralizedIteratorBlock block; + + block.IndVarSwitch[4] = true; //C3 + block.IndVarSwitch[5] = true; //DT EPO + block.IndVarSwitch[6] = true; //TLI plane change + + block.IndVarGuess[4] = C3_guess_ER; + block.IndVarGuess[5] = T_c_hrs; + block.IndVarGuess[6] = delta; + + block.IndVarStep[4] = pow(2, -21); + block.IndVarStep[5] = pow(2, -22); + block.IndVarStep[6] = pow(2, -21); + + block.IndVarWeight[4] = 1.0; + block.IndVarWeight[5] = 1.0; + block.IndVarWeight[6] = 1.0; + + double R_nd; + R_nd = OrbMech::R_Moon + MEDQuantities.h_PC; + + block.DepVarSwitch[0] = true; + block.DepVarSwitch[1] = true; + block.DepVarSwitch[2] = true; + block.DepVarSwitch[3] = true; + block.DepVarLowerLimit[0] = (R_nd - 0.5*1852.0) / R_E; + block.DepVarLowerLimit[1] = lat_nd - 0.01*RAD; + block.DepVarLowerLimit[2] = lng_nd - 0.01*RAD; + block.DepVarLowerLimit[3] = 90.0*RAD; + block.DepVarUpperLimit[0] = (R_nd + 0.5*1852.0) / R_E; + block.DepVarUpperLimit[1] = lat_nd + 0.01*RAD; + block.DepVarUpperLimit[2] = lng_nd + 0.01*RAD; + block.DepVarUpperLimit[3] = 182.0*RAD; + block.DepVarWeight[3] = 64.0; + block.DepVarClass[0] = 1; + block.DepVarClass[1] = 1; + block.DepVarClass[2] = 1; + block.DepVarClass[3] = 2; + + std::vector result; + std::vector y_vals; + return GenIterator::GeneralizedIterator(fptr, block, constPtr, (void*)this, result, y_vals); +} + +bool TLIProcessor::IntegratedTLIFlyby(double C3_guess_ER, double T_c_hrs, double delta, double sigma, double lat_pc) +{ + void *constPtr; + outarray.TLIIndicator = true; + outarray.EllipticalCaseIndicator = false; + outarray.sigma_TLI = sigma; + outarray.NodeStopIndicator = false; + outarray.LunarFlybyIndicator = false; + + constPtr = &outarray; + + bool IntegratedTrajectoryComputerPointer(void *data, std::vector &var, void *varPtr, std::vector& arr, bool mode); + bool(*fptr)(void *, std::vector&, void*, std::vector&, bool) = &IntegratedTrajectoryComputerPointer; + + GenIterator::GeneralizedIteratorBlock block; + + block.IndVarSwitch[4] = true; //C3 + block.IndVarSwitch[5] = true; //DT EPO + block.IndVarSwitch[6] = true; //TLI plane change + + block.IndVarGuess[4] = C3_guess_ER; + block.IndVarGuess[5] = T_c_hrs; + block.IndVarGuess[6] = delta; + + block.IndVarStep[4] = pow(2, -21); + block.IndVarStep[5] = pow(2, -22); + block.IndVarStep[6] = pow(2, -21); + + block.IndVarWeight[4] = 1.0; + block.IndVarWeight[5] = 1.0; + block.IndVarWeight[6] = 1.0; + + block.DepVarSwitch[3] = true; + block.DepVarSwitch[4] = true; + block.DepVarSwitch[5] = true; + block.DepVarSwitch[6] = true; + block.DepVarSwitch[7] = true; + block.DepVarLowerLimit[3] = 90.0*RAD; + block.DepVarLowerLimit[4] = (MEDQuantities.h_PC - 0.5*1852.0) / R_E; + block.DepVarLowerLimit[5] = lat_pc - 0.01*RAD; + block.DepVarLowerLimit[6] = 0.0; + block.DepVarLowerLimit[7] = (60.85*1852.0) / R_E; + block.DepVarUpperLimit[3] = 182.0*RAD; + block.DepVarUpperLimit[4] = (MEDQuantities.h_PC + 0.5*1852.0) / R_E; + block.DepVarUpperLimit[5] = lat_pc + 0.01*RAD; + block.DepVarUpperLimit[6] = 90.0*RAD; + block.DepVarUpperLimit[7] = (70.85*1852.0) / R_E; + block.DepVarWeight[3] = 64.0; + block.DepVarWeight[6] = 8.0; + block.DepVarClass[3] = 2; + block.DepVarClass[4] = 1; + block.DepVarClass[5] = 1; + block.DepVarClass[6] = 2; + block.DepVarClass[7] = 1; + + std::vector result; + std::vector y_vals; + return GenIterator::GeneralizedIterator(fptr, block, constPtr, (void*)this, result, y_vals); +} + OELEMENTS TLIProcessor::LVTAR(OELEMENTS coe, double lng_PAD, double RAGL) const { //lng_PAD: longitude of the launch pad diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.h b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.h index 20d8f3d45d..64fefadadf 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.h +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLIProcessor.h @@ -25,16 +25,119 @@ See http://nassp.sourceforge.net/license/ for more details. #include "TLTrajectoryComputers.h" +struct TLIFirstGuessInputs +{ + double BHA; //Hour angle at midnight, radians + double Hour; //Hour at which to start the search, hours + + double C1; //Declination of the input state vector, radians + double FLO1; //Geographic longitude of the input orbit state vector, radians + double AZ1; //Inertial azimuth of the input orbit state vector, radians + double R1; //Radius of the input orbit state vector, nautical miles + double ORBNUM; //Number of the inertial orbit revolution + int IPOA; //1 = Pacific window (AZ2V <= 90°), 2 = Atlantic window (AZ2V > 90°) + int IPERT; //0 = no perturbations, 1 = Earth oblateness, 2 = solar gravitation, 3 = both + double XPC; //Longitude of pericynthion of the simulated trajectory, measured in a moon-centered MOP coordinate system + //from the extension of the earth-moon axis on the back side of the moon, degrees + double YPC; //Declination of pericynthion of the simulated trajectory, in a moon-centered MOP coordinate system + double RPC; //Radius of pericynthion of the simulation trajectory, nautical miles + int MAXIT = 50; + double TOL = 0.001; + double TTW = 0.7; //Valid for 0.63 to 0.8 + bool debug = true; +}; + +struct TLIFirstGuessOutputs +{ + double T1; //Time of launch from base time + double CTIO; //Time in Earth orbit + double C3; //Energy of TLI + double S; //Angle sigma + double TOPCY; //Time of PC from base time + double DEL; //Latitude of the updated unit M TLI targeting vector relative to the dispersed orbit plane +}; + +class TLIFirstGuess : public RTCCModule +{ +public: + TLIFirstGuess(RTCC *r); + + void Calculate(const TLIFirstGuessInputs& in); + + void GetOutput(TLIFirstGuessOutputs& out); + int GetError() { return CO1; } +private: + void CIST(double RAGBT); + void UPDATE(double TOIDIS); + + void PERCYN(double T); + double ENERGY(int IPERT, double C4) const; + double FLYTYM(int IPERT) const; + void GEOLAT(double FI, double C, double RA, int I, double& A, double& B, double& AZ, double& RN) const; + void GEOARG(double FI, double A, double RN, double& AZ, double& B, double& C, double& RA) const; + void SUBB(int IPERT, double &BETA, double &AFNTMH) const; + double SUBCL(int IPERT) const; + void TLIMP(double W); + double HELP(double X) const; + double ANGLE(VECTOR3 VEC1, VECTOR3 VEC2, VECTOR3 VEC3) const; + + TLIFirstGuessInputs inp; + TLIFirstGuessOutputs outp; + + VECTOR3 MH; //Hypersurface + double WV; + double FIV; + double RNV; + double A1; + + double FIVTL; + double R4; + double APS; + double FTOCO; + + VECTOR3 HM; //Angular momentum vector of the Moon + double EMR; + double EMRDOT; + double SMOPL; + double A6; + double FIM; + double RNM; + double WM; + double AM; + double DS; + double DEL; //Latitude of the updated unit M TLI targeting vector relative to the dispersed orbit plane + + //Error indicator + int CO1; + + //Earth rotational rate + const double WE; +}; + struct TLIMEDQuantities { - //1 = S-IVB hypersurface solution, 2 = integrated free-return, 3 = hybrid ellipse, 4 = E-type mission ellipse + //1 = CSM, 3 = LEM + int mpt; + + //1 = S-IVB hypersurface solution, 2 = integrated free-return, 3 = hybrid ellipse, 4 = E-type mission ellipse, 5 = non-free return int Mode; EphemerisData state; PLAWDTOutput WeightsTable; + double GMT_TIG; //TIG or estimated TIG + int Opportunity; //1 or 2 + + //Mode 2/5 + int IPOA; //1 = Pacific window (AZ2V <= 90°), 2 = Atlantic window (AZ2V > 90°), for first guess logic + double h_PC; //Pericynthion flyby altitude, meters + double lat_PC; //Latitude of pericynthion (mode 2) or of node (mode 5) + double lng_node; //Longitude of node (mode 5) + double GMT_node; //Time at node (mode 5) - //Mode1 - double GMT_TIG; - double h_ap; + //Mode 3 + double dv_available; //DV available for TLI + + //Mode 4 + double h_ap; //Desired apogee altitude at TLI cutoff }; struct SevenParameterUpdate @@ -65,11 +168,20 @@ class TLIProcessor : public TLTrajectoryComputers void Main(TLIOutputData &out); void Init(TLIMEDQuantities med, TLMCCMissionConstants constants, double GMTBase); protected: - + //Hypersurface void Option1(); - + //Free return or non-free return + void Option2_5(bool freereturn); + //Hybrid ellipse + void Option3(); + //Desired apogee + void Option4(); + + bool HybridMission(double C3_guess, double dv_TLI); bool ConicTLIIEllipse(double C3_guess, double h_ap); bool IntegratedTLIIEllipse(double C3_guess_ER, double h_ap); + bool IntegratedTLIToNode(double C3_guess_ER, double T_c_hrs, double delta, double sigma, double GMT_nd, double lat_nd, double lng_nd); + bool IntegratedTLIFlyby(double C3_guess_ER, double T_c_hrs, double delta, double sigma, double lat_pc); OELEMENTS LVTAR(OELEMENTS coe, double lng_PAD, double RAGL) const; diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.cpp index 7ee910ddc6..a5f5333c54 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.cpp @@ -37,7 +37,7 @@ TLTrajectoryComputers::TLTrajectoryComputers(RTCC *r) : RTCCModule(r) F_I_SIVB = 179847.1544797684; //LBF, 800000.0 Newton F_SIVB = 202328.0487897395; //LBF, 900000.0 Newton - WDOT_SIVB = 1677750.443641722;; //LBS per hours, 211.393 kg/s + WDOT_SIVB = 1677750.443641722; //LBS per hours, 211.393 kg/s T_MRS_SIVB = 50.0 / 3600.0; //Hours gamma_reentry = -6.52*RAD; @@ -553,6 +553,7 @@ bool TLTrajectoryComputers::IntegratedTrajectoryComputer(std::vector &va //6: Inclination of return in rad //7: Height of return in Er //8: Apogee height in Er + //9: TLI Delta V in m/s TLMCCGeneralizedIteratorArray *vars; vars = static_cast(varPtr); @@ -571,8 +572,6 @@ bool TLTrajectoryComputers::IntegratedTrajectoryComputer(std::vector &va vars->dt_EPO = var[5] * 3600.0; vars->delta_TLI = var[6]; - dt_node = 0.0; //Not needed? - sv0 = vars->sv0; //Propagate to TLI ignition @@ -593,17 +592,36 @@ bool TLTrajectoryComputers::IntegratedTrajectoryComputer(std::vector &va state_TLI2.sv.R *= R_E; state_TLI2.sv.V *= R_E / 3600.0; state_TLI2.sv.GMT *= 3600.0; + state_TLI2.Mass *= 0.45359237; vars->dv_TLI *= R_E / 3600.0; vars->sv_tli_cut = state_TLI2.sv; + arr[9] = vars->dv_TLI; + //Simulate only TLI? + if (vars->TLIOnlyIndicator) return false; + //Simulate through apogee? if (vars->EllipticalCaseIndicator) { //Propagate state vector to apogee pRTCC->PMMCEN(state_TLI2.sv, 0.0, 100.0*3600.0, 2, 0.0, 1.0, sv_ap, ITS); + + //Error: Did not find apogee or reached lunar SOI + if (ITS != 2 || sv_ap.RBI != BODY_EARTH) + { + return true; + } + arr[8] = (length(sv_ap.R) - R_E) / R_E; return false; } + + sv0_apo = vars->sv_tli_cut; + + if (vars->NodeStopIndicator) + { + dt_node = vars->GMT_nd - vars->sv_tli_cut.GMT; + } } else { diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.h b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.h index 093323165a..ec71a6ca30 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.h +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/TLTrajectoryComputers.h @@ -53,6 +53,7 @@ struct TLMCCGeneralizedIteratorArray //For integrating trajectory computer bool NodeStopIndicator; bool EllipticalCaseIndicator; + bool TLIOnlyIndicator = false; //For conic trajectory computer bool FreeReturnIndicator; bool FreeReturnOnlyIndicator; @@ -148,8 +149,8 @@ struct TLMCCGeneralizedIteratorArray //TLI only double dv_TLI; - double C3_TLI; - double dt_EPO; + double C3_TLI; //Er^2/hr^2 + double dt_EPO; //sec double delta_TLI; double sigma_TLI; }; @@ -218,7 +219,7 @@ class TLTrajectoryComputers : public RTCCModule double F_I_SIVB; //S-IVB thrust magnitude from ignition to MRS double F_SIVB; //S-IVB thrust magnitude from MRS to cutoff double WDOT_SIVB; //Mass flow rate of S-IVB - double T_MRS_SIVB; //TEstimated time of MRS, measured from ignition + double T_MRS_SIVB; //Estimated time of MRS, measured from ignition double gamma_reentry; double Reentry_dt; }; \ No newline at end of file diff --git a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/rtcc_library_programs.cpp b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/rtcc_library_programs.cpp index 421abe0406..71da259d3d 100644 --- a/Orbitersdk/samples/ProjectApollo/src_rtccmfd/rtcc_library_programs.cpp +++ b/Orbitersdk/samples/ProjectApollo/src_rtccmfd/rtcc_library_programs.cpp @@ -2037,6 +2037,14 @@ void RTCC::PLAWDT(const PLAWDTInput &in, PLAWDTOutput &out) RTCC_PLAWDT_9_T: out.Err = 3; //Move areas and weights to output + out.CSMArea = in.CSMArea; + out.SIVBArea = in.SIVBArea; + out.LMAscArea = in.LMAscArea; + out.LMDscArea = in.LMDscArea; + out.CSMWeight = in.CSMWeight; + out.SIVBWeight = in.SIVBWeight; + out.LMAscWeight = in.LMAscWeight; + out.LMDscWeight = in.LMDscWeight; goto RTCC_PLAWDT_M_5; }