korba 0.8.0 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5dc3a4abbdd189f4388673e3960a615940a4feb68c359cce37a29ff47fa7efcb
4
- data.tar.gz: c312b2be706e321242d0f4c762134ad82ea681348e5631ec36a13ed91c36db11
3
+ metadata.gz: e2c09e63837c73c4e90d0154d64f821f9ce483dff5658cc4260010ccdc34d867
4
+ data.tar.gz: a5cff68159d77759daa51b9ab3b6fe338bbe5f267273074b81f81da3c4abf6ee
5
5
  SHA512:
6
- metadata.gz: f0cc15b9fa16cec125d3902e3b52e1336acf4246c72860049955c32456d1eaa0e56a2fd99953477d36141da23b3b8696ed0b8a611c80bc51e1249368c7f07f84
7
- data.tar.gz: ffcbf5ed21c86f069d83276227c9db29e5b59d257d1630f5a1fb15eaff0550b881b1e7753fcc551ce6c647c49faf2bc84ee52efb015fbd9ad8efa9355b2da6ad
6
+ metadata.gz: 89c6ea54a2fc3e6499bc93b3cc6079269e3f8b31b3655339cd7d8ea9a82165bb46507fe8db71fbba2342559d466ff6df32a3afe435a679ab77631046674bb089
7
+ data.tar.gz: 750c1c7f2b926cc1b070550bfd6e4f18f359b0ea9658a92873a2ef3ade1ee30b63a20374490f22088d6fdf14d98fcf471cfb883900ed0476240f8a9ccfe96366
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.9.1] - 2026-05-06
2
+
3
+ - FIX: TLE epoch handling: convert to datetime and adjust SGP4 propagation epoch
4
+ - feat: Implement Orbit Model
5
+
6
+ ## [0.9.0] - 2026-05-04
7
+
8
+ - Enable orbit propagation with Keplerian elements
9
+
1
10
  ## [0.8.0] - 2026-04-19
2
11
 
3
12
  - Enable orbit propagation using 4th-order Runge-Kutta
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Korba
4
+ module Converter
5
+ def tle_to_cartesian(tle)
6
+ return nil if tle.nil?
7
+
8
+ tle.to_car
9
+ end
10
+
11
+ def tle_to_keplerian(tle)
12
+ return nil if tle.nil?
13
+
14
+ tle.to_kep
15
+ end
16
+
17
+ def keplerian_to_cartesian(keplerian)
18
+ return nil if keplerian.nil?
19
+
20
+ keplerian.to_car
21
+ end
22
+
23
+ def cartesian_to_keplerian(cartesian)
24
+ return nil if cartesian.nil?
25
+
26
+ cartesian.to_kep
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Korba
4
+ class Orbit
5
+ include Converter
6
+ attr_reader :epoch, :name, :tle
7
+
8
+ class << self
9
+ def from_tle(tle = nil, type: :string)
10
+ tle = Tle.new(tle, type:)
11
+ new(tle:, epoch: tle.epoch_datetime, name: tle.object_name)
12
+ end
13
+
14
+ def from_keplerian(keplerian)
15
+ new(keplerian:, epoch: keplerian.epoch, name: keplerian.object_name)
16
+ end
17
+
18
+ def from_cartesian(cartesian)
19
+ new(cartesian:, epoch: cartesian.epoch, name: cartesian.object_name)
20
+ end
21
+ end
22
+
23
+ def initialize(keplerian: nil, cartesian: nil, tle: nil, epoch: nil, name: nil)
24
+ @keplerian = keplerian
25
+ @cartesian = cartesian
26
+ @tle = tle
27
+ @epoch = epoch
28
+ @name = name
29
+ end
30
+
31
+ def cartesian
32
+ @cartesian ||= tle_to_cartesian(tle) || keplerian_to_cartesian(keplerian)
33
+ end
34
+
35
+ def keplerian
36
+ @keplerian ||= tle_to_keplerian(tle) || cartesian_to_keplerian(cartesian)
37
+ end
38
+
39
+ def propagate(type:, seconds_after_epoch:, disable_j2: false)
40
+ case type.to_sym
41
+ in :sgp4
42
+ propagated_cartesian = tle.propagate_to(seconds_after_epoch / 60.0)
43
+ return Orbit.from_cartesian(propagated_cartesian)
44
+ in :rk4
45
+ propagator = Propagator::Rk4.new(cartesian, disable_j2:)
46
+ propagated_cartesian = propagator.propagate(seconds_after_epoch)
47
+ return Orbit.from_cartesian(propagated_cartesian)
48
+ in :kepler
49
+ propagator = Propagator::Kepler.new(keplerian, disable_j2:)
50
+ propagated_keplerian = propagator.propagate(seconds_after_epoch)
51
+ return Orbit.from_keplerian(propagated_keplerian)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -7,6 +7,10 @@ module Korba
7
7
  (Constant::GME / (mean_motion * 2.0 * Math::PI / 86400.0) ** 2.0) ** (1.0 / 3.0)
