Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More accurate look angles #9

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ Calc GST given year, month, day, hour, minute and second
#### func JDay

```go
func JDay(year, mon, day, hr, min, sec int) float64
func JDay(year, mon, day, hr, min int, sec float64) (float64, float64)
```
Calc julian date given year, month, day, hour, minute and second the julian date
is defined by each elapsed day since noon, jan 1, 4713 bc.

#### func Propagate

```go
func Propagate(sat Satellite, year int, month int, day, hours, minutes, seconds int) (position, velocity Vector3)
func Propagate(sat Satellite, jDay, jF float64) (position, velocity Vector3)
```
Calculates position and velocity vectors for given time

Expand Down Expand Up @@ -96,7 +96,7 @@ Holds an azimuth, elevation and range
#### func ECIToLookAngles

```go
func ECIToLookAngles(eciSat Vector3, obsCoords LatLong, obsAlt, jday float64) (lookAngles LookAngles)
func ECIToLookAngles(eciSat Vector3, obsCoords LatLong, obsAlt, jday float64, gravConst GravConst) (lookAngles LookAngles)
```
Calculate look angles for given satellite position and observer position obsAlt
in km Reference: http://celestrak.com/columns/v02n02/
Expand Down
49 changes: 30 additions & 19 deletions conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ func days2mdhms(year int64, epochDays float64) (mon, day, hr, min, sec float64)

