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.
@@ -0,0 +1,17 @@
1
+ *.rbc
2
+ *.sassc
3
+ .sass-cache
4
+ capybara-*.html
5
+ .rspec
6
+ /.bundle
7
+ /vendor/bundle
8
+ /log/*
9
+ /tmp/*
10
+ /db/*.sqlite3
11
+ /public/system/*
12
+ /coverage/
13
+ /spec/tmp/*
14
+ **.orig
15
+ rerun.txt
16
+ pickle-email-*.html
17
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,14 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ orbit (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+
10
+ PLATFORMS
11
+ ruby
12
+
13
+ DEPENDENCIES
14
+ orbit!
@@ -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}"
@@ -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
@@ -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}"
@@ -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
@@ -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