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
62 changes: 47 additions & 15 deletions RMS/Astrometry/AstrometryNet.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

def astrometryNetSolveLocal(ff_file_path=None, img=None, mask=None, x_data=None, y_data=None,
fov_w_range=None, fov_w_hint=None, max_stars=100, verbose=False, x_center=None, y_center=None,
lat=None, lon=None, jd=None, input_intensities=None):
lat=None, lon=None, jd=None, input_intensities=None, position_hint=None):
""" Find an astrometric solution of X, Y image coordinates of stars detected on an image using the
local installation of astrometry.net.

Expand All @@ -52,6 +52,9 @@ def astrometryNetSolveLocal(ff_file_path=None, img=None, mask=None, x_data=None,
lon: [float] Station longitude in degrees. Required for iterative matching.
jd: [float] Julian date. Required for iterative matching.
input_intensities: [ndarray] Star intensities for brightness-based matching. Optional.
position_hint: [tuple] Optional (ra_deg, dec_deg, radius_deg). When given, astrometry.net is
told roughly where the field is, collapsing the blind all-sky search to that patch --
which lets narrow-FOV / faint cameras solve with far fewer stars. None = blind (default).

Returns:
[tuple] A tuple containing the following elements:
Expand Down Expand Up @@ -207,6 +210,7 @@ def astrometryNetSolveLocal(ff_file_path=None, img=None, mask=None, x_data=None,
scales = {14, 15, 16, 17, 18, 19}

size_hint = None
avg_fov_w = fov_h = None # set below when a FOV estimate is available

if fov_w_range is not None:

Expand Down Expand Up @@ -324,6 +328,30 @@ def astrometryNetSolveLocal(ff_file_path=None, img=None, mask=None, x_data=None,
)
)

# Optional position hint: when the caller knows roughly where the camera points (e.g. a
# previous platepar, or a known station pointing), this collapses the blind all-sky search
# to a small patch, letting narrow-FOV / faint cameras solve with far fewer stars than a
# blind solve needs. None (the default) preserves the original blind behaviour.
position_hint_obj = None
if position_hint is not None:
ra_hint, dec_hint, radius_hint = position_hint

# The local solver restricts the reference catalog to within radius_hint of the hint
# centre. If that radius is smaller than the image's own angular field, the outer stars
# get excluded and the quad match fails - a regression on wide-FOV cameras (e.g. a 15 deg
# hint on a ~64 deg fisheye drops everything past 15 deg from centre). Never let the hint
# be tighter than the field's circum-radius (half-diagonal, small margin). Only when a
# FOV estimate is available (it always is from autoFitPlatepar).
if avg_fov_w is not None and fov_h is not None:
field_radius = np.hypot(avg_fov_w, fov_h)/2.0*1.1
radius_hint = max(float(radius_hint), field_radius)

position_hint_obj = astrometry.PositionHint(
ra_deg=float(ra_hint), dec_deg=float(dec_hint), radius_deg=float(radius_hint))
if verbose:
print("Using position hint: RA={:.2f} Dec={:.2f} radius={:.1f} deg (>= field radius)".format(
ra_hint, dec_hint, radius_hint))

# If the solver.solve has the argument "stars", use a 2D array of stars instead of stars_xs and stars_ys
solve_args = inspect.getfullargspec(solver.solve).args
if "stars" in solve_args:
Expand All @@ -332,7 +360,7 @@ def astrometryNetSolveLocal(ff_file_path=None, img=None, mask=None, x_data=None,
solution = solver.solve(
stars=star_data,
size_hint=size_hint,
position_hint=None,
position_hint=position_hint_obj,
solution_parameters=solution_parameters
)

Expand All @@ -342,7 +370,7 @@ def astrometryNetSolveLocal(ff_file_path=None, img=None, mask=None, x_data=None,
stars_xs=x_data,
stars_ys=y_data,
size_hint=size_hint,
position_hint=None,
position_hint=position_hint_obj,
solution_parameters=solution_parameters
)

Expand Down Expand Up @@ -451,9 +479,9 @@ def astrometryNetSolveLocal(ff_file_path=None, img=None, mask=None, x_data=None,

def astrometryNetSolve(ff_file_path=None, img=None, mask=None, x_data=None, y_data=None, fov_w_range=None,
fov_w_hint=None, max_stars=100, verbose=False, x_center=None, y_center=None,
lat=None, lon=None, jd=None, input_intensities=None):
lat=None, lon=None, jd=None, input_intensities=None, position_hint=None):
""" Find an astrometric solution of X, Y image coordinates of stars detected on an image using the
local installation of astrometry.net.
local installation of astrometry.net or a compatible remote service.

