katmlutils 0.0.1__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.
katmlutils/__init__.py ADDED
@@ -0,0 +1 @@
1
+
katmlutils/ga.py ADDED
@@ -0,0 +1,329 @@
1
+ import math
2
+ import json
3
+ import random
4
+ from datetime import datetime, date, time, timedelta
5
+
6
+ GLOBAL_VARS_FILE = "tmp/global_vars.json"
7
+
8
+ SECONDS_PER_DAY: int = 86400
9
+ SIDEREAL_DAY_SECONDS: int = 86164.0905
10
+ J2000: datetime = datetime(2000, 1, 1, 12) # Reference epoch for JD
11
+
12
+ # Constants for SKA site
13
+ SKA_LATITUDE_STR: str = "-30:42:39.8"
14
+ SKA_LONGITUDE_STR: str = "21:26:38.0"
15
+
16
+ ZENITH = 90 + 50 / 60 # Official zenith for sunrise/sunset in degrees
17
+
18
+ # MATH CONSTANT
19
+ TO_RAD = math.pi/180.0
20
+
21
+ # PLANE TIME CONSTANTS
22
+ PLANE_WEEK_DAY: int = 2 # Wednesday = 2 according to date.weekday()
23
+ PLANE_ARRIVAL_TIME_BLOCK: tuple[time, time] = (time(6, 0, 0), time(7, 0, 0)) # These values are in UTC
24
+ PLANE_DEPARTURE_TIME_BLOCK: tuple[time, time] = (time(13, 0, 0), time(15, 0, 0)) # These values are in UTC
25
+
26
+ def degrees_string_to_float(degrees: str) -> float:
27
+ """
28
+ Convert a string in the format "hh:mm:ss.s" to a float in degrees.
29
+
30
+ Args:
31
+ degrees (str): String in the format "hh:mm:ss.s".
32
+
33
+ Returns:
34
+ float: Float representation in degrees.
35
+ """
36
+ sign = -1 if degrees.startswith("-") else 1
37
+ if sign == -1:
38
+ degrees = degrees[1:]
39
+ h, m, s = map(float, degrees.split(":"))
40
+ return sign * (h + m / 60 + s / 3600)
41
+
42
+ SKA_LATITUDE: float = degrees_string_to_float(SKA_LATITUDE_STR)
43
+ SKA_LONGITUDE: float = degrees_string_to_float(SKA_LONGITUDE_STR)
44
+
45
+ def get_global_vars() -> tuple[date, date, list[dict]]:
46
+ """
47
+ Get the current values of the global variables from the JSON file.
48
+
49
+ Args:
50
+ None
51
+
52
+ Returns:
53
+ tuple[date, date, list[dict]]: The current values of START_DATE, END_DATE, and PROPOSALS.
54
+ """
55
+ try:
56
+ with open(GLOBAL_VARS_FILE, "r") as file:
57
+ data = json.load(file)
58
+ start_date = date.fromisoformat(data["START_DATE"])
59
+ end_date = date.fromisoformat(data["END_DATE"])
60
+ proposals = data["PROPOSALS"]
61
+ return start_date, end_date, proposals
62
+ except FileNotFoundError:
63
+ return date(2024, 1, 1), date(2024, 1, 22), []
64
+
65
+ def update_global_vars(start_date: date = date.today(), end_date: date = date.today(),proposals: list[dict] = []) -> None:
66
+ """
67
+ Update the global variables and write them to the JSON file.
68
+
69
+ Args:
70
+ start_date (date): The new start date.
71
+ end_date (date): The new end date.
72
+ proposals (list[dict]): The new list of proposal data.
73
+
74
+ Returns:
75
+ None
76
+ """
77
+ data = {
78
+ "START_DATE": start_date.isoformat(),
79
+ "END_DATE": end_date.isoformat(),
80
+ "PROPOSALS": proposals
81
+ }
82
+ with open(GLOBAL_VARS_FILE, "w") as file:
83
+ json.dump(data, file)
84
+
85
+ def parse_time(time_str: str) -> time:
86
+ """
87
+ Parses a time string in the format "HH:MM" and returns a datetime.time object.
88
+
89
+ Args:
90
+ time_str (str): A time string in the format "HH:MM".
91
+
92
+ Returns:
93
+ time: A datetime.time object representing the parsed time.
94
+ """
95
+ hour, minute = map(int, time_str.split(":"))
96
+ return time(hour, minute)
97
+
98
+ def julian_date(date_obj: datetime) -> float:
99
+ """
100
+ Convert a datetime to Julian Date.
101
+ """
102
+ a = (14 - date_obj.month) // 12
103
+ y = date_obj.year + 4800 - a
104
+ m = date_obj.month + 12 * a - 3
105
+
106
+ jdn = date_obj.day + ((153 * m + 2) // 5) + 365 * y + y // 4 - y // 100 + y // 400 - 32045
107
+ day_frac = (date_obj.hour - 12) / 24 + date_obj.minute / 1440 + date_obj.second / 86400
108
+ return jdn + day_frac
109
+
110
+ def gmst_at_0h_utc(jd: float) -> float:
111
+ """
112
+ Compute GMST at 0h UTC in decimal hours.
113
+ """
114
+ d = jd - 2451545.0 # Days since J2000
115
+ gmst = 6.697374558 + 0.06570982441908 * d + 1.00273790935 * 0
116
+ return gmst % 24
117
+
118
+ def lst_to_utc(date_obj: date, lst_time: time, longitude: float = SKA_LONGITUDE) -> datetime:
119
+ """
120
+ Convert Local Sidereal Time (LST) to UTC datetime (approximate method).
121
+
122
+ Args:
123
+ date_obj (date): UTC calendar date.
124
+ lst_time (time): LST as time object.
125
+ longitude (float): Observer longitude in degrees East (default is SKA site).
126
+
127
+ Returns:
128
+ datetime: Approximate UTC datetime corresponding to the given LST.
129
+ """
130
+ # 1. Convert LST to decimal hours
131
+ lst_hours: float = lst_time.hour + lst_time.minute / 60 + lst_time.second / 3600
132
+
133
+ # 2. Convert observer longitude from degrees to hours
134
+ longitude_hours: float = longitude / 15.0
135
+
136
+ # 3. Compute Julian Date at 0h UTC for the given date
137
+ jd_0h: float = julian_date(datetime.combine(date_obj, time(0, 0, 0)))
138
+
139
+ # 4. Compute GMST at 0h UTC
140
+ gmst0: float = gmst_at_0h_utc(jd_0h)
141
+
142
+ # 5. Calculate Greenwich Sidereal Time (GST) from Local Sidereal Time (LST)
143
+ gst: float = (lst_hours - longitude_hours) % 24
144
+
145
+ # 6. Difference between GST and GMST at 0h UTC (in sidereal hours)
146
+ delta_sidereal_hours: float = (gst - gmst0) % 24
147
+
148
+ # 7. Convert sidereal time to solar (UTC) time
149
+ SIDEREAL_TO_SOLAR: float = 0.9972695663 # conversion factor
150
+ delta_utc_hours: float = delta_sidereal_hours * SIDEREAL_TO_SOLAR
151
+
152
+ # 8. Compute the UTC datetime
153
+ utc_datetime: datetime = datetime.combine(date_obj, time(0, 0, 0)) + timedelta(hours=delta_utc_hours)
154
+
155
+ return utc_datetime.replace(microsecond=0)
156
+
157
+ def get_sunrise_sunset(date: date, latitude: float = SKA_LATITUDE, longitude: float = SKA_LONGITUDE) -> tuple[datetime, datetime]:
158
+ """
159
+ Calculate sunrise and sunset times for a given date, latitude, and longitude.
160
+
161
+ Args:
162
+ date (date): The date for which to calculate the sunrise and sunset.
163
+ latitude (float): The latitude of the location.
164
+ longitude (float): The longitude of the location.
165
+
166
+ Returns:
167
+ tuple[datetime, datetime]: The sunrise and sunset times as naive datetime objects.
168
+ """
169
+
170
+ # 1. Get the day of the year
171
+ N = date.timetuple().tm_yday
172
+
173
+ # 2. Convert the longitude to hour value and calculate an approximate time
174
+ lng_hour = longitude / 15.0
175
+ t_rise = N + ((6 - lng_hour) / 24) # For sunrise
176
+ t_set = N + ((18 - lng_hour) / 24) # For sunset
177
+
178
+ # 3a. Calculate the Sun's mean anomaly
179
+ M_rise = (0.9856 * t_rise) - 3.289
180
+ M_set = (0.9856 * t_set) - 3.289
181
+
182
+ # 3b. Calculate the Sun's true longitude
183
+ L_rise = M_rise + (1.916 * math.sin(TO_RAD * M_rise)) + (0.020 * math.sin(TO_RAD * 2 * M_rise)) + 282.634
184
+ L_set = M_set + (1.916 * math.sin(TO_RAD * M_set)) + (0.020 * math.sin(TO_RAD * 2 * M_set)) + 282.634
185
+
186
+ # Adjust L into the range [0, 360)
187
+ L_rise = force_range(L_rise, 360)
188
+ L_set = force_range(L_set, 360)
189
+
190
+ # 4a. Calculate the Sun's declination
191
+ sinDec_rise = 0.39782 * math.sin(TO_RAD * L_rise)
192
+ cosDec_rise = math.cos(math.asin(sinDec_rise))
193
+
194
+ sinDec_set = 0.39782 * math.sin(TO_RAD * L_set)
195
+ cosDec_set = math.cos(math.asin(sinDec_set))
196
+
197
+ # 4b. Calculate the Sun's local hour angle
198
+ cosH_rise = (math.cos(TO_RAD * ZENITH) - (sinDec_rise * math.sin(TO_RAD * latitude))) / (cosDec_rise * math.cos(TO_RAD * latitude))
199
+ cosH_set = (math.cos(TO_RAD * ZENITH) - (sinDec_set * math.sin(TO_RAD * latitude))) / (cosDec_set * math.cos(TO_RAD * latitude))
200
+
201
+ # Check if the sun never rises or sets
202
+ if cosH_rise > 1:
203
+ return None, None # The sun never rises
204
+ if cosH_set < -1:
205
+ return None, None # The sun never sets
206
+
207
+ # 4c. Finish calculating H and convert into hours
208
+ H_rise = 360 - (1 / TO_RAD) * math.acos(cosH_rise)
209
+ H_set = (1 / TO_RAD) * math.acos(cosH_set)
210
+
211
+ H_rise /= 15
212
+ H_set /= 15
213
+
214
+ # 5a. Calculate the Sun's right ascension
215
+ RA_rise = (1 / TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD * L_rise))
216
+ RA_set = (1 / TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD * L_set))
217
+
218
+ # Adjust RA into the range [0, 360)
219
+ RA_rise = force_range(RA_rise, 360)
220
+ RA_set = force_range(RA_set, 360)
221
+
222
+ # 5b. Right ascension value needs to be in the same quadrant as L
223
+ L_quadrant_rise = (math.floor(L_rise / 90)) * 90
224
+ RA_quadrant_rise = (math.floor(RA_rise / 90)) * 90
225
+ RA_rise += (L_quadrant_rise - RA_quadrant_rise)
226
+
227
+ L_quadrant_set = (math.floor(L_set / 90)) * 90
228
+ RA_quadrant_set = (math.floor(RA_set / 90)) * 90
229
+ RA_set += (L_quadrant_set - RA_quadrant_set)
230
+
231
+ # 5c. Right ascension value needs to be converted into hours
232
+ RA_rise /= 15
233
+ RA_set /= 15
234
+
235
+ # 6. Calculate local mean time of rising/setting
236
+ T_rise = H_rise + RA_rise - (0.06571 * t_rise) - 6.622
237
+ T_set = H_set + RA_set - (0.06571 * t_set) - 6.622
238
+
239
+ # 7. Adjust back to UTC
240
+ UT_rise = T_rise - lng_hour
241
+ UT_set = T_set - lng_hour
242
+
243
+ # 7c. rounding and impose range bounds
244
+ UT_rise = round(UT_rise, 2)
245
+ UT_set = round(UT_set, 2)
246
+
247
+ UT_rise = force_range(UT_rise, 24.0)
248
+ UT_set = force_range(UT_set, 24.0)
249
+
250
+ # Convert UT to naive datetime objects
251
+ sunrise = datetime.combine(date, time(0, 0, 0)) + timedelta(hours=UT_rise)
252
+ sunset = datetime.combine(date, time(0, 0, 0)) + timedelta(hours=UT_set)
253
+
254
+ return sunrise.replace(second=0, microsecond=0), sunset.replace(second=0, microsecond=0)
255
+
256
+ def force_range(value: float, max_value: float):
257
+ """
258
+ Adjusts the value to wrap around within the range [0, max).
259
+
260
+ Args:
261
+ value (float): The value to adjust.
262
+ max_value (float): The exclusive upper bound of the range.
263
+
264
+ Returns:
265
+ float: The adjusted value within the range [0, max).
266
+ """
267
+ if value < 0:
268
+ return value + max_value
269
+ elif value >= max_value:
270
+ return value - max_value
271
+ return value
272
+
273
+ def get_night_window(date: date, latitude: float = SKA_LATITUDE, longitude: float = SKA_LONGITUDE) -> tuple[datetime, datetime]:
274
+ """
275
+ Calculate the night window for a given date and geographic location. The night window is defined as starting 5 minutes after sunset on the specified date and ending 5 minutes before sunrise on the following day.
276
+
277
+ Args:
278
+ date (date): The date for which to calculate the night window.
279
+ latitude (float): The latitude of the location for which the night window is calculated. Default is set to SKA_LATITUDE.
280
+ longitude (float): The longitude of the location for which the night window is calculated. Default is set to SKA_LONGITUDE.
281
+
282
+ Returns:
283
+ tuple[datetime, datetime]: A tuple containing night start and end datetime.
284
+ """
285
+ # Getting sunset and sunrise of the current and next day
286
+ _, current_day_sunset = get_sunrise_sunset(date, latitude, longitude)
287
+ next_day_sunsrise, _ = get_sunrise_sunset(date + timedelta(days=1), latitude, longitude)
288
+
289
+ # Assuming night starts 5 min after sunset of the current day and ends 5 minutes before sunrise the next day
290
+ return (current_day_sunset + timedelta(minutes=5), next_day_sunsrise - timedelta(minutes=5))
291
+
292
+ def get_plane_arrival_and_departure_blocks(
293
+ date: date,
294
+ plane_weekday: int = PLANE_WEEK_DAY,
295
+ plane_arrival_time_block: tuple[time, time] = PLANE_ARRIVAL_TIME_BLOCK,
296
+ plane_departure_time_block: tuple[time, time] = PLANE_DEPARTURE_TIME_BLOCK
297
+ ) -> list[tuple[datetime, datetime]]:
298
+ """
299
+ Retrieve the plane arrival and departure time blocks for a specified date.
300
+
301
+ Args:
302
+ date (date): The date for which to retrieve the plane arrival and departure blocks.
303
+ plane_weekday (int): The weekday (0=Monday, 6=Sunday) when the plane operates.
304
+ Defaults to PLANE_WEEK_DAY.
305
+ plane_arrival_time_block (tuple[time, time]): A tuple representing the start and end time
306
+ for plane arrivals. Defaults to PLANE_ARRIVAL_TIME_BLOCK.
307
+ plane_departure_time_block (tuple[time, time]): A tuple representing the start and end time
308
+ for plane departures. Defaults to PLANE_DEPARTURE_TIME_BLOCK.
309
+
310
+ Returns:
311
+ list[tuple[datetime, datetime]]: A list of tuples, each containing:
312
+ - Arrival or departure start time as a datetime object.
313
+ - Arrival or departure end time as a datetime object.
314
+ """
315
+ # Initialize an empty list to hold the arrival and departure blocks
316
+ plane_arrival_and_departure_blocks: list[tuple[datetime, datetime]] = []
317
+
318
+ # Check if the specified date is the correct weekday for operations
319
+ if date.weekday() == plane_weekday:
320
+ # Define the time blocks for arrival and departure
321
+ time_blocks = [plane_arrival_time_block, plane_departure_time_block]
322
+
323
+ # Combine the date with each time block and append to the list
324
+ for (start_time, end_time) in time_blocks:
325
+ start_datetime = datetime.combine(date, start_time)
326
+ end_datetime = datetime.combine(date, end_time)
327
+ plane_arrival_and_departure_blocks.append((start_datetime, end_datetime))
328
+
329
+ return plane_arrival_and_departure_blocks
katmlutils/rl.py ADDED
File without changes
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: katmlutils
3
+ Version: 0.0.1
4
+ Summary: This is a package that keeps all the Machine learning Utils in the Software Team.
5
+ Author-email: SARAO MLOps <saraomlops@gmail.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: machine learning,utils
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+
15
+ # SARAO Machine Learning Utils
@@ -0,0 +1,8 @@
1
+ katmlutils/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
+ katmlutils/ga.py,sha256=GCXkWK1FTdjszlnquF-8CNsW2eF4Y9xFOXbqF5qmvO0,12792
3
+ katmlutils/rl.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ katmlutils-0.0.1.dist-info/METADATA,sha256=Beka4ZKgqMR08dGPSd8jt76HIuik7WMVJBFB1e1BXeY,504
5
+ katmlutils-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
6
+ katmlutils-0.0.1.dist-info/entry_points.txt,sha256=OwUQh7vusegJ0YnwJxULL6njZ_AU6Eldjy1pZ0OfW6k,47
7
+ katmlutils-0.0.1.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ katmlutils-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mlutils = katmlutils.ga:main
File without changes