libephemeris 0.1.6__py3-none-any.whl

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,151 @@
1
+ from .constants import *
2
+ from .time_utils import swe_julday, swe_revjul, swe_deltat
3
+ from .planets import (
4
+ swe_calc_ut,
5
+ swe_calc,
6
+ swe_get_ayanamsa_ut,
7
+ swe_get_ayanamsa,
8
+ swe_set_sid_mode,
9
+ )
10
+ from .houses import swe_houses, swe_houses_ex, swe_house_name
11
+ from .state import set_topo as swe_set_topo, set_ephe_path as swe_set_ephe_path
12
+ from .crossing import swe_solcross_ut, swe_mooncross_ut, swe_cross_ut
13
+ from .utils import difdeg2n
14
+ from .fixed_stars import swe_fixstar_ut
15
+
16
+ # =============================================================================
17
+ # PYSWISSEPH-COMPATIBLE FUNCTION ALIASES (without swe_ prefix)
18
+ # =============================================================================
19
+ # pyswisseph uses function names without the swe_ prefix
20
+ # These aliases provide 100% API compatibility with pyswisseph
21
+
22
+ # Time functions
23
+ julday = swe_julday
24
+ revjul = swe_revjul
25
+ deltat = swe_deltat
26
+
27
+ # Planet calculation
28
+ calc_ut = swe_calc_ut
29
+ calc = swe_calc
30
+
31
+ # Houses
32
+ houses = swe_houses
33
+ houses_ex = swe_houses_ex
34
+ house_name = swe_house_name
35
+
36
+ # Ayanamsa (sidereal)
37
+ get_ayanamsa_ut = swe_get_ayanamsa_ut
38
+ get_ayanamsa = swe_get_ayanamsa
39
+ set_sid_mode = swe_set_sid_mode
40
+
41
+ # Observer location
42
+ set_topo = swe_set_topo
43
+ set_ephe_path = swe_set_ephe_path
44
+
45
+ # Fixed Stars
46
+ fixstar_ut = swe_fixstar_ut
47
+
48
+ # Crossings
49
+ solcross_ut = swe_solcross_ut
50
+ mooncross_ut = swe_mooncross_ut
51
+
52
+
53
+ # Helper function to pre-calculate positions for Arabic parts
54
+ def swe_calc_angles(jd_ut: float, lat: float, lon: float):
55
+ """
56
+ Pre-calculate and cache astrological angles and planet positions
57
+ for use with Arabic parts.
58
+
59
+ Args:
60
+ jd_ut: Julian Day (UT)
61
+ lat: Latitude (degrees)
62
+ lon: Longitude (degrees)
63
+
64
+ Returns:
65
+ Dictionary with calculated positions
66
+ """
67
+ from .state import set_angles_cache
68
+ from .angles import calc_angles
69
+
70
+ # Set observer location
71
+ swe_set_topo(lon, lat, 0)
72
+
73
+ # Calculate angles
74
+ angles_dict = calc_angles(jd_ut, lat, lon)
75
+
76
+ # Calculate and add planet positions for Arabic parts
77
+ from .constants import SE_SUN, SE_MOON, SE_MERCURY, SE_VENUS
78
+
79
+ sun_pos, _ = swe_calc_ut(jd_ut, SE_SUN, 0)
80
+ moon_pos, _ = swe_calc_ut(jd_ut, SE_MOON, 0)
81
+ mercury_pos, _ = swe_calc_ut(jd_ut, SE_MERCURY, 0)
82
+ venus_pos, _ = swe_calc_ut(jd_ut, SE_VENUS, 0)
83
+
84
+ angles_dict['Sun'] = sun_pos[0]
85
+ angles_dict['Moon'] = moon_pos[0]
86
+ angles_dict['Mercury'] = mercury_pos[0]
87
+ angles_dict['Venus'] = venus_pos[0]
88
+
89
+ # Cache for Arabic parts
90
+ set_angles_cache(angles_dict)
91
+
92
+ return angles_dict
93
+
94
+ # Helper for Arabic parts
95
+ from .arabic_parts import calc_all_arabic_parts
96
+
97
+ # Constants (planet IDs, flags, sidereal modes)
98
+ from .constants import *
99
+
100
+ __version__ = "0.1.0"
101
+ __author__ = "Giacomo Battaglia"
102
+ __license__ = "LGPL-3.0"
103
+
104
+ __all__ = [
105
+ # Time functions (both swe_ and non-prefixed aliases)
106
+ "swe_julday",
107
+ "julday",
108
+ "swe_revjul",
109
+ "revjul",
110
+ "swe_deltat",
111
+ "deltat",
112
+ # Planet calculation
113
+ "swe_calc_ut",
114
+ "calc_ut",
115
+ "swe_calc",
116
+ "calc",
117
+ "swe_get_planet_name",
118
+ # Houses
119
+ "swe_houses",
120
+ "houses",
121
+ "swe_houses_ex",
122
+ "houses_ex",
123
+ "swe_house_pos",
124
+ "swe_house_name",
125
+ # Ayanamsa (sidereal)
126
+ "swe_set_sid_mode",
127
+ "set_sid_mode",
128
+ "swe_get_ayanamsa_ut",
129
+ "get_ayanamsa_ut",
130
+ "swe_get_ayanamsa",
131
+ "get_ayanamsa",
132
+ "swe_get_ayanamsa_name",
133
+ # Observer location
134
+ "swe_set_topo",
135
+ "set_topo",
136
+ "swe_set_ephe_path",
137
+ "set_ephe_path",
138
+ # Crossings
139
+ "swe_solcross_ut",
140
+ "solcross_ut",
141
+ "swe_mooncross_ut",
142
+ "mooncross_ut",
143
+ "swe_cross_ut",
144
+ # Utilities
145
+ "difdeg2n",
146
+ # Helpers
147
+ "calc_all_arabic_parts",
148
+ # Fixed Stars
149
+ "swe_fixstar_ut",
150
+ "fixstar_ut",
151
+ ]
libephemeris/angles.py ADDED
@@ -0,0 +1,106 @@
1
+ """
2
+ Astrological angles and chart points for libephemeris.
3
+
4
+ This module calculates the primary astrological angles:
5
+ - Ascendant (ASC): Ecliptic degree rising on eastern horizon
6
+ - Midheaven (MC): Ecliptic degree culminating on meridian
7
+ - Descendant (DSC): Western horizon point (ASC + 180°)
8
+ - Imum Coeli (IC): Lower meridian (MC + 180°)
9
+ - Vertex: Intersection of prime vertical with western ecliptic
10
+ - Equatorial Ascendant: Equator crossing eastern horizon
11
+
12
+ All angles require observer geographic location (swe_set_topo).
13
+ Calculations are based on spherical astronomy and house system cusps.
14
+
15
+ Note:
16
+ Angles are independent of house system choice (though computed via houses).
17
+ They represent fundamental horizon/meridian intersections with the ecliptic.
18
+ """
19
+
20
+ from typing import Dict
21
+ from .constants import (
22
+ SE_ASCENDANT,
23
+ SE_MC,
24
+ SE_DESCENDANT,
25
+ SE_IC,
26
+ SE_VERTEX,
27
+ SE_ANTIVERTEX,
28
+ )
29
+ from .houses import swe_houses
30
+
31
+
32
+ def calc_angles(jd_ut: float, lat: float, lon: float) -> Dict[str, float]:
33
+ """
34
+ Calculate all astrological angles for a given time and location.
35
+
36
+ Args:
37
+ jd_ut: Julian Day in Universal Time (UT1)
38
+ lat: Geographic latitude in degrees (North positive)
39
+ lon: Geographic longitude in degrees (East positive)
40
+
41
+ Returns:
42
+ Dict[str, float]: Dictionary mapping angle names to ecliptic longitudes:
43
+ - "Ascendant": Rising degree on eastern horizon
44
+ - "MC": Midheaven (culminating degree)
45
+ - "ARMC": Right Ascension of MC in degrees
46
+ - "Vertex": Prime vertical intersection (west)
47
+ - "Equatorial_Ascendant": Equator on eastern horizon
48
+ - "Descendant": Setting degree (Asc + 180°)
49
+ - "IC": Lower meridian (MC + 180°)
50
+ - "AntiVertex": Prime vertical intersection (east)
51
+
52
+ Note:
53
+ Uses Placidus house system internally, but angle values are
54
+ system-independent. They are purely geometric intersections.
55
+
56
+ Angles are sensitive to observer location. Polar regions may
57
+ have undefined values for some angles.
58
+ """
59
+ _, ascmc = swe_houses(jd_ut, lat, lon, ord("P"))
60
+
61
+ return {
62
+ "Ascendant": ascmc[0],
63
+ "MC": ascmc[1],
64
+ "ARMC": ascmc[2],
65
+ "Vertex": ascmc[3],
66
+ "Equatorial_Ascendant": ascmc[4] if len(ascmc) > 4 else 0.0,
67
+ "Descendant": (ascmc[0] + 180.0) % 360.0,
68
+ "IC": (ascmc[1] + 180.0) % 360.0,
69
+ "AntiVertex": (ascmc[3] + 180.0) % 360.0,
70
+ }
71
+
72
+
73
+ def get_angle_value(angle_id: int, jd_ut: float, lat: float, lon: float) -> float:
74
+ """
75
+ Get ecliptic longitude for a specific angle.
76
+
77
+ Args:
78
+ angle_id: Angle identifier constant (SE_ASCENDANT, SE_MC, etc.)
79
+ jd_ut: Julian Day in Universal Time (UT1)
80
+ lat: Geographic latitude in degrees (North positive)
81
+ lon: Geographic longitude in degrees (East positive)
82
+
83
+ Returns:
84
+ float: Ecliptic longitude of the angle in degrees (0-360)
85
+ Returns 0.0 if angle_id is not recognized
86
+
87
+ Example:
88
+ >>> asc = get_angle_value(SE_ASCENDANT, jd, 41.9, 12.5)
89
+ >>> mc = get_angle_value(SE_MC, jd, 41.9, 12.5)
90
+ """
91
+ angles = calc_angles(jd_ut, lat, lon)
92
+
93
+ angle_map = {
94
+ SE_ASCENDANT: "Ascendant",
95
+ SE_MC: "MC",
96
+ SE_DESCENDANT: "Descendant",
97
+ SE_IC: "IC",
98
+ SE_VERTEX: "Vertex",
99
+ SE_ANTIVERTEX: "AntiVertex",
100
+ }
101
+
102
+ if angle_id in angle_map:
103
+ return angles[angle_map[angle_id]]
104
+
105
+ return 0.0
106
+
@@ -0,0 +1,232 @@
1
+ """
2
+ Arabic Parts (Lots) calculations for libephemeris.
3
+
4
+ Arabic Parts are classical astrological points calculated from combinations
5
+ of planetary positions and angles. They represent specific life areas and
6
+ are used in Hellenistic, Medieval, and modern astrology.
7
+
8
+ Formulas follow traditional methods from:
9
+ - Dorotheus of Sidon (1st century CE)
10
+ - Vettius Valens (2nd century CE)
11
+ - Al-Biruni "Book of Instruction" (11th century)
12
+ - Robert Schmidt translations (Project Hindsight)
13
+
14
+ Standard formula structure: ASC + [Point A] - [Point B]
15
+ Many parts have day/night variations for sect consideration.
16
+
17
+ Supported Parts:
18
+ - Part of Fortune (Pars Fortunae): Body, health, material success
19
+ - Part of Spirit (Pars Spiritus): Soul, intellect, spiritual matters
20
+ - Part of Love (Pars Amoris/Eros): Romance, desire, relationships
21
+ - Part of Faith (Pars Fidei): Belief, religion, trust
22
+ """
23
+
24
+ from typing import Dict
25
+ from .constants import (
26
+ SE_PARS_FORTUNAE,
27
+ SE_PARS_SPIRITUS,
28
+ SE_PARS_AMORIS,
29
+ SE_PARS_FIDEI,
30
+ )
31
+
32
+
33
+ def calc_arabic_part_of_fortune(
34
+ asc: float, sun: float, moon: float, is_diurnal: bool = True
35
+ ) -> float:
36
+ """
37
+ Calculate Part of Fortune (Pars Fortunae / Lot of Fortune).
38
+
39
+ The most important Arabic Part, representing body, health, and material fortune.
40
+
41
+ Formula (Vettius Valens, Al-Biruni):
42
+ - Day chart (Sun above horizon): ASC + Moon - Sun
43
+ - Night chart (Sun below horizon): ASC + Sun - Moon
44
+
45
+ Args:
46
+ asc: Ascendant (ecliptic) longitude in degrees
47
+ sun: Sun ecliptic longitude in degrees
48
+ moon: Moon ecliptic longitude in degrees
49
+ is_diurnal: True if day chart (Sun above horizon), False if night
50
+
51
+ Returns:
52
+ float: Part of Fortune ecliptic longitude in degrees (0-360)
53
+
54
+ Note:
55
+ The formula reflects sect: in day charts, emphasize the Moon (nocturnal);
56
+ in night charts, emphasize the Sun (diurnal). This balances opposites.
57
+
58
+ Some modern astrologers use only the day formula regardless of sect.
59
+ This implementation follows classical tradition.
60
+ """
61
+ if is_diurnal:
62
+ lot = (asc + moon - sun) % 360.0
63
+ else:
64
+ lot = (asc + sun - moon) % 360.0
65
+
66
+ return lot
67
+
68
+
69
+ def calc_arabic_part_of_spirit(
70
+ asc: float, sun: float, moon: float, is_diurnal: bool = True
71
+ ) -> float:
72
+ """
73
+ Calculate Part of Spirit (Pars Spiritus / Lot of Spirit / Daimon).
74
+
75
+ Represents the soul, intellect, character, and spiritual development.
76
+
77
+ Formula (opposite of Part of Fortune by sect):
78
+ - Day chart: ASC + Sun - Moon
79
+ - Night chart: ASC + Moon - Sun
80
+
81
+ Args:
82
+ asc: Ascendant longitude in degrees
83
+ sun: Sun longitude in degrees
84
+ moon: Moon longitude in degrees
85
+ is_diurnal: True if day chart
86
+
87
+ Returns:
88
+ float: Part of Spirit longitude in degrees (0-360)
89
+
90
+ Note:
91
+ In Hellenistic astrology (Valens), Part of Spirit was called "Daimon"
92
+ and considered complementary to Fortune. Together they represent
93
+ body (Fortune) and soul (Spirit).
94
+ """
95
+ if is_diurnal:
96
+ lot = (asc + sun - moon) % 360.0
97
+ else:
98
+ lot = (asc + moon - sun) % 360.0
99
+
100
+ return lot
101
+
102
+
103
+ def calc_arabic_part_of_love(asc: float, venus: float, sun: float) -> float:
104
+ """
105
+ Calculate Part of Love (Pars Amoris / Lot of Eros).
106
+
107
+ Represents romantic love, desire, sexual attraction, and relationships.
108
+
109
+ Formula: ASC + Venus - Sun
110
+
111
+ Args:
112
+ asc: Ascendant longitude in degrees
113
+ venus: Venus longitude in degrees
114
+ sun: Sun longitude in degrees
115
+
116
+ Returns:
117
+ float: Part of Love longitude in degrees (0-360)
118
+
119
+ Note:
120
+ This formula is not sect-dependent. Venus naturally signifies
121
+ love and attraction in all charts. Some medieval sources use
122
+ alternate formulas with Moon or Mars for male/female charts.
123
+ """
124
+ return (asc + venus - sun) % 360.0
125
+
126
+
127
+ def calc_arabic_part_of_faith(asc: float, mercury: float, moon: float) -> float:
128
+ """
129
+ Calculate Part of Faith (Pars Fidei / Lot of Faith).
130
+
131
+ Represents religious belief, trust, philosophical convictions.
132
+
133
+ Formula: ASC + Mercury - Moon
134
+
135
+ Args:
136
+ asc: Ascendant longitude in degrees
137
+ mercury: Mercury longitude in degrees
138
+ moon: Moon longitude in degrees
139
+
140
+ Returns:
141
+ float: Part of Faith longitude in degrees (0-360)
142
+
143
+ Note:
144
+ Mercury represents rational thought and communication of beliefs.
145
+ The Moon represents unconscious reception and emotional faith.
146
+ This part shows how one's beliefs are communicated/manifested.
147
+ """
148
+ return (asc + mercury - moon) % 360.0
149
+
150
+
151
+ def is_day_chart(sun_lon: float, asc: float) -> bool:
152
+ """
153
+ Determine if chart is diurnal (day) or nocturnal (night) based on sect.
154
+
155
+ A chart is diurnal if the Sun is above the horizon (in houses 7-12).
156
+
157
+ Args:
158
+ sun_lon: Sun ecliptic longitude in degrees (0-360)
159
+ asc: Ascendant ecliptic longitude in degrees (0-360)
160
+
161
+ Returns:
162
+ bool: True if day chart (Sun above horizon), False if night chart
163
+
164
+ Algorithm:
165
+ The horizon runs from Ascendant (east) to Descendant (west).
166
+ Points between ASC and DSC (going counter-clockwise through MC)
167
+ are above the horizon. This is the zodiacal arc from ASC to ASC+180°.
168
+
169
+ Note:
170
+ This is a simplified 2D calculation using ecliptic longitude only.
171
+ It assumes the horizon plane intersects the ecliptic at ASC/DSC.
172
+
173
+ For extreme latitudes or precise calculations, use 3D horizon
174
+ coordinates (altitude/azimuth). This method is traditional and
175
+ sufficient for most astrological purposes.
176
+ """
177
+ desc = (asc + 180.0) % 360.0
178
+
179
+ # Check if Sun is in upper hemisphere (ASC to DSC counter-clockwise)
180
+ if asc < desc:
181
+ # Normal case: e.g., ASC=0°, DSC=180°
182
+ return asc <= sun_lon <= desc
183
+ else:
184
+ # Wrapped case: e.g., ASC=350°, DSC=170°
185
+ # Sun is above if >= ASC (e.g., 350-360°) OR <= DSC (e.g., 0-170°)
186
+ return sun_lon >= asc or sun_lon <= desc
187
+
188
+
189
+ def calc_all_arabic_parts(positions: Dict[str, float]) -> Dict[str, float]:
190
+ """
191
+ Calculate all standard Arabic parts from a position dictionary.
192
+
193
+ Args:
194
+ positions: Dictionary of celestial positions in ecliptic longitude degrees.
195
+ Required keys: 'Asc', 'Sun', 'Moon', 'Mercury', 'Venus'
196
+ All values should be in range 0-360 degrees.
197
+
198
+ Returns:
199
+ Dict[str, float]: Dictionary mapping part names to longitudes:
200
+ - "Pars_Fortunae": Part of Fortune
201
+ - "Pars_Spiritus": Part of Spirit
202
+ - "Pars_Amoris": Part of Love
203
+ - "Pars_Fidei": Part of Faith
204
+
205
+ Example:
206
+ >>> positions = {
207
+ ... 'Asc': 15.5, 'Sun': 120.0, 'Moon': 240.0,
208
+ ... 'Mercury': 130.0, 'Venus': 110.0
209
+ ... }
210
+ >>> parts = calc_all_arabic_parts(positions)
211
+ >>> print(parts['Pars_Fortunae'])
212
+ 135.5
213
+
214
+ Note:
215
+ Missing keys will default to 0.0. Ensure all required positions
216
+ are present for accurate results.
217
+ """
218
+ asc = positions.get("Asc", 0.0)
219
+ sun = positions.get("Sun", 0.0)
220
+ moon = positions.get("Moon", 0.0)
221
+ mercury = positions.get("Mercury", 0.0)
222
+ venus = positions.get("Venus", 0.0)
223
+
224
+ is_diurnal = is_day_chart(sun, asc)
225
+
226
+ return {
227
+ "Pars_Fortunae": calc_arabic_part_of_fortune(asc, sun, moon, is_diurnal),
228
+ "Pars_Spiritus": calc_arabic_part_of_spirit(asc, sun, moon, is_diurnal),
229
+ "Pars_Amoris": calc_arabic_part_of_love(asc, venus, sun),
230
+ "Pars_Fidei": calc_arabic_part_of_faith(asc, mercury, moon),
231
+ }
232
+