﻿// This single file includes all of the orbital code. Sorry about the namespace pollution;
// it's Not My Fault.

// The actual functions to be used are at the very bottom of this file:
//		orbitSetTLE(tle): Sets the two-line element to be used when calculating positions
//		orbitGetLocation(date): Takes a Date object and returns the corresponding
//			orbit location, based on the current TLE. Returns object with properties:
//				lat: Latitude
//				lon: Longitude
//				alt: Altitude

/************************************** math.js *****************************************/

var deg2rad = 1.745329251994330E-2;	/* Degrees to radians */
var pi 			= 3.14159265358979323846; /* Pi */
var pio2		= 1.57079632679489656; /* Pi/2 */
var x3pio2	= 4.71238898038468967;	/* 3*Pi/2 */
var twopi		= 6.28318530717958623	; /* 2*Pi  */
var xmnpda 	= 1.44E3; 	/* Minutes per day */
var ae			= 1.0;
var xke 		= 7.43669161E-2;
var tothrd	= 6.6666666666666666E-1;	/* 2/3 */
var e6a 		= 1.0E-6;
var xj3 		= -2.53881E-6; /* J3 Harmonic (WGS '72) */   
var xkmper 	= 6.378137E3; /* WGS 84 Earth radius km */
var ck2			= 5.413079E-4;
var ck4			= 6.209887E-7;
var f 			= 3.35281066474748E-3;	/* Flattening factor */
var s				= 1.012229;
var qoms2t	= 1.880279E-09;
var secday	= 8.6400E4;	/* Seconds per day */
var omega_E	= 1.00273790934;	/* Earth rotations/siderial day */

function Sign(arg) /* return int; input double */
{
	/* Returns sign of a double */
	if (arg>0)
		return 1;

	else if (arg<0)
		return -1;

	else
		return 0;
}

function Sqr(arg) /* return double; input double */
{
	/* Returns square of a double */
	return (arg*arg);
}

function Cube(arg) /* return double; input double */
{
	/* Returns cube of a double */
	return (arg*arg*arg);
}

function Radians(arg) /* return double; input double */
{
	/* Returns angle in radians from argument in degrees */
	return (arg*deg2rad);
}

function Degrees(arg) /* return double; input double */
{
	/* Returns angle in degrees from argument in radians */
	return (arg/deg2rad);
}

function ArcSin(arg) /* return double; input double */
{
	/* Returns the arcsine of the argument */

	if (fabs(arg)>=1.0)
		return(Sign(arg)*pio2);
	else

	return(atan(arg/Math.sqrt(1.0-arg*arg)));
}

function ArcCos(arg) /* return double; input double */
{
	/* Returns arccosine of argument */
	return(pio2-ArcSin(arg));
}

function FMod2p(x)
{
	/* Returns mod 2PI of argument */

	var i; /*int */
	var ret_val;

	ret_val=x;
	i=Math.round( (ret_val/twopi)-0.5); /* preserve int value for i */
	ret_val-=i*twopi;

	if (ret_val<0.0)
		ret_val+=twopi;

	return ret_val;
}

function AcTan(sinx, cosx)
{
	/* Four-quadrant arctan function */

	if (cosx==0.0)
	{
		if (sinx>0.0)
			return (pio2);
		else
			return (x3pio2);
	}

	else
	{
		if (cosx>0.0)
		{
			if (sinx>0.0)
				return (Math.atan(sinx/cosx));
			else
				return (twopi+Math.atan(sinx/cosx));
		}

		else
			return (Math.PI+Math.atan(sinx/cosx));
	}
}

/************************************** vector.js *****************************************/

// JScript File
if(!window.Sqr){
	function Sqr(arg) { /* return double; input double */
		/* Returns square of a double */
		return (arg*arg);
	}
}

if(!window.ArcCos){
	function ArcCos(arg) /* return double; input double */
	{
		/* Returns arccosine of argument */
		return(pio2-ArcSin(arg));
	}
}

function vector_t () {
	this.x = 0.0;
	this.y = 0.0;
	this.z = 0.0;
	this.w = 0.0; //magnitude compnent
	this.name = "vector_";
	
	this.Magnitude = function () {
		/* Calculates scalar magnitude of a vector_t */
		this.w = Math.sqrt(Sqr(this.x) + Sqr(this.y) + Sqr(this.z));
	}
	
	this.Scale_Vector = function (k) { 
		/* Multiplies the vector v1 by the scalar k */
		this.x *= k;
		this.y *= k;
		this.z *= k;
		this.w = Math.abs(k) * this.w;//this.Magnitude(); saves a little precision not using Magnitude()
	}
	
	this.Normalize = function () {
		/* Normalizes a vector */
		this.x /= this.w;
		this.y /= this.w;
		this.z /= this.w;
		this.w = 1.0;
	}
	
	this.Print = function () {
		/* Prints this vectors params to the current document */
		document.write(this.name+"["+this.x+", "+this.y+", "+this.z+", "+this.w+"]");
	}
}

