Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a881bd6
Adding WDS import, and a new way of handling object images
mrosseel Oct 21, 2024
1dea305
Fix missing catalogs, get_objects query and unit tests
mrosseel Oct 21, 2024
5d950e4
Merge remote-tracking branch 'origin/main' into wds
mrosseel Dec 10, 2024
2148a57
Merge remote-tracking branch 'origin/main' into wds
mrosseel Jan 20, 2025
6209561
updated db
mrosseel Jan 20, 2025
5b8c022
Merge remote-tracking branch 'upstream/main' into wds
mrosseel Sep 6, 2025
73785ad
optimise WDS catalog import speed, merge into new catalog_imports
mrosseel Sep 9, 2025
7da2aff
Update db, add readme
mrosseel Sep 9, 2025
5648797
Reset tetra3 submodule to match upstream/main
mrosseel Sep 9, 2025
7a88eae
Merge remote-tracking branch 'upstream/main' into wds
mrosseel Sep 21, 2025
5ee10bd
Merge remote-tracking branch 'upstream/main' into wds
mrosseel Sep 23, 2025
104b609
Merge remote-tracking branch 'upstream/main' into wds
mrosseel Sep 28, 2025
9ee0449
first pass on missing radecs for WDS
mrosseel Sep 29, 2025
eb318a3
Merge remote-tracking branch 'upstream/main' into wds
mrosseel Sep 29, 2025
6dbc20f
Reenable WDS, beter parsing of coords+fallback coords
mrosseel Sep 29, 2025
2068e16
Speed up name loading
mrosseel Sep 29, 2025
1913bea
Add debugging timing
mrosseel Sep 29, 2025
6261156
eliminate double call
mrosseel Sep 29, 2025
ed8b132
db optimisations
mrosseel Sep 29, 2025
4912b1b
Background loading of objects
mrosseel Sep 29, 2025
11176f3
Enable mag loading again
mrosseel Sep 29, 2025
ef0b2c4
init mag with object
mrosseel Sep 29, 2025
b7ecd89
Defer catalogs
mrosseel Sep 29, 2025
cf77686
thread safe background loader
mrosseel Sep 30, 2025
629d851
improve startup time by deferring filtering
mrosseel Sep 30, 2025
9d15d59
Preload modules to speed up startup
mrosseel Sep 30, 2025
e672573
Back out the module preloading, need to do perf profiling first
mrosseel Sep 30, 2025
455bc8a
Profiling
mrosseel Sep 30, 2025
cb64f5d
Notify users of background loading
mrosseel Sep 30, 2025
9c1ce32
Catalog refreshes after loading
mrosseel Sep 30, 2025
0389f32
fix infinite loop bug
mrosseel Sep 30, 2025
c1035a0
Tests should pass on CI
mrosseel Sep 30, 2025
1a46d46
Remove timing code
mrosseel Sep 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified astro_data/pifinder_objects.db
Binary file not shown.
122 changes: 78 additions & 44 deletions python/PiFinder/catalog_imports/wds_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,55 @@ def load_wds():
data = read_wds_catalog(data_path)

def parse_coordinates_2000(coord):
ra_h = float(coord[:2])
ra_m = float(coord[2:4])
ra_s = float(coord[4:5]) * 6 # Convert tenths of minutes to seconds
dec_deg = float(coord[5:8])
dec_m = float(coord[8:10])
return ra_to_deg(ra_h, ra_m, ra_s), dec_to_deg(dec_deg, dec_m, 0)

def parse_coordinates_arcsec(coord):
try:
# Check for correct length (WDS identifier is always 10 chars)
if len(coord) != 10:
return None, None

# Format: HHMM.t±DDMM (10 characters) - example: 00001-0122
ra_h = float(coord[:2])
ra_m = float(coord[2:4])
ra_s = float(coord[4:9])
dec_sign = 1 if coord[9] == "+" else -1
dec_deg = float(coord[10:12]) * dec_sign
dec_m = float(coord[12:14])
dec_s = float(coord[14:])
# 00000+7530A 000006.64+752859.8
except ValueError:
ra_s = float(coord[4:5]) * 6 # Convert tenths of minutes to seconds
dec_sign = 1 if coord[5] == "+" else -1
dec_deg = float(coord[6:8]) * dec_sign
dec_m = float(coord[8:10])
return ra_to_deg(ra_h, ra_m, ra_s), dec_to_deg(dec_deg, dec_m, 0)
except (ValueError, IndexError):
return None, None

def parse_coordinates_arcsec(coord):
try:
# Handle empty, missing, or '.' coordinates
coord_clean = coord.strip()
if not coord_clean or coord_clean == '.':
return None, None

# Find the sign position (+ or -)
sign_pos = -1
for i, char in enumerate(coord_clean):
if char in ['+', '-']:
sign_pos = i
break

if sign_pos == -1:
return None, None

# Parse RA part (before sign)
ra_part = coord_clean[:sign_pos].strip()
ra_h = float(ra_part[:2])
ra_m = float(ra_part[2:4])
ra_s = float(ra_part[4:]) # Variable length seconds

