Satellite tracking for the mbed platform(s). Note: This software is beerware.
Satellite Tracking with mbed!
This is a quick example of tracking satellites with the mbed platform. Ensure you have the latest TLE information and map it correctly. I have commented in the original TLEs used for the example to help you do this.
This code is not restricted to geostationary/geosynchronous satellites, you can track any satellite for which you can get a TLE. (Which is almost all non military satellites).
See http://www.celestrak.com/NORAD/elements/ http://en.wikipedia.org/wiki/Two-line_element_set
If you use this code, please send me a beer (see main.cpp header). Enjoy!
Plan13.cpp
- Committer:
- SamClarke
- Date:
- 2014-01-20
- Revision:
- 5:8af1b8452986
- Parent:
- 1:ab43ae956815
File content as of revision 5:8af1b8452986:
// Code ported from qrptracker
// https://code.google.com/p/qrptracker/
#include "mbed.h"
#include "Plan13.h"
#define DEBUG false
#define TEST false
//Plan13::Plan13() {
//
//}
double Plan13::rad(double deg)
{
return (M_PI / 180.0 * deg);
}
double Plan13::deg(double rad)
{
return (rad * 180.0 / M_PI);
}
double Plan13::FNatn(double y, double x)
{
float a;
if (x != 0)
{
a = atan(y / x);
}
else
{
a = M_PI / 2.0 * sin(y);
}
if (x < 0)
{
a = a + M_PI;
}
if (a < 0)
{
a = a + 2.0 * M_PI;
}
return (a);
}
/* Convert date to day number
*
* Function returns a general day number from year, month, and day.
* Value is (JulianDay - 1721409.5) or (AmsatDay + 722100)
*
*/
double Plan13::FNday(int year, int month, int day)
{
double JulianDate;
if (month <= 2)
{
year -= 1;
month += 12;
}
JulianDate = (long)(year * YM) + (int)((month + 1) * 30.6) + (day - 428);
return (JulianDate);
}
void Plan13::initSat(void)
{
//readElements(SAT); //now done beforehand
/* Observer's location */
LA = rad( observer_lat );
LO = rad( observer_lon );
HT = ((float) observer_height)/1000.0; // this needs to be in km
CL = cos(LA);
SL = sin(LA);
CO = cos(LO);
SO = sin(LO);
/* WGS-84 Earth Ellipsoid */
RE = 6378.137;
FL = 1.0 / 298.257224;
/* IAU-76 Earth Ellipsoid */
// RE = 6378.140;
// FL = 1.0 / 298.257;
RP = RE * (1.0 - FL);
XX = RE * RE;
ZZ = RP * RP;
D = sqrt( XX * CL * CL + ZZ * SL * SL );
Rx = XX / D + HT;
Rz = ZZ / D + HT;
/* Observer's unit vectors Up EAST and NORTH in geocentric coordinates */
Ux = CL * CO;
Ex = -SO;
Nx = -SL * CO;
Uy = CL * SO;
Ey = CO;
Ny = -SL * SO;
Uz = SL;
Ez = 0;
Nz = CL;
/* Observer's XYZ coordinates at earth's surface */
Ox = Rx * Ux;
Oy = Rx * Uy;
Oz = Rz * Uz;
/* Convert angles to radians, etc. */
RA = rad(RA);
IN = rad(IN);
WP = rad(WP);
MA = rad(MA);
MM = MM * 2.0 * M_PI;
M2 = M2 * 2.0 * M_PI;
// YM = 365.25; /* Mean year, days */
// YT = 365.2421970; /* Tropical year, days */
WW = 2.0 * M_PI / YT; /* Earth's rotation rate, rads/whole day */
WE = 2.0 * M_PI + WW; /* Earth's rotation rate, rads/day */
W0 = WE / 86400; /* Earth's rotation rate, rads/sec */
/* Observer's velocity, geocentric coordinates */
VOx = -Oy * W0;
VOy = Ox * W0;
/* Convert satellite epoch to Day No. and fraction of a day */
DE = FNday(YE, 1, 0) + (int)TE;
TE = TE - (int)TE;
/* Average Precession rates */
GM = 3.986E5; /* Earth's gravitational constant km^3/s^2 */
J2 = 1.08263E-3; /* 2nd Zonal coeff, Earth's gravity Field */
N0 = MM / 86400.0; /* Mean motion rads/s */
AU = pow((float)GM / N0 / N0, (float)1.0 / 3.0); /* Semi major axis km */
b0 = AU * sqrt(1.0 - EC * EC); /* Semi minor axis km */
SI = sin(IN);
CI = cos(IN);
PC = RE * AU / (b0 * b0);
PC = 1.5 * J2 * PC * PC * MM; /* Precession const, rad/day */
QD = -PC * CI; /* Node Precession rate, rad/day */
WD = PC *(5.0 * CI*CI - 1.0) / 2.0; /* Perigee Precession rate, rad/day */
DC = -2.0 * M2 / MM / 3.0; /* Drag coeff */
/* Sideral and solar data. Never needs changing. Valid to year 2000+ */
/* GHAA, Year YG, Jan 0.0 */
YG = 2014;
G0 = 99.5578;
/* MA Sun and rate, deg, deg/day */
MAS0 = 356.4485;
MASD = 0.98560028;
/* Sun's inclination */
INS = rad(23.4380);
CNS = cos(INS);
SNS = sin(INS);
/* Sun's equation of center terms */
EQC1 = 0.03341;
EQC2 = 0.00035;
/* Bring Sun data to satellite epoch */
TEG = (DE - FNday(YG, 1, 0)) + TE; /* Elapsed Time: Epoch - YG */
GHAE = rad(G0) + TEG * WE; /* GHA Aries, epoch */
MRSE = rad(G0) + (TEG * WW) + M_PI; /* Mean RA Sun at Sat Epoch */
MASE = rad(MAS0 + MASD * TEG); /* Mean MA Sun */
/* Antenna unit vector in orbit plane coordinates */
CO = cos(rad(ALON));
SO = sin(rad(ALON));
CL = cos(rad(ALAT));
SL = sin(rad(ALAT));
ax = -CL * CO;
ay = -CL * SO;
az = -SL;
/* Miscellaneous */
OLDRN = -99999;
}
/*
* Calculate satellite position at DN, TN
*/
void Plan13::satvec()
{
T = (DN - DE) + (TN - TE);//83.848 ; /* Elapsed T since epoch */
DT = DC * T / 2.0; /* Linear drag terms */
KD = 1.0 + 4.0 * DT;
KDP = 1.0 - 7.0 * DT;
M = MA + MM * T * (1.0 - 3.0 * DT); /* Mean anomaly at YR,/ TN */
DR = (int)(M / (2.0 * M_PI)); /* Strip out whole no of revs */
M = M - DR * 2.0 * M_PI; /* M now in range 0 - 2PI */
RN = RV + DR + 1; /* Current orbit number */
/* Solve M = EA - EC * sin(EA) for EA given M, by Newton's method */
EA = M; /* Initail solution */
do
{
C = cos(EA);
S = sin(EA);
DNOM = 1.0 - EC * C;
D = (EA - EC * S - M) / DNOM; /* Change EA to better resolution */
EA = EA - D; /* by this amount until converged */
}
while (fabs(D) > 1.0E-5);
/* Distances */
A = AU * KD;
B = b0 * KD;
RS = A * DNOM;
/* Calculate satellite position and velocity in plane of ellipse */
Sx = A * (C - EC);
Vx = -A * S / DNOM * N0;
Sy = B * S;
Vy = B * C / DNOM * N0;
AP = WP + WD * T * KDP;
CW = cos(AP);
SW = sin(AP);
RAAN = RA + QD * T * KDP;
CQ = cos(RAAN);
SQ = sin(RAAN);
/* Plane -> celestial coordinate transformation, [C] = [RAAN]*[IN]*[AP] */
CXx = CW * CQ - SW * CI * SQ;
CXy = -SW * CQ - CW * CI * SQ;
CXz = SI * SQ;
CYx = CW * SQ + SW * CI * CQ;
CYy = -SW * SQ + CW * CI * CQ;
CYz = -SI * CQ;
CZx = SW * SI;
CZy = CW * SI;
CZz = CI;
/* Compute satellite's position vector, ANTenna axis unit vector */
/* and velocity in celestial coordinates. (Note: Sz = 0, Vz = 0) */
SATx = Sx * CXx + Sy * CXy;
ANTx = ax * CXx + ay * CXy + az * CXz;
VELx = Vx * CXx + Vy * CXy;
SATy = Sx * CYx + Sy * CYy;
ANTy = ax * CYx + ay * CYy + az * CYz;
VELy = Vx * CYx + Vy * CYy;
SATz = Sx * CZx + Sy * CZy;
ANTz = ax * CZx + ay * CZy + az * CZz;
VELz = Vx * CZx + Vy * CZy;
/* Also express SAT, ANT, and VEL in geocentric coordinates */
GHAA = GHAE + WE * T; /* GHA Aries at elaprsed time T */
C = cos(-GHAA);
S = sin(-GHAA);
Sx = SATx * C - SATy * S;
Ax = ANTx * C - ANTy * S;
Vx = VELx * C - VELy * S;
Sy = SATx * S + SATy * C;
Ay = ANTx * S + ANTy * C;
Vy = VELx * S + VELy * C;
Sz = SATz;
Az = ANTz;
Vz = VELz;
}
/*
* Compute and manipulate range/velocity/antenna vectors
*/
void Plan13::rangevec(void)
{
/* Range vector = sat vector - observer vector */
Rx = Sx - Ox;
Ry = Sy - Oy;
Rz = Sz - Oz;
R = sqrt(Rx * Rx + Ry * Ry + Rz * Rz); /* Range Magnitute */
/* Normalize range vector */
Rx = Rx / R;
Ry = Ry / R;
Rz = Rz / R;
U = Rx * Ux + Ry * Uy + Rz * Uz;
E = Rx * Ex + Ry * Ey;
N = Rx * Nx + Ry * Ny + Rz * Nz;
AZ = deg(FNatn(E, N));
EL = deg(asin(U));
/* Solve antenna vector along unit range vector, -r.a = cos(SQ) */
/* SQ = deg(acos(-(Ax * Rx + Ay * Ry + Az * Rz)));
*/
/* Calculate sub-satellite Lat/Lon */
SLON = deg(FNatn(Sy, Sx)); /* Lon, + East */
SLAT = deg(asin(Sz / RS)); /* Lat, + North */
/* Resolve Sat-Obs velocity vector along unit range vector. (VOz = 0) */
RR = (Vx - VOx) * Rx + (Vy - VOy) * Ry + Vz * Rz; /* Range rate, km/sec */
//FR = rxFrequency * (1 - RR / 299792);
dopplerFactor = RR / 299792.0;
int rxDoppler = getDoppler(rxFrequencyLong);
int txDoppler = getDoppler(txFrequencyLong);
rxOutLong = rxFrequencyLong - rxDoppler;
txOutLong = txFrequencyLong + txDoppler;
}
void Plan13::sunvec(void)
{
//TODO
}
int Plan13::getDoppler(unsigned long freq) {
freq = (freq + 50000L) / 100000L;
long factor = dopplerFactor * 1E11;
int digit;
float tally = 0;
for (int x = 4; x > -1; x--) {
digit = freq/pow((float)10,(float)x);
long bare = digit * pow((float)10,(float)x);
freq = freq - bare;
float inBetween = factor * (float(bare) / 1E6);
tally += inBetween;
}
return int( tally + .5); //round
}
int Plan13::getDoppler64(unsigned long freq) {
//long factor = dopplerFactor * 1E11;
uint64_t doppler_sixfour = freq * dopplerFactor;
return (int) doppler_sixfour/1E11;
}
void Plan13::setFrequency(unsigned long rxFrequency_in, unsigned long txFrequency_in) {
rxFrequencyLong = rxFrequency_in;
txFrequencyLong = txFrequency_in;
if (TEST) {
rxFrequencyLong = 435300000L;
txFrequencyLong = 45920000L;
}
}
void Plan13::setLocation(double observer_lon_in, double observer_lat_in, int height) {
observer_lon = observer_lon_in;//-64.375; //0.06; // lon east is positive, west is negative
observer_lat = observer_lat_in;//45.8958; //52.21; //Cambridge UK
observer_height = height; //60m height in meters
}
void Plan13::setTime(int yearIn, int monthIn, int mDayIn, int hourIn, int minIn, int secIn) {
int aYear = yearIn;
int aMonth = monthIn;
int aMday = mDayIn;
int aHour = hourIn;
int aMin = minIn;
int aSec = secIn;
DN = FNday(aYear, aMonth, aMday);
TN = ((float)aHour + ((float)aMin + ((float)aSec/60.0)) /60.0)/24.0;
DN = (long)DN;
}
void Plan13::setElements(double YE_in, double TE_in, double IN_in, double
RA_in, double EC_in, double WP_in, double MA_in, double MM_in,
double M2_in, double RV_in, double ALON_in ) {
YE = YE_in;
TE = TE_in;
IN = IN_in;
RA = RA_in;
EC = EC_in;
WP = WP_in;
MA = MA_in;
MM = MM_in;
M2 = M2_in;
RV = RV_in;
ALON = ALON_in;
}
void Plan13::calculate() {
initSat();
satvec();
rangevec();
}
float *Plan13::footprintOctagon(float slat, float slon) {
static float points[16];
float srad = acos(RE/RS);
float cla= cos(slat);
float sla = sin(slat);
float clo = cos(slon);
float slo = sin(slon);
float sra = sin(srad);
float cra = cos(srad);
for (int i = 0; i < 16; i = i +2) {
float a = 2 * M_PI * i / 8;
float X = cra;
float Y = sra*sin(a);
float Z = sra*cos(a);
float x = X*cla - Z*sla;
float y = Y;
float z = X*sla + Z*cla;
X = x*clo - y*slo;
Y = x*slo + y*clo;
Z = z;
points[i] = FNatn(Y,X);
points[i+1] = asin(Z);
}
return points;
}