예제 #1
0
double AscentAP::GetTargetInclination ()
{
	double a=0.0, B=0.0;
	if (vessel->status == 0) {
		if (!launch_lat && !launch_lng) {
			double r;
			vessel->GetEquPos(launch_lng, launch_lat, r);
		}
		a = PI05-launch_lat;

		// correct launch azimuth for surface rotation
		const OBJHANDLE hRef = vessel->GetGravityRef();
		double R = oapiGetSize(hRef);           // planet mean radius
		double r = R + tgt_alt;                 // target orbit radius
		double M = oapiGetMass (hRef);          // reference body mass
		double v0 = sqrt(GGRAV*M/r);            // target orbit speed
		double vg = PI2*R/oapiGetPlanetPeriod(hRef)*cos(launch_lat);
		                                        // surface speed at launch position
		double vx0 = v0*sin(launch_azimuth);    // longitudinal velocity component
		double vx1 = vx0 + vg;                  // corrected for planet rotation
		double vy  = v0*cos(launch_azimuth);    // latitudinal velocity component
		B = atan2(vx1,vy);                      // effective launch azimuth
	}
	return PI05 - asin(sin(a)*sin(B));
}
예제 #2
0
void AscentApMfd::UpdatePg_Prm (oapi::Sketchpad *skp)
{
	char cbuf[256];

	if (!ap->GetVessel()->status) {
		sprintf (cbuf, "Launch azimuth: %0.1fº", ap->GetLaunchAzimuth()*DEG);
		skp->Text (cw/2, (ch*3)/2, cbuf, strlen(cbuf));
		sprintf (cbuf, "Orbit inc:      %0.1fº", ap->GetTargetInclination()*DEG);
		skp->Text (cw/2, (ch*5)/2, cbuf, strlen(cbuf));
		sprintf (cbuf, "Orbit altitude: %0.1fkm", ap->GetOrbitAltitude()*1e-3);
		skp->Text (cw/2, (ch*7)/2, cbuf, strlen(cbuf));
	} else {
		OBJHANDLE hRef = ap->GetVessel()->GetGravityRef();
		double R = oapiGetSize(hRef);
		double az_tgt = ap->GetTargetAzimuth();
		double az_cur = ap->GetVessel()->GetYaw();
		double az_err = fabs(az_cur-az_tgt);
		if (az_err > PI) az_err = PI2-az_err;
		if (az_cur < az_tgt) az_err = -az_err;
		double pt_tgt = ap->GetTargetPitch();
		double pt_cur = ap->GetVessel()->GetPitch();
		double pt_err = pt_cur-pt_tgt;
		double alt_tgt = ap->GetOrbitAltitude();
		double alt_ap_cur, alt_pe_cur;
		ap->GetVessel()->GetApDist(alt_ap_cur);
		ap->GetVessel()->GetPeDist(alt_pe_cur);
		alt_ap_cur -= R; alt_pe_cur -= R;
		skp->Text (cw*13, ch*2, "Cur    Tgt    D", 15);
		skp->Text (cw/2, ch*3, "Azimuth [º]", 11);
		sprintf (cbuf, "%0.1lf", az_cur*DEG);
		skp->Text (cw*13, ch*3, cbuf, strlen(cbuf));
		sprintf (cbuf, "%0.1lf", az_tgt*DEG);
		skp->Text (cw*20, ch*3, cbuf, strlen(cbuf));
		sprintf (cbuf, "%+0.2f", az_err*DEG);
		skp->Text (cw*27, ch*3, cbuf, strlen(cbuf));
		skp->Text (cw/2, ch*4, "Pitch [º]", 9);
		sprintf (cbuf, "%0.1lf", pt_cur*DEG);
		skp->Text (cw*13, ch*4, cbuf, strlen(cbuf));
		sprintf (cbuf, "%0.1lf", pt_tgt*DEG);
		skp->Text (cw*20, ch*4, cbuf, strlen(cbuf));
		sprintf (cbuf, "%+0.2lf", pt_err*DEG);
		skp->Text (cw*27, ch*4, cbuf, strlen(cbuf));
		skp->Text (cw/2, ch*5, "Ap.Alt [km]", 11);
		sprintf (cbuf, "% 0.1lf", alt_ap_cur*1e-3);
		skp->Text (cw*12, ch*5, cbuf, strlen(cbuf));
		sprintf (cbuf, "%0.1lf", alt_tgt*1e-3);
		skp->Text (cw*20, ch*5, cbuf, strlen(cbuf));
		sprintf (cbuf, "%+0.2lf", (alt_ap_cur-alt_tgt)*1e-3);
		skp->Text (cw*27, ch*5, cbuf, strlen(cbuf));
		skp->Text (cw/2, ch*6, "Pe.Alt [km]", 11);
		sprintf (cbuf, "% 0.1lf", alt_pe_cur*1e-3);
		skp->Text (cw*12, ch*6, cbuf, strlen(cbuf));
		sprintf (cbuf, "%0.1lf", alt_tgt*1e-3);
		skp->Text (cw*20, ch*6, cbuf, strlen(cbuf));
		sprintf (cbuf, "%+0.2lf", (alt_pe_cur-alt_tgt)*1e-3);
		skp->Text (cw*27, ch*6, cbuf, strlen(cbuf));
	}
}
예제 #3
0
파일: lemmesh.cpp 프로젝트: dseagrav/NASSP
void LEM::ToggleEVA()

{
	ToggleEva = false;	
	
	if (CDREVA_IP) {
		// Nothing for now, the EVA is ended, when the LEVA vessel calls StopEVA
		/// \todo Support for 2 LEVA vessels
	}
	else {
		VESSELSTATUS vs1;
		GetStatus(vs1);

		// The LM must be in landed state
		if (vs1.status != 1) return;

		CDREVA_IP = true;

		OBJHANDLE hbody = GetGravityRef();
		double radius = oapiGetSize(hbody);
		vs1.vdata[0].x += 4.5 * sin(vs1.vdata[0].z) / radius;
		vs1.vdata[0].y += 4.5 * cos(vs1.vdata[0].z) / radius;

		char VName[256]="";
		strcpy (VName, GetName()); strcat (VName, "-LEVA");
		hLEVA = oapiCreateVessel(VName,"ProjectApollo/LEVA",vs1);
		
		SwitchFocusToLeva = 10;

		LEVA *leva = (LEVA *) oapiGetVesselInterface(hLEVA);
		if (leva) {
			LEVASettings evas;

			evas.MissionNo = agc.GetApolloNo();
			evas.Realism = Realism;
			leva->SetEVAStats(evas);
		}
	}
}
예제 #4
0
// ==============================================================================================================================================
//
void Orbit::LEO(OBJHANDLE ref)
{	
	if (!ref) {	LeoRef=NULL; return; }

	double obli  = oapiGetPlanetObliquity(ref);
	double peri  = oapiGetPlanetPeriod(ref);
	double trans = oapiGetPlanetTheta(ref);

	VECTOR3 rota, refd;
	PlanetAxis(obli,trans,&rota,&refd);

	VECTOR3 velo = crossp(rota,refd);
	double  myy  = oapiGetMass(ref)*GC;
	double  rad  = oapiGetSize(ref);
	double  vel  = sqrt(myy/rad);

	refd=set_length(refd,rad);
	velo=set_length(velo,vel);
	if (peri<0) velo=_V(0,0,0)-velo;

	Elements(refd,velo,myy);
	LeoRef=ref;
}
예제 #5
0
파일: InstrVs.cpp 프로젝트: Artoria2e5/obtr
bool InstrSpd::Redraw2D (SURFHANDLE surf)
{
	double spd = vessel->GetGroundspeed()*0.1;
	double vspd, aspd = fabs(spd);

	static double texw = PANELEL_TEXW, texh = PANELEL_TEXH;
	static double scalecnt = texh-422.0+152.0;
	static int scaleunit = 15;
	static double viewh = 50.0;
	double ycnt, y0, y1, dy, ddy;
	char *c, cbuf[12];
	bool centered = (fabs(spd) <= 4.0);

	dy = spd-floor(spd);
	if (centered) {
		ycnt = scalecnt - spd*scaleunit;
	} else {
		if (spd > 0.0) ycnt = scalecnt - (5.0+dy)*scaleunit;
		else           ycnt = scalecnt + (5.0-dy)*scaleunit;
	}
	y0 = ycnt-viewh;
	y1 = ycnt+viewh;
	grp->Vtx[0+vtxofs].tv = grp->Vtx[1+vtxofs].tv = (float)(y0/texh);
	grp->Vtx[2+vtxofs].tv = grp->Vtx[3+vtxofs].tv = (float)(y1/texh);

	// copy labels onto scale
	const int labelx = 70;
	int i, j, n, smin, smax, iy, len, ysrc;
	int s0 = (int)floor(spd);
	smin = s0-3;
	if (smin != psmin) {
		psmin = smin;
		smax = smin+7;
		for (i = smin; i <= smax; i++) {
			sprintf (cbuf, "%03d", abs((i%100)*10));
			len = 3;
			if (centered) {
				if (!i) continue;
				iy = (int)scalecnt-i*scaleunit-5;
			} else {
				if (i > 0) iy = (int)scalecnt-(2+i-smin)*scaleunit-5;
				else       iy = (int)scalecnt+(8-i+smin)*scaleunit-5;
			}
			for (j = 0, c = cbuf; j < 3; c++, j++) {
				n = *c-'0';
				ysrc = (int)texh-265+n*8;
				if (i < 0) ysrc += 88;
				oapiBlt (surf, surf, labelx+j*6, iy, label_srcx, ysrc, 6, 8);
			}
		}
	}

#ifdef UNDEF
	// apoapsis/periapsis altitude indicators
	OBJHANDLE hRef = vessel->GetSurfaceRef();
	if (hRef) {
		double rad = oapiGetSize (hRef);
		double apalt, pealt;
		vessel->GetApDist(apalt); apalt -= rad; apalt *= 1e-3;
		vessel->GetPeDist(pealt); pealt -= rad; pealt *= 1e-3;

		float yofs;
		if (apalt < alt+4 && apalt > alt-4)
			yofs = (float)((apalt-alt)*scaleunit*(48.0/50.0));
		else yofs = -60;
		static const float y0[3] = {atape_ycnt, atape_ycnt, atape_ycnt-11};
		for (i = 0; i < 3; i++)
			grp->Vtx[vtxofs+i+16].y = y0[i]-yofs;

		if (pealt < alt+4 && pealt > alt-4)
			yofs = (float)((pealt-alt)*scaleunit*(48.0/50.0));
		else yofs = -60;
		static const float y1[3] = {atape_ycnt, atape_ycnt, atape_ycnt+11};
		for (i = 0; i < 3; i++)
			grp->Vtx[vtxofs+i+19].y = y1[i]-yofs;
	}
#endif

	// km*1000 indicator wheels
	sprintf (cbuf, "%06d", (((int)aspd)%100000)*10);
	for (i = 0; i < 3; i++) {
		float yofs = (float)(texh-111 - (cbuf[i]-'0')*15);
		if (aspd > 50.0) {
			const double scl[3] = {1e4,1e3,1e2};
			vspd = aspd/scl[i];
			ddy = (vspd-floor(vspd))*scl[i];
			// number dials in rotation phase
			if (ddy < 0.5) yofs += (float)((0.5-ddy)*15.0);
			else if (ddy > scl[i]-0.5) yofs += (float)((scl[i]-0.5-ddy)*15.0);
		}
		for (j = 0; j < 4; j++) {
			grp->Vtx[4+i*4+vtxofs].tv = grp->Vtx[5+i*4+vtxofs].tv = yofs/(float)texh;
			grp->Vtx[6+i*4+vtxofs].tv = grp->Vtx[7+i*4+vtxofs].tv = (yofs+17.0f)/(float)texh;
		}
	}

	return false;
}
예제 #6
0
int SoundEvent::play(SoundLib soundlib,
					 VESSEL   *Vessel,
					  char    *names ,
					  double  *offset,
					  int     *newbuffer,
					  double  simcomputert,
					  double  MissionTime,
					  int     mode,
					  double  timeremaining,
					  double  timeafterpdi,
					  double  timetoapproach,
					  int     flags,
					  int     volume)