// Calc julian date given year, month, day, hour, minute and second
// the julian date is defined by each elapsed day since noon, jan 1, 4713 bc.
func JDay(year, mon, day, hr, min, sec int) float64 {
return (367.0*float64(year) - math.Floor((7*(float64(year)+math.Floor((float64(mon)+9)/12.0)))*0.25) + math.Floor(275*float64(mon)/9.0) + float64(day) + 1721013.5 + ((float64(sec)/60.0+float64(min))/60.0+float64(hr))/24.0)
func JDay(year, mon, day, hr, minute int, sec float64) (float64, float64) {
jd := (367.0*float64(year) - math.Floor(7*(float64(year)+math.Floor((float64(mon)+9)/12.0))*0.25) + math.Floor(275*float64(mon)/9.0) + float64(day) + 1721013.5)
fr := (sec + float64(minute)*60.0 + float64(hr)*3600.0) / 86400.0
return jd, fr
}

// this function finds the greenwich sidereal time (iau-82)
Expand All @@ -57,9 +59,9 @@ func gstime(jdut1 float64) (temp float64) {
}

// Calc GST given year, month, day, hour, minute and second
func GSTimeFromDate(year, mon, day, hr, min, sec int) float64 {
jDay := JDay(year, mon, day, hr, min, sec)
return gstime(jDay)
func GSTimeFromDate(year, mon, day, hr, min int, sec float64) float64 {
jDay, jf := JDay(year, mon, day, hr, min, sec)
return gstime(jDay + jf)
}

// Convert Earth Centered Inertial coordinated into equivalent latitude, longitude, altitude and velocity.
Expand Down Expand Up @@ -125,13 +127,17 @@ func ThetaG_JD(jday float64) (ret float64) {

// Convert latitude, longitude and altitude into equivalent Earth Centered Intertial coordinates
// Reference: The 1992 Astronomical Almanac, page K11.
func LLAToECI(obsCoords LatLong, alt, jday float64) (eciObs Vector3) {
re := 6378.137
func LLAToECI(obsCoords LatLong, alt, jday float64, gravConst GravConst) (eciObs Vector3) {
theta := math.Mod(ThetaG_JD(jday)+obsCoords.Longitude, TWOPI)
r := (re + alt) * math.Cos(obsCoords.Latitude)
eciObs.X = r * math.Cos(theta)
eciObs.Y = r * math.Sin(theta)
eciObs.Z = (re + alt) * math.Sin(obsCoords.Latitude)
latSin := math.Sin(obsCoords.Latitude)
latCos := math.Cos(obsCoords.Latitude)
c := 1 / math.Sqrt(1+gravConst.f*(gravConst.f-2)*latSin*latSin)
sq := c * (1 - gravConst.f) * (1 - gravConst.f)
achcp := (gravConst.radiusearthkm*c + alt) * latCos

eciObs.X = achcp * math.Cos(theta)
eciObs.Y = achcp * math.Sin(theta)
eciObs.Z = (gravConst.radiusearthkm*sq + alt) * latSin
return
}

Expand All @@ -147,27 +153,32 @@ func ECIToECEF(eciCoords Vector3, gmst float64) (ecfCoords Vector3) {
// Calculate look angles for given satellite position and observer position
// obsAlt in km
// Reference: http://celestrak.com/columns/v02n02/
func ECIToLookAngles(eciSat Vector3, obsCoords LatLong, obsAlt, jday float64) (lookAngles LookAngles) {
func ECIToLookAngles(eciSat Vector3, obsCoords LatLong, obsAlt, jday float64, gravConst GravConst) (lookAngles LookAngles) {
theta := math.Mod(ThetaG_JD(jday)+obsCoords.Longitude, 2*math.Pi)
obsPos := LLAToECI(obsCoords, obsAlt, jday)
obsPos := LLAToECI(obsCoords, obsAlt, jday, gravConst)

rx := eciSat.X - obsPos.X
ry := eciSat.Y - obsPos.Y
rz := eciSat.Z - obsPos.Z

top_s := math.Sin(obsCoords.Latitude)*math.Cos(theta)*rx + math.Sin(obsCoords.Latitude)*math.Sin(theta)*ry - math.Cos(obsCoords.Latitude)*rz
top_e := -math.Sin(theta)*rx + math.Cos(theta)*ry
top_z := math.Cos(obsCoords.Latitude)*math.Cos(theta)*rx + math.Cos(obsCoords.Latitude)*math.Sin(theta)*ry + math.Sin(obsCoords.Latitude)*rz
latSin := math.Sin(obsCoords.Latitude)
latCos := math.Cos(obsCoords.Latitude)
thetaSin := math.Sin(theta)
thetaCos := math.Cos(theta)

lookAngles.Az = math.Atan(-top_e / top_s)
if top_s > 0 {
topS := latSin*thetaCos*rx + latSin*thetaSin*ry - latCos*rz
topE := -thetaSin*rx + thetaCos*ry
topZ := latCos*thetaCos*rx + latCos*thetaSin*ry + latSin*rz

lookAngles.Az = math.Atan(-topE / topS)
if topS > 0 {
lookAngles.Az = lookAngles.Az + math.Pi
}
if lookAngles.Az < 0 {
lookAngles.Az = lookAngles.Az + 2*math.Pi
}
lookAngles.Rg = math.Sqrt(rx*rx + ry*ry + rz*rz)
lookAngles.El = math.Asin(top_z / lookAngles.Rg)
lookAngles.El = math.Asin(topZ / lookAngles.Rg)

return
}
5 changes: 4 additions & 1 deletion gravity.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

// Holds variables that are dependent upon selected gravity model
type GravConst struct {
mu, radiusearthkm, xke, tumin, j2, j3, j4, j3oj2 float64
mu, radiusearthkm, xke, tumin, j2, j3, j4, j3oj2, f float64
}

// Returns a GravConst with correct information on requested model provided through the name parameter
Expand All @@ -22,6 +22,7 @@ func getGravConst(name string) (grav GravConst) {
grav.j3 = -0.00000253881
grav.j4 = -0.00000165597
grav.j3oj2 = grav.j3 / grav.j2
grav.f = 1 / 298.26
case "wgs72":
grav.mu = 398600.8
grav.radiusearthkm = 6378.135
Expand All @@ -31,6 +32,7 @@ func getGravConst(name string) (grav GravConst) {
grav.j3 = -0.00000253881
grav.j4 = -0.00000165597
grav.j3oj2 = grav.j3 / grav.j2
grav.f = 1 / 298.26
case "wgs84":
grav.mu = 398600.5
grav.radiusearthkm = 6378.137
Expand All @@ -40,6 +42,7 @@ func getGravConst(name string) (grav GravConst) {
grav.j3 = -0.00000253215306
grav.j4 = -0.00000161098761
grav.j3oj2 = grav.j3 / grav.j2
grav.f = 1 / 298.257223563
default:
log.Fatal(name, "is not a valid gravity model")
}
Expand Down
4 changes: 2 additions & 2 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ func TLEToSat(line1, line2 string, gravconst string) Satellite {

mon, day, hr, min, sec := days2mdhms(year, sat.epochdays)

sat.jdsatepoch = JDay(int(year), int(mon), int(day), int(hr), int(min), int(sec))
sat.jdsatepoch, sat.jdsatepochF = JDay(int(year), int(mon), int(day), int(hr), int(min), sec)

sgp4init(&opsmode, sat.jdsatepoch-2433281.5, &sat)
sgp4init(&opsmode, sat.jdsatepoch+sat.jdsatepochF-2433281.5, &sat)

return sat
}
Expand Down
7 changes: 4 additions & 3 deletions satellite.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ type Satellite struct {
ErrorStr string
whichconst GravConst

epochyr int64
epochdays float64
jdsatepoch float64
epochyr int64
epochdays float64
jdsatepoch float64
jdsatepochF float64

ndot float64
nddot float64
Expand Down
30 changes: 28 additions & 2 deletions satellite_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"testing"
"time"
)

func TestSatellite(t *testing.T) {
Expand All @@ -20,6 +21,31 @@ type Result struct {
}

var _ = Describe("go-satellite", func() {

Describe("LookAngles", func() {

It("should return correct observer look angles for given ISS#22825 at 2020-05-23T20:23:37", func() {
sat := TLEToSat("1 25544U 98067A 20140.34419374 -.00000374 00000-0 13653-5 0 9990", "2 25544 51.6433 131.2277 0001338 330.3524 173.1622 15.49372617227549", "wgs72")

time := time.Date(2020, 5, 23, 20, 23, 37, 0, time.UTC)
year, month, day := time.Date()
hour, min, sec := time.Clock()

jDay, jF := JDay(year, int(month), day, hour, min, float64(sec))
pos, _ := Propagate(sat, jDay, jF)

latLong := LatLong{
Latitude: DEG2RAD * 55.6167,
Longitude: DEG2RAD * 12.6500}
alt := 0.005

angles := ECIToLookAngles(pos, latLong, alt, jDay+jF, sat.whichconst)

Expect(angles.El * RAD2DEG).To(Equal(42.06164214709452))
Expect(angles.Az * RAD2DEG).To(Equal(181.2902281625632))
})
})

Describe("ParseTLE", func() {
It("should return correctly parsed values for given ISS#25544", func() {
sat := ParseTLE("1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927", "2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537", "wgs84")
Expand Down Expand Up @@ -221,7 +247,7 @@ var _ = Describe("go-satellite", func() {
PropagationTestCase{
line1: "1 23599U 95029B 06171.76535463 .00085586 12891-6 12956-2 0 2905",
line2: "2 23599 6.9327 0.2849 5782022 274.4436 25.2425 4.47796565123555",
grav: "wgs72",
grav: "wgs72",
testData: `0.00000000 9892.63794341 35.76144969 -1.08228838 3.556643237 6.456009375 0.783610890
20.00000000 11931.95642997 7340.74973750 886.46365987 0.308329116 5.532328972 0.672887281
40.00000000 11321.71039205 13222.84749156 1602.40119049 -1.151973982 4.285810871 0.521919425
Expand Down Expand Up @@ -296,4 +322,4 @@ func propagationTest(testCase PropagationTestCase) {
})
})
}
}
}
7 changes: 3 additions & 4 deletions sgp4.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,9 @@ func initl(satn int64, grav GravConst, ecco, epoch, inclo, noIn float64, methodI
}

// Calculates position and velocity vectors for given time
func Propagate(sat Satellite, year int, month int, day, hours, minutes, seconds int) (position, velocity Vector3) {
j := JDay(year, month, day, hours, minutes, seconds)
m := (j - sat.jdsatepoch) * 1440
return sgp4(&sat, m)
func Propagate(sat Satellite, jDay, jF float64) (position, velocity Vector3) {
tsince := (jDay-sat.jdsatepoch)*1440 + (jF-sat.jdsatepochF)*1440
return sgp4(&sat, tsince)
}

// this procedure is the sgp4 prediction model from space command. this is an updated and combined version of sgp4 and sdp4, which were originally published separately in spacetrack report #3. this version follows the methodology from the aiaa paper (2006) describing the history and development of the code.
Expand Down