astroscript 0.3.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.
- checksums.yaml +7 -0
- data/.rubocop.defaults.yml +5818 -0
- data/.rubocop.yml +25 -0
- data/.ruby-version +1 -0
- data/ARCH.md +13 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +12 -0
- data/exe/astroscript +4 -0
- data/lib/astro_helper.rb +360 -0
- data/lib/astroscript/aspect.rb +188 -0
- data/lib/astroscript/body/const_body.rb +55 -0
- data/lib/astroscript/body/house_cusp.rb +34 -0
- data/lib/astroscript/body/method_body.rb +27 -0
- data/lib/astroscript/body/midpoint.rb +47 -0
- data/lib/astroscript/body.rb +495 -0
- data/lib/astroscript/calculator.rb +257 -0
- data/lib/astroscript/chart.rb +239 -0
- data/lib/astroscript/version.rb +5 -0
- data/lib/astroscript.rb +39 -0
- data/lib/ruby_extensions.rb +83 -0
- data/lib/swiss_ephemeris.rb +106 -0
- data/sig/astroscript.rbs +4 -0
- data/vendor/swe_data/houses.txt +33 -0
- data/vendor/swe_data/s136199s.se1 +0 -0
- data/vendor/swe_data/se00010s.se1 +0 -0
- data/vendor/swe_data/se90377s.se1 +0 -0
- data/vendor/swe_data/se90482s.se1 +0 -0
- data/vendor/swe_data/seas_18.se1 +0 -0
- data/vendor/swe_data/semo_18.se1 +0 -0
- data/vendor/swe_data/sepl_18.se1 +0 -0
- metadata +318 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astroscript
|
4
|
+
class HouseCusp < Body
|
5
|
+
attr_reader :house
|
6
|
+
|
7
|
+
def initialize(house, calc)
|
8
|
+
@house = house
|
9
|
+
@abbr = :"c#{@house}"
|
10
|
+
super(@abbr, calc)
|
11
|
+
end
|
12
|
+
|
13
|
+
def calculate!
|
14
|
+
@lon = calc.house_cusps[@house - 1]
|
15
|
+
update!
|
16
|
+
@lon = calc.flip(@lon) if antipode
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def symbol
|
21
|
+
@symbol = "C#{house}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def name
|
25
|
+
"House Cusp #{numeral}"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def numeral
|
31
|
+
HOUSES[@house]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astroscript
|
4
|
+
class MethodBody < Body
|
5
|
+
attr_accessor :symbol, :name, :chart
|
6
|
+
|
7
|
+
def initialize(deg, name, symbol = nil, _abbr = nil, chart:)
|
8
|
+
@lon = deg
|
9
|
+
@name = name
|
10
|
+
@chart = chart
|
11
|
+
@calc = chart.calc
|
12
|
+
@harmonic = 1
|
13
|
+
body = SwissEphemeris::BODIES.select{|_k, v| v[:name] == name }
|
14
|
+
if body.empty?
|
15
|
+
$logger.warn "Unknown body: #{name}"
|
16
|
+
else
|
17
|
+
@abbr = body.keys.first
|
18
|
+
@symbol = body[@abbr][:symbol] || symbol
|
19
|
+
end
|
20
|
+
update!
|
21
|
+
end
|
22
|
+
|
23
|
+
def calculate! # name should equal method
|
24
|
+
@lon = @chart.send name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astroscript
|
4
|
+
class Midpoint < Body
|
5
|
+
attr_reader :bodies
|
6
|
+
|
7
|
+
def initialize(b1, b2, opts = {})
|
8
|
+
@harmonic = opts[:harmonic] || 1
|
9
|
+
@bodies = [b1, b2]
|
10
|
+
@name = abbr.sub(operator, "_")
|
11
|
+
@symbol = @bodies.map(&:symbol).join(operator)
|
12
|
+
@lon = @bodies.map(&:lon).avg
|
13
|
+
if ((@lon - b1.lon).abs % 360) > 90
|
14
|
+
@lon = (@lon + 180) % 360 # flip!
|
15
|
+
end
|
16
|
+
update!
|
17
|
+
end
|
18
|
+
|
19
|
+
def calculate!
|
20
|
+
bodies.each(&:calculate!)
|
21
|
+
end
|
22
|
+
|
23
|
+
def abbr
|
24
|
+
bodies.map(&:abbr).join(operator)
|
25
|
+
end
|
26
|
+
|
27
|
+
def symbol
|
28
|
+
bodies.map(&:symbol).join(operator)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_sym
|
32
|
+
abbr.to_sym
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
return super unless defined?(IRB)
|
37
|
+
|
38
|
+
"#{bodies.first.abbr}/#{bodies.last.abbr}\t#{AstroHelper.deg_to_s(lon)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def operator
|
44
|
+
"/"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,495 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Astroscript
|
4
|
+
class Body
|
5
|
+
attr_reader :abbr, :lat, :distance, :velocity, :decl, :ra, :azimuth, :altitude, :calc, :harmonic, :hlat, :hlon,
|
6
|
+
:hdist, :hvel
|
7
|
+
attr_accessor :antipode
|
8
|
+
|
9
|
+
def initialize(abbr, calc)
|
10
|
+
@abbr = abbr
|
11
|
+
@calc = calc
|
12
|
+
@abbr = @abbr.upcase if @abbr.size == 2
|
13
|
+
@harmonic = 1
|
14
|
+
calculate!
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
return super unless defined?(IRB)
|
19
|
+
|
20
|
+
"#{abbr}\t#{AstroHelper.deg_to_s(lon)}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def radix
|
24
|
+
harmonize(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
lon == other.lon
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.phase(b1, b2, opts = {})
|
32
|
+
opts[:method] ||= :lon
|
33
|
+
# $logger.debug("#{b1}, #{b2} #{opts[:method]}")
|
34
|
+
bb1 = b1.send(opts[:method])
|
35
|
+
bb2 = b2.send(opts[:method])
|
36
|
+
(bb2 - bb1) % 360
|
37
|
+
# if b1.speed_order > b2.speed_order
|
38
|
+
# (bb2-bb1)%360
|
39
|
+
# else
|
40
|
+
# (bb1-bb2)%360
|
41
|
+
# end
|
42
|
+
# rescue => e
|
43
|
+
# raise e, "#{b1.abbr} - #{b2.abbr} => #{opts[:method]} == nil!"
|
44
|
+
end
|
45
|
+
|
46
|
+
def transit?
|
47
|
+
@transit
|
48
|
+
end
|
49
|
+
|
50
|
+
def transit!
|
51
|
+
@transit = true
|
52
|
+
end
|
53
|
+
|
54
|
+
def flip
|
55
|
+
@lon += 180
|
56
|
+
@lon % 360
|
57
|
+
end
|
58
|
+
|
59
|
+
def flip!
|
60
|
+
self.antipode = !antipode
|
61
|
+
calculate!
|
62
|
+
end
|
63
|
+
|
64
|
+
def lon
|
65
|
+
output = @lon # @calc.in_mundo? ? prime_degree : @lon
|
66
|
+
output *= @harmonic
|
67
|
+
output %= 360
|
68
|
+
output += @calc.arc if @calc
|
69
|
+
# TODO: make harmonic charts a calc transformer?
|
70
|
+
if (t = calc&.transformer)
|
71
|
+
t.call(self, output)
|
72
|
+
else
|
73
|
+
output
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
alias degree lon
|
78
|
+
alias to_f lon
|
79
|
+
|
80
|
+
def to_dms(opts = {})
|
81
|
+
lon.to_dms(opts)
|
82
|
+
end
|
83
|
+
|
84
|
+
def prefix
|
85
|
+
@calc&.prefix
|
86
|
+
end
|
87
|
+
|
88
|
+
def handle
|
89
|
+
"#{prefix}#{abbr}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def name
|
93
|
+
return unless (body = SwissEphemeris::BODIES[abbr])
|
94
|
+
|
95
|
+
body[:name].capitalize
|
96
|
+
end
|
97
|
+
|
98
|
+
def symbol
|
99
|
+
return unless (body = SwissEphemeris::BODIES[abbr])
|
100
|
+
|
101
|
+
body[:symbol]
|
102
|
+
end
|
103
|
+
|
104
|
+
def calculate!
|
105
|
+
# $logger.debug "calculating #{abbr}"
|
106
|
+
if @abbr == :SVP
|
107
|
+
@lon = -@calc.ayanamsha
|
108
|
+
@lat = 0
|
109
|
+
@distance = nil
|
110
|
+
@velocity = 0
|
111
|
+
end
|
112
|
+
raise ArgumentError, "unknown Body #{@abbr}!" unless (body = SwissEphemeris::BODIES[@abbr])
|
113
|
+
|
114
|
+
if (id = body[:id])
|
115
|
+
results = @calc.calc(id)
|
116
|
+
@lon, @lat, @distance, @velocity = results[0..4]
|
117
|
+
results = @calc.calc(id, equatorial: true)
|
118
|
+
# right ascension and declination
|
119
|
+
@ra, @decl = results[0..2]
|
120
|
+
@azimuth, @altitude, * = Swe4r.swe_azalt(calc.jd, Swe4r::SE_ECL2HOR, calc.longitude, calc.latitude,
|
121
|
+
calc.altitude, 1013.25, 15.0, @lon + calc.ayanamsha, @lat.to_f, @distance.to_f)
|
122
|
+
@azimuth = (@azimuth + 180) % 360 # adjusts to measure clockwise from north
|
123
|
+
# heliocentric
|
124
|
+
id = Swe4r::SE_EARTH if abbr == :EA
|
125
|
+
results = @calc.calc(id, heliocentric: true)
|
126
|
+
@hlon, @hlat, @hdist, @hvel = results[0..4]
|
127
|
+
else # virtual body - angles and nodes
|
128
|
+
method = if (a = body[:antipode])
|
129
|
+
SwissEphemeris::BODIES[a][:name]
|
130
|
+
else
|
131
|
+
body[:name]
|
132
|
+
end
|
133
|
+
@calc.get_houses
|
134
|
+
method = AstroHelper.symbolize(method)
|
135
|
+
# $logger.debug "sending #{method} to @calc"
|
136
|
+
if %i[NN SN].include?(@abbr)
|
137
|
+
node = @calc.true_node? ? Swe4r::SE_TRUE_NODE : Swe4r::SE_MEAN_NODE
|
138
|
+
@lon, @lat, @distance, @velocity = @calc.calc(node)
|
139
|
+
# @lon = calc.flip(@lon) if @abbr == :SN # TODO flip after harmonic
|
140
|
+
else
|
141
|
+
@lon = @calc.send(method)
|
142
|
+
end
|
143
|
+
# TODO: get values from sun at this degree
|
144
|
+
end
|
145
|
+
@lon = calc.flip(@lon) if body[:antipode] || antipode
|
146
|
+
update!
|
147
|
+
end
|
148
|
+
|
149
|
+
# https://www.timeanddate.com/astronomy/planets/distance
|
150
|
+
|
151
|
+
# According to BPHS the victor is the Graha who is further north.
|
152
|
+
# Surya Siddhanta adds that the brighter will win even if in the south.
|
153
|
+
# By brighter is meant an unusually brighter planet. To determine this we need to know the natural
|
154
|
+
# order of brightness from least bright to most bright which is:
|
155
|
+
# Saturn, Mars, Mercury, Jupiter, Venus. On very rare occasions when a planet in its oval shaped
|
156
|
+
# revolution is much closer to Earth and the other much further away, one planet can gain a lot of
|
157
|
+
# brightness while the other loses brightness. On these occasions a planet that is normally darker
|
158
|
+
# than another may appear brighter – in which case it wins the war. To determine the victor in a war,
|
159
|
+
# therefore, requires first that elaborate calculations are done based on the diameter of the planet
|
160
|
+
# and its proximity to Earth in order to determine the brightness. If the brightness of the two
|
161
|
+
# planets follows the regular order, then the Graha with the most northern declination wins the war.
|
162
|
+
# If, on the other hand, the planet that is regularly less bright is brighter, than it wins the war
|
163
|
+
# and no check for most northern position need be made. Venus, regardless, always wins the war, due
|
164
|
+
# to her much greater brilliance she is considered to never be defeated.
|
165
|
+
# https://astrology-videos.com/CourseMaterials/PlanetaryWar.pdf
|
166
|
+
def prime_degree
|
167
|
+
# lon, decl = Swe4r::swe_cotrans( 90, @azimuth+calc.ayanamsha, @altitude)
|
168
|
+
(Swe4r.swe_house_pos(calc.ramc, calc.latitude, calc.oe, "C".ord, @lon + calc.ayanamsha, @lat.to_f) * 30) - 30
|
169
|
+
end
|
170
|
+
alias pvl prime_degree
|
171
|
+
|
172
|
+
def meridian_degree # longitude
|
173
|
+
azimuth, altitude, * = Swe4r.swe_azalt(calc.jd, Swe4r::SE_ECL2HOR, calc.longitude, calc.latitude, calc.altitude,
|
174
|
+
0, 0, @lon, @lat.to_f, @distance.to_f)
|
175
|
+
azimuth = (azimuth + 90) % 360
|
176
|
+
lon, decl, * = Swe4r.swe_cotrans(90, azimuth, altitude)
|
177
|
+
output = [((lon + 180) % 360).round(4), decl.round(4)]
|
178
|
+
output[0]
|
179
|
+
end
|
180
|
+
|
181
|
+
# def /(body)
|
182
|
+
# Midpoint.new(self, body)
|
183
|
+
# end
|
184
|
+
|
185
|
+
ORBS = { tight: 2.0, close: 6.0, max: 12.0 }.freeze # Hamblin
|
186
|
+
# ORBS = {tight: 2.0, close: 4.0, max: 16.0} # Cochrane
|
187
|
+
|
188
|
+
def sign_i
|
189
|
+
sign
|
190
|
+
end
|
191
|
+
|
192
|
+
def sign
|
193
|
+
(lon / 30).floor
|
194
|
+
end
|
195
|
+
|
196
|
+
def sign_sym
|
197
|
+
SwissEphemeris::SIGNS[sign]
|
198
|
+
end
|
199
|
+
|
200
|
+
def to_s(_opts = {})
|
201
|
+
opts = { angle: true, symbol: true }.merge!(_opts)
|
202
|
+
postfix = " "
|
203
|
+
if opts[:angle]
|
204
|
+
postfix = "r" if retrograde?
|
205
|
+
postfix = "s" if stationary?
|
206
|
+
dignity = nil
|
207
|
+
# dignity = SYMBOLS[:exalted] if in_exaltation? # TODO
|
208
|
+
# dignity = SYMBOLS[:detriment] if in_detriment?
|
209
|
+
end
|
210
|
+
if opts[:symbol]
|
211
|
+
opts[:prefix] = format("%4s", symbol) if opts[:symbol]
|
212
|
+
opts[:padding] = 2
|
213
|
+
end
|
214
|
+
opts[:prefix] = "#{opts[:symbol] ? symbol : name}#{dignity}" if opts[:prefix]
|
215
|
+
if opts[:house]
|
216
|
+
opts[:postfix] = "#{postfix} #{format('%-5s', HOUSES[house])}"
|
217
|
+
opts[:postfix] += " (#{AstroHelper::HOUSE_THEMES[house]})" if opts[:theme]
|
218
|
+
end
|
219
|
+
AstroHelper.deg_to_s(lon, { postfix: postfix }.merge!(opts)) # .strip
|
220
|
+
end
|
221
|
+
|
222
|
+
def gate
|
223
|
+
AstroHelper.deg_to_gate(lon)
|
224
|
+
end
|
225
|
+
|
226
|
+
def print(opts = {})
|
227
|
+
puts to_s({ prefix: true, gate: true, spectrum: true, house: true, theme: true, symbol: true }.merge(opts))
|
228
|
+
end
|
229
|
+
|
230
|
+
def distance_to_sun
|
231
|
+
SwissEphemeris::BODIES.keys.index(abbr) || -1
|
232
|
+
end
|
233
|
+
|
234
|
+
# this assumes the that BODIES hash is defined in proper order
|
235
|
+
def closer_to_sun?(b2)
|
236
|
+
i1 = distance_to_sun
|
237
|
+
i2 = b2.distance_to_sun
|
238
|
+
i1 && i2 && i1 < i2
|
239
|
+
end
|
240
|
+
|
241
|
+
ANGLES = %i[AC MC DC IC VX POF POS POH].freeze
|
242
|
+
def is_angle?
|
243
|
+
ANGLES.include?(abbr)
|
244
|
+
end
|
245
|
+
|
246
|
+
def personal_angle?
|
247
|
+
(ANGLES + %i[VX POF POS POH]).include?(abbr)
|
248
|
+
end
|
249
|
+
|
250
|
+
def speed_order
|
251
|
+
a = abbr
|
252
|
+
a = :NN if %i[MN SN].include?(a)
|
253
|
+
return -1 * ANGLES.reverse.index(a) if is_angle?
|
254
|
+
|
255
|
+
# https://groups.io/g/SpiritusMundi/topic/speed_of_ascendant_and/83787680
|
256
|
+
SPEED_ORDER.index(a)
|
257
|
+
end
|
258
|
+
|
259
|
+
#################
|
260
|
+
### RULERSHIP ###
|
261
|
+
#################
|
262
|
+
|
263
|
+
def ruler(chart = nil)
|
264
|
+
ruler = RULERSHIPS.find{|_k, v| v.include? sign_sym }.first
|
265
|
+
if chart
|
266
|
+
chart.get_body(ruler)
|
267
|
+
else
|
268
|
+
ruler
|
269
|
+
end
|
270
|
+
end
|
271
|
+
alias dispositor ruler
|
272
|
+
|
273
|
+
def in_domicile?
|
274
|
+
RULERSHIPS[abbr].include?(sign_sym)
|
275
|
+
rescue NoMethodError
|
276
|
+
false
|
277
|
+
end
|
278
|
+
|
279
|
+
def in_fall? # opposite to rulership
|
280
|
+
RULERSHIPS[abbr].include?(rotate_sign(6))
|
281
|
+
rescue NoMethodError
|
282
|
+
false
|
283
|
+
end
|
284
|
+
|
285
|
+
def in_exaltation?
|
286
|
+
EXALTATION[abbr].first == sign_sym
|
287
|
+
rescue NoMethodError
|
288
|
+
false
|
289
|
+
end
|
290
|
+
|
291
|
+
def in_detriment?
|
292
|
+
EXALTATION[abbr].first == rotate_sign(6)
|
293
|
+
rescue NoMethodError
|
294
|
+
false
|
295
|
+
end
|
296
|
+
|
297
|
+
def retrograde?
|
298
|
+
@velocity&.negative?
|
299
|
+
end
|
300
|
+
|
301
|
+
# RETROGRADE = { # days
|
302
|
+
# ME: 24, VE: 42, MA: 80, JU: 120, SA: 140, UR: 150, NE: 160, PL: 160, CE: 60
|
303
|
+
# }
|
304
|
+
STATIONARY_VELOCITY = { # seconds/day from https://www.astro.com/astrowiki/en/Stationary_Phase
|
305
|
+
ME: 300, VE: 180, MA: 90, JU: 60, SA: 60, UR: 20, NE: 10, PL: 10, CE: 7
|
306
|
+
}.freeze
|
307
|
+
MAX_VELOCITY = { # in minutes from https://en.wikipedia.org/wiki/Planets_in_astrology
|
308
|
+
ME: (2 * 60) + 25, VE: (1 * 60) + 25, MA: 52, CE: 30, JU: 14 + (40 / 60.0), SA: 8 + (48 / 60.0),
|
309
|
+
UR: 4, NE: 2 + (25 / 60.0), PL: 2 + (30 / 60.0), PA: 40 + (30 / 60.0), JN: 39, VA: 36, CH: 10
|
310
|
+
}.freeze
|
311
|
+
AVG_VELOCITY = { # in minutes
|
312
|
+
SO: 59 + (8 / 60.0), MO: (13 * 60) + 11 + (36 / 60.0), ME: (1 * 60) + 23, VE: (1 * 60) + 12, MA: 31 + (27 / 60.0), CE: 12 + (40 / 60.0),
|
313
|
+
JU: 4 + (59 / 60.0), SA: 2 + (1 / 60.0), UR: 42 / 60.0, NE: 24 / 60.0, PL: 15 / 60.0, CH: 2,
|
314
|
+
PA: 12 + (20 / 60.0), JN: 14 + (15 / 60.0), VA: 16 + (15 / 60.0), SD: 0.289, ER: 6.307,
|
315
|
+
NN: 3.177
|
316
|
+
}.freeze
|
317
|
+
SPEED_ORDER = AVG_VELOCITY.sort_by{|_k, v| v }.reverse.map(&:first)
|
318
|
+
|
319
|
+
# Here is a snippet of the Python code I use for the speeds:
|
320
|
+
# # Adjust the speeds to be compatible with the old standalone version
|
321
|
+
# # of Haydn's Jyotish. { # Haydn Huntley haydn@hjyotish.com }
|
322
|
+
# daysPerEarthYear = 365.242199
|
323
|
+
# x = 360.0 / daysPerEarthYear ** 2
|
324
|
+
# self.speed[MOON] *= 0.09706 # Magic constant.
|
325
|
+
# self.speed[MARS] *= 686.971 * x # Orbital period in days,
|
326
|
+
# self.speed[JUPITER] *= 4332.59 * x # according to Wikipedia.
|
327
|
+
# self.speed[SATURN] *= 10759.22 * x
|
328
|
+
# self.speed[URANUS] *= 30799.095 * x
|
329
|
+
# self.speed[NEPTUNE] *= 60190.03 * x
|
330
|
+
# self.speed[PLUTO] *= 89865.65 * x
|
331
|
+
# self.speed[RAHU] *= 6585.3213 * x # Saros cycle in days.
|
332
|
+
# self.speed[KETU] *= 6585.3213 * x
|
333
|
+
|
334
|
+
def stationary?
|
335
|
+
return nil unless (minutes = MAX_VELOCITY[abbr])
|
336
|
+
|
337
|
+
velocity.abs <= (0.05 * minutes / 60.0) # 5% of velocity in degrees/day
|
338
|
+
end
|
339
|
+
|
340
|
+
def relative_velocity
|
341
|
+
return nil unless (minutes = AVG_VELOCITY[abbr])
|
342
|
+
|
343
|
+
begin
|
344
|
+
velocity / (minutes / 60.0)
|
345
|
+
rescue StandardError
|
346
|
+
1.0
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def house
|
351
|
+
@calc.which_house(lon)
|
352
|
+
end
|
353
|
+
|
354
|
+
def yin?
|
355
|
+
sign.odd?
|
356
|
+
end
|
357
|
+
|
358
|
+
def yang?
|
359
|
+
sign.even?
|
360
|
+
end
|
361
|
+
|
362
|
+
def modality
|
363
|
+
%i[c f m][sign % 3]
|
364
|
+
end
|
365
|
+
|
366
|
+
def element
|
367
|
+
%i[F E A W][sign % 4]
|
368
|
+
end
|
369
|
+
|
370
|
+
def element_sym
|
371
|
+
%w[🜂 🜃 🜁 🜄][sign % 4]
|
372
|
+
end
|
373
|
+
|
374
|
+
# qualities for interpretations
|
375
|
+
def feminine?
|
376
|
+
%i[MO VE NE].include?(abbr)
|
377
|
+
end
|
378
|
+
|
379
|
+
def masculine?
|
380
|
+
%i[SO MA JU UR PL].include?(abbr)
|
381
|
+
end
|
382
|
+
|
383
|
+
def outer?
|
384
|
+
%i[SA UR NE PL].include?(abbr)
|
385
|
+
end
|
386
|
+
|
387
|
+
def inner?
|
388
|
+
luminary? || %i[ME VE MA].include?(abbr)
|
389
|
+
end
|
390
|
+
|
391
|
+
def luminary?
|
392
|
+
%i[SO MO].include?(abbr)
|
393
|
+
end
|
394
|
+
|
395
|
+
def decan
|
396
|
+
((to_f % 30) / 10).floor + 1
|
397
|
+
end
|
398
|
+
|
399
|
+
#######################
|
400
|
+
# Harmonic Divisions
|
401
|
+
#######################
|
402
|
+
|
403
|
+
def harmonic_sign(h) # always use 1st harmonic longitude
|
404
|
+
SwissEphemeris::SIGNS[varga_sign(h)]
|
405
|
+
end
|
406
|
+
|
407
|
+
def triple_harmonic_signs(h)
|
408
|
+
harmonic_signs(1, h, h * h)
|
409
|
+
end
|
410
|
+
|
411
|
+
def harmonic_signs *args
|
412
|
+
args.map{|h| harmonic_sign(h) }
|
413
|
+
end
|
414
|
+
|
415
|
+
def harmonize!(h)
|
416
|
+
@harmonic = h
|
417
|
+
calculate!
|
418
|
+
self
|
419
|
+
end
|
420
|
+
|
421
|
+
def harmonize(h)
|
422
|
+
dup.harmonize!(h)
|
423
|
+
end
|
424
|
+
|
425
|
+
def *(other)
|
426
|
+
harmonize(other)
|
427
|
+
end
|
428
|
+
|
429
|
+
def pentan
|
430
|
+
harmonic_sign(6)
|
431
|
+
end
|
432
|
+
|
433
|
+
def subpentan
|
434
|
+
harmonic_sign(36)
|
435
|
+
end
|
436
|
+
|
437
|
+
def duad
|
438
|
+
SwissEphemeris::SIGNS[(varga_sign(12) - sign) % 12]
|
439
|
+
end
|
440
|
+
|
441
|
+
def subduad # is this correct
|
442
|
+
SwissEphemeris::SIGNS[(varga_sign(144) - sign) % 12]
|
443
|
+
end
|
444
|
+
|
445
|
+
def navamsha
|
446
|
+
harmonic_sign(9)
|
447
|
+
end
|
448
|
+
|
449
|
+
def dwad
|
450
|
+
harmonic_sign(12)
|
451
|
+
end
|
452
|
+
|
453
|
+
def subdwad
|
454
|
+
harmonic_sign(144)
|
455
|
+
end
|
456
|
+
|
457
|
+
def sign_and_house
|
458
|
+
"#{sign_sym.capitalize} in #{house.ordinalize}"
|
459
|
+
end
|
460
|
+
|
461
|
+
def midpoint?
|
462
|
+
is_a?(Astroscript::Midpoint)
|
463
|
+
end
|
464
|
+
|
465
|
+
def to_json(*_args)
|
466
|
+
d, m, * = AstroHelper.dms(lon)
|
467
|
+
key = "#{name.downcase} in #{SwissEphemeris::SIGNS[sign]}"
|
468
|
+
{
|
469
|
+
house: house,
|
470
|
+
name: name,
|
471
|
+
sign_degrees: d,
|
472
|
+
sign_minutes: m.round,
|
473
|
+
sign_name: SwissEphemeris::SIGNS[sign],
|
474
|
+
total_angle: @lon.round(2),
|
475
|
+
description: key.titleize,
|
476
|
+
key: key
|
477
|
+
}
|
478
|
+
end
|
479
|
+
|
480
|
+
private
|
481
|
+
|
482
|
+
def update!
|
483
|
+
@sign, @degree = (@lon * @harmonic).divmod(360)
|
484
|
+
@degree, m, s = AstroHelper.dms(@degree)
|
485
|
+
@minute = m.round
|
486
|
+
@minute_ = m.floor
|
487
|
+
@seconds = s.round
|
488
|
+
self
|
489
|
+
end
|
490
|
+
|
491
|
+
def varga_sign(h)
|
492
|
+
(harmonized_degree(h) / 30).floor
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|