Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions python/PiFinder/calc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,25 @@ def ra_to_ha(self, ra_deg, dt):

return ha_hrs * 180 / 12 # Hour angle [deg]

def radec_to_pa(self, ra_deg, dec_deg, dt):
"""
Returns the parallactic angle of an object at (ra, dec) as seen from an
observer at latitiude, lat and time, dt. See hadec_to_pa() for how
parallactic angle is defined.

INPUTS:
ra_deg: Right ascension [deg]
dec_deg: Declination [deg]
dt: Python datetime object (must be timezone-aware)

RETURNS:
pa_deg: Parallactic angle [deg]
"""
ha_deg = self.ra_to_ha(ra_deg, dt) # Note that HA is in deg
lat_deg = self._observer_geoid.latitude.degrees
pa_deg = hadec_to_pa(ha_deg, dec_deg, lat_deg)
return pa_deg # Parallactic angle [deg]

def radec_to_roll(self, ra_deg, dec_deg, dt):
"""
Returns the roll (field rotation) of an object at (ra, dec) as
Expand Down
116 changes: 61 additions & 55 deletions python/PiFinder/integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ def integrator(shared_state, solver_queue, console_queue, log_queue, is_debug=Fa
# This holds the last image solve position info
# so we can delta for IMU updates
last_image_solve = None
last_solve_time = time.time()
#last_solve_time = time.time()

while True:
pointing_updated = False # Flag to track if pointing was updated in this loop
state_utils.sleep_for_framerate(shared_state)

# Check for new camera solve in queue
Expand All @@ -75,6 +76,7 @@ def integrator(shared_state, solver_queue, console_queue, log_queue, is_debug=Fa
pass

if type(next_image_solve) is dict:
# TODO: Refactor this bit:
# For camera solves, always start from last successful camera solve
# NOT from shared_state (which may contain IMU drift)
# This prevents IMU noise accumulation during failed solves
Expand All @@ -100,20 +102,17 @@ def integrator(shared_state, solver_queue, console_queue, log_queue, is_debug=Fa
# For failed solves, preserve ALL position data from previous solve
# Don't recalculate from GPS (causes drift from GPS noise)

# Set solve_source and push camera solves immediately
if solved["RA"] is not None:
# Successfully plate-solved:
last_image_solve = copy.deepcopy(solved)
solved["solve_source"] = "CAM"
# Calculate constellation for successful solve
solved["constellation"] = (
calc_utils.sf_utils.radec_to_constellation(
solved["RA"], solved["Dec"]
)
)
shared_state.set_solve_state(True)
# We have a new image solve: Use plate-solving for RA/Dec
update_plate_solve_and_imu(imu_dead_reckoning, solved)
#last_solve_time = solved["solve_time"]
pointing_updated = True
else:
# TODO: In this case, it should run update_imu()
# Failed solve - clear constellation
solved["solve_source"] = "CAM_FAILED"
solved["constellation"] = ""
Expand All @@ -123,50 +122,26 @@ def integrator(shared_state, solver_queue, console_queue, log_queue, is_debug=Fa
shared_state.set_solution(solved)
shared_state.set_solve_state(False)

elif imu_dead_reckoning.tracking:
if imu_dead_reckoning.tracking and not pointing_updated:
# Previous plate-solve exists so use IMU dead-reckoning from
# the last plate solved coordinates.
imu = shared_state.imu()
if imu:
#last_solve_time = time.time()
update_imu(imu_dead_reckoning, solved, last_image_solve, imu)
pointing_updated = True

# Push IMU updates only if newer than last push
if (
solved["RA"] and solved["solve_time"] > last_solve_time
# and solved["solve_source"] == "IMU"
):
last_solve_time = time.time() # TODO: solve_time is ambiguous because it's also used for IMU dead-reckoning

# Update Alt, Az, Roll only if newer than last push
if pointing_updated:
#last_solve_time = time.time()
# Set location for roll and altaz calculations.
# TODO: Is it necessary to set location?
# TODO: Altaz doesn't seem to be required for catalogs when in
# EQ mode? Could be disabled in future when in EQ mode?
location = shared_state.location()
dt = shared_state.datetime()
if location:
calc_utils.sf_utils.set_location(
location.lat, location.lon, location.altitude
)

# Set the roll so that the chart is displayed appropriately for the mount type
solved["Roll"] = get_roll_by_mount_type(
solved["RA"], solved["Dec"], location, dt, mount_type
)

# Update remaining solved keys
# Calculate constellation for current position
solved["constellation"] = calc_utils.sf_utils.radec_to_constellation(
solved["RA"], solved["Dec"]
) # TODO: Can the outer brackets be omitted?

# Set Alt/Az because it's needed for the catalogs for the
# Alt/Az mount type. TODO: Can this be moved to the catalog?
dt = shared_state.datetime()
if location and dt:
solved["Alt"], solved["Az"] = calc_utils.sf_utils.radec_to_altaz(
solved["RA"], solved["Dec"], dt
)

update_solved_coords(solved,
location=shared_state.location(),
dt=shared_state.datetime(),
mount_type=mount_type)
# Push IMU update
shared_state.set_solution(solved)
shared_state.set_solve_state(True)
Expand All @@ -177,7 +152,6 @@ def integrator(shared_state, solver_queue, console_queue, log_queue, is_debug=Fa

# ======== Wrapper and helper functions ===============================


def update_plate_solve_and_imu(imu_dead_reckoning: ImuDeadReckoning, solved: dict):
"""
Wrapper for ImuDeadReckoning.update_plate_solve_and_imu() to
Expand Down Expand Up @@ -275,6 +249,36 @@ def update_imu(
)