function Vec_Add(v1, v2) {
	/* Adds vectors v1 and v2 together to produce v3 */
	var v3 = new vector_t();
	v3.name = v1.name+"+"+v2.name;
	v3.x = v1.x + v2.x;
	v3.y = v1.y + v2.y;
	v3.z = v1.z + v2.z;
	v3.Magnitude();
	return v3;
}

function Vec_Sub(v1, v2) {
	/* Subtracts vector v2 from v1 to produce v3 */
	var v3 = new vector_t();
	v3.name = v1.name+"-"+v2.name;
	v3.x = v1.x - v2.x;
	v3.y = v1.y - v2.y;
	v3.z = v1.z - v2.z;
	v3.Magnitude();
	return v3;
}

function Scalar_Multiply(k, v1) {
	/* Multiplies the vector v1 by the scalar k to produce the vector v2 */
	var v2 = new vector_t();
	v2.name = v1.name+"-ScalarMultiply";
	v2.x = k * v1.x;
	v2.y = k * v1.y;
	v2.z = k * v1.z;
	v2.w = Math.abs(k) * v1.w;
	return v2;
}

function Dot(v1, v2) {
	/* Returns the dot product of two vectors */
	return ( (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z) );
}

function Angle(v1, v2) {
	/* Calculates the angle between vectors v1 and v2 */
	v1.Magnitude();//verify that magnatude is set
	v2.Magnitude();//verify that magnatude is set
	return(ArcCos( Dot(v1,v2) / (v1.w * v2.w) ) );
}

function Cross(v1, v2)
{
	/* Produces cross product of v1 and v2, and returns in v3 */
	var v3 = new vector_t();
	v3.name = v1.name+"-Cross";
	v3.x = v1.y * v2.z - v1.z * v2.y;
	v3.y = v1.z * v2.x - v1.x * v2.z;
	v3.z = v1.x * v2.y - v1.y * v2.x;
	v3.Magnitude();
	return v3;
}

/************************************** satellite.js *****************************************/

// JScript File

//requires vector.js
//requires iss.js
//requires math.js


/* Flow control flag definitions */
var ALL_FLAGS = -1;
var SGP_INITIALIZED_FLAG  = 0x000001;	/* not used */
var SGP4_INITIALIZED_FLAG = 0x000002;
var SDP4_INITIALIZED_FLAG = 0x000004;
var SGP8_INITIALIZED_FLAG = 0x000008;	/* not used */
var SDP8_INITIALIZED_FLAG = 0x000010;	/* not used */
var SIMPLE_FLAG       		= 0x000020;
var DEEP_SPACE_EPHEM_FLAG = 0x000040;
var LUNAR_TERMS_DONE_FLAG = 0x000080;
var NEW_EPHEMERIS_FLAG    = 0x000100;	/* not used */
var DO_LOOP_FLAG          = 0x000200;
var RESONANCE_FLAG        = 0x000400;
var SYNCHRONOUS_FLAG      = 0x000800;
var EPOCH_RESTART_FLAG    = 0x001000;
var VISIBLE_FLAG          = 0x002000;
var SAT_ECLIPSED_FLAG     = 0x004000;

var Flags = 0;

/* Two-line-element satellite orbital data
   structure used directly by the SGP4/SDP4 code. */
