-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from lsst-sssc/ephemeris_service
Ephemeris service
- Loading branch information
Showing
18 changed files
with
1,976 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,27 @@ | ||
# Faint Solar System Object Detection Service | ||
|
||
[![Template](https://img.shields.io/badge/Template-LINCC%20Frameworks%20Python%20Project%20Template-brightgreen)](https://lincc-ppt.readthedocs.io/en/latest/) | ||
|
||
## Project Overview | ||
|
||
This repository contains a set of Python modules designed to identify potential low signal-to-noise (S/N) detections of solar system objects specified by users. | ||
The service searches for suitable survey images along the object's probable path and returns detections (within SNR and distance constraints) to the user, along with | ||
associated metadata and optionally, image cutouts. | ||
|
||
## Features | ||
|
||
- Obtain list of solar system objects to query from users via web form or API | ||
- Compute expected positions and uncertainty regions of given objects | ||
- Identify survey exposures containing the object's predicted position | ||
- Generate cutout images of each object's uncertainty region | ||
- Retrieve existing higher-S/N (S/N>=5) survey sources in each uncertainty region | ||
- Detect new low-S/N (S/N<5) sources within the uncertainty region | ||
- Conduct forced photometry at the expected position of given objects | ||
|
||
<h1 align="center"> | ||
<img src="https://raw.githubusercontent.com/lsst-sssc/ssoforcedphot/ephemeris_service/docs/SSSC_Faint_object_service.png" width="800"> | ||
</h1><br> | ||
|
||
## Installation | ||
|
||
(Instructions for installation will be added once more components are completed) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,6 @@ nbsphinx | |
sphinx | ||
sphinx-autoapi | ||
sphinx-copybutton | ||
sphinx-rtd-theme | ||
sphinx-rtd-theme | ||
astropy | ||
astroquery |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +0,0 @@ | ||
from .example_module import greetings, meaning | ||
|
||
__all__ = ["greetings", "meaning"] | ||
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import logging | ||
|
||
import numpy as np | ||
from astropy.table import Table | ||
from astropy.time import Time | ||
|
||
from forcedphot.ephemeris.data_model import EphemerisData | ||
|
||
|
||
class DataLoader: | ||
""" | ||
DataLoader is a class for loading ephemeris data from ECSV files and converting it into the | ||
EphemerisData class. | ||
This class provides methods to load ephemeris data from ECSV files and convert it to the | ||
EphemerisData class. It supports loading ephemeris data from a single ECSV file or from | ||
multiple ECSV files. | ||
Attributes: | ||
logger (logging.Logger): Logger for the class. | ||
Methods: | ||
load_ephemeris_from_ecsv(file_path: str) -> EphemerisData: | ||
Load ephemeris data from an ECSV file and return it as an EphemerisData object. | ||
load_multiple_ephemeris_files(file_paths: list[str]) -> list[EphemerisData]: | ||
Load ephemeris data from multiple ECSV files and return them as a list of EphemerisData objects. | ||
""" | ||
|
||
# Set up logging | ||
logging.basicConfig(level=logging.INFO) | ||
logger = logging.getLogger(__name__) | ||
|
||
@staticmethod | ||
def load_ephemeris_from_ecsv(file_path: str) -> EphemerisData: | ||
""" | ||
Load ephemeris data from an ECSV file and return it as an EphemerisData object. | ||
Parameters: | ||
----------- | ||
file_path : str | ||
Path to the ECSV file containing ephemeris data. | ||
Returns: | ||
-------- | ||
EphemerisData | ||
An EphemerisData object populated with the data from the ECSV file. | ||
Raises: | ||
------- | ||
FileNotFoundError | ||
If the specified file does not exist. | ||
ValueError | ||
If the ECSV file is missing required columns. | ||
""" | ||
try: | ||
# Read the ECSV file | ||
table = Table.read(file_path, format="ascii.ecsv") | ||
|
||
# Check if all required columns are present | ||
required_columns = [ | ||
"datetime", | ||
"RA_deg", | ||
"DEC_deg", | ||
"RA_rate_arcsec_per_h", | ||
"DEC_rate_arcsec_per_h", | ||
"AZ_deg", | ||
"EL_deg", | ||
"r_au", | ||
"delta_au", | ||
"V_mag", | ||
"alpha_deg", | ||
"RSS_3sigma_arcsec", | ||
] | ||
missing_columns = [col for col in required_columns if col not in table.colnames] | ||
if missing_columns: | ||
raise ValueError(f"Missing columns in ECSV file: {', '.join(missing_columns)}") | ||
|
||
# Create and populate the EphemerisData object | ||
ephemeris_data = EphemerisData( | ||
datetime=Time(table["datetime"], scale="utc", format="jd"), | ||
RA_deg=np.array(table["RA_deg"]), | ||
DEC_deg=np.array(table["DEC_deg"]), | ||
RA_rate_arcsec_per_h=np.array(table["RA_rate_arcsec_per_h"]), | ||
DEC_rate_arcsec_per_h=np.array(table["DEC_rate_arcsec_per_h"]), | ||
AZ_deg=np.array(table["AZ_deg"]), | ||
EL_deg=np.array(table["EL_deg"]), | ||
r_au=np.array(table["r_au"]), | ||
delta_au=np.array(table["delta_au"]), | ||
V_mag=np.array(table["V_mag"]), | ||
alpha_deg=np.array(table["alpha_deg"]), | ||
RSS_3sigma_arcsec=np.array(table["RSS_3sigma_arcsec"]), | ||
) | ||
|
||
DataLoader.logger.info( | ||
f"Loaded ephemeris data with {len(ephemeris_data.datetime)} points from {file_path}." | ||
) | ||
|
||
return ephemeris_data | ||
|
||
except FileNotFoundError: | ||
DataLoader.logger.error(f"The file {file_path} was not found.") | ||
raise | ||
except ValueError as ve: | ||
DataLoader.logger.error(f"Value error in file {file_path}: {str(ve)}") | ||
raise | ||
except Exception as e: | ||
DataLoader.logger.error(f"Unexpected error loading ECSV file {file_path}: {str(e)}") | ||
raise ValueError(f"Error loading ECSV file: {str(e)}") from e | ||
|
||
@staticmethod | ||
def load_multiple_ephemeris_files(file_paths: list[str]) -> list[EphemerisData]: | ||
""" | ||
Load multiple ephemeris files and return a list of EphemerisData objects. | ||
Parameters: | ||
----------- | ||
file_paths : List[str] | ||
A list of paths to ECSV files containing ephemeris data. | ||
Returns: | ||
-------- | ||
List[EphemerisData] | ||
A list of EphemerisData objects, each populated with data from one ECSV file. | ||
""" | ||
ephemeris_list = [] | ||
for file_path in file_paths: | ||
try: | ||
ephemeris_data = DataLoader.load_ephemeris_from_ecsv(file_path) | ||
ephemeris_list.append(ephemeris_data) | ||
except (FileNotFoundError, ValueError) as e: | ||
DataLoader.logger.error(f"Error loading file {file_path}: {str(e)}") | ||
raise # Re-raise the exception to be caught by the calling function | ||
|
||
return ephemeris_list | ||
|
||
|
||
if __name__ == "__main__": | ||
# Example usage | ||
# file_path = "./Ceres_2024-01-01_00-00-00.000_2025-12-31_23-59-00.000.ecsv" | ||
# try: | ||
# ephemeris_data = DataLoader.load_ephemeris_from_ecsv(file_path) | ||
# except Exception as e: | ||
# print(f"Error: {str(e)}") | ||
|
||
# Example of loading multiple files | ||
file_paths = [ | ||
"./Ceres_2024-01-01_00-00-00.000_2025-12-31_23-59-00.000.ecsv", | ||
"./Encke_2024-01-01_00-00-00.000_2024-06-30_23-59-00.000.ecsv", | ||
] | ||
try: | ||
ephemeris_list = DataLoader.load_multiple_ephemeris_files(file_paths) | ||
print(f"Loaded {len(ephemeris_list)} ephemeris files.") | ||
except Exception as e: | ||
print(f"Error: {str(e)}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from dataclasses import dataclass, field | ||
|
||
import numpy as np | ||
from astropy.time import Time | ||
|
||
|
||
@dataclass | ||
class QueryInput: | ||
""" | ||
A data class representing the input parameters for an ephemeris query. | ||
Attributes: | ||
target (str): The name or identifier of the celestial target. | ||
target_type (str): The type of celestial target. | ||
start (Time): The start time of the query period. | ||
end (Time): The end time of the query period. | ||
step (str): The time step interval for the query results. | ||
""" | ||
|
||
target: str | ||
target_type: str | ||
start: Time | ||
end: Time | ||
step: str | ||
|
||
|
||
@dataclass | ||
class QueryInputMiriade: | ||
""" | ||
A data class representing the input parameters for the Miriade ephemeris query. | ||
Attributes: | ||
target (str): The name or identifier of the celestial target. | ||
objtype (str): The type of celestial target. | ||
start (Time): The start time of the query period. | ||
step (str): The time step interval for the query results. | ||
nsteps (int): The number of steps in the query. | ||
""" | ||
|
||
target: str | ||
objtype: str | ||
start: Time | ||
step: str | ||
nsteps: int | ||
|
||
|
||
@dataclass | ||
class EphemerisData: | ||
""" | ||
A data class representing the ephemeris data for a celestial object. | ||
Attributes: | ||
datetime (Time): Time for the ephemeris data points. | ||
RA_deg (np.ndarray): Right Ascension in degrees. | ||
DEC_deg (np.ndarray): Declination in degrees. | ||
RA_rate_arcsec_per_h (np.ndarray): Rate of change of Right Ascension in arcseconds per hour. | ||
DEC_rate_arcsec_per_h (np.ndarray): Rate of change of Declination in arcseconds per hour. | ||
AZ_deg (np.ndarray): Azimuth in degrees. | ||
EL_deg (np.ndarray): Elevation in degrees. | ||
r_au (np.ndarray): Heliocentric distance in astronomical units. | ||
delta_au (np.ndarray): Geocentric distance in astronomical units. | ||
V_mag (np.ndarray): Visual magnitude. | ||
alpha_deg (np.ndarray): Phase angle in degrees. | ||
RSS_3sigma_arcsec (np.ndarray): Root Sum Square 3-sigma positional uncertainty in arcseconds. | ||
""" | ||
|
||
datetime: Time = field(default_factory=lambda: Time([], scale="utc", format="jd")) | ||
RA_deg: np.ndarray = field(default_factory=lambda: np.array([])) | ||
DEC_deg: np.ndarray = field(default_factory=lambda: np.array([])) | ||
RA_rate_arcsec_per_h: np.ndarray = field(default_factory=lambda: np.array([])) | ||
DEC_rate_arcsec_per_h: np.ndarray = field(default_factory=lambda: np.array([])) | ||
AZ_deg: np.ndarray = field(default_factory=lambda: np.array([])) | ||
EL_deg: np.ndarray = field(default_factory=lambda: np.array([])) | ||
r_au: np.ndarray = field(default_factory=lambda: np.array([])) | ||
delta_au: np.ndarray = field(default_factory=lambda: np.array([])) | ||
V_mag: np.ndarray = field(default_factory=lambda: np.array([])) | ||
alpha_deg: np.ndarray = field(default_factory=lambda: np.array([])) | ||
RSS_3sigma_arcsec: np.ndarray = field(default_factory=lambda: np.array([])) | ||
|
||
|
||
@dataclass | ||
class QueryResult: | ||
""" | ||
A data class representing the result of an ephemeris query. | ||
Attributes: | ||
target (str): The name or identifier of the celestial target. | ||
start (Time): The start time of the query period. | ||
end (Time): The end time of the query period. | ||
ephemeris (EphemerisData): The ephemeris data for the celestial object. | ||
""" | ||
|
||
target: str | ||
start: Time | ||
end: Time | ||
ephemeris: EphemerisData |
Oops, something went wrong.