8
8
  end
9
9
 
10
+ def period
11
+ 2.0 * Math::PI * Math.sqrt(semi_major_axis ** 3 / Constant::GME)
12
+ end
13
+
10
14
  def height_at_perigee
11
15
  semi_major_axis * (1 - eccentricity) - Constant::EARTH_RADIUS
12
16
  end
@@ -50,10 +54,7 @@ module Korba
50
54
  end
51
55
 
52
56
  def normalize_rad(rad)
53
- rad = rad + 2.0 * Math::PI if rad < 0
54
- normalize_rad = rad > 2.0 * Math::PI ? rad - 2.0 * Math::PI : rad
55
- normalize_rad(normalize_rad) if normalize_rad != rad
56
- normalize_rad
57
+ rad % (2.0 * Math::PI)
57
58
  end
58
59
 
59
60
  def rad_to_deg(rad)
@@ -62,10 +63,7 @@ module Korba
62
63
  end
63
64
 
64
65
  def normalize_deg(deg)
65
- deg = deg + 360.0 if deg < 0
66
- normalized_deg = deg > 360.0 ? deg - 360.0 : deg
67
- normalize_deg(normalized_deg) if normalized_deg != deg
68
- normalized_deg
66
+ deg % 360.0
69
67
  end
70
68
  end
71
69
  end
