sekki24 1.0.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,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sekki24
4
+ module Names
5
+ Definition = Struct.new(
6
+ :key,
7
+ :name_ja,
8
+ :reading,
9
+ :name_en,
10
+ :name_zh,
11
+ :longitude,
12
+ keyword_init: true
13
+ )
14
+
15
+ TERMS = [
16
+ [:risshun, "立春", "りっしゅん", "Start of spring", "立春", 315],
17
+ [:usui, "雨水", "うすい", "Rain water", "雨水", 330],
18
+ [:keichitsu, "啓蟄", "けいちつ", "Awakening of insects", "惊蛰", 345],
19
+ [:shunbun, "春分", "しゅんぶん", "Spring equinox", "春分", 0],
20
+ [:seimei, "清明", "せいめい", "Pure brightness", "清明", 15],
21
+ [:kokuu, "穀雨", "こくう", "Grain rain", "谷雨", 30],
22
+ [:rikka, "立夏", "りっか", "Start of summer", "立夏", 45],
23
+ [:shoman, "小満", "しょうまん", "Grain buds", "小满", 60],
24
+ [:boshu, "芒種", "ぼうしゅ", "Grain in ear", "芒种", 75],
25
+ [:geshi, "夏至", "げし", "Summer solstice", "夏至", 90],
26
+ [:shosho, "小暑", "しょうしょ", "Minor heat", "小暑", 105],
27
+ [:taisho, "大暑", "たいしょ", "Major heat", "大暑", 120],
28
+ [:risshu, "立秋", "りっしゅう", "Start of autumn", "立秋", 135],
29
+ [:shoshu, "処暑", "しょしょ", "End of heat", "处暑", 150],
30
+ [:hakuro, "白露", "はくろ", "White dew", "白露", 165],
31
+ [:shubun, "秋分", "しゅうぶん", "Autumn equinox", "秋分", 180],
32
+ [:kanro, "寒露", "かんろ", "Cold dew", "寒露", 195],
33
+ [:soko, "霜降", "そうこう", "Frost descent", "霜降", 210],
34
+ [:ritto, "立冬", "りっとう", "Start of winter", "立冬", 225],
35
+ [:shosetsu, "小雪", "しょうせつ", "Minor snow", "小雪", 240],
36
+ [:taisetsu, "大雪", "たいせつ", "Major snow", "大雪", 255],
37
+ [:toji, "冬至", "とうじ", "Winter solstice", "冬至", 270],
38
+ [:shokan, "小寒", "しょうかん", "Minor cold", "小寒", 285],
39
+ [:daikan, "大寒", "だいかん", "Major cold", "大寒", 300]
40
+ ].map do |key, name_ja, reading, name_en, name_zh, longitude|
41
+ Definition.new(
42
+ key: key,
43
+ name_ja: name_ja,
44
+ reading: reading,
45
+ name_en: name_en,
46
+ name_zh: name_zh,
47
+ longitude: longitude
48
+ ).freeze
49
+ end.freeze
50
+
51
+ BY_KEY = TERMS.to_h { |term| [term.key, term] }.freeze
52
+ BY_LONGITUDE = TERMS.to_h { |term| [term.longitude, term] }.freeze
53
+ CALENDAR_ORDER = (TERMS.drop(22) + TERMS.take(22)).freeze
54
+
55
+ module_function
56
+
57
+ def fetch(key)
58
+ BY_KEY.fetch(key.to_sym)
59
+ rescue NoMethodError, KeyError
60
+ raise ArgumentError, "unknown solar term: #{key.inspect}"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sekki24
4
+ module Nutation
5
+ J2000 = 2_451_545.0
6
+ JULIAN_CENTURY = 36_525.0
7
+ DEGREES_TO_RADIANS = Math::PI / 180.0
8
+
9
+ module_function
10
+
11
+ # Four dominant IAU 1980 terms, returned in degrees.
12
+ def longitude(jde)
13
+ centuries = (Float(jde) - J2000) / JULIAN_CENTURY
14
+ sun_mean_longitude = 280.4665 + (36_000.7698 * centuries)
15
+ moon_mean_longitude = 218.3165 + (481_267.8813 * centuries)
16
+ ascending_node = 125.04452 - (1934.136261 * centuries) +
17
+ (0.0020708 * centuries**2) + (centuries**3 / 450_000.0)
18
+
19
+ arcseconds = (-17.20 * sin_degrees(ascending_node)) -
20
+ (1.32 * sin_degrees(2.0 * sun_mean_longitude)) -
21
+ (0.23 * sin_degrees(2.0 * moon_mean_longitude)) +
22
+ (0.21 * sin_degrees(2.0 * ascending_node))
23
+ arcseconds / 3600.0
24
+ end
25
+
26
+ def sin_degrees(angle)
27
+ Math.sin(angle * DEGREES_TO_RADIANS)
28
+ end
29
+ private_class_method :sin_degrees
30
+ end
31
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "kou_names"
4
+ require_relative "kou"
5
+
6
+ module Sekki24
7
+ module SeventyTwoKou
8
+ @cache_mutex = Mutex.new
9
+ @year_cache = {}
10
+ @candidate_cache = {}
11
+
12
+ class << self
13
+ def year(year, tz:, precision:)
14
+ calendar_year = validate_year(year)
15
+ mode = normalize_precision(precision)
16
+ solar = Solar.model(mode)
17
+ cache_key = [calendar_year, TimeScale.timezone_key(tz), mode].freeze
18
+ cached = @cache_mutex.synchronize { @year_cache[cache_key] }
19
+ return cached if cached
20
+
21
+ calculated = calculate_year(calendar_year, tz, mode, solar)
22
+ @cache_mutex.synchronize { @year_cache[cache_key] ||= calculated }
23
+ end
24
+
25
+ def fetch(year, ordinal, tz:, precision:)
26
+ definition = KouNames.fetch(ordinal)
27
+ self.year(year, tz: tz, precision: precision).find { |entry| entry.ordinal == definition.ordinal }
28
+ end
29
+
30
+ def current(time, tz:, precision:)
31
+ navigation_terms(time, tz, precision).select { |entry| entry.time <= time }.max ||
32
+ raise(RangeError, "no current kou in the supported range")
33
+ end
34
+
35
+ def next_kou(time, tz:, precision:)
36
+ navigation_terms(time, tz, precision).select { |entry| entry.time > time }.min ||
37
+ raise(RangeError, "no next kou in the supported range")
38
+ end
39
+
40
+ def previous(time, tz:, precision:)
41
+ navigation_terms(time, tz, precision).select { |entry| entry.time < time }.max ||
42
+ raise(RangeError, "no previous kou in the supported range")
43
+ end
44
+
45
+ def clear_cache!
46
+ @cache_mutex.synchronize do
47
+ @year_cache.clear
48
+ @candidate_cache.clear
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def calculate_year(calendar_year, timezone, mode, solar)
55
+ entries = candidate_times(calendar_year, mode, solar).map do |definition, utc_time|
56
+ local_time = TimeScale.localize(utc_time, timezone)
57
+ Kou.new(definition: definition, time: local_time)
58
+ end.sort.freeze
59
+ return entries if entries.length == 72
60
+
61
+ raise Error, "expected 72 kou for #{calendar_year}, got #{entries.length}"
62
+ end
63
+
64
+ def candidate_times(year, mode, solar)
65
+ cache_key = [year, mode].freeze
66
+ cached = @cache_mutex.synchronize { @candidate_cache[cache_key] }
67
+ return cached if cached
68
+
69
+ calculated = Names::CALENDAR_ORDER.flat_map do |term_definition|
70
+ term_time = Finder.find(year: year, longitude: term_definition.longitude, solar: solar).freeze
71
+ KouNames::TERMS.select { |definition| definition.term_key == term_definition.key }.map do |definition|
72
+ time = if definition.position == :initial
73
+ term_time
74
+ else
75
+ Finder.find_after(time: term_time, longitude: definition.longitude, solar: solar).freeze
76
+ end
77
+ [definition, time].freeze
78
+ end
79
+ end.freeze
80
+ @cache_mutex.synchronize { @candidate_cache[cache_key] ||= calculated }
81
+ end
82
+
83
+ def navigation_terms(time, timezone, precision)
84
+ raise TypeError, "expected Time, got #{time.class}" unless time.is_a?(Time)
85
+
86
+ local_year = TimeScale.localize(time, timezone).year
87
+ years = ((local_year - 1)..(local_year + 1)).select { |value| (MIN_YEAR..MAX_YEAR).cover?(value) }
88
+ years.flat_map { |value| year(value, tz: timezone, precision: precision) }
89
+ end
90
+
91
+ def validate_year(year)
92
+ value = Integer(year)
93
+ return value if (MIN_YEAR..MAX_YEAR).cover?(value)
94
+
95
+ raise RangeError, "year must be between #{MIN_YEAR} and #{MAX_YEAR}"
96
+ rescue ArgumentError, TypeError
97
+ raise ArgumentError, "year must be an Integer between #{MIN_YEAR} and #{MAX_YEAR}"
98
+ end
99
+
100
+ def normalize_precision(precision)
101
+ precision.to_sym
102
+ rescue NoMethodError
103
+ raise ArgumentError, "precision must be :fast or :precise"
104
+ end
105
+ end
106
+ end
107
+
108
+ class << self
109
+ def kou_year(year, tz: DEFAULT_TIMEZONE, precision: DEFAULT_PRECISION)
110
+ SeventyTwoKou.year(year, tz: tz, precision: precision)
111
+ end
112
+
113
+ def kou(year, ordinal, tz: DEFAULT_TIMEZONE, precision: DEFAULT_PRECISION)
114
+ SeventyTwoKou.fetch(year, ordinal, tz: tz, precision: precision)
115
+ end
116
+
117
+ def current_kou(time = Time.now, tz: DEFAULT_TIMEZONE, precision: DEFAULT_PRECISION)
118
+ SeventyTwoKou.current(time, tz: tz, precision: precision)
119
+ end
120
+
121
+ def next_kou(time = Time.now, tz: DEFAULT_TIMEZONE, precision: DEFAULT_PRECISION)
122
+ SeventyTwoKou.next_kou(time, tz: tz, precision: precision)
123
+ end
124
+
125
+ def prev_kou(time = Time.now, tz: DEFAULT_TIMEZONE, precision: DEFAULT_PRECISION)
126
+ SeventyTwoKou.previous(time, tz: tz, precision: precision)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sekki24
4
+ module Solar
5
+ module Fast
6
+ J2000 = 2_451_545.0
7
+ JULIAN_CENTURY = 36_525.0
8
+ DEGREES_TO_RADIANS = Math::PI / 180.0
9
+
10
+ module_function
11
+
12
+ def longitude(jde)
13
+ centuries = (Float(jde) - J2000) / JULIAN_CENTURY
14
+ mean_longitude = 280.46646 + (36_000.76983 * centuries) + (0.0003032 * centuries**2)
15
+ mean_anomaly = 357.52911 + (35_999.05029 * centuries) - (0.0001537 * centuries**2) +
16
+ (centuries**3 / 24_490_000.0)
17
+ center = equation_of_center(mean_anomaly, centuries)
18
+ omega = 125.04 - (1934.136 * centuries)
19
+
20
+ normalize(mean_longitude + center - 0.00569 - (0.00478 * sin_degrees(omega)))
21
+ end
22
+
23
+ def equation_of_center(mean_anomaly, centuries)
24
+ ((1.914602 - (0.004817 * centuries) - (0.000014 * centuries**2)) * sin_degrees(mean_anomaly)) +
25
+ ((0.019993 - (0.000101 * centuries)) * sin_degrees(2.0 * mean_anomaly)) +
26
+ (0.000289 * sin_degrees(3.0 * mean_anomaly))
27
+ end
28
+ private_class_method :equation_of_center
29
+
30
+ def sin_degrees(angle)
31
+ Math.sin(angle * DEGREES_TO_RADIANS)
32
+ end
33
+ private_class_method :sin_degrees
34
+
35
+ def normalize(angle)
36
+ angle % 360.0
37
+ end
38
+ private_class_method :normalize
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "vsop87_earth"
4
+ require_relative "../nutation"
5
+
6
+ module Sekki24
7
+ module Solar
8
+ module Precise
9
+ J2000 = 2_451_545.0
10
+ JULIAN_MILLENNIUM = 365_250.0
11
+ JULIAN_CENTURY = 36_525.0
12
+ RADIANS_TO_DEGREES = 180.0 / Math::PI
13
+ ABERRATION_ARCSECONDS = 20.4898
14
+ FK5_LONGITUDE_CORRECTION = -0.09033 / 3600.0
15
+
16
+ module_function
17
+
18
+ def longitude(jde)
19
+ jde = Float(jde)
20
+ millennia = (jde - J2000) / JULIAN_MILLENNIUM
21
+ earth_longitude = evaluate(VSOP87Earth::LONGITUDE, millennia) * RADIANS_TO_DEGREES
22
+ radius = evaluate(VSOP87Earth::RADIUS, millennia)
23
+ geometric_longitude = earth_longitude + 180.0 + FK5_LONGITUDE_CORRECTION
24
+ apparent_longitude = geometric_longitude + Nutation.longitude(jde) -
25
+ (ABERRATION_ARCSECONDS / (3600.0 * radius))
26
+
27
+ apparent_longitude % 360.0
28
+ end
29
+
30
+ def evaluate(series, time)
31
+ power = 1.0
32
+ series.sum do |terms|
33
+ value = terms.sum do |amplitude, phase, frequency|
34
+ amplitude * Math.cos(phase + (frequency * time))
35
+ end
36
+ result = value * power
37
+ power *= time
38
+ result
39
+ end
40
+ end
41
+ private_class_method :evaluate
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sekki24
4
+ module Solar
5
+ # Truncated VSOP87D Earth series (Bretagnon & Francou, 1988).
6
+ # Coefficients are from the official IMCCE VSOP87D.ear distribution.
7
+ module VSOP87Earth
8
+ LONGITUDE = [
9
+ [
10
+ [1.75347045673, 0.00000000000, 0.00000000000].freeze,
11
+ [0.03341656456, 4.66925680417, 6283.07584999140].freeze,
12
+ [0.00034894275, 4.62610241759, 12566.15169998280].freeze,
13
+ [0.00003417571, 2.82886579606, 3.52311834900].freeze,
14
+ [0.00003497056, 2.74411800971, 5753.38488489680].freeze,
15
+ [0.00003135896, 3.62767041758, 77713.77146812050].freeze,
16
+ [0.00002676218, 4.41808351397, 7860.41939243920].freeze,
17
+ [0.00002342687, 6.13516237631, 3930.20969621960].freeze,
18
+ [0.00001273166, 2.03709655772, 529.69096509460].freeze,
19
+ [0.00001324292, 0.74246356352, 11506.76976979360].freeze,
20
+ [0.00000901855, 2.04505443513, 26.29831979980].freeze,
21
+ [0.00001199167, 1.10962944315, 1577.34354244780].freeze,
22
+ [0.00000857223, 3.50849156957, 398.14900340820].freeze,
23
+ [0.00000779786, 1.17882652114, 5223.69391980220].freeze,
24
+ [0.00000990250, 5.23268129594, 5884.92684658320].freeze,
25
+ [0.00000753141, 2.53339053818, 5507.55323866740].freeze,
26
+ [0.00000505264, 4.58292563052, 18849.22754997420].freeze,
27
+ [0.00000492379, 4.20506639861, 775.52261132400].freeze,
28
+ [0.00000356655, 2.91954116867, 0.06731030280].freeze,
29
+ [0.00000284125, 1.89869034186, 796.29800681640].freeze,
30
+ [0.00000242810, 0.34481140906, 5486.77784317500].freeze,
31
+ [0.00000317087, 5.84901952218, 11790.62908865880].freeze,
32
+ [0.00000271039, 0.31488607649, 10977.07880469900].freeze,
33
+ [0.00000206160, 4.80646606059, 2544.31441988340].freeze,
34
+ [0.00000205385, 1.86947813692, 5573.14280143310].freeze,
35
+ [0.00000202261, 2.45767795458, 6069.77675455340].freeze,
36
+ [0.00000126184, 1.08302630210, 20.77539549240].freeze,
37
+ [0.00000155516, 0.83306073807, 213.29909543800].freeze,
38
+ [0.00000115132, 0.64544911683, 0.98032106820].freeze,
39
+ [0.00000102851, 0.63599846727, 4694.00295470760].freeze,
40
+ [0.00000101724, 4.26679821365, 7.11354700080].freeze,
41
+ [0.00000099206, 6.20992940258, 2146.16541647520].freeze,
42
+ [0.00000132212, 3.41118275555, 2942.46342329160].freeze,
43
+ [0.00000097607, 0.68101272270, 155.42039943420].freeze,
44
+ [0.00000085128, 1.29870743025, 6275.96230299060].freeze,
45
+ [0.00000074651, 1.75508916159, 5088.62883976680].freeze,
46
+ [0.00000101895, 0.97569221824, 15720.83878487840].freeze,
47
+ [0.00000084711, 3.67080093025, 71430.69561812909].freeze,
48
+ [0.00000073547, 4.67926565481, 801.82093112380].freeze,
49
+ [0.00000073874, 3.50319443167, 3154.68708489560].freeze,
50
+ [0.00000078756, 3.03698313141, 12036.46073488820].freeze,
51
+ [0.00000079637, 1.80791330700, 17260.15465469040].freeze,
52
+ [0.00000085803, 5.98322631256, 161000.68573767410].freeze,
53
+ [0.00000056963, 2.78430398043, 6286.59896834040].freeze,
54
+ [0.00000061148, 1.81839811024, 7084.89678111520].freeze,
55
+ [0.00000069627, 0.83297596966, 9437.76293488700].freeze,
56
+ [0.00000056116, 4.38694880779, 14143.49524243060].freeze,
57
+ [0.00000062449, 3.97763880587, 8827.39026987480].freeze,
58
+ [0.00000051145, 0.28306864501, 5856.47765911540].freeze,
59
+ [0.00000055577, 3.47006009062, 6279.55273164240].freeze,
60
+ [0.00000041036, 5.36817351402, 8429.24126646660].freeze,
61
+ [0.00000051605, 1.33282746983, 1748.01641306700].freeze,
62
+ [0.00000051992, 0.18914945834, 12139.55350910680].freeze,
63
+ [0.00000049000, 0.48735065033, 1194.44701022460].freeze,
64
+ [0.00000039200, 6.16832995016, 10447.38783960440].freeze,
65
+ [0.00000035566, 1.77597314691, 6812.76681508600].freeze,
66
+ [0.00000036770, 6.04133859347, 10213.28554621100].freeze,
67
+ [0.00000036596, 2.56955238628, 1059.38193018920].freeze,
68
+ [0.00000033291, 0.59309499459, 17789.84561978500].freeze,
69
+ [0.00000035954, 1.70876111898, 2352.86615377180].freeze,
70
+ [0.00000040938, 2.39850881707, 19651.04848109800].freeze,
71
+ [0.00000030047, 2.73975123935, 1349.86740965880].freeze,
72
+ [0.00000030412, 0.44294464135, 83996.84731811189].freeze,
73
+ [0.00000023663, 0.48473567763, 8031.09226305840].freeze,
74
+ ].freeze,
75
+ [
76
+ [6283.31966747491, 0.00000000000, 0.00000000000].freeze,
77
+ [0.00206058863, 2.67823455584, 6283.07584999140].freeze,
78
+ [0.00004303430, 2.63512650414, 12566.15169998280].freeze,
79
+ [0.00000425264, 1.59046980729, 3.52311834900].freeze,
80
+ [0.00000108977, 2.96618001993, 1577.34354244780].freeze,
81
+ [0.00000093478, 2.59212835365, 18849.22754997420].freeze,
82
+ [0.00000119261, 5.79557487799, 26.29831979980].freeze,
83
+ [0.00000072122, 1.13846158196, 529.69096509460].freeze,
84
+ [0.00000067768, 1.87472304791, 398.14900340820].freeze,
85
+ [0.00000067327, 4.40918235168, 5507.55323866740].freeze,
86
+ [0.00000059027, 2.88797038460, 5223.69391980220].freeze,
87
+ [0.00000055976, 2.17471680261, 155.42039943420].freeze,
88
+ [0.00000045407, 0.39803079805, 796.29800681640].freeze,
89
+ [0.00000036369, 0.46624739835, 775.52261132400].freeze,
90
+ [0.00000028958, 2.64707383882, 7.11354700080].freeze,
91
+ [0.00000019097, 1.84628332577, 5486.77784317500].freeze,
92
+ [0.00000020844, 5.34138275149, 0.98032106820].freeze,
93
+ [0.00000018508, 4.96855124577, 213.29909543800].freeze,
94
+ [0.00000016233, 0.03216483047, 2544.31441988340].freeze,
95
+ [0.00000017293, 2.99116864949, 6275.96230299060].freeze,
96
+ [0.00000015832, 1.43049285325, 2146.16541647520].freeze,
97
+ [0.00000014615, 1.20532366323, 10977.07880469900].freeze,
98
+ [0.00000011877, 3.25804815607, 5088.62883976680].freeze,
99
+ [0.00000011514, 2.07502418155, 4694.00295470760].freeze,
100
+ [0.00000009721, 4.23925472239, 1349.86740965880].freeze,
101
+ [0.00000009969, 1.30262991097, 6286.59896834040].freeze,
102
+ [0.00000009452, 2.69957062864, 242.72860397400].freeze,
103
+ [0.00000012461, 2.83432285512, 1748.01641306700].freeze,
104
+ [0.00000011808, 5.27379790480, 1194.44701022460].freeze,
105
+ [0.00000008577, 5.64475868067, 951.71840625060].freeze,
106
+ [0.00000010641, 0.76614199202, 553.56940284240].freeze,
107
+ [0.00000007576, 5.30062664886, 2352.86615377180].freeze,
108
+ [0.00000005834, 1.76649917904, 1059.38193018920].freeze,
109
+ [0.00000006385, 2.65033984967, 9437.76293488700].freeze,
110
+ ].freeze,
111
+ [
112
+ [0.00052918870, 0.00000000000, 0.00000000000].freeze,
113
+ [0.00008719837, 1.07209665242, 6283.07584999140].freeze,
114
+ [0.00000309125, 0.86728818832, 12566.15169998280].freeze,
115
+ [0.00000027339, 0.05297871691, 3.52311834900].freeze,
116
+ [0.00000016334, 5.18826691036, 26.29831979980].freeze,
117
+ [0.00000015752, 3.68457889430, 155.42039943420].freeze,
118
+ [0.00000009541, 0.75742297675, 18849.22754997420].freeze,
119
+ [0.00000008937, 2.05705419118, 77713.77146812050].freeze,
120
+ [0.00000006952, 0.82673305410, 775.52261132400].freeze,
121
+ [0.00000005064, 4.66284525271, 1577.34354244780].freeze,
122
+ [0.00000004061, 1.03057162962, 7.11354700080].freeze,
123
+ [0.00000003463, 5.14074632811, 796.29800681640].freeze,
124
+ [0.00000003169, 6.05291851171, 5507.55323866740].freeze,
125
+ [0.00000003020, 1.19246506441, 242.72860397400].freeze,
126
+ [0.00000002886, 6.11652627155, 529.69096509460].freeze,
127
+ [0.00000003810, 3.44050803490, 5573.14280143310].freeze,
128
+ [0.00000002714, 0.30637881025, 398.14900340820].freeze,
129
+ [0.00000002371, 4.38118838167, 5223.69391980220].freeze,
130
+ [0.00000002538, 2.27992810679, 553.56940284240].freeze,
131
+ [0.00000002079, 3.75435330484, 0.98032106820].freeze,
132
+ ].freeze,
133
+ [
134
+ [0.00000289226, 5.84384198723, 6283.07584999140].freeze,
135
+ [0.00000034955, 0.00000000000, 0.00000000000].freeze,
136
+ [0.00000016819, 5.48766912348, 12566.15169998280].freeze,
137
+ [0.00000002962, 5.19577265202, 155.42039943420].freeze,
138
+ [0.00000001288, 4.72200252235, 3.52311834900].freeze,
139
+ [0.00000000635, 5.96925937141, 242.72860397400].freeze,
140
+ [0.00000000714, 5.30045809128, 18849.22754997420].freeze,
141
+ ].freeze,
142
+ [
143
+ [0.00000114084, 3.14159265359, 0.00000000000].freeze,
144
+ [0.00000007717, 4.13446589358, 6283.07584999140].freeze,
145
+ [0.00000000765, 3.83803776214, 12566.15169998280].freeze,
146
+ ].freeze,
147
+ [
148
+ [0.00000000878, 3.14159265359, 0.00000000000].freeze,
149
+ ].freeze,
150
+ ].freeze
151
+ RADIUS = [
152
+ [
153
+ [1.00013988799, 0.00000000000, 0.00000000000].freeze,
154
+ [0.01670699626, 3.09846350771, 6283.07584999140].freeze,
155
+ [0.00013956023, 3.05524609620, 12566.15169998280].freeze,
156
+ [0.00003083720, 5.19846674381, 77713.77146812050].freeze,
157
+ [0.00001628461, 1.17387749012, 5753.38488489680].freeze,
158
+ [0.00001575568, 2.84685245825, 7860.41939243920].freeze,
159
+ [0.00000924799, 5.45292234084, 11506.76976979360].freeze,
160
+ [0.00000542444, 4.56409149777, 3930.20969621960].freeze,
161
+ [0.00000472110, 3.66100022149, 5884.92684658320].freeze,
162
+ [0.00000328780, 5.89983646482, 5223.69391980220].freeze,
163
+ [0.00000345983, 0.96368617687, 5507.55323866740].freeze,
164
+ [0.00000306784, 0.29867139512, 5573.14280143310].freeze,
165
+ [0.00000174844, 3.01193636534, 18849.22754997420].freeze,
166
+ [0.00000243189, 4.27349536153, 11790.62908865880].freeze,
167
+ [0.00000211829, 5.84714540314, 1577.34354244780].freeze,
168
+ [0.00000185752, 5.02194447178, 10977.07880469900].freeze,
169
+ [0.00000109835, 5.05510636285, 5486.77784317500].freeze,
170
+ [0.00000098316, 0.88681311277, 6069.77675455340].freeze,
171
+ [0.00000086499, 5.68959778254, 15720.83878487840].freeze,
172
+ [0.00000085825, 1.27083733351, 161000.68573767410].freeze,
173
+ [0.00000062916, 0.92177108832, 529.69096509460].freeze,
174
+ [0.00000057056, 2.01374292014, 83996.84731811189].freeze,
175
+ [0.00000064903, 0.27250613787, 17260.15465469040].freeze,
176
+ [0.00000049384, 3.24501240359, 2544.31441988340].freeze,
177
+ [0.00000055736, 5.24159798933, 71430.69561812909].freeze,
178
+ [0.00000042515, 6.01110242003, 6275.96230299060].freeze,
179
+ [0.00000046963, 2.57805070386, 775.52261132400].freeze,
180
+ [0.00000038968, 5.36071738169, 4694.00295470760].freeze,
181
+ [0.00000044661, 5.53715807302, 9437.76293488700].freeze,
182
+ [0.00000035660, 1.67468058995, 12036.46073488820].freeze,
183
+ [0.00000031921, 0.18368229781, 5088.62883976680].freeze,
184
+ [0.00000031846, 1.77775642085, 398.14900340820].freeze,
185
+ [0.00000033193, 0.24370300098, 7084.89678111520].freeze,
186
+ [0.00000038245, 2.39255343974, 8827.39026987480].freeze,
187
+ [0.00000028464, 1.21344868176, 6286.59896834040].freeze,
188
+ [0.00000037490, 0.82952922332, 19651.04848109800].freeze,
189
+ [0.00000036957, 4.90107591914, 12139.55350910680].freeze,
190
+ [0.00000034537, 1.84270693282, 2942.46342329160].freeze,
191
+ [0.00000026275, 4.58896850401, 10447.38783960440].freeze,
192
+ [0.00000024596, 3.78660875483, 8429.24126646660].freeze,
193
+ ].freeze,
194
+ [
195
+ [0.00103018608, 1.10748969588, 6283.07584999140].freeze,
196
+ [0.00001721238, 1.06442301418, 12566.15169998280].freeze,
197
+ [0.00000702215, 3.14159265359, 0.00000000000].freeze,
198
+ [0.00000032346, 1.02169059149, 18849.22754997420].freeze,
199
+ [0.00000030799, 2.84353804832, 5507.55323866740].freeze,
200
+ [0.00000024971, 1.31906709482, 5223.69391980220].freeze,
201
+ [0.00000018485, 1.42429748614, 1577.34354244780].freeze,
202
+ [0.00000010078, 5.91378194648, 10977.07880469900].freeze,
203
+ [0.00000008634, 0.27146150602, 5486.77784317500].freeze,
204
+ [0.00000008654, 1.42046854427, 6275.96230299060].freeze,
205
+ ].freeze,
206
+ [
207
+ [0.00004359385, 5.78455133738, 6283.07584999140].freeze,
208
+ [0.00000123633, 5.57934722157, 12566.15169998280].freeze,
209
+ [0.00000012341, 3.14159265359, 0.00000000000].freeze,
210
+ [0.00000008792, 3.62777733395, 77713.77146812050].freeze,
211
+ [0.00000005689, 1.86958905084, 5573.14280143310].freeze,
212
+ [0.00000003301, 5.47027913302, 18849.22754997420].freeze,
213
+ ].freeze,
214
+ [
215
+ [0.00000144595, 4.27319435148, 6283.07584999140].freeze,
216
+ [0.00000006729, 3.91697608662, 12566.15169998280].freeze,
217
+ ].freeze,
218
+ [
219
+ [0.00000003858, 2.56384387339, 6283.07584999140].freeze,
220
+ ].freeze,
221
+ [
222
+ ].freeze,
223
+ ].freeze
224
+ end
225
+ end
226
+ end
227
+
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "solar/fast"
4
+ require_relative "solar/precise"
5
+
6
+ module Sekki24
7
+ module Solar
8
+ MODELS = {
9
+ fast: Fast,
10
+ precise: Precise
11
+ }.freeze
12
+
13
+ module_function
14
+
15
+ def model(precision)
16
+ MODELS.fetch(precision.to_sym)
17
+ rescue NoMethodError, KeyError
18
+ raise ArgumentError, "precision must be :fast or :precise"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "time"
5
+
6
+ module Sekki24
7
+ class Term
8
+ include Comparable
9
+
10
+ ATTRIBUTES = %i[key name_ja reading name_en name_zh longitude time].freeze
11
+
12
+ attr_reader(*ATTRIBUTES)
13
+
14
+ def initialize(definition:, time:)
15
+ @key = definition.key
16
+ @name_ja = definition.name_ja
17
+ @reading = definition.reading
18
+ @name_en = definition.name_en
19
+ @name_zh = definition.name_zh
20
+ @longitude = definition.longitude
21
+ @time = time.dup.freeze
22
+ freeze
23
+ end
24
+
25
+ def <=>(other)
26
+ return unless other.respond_to?(:time)
27
+
28
+ time <=> other.time
29
+ end
30
+
31
+ def ==(other)
32
+ other.is_a?(Term) && key == other.key && time == other.time
33
+ end
34
+ alias eql? ==
35
+
36
+ def hash
37
+ [self.class, key, time].hash
38
+ end
39
+
40
+ def to_date
41
+ time.to_date
42
+ end
43
+
44
+ def to_h
45
+ ATTRIBUTES.to_h { |attribute| [attribute, public_send(attribute)] }
46
+ end
47
+
48
+ def inspect
49
+ "#<#{self.class} #{name_ja} (#{key}) #{time.iso8601} longitude=#{longitude}°>"
50
+ end
51
+ end
52
+ end