////////////////////////////////////////////////////////////////////
/// DemoController.cs
/// © 2005 Carl Johansen
///
/// Controls the Hohmann Transfer Orbit demonstration
////////////////////////////////////////////////////////////////////
using System;
using System.Drawing;
using System.Resources;
using System.Reflection;
using System.Globalization;
namespace CarlInc.Demos.Hohmann
{
///
/// Controls the Hohmann Transfer Orbit demonstration
///
public class DemoController
{
public delegate void ShowInfoTextDelegate(string infoMessage, bool showContinueButton, bool showResetButton);
public CircularOrbitingBody bodyEarth, bodyMars;
public EllipticalOrbitingBody bodySatellite;
private const double rEarth = 1.0, rMars = 1.5133;
private bool showingSatellite, freeSpinning;
private SignWatcher satEventWatcher;
private Byte currStatus;
private int simulationDaysElapsed;
private DateTime simulationCurrDate;
public DateTime SimulationCurrDate { get { return simulationCurrDate; } }
public double SatMajorAxisAngle;
public double DisplayHeightInAU() { return rMars * 2.0; }
private RectangleF satOrbitRect;
public RectangleF SatOrbitRectangle { get { return satOrbitRect; } }
public bool ShowingSatellite { get { return showingSatellite; } set { showingSatellite = value; } }
public DemoController() { }
public void Init()
{
currStatus = 0;
simulationDaysElapsed = 0;
simulationCurrDate = new DateTime(2001,6,13,0,0,0,0); //Simulation starts with Earth and Mars in
//opposition. This occurred on June 13, 2001
showingSatellite = false; freeSpinning = false;
bodyEarth = new CircularOrbitingBody(rEarth);
bodyMars = new CircularOrbitingBody(rMars);
bodySatellite = new EllipticalOrbitingBody((bodyEarth.SemiMajorAxis + bodyMars.SemiMajorAxis) / 2, (bodyMars.SemiMajorAxis - bodyEarth.SemiMajorAxis) / 2);
satOrbitRect = new RectangleF(0, 0, 0, 0);
satEventWatcher = new SignWatcher();
}
public void AdvanceOneDay(ShowInfoTextDelegate ShowInfoText)
{
double satAnomaly;
simulationDaysElapsed++;
simulationCurrDate = simulationCurrDate.AddDays(1);
bodyEarth.AdvanceOneDay();
bodyMars.AdvanceOneDay();
if(showingSatellite)
{
satAnomaly = bodySatellite.AdvanceOneDay();
if(!freeSpinning && satEventWatcher.SignChangeInDifference(satAnomaly, Math.PI)) //check for completion of half an orbit (ie arrival on Mars)
{
MoveToNextStatus(ShowInfoText, true, true);
freeSpinning = true;
return;
}
}
if(!showingSatellite)
{ //look for launch window
int Tmars = bodyMars.OrbitalPeriod, Tsat = bodySatellite.OrbitalPeriod;
//In Tsat/2 days, satellite will travel Pi radians, and Mars will travel [Tsat/Tmars] * Pi radians,
//so need to launch when Mars has a headstart of Pi (1- [Tsat/Tmars]) radians on the Earth
double marsHeadstartRequired = Math.PI * (1 - (double) Tsat / Tmars);
double earthAngle = bodyEarth.GetTrueAnomaly(), marsAngle = bodyMars.GetTrueAnomaly();
if(marsAngle < earthAngle) marsAngle += 2.0 * Math.PI; //always consider Mars to be ahead of Earth
//We're looking for HeadstartRequired = CurrentHeadstart, but we need to cope with floating-point
// inaccuracies. The best way to do this is to look for a sign change in (HeadstartRequired - CurrentHeadstart)
double planetAngularSeparation = marsAngle - earthAngle;
if(satEventWatcher.SignChangeInDifference(marsHeadstartRequired, planetAngularSeparation))
{
showingSatellite = true;
SatMajorAxisAngle = bodyEarth.GetTrueAnomaly();
bodySatellite.Init();
satOrbitRect = bodySatellite.OrbitBoundingRectUnrotated;
satEventWatcher.Reset(); //now we will use the watcher to look for the satellite's arrival on Mars
MoveToNextStatus(ShowInfoText, true, false);
return;
}
}
}
public void MoveToNextStatus(ShowInfoTextDelegate ShowInfoText, bool showContinueButton, bool showResetButton)
{
ResourceManager rm = new ResourceManager("Hohmann.InfoText", Assembly.GetExecutingAssembly());
currStatus++;
string infoMessage = rm.GetString("txtInfo" + currStatus.ToString(), new CultureInfo(System.String.Empty));
ShowInfoText(infoMessage, showContinueButton, showResetButton);
}
public void ChangeEarthRadius(int sliderValue)
{
bodyEarth.SemiMajorAxis = 1.0 + (sliderValue - 5) / 10.0;
bodySatellite.SemiMajorAxis = (bodyEarth.SemiMajorAxis + bodyMars.SemiMajorAxis) / 2;
bodySatellite.C = (bodyMars.SemiMajorAxis - bodyEarth.SemiMajorAxis) / 2;
satOrbitRect = bodySatellite.OrbitBoundingRectUnrotated;
satEventWatcher.Reset();
}
}
///
/// Watches the sign of the difference between two doubles over successive calls and
/// flags any changes.
///
public class SignWatcher
{
private sbyte previousSign;
public SignWatcher(double val1, double val2)
{ previousSign = (val1 < val2) ? (sbyte) -1 : (sbyte) 1; }
public SignWatcher()
{ previousSign = 0; }
public void Reset() { previousSign = 0; }
public bool SignChangeInDifference(double val1, double val2)
{
sbyte newSign = (val1 < val2) ? (sbyte) -1 : (sbyte) 1;
bool blnSignChange = ((previousSign != 0) && (newSign != previousSign));
previousSign = newSign;
return blnSignChange;
}
}
}