From dac191cf4f5a81a84ddbdb930e80aa8943c4ff9b Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Tue, 12 May 2026 20:01:08 +0200 Subject: [PATCH 01/11] Refactor RaDecRoll: Merge methods to reduce redundancy. Use class methods --- python/PiFinder/integrator.py | 15 ++-- .../PiFinder/pointing_model/astro_coords.py | 71 +++++++------------ .../pointing_model/imu_dead_reckoning.py | 6 +- 3 files changed, 35 insertions(+), 57 deletions(-) diff --git a/python/PiFinder/integrator.py b/python/PiFinder/integrator.py index 598d8cb9f..46b7abbed 100644 --- a/python/PiFinder/integrator.py +++ b/python/PiFinder/integrator.py @@ -164,11 +164,11 @@ def update_plate_solve_and_imu(imu_dead_reckoning: ImuDeadReckoning, solved: dic q_x2imu = solved["imu_quat"] # IMU measurement at the time of plate solving # Update: - solved_cam = RaDecRoll() - solved_cam.set_from_deg( + solved_cam = RaDecRoll.set( solved["camera_center"]["RA"], solved["camera_center"]["Dec"], solved["camera_center"]["Roll"], + deg=True ) imu_dead_reckoning.update_plate_solve_and_imu(solved_cam, q_x2imu) @@ -220,11 +220,11 @@ def update_imu( solved["camera_center"]["RA"], solved["camera_center"]["Dec"], solved["camera_center"]["Roll"], - ) = cam_eq.get_deg(use_none=True) + ) = cam_eq.get(deg=True) # Store the current scope pointing estimate scope_eq = imu_dead_reckoning.get_scope_radec() - solved["RA"], solved["Dec"], solved["Roll"] = scope_eq.get_deg(use_none=True) + solved["RA"], solved["Dec"], solved["Roll"] = scope_eq.get(deg=True) solved["solve_time"] = imu_time solved["solve_source"] = "IMU" @@ -271,17 +271,16 @@ def set_cam2scope_alignment(imu_dead_reckoning: ImuDeadReckoning, solved: dict): TODO: Do this once at alignment """ # RA, Dec of camera center:: - solved_cam = RaDecRoll() - solved_cam.set_from_deg( + solved_cam = RaDecRoll.set( solved["camera_center"]["RA"], solved["camera_center"]["Dec"], solved["camera_center"]["Roll"], + deg=True ) # RA, Dec of target (where scope is pointing): solved["Roll"] = 0 # Target roll isn't calculated by Tetra3. Set to zero here - solved_scope = RaDecRoll() - solved_scope.set_from_deg(solved["RA"], solved["Dec"], solved["Roll"]) + solved_scope = RaDecRoll.set(solved["RA"], solved["Dec"], solved["Roll"], deg=True) # Set alignment in imu_dead_reckoning imu_dead_reckoning.set_cam2scope_alignment(solved_cam, solved_scope) diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/pointing_model/astro_coords.py index 1c9de2c8f..20b88ee28 100644 --- a/python/PiFinder/pointing_model/astro_coords.py +++ b/python/PiFinder/pointing_model/astro_coords.py @@ -34,40 +34,38 @@ def reset(self): self.dec = np.nan self.roll = np.nan self.is_set = False - + + @classmethod def set( - self, ra: Union[float, None], dec: Union[float, None], roll: Union[float, None] + cls, + ra: Union[float, None], + dec: Union[float, None], + roll: Union[float, None], + deg=False # If True, input angles are in degrees ): """Set using radians""" - self.ra = ra if ra is not None else np.nan - self.dec = dec if dec is not None else np.nan - self.roll = roll if roll is not None else np.nan - self.is_set = True - - def set_from_deg( - self, - ra_deg: Union[float, None], - dec_deg: Union[float, None], - roll_deg: Union[float, None], - ): - """Set using degrees""" - ra = np.deg2rad(ra_deg) if ra_deg is not None else np.nan - dec = np.deg2rad(dec_deg) if dec_deg is not None else np.nan - roll = np.deg2rad(roll_deg) if roll_deg is not None else np.nan - - self.set(ra, dec, roll) - - def set_from_quaternion(self, q_eq: quaternion.quaternion): + cls.ra = ra if ra is not None else np.nan + cls.dec = dec if dec is not None else np.nan + cls.roll = roll if roll is not None else np.nan + cls.is_set = True + if deg: + cls.ra = np.deg2rad(cls.ra) + cls.dec = np.deg2rad(cls.dec) + cls.roll = np.deg2rad(cls.roll) + + @classmethod + def set_from_quaternion(cls, q_eq: quaternion.quaternion): """ Set from a quaternion rotation relative to the Equatorial frame. Re-using code from quaternion_transforms.q_eq2radec. """ ra, dec, roll = qt.q_eq2radec(q_eq) - self.set(ra, dec, roll) + cls.set(ra, dec, roll) - def get( - self, use_none=False - ) -> tuple[Union[float, None], Union[float, None], Union[float, None]]: + def get(self, + use_none=True, # If True, returns None instead of np.nan + deg=False # If True, returns degrees + ) -> tuple[Union[float, None], Union[float, None], Union[float, None]]: """ Returns (ra, dec, roll) in radians. If use_none is True, returns None for any unset (nan) values. @@ -79,24 +77,7 @@ def get( else: ra, dec, roll = self.ra, self.dec, self.roll - return ra, dec, roll - - def get_deg( - self, use_none=False - ) -> tuple[Union[float, None], Union[float, None], Union[float, None]]: - """ - Returns (ra, dec, roll) in degrees. If use_none is True, returns None - for any unset (nan) values. - """ - if use_none: - ra = np.rad2deg(self.ra) if not np.isnan(self.ra) else None - dec = np.rad2deg(self.dec) if not np.isnan(self.dec) else None - roll = np.rad2deg(self.roll) if not np.isnan(self.roll) else None + if deg: + return np.rad2deg(ra), np.rad2deg(dec), np.rad2deg(roll) else: - ra, dec, roll = ( - np.rad2deg(self.ra), - np.rad2deg(self.dec), - np.rad2deg(self.roll), - ) - - return ra, dec, roll + return ra, dec, roll diff --git a/python/PiFinder/pointing_model/imu_dead_reckoning.py b/python/PiFinder/pointing_model/imu_dead_reckoning.py index 905625de3..bb1f3e821 100644 --- a/python/PiFinder/pointing_model/imu_dead_reckoning.py +++ b/python/PiFinder/pointing_model/imu_dead_reckoning.py @@ -142,8 +142,7 @@ def get_cam_radec(self) -> RaDecRoll: dead_reckoning to indicate if the estimate is from dead-reckoning (True) or from plate solving (False). """ - ra_dec_roll = RaDecRoll() - ra_dec_roll.set_from_quaternion(self.q_eq2cam) + ra_dec_roll = RaDecRoll.set_from_quaternion(self.q_eq2cam) return ra_dec_roll @@ -153,8 +152,7 @@ def get_scope_radec(self) -> RaDecRoll: to indicate if the estimate is from dead-reckoning (True) or from plate solving (False). """ - ra_dec_roll = RaDecRoll() - ra_dec_roll.set_from_quaternion(self.q_eq2scope) + ra_dec_roll = RaDecRoll.set_from_quaternion(self.q_eq2scope) return ra_dec_roll From 72ab6b5a4ccdba906d00166896dc9351e4139329 Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Tue, 12 May 2026 20:06:30 +0200 Subject: [PATCH 02/11] Fix --- .../PiFinder/pointing_model/astro_coords.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/pointing_model/astro_coords.py index 20b88ee28..ec01d476b 100644 --- a/python/PiFinder/pointing_model/astro_coords.py +++ b/python/PiFinder/pointing_model/astro_coords.py @@ -7,7 +7,7 @@ import quaternion from typing import Union # When updated to Python 3.10+, remove and use new type hints -import PiFinder.pointing_model.quaternion_transforms as qt +from PiFinder.pointing_model.quaternion_transforms import q_eq2radec @dataclass @@ -59,7 +59,7 @@ def set_from_quaternion(cls, q_eq: quaternion.quaternion): Set from a quaternion rotation relative to the Equatorial frame. Re-using code from quaternion_transforms.q_eq2radec. """ - ra, dec, roll = qt.q_eq2radec(q_eq) + ra, dec, roll = q_eq2radec(q_eq) cls.set(ra, dec, roll) def get(self, @@ -70,14 +70,14 @@ def get(self, Returns (ra, dec, roll) in radians. If use_none is True, returns None for any unset (nan) values. """ - if use_none: - ra = self.ra if not np.isnan(self.ra) else None - dec = self.dec if not np.isnan(self.dec) else None - roll = self.roll if not np.isnan(self.roll) else None + if deg: + ra, dec, roll = np.rad2deg(ra), np.rad2deg(dec), np.rad2deg(roll) else: ra, dec, roll = self.ra, self.dec, self.roll - if deg: - return np.rad2deg(ra), np.rad2deg(dec), np.rad2deg(roll) - else: - return ra, dec, roll + if use_none: + ra = ra if not np.isnan(ra) else None + dec = dec if not np.isnan(dec) else None + roll = roll if not np.isnan(roll) else None + + return ra, dec, roll From 360d117343004f5dc5e4aad72580d689bbf5dc76 Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Tue, 12 May 2026 21:34:27 +0200 Subject: [PATCH 03/11] Fix classmethods --- .../PiFinder/pointing_model/astro_coords.py | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/pointing_model/astro_coords.py index ec01d476b..5320e48e9 100644 --- a/python/PiFinder/pointing_model/astro_coords.py +++ b/python/PiFinder/pointing_model/astro_coords.py @@ -19,15 +19,21 @@ class RaDecRoll: The set methods allow values to be float or None but internally, None will be stored as np.nan so that the type is consistent. the get methods will return None if the value is np.nan. - - NOTE: All angles are in radians. """ - ra: float = np.nan + ra: float = np.nan # Angles in radians dec: float = np.nan roll: float = np.nan is_set = False + def __init__(self, ra: float, dec: float, roll: float, deg=False): + self.set(ra, dec, roll, deg=deg) + + @classmethod + def from_quaternion(cls, q_eq: quaternion.quaternion): + ra, dec, roll = q_eq2radec(q_eq) + return cls(ra, dec, roll) + def reset(self): """Reset to unset state""" self.ra = np.nan @@ -35,32 +41,30 @@ def reset(self): self.roll = np.nan self.is_set = False - @classmethod def set( - cls, + self, ra: Union[float, None], dec: Union[float, None], roll: Union[float, None], deg=False # If True, input angles are in degrees ): """Set using radians""" - cls.ra = ra if ra is not None else np.nan - cls.dec = dec if dec is not None else np.nan - cls.roll = roll if roll is not None else np.nan - cls.is_set = True + self.ra = ra if ra is not None else np.nan + self.dec = dec if dec is not None else np.nan + self.roll = roll if roll is not None else np.nan + self.is_set = True if deg: - cls.ra = np.deg2rad(cls.ra) - cls.dec = np.deg2rad(cls.dec) - cls.roll = np.deg2rad(cls.roll) + self.ra = np.deg2rad(self.ra) + self.dec = np.deg2rad(self.dec) + self.roll = np.deg2rad(self.roll) - @classmethod - def set_from_quaternion(cls, q_eq: quaternion.quaternion): + def set_from_quaternion(self, q_eq: quaternion.quaternion): """ Set from a quaternion rotation relative to the Equatorial frame. Re-using code from quaternion_transforms.q_eq2radec. """ ra, dec, roll = q_eq2radec(q_eq) - cls.set(ra, dec, roll) + self.set(ra, dec, roll) def get(self, use_none=True, # If True, returns None instead of np.nan From a1b3d3df40e3cb93655f040a68adcb918d482787 Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Tue, 12 May 2026 23:11:58 +0200 Subject: [PATCH 04/11] Add method: .as_quaternion() --- python/PiFinder/pointing_model/astro_coords.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/pointing_model/astro_coords.py index 5320e48e9..969d58ef0 100644 --- a/python/PiFinder/pointing_model/astro_coords.py +++ b/python/PiFinder/pointing_model/astro_coords.py @@ -61,11 +61,16 @@ def set( def set_from_quaternion(self, q_eq: quaternion.quaternion): """ Set from a quaternion rotation relative to the Equatorial frame. - Re-using code from quaternion_transforms.q_eq2radec. """ ra, dec, roll = q_eq2radec(q_eq) self.set(ra, dec, roll) + def as_quaternion(self) -> quaternion.quaternion: + """ + Return the quaternion rotation relative to the Equatorial frame. + """ + return radec2q_eq(self.ra, self.dec, self.roll) + def get(self, use_none=True, # If True, returns None instead of np.nan deg=False # If True, returns degrees From 339b24bdc099bdc5f65ccf4596ce3b5115ad208a Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Wed, 13 May 2026 00:12:35 +0200 Subject: [PATCH 05/11] Change is_set --> valid. Set valid = False if None provided --- python/PiFinder/pointing_model/astro_coords.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/pointing_model/astro_coords.py index 969d58ef0..888d72f02 100644 --- a/python/PiFinder/pointing_model/astro_coords.py +++ b/python/PiFinder/pointing_model/astro_coords.py @@ -10,6 +10,7 @@ from PiFinder.pointing_model.quaternion_transforms import q_eq2radec + @dataclass class RaDecRoll: """ @@ -21,10 +22,10 @@ class RaDecRoll: return None if the value is np.nan. """ - ra: float = np.nan # Angles in radians + ra: float = np.nan # All angles in radians dec: float = np.nan roll: float = np.nan - is_set = False + valid = False def __init__(self, ra: float, dec: float, roll: float, deg=False): self.set(ra, dec, roll, deg=deg) @@ -39,7 +40,7 @@ def reset(self): self.ra = np.nan self.dec = np.nan self.roll = np.nan - self.is_set = False + self.valid = False def set( self, @@ -52,7 +53,12 @@ def set( self.ra = ra if ra is not None else np.nan self.dec = dec if dec is not None else np.nan self.roll = roll if roll is not None else np.nan - self.is_set = True + + if np.isnan(self.ra) or np.isnan(self.dec) or np.isnan(self.roll): + self.valid = False + else: + self.valid = True + if deg: self.ra = np.deg2rad(self.ra) self.dec = np.deg2rad(self.dec) From 836611823a9b0873a01c418cb22ce8af39dee58a Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Wed, 13 May 2026 00:17:10 +0200 Subject: [PATCH 06/11] Fix import --- python/PiFinder/pointing_model/astro_coords.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/pointing_model/astro_coords.py index 888d72f02..2d657aaa4 100644 --- a/python/PiFinder/pointing_model/astro_coords.py +++ b/python/PiFinder/pointing_model/astro_coords.py @@ -7,8 +7,7 @@ import quaternion from typing import Union # When updated to Python 3.10+, remove and use new type hints -from PiFinder.pointing_model.quaternion_transforms import q_eq2radec - +from PiFinder.pointing_model.quaternion_transforms import q_eq2radec, radec2q_eq @dataclass From 84ee42f73de622f15c42c8bcc43f59cea3ddc4d7 Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Wed, 13 May 2026 00:20:20 +0200 Subject: [PATCH 07/11] Fix --- python/PiFinder/pointing_model/astro_coords.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/pointing_model/astro_coords.py index 2d657aaa4..6a67dd803 100644 --- a/python/PiFinder/pointing_model/astro_coords.py +++ b/python/PiFinder/pointing_model/astro_coords.py @@ -85,7 +85,7 @@ def get(self, for any unset (nan) values. """ if deg: - ra, dec, roll = np.rad2deg(ra), np.rad2deg(dec), np.rad2deg(roll) + ra, dec, roll = np.rad2deg(self.ra), np.rad2deg(self.dec), np.rad2deg(self.roll) else: ra, dec, roll = self.ra, self.dec, self.roll From 78d325dfa4c835856df1d16f55fa73db53ff5091 Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Wed, 13 May 2026 00:26:51 +0200 Subject: [PATCH 08/11] Fix usages of RaDecRoll after the changes --- python/PiFinder/integrator.py | 6 +++--- python/PiFinder/pointing_model/imu_dead_reckoning.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/PiFinder/integrator.py b/python/PiFinder/integrator.py index 46b7abbed..63c57499f 100644 --- a/python/PiFinder/integrator.py +++ b/python/PiFinder/integrator.py @@ -164,7 +164,7 @@ def update_plate_solve_and_imu(imu_dead_reckoning: ImuDeadReckoning, solved: dic q_x2imu = solved["imu_quat"] # IMU measurement at the time of plate solving # Update: - solved_cam = RaDecRoll.set( + solved_cam = RaDecRoll( solved["camera_center"]["RA"], solved["camera_center"]["Dec"], solved["camera_center"]["Roll"], @@ -271,7 +271,7 @@ def set_cam2scope_alignment(imu_dead_reckoning: ImuDeadReckoning, solved: dict): TODO: Do this once at alignment """ # RA, Dec of camera center:: - solved_cam = RaDecRoll.set( + solved_cam = RaDecRoll( solved["camera_center"]["RA"], solved["camera_center"]["Dec"], solved["camera_center"]["Roll"], @@ -280,7 +280,7 @@ def set_cam2scope_alignment(imu_dead_reckoning: ImuDeadReckoning, solved: dict): # RA, Dec of target (where scope is pointing): solved["Roll"] = 0 # Target roll isn't calculated by Tetra3. Set to zero here - solved_scope = RaDecRoll.set(solved["RA"], solved["Dec"], solved["Roll"], deg=True) + solved_scope = RaDecRoll(solved["RA"], solved["Dec"], solved["Roll"], deg=True) # Set alignment in imu_dead_reckoning imu_dead_reckoning.set_cam2scope_alignment(solved_cam, solved_scope) diff --git a/python/PiFinder/pointing_model/imu_dead_reckoning.py b/python/PiFinder/pointing_model/imu_dead_reckoning.py index bb1f3e821..9121f97b3 100644 --- a/python/PiFinder/pointing_model/imu_dead_reckoning.py +++ b/python/PiFinder/pointing_model/imu_dead_reckoning.py @@ -142,7 +142,7 @@ def get_cam_radec(self) -> RaDecRoll: dead_reckoning to indicate if the estimate is from dead-reckoning (True) or from plate solving (False). """ - ra_dec_roll = RaDecRoll.set_from_quaternion(self.q_eq2cam) + ra_dec_roll = RaDecRoll.from_quaternion(self.q_eq2cam) return ra_dec_roll @@ -152,7 +152,7 @@ def get_scope_radec(self) -> RaDecRoll: to indicate if the estimate is from dead-reckoning (True) or from plate solving (False). """ - ra_dec_roll = RaDecRoll.set_from_quaternion(self.q_eq2scope) + ra_dec_roll = RaDecRoll.from_quaternion(self.q_eq2scope) return ra_dec_roll From 1e6beab3ceeb64967cfe04bc3b6477e83d3ae960 Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Wed, 13 May 2026 00:36:17 +0200 Subject: [PATCH 09/11] Fix: Wasn't changed to RaDecRoll.valid --- python/PiFinder/pointing_model/imu_dead_reckoning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/PiFinder/pointing_model/imu_dead_reckoning.py b/python/PiFinder/pointing_model/imu_dead_reckoning.py index 9121f97b3..aafa3be14 100644 --- a/python/PiFinder/pointing_model/imu_dead_reckoning.py +++ b/python/PiFinder/pointing_model/imu_dead_reckoning.py @@ -101,7 +101,7 @@ def update_plate_solve_and_imu( q_x2imu: [quaternion] Raw IMU measurement quaternions. This is the IMU frame orientation wrt unknown drifting reference frame X. """ - if not solved_cam.is_set: + if not solved_cam.valid: return # No update # Update plate-solved coord: Camera frame relative to the Equatorial From 0ef5ed0b7d94c3ba5ac060ebf5582cdd9477ab0c Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Fri, 15 May 2026 22:23:42 +0200 Subject: [PATCH 10/11] Move astro_coords.py to types/coordinates.py --- python/PiFinder/integrator.py | 2 +- python/PiFinder/pointing_model/imu_dead_reckoning.py | 2 +- python/PiFinder/types/__init__.py | 0 .../{pointing_model/astro_coords.py => types/coordinates.py} | 0 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 python/PiFinder/types/__init__.py rename python/PiFinder/{pointing_model/astro_coords.py => types/coordinates.py} (100%) diff --git a/python/PiFinder/integrator.py b/python/PiFinder/integrator.py index 63c57499f..fb96a418c 100644 --- a/python/PiFinder/integrator.py +++ b/python/PiFinder/integrator.py @@ -26,7 +26,7 @@ from PiFinder import state_utils import PiFinder.calc_utils as calc_utils from PiFinder.multiproclogging import MultiprocLogging -from PiFinder.pointing_model.astro_coords import RaDecRoll +from PiFinder.types.coordinates import RaDecRoll from PiFinder.solver import get_initialized_solved_dict from PiFinder.pointing_model.imu_dead_reckoning import ImuDeadReckoning import PiFinder.pointing_model.quaternion_transforms as qt diff --git a/python/PiFinder/pointing_model/imu_dead_reckoning.py b/python/PiFinder/pointing_model/imu_dead_reckoning.py index aafa3be14..8ddc5755e 100644 --- a/python/PiFinder/pointing_model/imu_dead_reckoning.py +++ b/python/PiFinder/pointing_model/imu_dead_reckoning.py @@ -10,7 +10,7 @@ import numpy as np import quaternion -from PiFinder.pointing_model.astro_coords import RaDecRoll +from PiFinder.types.coordinates import RaDecRoll import PiFinder.pointing_model.quaternion_transforms as qt diff --git a/python/PiFinder/types/__init__.py b/python/PiFinder/types/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/PiFinder/pointing_model/astro_coords.py b/python/PiFinder/types/coordinates.py similarity index 100% rename from python/PiFinder/pointing_model/astro_coords.py rename to python/PiFinder/types/coordinates.py From 7f186035d968239613d14189d80bfaf7b754a6fc Mon Sep 17 00:00:00 2001 From: Tak Kaneko <> Date: Fri, 15 May 2026 22:29:31 +0200 Subject: [PATCH 11/11] coordinates: Add templates for RaDec and AltAz classes --- python/PiFinder/types/coordinates.py | 37 ++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/python/PiFinder/types/coordinates.py b/python/PiFinder/types/coordinates.py index 6a67dd803..34e56502d 100644 --- a/python/PiFinder/types/coordinates.py +++ b/python/PiFinder/types/coordinates.py @@ -1,5 +1,5 @@ """ -Various astronomical coordinates functions +Astronomical coordinate types """ from dataclasses import dataclass @@ -20,7 +20,6 @@ class RaDecRoll: be stored as np.nan so that the type is consistent. the get methods will return None if the value is np.nan. """ - ra: float = np.nan # All angles in radians dec: float = np.nan roll: float = np.nan @@ -95,3 +94,37 @@ def get(self, roll = roll if not np.isnan(roll) else None return ra, dec, roll + + +@dataclass +class RaDec: + """ + Data class for equatorial coordinates defined by (RA, Dec). + + The set methods allow values to be float or None but internally, None will + be stored as np.nan so that the type is consistent. the get methods will + return None if the value is np.nan. + """ + ra: float = np.nan # All angles in radians + dec: float = np.nan + valid = False + + def __init__(self, ra: float, dec: float, roll: float, deg=False): + raise NotImplementedError("Outline for RaDec class") + + +@dataclass +class AltAz: + """ + Data class for horizontal coordinates defined by (Alt, Az). + + The set methods allow values to be float or None but internally, None will + be stored as np.nan so that the type is consistent. the get methods will + return None if the value is np.nan. + """ + alt: float = np.nan # All angles in radians + az: float = np.nan + valid = False + + def __init__(self, alt: float, az: float, deg=False): + raise NotImplementedError("Outline for AltAz class")