{
	int kk;
	char buffers[255];
	double altitude;
	double vlon,vlat,vrad;


    double timetopdi;
	double timesincepdi;
	double deltaoffset =0.;

    if (!isValid())
		return(false);

	// is Sound still playing ?
	// if yes let it play

//   TRACESETUP("SOUNDEVENT PLAY");

	if(lastplayed >= 0)
	{
        if(soundevents[lastplayed].offset == -1)
		{
	        if (IsPlaying())
			{ 
            //		TRACE("EN TRAIN DE JOUER");
                return(false);
			}
		}
		else if (IsPlaying())
		{
			if (soundevents[lastplayed+1].offset > soundevents[lastplayed].offset)
			{
			    if(Finish((double) soundevents[lastplayed+1].offset - 0.1))
				{
				}
				else
				    return(false);
			}
		}
	}


//  no more sounds to be played just return

	if(soundevents[lastplayed+1].met == 0)
		return(false);


//	sprintf(buffers,"ENTREE PLAY MODE %d timeremaining %f timeafterpdi %f altitude %f",
//		mode,timeremaining,timeafterpdi,altitude);
//	TRACE(buffers);

	timetopdi = timeremaining  -(MissionTime - simcomputert);
	timesincepdi = timeafterpdi +(MissionTime - simcomputert);
	timetoapproach = timetoapproach -(MissionTime -simcomputert);
    if (mode == 0)
	{ 
/*		sprintf(buffers,"AVANT PDI IGNIT %f %f %f ",
					   timetopdi, MissionTime, simcomputert);
		sprintf(oapiDebugString(),"%s",buffers);
*/
	}	

	if (mode >= 1 && mode < 3)
	{ 
		OBJHANDLE hbody=Vessel->GetGravityRef();
		double bradius=oapiGetSize(hbody);
		Vessel->GetEquPos(vlon, vlat, vrad);
		double cgelev=Vessel->GetCOG_elev();
		altitude=vrad-bradius-cgelev;

							
/*		sprintf(buffers,"AFTER PDI : TIMEAFTER %f ALTITUDE %f",
			        timesincepdi,altitude);
		sprintf(oapiDebugString(),"%s",buffers);
*/
	}	

    kk = lastplayed+1;
	sprintf(buffers,"%f",soundevents[kk].altitude);
//	TRACE (buffers);
//	sprintf(buffers,"%f",altitude);
//	TRACE (buffers);

    // if mode = 1 BRAKING it's altitude 
	// if mode = 0 MONITOR it's time to ignition

	if (mode == 0)
    {
//		TRACE("TEST PDI IGNIT");
		sprintf(buffers,"TEST PDI IGNIT NEXTEVENT %d %f %s VERSUS %f",kk,
			        soundevents[kk].timetoignition,soundevents[kk].filenames,
					   timetopdi);
//		sprintf(oapiDebugString(),"%s",buffers);
//		TRACE(buffers);
        /* skip too old sounds */
		while (    (soundevents[kk].timetoignition > timetopdi)
			    && (soundevents[kk+1].timetoignition > timetopdi)
			  )
	        kk++;
        if (soundevents[kk].timetoignition < timetopdi)
			return 0;
		deltaoffset = soundevents[kk].timetoignition - timetopdi;
	}
	else if (mode == 1)
	{
//sprintf(buffers,"TIME AFTER PDI %f", timesincepdi);
//TRACE(buffers);
        
		if ((timeafterpdi < 160))
		{
//        TRACE("TIME AFTER");
		sprintf(buffers,"TIME AFTER %f %f",soundevents[kk].timeafterignition,
			                                                     timesincepdi);
//		TRACE(buffers);

		sprintf(buffers,"TEST AFTER PDI NEXTEVENT %d %f %s VERSUS %f",kk,
			        soundevents[kk].timeafterignition,soundevents[kk].filenames,
					   timesincepdi);
//		sprintf(oapiDebugString(),"%s",buffers);

		while (    (soundevents[kk].timeafterignition < timesincepdi)
			    && (soundevents[kk+1].timeafterignition < timesincepdi)
				&& (!soundevents[kk].mandatory)
			  )
	        kk++;

        if (soundevents[kk].timeafterignition>timesincepdi)
		    return 0;

		}
        else
		{
//        TRACE("TIME TO APPROACH");
		sprintf(buffers,"TIME TO APP %f %f",soundevents[kk].timetoapproach,
			                                                     timetoapproach);
//		TRACE(buffers);

		sprintf(buffers,"TEST TO APPROACH NEXTEVENT %d %f %s VERSUS %f",kk,
			        soundevents[kk].timetoapproach,soundevents[kk].filenames,
					   timetoapproach);
//		sprintf(oapiDebugString(),"%s",buffers);

		while (    (soundevents[kk].timetoapproach > timetoapproach)
			    && (soundevents[kk+1].timetoapproach > timetoapproach)
				&& (!soundevents[kk].mandatory)
			  )
	        kk++;

        if (soundevents[kk].timetoapproach<timetoapproach)
		    return 0;

		}
	}
	else if (mode == 2)
	{
//        TRACE("TEST ALTITUDE");
		sprintf(buffers,"ALT %d %f %f",mode,soundevents[kk].altitude,altitude);
//		TRACE(buffers);

//		sprintf(buffers,"TEST ALTI NEXTEVENT %d %f %s VERSUS %f MODE %d",kk,
//			        soundevents[kk].altitude,soundevents[kk].filenames,
//					   altitude,mode);
//		sprintf(oapiDebugString(),"%s",buffers);

        while (soundevents[kk].mode != mode)
		{
			kk++;
		}


		while (    (soundevents[kk].altitude > altitude)
			    && (soundevents[kk+1].altitude > altitude)
				&& (!soundevents[kk].mandatory)
			  )
	        kk++;

        if (soundevents[kk].altitude<altitude)
		    return 0;
	}
	else if (mode == 3)
	{
        if (soundevents[kk].met > MissionTime)
			return 0;
		deltaoffset = 0.0;
	}
	else return 0;
 

//	TRACE("ON VA JOUER ");
//	TRACE(soundevents[kk].filenames);
	

	strcpy(names,soundevents[kk].filenames);

	*offset = (double) soundevents[kk].offset + deltaoffset;

	*newbuffer = true;

	if(lastplayed >= 0)
	{
	    if (!strcmp(soundevents[kk].filenames,soundevents[lastplayed].filenames))
	        *newbuffer = false;
    }

	lastplayed = kk;

//	sprintf(buffers,"SON %d NEW %d OFFSET %f", kk, *newbuffer,*offset);
//	TRACE(buffers);

	return 1;
}
예제 #7
0
void AscentAP::Update (double simt)
{
	const double eps=1e-5;

	tgt.az = CalcTargetAzimuth();
	tgt.pitch = CalcTargetPitch();

	if (met_active)
		met = simt-t_launch;

	if (active) {
		if (vessel->status == 0) {
			if (met < 0.0) {
				vessel->SetEngineLevel (ENGINE_MAIN, min (1.0, (SRB_STABILISATION_TIME+met)*0.4));
			} else {
				if (vessel->status == 0) {
					t_launch = vessel->t0 = simt;
					vessel->pET->IgniteSRBs ();
					vessel->status = 1;
				}
			}
		}
		if (vessel->status < 3) {
			if (met_meco < 0.0) {
				double apalt;
				OBJHANDLE hRef = vessel->GetApDist(apalt);
				bool fuel_down = (vessel->pET ? vessel->pET->GetMainPropellantMass() < 10.0 : false);
				apalt -= oapiGetSize (hRef);
				if (apalt >= tgt_alt || fuel_down) {
					vessel->SetThrusterGroupLevel (THGROUP_MAIN, 0.0); // MECO
					met_meco = met;
				} else {
					vessel->SetThrusterGroupLevel (THGROUP_MAIN, 1.0);
					// make sure the user can't manually throttle down the SSME
				}
			} else if (met-met_meco >= 10.0) {
				vessel->SeparateTank();
				double apalt;
				OBJHANDLE hRef = vessel->GetApDist(apalt);
				apalt -= oapiGetSize (hRef);
				if (apalt + 1e3 < tgt_alt) {
					schedule_oms1 = met+20.0;
				}
			}
		} else if (schedule_oms1 > 0.0) {
			if (met >= schedule_oms1) {
				vessel->SetThrusterGroupLevel (THGROUP_MAIN, 1.0);
				schedule_oms1 = -1.0;
				met_oms1_start = met;
			}
		} else if (met_oms1_start > 0.0) {
			double apalt;
			OBJHANDLE hRef = vessel->GetApDist(apalt);
			apalt -= oapiGetSize (hRef);
			if (apalt >= tgt_alt) {
				vessel->SetThrusterGroupLevel (THGROUP_MAIN, 0.0); // OMS1 end
				met_oms1_start = -1.0;
			}
		} else {
			if (met_oms_start < 0.0) {
				OBJHANDLE hRef = vessel->GetSurfaceRef();
				ELEMENTS el;
				ORBITPARAM prm;
				vessel->GetElements(hRef, el, &prm);
				if (prm.ApT < 70.0) {
					vessel->SetThrusterGroupLevel (THGROUP_MAIN, 1.0); // OMS ignition
					met_oms_start = met;
				}
			} else if (met_oms_end < 0.0) {
				double perad, aprad, pealt, ecc;
				OBJHANDLE hRef = vessel->GetPeDist(perad);
				hRef = vessel->GetApDist(aprad);
				pealt = perad-oapiGetSize(hRef);
				ecc = (aprad-perad)/(aprad+perad);
				if (ecc < ecc_min) ecc_min = ecc;
				if (ecc > ecc_min+eps) {
					vessel->SetThrusterGroupLevel (THGROUP_MAIN, 0.0); // OMS cut off
					met_oms_end = met;
					active = false; // turn off ascent autopilot
				}
			}
		}
	}
}
예제 #8
0
파일: sat1ap.cpp 프로젝트: dseagrav/NASSP
void Saturn1b::AutoPilot(double autoT)
{
	TRACESETUP("Saturn1b::AutoPilot");

	const double GRAVITY=6.67259e-11;
	static int first_time=1;
	static int t = 0;
	static int out_level=0;
	double level=0.;
	double altitude;
	double pitch;
	double pitch_c;
	double heading;
	double bank;
	VECTOR3 rhoriz;

	double TO_HDG = agc.GetDesiredAzimuth();

	AltitudePREV = altitude = GetAltitude();
	VESSELSTATUS vsp;
	GetStatus(vsp);
	double totalRot=0;
	totalRot=vsp.vrot.x+vsp.vrot.y+vsp.vrot.z;
	if (fabs(totalRot) >= 0.0025){
		StopRot = true;
	}

	// This vector rotation will be used to tell if heads up (rhoriz.z<0) or heads down.
	HorizonRot(_V(1,0,0), rhoriz);

	//
	// Shut down the engines when we reach the desired
	// orbit.
	//

	double apogee, perigee;
	OBJHANDLE ref = GetGravityRef();
	GetApDist(apogee);
	GetPeDist(perigee);
	apogee = (apogee - oapiGetSize(ref)) / 1000.;
	perigee = (perigee - oapiGetSize(ref)) / 1000.;

	// We're aiming for periapsis and shutdown when apoapsis is reached at the opposite side of the orbit
	if (apogee >= agc.GetDesiredApogee() && perigee >= agc.GetDesiredPerigee() - 0.1) {
		// See Saturn::CheckForLaunchShutdown()
		if (GetThrusterLevel(th_main[0]) > 0){
			SetThrusterLevel(th_main[0], 0);			
			if (oapiGetTimeAcceleration() > 1.0)
				oapiSetTimeAcceleration(1.0);

			agc.LaunchShutdown();

			// Reset autopilot commands
			AtempP  = 0;
			AtempY  = 0;
			AtempR  = 0;			
		}
		return;
	}

	// navigation
	pitch = GetPitch();
	pitch = pitch*180./PI;
	//sprintf(oapiDebugString(), "Autopilot %f", altitude);
	// guidance
	pitch_c = GetCPitch(autoT);
	// control
	if (altitude > 4500) {
		// Damp roll motion
		bank = GetBank();
		bank = bank *180. / PI;
		if (bank > 90) bank = bank - 180.;
		else if (bank < -90) bank = bank + 180.;
		AtempR = -bank / 20.0;
		if (fabs(bank) < 0.3) AtempR = 0;

		// navigation
		pitch = GetPitch();
		pitch = pitch * 180. / PI;

		if (IGMEnabled) {
			VECTOR3 target;
			double pit, yaw;
			double bradius = oapiGetSize(ref);
			double bmass = oapiGetMass(ref);
			double mu = GRAVITY * bmass;
			// Aim for periapsis
			double altco = agc.GetDesiredPerigee() * 1000.;
			double velo = sqrt(mu / (bradius + altco));
			target.x = velo;
			target.y = 0.0;
			target.z = altco;
			LinearGuidance(target, pit, yaw);
			AtempP=(pit * DEG - pitch) / 30.0;
			if (AtempP < -0.15) AtempP = -0.15;
			if (AtempP >  0.15) AtempP =  0.15;
		}
		else {
			 // guidance
			pitch_c = GetCPitch(autoT);

			 // control
			double SatApo;
			GetApDist(SatApo);

			if ((SatApo >= ((agc.GetDesiredApogee() *.90) + ERADIUS)*1000) || MissionTime >= IGMStartTime)
				IGMEnabled = true;
		
			level = pitch_c - pitch;

			//sprintf(oapiDebugString(), "Autopilot Pitch Mode%f", elemSaturn1B.a );

			if (fabs(level)<10 && StopRot){	// above atmosphere, soft correction
				AtempP = 0.0;
				AtempR = 0.0;
				AtempY = 0.0;
				StopRot = false;
			}
			if (fabs(level)<0.05){	// above atmosphere, soft correction
				AtempP = 0.0;
				AtempR = 0.0;
				AtempY = 0.0;
			}
			else if (level>0 && fabs(vsp.vrot.z) < 0.09){
				AtempP = -(fabs(level) / 10.);
				if (AtempP < -1.0)AtempP = -1.0;
				if (rhoriz.z>0) AtempP = -AtempP;
			}
			else if (level<0 && fabs(vsp.vrot.z) < 0.09) {
				AtempP = (fabs(level) / 10.);
				if (AtempP > 1.0) AtempP = 1.0;
				if (rhoriz.z>0) AtempP = -AtempP;
			}
			else {
				AtempP = 0.0;
				AtempR = 0.0;
				AtempY = 0.0;
			}
			// sprintf(oapiDebugString(), "autoT %f AtempP %f AtempR %f AtempY %f altitude %f pitch %f pitch_c %f", 
			//  					       autoT, AtempP, AtempR, AtempY, altitude, pitch, pitch_c);
		}
	}
	// sprintf(oapiDebugString(), "Alt %f Pitch %f Roll %f Yaw %f autoT %f", altitude, AtempP, AtempR, AtempY, autoT);

	double slip;
	VECTOR3 az;
	VECTOR3 up, north, east, ygl, zgl, zerogl;
	OBJHANDLE hbody=GetGravityRef();
	double bradius=oapiGetSize(hbody);

	// set up our reference frame
	Local2Global(_V(0.0, 0.0, 0.0), zerogl);
	Local2Global(_V(0.0, 1.0, 0.0), ygl);
	Local2Global(_V(0.0, 0.0, 1.0), zgl);
	ygl=ygl-zerogl;
	zgl=zgl-zerogl;

	oapiGetHeading(GetHandle(),&heading);
	heading = heading*180./PI;

	// Inclination control
	static int incinit = 0; 
	static ELEMENTS elemlast; 
	static double incratelast;

	ELEMENTS elem;
	GetElements(ref, elem, 0, 0, FRAME_EQU);
	double incrate = (elem.i - elemlast.i) / oapiGetSimStep();
	double incraterate = (incrate - incratelast) / oapiGetSimStep();	
	double target = (agc.GetDesiredInclination() - elem.i * DEG) / (FirstStageShutdownTime - MissionTime);

	if (agc.GetDesiredInclination() != 0 && autoT > 45) {	
		if (incinit < 2) {
			incinit++;
			AtempY = 0;
		} else {
			if (autoT < FirstStageShutdownTime - 10) {	
				AtempY = (incrate * DEG - target) / 0.7 + incraterate * DEG / 2.;
				if (AtempY < -0.1) AtempY = -0.1;
				if (AtempY >  0.1) AtempY =  0.1;
			} else if (autoT < FirstStageShutdownTime + 10) {	
				AtempY = 0;
			} else {
				AtempY = (elem.i * DEG - agc.GetDesiredInclination()) / 7. + (incrate * DEG ) / 1.;
				if (AtempY < -0.01) AtempY = -0.01;
				if (AtempY >  0.01) AtempY =  0.01;
			}
		}
	}
	
	elemlast = elem;
	incratelast = incrate;

	// stage handling
	switch (stage){
		case LAUNCH_STAGE_ONE:
			GetRelativePos(hbody, up);
			up=Normalize(up);
			agc.EquToRel(PI/2.0, 0.0, bradius, north);
			north=Normalize(north);
			east=Normalize(CrossProduct(north, up));
			north=Normalize(CrossProduct(up, east));
			az=east*sin(TO_HDG*RAD)-north*cos(TO_HDG*RAD);
			if(autoT < 60.0) normal=Normalize(CrossProduct(up, az));

			slip=GetSlipAngle()*DEG;

			if(autoT < 10.) {
				AtempR=0.0;
				AtempY=0.0;
				// cancel out the yaw maneuver...
				AtempY=(-0.4+asin(zgl*normal)*DEG)/20.0;
			}

			if(autoT > 10.0 && autoT < 30.0) {
				// roll program
				AtempR=asin(ygl*normal)*DEG/20.0;
				AtempY=asin(zgl*normal)*DEG/20.0;
				if (AtempR < -0.25) AtempR = -0.25;
				if (AtempR >  0.25) AtempR =  0.25;
			}

			if(autoT > 30.0 && autoT < 45.0) {
				//pitch and adjust for relative wind
				AtempR=asin(ygl*normal)*DEG/20.0;
				//AtempY=(slip+asin(zgl*normal)*DEG)/20.0;
				AtempY=(TO_HDG-(heading+slip))/20.0;
				if (AtempR < -0.25) AtempR = -0.25;
				if (AtempR >  0.25) AtempR =  0.25;
			}
			pitch = GetPitch();
			pitch=pitch*180./PI;
			pitch_c=GetCPitch(autoT);
			AtempP = (pitch_c - pitch);

			// Fix for LC 39
			if (autoT < 10 && heading > 180)
				AtempP = -(180. - pitch_c - pitch);

			if (AtempP > 1.0) AtempP = 1.0;
			if (AtempP < -1.0) AtempP = -1.0;

			// zero angle-of-attack...
			if(autoT > 45.0 && autoT < 115.0) {

				/// \todo Disabled for now, the Saturn 1B doesn't seem to do that...
				//double aoa=GetAOA()*DEG;
				//pitch_c=pitch+aoa-0.3;

				AtempP=(pitch_c - pitch) / 5.0;
				if(AtempP < -0.2) AtempP = -0.2;
				if(AtempP >  0.2) AtempP = 0.2;
				// sprintf(oapiDebugString(), " pitch=%.3f pc=%.3f ap=%.3f", pitch, pitch_c, AtempP);
			}
			if (autoT > 115.0) {
				if (autoT < 120.0) {
					if (AtempP < -0.1) AtempP = -0.1;
					if (AtempP >  0.1) AtempP =  0.1;
				} else {
					if (AtempP < -0.2) AtempP = -0.2;
					if (AtempP >  0.2) AtempP =  0.2;
				}
				normal=Normalize(CrossProduct(Normalize(vsp.rpos), Normalize(vsp.rvel)));
			}
			// sprintf(oapiDebugString(), "roll=%.3f yaw=%.3f slip=%.3f sum=%.3f hdg+slip=%.3f hdg=%.3f ay=%.3f", 
			//     asin(ygl*normal)*DEG, asin(zgl*normal)*DEG, slip, slip+asin(zgl*normal)*DEG, heading+slip, heading, AtempY);
			// sprintf(oapiDebugString(), "autoT %f AtempP %f AtempR %f AtempY %f altitude %f pitch %f pitch_c %f rhoriz.z %f", 
			//     autoT, AtempP, AtempR, AtempY, altitude, pitch, pitch_c, rhoriz.z);
			/*
			char buffer[80];
			sprintf(buffer,"AtempP %f AtempR %f AtempY %f", AtempP, AtempR, AtempY);	
			TRACE(buffer);
			*/

			AttitudeLaunch1();
			break;

		case LAUNCH_STAGE_SIVB:
			AttitudeLaunchSIVB();
			break;
	}

	// sprintf(oapiDebugString(), "AP - inc %f rate %f target %f raterate %f AtempP %f AtempR %f AtempY %f", elem.i * DEG, incrate * DEG, target, incraterate * DEG, AtempP, AtempR, AtempY);
	// sprintf(oapiDebugString(), "AP - pitch %f pitch_c %f heading %f AtempP %f AtempR %f AtempY %f", pitch, pitch_c, heading, AtempP, AtempR, AtempY);
}
예제 #9
0
파일: Sync.cpp 프로젝트: ADSWNJ/BaseSyncMFD
// ==============================================================================================================================================
//
void SyncMFD::Update(HDC hDC)
{

	int pos;
	int ld=MFD->LineHeight;
	int fbp=MFD->FirstButton;
	char *Mnu = {"BaseSync v3.2\0"};

	int width=MFD->width;
	int height=MFD->height;

	double w=(double) width;
	double h=(double) height;

	int i;
	VECTOR3 Rot,Off,opos;
	double obli,trans,per,offset,diff,p,op,t,time,lon,lat;
	double trl,heading;
	char   name[32];
	static const int MAXSOLN = 160;
	double times[MAXSOLN],diffs[MAXSOLN],heads[MAXSOLN];
	int sol_found = 0;
	double max_diff=1000;
	bool   draw_text=false;

	Orbit LEO,ShipOrbit,Ecliptic;

	SetTextColor(hDC,white);
	SetTextAlign(hDC,TA_LEFT);
	TextOut(hDC,5,1,Mnu, (int)strlen(Mnu));

	if (mode.enc==0) Text(hDC,5,1+ld,"Latitude");
	if (mode.enc==1) Text(hDC,5,1+ld,"Closest passage");
	if (mode.enc==2) Text(hDC,5,1+ld,"Apoapsis");
	if (mode.enc==3) Text(hDC,5,1+ld,"Periapsis");

	if (sync_num<1) sync_num=1;
	if (sync_num>99) sync_num=99;


	OBJHANDLE ref = oapiGetObjectByName(trgt->ref);
	if (ref==NULL) {
    usingGS2 = false;
    trgt = &bstrgt;
    ref=ship->GetSurfaceRef();
  }

	if (ref==NULL) return;



	ShipOrbit.Elements(ship->GetHandle(), ref);
	Ecliptic.Ecliptic();

	oapiGetObjectName(ref, trgt->ref, 31);


  // Rotation elements

	obli   = oapiGetPlanetObliquity(ref);
	trans  = oapiGetPlanetTheta(ref);
	per    = oapiGetPlanetPeriod(ref);
	offset = oapiGetPlanetCurrentRotation(ref);

	// LEO
	LEO.LEO(ref);

  if (!usingGS2) {
    OBJHANDLE tgt = oapiGetBaseByName(ref, trgt->name);
    if (tgt) {
		  oapiGetObjectName(tgt,trgt->name,32);
		  oapiGetBaseEquPos(tgt,&trgt->lon, &trgt->lat);
	  }
	  else strcpy(trgt->name,"Surface");
  }


	double trle=ShipOrbit.TrlOfNode(&LEO);
	double EqI=angle(ShipOrbit.norv, LEO.norv);
	double ang=asin(sin(trgt->lat) / sin(EqI));

	double apos=limit(trle+ang);
	double bpos=limit(trle+PI-ang);

	double atime=ShipOrbit.TimeTo(apos);
	double btime=ShipOrbit.TimeTo(bpos);

	double dist=ShipOrbit.PeriapsisDistance();
	double apodist = dist=ShipOrbit.AopapsisDistance();
	if (dist<oapiGetSize(ref)) dist=oapiGetSize(ref);

	double zoom=w/(2.55 * (apodist + apodist + dist) / 3.0);
	double r=oapiGetSize(ref)*zoom;

	double x=w/2,y=h/2;
	double intpos=0;

	if (display_texts&2) {
		SelectObject(hDC,solid_pen_dgrey);
		DrawEllipse(hDC,x-r,y-r,x+r,y+r,w,h);

		if (mode.enc==0) {  // Latitude
			if ((sync_sel&1)==0) SelectObject(hDC,solid_pen_y), intpos=apos;
			else SelectObject(hDC,solid_pen_dgrey);
			r=ShipOrbit.Radius(apos)*zoom;
			DrawLine(hDC,x,y,x+r*cos(apos),x-r*sin(apos),w,h,false);

			if ((sync_sel&1)==1) SelectObject(hDC,solid_pen_y), intpos=bpos;
			else SelectObject(hDC,solid_pen_dgrey);
			r=ShipOrbit.Radius(bpos)*zoom;
			DrawLine(hDC,x,y,x+r*cos(bpos),x-r*sin(bpos),w,h,false);
		}

		if (mode.enc==1) { // Closest Passage
			r=ShipOrbit.Radius(sync_trl)*zoom;
			intpos=sync_trl;

			SelectObject(hDC,solid_pen_y);
			DrawLine(hDC,x,y,x+r*cos(sync_trl),x-r*sin(sync_trl),w,h,false);
		}

		if (mode.enc==2 || mode.enc==3) { // Aopapsis Periapsis
			SelectObject(hDC,solid_pen_y);
			r=ShipOrbit.Radius(sync_trl)*zoom;
			DrawLine(hDC,x,y,x+r*cos(sync_trl),x-r*sin(sync_trl),w,h,false);

			SelectObject(hDC,solid_pen_grey);
			r=ShipOrbit.Radius(sync_line)*zoom;
			DrawLine(hDC,x,y,x+r*cos(sync_line),x-r*sin(sync_line),w,h,false);
		}

		ShipOrbit.SetProjection(&ShipOrbit);
		ShipOrbit.GDIDraw(hDC,green,w,h,zoom,true,true);

		if (mode.deo) {
			r=ShipOrbit.Radius(deo.trlBurn)*zoom;
			SelectObject(hDC,solid_pen_white);
			DrawLine(hDC,x,y,x+r*cos(deo.trlBurn),x-r*sin(deo.trlBurn),w,h,false);
		}
	}



	if (EqI>=trgt->lat || mode.enc!=0) {
		// Usual case... target in range

		draw_text=true;
		SetTextColor(hDC,green);
		pos=(fbp+(ld*8));

		if (display_texts&1 && !mode.deo) {
			if (mode.enc==0) {
				Text(hDC,5,pos," #: Time:");
			Text(hDC,width/2,pos,"Lon Diff:"), pos+=ld;
			}	else {
				Text(hDC,5,pos," #: Time:");
			Text(hDC,5+width/3,pos,"  Dist:");
			Text(hDC,5+width*2/3,pos,"  Heading:"), pos+=ld;
			}
		}

		if (atime<btime && atime>0) sync_sel=0;
		else if (btime>0) sync_sel=1;
		else sync_sel=0;

		for (i=0;i<MAXSOLN;i++) {
			times[i]=0;
			diffs[i]=0;
		}
		sync_min = -1;


		if (ShipOrbit.ecc<1 && mode.enc==0) {
			for (i=0;i<MAXSOLN;i++) {

				if (i&1) time=MAX(atime, btime);
				else     time=MIN(atime, btime);

				if (time==atime) op=apos;
				else             op=bpos;

				p=(double)i;
				if (i>1) time+=ShipOrbit.Period()*floor(p/2);

				t = time/86400;
				PlanetAxis(obli,trans,offset,per,t,&Rot,&Off);

				opos=ShipOrbit.Position(op);

				LonLat(opos,Rot,Off,&lon,&lat);
				diff=lon-trgt->lon;

				if (fabs(diff)<max_diff) {
					max_diff=fabs(diff), sync_time=time, sync_trl=op;
					sync_min=i;
				}

				times[i]=time;
				diffs[i]=diff;
				if (time > 0.0 && diff >0.0) {
					sol_found++;
					if (sol_found==sync_num) break;
				}
			}
		}


		max_diff=1e10;

		if (ShipOrbit.ecc<1 && mode.enc==1) {

			double posit=ShipOrbit.trl;

			for (i=0;i<MAXSOLN;i++) {

				InterpolateClosestPassage(ref,&ShipOrbit,trgt->lon,trgt->lat,posit,&diff,&time,&heading,&trl);

				posit=trl+PI05;

				if (diff<max_diff && diff>0) {
					max_diff=diff, sync_time=time, sync_trl=limit(trl);
					sync_min=i;
				}

				heads[i]=heading;
				times[i]=time;
				diffs[i]=diff;
				if (time > 0.0 && diff >0.0) {
					sol_found++;
					if (sol_found==sync_num) break;
				}
			}
		}

		if (ShipOrbit.ecc<1 && (mode.enc==2 || mode.enc==3)) {
			for (i=0;i<MAXSOLN;i++) {

				if (mode.enc==2) sync_line=limit(ShipOrbit.lpe+PI);
				else sync_line=limit(ShipOrbit.lpe);

				time=ShipOrbit.TimeTo(sync_line) + ShipOrbit.Period() * (double)i;
				t = time / 86400.0;

				PlanetAxis(obli,trans,offset,per,t,&Rot,&Off);

				VECTOR3 gpv=VectorByLonLat(Rot,Off,trgt->lon,trgt->lat);
							VECTOR3 pos=ShipOrbit.Position(sync_line);

				diff = angle(gpv,pos);
				heading = nangle(pos-gpv,Rot,gpv);

				ShipOrbit.Longitude(gpv,NULL,NULL,&trl);

				if (diff<max_diff && diff>0) {
					max_diff=diff, sync_time=time, sync_trl=limit(trl);
					sync_min=i;
				}

				if (time==0) time=0.1;
				heads[i]=heading;
				times[i]=time;
				diffs[i]=diff;
				if (time > 0.0 && diff >0.0) {
					sol_found++;
					if (sol_found==sync_num) break;
				}
			}
		}

		// Hyperbolic Orbit
		if (ShipOrbit.ecc>=1 && (atime>0 || btime>0)) {
			mode.enc=0;
			for (i=0;i<2;i++) {

				if (atime>0 && btime>0) {
					if (i&1) time=MAX(atime, btime);
					else     time=MIN(atime, btime);
				}
				else time=(atime>0 ? atime : btime);

				op = (time==atime? apos : bpos);

				p=(double)i;
				if (i>1) time+=ShipOrbit.Period()*floor(p/2);
				t = time/86400;
				PlanetAxis(obli,trans,offset,per,t,&Rot,&Off);

				opos=ShipOrbit.Position(op);
				LonLat(opos,Rot,Off,&lon,&lat);
				diff=lon-trgt->lon;

				if (fabs(diff)<max_diff) {
					max_diff=fabs(diff), sync_time=time, sync_trl=op;
					sync_min=i;
				}

				times[i]=time;
				diffs[i]=diff;

				if (atime<0 || btime<0) break;
			}
		}

		if (sync_min > -1) {
			sol.num = sync_min+1;
			sol.tSol = times[sync_min];
			sol.dist = diffs[sync_min];
			sol.hdg = heads[sync_min];
			sol.dataValid = true;
		} else {
			sol.dataValid = false;
		}

		double rad=oapiGetSize(ref);
		int no=0;
		int disp_i = 1;

		if (display_texts&1 && !mode.deo) {
			for (i=0;i<MAXSOLN;i++) {
				if (i==sync_min) SetTextColor(hDC,lyellow);
				else SetTextColor(hDC,lgreen);
				if (times[i]>0.0 && diffs[i]>=0.0) {
					if (disp_i >= sync_dispmin && disp_i <= sync_dispmin+7) {
						if (mode.enc==0) {
							sprintf(name,"%2d: ",disp_i);
							Text(hDC,5,pos,name,times[i]);
							TextA(hDC,width/2,pos,"",diffs[i]*DEG), pos+=ld;
						}	else { // enc_mode 1,2,3
							no++;
							sprintf(name,"%2d: ",disp_i);
							Text(hDC,5,pos,name,times[i]);
							Text(hDC,5+width/3,pos,"  ",diffs[i]*rad);
							TextA(hDC,5+width*2/3,pos,"  ",heads[i]*DEG), pos+=ld;
						}
					}
					disp_i++;
					if (disp_i > sync_num) break;
				}
			}
		}
		sync_sel+=sync_min;
	}	else {
		// We are in Latitude mode, and the target is outside of our inclination - i.e. no solution

		sol.dataValid = false;
		SetTextAlign(hDC,TA_CENTER);
		SetTextColor(hDC,yellow);
		pos=(fbp+(ld*8));
		Text(hDC,width/2,pos,"Target Out of Range"); pos+=ld;
	}



	if (mode.dir) {
		// Calculate burn for plane change correction in DIRECT mode

		if (EqI<trgt->lat && mode.enc==0) {
			double trl;
			PlanetAxis(obli,trans,offset,per,0,&Rot,&Off);
			VECTOR3 gpv = VectorByLonLat(Rot,Off,trgt->lon,trgt->lat);
			ShipOrbit.Longitude(gpv,NULL,NULL,&trl);
			sync_time=ShipOrbit.TimeTo(trl);
			sync_trl=trl;
		}

		time_to_int=sync_time;
		double trl=sync_trl;

		PlanetAxis(obli,trans,offset,per,time_to_int/86400.0,&Rot,&Off);
		VECTOR3 pos = ShipOrbit.Position(trl);
		VECTOR3 gpv = VectorByLonLat(Rot,Off,trgt->lon,trgt->lat);
		VECTOR3 lan = crossp(gpv, pos);
		VECTOR3 nor = ShipOrbit.norv;

		ShipOrbit.Longitude(lan,NULL,NULL,&sol.trlBurn);

		sol.rIn = fabs(asin(dotp(gpv,nor)));

		double a = ShipOrbit.TimeToPoint(sol.trlBurn);
		double b = ShipOrbit.TimeToPoint(limit(sol.trlBurn+PI));

		if (fabs(a)<fabs(b)) sol.tToBurn =a, sol.nmlBurn=true;
		else				 sol.tToBurn =b, sol.nmlBurn=false;

		sol.dV = sol.rIn*ShipOrbit.ang/ShipOrbit.Radius(sol.trlBurn);
		sol.tBurn =BurnTimeBydV(sol.dV,ship);

	}	else {
		// Calculate burn for plane change correction in EQUATORIAL mode

		sol.trlBurn=ShipOrbit.TrlOfNode(&LEO);
		sol.rIn=MAX(trgt->lat-EqI,0);

		double a=ShipOrbit.TimeToPoint(sol.trlBurn);
		double b=ShipOrbit.TimeToPoint(limit(sol.trlBurn+PI));

		if (fabs(a)<fabs(b)) sol.tToBurn =a, sol.nmlBurn=true;
		else				 sol.tToBurn =b, sol.nmlBurn=false;

		sol.dV = sol.rIn*ShipOrbit.ang/ShipOrbit.Radius(sol.trlBurn);
		sol.tBurn =BurnTimeBydV(sol.dV,ship);
	}

	if (display_texts&2) {
		ShipOrbit.DrawPlaneIntersection(hDC,sol.trlBurn,w/2,h/2,zoom,grey);
	}

	SetTextAlign(hDC,TA_LEFT);
	pos=1;
	SetTextColor(hDC,grey);
  if (usingGS2) {
	  Text(hDC,width*1/2,pos,"Linked ","to GS"); pos+=ld;

    // Check if GS2 changed target
    if (bstrgt.lat != gs2trgt->lat || bstrgt.lon != gs2trgt->lon) {
        strcpy(bstrgt.ref, gs2trgt->ref);
        strcpy(bstrgt.name, gs2trgt->name);
        bstrgt.lat = gs2trgt->lat;
        bstrgt.lon = gs2trgt->lon;
        bstrgt.alt = gs2trgt->alt;
        bstrgt.ang = gs2trgt->ang;
        bstrgt.ant = gs2trgt->ant;
    }

  } else {
	  Text(hDC,width*1/2,pos,"Ref ",trgt->ref); pos+=ld;
  }
  Text(hDC,width*1/2,pos,"Tgt ",trgt->name); pos+=ld;

	if (display_texts&1) {
		if (!mode.deo) {
			// Display the burn solution for plane change

			deo.dataValid = false;
			pos=fbp;
			SetTextColor(hDC,lgreen);

			if (trgt->lat<0) TextA(hDC,5,pos,"Lat ",fabs(DEG*trgt->lat),"S"), pos+=ld;
			else TextA(hDC,5,pos,"Lat ",DEG*trgt->lat,"N"), pos+=ld;

			if (trgt->lon<0) TextA(hDC,5,pos,"Lon ",fabs(DEG*trgt->lon),"W"), pos+=ld;
			else TextA(hDC,5,pos,"Lon ",DEG*trgt->lon,"E"), pos+=ld;

			TextA(hDC,5,pos,"EqI ",EqI*DEG), pos+=ld;


			PlanetAxis(ref,0,&Rot,&Off);
			VECTOR3 base_pos = VectorByLonLat(Rot,Off,trgt->lon,trgt->lat);

			VECTOR3 ship_pos; ship->GetRelativePos(ref,ship_pos);
			VECTOR3 ship_vel; ship->GetRelativeVel(ref,ship_vel);

			VECTOR3 gsp=GroundSpeedVector(ref,ship);
			VECTOR3 zero=crossp(ship_pos,ship_vel);
			double head = nangle(gsp,zero,ship_pos);

      double baseHeight = oapiGetSize(ref);
#if ORBITER_VERSION == 2016
      ELEVHANDLE eh = oapiElevationManager(ref);
      if (eh) {
        baseHeight += oapiSurfaceElevation(ref, trgt->lon, trgt->lat);
      }
#endif


			TextA(hDC,5,pos,"Hed ",head*DEG), pos+=ld;
			Text(hDC,5,pos, "GSp ",length(gsp)), pos+=ld;
			Text(hDC,5,pos, "Dst ",angle(ship_pos,base_pos)*baseHeight), pos+=ld;
			if (sol.dataValid) {
        double altitude = length(ShipOrbit.Position(intpos));
        altitude -= baseHeight;
				Text(hDC,5,pos, "Alt ",altitude);
      }
			pos+=ld;


			pos=fbp;
			int xx=width/2;
			SetTextColor(hDC,green);
			if (mode.dir) Text(hDC,xx,pos,"Direct:"), pos+=ld;
			else Text(hDC,xx,pos,"Equator:"), pos+=ld;

			SetTextColor(hDC,lgreen);
			TextA(hDC,xx,pos,"RIn ",sol.rIn*DEG), pos+=ld;
			TextA(hDC,xx,pos,"LAN ",sol.trlBurn*DEG), pos+=ld;
			Text(hDC,xx,pos," Tn ",sol.tToBurn), pos+=ld;
      if (abs(sol.tBurn) > 0.1) {
  			if (sol.nmlBurn) Text(hDC,xx,pos,"PlC ",sol.tBurn,"s (+)"), pos+=ld;
	  		else Text(hDC,xx,pos,"PlC ",sol.tBurn,"s (-)"), pos+=ld;
		  	Text(hDC,xx,pos," dV ",sol.dV,"m/s"), pos+=ld;
      } else {
        Text(hDC,xx,pos,"PlC 0.000s"), pos+=ld;
		  	Text(hDC,xx,pos," dV 0.000m/s"), pos+=ld;
      }

      if (sol.dataValid) {
        burn.dV = sol.dV;
        burn.orientation = sol.nmlBurn ? 1 : -1;
        burn.tToInstBurn = sol.tToBurn;
        burn.dataValid = true;
      } else {
        burn.dataValid = false;
      }

		}	else {
			// Display the deorbit parameters and burn solution

			pos=fbp;
			SetTextColor(hDC,white);
			Text(hDC,5,pos,"De-Orbit Program"), pos+=2*ld;

			SetTextColor(hDC,lgreen);
      if (usingGS2) {
			  Text(hDC,5,pos, "Ang, Ant, Alt from GS"), pos+=ld;
        TextA(hDC,5,pos,"GS Ang ",trgt->ang*DEG), pos+=ld;
			  TextA(hDC,5,pos,"GS Ant ",trgt->ant*DEG), pos+=ld;
			  Text(hDC,5,pos, "GS Alt ",trgt->alt,"km"), pos+=2*ld;
      } else {
        TextA(hDC,5,pos,"Ang ",bstrgt.ang*DEG), pos+=ld;
			  TextA(hDC,5,pos,"Ant ",bstrgt.ant*DEG), pos+=ld;
			  Text(hDC,5,pos, "Alt ",bstrgt.alt,"km"), pos+=2*ld;
      }
			double pre;
			ComputeDeOrbit(ShipOrbit.myy,ShipOrbit.rad,trgt->alt*1000+oapiGetSize(ref),trgt->ang,&pre,&deo.dV);
			deo.dV=ShipOrbit.vel-deo.dV;

			deo.trlBurn = limit(sync_trl - trgt->ant - pre);
			deo.tInstBurn = ShipOrbit.TimeToPoint(deo.trlBurn);
			deo.tBurn = BurnTimeBydV(deo.dV,ship);
			deo.tToBurn = deo.tInstBurn - 0.5 * deo.tBurn;

			Text(hDC,5,pos, "TBn ",deo.tToBurn,"s"), pos+=ld;
			TextA(hDC,5,pos,"TrL ",deo.trlBurn*DEG), pos+=ld;
			Text(hDC,5,pos, " dV ",deo.dV,"m/s"), pos+=ld;
			Text(hDC,5,pos, " BT ",deo.tBurn,"s"), pos+=2*ld;

			if (ShipOrbit.ecc>0.015) {
				SetTextColor(hDC,lgreen);
				Text(hDC,5,pos,"De-orbit burn data only accurate"); pos+=ld;
				Text(hDC,5,pos,"if your Ecc is 0.015 or less."); pos+=ld;
				Text(hDC,5,pos,"(If burn complete, please ignore.)"); pos+=ld;pos+=ld;
			}
			if (!mode.dir) {
				SetTextColor(hDC,lgreen);
				Text(hDC,5,pos,"Use \"Direct\" mode to re-synchronize"); pos+=ld;
				Text(hDC,5,pos,"the approach after the de-orbit");
			}
			deo.dataValid = true;
      burn.dV = -deo.dV;
      burn.orientation = 0;
      burn.tToInstBurn = deo.tToBurn;
      burn.dataValid = true;
		}
	}

	if (!MMPut_done) {
    mma.PutMMStruct("BaseSyncTarget", &bstrgt);
    mma.PutMMStruct("BaseSyncMode", &mode);
    mma.PutMMStruct("BaseSyncSolution", &sol);
    mma.PutMMStruct("BaseSyncDeorbit", &deo);
    mma.PutMMStruct("BaseSyncBurn", &burn); // To be removed after BTC synchronization
		if (burn.dataValid)
        {
            mma.Put("dv", burn.dV);
            mma.Put("InstantaneousBurnTime", burn.tToInstBurn);
            mma.Put("Orientation", burn.orientation);
        }
        else
        {
          mma.Delete("dv");
          mma.Delete("InstantaneousBurnTime");
          mma.Delete("Orientation");
        }
		MMPut_done = true;
	}
}
예제 #10
0
파일: Sync.cpp 프로젝트: ADSWNJ/BaseSyncMFD
// ==============================================================================================================================================
//
bool SyncMFD::InterpolateClosestPassage(OBJHANDLE ref, Orbit *src, double lon,double lat,double orbit,double *diff,double *tim,double *head,double *tr)
{
	VECTOR3 Rot,Off;
	Orbit LEO;
	int i;
	double obli = 0.0;
	double trans = 0.0;
	double per = 0.0;
	double offset = 0.0;
	double size = 0.0;
	double time = 0.0;

	// Rotation elements
	obli   = oapiGetPlanetObliquity(ref);
	trans  = oapiGetPlanetTheta(ref);
	per    = oapiGetPlanetPeriod(ref);
	offset = oapiGetPlanetCurrentRotation(ref);
	size   = oapiGetSize(ref);

	LEO.LEO(ref);

	double step  = PI2/16.01;
	double start = orbit;
	double end   = orbit+PI2+(step/2.0);
	double trl, dst2;
	double dot,old_dot=0;


	VECTOR3 pos = _V(0,0,0);
	VECTOR3 vel = _V(0,0,0);
	VECTOR3 gpv = _V(0,0,0);
	VECTOR3 spd = _V(0,0,0);
	VECTOR3 relv = _V(0,0,0);
	VECTOR3 relp = _V(0,0,0);

	bool no_intercept=true;
	bool first=true;

	for (trl=start;trl<end;trl+=step) {

		// Caluclate time to a location
		time=Trl2Time(trl,src)/86400.0;

		// Caluclate Planets Rotation offset at that time
		PlanetAxis(obli,trans,offset,per,time,&Rot,&Off);

		// Calculate Ship's position vector and distance^2
		pos  = src->Position(trl);
		dst2 = dotp(pos,pos);

		// Reset the distance to correspond a surface position.
		pos  = set_length(pos,size);

		// Compute ship's surface velocity vector direction
		vel = crossp(src->norv,pos);

		// Compute ship's surface velocity vector magnitude
		vel = set_length(vel,src->ang*size/dst2);

		// Compute base's position vector
		gpv = VectorByLonLat(Rot,Off,lon,lat)*size;

		// Compute speed of the base
		spd = GroundSpeedVector(ref,time,lon,lat);

		// Copmute relative position to the base
		relv = (vel-spd);
		relp = (pos-gpv);

		dot = dotp(relv,relp);

		if (old_dot<0 && dot>0 && !first) {
			if (angle(pos,gpv)<PI05) {
				start=trl-step;
				no_intercept=false;
				break;
			}
		}

		first=false;
		old_dot=dot;
	}


    // Return N/A if no passage points found
	if (no_intercept) {
		if (head) *head = 0;
		if (diff) *diff = 0;
		if (tim)  *tim  = 0;
		if (tr)   *tr   = start+PI2-PI05;
		return false;
	}


	trl=start;
	old_dot=-1; // We are approaching the base
	double trll=0;
	no_intercept=true;

	for (i=0;i<24;i++) {

		step/=2.0;

		// Caluclate time to a location
		time=Trl2Time(trl,src)/86400.0;

		// Caluclate Planets Rotation offset at that time
		PlanetAxis(obli,trans,offset,per,time,&Rot,&Off);

		// Calculate Ship's position vector and distance^2
		pos  = src->Position(trl);
		dst2 = dotp(pos,pos);

		// Reset the distance to correspond a surface position.
		pos  = set_length(pos,size);

		// Compute ship's surface velocity vector direction
		vel = crossp(src->norv,pos);

		// Compute ship's surface velocity vector magnitude
		vel = set_length(vel,src->ang*size/dst2);

		// Compute base's position vector
		gpv = VectorByLonLat(Rot,Off,lon,lat)*size;

		// Compute speed of the base
		spd = GroundSpeedVector(ref,time,lon,lat);

		// Copmute relative position to the base
		relv = (vel-spd);
		relp = (pos-gpv);

		dot = dotp(relv,relp);

		trll=trl;
		if (dot==0) {
			 no_intercept=false;
			 break; // closest position archived, break
		}

		if (dot>0) { // base is behind of us, step back
			trl=trl-step;
			no_intercept=false;  // Verify, We have a base passage.
		}
		else trl=trl+step;	// base is front of us, cuntinue stepping
	}


	double dif=angle(pos, gpv);

	VECTOR3 zero=crossp(pos,spd);

	if (head) *head = nangle(relp,zero,pos);
	if (diff) *diff = dif;
	if (tim)  *tim  = time*86400.0;
	if (tr)   *tr   = trll;

	return true;
}
예제 #11
0
void DockingProbe::TimeStep(double simt, double simdt)