function tle_t() {
	this.epoch=0;
	this.xndt2o=0;
	this.xndd6o=0;
	this.bstar=0;
	this.xincl=0;
	this.xnodeo=0;
	this.eo=0;
	this.omegao=0;
	this.xmo=0;
	this.xno=0;
	this.catnr=0;
	this.elset=0;
	this.revnum=0;
	this.sat_name = new String;
	this.idesg = new String;
	
	this.select_ephemeris = function ()
	{
		/* Selects the apropriate ephemeris type to be used */
		/* for predictions according to the data in the TLE */
		/* It also processes values in the tle set so that  */
		/* they are apropriate for the sgp4/sdp4 routines   */
	
		var ao, xnodp, dd1, dd2, delo, temp, a1, del1, r1;
	
		/* Preprocess tle set */
		this.xnodeo *= deg2rad;
		this.omegao *= deg2rad;
		this.xmo *= deg2rad;
		this.xincl *= deg2rad;
		temp = twopi / xmnpda / xmnpda;
		this.xno = this.xno * temp * xmnpda;
		this.xndt2o *= temp;
		this.xndd6o = this.xndd6o * temp / xmnpda;
		this.bstar /= ae;
	
		/* Period > 225 minutes is deep space */
		dd1 = (xke / this.xno);
		dd2 = tothrd;
		a1 = Math.pow(dd1,dd2);
		r1 = Math.cos(this.xincl);
		dd1 = (1.0 - this.eo * this.eo);
		temp = ck2 * 1.5 *(r1 * r1 * 3.0 - 1.0) / Math.pow(dd1,1.5);
		del1 = temp / (a1 * a1);
		ao = a1 * (1.0 - del1 * (tothrd * 0.5 + del1 * (del1 * 1.654320987654321 + 1.0) ) );
		delo = temp / (ao * ao);
		xnodp = this.xno / (delo + 1.0);

		/* Select a deep-space/near-earth ephemeris */
		if(twopi/xnodp/xmnpda >= 0.15625)
			SetFlag(DEEP_SPACE_EPHEM_FLAG);
		else
			ClearFlag(DEEP_SPACE_EPHEM_FLAG);
	}
}

/* Geodetic position structure used by SGP4/SDP4 code. */
function geodetic_t(that)
{
	if(that){
		this.lat = that.lat;
		this.lon = that.lon;
		this.alt = that.alt;
		this.theta = that.theta; /* double */
	}
	else {
		this.lat = 0;
		this.lon = 0;
		this.alt = 0;
		this.theta = 0; /* double */
	}
}

function sat_t()
{
	this.name = new String; /* String */
	this.catnum; /* long */
	this.setnum; /* long */
	this.designator = new String; /* String */
	this.year; /* int */
	this.refepoch; /* double */
	this.incl; /* double */
	this.raan; /* double */
	this.eccn; /* double */
	this.argper; /* double */
	this.meanan; /* double */
	this.meanmo; /* double */
	this.drag; /* double */
	this.nddot6; /* double */
	this.bstar; /* double */
	this.orbitnum; /* long */
	
	this.loadTLE = function (string) {
		var temp = new Array();
		var i, cur, tempnum;

		temp = string.split("\n");
		this.name = temp[0];

		this.catnum = Number(temp[1].substring(2,7) );
		this.designator = temp[1].substring(9,17);
		this.year = Number(temp[1].substring(18,20) );
		this.refepoch = Number(temp[1].substring(20,32) );
		this.drag = Number(temp[1].substring(33,43) );
		tempnum = 1.0e-5 * Number(temp[1].substring(44,50) );
		this.nddot6 = tempnum / Math.pow(10.0,(temp[1].substring(51, 52)-'0'));

		tempnum = 1.0e-5 * Number(temp[1].substring(53,59) );
		this.bstar = tempnum / Math.pow(10.0,(temp[1].substring(60, 61)-'0') );
		this.setnum = Number(temp[1].substring(64,68) );

		this.incl = Number(temp[2].substring(8,16) );
		this.raan = Number(temp[2].substring(17,25) );
		this.eccn = 1.0e-07 * Number(temp[2].substring(26,33) );
		this.argper = Number(temp[2].substring(34,42) );
		this.meanan = Number(temp[2].substring(43,51) );
		this.meanmo = Number(temp[2].substring(52,63) );
		this.orbitnum = Number(temp[2].substring(63,68) );
	}
}

