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,,
|
|
File without changes
|