@@ -0,0 +1,54 @@
1
+ module Korba
2
+ module Propagator
3
+ class Kepler
4
+ include OrbitUtils
5
+
6
+ def initialize(initial_kep, disable_j2: false)
7
+ @initial_kep = initial_kep
8
+ @disable_j2 = disable_j2
9
+ end
10
+
11
+ def propagate(seconds_after_epoch)
12
+ mean_motion = Math.sqrt(Constant::GME / @initial_kep.semi_major_axis ** 3)
13
+ mean_anomaly = rad_to_deg(deg_to_rad(@initial_kep.mean_anomaly) + mean_motion * seconds_after_epoch)
14
+
15
+ Kep.new(
16
+ object_name: @initial_kep.object_name,
17
+ epoch: @initial_kep.epoch + seconds_after_epoch,
18
+ semi_major_axis: @initial_kep.semi_major_axis,
19
+ eccentricity: @initial_kep.eccentricity,
20
+ inclination: @initial_kep.inclination,
21
+ ra_of_asc_node: ra_of_asc_node(seconds_after_epoch),
22
+ arg_of_pericenter: arg_of_pericenter(seconds_after_epoch),
23
+ mean_anomaly: mean_anomaly,
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ def ra_of_asc_node(seconds_after_epoch)
30
+ return @initial_kep.ra_of_asc_node if @disable_j2
31
+
32
+ rad_to_deg(deg_to_rad(@initial_kep.ra_of_asc_node) + ra_of_asc_node_dot_radian * seconds_after_epoch)
33
+ end
34
+
35
+ def ra_of_asc_node_dot_radian
36
+ -(3 * Math::PI * Constant::J2 *
37
+ (Constant::EARTH_RADIUS / @initial_kep.semi_major_axis * (1 - @initial_kep.eccentricity ** 2)) ** 2 *
38
+ Math.cos(deg_to_rad(@initial_kep.inclination))) / @initial_kep.period
39
+ end
40
+
41
+ def arg_of_pericenter(seconds_after_epoch)
42
+ return @initial_kep.arg_of_pericenter if @disable_j2
43
+
44
+ rad_to_deg(deg_to_rad(@initial_kep.arg_of_pericenter) + arg_of_pericenter_dot_radian * seconds_after_epoch)
45
+ end
46
+
47
+ def arg_of_pericenter_dot_radian
48
+ (3 * Math::PI / 2.0 * Constant::J2 *
49
+ (Constant::EARTH_RADIUS / @initial_kep.semi_major_axis * (1 - @initial_kep.eccentricity ** 2)) ** 2 *
50
+ (5 * (Math.cos(deg_to_rad(@initial_kep.inclination)) ** 2) - 1)) / @initial_kep.period
51
+ end
52
+ end
53
+ end
54
+ end
data/lib/korba/tle.rb CHANGED
@@ -25,7 +25,7 @@ module Korba
25
25
 
26
26
  def to_kep
27
27
  Kep.new(object_name:,
28
- epoch:,
28
+ epoch: epoch_datetime,
29
29
  semi_major_axis:,
30
30
  eccentricity:,
31
31
  inclination:,
@@ -47,7 +47,7 @@ module Korba
47
47
  @sgp4Error = @element_set_record.error
48
48
  r = r.map { _1 * 1000 }
49
49
  v = v.map { _1 * 1000 }
50
- Car.new(object_name:, epoch:, x: r[0], y: r[1], z: r[2], vx: v[0], vy: v[1], vz: v[2])
50
+ Car.new(object_name:, epoch: epoch_datetime + minutesAfterEpoch * 60.0, x: r[0], y: r[1], z: r[2], vx: v[0], vy: v[1], vz: v[2])
51
51
  end
52
52
 
53
53
  private
data/lib/korba/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Korba
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.1"
5
5
  end
data/lib/korba.rb CHANGED
@@ -6,10 +6,13 @@ require_relative "korba/tle"
6
6
  require_relative "korba/kep"
7
7
  require_relative "korba/car"
8
8
  require_relative "korba/orbit_utils"
9
+ require_relative "korba/converter"
10
+ require_relative "korba/orbit"
9
11
  require_relative "korba/keplers_equation"
10
12
  require_relative "korba/sgp4/elset_rec"
11
13
  require_relative "korba/propagator/sgp4"
12
14
  require_relative "korba/propagator/rk4"
15
+ require_relative "korba/propagator/kepler"
13
16
 
14
17
  module Korba
15
18
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: korba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - kk0000-kk
@@ -23,9 +23,12 @@ files:
23
23
  - lib/korba.rb
24
24
  - lib/korba/car.rb
25
25
  - lib/korba/constant.rb
26
+ - lib/korba/converter.rb
26
27
  - lib/korba/kep.rb
27
28
  - lib/korba/keplers_equation.rb
29
+ - lib/korba/orbit.rb
28
30
  - lib/korba/orbit_utils.rb
31
+ - lib/korba/propagator/kepler.rb
29
32
  - lib/korba/propagator/rk4.rb
30
33
  - lib/korba/propagator/sgp4.rb
31
34
  - lib/korba/sgp4/elset_rec.rb
@@ -34,7 +37,8 @@ files:
34
37
  - mise.toml
35
38
  - sig/korba.rbs
36
39
  homepage: https://github.com/kk0000-kk/korba
37
- licenses: []
40
+ licenses:
41
+ - MIT
38
42
  metadata:
39
43
  allowed_push_host: https://rubygems.org
40
44
  homepage_uri: https://github.com/kk0000-kk/korba