orbit 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +14 -0
- data/README.md +16 -0
- data/lib/orbit.rb +27 -0
- data/lib/orbit/demo.rb +152 -0
- data/lib/orbit/eci.rb +130 -0
- data/lib/orbit/geocentric_coordinates.rb +37 -0
- data/lib/orbit/julian.rb +153 -0
- data/lib/orbit/norad_base.rb +261 -0
- data/lib/orbit/norad_sdp4.rb +4 -0
- data/lib/orbit/norad_sgp4.rb +101 -0
- data/lib/orbit/orbit.rb +163 -0
- data/lib/orbit/orbit_globals.rb +128 -0
- data/lib/orbit/satellite.rb +40 -0
- data/lib/orbit/site.rb +115 -0
- data/lib/orbit/tle.rb +127 -0
- data/lib/orbit/topocentric_horizon_coordinates.rb +33 -0
- data/lib/orbit/vector.rb +56 -0
- data/orbit.gemspec +17 -0
- metadata +67 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
orbit
|
2
|
+
=====
|
3
|
+
|
4
|
+
A ruby gem for calculating satellite positions and observation angles, etc.
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
tle = "EYESAT-1 (AO-27)\n1 22825U 93061C 12265.90994989 .00000070 00000-0 44528-4 0 2022\n2 22825 98.5823 207.2528 0008444 2.3056 357.8161 14.29486540990291"
|
9
|
+
s = Orbit::Satellite.new( tle )
|
10
|
+
l = Orbit::Site.new( 33.5, -95.3, 23 ) # Lat, Lng, Elevation Above Sea Level in Meters
|
11
|
+
tc = l.view_angle_to_satellite_at_time( s, Time.now )
|
12
|
+
|
13
|
+
elevation = Orbit::OrbitGlobals.rad_to_deg( tc.elevation )
|
14
|
+
azimuth = Orbit::OrbitGlobals.rad_to_deg( tc.azimuth )
|
15
|
+
|
16
|
+
puts "Elevation: #{elevation}, Azimuth: #{azimuth}"
|
data/lib/orbit.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'orbit/orbit_globals'
|
2
|
+
require 'orbit/geocentric_coordinates'
|
3
|
+
require 'orbit/topocentric_horizon_coordinates'
|
4
|
+
require 'orbit/julian'
|
5
|
+
require 'orbit/norad_base'
|
6
|
+
require 'orbit/norad_sgp4'
|
7
|
+
require 'orbit/norad_sdp4'
|
8
|
+
require 'orbit/eci'
|
9
|
+
require 'orbit/site'
|
10
|
+
require 'orbit/satellite'
|
11
|
+
require 'orbit/tle'
|
12
|
+
require 'orbit/orbit'
|
13
|
+
require 'orbit/vector'
|
14
|
+
require 'date'
|
15
|
+
|
16
|
+
module Orbit
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
# Testing
|
21
|
+
# tle_string = "CUBESAT XI-V (CO-58)\n1 28895U 05043F 12264.16320281 .00000287 00000-0 67805-4 0 2813\n2 28895 97.9003 126.1967 0016829 209.8455 150.1749 14.60744896367522"
|
22
|
+
#
|
23
|
+
# tle = Orbit::Tle.new( tle_string )
|
24
|
+
# o = Orbit::Orbit.new( tle )
|
25
|
+
# eci = o.get_position( ( Time.new.utc.to_f - tle.epoch.to_f ) / 60.0 )
|
26
|
+
# puts eci
|
27
|
+
# puts eci.to_geo
|
data/lib/orbit/demo.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require './orbit_globals.rb'
|
2
|
+
require './geocentric_coordinates.rb'
|
3
|
+
require './topocentric_horizon_coordinates.rb'
|
4
|
+
require './julian.rb'
|
5
|
+
require './norad_base.rb'
|
6
|
+
require './norad_sgp4.rb'
|
7
|
+
require './norad_sdp4.rb'
|
8
|
+
require './eci.rb'
|
9
|
+
require './site.rb'
|
10
|
+
require './tle.rb'
|
11
|
+
require './orbit.rb'
|
12
|
+
require './vector.rb'
|
13
|
+
require 'pp'
|
14
|
+
require 'date'
|
15
|
+
|
16
|
+
# def PrintPosVel(Tle tle)
|
17
|
+
# const int Step = 360;
|
18
|
+
#
|
19
|
+
# Orbit orbit = new Orbit(tle);
|
20
|
+
# List<Eci> coords = new List<Eci>();
|
21
|
+
#
|
22
|
+
# # Calculate position, velocity
|
23
|
+
# # mpe = "minutes past epoch"
|
24
|
+
# for (int mpe = 0; mpe <= (Step * 4); mpe += Step)
|
25
|
+
# {
|
26
|
+
# # Get the position of the satellite at time "mpe".
|
27
|
+
# # The coordinates are placed into the variable "eci".
|
28
|
+
# Eci eci = orbit.GetPosition(mpe);
|
29
|
+
#
|
30
|
+
# # Add the coordinate object to the list
|
31
|
+
# coords.Add(eci);
|
32
|
+
# }
|
33
|
+
#
|
34
|
+
# # Print TLE data
|
35
|
+
# Console.Write("{0}\n", tle.Name);
|
36
|
+
# Console.Write("{0}\n", tle.Line1);
|
37
|
+
# Console.Write("{0}\n", tle.Line2);
|
38
|
+
#
|
39
|
+
# # Header
|
40
|
+
# Console.Write("\n TSINCE X Y Z\n\n");
|
41
|
+
#
|
42
|
+
# # Iterate over each of the ECI position objects pushed onto the
|
43
|
+
# # coordinate list, above, printing the ECI position information
|
44
|
+
# # as we go.
|
45
|
+
# for (int i = 0; i < coords.Count; i++)
|
46
|
+
# {
|
47
|
+
# Eci e = coords[i] as Eci;
|
48
|
+
#
|
49
|
+
# Console.Write("{0,8}.00 {1,16:f8} {2,16:f8} {3,16:f8}\n",
|
50
|
+
# i * Step,
|
51
|
+
# e.Position.X,
|
52
|
+
# e.Position.Y,
|
53
|
+
# e.Position.Z);
|
54
|
+
# }
|
55
|
+
#
|
56
|
+
# Console.Write("\n XDOT YDOT ZDOT\n\n");
|
57
|
+
#
|
58
|
+
# # Iterate over each of the ECI position objects in the coordinate
|
59
|
+
# # list again, but this time print the velocity information.
|
60
|
+
# for (int i = 0; i < coords.Count; i++)
|
61
|
+
# {
|
62
|
+
# Eci e = coords[i] as Eci;
|
63
|
+
#
|
64
|
+
# Console.Write("{0,24:f8} {1,16:f8} {2,16:f8}\n",
|
65
|
+
# e.Velocity.X,
|
66
|
+
# e.Velocity.Y,
|
67
|
+
# e.Velocity.Z);
|
68
|
+
# }
|
69
|
+
# end
|
70
|
+
|
71
|
+
|
72
|
+
tle_string = "OSCAR 3\n"
|
73
|
+
tle_string += "1 01293U 65016F 12263.95608778 +.00000319 +00000-0 +23014-3 0 04606\n"
|
74
|
+
tle_string += "2 01293 070.0701 117.1490 0017260 014.5647 345.5955 14.05174409427057"
|
75
|
+
|
76
|
+
tle1 = Tle.new(tle_string)
|
77
|
+
|
78
|
+
#puts tle1
|
79
|
+
|
80
|
+
# PrintPosVel(tle1);
|
81
|
+
|
82
|
+
# puts ""
|
83
|
+
|
84
|
+
# Test SDP4
|
85
|
+
# tle_string = "AO-7\n"
|
86
|
+
# tle_string += "1 07530U 74089B 12263.39564296 -.00000027 00000-0 10000-3 0 4844\n"
|
87
|
+
# tle_string += "2 07530 101.4097 256.9929 0012088 96.9320 263.3131 12.53591429731853"
|
88
|
+
|
89
|
+
tle2 = Tle.new(tle_string)
|
90
|
+
|
91
|
+
# pp tle2
|
92
|
+
# puts "Epoch: #{tle2.epoch}"
|
93
|
+
# puts "Now: #{Time.now}"
|
94
|
+
|
95
|
+
diff = ( Time.new.to_i - tle2.epoch.to_i ) / 60.0
|
96
|
+
|
97
|
+
# puts "Diff: #{diff}"
|
98
|
+
|
99
|
+
# PrintPosVel(tle2);
|
100
|
+
|
101
|
+
#puts "\nExample output:"
|
102
|
+
|
103
|
+
# Example: Define a location on the earth, then determine the look-angle
|
104
|
+
# to the SDP4 satellite defined above.
|
105
|
+
|
106
|
+
# Create an orbit object using the SDP4 TLE object.
|
107
|
+
orbitSDP4 = Orbit.new(tle2)
|
108
|
+
|
109
|
+
# pp orbitSDP4
|
110
|
+
|
111
|
+
# Get the location of the satellite from the Orbit object. The
|
112
|
+
# earth-centered inertial information is placed into eciSDP4.
|
113
|
+
# Here we ask for the location of the satellite 90 minutes after
|
114
|
+
# the TLE epoch.
|
115
|
+
eciSDP4 = orbitSDP4.get_position( diff )
|
116
|
+
|
117
|
+
eciGeo = eciSDP4.to_geo
|
118
|
+
#pp eciSDP4
|
119
|
+
|
120
|
+
sat_lat = OrbitGlobals.rad_to_deg( eciGeo.latitude_rad )
|
121
|
+
sat_lon = OrbitGlobals.rad_to_deg( eciGeo.longitude_rad )
|
122
|
+
sat_alt = eciGeo.altitude
|
123
|
+
|
124
|
+
if sat_lon > 180
|
125
|
+
sat_lon = 360 - sat_lon
|
126
|
+
sat_lon *= -1
|
127
|
+
end
|
128
|
+
|
129
|
+
puts "Sat Lat: #{sat_lat }"
|
130
|
+
puts "Sat Lon: #{sat_lon}"
|
131
|
+
puts "Sat Ele: #{sat_alt}"
|
132
|
+
|
133
|
+
# Now create a site object. Site objects represent a location on the
|
134
|
+
# surface of the earth. Here we arbitrarily select a point on the
|
135
|
+
# equator.
|
136
|
+
site_me = Site.new(33.5, -112.05, 380) # 0.00 N, 100.00 W, 0 km altitude
|
137
|
+
|
138
|
+
# pp site_me.get_position( eciSDP4.date )
|
139
|
+
|
140
|
+
# Now get the "look angle" from the site to the satellite.
|
141
|
+
# Note that the ECI object "eciSDP4" has a time associated
|
142
|
+
# with the coordinates it contains; this is the time at which
|
143
|
+
# the look angle is valid.
|
144
|
+
topoLook = site_me.get_look_angle(eciSDP4)
|
145
|
+
|
146
|
+
# pp topoLook
|
147
|
+
|
148
|
+
# Print out the results. Note that the Azimuth and Elevation are
|
149
|
+
# stored in the CoordTopo object as radians. Here we convert
|
150
|
+
# to degrees using Rad2Deg()
|
151
|
+
|
152
|
+
puts "AZ: #{OrbitGlobals.rad_to_deg(topoLook.azimuth).to_f} EL: #{OrbitGlobals.rad_to_deg(topoLook.elevation).to_f}"
|
data/lib/orbit/eci.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
module Orbit
|
2
|
+
class Eci
|
3
|
+
attr_accessor :m_Position
|
4
|
+
attr_accessor :m_Velocity
|
5
|
+
attr_accessor :m_Date
|
6
|
+
|
7
|
+
def self.new_with_pos_vel_gmt(pos, vel, gmt)
|
8
|
+
# puts "new_with_pos_vel_gmt #{pos}, #{vel}, #{gmt}"
|
9
|
+
|
10
|
+
eci = self.new
|
11
|
+
|
12
|
+
eci.m_Position = pos
|
13
|
+
eci.m_Velocity = vel
|
14
|
+
eci.m_Date = gmt
|
15
|
+
|
16
|
+
eci
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize( geo = nil, date = nil )
|
20
|
+
if !geo.nil? && !date.nil?
|
21
|
+
setup( geo, date )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup( geo, date )
|
26
|
+
# puts "Eci.setup( geo, #{date} )"
|
27
|
+
mfactor = OrbitGlobals::TWO_PI * (OrbitGlobals::OMEGAE / OrbitGlobals::SEC_PER_DAY)
|
28
|
+
lat = geo.latitude_rad
|
29
|
+
lon = geo.longitude_rad
|
30
|
+
alt = geo.altitude
|
31
|
+
|
32
|
+
# Calculate Local Mean Sidereal Time (theta)
|
33
|
+
theta = OrbitGlobals.time_to_lmst( date, lon )
|
34
|
+
c = 1.0 / Math.sqrt(1.0 + OrbitGlobals::F * (OrbitGlobals::F - 2.0) * OrbitGlobals::sqr(Math.sin(lat)))
|
35
|
+
s = ((1.0 - OrbitGlobals::F) ** 2 ) * c
|
36
|
+
achcp = (OrbitGlobals::XKMPER * c + alt) * Math.cos(lat)
|
37
|
+
|
38
|
+
@m_Date = date
|
39
|
+
@m_Position = Vector.new()
|
40
|
+
|
41
|
+
@m_Position.m_x = achcp * Math.cos(theta) # km
|
42
|
+
@m_Position.m_y = achcp * Math.sin(theta) # km
|
43
|
+
@m_Position.m_z = (OrbitGlobals::XKMPER * s + alt) * Math.sin(lat) # km
|
44
|
+
@m_Position.m_w = Math.sqrt(OrbitGlobals::sqr(@m_Position.m_x) +
|
45
|
+
OrbitGlobals::sqr(@m_Position.m_y) +
|
46
|
+
OrbitGlobals::sqr(@m_Position.m_z)) # range, km
|
47
|
+
|
48
|
+
@m_Velocity = Vector.new()
|
49
|
+
|
50
|
+
@m_Velocity.m_x = -mfactor * @m_Position.m_y # km / sec
|
51
|
+
@m_Velocity.m_y = mfactor * @m_Position.m_x
|
52
|
+
@m_Velocity.m_z = 0.0
|
53
|
+
@m_Velocity.m_w = Math.sqrt(OrbitGlobals::sqr(@m_Velocity.m_x) + # range rate km/sec^2
|
54
|
+
OrbitGlobals::sqr(@m_Velocity.m_y))
|
55
|
+
end
|
56
|
+
|
57
|
+
#endregion
|
58
|
+
|
59
|
+
#region Properties
|
60
|
+
|
61
|
+
def position
|
62
|
+
@m_Position
|
63
|
+
end
|
64
|
+
|
65
|
+
def velocity
|
66
|
+
@m_Velocity
|
67
|
+
end
|
68
|
+
|
69
|
+
def date
|
70
|
+
@m_Date
|
71
|
+
end
|
72
|
+
|
73
|
+
#endregion
|
74
|
+
|
75
|
+
# #####################################/
|
76
|
+
# Return the corresponding geodetic position (based on the current ECI
|
77
|
+
# coordinates/Julian date).
|
78
|
+
# Assumes the earth is an oblate spheroid as defined in WGS '72.
|
79
|
+
# Reference: The 1992 Astronomical Almanac, page K12.
|
80
|
+
# Reference: www.celestrak.com (Dr. TS Kelso)
|
81
|
+
def to_geo
|
82
|
+
# puts "to_geo"
|
83
|
+
theta = OrbitGlobals::actan(@m_Position.m_y, @m_Position.m_x)
|
84
|
+
# puts "theta: #{theta}"
|
85
|
+
# puts "m_Date: #{@m_Date}"
|
86
|
+
lon = (theta - OrbitGlobals.time_to_gmst( @m_Date )) % OrbitGlobals::TWO_PI
|
87
|
+
|
88
|
+
if (lon < 0.0)
|
89
|
+
lon += OrbitGlobals::TWO_PI # "wrap" negative modulo
|
90
|
+
end
|
91
|
+
|
92
|
+
r = Math.sqrt(OrbitGlobals.sqr(@m_Position.m_x) + OrbitGlobals.sqr(@m_Position.m_y))
|
93
|
+
e2 = OrbitGlobals::F * (2.0 - OrbitGlobals::F)
|
94
|
+
lat = OrbitGlobals.actan(@m_Position.m_z, r)
|
95
|
+
|
96
|
+
delta = 1.0e-07
|
97
|
+
phi = nil
|
98
|
+
c = nil
|
99
|
+
|
100
|
+
begin
|
101
|
+
phi = lat
|
102
|
+
c = 1.0 / Math.sqrt(1.0 - e2 * OrbitGlobals::sqr(Math.sin(phi)))
|
103
|
+
lat = OrbitGlobals::actan(@m_Position.m_z + OrbitGlobals::XKMPER * c * e2 * Math.sin(phi), r)
|
104
|
+
end while ((lat - phi).abs > delta)
|
105
|
+
|
106
|
+
alt = r / Math.cos(lat) - OrbitGlobals::XKMPER * c
|
107
|
+
|
108
|
+
return GeocentricCoordinates.new(lat, lon, alt) # radians, radians, kilometers
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
#/ <summary>
|
113
|
+
#/ Scale the position vector by the given scaling factor.
|
114
|
+
#/ </summary>
|
115
|
+
#/ <param name="factor">The scaling factor.</param>
|
116
|
+
def scale_pos_vector(factor)
|
117
|
+
@m_Position.mul(factor)
|
118
|
+
end
|
119
|
+
|
120
|
+
#/ <summary>
|
121
|
+
#/ Scales the velocity vector by the given scaling factor.
|
122
|
+
#/ </summary>
|
123
|
+
#/ <param name="factor">The scaling factor.</param>
|
124
|
+
def scale_vel_vector(factor)
|
125
|
+
@m_Velocity.mul(factor)
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Orbit
|
2
|
+
class GeocentricCoordinates
|
3
|
+
attr_accessor :latitude_rad
|
4
|
+
attr_accessor :longitude_rad
|
5
|
+
attr_accessor :altitude
|
6
|
+
|
7
|
+
def initialize( lat = nil, lon = nil, alt = 0 )
|
8
|
+
@latitude_rad = lat
|
9
|
+
@longitude_rad = lon
|
10
|
+
@altitude = alt
|
11
|
+
end
|
12
|
+
|
13
|
+
def latitude
|
14
|
+
OrbitGlobals.rad_to_deg( @latitude_rad )
|
15
|
+
end
|
16
|
+
|
17
|
+
def longitude
|
18
|
+
l = OrbitGlobals.rad_to_deg( @longitude_rad )
|
19
|
+
|
20
|
+
if l > 180
|
21
|
+
l = 360 - l
|
22
|
+
l *= -1
|
23
|
+
end
|
24
|
+
|
25
|
+
l
|
26
|
+
end
|
27
|
+
|
28
|
+
def altitude
|
29
|
+
@altitude * 1000.0 #Convert from km to m
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
"Lat: #{latitude}, Lng: #{longitude}, Alt: #{altitude}"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/orbit/julian.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
class Julian
|
2
|
+
# EPOCH_JAN0_12H_1900 = 2415020.0 # Dec 31.5 1899 = Dec 31 1899 12h UTC
|
3
|
+
# EPOCH_JAN1_00H_1900 = 2415020.5 # Jan 1.0 1900 = Jan 1 1900 00h UTC
|
4
|
+
# EPOCH_JAN1_12H_1900 = 2415021.0 # Jan 1.5 1900 = Jan 1 1900 12h UTC
|
5
|
+
# EPOCH_JAN1_12H_2000 = 2451545.0 # Jan 1.5 2000 = Jan 1 2000 12h UTC
|
6
|
+
#
|
7
|
+
# @m_Date # Julian date
|
8
|
+
# @m_Year # Year including century
|
9
|
+
# @m_Day # Day of year, 1.0 = Jan 1 00h
|
10
|
+
#
|
11
|
+
# #region Construction
|
12
|
+
#
|
13
|
+
# # ##################################/
|
14
|
+
# # Create a Julian date object from a DateTime object. The time
|
15
|
+
# # contained in the DateTime object is assumed to be UTC.
|
16
|
+
# public Julian(DateTime dt)
|
17
|
+
# {
|
18
|
+
# double day = dt.DayOfYear +
|
19
|
+
# (dt.Hour +
|
20
|
+
# ((dt.Minute +
|
21
|
+
# ((dt.Second + (dt.Millisecond / 1000.0)) / 60.0)) / 60.0)) / 24.0
|
22
|
+
#
|
23
|
+
# Initialize(dt.Year, day)
|
24
|
+
# }
|
25
|
+
#
|
26
|
+
# # ##################################/
|
27
|
+
# # Create a Julian date object from a year and day of year.
|
28
|
+
# # The year is given with the century (i.e. 2001).
|
29
|
+
# # The integer part of the day value is the day of year, with 1 meaning
|
30
|
+
# # January 1.
|
31
|
+
# # The fractional part of the day value is the fractional portion of
|
32
|
+
# # the day.
|
33
|
+
# # Examples:
|
34
|
+
# # day = 1.0 Jan 1 00h
|
35
|
+
# # day = 1.5 Jan 1 12h
|
36
|
+
# # day = 2.0 Jan 2 00h
|
37
|
+
# public Julian(int year, double day)
|
38
|
+
# {
|
39
|
+
# Initialize(year, day)
|
40
|
+
# }
|
41
|
+
#
|
42
|
+
# #endregion
|
43
|
+
#
|
44
|
+
# #region Properties
|
45
|
+
#
|
46
|
+
# public double Date { get { return @m_Date } }
|
47
|
+
#
|
48
|
+
# public double FromJan1_00h_1900() { return @m_Date - EPOCH_JAN1_00H_1900 }
|
49
|
+
# public double FromJan1_12h_1900() { return @m_Date - EPOCH_JAN1_12H_1900 }
|
50
|
+
# public double FromJan0_12h_1900() { return @m_Date - EPOCH_JAN0_12H_1900 }
|
51
|
+
# public double FromJan1_12h_2000() { return @m_Date - EPOCH_JAN1_12H_2000 }
|
52
|
+
#
|
53
|
+
# #endregion
|
54
|
+
#
|
55
|
+
# # ##################################/
|
56
|
+
# public TimeSpan Diff(Julian date)
|
57
|
+
# {
|
58
|
+
# const double TICKS_PER_DAY = 8.64e11 # 1 tick = 100 nanoseconds
|
59
|
+
# return new TimeSpan((long)((m_Date - date.m_Date) * TICKS_PER_DAY))
|
60
|
+
# }
|
61
|
+
#
|
62
|
+
# # ##################################/
|
63
|
+
# # Initialize the Julian object.
|
64
|
+
# # The first day of the year, Jan 1, is day 1.0. Noon on Jan 1 is
|
65
|
+
# # represented by the day value of 1.5, etc.
|
66
|
+
# protected void Initialize(int year, double day)
|
67
|
+
# {
|
68
|
+
# # Arbitrary years used for error checking
|
69
|
+
# if (year < 1900 || year > 2100)
|
70
|
+
# {
|
71
|
+
# throw new ArgumentOutOfRangeException("year")
|
72
|
+
# }
|
73
|
+
#
|
74
|
+
# # The last day of a leap year is day 366
|
75
|
+
# if (day < 1.0 || day >= 367.0)
|
76
|
+
# {
|
77
|
+
# throw new ArgumentOutOfRangeException("day")
|
78
|
+
# }
|
79
|
+
#
|
80
|
+
# @m_Year = year
|
81
|
+
# @m_Day = day
|
82
|
+
#
|
83
|
+
# # Now calculate Julian date
|
84
|
+
# # Ref: "Astronomical Formulae for Calculators", Jean Meeus, pages 23-25
|
85
|
+
#
|
86
|
+
# year--
|
87
|
+
#
|
88
|
+
# # Centuries are not leap years unless they divide by 400
|
89
|
+
# int A = (year / 100)
|
90
|
+
# int B = 2 - A + (A / 4)
|
91
|
+
#
|
92
|
+
# double NewYears = (int)(365.25 * year) +
|
93
|
+
# (int)(30.6001 * 14) +
|
94
|
+
# 1720994.5 + B
|
95
|
+
#
|
96
|
+
# @m_Date = NewYears + day
|
97
|
+
# }
|
98
|
+
#
|
99
|
+
# # ##################################/
|
100
|
+
# # ToGmst()
|
101
|
+
# # Calculate Greenwich Mean Sidereal Time for the Julian date. The
|
102
|
+
# # return value is the angle, in radians, measuring eastward from the
|
103
|
+
# # Vernal Equinox to the prime meridian. This angle is also referred
|
104
|
+
# # to as "ThetaG" (Theta GMST).
|
105
|
+
# #
|
106
|
+
# # References:
|
107
|
+
# # The 1992 Astronomical Almanac, page B6.
|
108
|
+
# # Explanatory Supplement to the Astronomical Almanac, page 50.
|
109
|
+
# # Orbital Coordinate Systems, Part III, Dr. T.S. Kelso,
|
110
|
+
# # Satellite Times, Nov/Dec 1995
|
111
|
+
# public double ToGmst()
|
112
|
+
# {
|
113
|
+
# double UT = (m_Date + 0.5) % 1.0
|
114
|
+
# double TU = (FromJan1_12h_2000() - UT) / 36525.0
|
115
|
+
#
|
116
|
+
# double GMST = 24110.54841 + TU *
|
117
|
+
# (8640184.812866 + TU * (0.093104 - TU * 6.2e-06))
|
118
|
+
#
|
119
|
+
# GMST = (GMST + Globals.SecPerDay * Globals.OmegaE * UT) % Globals.SecPerDay
|
120
|
+
#
|
121
|
+
# if (GMST < 0.0)
|
122
|
+
# {
|
123
|
+
# GMST += Globals.SecPerDay # "wrap" negative modulo value
|
124
|
+
# }
|
125
|
+
#
|
126
|
+
# return (Globals.TwoPi * (GMST / Globals.SecPerDay))
|
127
|
+
# }
|
128
|
+
#
|
129
|
+
# # ##################################/
|
130
|
+
# # ToLmst()
|
131
|
+
# # Calculate Local Mean Sidereal Time for given longitude (for this date).
|
132
|
+
# # The longitude is assumed to be in radians measured west from Greenwich.
|
133
|
+
# # The return value is the angle, in radians, measuring eastward from the
|
134
|
+
# # Vernal Equinox to the given longitude.
|
135
|
+
# public double ToLmst(double lon)
|
136
|
+
# {
|
137
|
+
# return (ToGmst() + lon) % Globals.TwoPi
|
138
|
+
# }
|
139
|
+
#
|
140
|
+
# # ##################################/
|
141
|
+
# # ToTime()
|
142
|
+
# # Convert to type DateTime. The resulting value is UTC.
|
143
|
+
# public DateTime ToTime()
|
144
|
+
# {
|
145
|
+
# # Jan 1
|
146
|
+
# DateTime dt = new DateTime(m_Year, 1, 1)
|
147
|
+
#
|
148
|
+
# # @m_Day = 1 = Jan1
|
149
|
+
# dt = dt.AddDays(m_Day - 1.0)
|
150
|
+
#
|
151
|
+
# return dt
|
152
|
+
# }
|
153
|
+
end
|