function Satellite() {
	this.sat = new sat_t();

	this.tle = new tle_t(); //2-line Element
	this.sat_geodetic = new geodetic_t(); // location

	// Limits for various calculations
	this.calcLatLonAltLimit = 1E-10;	// Allowable error in calculation (default 1E-10)
	this.keplerIterations = 10;			// Number of iterations required (default 10)
	
	/* private data */
	/* Satellite position and velocity vectors */		
	this.vel = new vector_t();
	this.vel.name = "velocity";
	this.pos = new vector_t();
	this.pos.name = "position";
	
	/* Satellite Az, El, Range, Range rate */
//	this.obs_set = new vector_t();
//	this.obs_set.name = "obs_set";

	this.jul_utc =0;
	this.jul_epoch = 0;
	this.daynum = 0;
	this.tsince = 0;
	this.age = 0;
	this.ephem='';
	this.io_lat='N';
	this.io_lon='E';
	
	this.Locate = function (currentTime){
		//this function will need to be modified to fetch the passed in currentTime for daynum
		if(!currentTime)
			this.daynum = CurrentDaynum();
		else
			this.daynum = MakeDaynum(currentTime);

//		this.PreCalc();
		this.Calc();
		return new geodetic_t(this.sat_geodetic);
	}
	
	this.LocateRange = function (startTime, endTime, inc){
//add input error checking

		var end = endTime.getTime();
		var increment = inc*1000;//convert to microseconds
		var count = 0;
		var holder = new Array();


		this.PreCalc();
		for(var current = startTime.getTime(); current <= end; current += increment)//1000 millisec * inc (in seconds)
		{	
			this.daynum = MakeDaynum(new Date(current) );
			this.Calc();
			
			holder.push(new geodetic_t(this.sat_geodetic) );
		}

//		for(var i=0; i<holder.length; i++)
//			alert(holder[i].lat+" "+holder[i].lon);
		
		return holder;//return an array of geodetic_t
	}
	
	this.PreCalc = function () {
	/* This function copies TLE data from PREDICT's sat structure
	   to the SGP4/SDP4's single dimensioned tle structure, and
	   prepares the tracking code for the update. */

		this.tle.sat_name = this.sat.name;
		this.tle.idesg = this.sat.designator;
		this.tle.catnr = this.sat.catnum;
		this.tle.epoch = (1000.0 * this.sat.year) + this.sat.refepoch;
		this.tle.xndt2o = this.sat.drag;
		this.tle.xndd6o = this.sat.nddot6;
		this.tle.bstar = this.sat.bstar;
		this.tle.xincl = this.sat.incl;
		this.tle.xnodeo = this.sat.raan;
		this.tle.eo = this.sat.eccn;
		this.tle.omegao = this.sat.argper;
		this.tle.xmo = this.sat.meanan;
		this.tle.xno = this.sat.meanmo;
		this.tle.revnum = this.sat.orbitnum;
		 
		/* Clear all flags */	
		ClearFlag(ALL_FLAGS);
		/* Select ephemeris type.  This function will set or clear the
			 DEEP_SPACE_EPHEM_FLAG depending on the TLE parameters of the
			 satellite.  It will also pre-process tle members for the
			 ephemeris functions SGP4 or SDP4, so this function must
			 be called each time a new tle set is used. */

		this.tle.select_ephemeris();
	}
	
	this.Calc = function ()
	{
		/* This is the stuff we need to do repetitively while tracking. */
	
		/* Solar ECI position vector  */
		var solar_vector = new vector_t();
	
		/* Solar observed azi and ele vector  */
		var solar_set = new vector_t();
	
		/* Satellite's predicted geodetic position */
		var sat_geodetic = new geodetic_t();

		this.jul_utc=this.daynum+2444238.5;

		/* Convert satellite's epoch time to Julian  */
		/* and calculate time since epoch in minutes */

		this.jul_epoch=Julian_Date_of_Epoch(this.tle.epoch);
		this.tsince=(this.jul_utc-this.jul_epoch)*xmnpda;
		this.age=this.jul_utc-this.jul_epoch;

		/* Copy the ephemeris type in use to ephem string. */
			if (isFlagSet(DEEP_SPACE_EPHEM_FLAG))
				this.ephem = "SDP4";	//Should never be this for a normal satellite
			else
				this.ephem = "SGP4";
		/* Call NORAD routines according to deep-space flag. */

		if (isFlagSet(DEEP_SPACE_EPHEM_FLAG))
			this.SDP4();
		else
			this.SGP4();
	
		/* Scale position and velocity vectors to km and km/sec */
	
		this.Convert_Sat_State();
		
//		alert("pos["+this.pos.x+", "+this.pos.y+", "+this.pos.z+"] vel["+this.vel.x+", "+this.vel.y+", "+this.vel.z+"]");
	
		/* Calculate velocity of satellite */
	
		this.vel.Magnitude();
		sat_vel=this.vel.w;
		/** All angles in rads. Distance in km. Velocity in km/s **/
		/* Calculate satellite Azi, Ele, Range and Range-rate */
	
		/* Calculate satellite Lat North, Lon East and Alt. */
	
		this.Calculate_LatLonAlt();//jul_utc, &pos, &sat_geodetic);

		//convert to degrees from radians
		this.sat_geodetic.lat = Degrees(this.sat_geodetic.lat);
		this.sat_geodetic.lon = Degrees(this.sat_geodetic.lon);

		ClearFlag(ALL_FLAGS);
	}
	
	this.SGP4 = function()
	{

		/* This function is used to calculate the position and velocity */
		/* of near-earth (period < 225 minutes) satellites. tsince is   */
		/* time since epoch in minutes, tle is a pointer to a tle_t     */
		/* structure with Keplerian orbital elements and pos and vel    */
		/* are vector_t structures returning ECI satellite position and */ 
		/* velocity. Use Convert_Sat_State() to convert to km and km/s. */
	
		var aodp, aycof, c1, c4, c5, cosio, d2, d3, d4, delmo,
		omgcof, eta, omgdot, sinio, xnodp, sinmo, t2cof, t3cof, t4cof,
		t5cof, x1mth2, x3thm1, x7thm1, xmcof, xmdot, xnodcf, xnodot, xlcof;
	
		var cosuk, sinuk, rfdotk, vx, vy, vz, ux, uy, uz, xmy, xmx, cosnok,
		sinnok, cosik, sinik, rdotk, xinck, xnodek, uk, rk, cos2u, sin2u,
		u, sinu, cosu, betal, rfdot, rdot, r, pl, elsq, esine, ecose, epw,
		cosepw, x1m5th, xhdot1, tfour, sinepw, capu, ayn, xlt, aynl, xll,
		axn, xn, beta, xl, e, a, tcube, delm, delomg, templ, tempe, tempa,
		xnode, tsq, xmp, omega, xnoddf, omgadf, xmdf, a1, a3ovk2, ao,
		betao, betao2, c1sq, c2, c3, coef, coef1, del1, delo, eeta, eosq,
		etasq, perigee, pinvsq, psisq, qoms24, s4, temp, temp1, temp2,
		temp3, temp4, temp5, temp6, theta2, theta4, tsi;
	
		var i;
	
		/* Initialization */
	
		if (isFlagClear(SGP4_INITIALIZED_FLAG))
		{
			SetFlag(SGP4_INITIALIZED_FLAG);
	
			/* Recover original mean motion (xnodp) and   */
			/* semimajor axis (aodp) from input elements. */
	
			a1=Math.pow(xke/this.tle.xno,tothrd);
			cosio=Math.cos(this.tle.xincl);
			theta2=cosio*cosio;
			x3thm1=3*theta2-1.0;
			eosq=this.tle.eo*this.tle.eo;
			betao2=1.0-eosq;
			betao=Math.sqrt(betao2);
			del1=1.5*ck2*x3thm1/(a1*a1*betao*betao2);
			ao=a1*(1.0-del1*(0.5*tothrd+del1*(1.0+134.0/81.0*del1)));
			delo=1.5*ck2*x3thm1/(ao*ao*betao*betao2);
			xnodp=this.tle.xno/(1.0+delo);
			aodp=ao/(1.0-delo);
	
			/* For perigee less than 220 kilometers, the "simple"     */
			/* flag is set and the equations are truncated to linear  */
			/* variation in sqrt a and quadratic variation in mean    */
			/* anomaly.  Also, the c3 term, the delta omega term, and */
			/* the delta m term are dropped.                          */
	
			if ((aodp*(1-this.tle.eo)/ae)<(220/xkmper+ae))
					SetFlag(SIMPLE_FLAG);
	
			else
					ClearFlag(SIMPLE_FLAG);
	
			/* For perigees below 156 km, the      */
			/* values of s and qoms2t are altered. */
	
			s4=s;
			qoms24=qoms2t;
			perigee=(aodp*(1-this.tle.eo)-ae)*xkmper;
	
			if (perigee<156.0)
			{
				if (perigee<=98.0)
						s4=20;
				else
					 s4=perigee-78.0;
	
				qoms24=Math.pow((120-s4)*ae/xkmper,4);
				s4=s4/xkmper+ae;
			}
	
			pinvsq=1/(aodp*aodp*betao2*betao2);
			tsi=1/(aodp-s4);
			eta=aodp*this.tle.eo*tsi;
			etasq=eta*eta;
			eeta=this.tle.eo*eta;
			psisq=Math.abs(1-etasq);
			coef=qoms24*Math.pow(tsi,4);
			coef1=coef/Math.pow(psisq,3.5);
			c2=coef1*xnodp*(aodp*(1+1.5*etasq+eeta*(4+etasq))+0.75*ck2*tsi/psisq*x3thm1*(8+3*etasq*(8+etasq)));
			c1=this.tle.bstar*c2;
			sinio=Math.sin(this.tle.xincl);
			a3ovk2=-xj3/ck2*Math.pow(ae,3);
			c3=coef*tsi*a3ovk2*xnodp*ae*sinio/this.tle.eo;
			x1mth2=1-theta2;
	
			c4=2*xnodp*coef1*aodp*betao2*(eta*(2+0.5*etasq)+this.tle.eo*(0.5+2*etasq)-2*ck2*tsi/(aodp*psisq)*(-3*x3thm1*(1-2*eeta+etasq*(1.5-0.5*eeta))+0.75*x1mth2*(2*etasq-eeta*(1+etasq))*Math.cos(2*this.tle.omegao)));
			c5=2*coef1*aodp*betao2*(1+2.75*(etasq+eeta)+eeta*etasq);
	
			theta4=theta2*theta2;
			temp1=3*ck2*pinvsq*xnodp;
			temp2=temp1*ck2*pinvsq;
			temp3=1.25*ck4*pinvsq*pinvsq*xnodp;
			xmdot=xnodp+0.5*temp1*betao*x3thm1+0.0625*temp2*betao*(13-78*theta2+137*theta4);
			x1m5th=1-5*theta2;
			omgdot=-0.5*temp1*x1m5th+0.0625*temp2*(7-114*theta2+395*theta4)+temp3*(3-36*theta2+49*theta4);
			xhdot1=-temp1*cosio;
			xnodot=xhdot1+(0.5*temp2*(4-19*theta2)+2*temp3*(3-7*theta2))*cosio;
			omgcof=this.tle.bstar*c3*Math.cos(this.tle.omegao);
			xmcof=-tothrd*coef*this.tle.bstar*ae/eeta;
			xnodcf=3.5*betao2*xhdot1*c1;
			t2cof=1.5*c1;
			xlcof=0.125*a3ovk2*sinio*(3+5*cosio)/(1+cosio);
			aycof=0.25*a3ovk2*sinio;
			delmo=Math.pow(1+eta*Math.cos(this.tle.xmo),3);
			sinmo=Math.sin(this.tle.xmo);
			x7thm1=7*theta2-1;
	
			if (isFlagClear(SIMPLE_FLAG))
			{
				c1sq=c1*c1;
				d2=4*aodp*tsi*c1sq;
				temp=d2*tsi*c1/3;
				d3=(17*aodp+s4)*temp;
				d4=0.5*temp*aodp*tsi*(221*aodp+31*s4)*c1;
				t3cof=d2+2*c1sq;
				t4cof=0.25*(3*d3+c1*(12*d2+10*c1sq));
				t5cof=0.2*(3*d4+12*c1*d3+6*d2*d2+15*c1sq*(2*d2+c1sq));
			}
		}
	
		/* Update for secular gravity and atmospheric drag. */
		xmdf=this.tle.xmo+xmdot*this.tsince;
		omgadf=this.tle.omegao+omgdot*this.tsince;
		xnoddf=this.tle.xnodeo+xnodot*this.tsince;
		omega=omgadf;
		xmp=xmdf;
		tsq=this.tsince*this.tsince;
		xnode=xnoddf+xnodcf*tsq;
		tempa=1-c1*this.tsince;
		tempe=this.tle.bstar*c4*this.tsince;
		templ=t2cof*tsq;
			
		if (isFlagClear(SIMPLE_FLAG))
		{
			delomg=omgcof*this.tsince;
			delm=xmcof*(Math.pow(1+eta*Math.cos(xmdf),3)-delmo);
			temp=delomg+delm;
			xmp=xmdf+temp;
			omega=omgadf-temp;
			tcube=tsq*this.tsince;
			tfour=this.tsince*tcube;
			tempa=tempa-d2*tsq-d3*tcube-d4*tfour;
			tempe=tempe+this.tle.bstar*c5*(Math.sin(xmp)-sinmo);
			templ=templ+t3cof*tcube+tfour*(t4cof+this.tsince*t5cof);
		}
	
		a=aodp*Math.pow(tempa,2);
		e=this.tle.eo-tempe;
		xl=xmp+omega+xnode+xnodp*templ;
		beta=Math.sqrt(1-e*e);
		xn=xke/Math.pow(a,1.5);
	
		/* Long period periodics */
		axn=e*Math.cos(omega);
		temp=1/(a*beta*beta);
		xll=temp*xlcof*axn;
		aynl=temp*aycof;
		xlt=xl+xll;
		ayn=e*Math.sin(omega)+aynl;
	
		/* Solve Kepler's Equation */
		capu=FMod2p(xlt-xnode);
		temp2=capu;
		i=0;

		do
		{
			sinepw=Math.sin(temp2);
			cosepw=Math.cos(temp2);
			temp3=axn*sinepw;
			temp4=ayn*cosepw;
			temp5=axn*cosepw;
			temp6=ayn*sinepw;
			epw=(capu-temp4+temp3-temp2)/(1-temp5-temp6)+temp2;
			
			if (Math.abs(epw-temp2)<= e6a)
				break;
					
			temp2=epw;
	
		} while (i++<this.keplerIterations);
	
		/* Short period preliminary quantities */
//IE bugs in here 
		ecose=temp5+temp6;
		esine=temp3-temp4;
		elsq=axn*axn+ayn*ayn;
		temp=1-elsq;
		pl=a*temp;
		r=a*(1-ecose);
		temp1=1/r;
		rdot=xke*Math.sqrt(a)*esine*temp1;
		rfdot=xke*Math.sqrt(pl)*temp1;
		temp2=a*temp1;
		betal=Math.sqrt(temp);
		temp3=1/(1+betal);
		cosu=temp2*(cosepw-axn+ayn*esine*temp3);
		sinu=temp2*(sinepw-ayn-axn*esine*temp3);
		u=AcTan(sinu,cosu);
		sin2u=2*sinu*cosu;
		cos2u=2*cosu*cosu-1;
		temp=1/pl;
		temp1=ck2*temp;
		temp2=temp1*temp;
	
		/* Update for short periodics */
		rk=r*(1-1.5*temp2*betal*x3thm1)+0.5*temp1*x1mth2*cos2u;
		uk=u-0.25*temp2*x7thm1*sin2u;
		xnodek=xnode+1.5*temp2*cosio*sin2u;
		xinck=this.tle.xincl+1.5*temp2*cosio*sinio*cos2u;
		rdotk=rdot-xn*temp1*x1mth2*sin2u;
		rfdotk=rfdot+xn*temp1*(x1mth2*cos2u+1.5*x3thm1);
	
		/* Orientation vectors */
		sinuk=Math.sin(uk);
		cosuk=Math.cos(uk);
		sinik=Math.sin(xinck);
		cosik=Math.cos(xinck);
		sinnok=Math.sin(xnodek);
		cosnok=Math.cos(xnodek);
		xmx=-sinnok*cosik;
		xmy=cosnok*cosik;
		ux=xmx*sinuk+cosnok*cosuk;
		uy=xmy*sinuk+sinnok*cosuk;
		uz=sinik*sinuk;
		vx=xmx*cosuk-cosnok*sinuk;
		vy=xmy*cosuk-sinnok*sinuk;
		vz=sinik*cosuk;
	
		/* Position and velocity */
		this.pos.x=rk*ux;
		this.pos.y=rk*uy;
		this.pos.z=rk*uz;
		this.vel.x=rdotk*ux+rfdotk*vx;
		this.vel.y=rdotk*uy+rfdotk*vy;
		this.vel.z=rdotk*uz+rfdotk*vz;

		/* Phase in radians */
		phase=xlt-xnode-omgadf+twopi;
			
		if (phase<0.0)
			phase+=twopi;
	
		phase=FMod2p(phase);
	}
	
	this.SDP4 = function ()
	{
		alert("SDP4 functionality has not been converted for use in this application");		
	}
	
	this.Convert_Sat_State = function ()
	{
		/* Converts the satellite's position and velocity  */
		/* vectors from normalized values to km and km/sec */ 
		this.pos.Scale_Vector(xkmper);
		this.vel.Scale_Vector(xkmper*xmnpda/secday);
	}

	this.Calculate_LatLonAlt = function ()//double time, vector_t *pos,  geodetic_t *geodetic)
	{
		/* Procedure Calculate_LatLonAlt will calculate the geodetic  */
		/* position of an object given its ECI position pos and time. */
		/* It is intended to be used to determine the ground track of */
		/* a satellite.  The calculations  assume the earth to be an  */
		/* oblate spheroid as defined in WGS '72.                     */
	
		/* Reference:  The 1992 Astronomical Almanac, page K12. */
	
		var r, e2, phi, c;
	
		this.sat_geodetic.theta = AcTan(this.pos.y, this.pos.x); /* radians */
		this.sat_geodetic.lon = FMod2p(this.sat_geodetic.theta - ThetaG_JD(this.jul_utc)); /* radians */
		r = Math.sqrt(Sqr(this.pos.x)+Sqr(this.pos.y));
		e2 = f * (2-f);
		this.sat_geodetic.lat = AcTan(this.pos.z, r); /* radians */
	
		do
		{
			phi = this.sat_geodetic.lat;
			c = 1/Math.sqrt(1-e2*Sqr(Math.sin(phi)));
			this.sat_geodetic.lat = AcTan(this.pos.z+xkmper*c*e2*Math.sin(phi),r);
	
		} while (Math.abs(this.sat_geodetic.lat-phi)>=this.calcLatLonAltLimit);
	
		this.sat_geodetic.alt = r/Math.cos(this.sat_geodetic.lat) - xkmper*c; /* kilometers */
	
		if (this.sat_geodetic.lat>pio2)
			this.sat_geodetic.lat-=twopi;
	}
}