# Parse DEC part (after sign)
dec_part = coord_clean[sign_pos:]
dec_sign = 1 if dec_part[0] == '+' else -1
dec_coords = dec_part[1:].strip() # Remove sign

dec_deg = float(dec_coords[:2]) * dec_sign
dec_m = float(dec_coords[2:4])
dec_s = float(dec_coords[4:]) if len(dec_coords) > 4 else 0.0

except (ValueError, IndexError):
return None, None
return ra_to_deg(ra_h, ra_m, ra_s), dec_to_deg(dec_deg, dec_m, dec_s)

Expand All @@ -152,6 +183,13 @@ def handle_multiples(key, values) -> dict:
mag1 = round(value["Mag_First"].item(), 2)
mag2 = round(value["Mag_Second"].item(), 2)
if i == 0:
# Validate RA/DEC in the first (primary) object
if value['ra'] is None or value['dec'] is None or np.isnan(value['ra']) or np.isnan(value['dec']):
logging.error(f"Empty or invalid RA/DEC in handle_multiples for WDS object '{key}'")
logging.error(f" Primary object RA: {value['ra']}, DEC: {value['dec']}")
logging.error(f" Coordinates_2000: '{value['Coordinates_2000']}'")
logging.error(f" Coordinates_Arcsec: '{value['Coordinates_Arcsec']}'")
raise ValueError(f"Invalid RA/DEC coordinates for primary WDS object '{key}': RA={value['ra']}, DEC={value['dec']}")
result["ra"] = value["ra"]
result["dec"] = value["dec"]
result["mag"] = MagnitudeObject([mag1, mag2])
Expand All @@ -177,46 +215,42 @@ def handle_multiples(key, values) -> dict:
result["description"] = "\n".join(descriptions)
return result

# Convert coordinates
ra_2000, dec_2000 = np.vectorize(parse_coordinates_2000)(data["Coordinates_2000"])
ra_arcsec, dec_arcsec = np.vectorize(parse_coordinates_arcsec)(
data["Coordinates_Arcsec"]
)

# Add these new coordinates to the numpy array
new_dtype = data.dtype.descr + [
("ra_2000", "f8"),
("dec_2000", "f8"),
("ra_arcsec", "f8"),
("dec_arcsec", "f8"),
("ra", "f8"),
("dec", "f8"),
]
# Add coordinate columns to the numpy array
new_dtype = data.dtype.descr + [("ra", "f8"), ("dec", "f8")]
new_data = np.empty(data.shape, dtype=new_dtype)

# Copy existing data
for name in data.dtype.names:
new_data[name] = data[name]

# Add new data
new_data["ra_2000"] = ra_2000
new_data["dec_2000"] = dec_2000
new_data["ra_arcsec"] = ra_arcsec
new_data["dec_arcsec"] = dec_arcsec
new_data["ra"] = 0
new_data["dec"] = 0

# Replace the old data with the new data
data = new_data

# Append new columns to data
# Parse coordinates on demand and assign final values
for i, entry in enumerate(data):
if ra_arcsec[i] is None or dec_arcsec[i] is None:
entry["ra"] = ra_2000[i]
entry["dec"] = dec_2000[i]
# Try arcsecond coordinates first
ra_arcsec, dec_arcsec = parse_coordinates_arcsec(entry["Coordinates_Arcsec"])

if ra_arcsec is not None and dec_arcsec is not None:
entry["ra"] = ra_arcsec
entry["dec"] = dec_arcsec
else:
entry["ra"] = ra_arcsec[i]
entry["dec"] = dec_arcsec[i]
# Fall back to 2000 coordinates
ra_2000, dec_2000 = parse_coordinates_2000(entry["Coordinates_2000"])
entry["ra"] = ra_2000
entry["dec"] = dec_2000

# Validate RA/DEC values are not empty/invalid
if entry['ra'] is None or entry['dec'] is None or np.isnan(entry['ra']) or np.isnan(entry['dec']):
coord_2000 = entry['Coordinates_2000']
coord_arcsec = entry['Coordinates_Arcsec']
logging.error(f"Empty or invalid RA/DEC detected for WDS object at line {i+1}")
logging.error(f" Coordinates_2000: '{coord_2000}'")
logging.error(f" Coordinates_Arcsec: '{coord_arcsec}'")
logging.error(f" Parsed RA_2000: {ra_2000[i]}, DEC_2000: {dec_2000[i]}")
logging.error(f" Parsed RA_arcsec: {ra_arcsec[i]}, DEC_arcsec: {dec_arcsec[i]}")
logging.error(f" Final RA: {entry['ra']}, DEC: {entry['dec']}")
raise ValueError(f"Invalid RA/DEC coordinates for WDS object at line {i+1}: RA={entry['ra']}, DEC={entry['dec']}")

# make a dictionary of WDS objects to group duplicates
wds_dict = defaultdict(list)
Expand Down
Loading