{
	if (!FirstTimeStepDone) {
		DoFirstTimeStep();
		FirstTimeStepDone = true;
		return;
	}

	if (UndockNextTimestep) {
		UpdatePort(Dockparam[1] * 0.5, simdt);
		OurVessel->Undock(ourPort);
		UndockNextTimestep = false;
	}

	if (ExtendingRetracting > 0) {
		if (Status >= DOCKINGPROBE_STATUS_EXTENDED) {
			Status = DOCKINGPROBE_STATUS_EXTENDED;
			ExtendingRetracting = 0;
			Dockproc = DOCKINGPROBE_PROC_UNDOCKED;
			OurVessel->Undocking(ourPort);
			OurVessel->SetDockingProbeMesh();
		} else {
			Status += 0.33 * simdt;
		}
	} else if (ExtendingRetracting < 0) {
		if (Status <= DOCKINGPROBE_STATUS_RETRACTED) {
			Status = DOCKINGPROBE_STATUS_RETRACTED;
			ExtendingRetracting = 0;
			OurVessel->HaveHardDocked(ourPort);		
			OurVessel->SetDockingProbeMesh();
		} else {
			Status -= 0.33 * simdt;
		}	
	}

	if (Dockproc == DOCKINGPROBE_PROC_SOFTDOCKED) {
		UpdatePort(Dockparam[1] * 0.5, simdt);
		Dockproc = DOCKINGPROBE_PROC_HARDDOCKED;
	} else if (Dockproc == DOCKINGPROBE_PROC_HARDDOCKED) {
		if (Status > DOCKINGPROBE_STATUS_RETRACTED) {
			UpdatePort(Dockparam[1] * 0.5 * Status / 0.9, simdt);
		} else {
			UpdatePort(_V(0,0,0), simdt);
			Dockproc = DOCKINGPROBE_PROC_UNDOCKED;
		}
	}
	// sprintf(oapiDebugString(), "Docked %d Status %.3f Dockproc %d  ExtendingRetracting %d", (Docked ? 1 : 0), Status, Dockproc, ExtendingRetracting); 

	// Switching logic
	if (OurVessel->DockingProbeExtdRelSwitch.IsUp() && IsPowered()) {
		Extend();

	} else if (OurVessel->DockingProbeExtdRelSwitch.IsDown()) {
		if ((!OurVessel->DockingProbeRetractPrimSwitch.IsCenter() && OurVessel->DockProbeMnACircuitBraker.IsPowered() && OurVessel->PyroBusA.Voltage() > SP_MIN_DCVOLTAGE) ||
			(!OurVessel->DockingProbeRetractSecSwitch.IsCenter()  && OurVessel->DockProbeMnBCircuitBraker.IsPowered() && OurVessel->PyroBusB.Voltage() > SP_MIN_DCVOLTAGE)) {

			int ActiveCharges = 0;

			if (OurVessel->DockingProbeRetractPrimSwitch.IsUp()) ActiveCharges = ActiveCharges | DOCKINGPROBE_CHARGE_PRIM1;
			if (OurVessel->DockingProbeRetractPrimSwitch.IsDown()) ActiveCharges = ActiveCharges | DOCKINGPROBE_CHARGE_PRIM2;
			if (OurVessel->DockingProbeRetractSecSwitch.IsUp()) ActiveCharges = ActiveCharges | DOCKINGPROBE_CHARGE_SEC1;
			if (OurVessel->DockingProbeRetractSecSwitch.IsDown()) ActiveCharges = ActiveCharges | DOCKINGPROBE_CHARGE_SEC2;

			if ((ActiveCharges & RetractChargesUsed)!= ActiveCharges) Retract();

			RetractChargesUsed = RetractChargesUsed | ActiveCharges;

			// sprintf(oapiDebugString(), "Charge Used: P1%d P2%d S1%d S2%d", RetractChargesUsed & DOCKINGPROBE_CHARGE_PRIM1 , RetractChargesUsed & DOCKINGPROBE_CHARGE_PRIM2 , RetractChargesUsed & DOCKINGPROBE_CHARGE_SEC1 , RetractChargesUsed & DOCKINGPROBE_CHARGE_SEC2); 
		}
	}

	///
	/// Begin Advanced Docking Code
	///
	if (DockingMethod > ADVANCED){
		// Code that follows is largely lifted from Atlantis...
		// Goal is to handle close proximity docking between a probe and drogue

		VECTOR3 gdrgPos, gdrgDir, gprbPos, gprbDir, gvslPos, rvel, pos, dir, rot;
		OurVessel->Local2Global (Dockparam[0],gprbPos);  //converts probe location to global
		OurVessel->GlobalRot (Dockparam[1],gprbDir);     //rotates probe direction to global

		// Search the complete vessel list for a grappling candidate.
		// Not very scalable ...
		for (DWORD i = 0; i < oapiGetVesselCount(); i++) {
			OBJHANDLE hV = oapiGetVesselByIndex (i);
			if (hV == OurVessel->GetHandle()) continue; // we don't want to grapple ourselves ...
			oapiGetGlobalPos (hV, &gvslPos);
			if (dist (gvslPos, gprbPos) < oapiGetSize (hV)) { // in range
				VESSEL *v = oapiGetVesselInterface (hV);
				DWORD nAttach = v->AttachmentCount (true);
				for (DWORD j = 0; j < nAttach; j++) { // now scan all attachment points of the candidate
					ATTACHMENTHANDLE hAtt = v->GetAttachmentHandle (true, j);
					const char *id = v->GetAttachmentId (hAtt);
					if (strncmp (id, "PADROGUE", 8)) continue; // attachment point not compatible
					v->GetAttachmentParams (hAtt, pos, dir, rot);
					v->Local2Global (pos, gdrgPos);  // converts found drogue position to global
					v->GlobalRot (dir, gdrgDir);     // rotates found drogue direction to global
					if (dist (gdrgPos, gprbPos) < COLLISION_DETECT_RANGE && DockingMethod == ADVANCEDPHYSICS) { // found one less than a meter away!
						//  Detect if collision has happend, if so, t will return intersection point along the probe line X(t) = gprbPos + t * gprbDir
						double t = CollisionDetection(gprbPos, gprbDir, gdrgPos, gdrgDir);	
						//  Calculate time of penetration according to current velocity
						OurVessel->GetRelativeVel(hV, rvel);
						//  Determine resultant force

						//APPLY rforce to DockingProbe Vessel, and APPLY -rforce to Drogue Vessel
						return;
					} 
					if (dist(gdrgPos, gprbPos) < CAPTURE_DETECT_RANGE && DockingMethod > ADVANCED) {
						// If we're within capture range, set docking port to attachment so docking can take place
						// Originally, I would have used the Attachment features to soft dock and move the LM during retract
						// but Artlav's docking method does this better and uses the docking port itself.
						// Attachment is being used as a placeholder for the docking port and to identify its orientation.
						OurVessel->GetAttachmentParams(hattPROBE, pos, dir, rot);
						DOCKHANDLE dock = OurVessel->GetDockHandle(ourPort);
						OurVessel->SetDockParams(dock, pos, dir, rot);
					}
				}//for nAttach
			}//if inRange
		}//for nVessel
	}
}
예제 #12
0
bool InstrHSI::Redraw2D (SURFHANDLE surf)
{
	const float horzx = (float)(texw-312), horzy = (float)(texh-252);
	DWORD tp;
	int i, j, vofs;
	double c, sinc, cosc, brg, slope;
	double yaw = vessel->GetYaw();   if (yaw < 0.0) yaw += PI2;
	double siny = sin(yaw), cosy = cos(yaw);

	dev = 0.0;
	NAVHANDLE nv = vessel->GetNavSource (0);
	if (nv) {
		tp = oapiGetNavType(nv);
		if (tp != TRANSMITTER_VOR && tp != TRANSMITTER_ILS)
			nv = NULL;
	}
	if (nv != nav) {
		if (nav = nv) {
			navRef = vessel->GetSurfaceRef();
			navType = tp;
			if (navRef) {
				VECTOR3 npos;
				NAVDATA data;
				double rad;
				oapiGetNavPos (nav, &npos);
				oapiGlobalToEqu (navRef, npos, &navlng, &navlat, &rad);
				oapiGetNavData (nav, &data);
				if (navType == TRANSMITTER_ILS) crs = data.ils.appdir;
			} else nav = NULL;
		} else {
			navType = TRANSMITTER_NONE;
		}
		// transform glideslope background
		static float gs_tv[4] = {(horzy+171.5f)/(float)texh,(horzy+154.5f)/(float)texh,(horzy+171.5f)/(float)texh,(horzy+154.5f)/(float)texh};
		vofs = vtxofs+4;
		for (i = 0; i < 4; i++)
			grp->Vtx[vofs+i].tv = (navType == TRANSMITTER_ILS ? gs_tv[i] : (horzy+154.5f)/(float)texh);
		// transform glideslope indicator
		if (navType != TRANSMITTER_ILS) {
			vofs = vtxofs+8;
			for (i = 0; i < 4; i++) grp->Vtx[vofs+i].y = ycnt-64;
		}
	}
	if (nav) {
		double vlng, vlat, vrad, adist;
		OBJHANDLE hRef = vessel->GetEquPos (vlng, vlat, vrad);
		if (hRef && hRef == navRef) {
			Orthodome (vlng, vlat, navlng, navlat, adist, brg);
			adist *= oapiGetSize (hRef);
			dev = brg-crs;
			if      (dev < -PI) dev += PI2;
			else if (dev >= PI) dev -= PI2;
			if      (dev < -PI05) dev = -PI-dev;
			else if (dev >= PI05) dev =  PI-dev;

			// calculate slope
			if (navType == TRANSMITTER_ILS) {
				double s = adist * cos(crs-brg);
				double alt = vessel->GetAltitude();
				slope = atan2 (alt, s) * DEG;

				// transform glideslope indicator
				const double tgtslope = 4.0;
				double dslope = slope - tgtslope;
				float yshift = (float)min(fabs(dslope)*20.0,45.0);
				if (dslope < 0.0) yshift = -yshift;
				static float gs_y[4] = {ycnt-4.0f, ycnt-4.0f, ycnt+4.0f, ycnt+4.0f};
				vofs = vtxofs+8;
				for (i = 0; i < 4; i++)
					grp->Vtx[vofs+i].y = gs_y[i]+yshift;
			}
		}
	}

	static double xp[4] = {-60.5,60.5,-60.5,60.5};
	static double yp[4] = {-60.5,-60.5,60.5,60.5};

	// transform compass rose
	vofs = vtxofs;
	for (i = 0; i < 4; i++) {
		grp->Vtx[i+vofs].x = (float)(cosy*xp[i] + siny*yp[i] + xcnt);
		grp->Vtx[i+vofs].y = (float)(-siny*xp[i] + cosy*yp[i] + ycnt);
	}
	// transform source bearing indicator
	vofs = vtxofs+12;
	if (nav) {
		c = yaw-brg;
		sinc = sin(c), cosc = cos(c);
		static double xs[4] = {-6.2,6.2,-6.2,6.2};
		static double ys[4] = {-61,-61,-45,-45};
		for (i = 0; i < 4; i++) {
			grp->Vtx[i+vofs].x = (float)(cosc*xs[i] + sinc*ys[i] + xcnt);
			grp->Vtx[i+vofs].y = (float)(-sinc*xs[i] + cosc*ys[i] + ycnt);
		}
	} else { // hide indicator
		for (i = 0; i < 4; i++) {
			grp->Vtx[i+vofs].x = (float)(xcnt-65.0);
			grp->Vtx[i+vofs].y = (float)ycnt;
		}
	}
	// transform course indicator + scale
	c = yaw-crs;
	sinc = sin(c), cosc = cos(c);
	static double xc[8] = {-32.2,32.2,-32.2,32.2, -6.2, 6.2, -6.2, 6.2};
	static double yc[8] = { -4.7, -4.7, 4.7, 4.7,-60.5,-60.5,60.5,60.5};
	vofs = vtxofs+16;
	for (i = 0; i < 8; i++) {
		grp->Vtx[i+vofs].x = (float)(cosc*xc[i] + sinc*yc[i] + xcnt);
		grp->Vtx[i+vofs].y = (float)(-sinc*xc[i] + cosc*yc[i] + ycnt);
	}
	// transform deviation indicator
	static double xd[4] = {-3.65,3.65,-3.65,3.65};
	static double yd[4] = {-26.82,-26.82,26.82,26.82};
	double dx = min(8.0,fabs(dev)*DEG)*5.175;
	if (dev < 0.0) dx = -dx;
	vofs = vtxofs+24;
	for (i = 0; i < 4; i++) {
		grp->Vtx[i+vofs].x = (float)(cosc*(xd[i]+dx) + sinc*yd[i] + xcnt);
		grp->Vtx[i+vofs].y = (float)(-sinc*(xd[i]+dx) + cosc*yd[i] + ycnt);
	}

	// course readout
	int icrs = (int)(crs*DEG+0.5) % 360;
	char *cc, cbuf[16];
	sprintf (cbuf, "%03d", icrs);
	vofs = vtxofs+32;
	static double numw = 10.0, num_ofs = horzx+1.0;
	static double tu_num[4] = {0,numw/texw,0,numw/texw};
	for (cc = cbuf, i = 0; i < 3; cc++, i++) {
		double x = ((*cc-'0') * numw + num_ofs)/texw;
		for (j = 0; j < 4; j++)
			grp->Vtx[i*4+j+vofs].tu = (float)(tu_num[j]+x);
	}

	return false;
}
예제 #13
0
파일: attref.cpp 프로젝트: Artoria2e5/obtr
void AttitudeReference::PostStep (double simt, double simdt, double mjd)
{
	valid_axes = false;
	valid_euler = false;
	valid_tgteuler = false;

	if (mode >= 4 && tgtmode == 3) {
		NAVHANDLE hNav = v->GetNavSource (navid);
		if (hNav) {
			VECTOR3 tvel,svel;
			v->GetGlobalVel (svel);
			NAVDATA data;
			OBJHANDLE hObj = NULL;
			oapiGetNavData (hNav, &data);
			switch (data.type) {
				case TRANSMITTER_IDS:
					hObj = data.ids.hVessel;
					break;
				case TRANSMITTER_XPDR:
					hObj = data.xpdr.hVessel;
					break;
				case TRANSMITTER_VTOL:
					hObj = data.vtol.hBase;
					break;
				case TRANSMITTER_VOR: {
					hObj = data.vor.hPlanet;
					MATRIX3 Rp;
					oapiGetRotationMatrix (hObj, &Rp);
					oapiGetGlobalVel (hObj, &tvel);
					tvel += mul (Rp, _V(-sin(data.vor.lng),0,cos(data.vor.lng)) * PI2/oapiGetPlanetPeriod(hObj)*oapiGetSize(hObj)*cos(data.vor.lat));
					tgt_rvel = svel-tvel;
					sprintf (oapiDebugString(), "rvel: x=%f, y=%f, z=%f", tgt_rvel.x, tgt_rvel.y, tgt_rvel.z);
					} return; // done
			}
			if (hObj) {
				oapiGetGlobalVel (hObj, &tvel);
				tgt_rvel = svel-tvel;
			} else {
				// TODO
			}
		}
	}
}