function CurrentDaynum()
{
	return MakeDaynum(new Date());
}

function MakeDaynum(timestamp){
	var secs = timestamp.getTime()/1000;

	return ((secs/86400.0)-3651.0);
}

function Julian_Date_of_Epoch(epoch)
{ 
	/* The function Julian_Date_of_Epoch returns the Julian Date of     */
	/* an epoch specified in the format used in the NORAD two-line      */
	/* element sets. It has been modified to support dates beyond       */
	/* the year 1999 assuming that two-digit years in the range 00-56   */
	/* correspond to 2000-2056. Until the two-line element set format   */
	/* is changed, it is only valid for dates through 2056 December 31. */

	var year, day;

	/* Modification to support Y2K */
	/* Valid 1957 through 2056     */

//		day=modf(epoch*1E-3, &year)*1E3;
	day = (epoch*1E-3%1)*1E3;
	year = epoch*1E-3 - (epoch*1E-3%1)

	if (year<57)
		year=year+2000;
	else
		year=year+1900;
	return (Julian_Date_of_Year(year)+day);
}

function Julian_Date_of_Year(year)
{
	/* The function Julian_Date_of_Year calculates the Julian Date  */
	/* of Day 0.0 of {year}. This function is used to calculate the */
	/* Julian Date of any date by using Julian_Date_of_Year, DOY,   */
	/* and Fraction_of_Day. */

	/* Astronomical Formulae for Calculators, Jean Meeus, */
	/* pages 23-25. Calculate Julian Date of 0.0 Jan year */

//rounding is used for forcing integer values like the C code
	var A, B, i;
	var jdoy;

	year=year-1;
	i=Math.round( (year/100)-0.5); 
	A=i;
	i=Math.round( (A/4)-0.5);
	B= 2 - A + i;
	i=Math.round( (365.25*year)-0.5);
	i+=Math.round( (30.6001*14) -0.5);
	jdoy=i+1720994.5+B;

	return jdoy;
}