def update_solved_coords(solved, location, dt, mount_type):
"""
Based on RA/Dec, update the following in dictionary 'solved':
solved["Alt"], solved["Az"], solved["Roll"], solved["constellation"]
"""
if solved["RA"] is None or solved["Dec"] is None:
solved["constellation"] = None
solved["Alt"], solved["Az"] = None, None
solved["Roll"] = None
return

# Calculate constellation for current position
solved["constellation"] = calc_utils.sf_utils.radec_to_constellation(
solved["RA"], solved["Dec"]
)

if location and dt:
# Location needs to be set for Alt/Az and roll calculations
calc_utils.sf_utils.set_location(location.lat, location.lon, location.altitude)
# Set Alt/Az
solved["Alt"], solved["Az"] = calc_utils.sf_utils.radec_to_altaz(
solved["RA"], solved["Dec"], dt)
# Set the roll so that the chart is displayed appropriately for the mount type
solved["Roll"] = get_roll_by_mount_type(
solved["RA"], solved["Dec"], location, dt, mount_type)
else:
solved["Alt"], solved["Az"] = None, None
solved["Roll"] = None


def set_cam2scope_alignment(imu_dead_reckoning: ImuDeadReckoning, solved: dict):
"""
Set alignment.
Expand Down Expand Up @@ -319,6 +323,7 @@ def get_roll_by_mount_type(
if mount_type == "Alt/Az":
# Altaz mounts: Display chart in horizontal coordinates
if location and dt:
"""
# We have location and time/date (and assume that location has been set)
# Roll at the target RA/Dec in the horizontal frame
roll_deg = calc_utils.sf_utils.radec_to_roll(ra_deg, dec_deg, dt)
Expand All @@ -332,21 +337,22 @@ def get_roll_by_mount_type(
roll_deg - np.sign(ha_deg) * 180
) # In essence, gives: roll_deg = -pa_deg
# End of HACK
"""
# chart.py uses roll to rotate the chart around the target center
# by roll in anti-clockwise direction. Use -parallactic_angle
roll_deg = -calc_utils.sf_utils.radec_to_pa(ra_deg, dec_deg, dt)
else:
# No position or time/date available, so set roll to 0.0
roll_deg = 0.0
# No position or time/date available. Default to display in equatorial coordinate
roll_deg = 0.0 # NCP up
elif mount_type == "EQ":
# EQ-mounts: Display chart with NCP up so roll = 0.0
roll_deg = 0.0
# EQ-mounts: Display chart in equatorial coordinates
roll_deg = 0.0 # NCP up
# If location is available, adjust roll for hemisphere:
if location:
if location.lat < 0.0:
roll_deg = 180.0 # SCP up (for southern hemisphere)
else:
logger.error(f"Unknown mount type: {mount_type}. Cannot set roll.")
roll_deg = 0.0

# If location is available, adjust roll for hemisphere:
# Altaz: North up in northern hemisphere, South up in southern hemisphere
# EQ mounts: NCP up in northern hemisphere, SCP up in southern hemisphere
if location:
if location.lat < 0.0:
roll_deg += 180.0 # Southern hemisphere
roll_deg = 0.0 # NCP up

return roll_deg
Loading