Keyword arguments:
ff_file_path: [str] Path to the FF file to load.
Expand All @@ -471,10 +499,14 @@ def astrometryNetSolve(ff_file_path=None, img=None, mask=None, x_data=None, y_da
lon: [float] Station longitude in degrees. Required for iterative matching.
jd: [float] Julian date. Required for iterative matching.
input_intensities: [ndarray] Star intensities for brightness-based matching. Optional.
position_hint: [tuple] Optional (ra_deg, dec_deg, radius_deg). When given, astrometry.net is
told roughly where the field is, collapsing the blind all-sky search to that patch --
which lets narrow-FOV / faint cameras solve with far fewer stars. None = blind (default).
"""

# Helper to try coordinate-only first, then fall back to image if available
def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_cen, y_cen):
def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_cen, y_cen,
position_hint):
"""Try coordinate-only solve first, fall back to image if that fails."""

# If we have coordinates, try coordinate-only first (faster, less bandwidth)
Expand All @@ -484,7 +516,7 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
result = novaAstrometryNetSolve(
ff_file_path=None, img=None, x_data=x_coords, y_data=y_coords,
fov_w_range=fov_range, x_center=x_cen, y_center=y_cen,
api_url=api_url
api_url=api_url, position_hint=position_hint
)
if result is not None:
return result
Expand All @@ -498,15 +530,15 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
return novaAstrometryNetSolve(
ff_file_path=ff_path, img=image, x_data=None, y_data=None,
fov_w_range=fov_range, x_center=x_cen, y_center=y_cen,
api_url=api_url
api_url=api_url, position_hint=position_hint
)
return None

# No coordinates, just try with image
return novaAstrometryNetSolve(
ff_file_path=ff_path, img=image, x_data=x_coords, y_data=y_coords,
fov_w_range=fov_range, x_center=x_cen, y_center=y_cen,
api_url=api_url
api_url=api_url, position_hint=position_hint
)

# If the local installation of astrometry.net is not available, use remote API
Expand All @@ -518,7 +550,7 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
try:
result = _tryRemoteSolve(
PRIMARY_API_URL, ff_file_path, img, x_data, y_data,
fov_w_range, x_center, y_center
fov_w_range, x_center, y_center, position_hint
)
if result is not None:
return result
Expand All @@ -529,7 +561,7 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
print(f"Trying fallback server: {FALLBACK_API_URL}")
return _tryRemoteSolve(
FALLBACK_API_URL, ff_file_path, img, x_data, y_data,
fov_w_range, x_center, y_center
fov_w_range, x_center, y_center, position_hint
)

else:
Expand All @@ -540,7 +572,7 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
ff_file_path=ff_file_path, img=img, mask=mask, x_data=x_data, y_data=y_data,
fov_w_range=fov_w_range, fov_w_hint=fov_w_hint, max_stars=max_stars, verbose=verbose,
x_center=x_center, y_center=y_center,
lat=lat, lon=lon, jd=jd, input_intensities=input_intensities
lat=lat, lon=lon, jd=jd, input_intensities=input_intensities, position_hint=position_hint
)

# If local fails, try remote APIs
Expand All @@ -554,7 +586,7 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
try:
result = _tryRemoteSolve(
PRIMARY_API_URL, ff_file_path, img, x_data, y_data,
fov_w_range, x_center, y_center
fov_w_range, x_center, y_center, position_hint
)
if result is not None:
return result
Expand All @@ -565,7 +597,7 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
print(f"Trying fallback server: {FALLBACK_API_URL}")
return _tryRemoteSolve(
FALLBACK_API_URL, ff_file_path, img, x_data, y_data,
fov_w_range, x_center, y_center
fov_w_range, x_center, y_center, position_hint
)


Expand Down Expand Up @@ -628,4 +660,4 @@ def _tryRemoteSolve(api_url, ff_path, image, x_coords, y_coords, fov_range, x_ce
star['ra_deg'], star['dec_deg'], star['x_pix'], star['y_pix']))

else:
print("No solution found.")
print("No solution found.")
12 changes: 10 additions & 2 deletions RMS/Astrometry/AstrometryNetNova.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def jobs_by_tag(self, tag, exact):


def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None, fov_w_range=None,
api_key=None, x_center=None, y_center=None, api_url=None):
api_key=None, x_center=None, y_center=None, api_url=None, position_hint=None):
""" Find an astrometric solution of X, Y image coordinates of stars detected on an image using the
nova.astrometry.net service or a compatible API.

Expand All @@ -299,6 +299,7 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None
y_center: [float] Y coordinate of the image center. If not given, the image center will be used.
api_url: [str] Custom API URL. None by default, in which case nova.astrometry.net will be used.
Can be set to use alternative servers like 'https://astro.contrailcast.com/api/'.
position_hint: [tuple] Optional (ra_deg, dec_deg, radius_deg) search-position hint.

Return:
(ra, dec, orientation, scale, fov_w, fov_h, star_data): [tuple of floats] All in degrees,
Expand Down Expand Up @@ -390,6 +391,13 @@ def _printWebLink(stat, first_status=None):
kwargs['scale_upper'] = scale_upper
kwargs['scale_units'] = 'degwidth' # FOV range is in degrees

# Restrict the remote all-sky search when an approximate field position is known
if position_hint is not None:
ra_hint, dec_hint, radius_hint = position_hint
kwargs['center_ra'] = float(ra_hint)
kwargs['center_dec'] = float(dec_hint)
kwargs['radius'] = float(radius_hint)


# Upload image or the list of stars
if file_handle is not None:
Expand Down Expand Up @@ -701,4 +709,4 @@ def _printWebLink(stat, first_status=None):
# 'radius': 29.77070194214109,
# 'dec': 75.03531583306331,
# 'height_arcsec': 105022.27049595825,
# 'orientation': -75.0809053003417}
# 'orientation': -75.0809053003417}
9 changes: 8 additions & 1 deletion RMS/Astrometry/AutoPlatepar.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ def autoFitPlatepar(dir_path, config, catalog_stars, platepar_template=None,
fwhm_mult=DEFAULT_BLEND_FWHM_MULT,
wide_fov_search=False,
final_catalog_stars=None,
verbose=True):
verbose=True,
position_hint=None):
"""
Automatically create a platepar from CALSTARS data in a directory.

Expand Down Expand Up @@ -366,7 +367,11 @@ def autoFitPlatepar(dir_path, config, catalog_stars, platepar_template=None,

wide_fov_search: [bool] If True, use a wide FOV search range (2掳 to 200掳) instead of
the config-based range. Used as fallback when the tight search fails.
final_catalog_stars: [ndarray] Optional catalog used for the final astrometric fit.
verbose: [bool] Print progress information
position_hint: [tuple] Optional (ra_deg, dec_deg, radius_deg) forwarded to astrometry.net
as a search-position hint. Lets star-starved / narrow-FOV cameras solve
with fewer stars. None = blind all-sky search (default).

Returns:
platepar: [Platepar] Fitted platepar object, or None if fitting failed
Expand Down Expand Up @@ -533,6 +538,7 @@ def autoFitPlatepar(dir_path, config, catalog_stars, platepar_template=None,
lon=platepar.lon,
jd=jd,
input_intensities=input_intensities,
position_hint=position_hint,
verbose=verbose
)

Expand All @@ -550,6 +556,7 @@ def autoFitPlatepar(dir_path, config, catalog_stars, platepar_template=None,
photometric_sigma=photometric_sigma,
fwhm_mult=fwhm_mult,
wide_fov_search=True,
position_hint=position_hint,
verbose=verbose
)

Expand Down
Loading