function ThetaG_JD(jd)
{
	/* Reference:  The 1992 Astronomical Almanac, page B6. */

	var UT, TU, GMST;

	UT=(jd+0.5)%1;
	jd=jd-UT;
	TU=(jd-2451545.0)/36525;
	GMST=24110.54841+TU*(8640184.812866+TU*(0.093104-TU*6.2E-6));
	var test = GMST;
	GMST = (GMST+secday*omega_E*UT) % secday;
	return (twopi*GMST/secday);
}

/* Functions for testing and setting/clearing flags used in SGP4 code */

function isFlagSet(flag) /* return int; input int */
{
	return (Flags&flag);
}

function isFlagClear(flag) /* return int; input int */
{
	return (~Flags&flag);
}

function SetFlag(flag) /* return void; input int */
{
	Flags|=flag;
}

function ClearFlag(flag) /* return void; input int */
{
	Flags&=~flag;
}

/************************************** glue code *****************************************/


var orbitSat = new Satellite();

// Reduce precision to speed things up
orbitSat.keplerIterations = 1;			// More iterations don't help
orbitSat.calcLatLonAltLimit = 0.0001;	// 10m error on orbit

// This sets the orbit's two-line element.
function orbitSetTLE(tle) {
	orbitSat.sat.loadTLE(tle);
	orbitSat.PreCalc();
}

// This takes a Date object and returns the corresponding orbit location. The location
// is an object with the following properties:
//		lat: Latitude (degrees)
//		lon: Longitude (degrees)
//		alt: Altitude (kilometers)
// The following are not yet implemented
//		vLat: Surface velocity in North direction (km/sec)
//		vLon: Surface velocity in East direction (km/sec)
//		vAlt: Surface velocity in altitude direction (km/sec)
function orbitGetLocation(date) {
//	debugln("orbitGetLocation():");
	var vector = new vector_t();
	orbitSat.daynum = MakeDaynum(date);
	orbitSat.Calc();
	var loc = new Object();
	// Get lat/lon/alt
	loc.lat = orbitSat.sat_geodetic.lat;
	loc.lon = orbitSat.sat_geodetic.lon;
	loc.alt = orbitSat.sat_geodetic.alt;
	loc.theta = orbitSat.sat_geodetic.theta;
	loc.velX = orbitSat.vel.x;
	loc.velY = orbitSat.vel.y;
	loc.velZ = orbitSat.vel.z;
	loc.velW = orbitSat.vel.w;
	loc.bearing = Math.atan2(loc.velY, loc.velX) * 360 / (Math.PI * 2);
	return loc;
}



