API Reference

Contents

API Reference#

Autofocusers#

class astrafocus.autofocuser.AnalyticResponseAutofocuser(autofocus_device_manager: AutofocusDeviceManager, exposure_time: float, focus_measure_operator: type[AnalyticResponseFocusedMeasureOperator], percent_to_cut: float = 50.0, focus_measure_operator_kwargs: dict | None = None, **kwargs)[source]#

Bases: SweepingAutofocuser

Autofocuser that fits a curve to the focus response curve and finds the best focus position.

Parameters:
  • autofocus_device_manager (AutofocusDeviceManager) – Interface to control the telescope and its focuser.

  • exposure_time (float) – Exposure time for image acquisition.

  • focus_measure_operator (AnalyticResponseFocusedMeasureOperator) – Operator to measure the focus of images using an analytic response curve.

  • percent_to_cut (float, optional) – Percentage of worst-performing focus positions to exclude when updating the search range (default is 50.0).

  • **kwargs – Additional keyword arguments.

Examples

>>> from astrafocus.interface.simulation import CabaretDeviceSimulator
>>> from astrafocus.star_size_focus_measure_operators import HFRStarFocusMeasure
>>> from astrafocus.autofocuser import AnalyticResponseAutofocuser
>>> sim = CabaretDeviceSimulator.default()
>>> araf = AnalyticResponseAutofocuser(
...     autofocus_device_manager=sim,
...     exposure_time=1.0,
...     focus_measure_operator=HFRStarFocusMeasure,
...     n_steps=(20, 5),
...     n_exposures=1,
...     decrease_search_range=True,
...     percent_to_cut=50,
... )
>>> _ = araf.run()
>>> abs(araf.best_focus_position - 10000) < 500
True
fit_focus_response_curve(focus_pos: ndarray, focus_measure: ndarray)[source]#
get_focus_response_curve_fit(focus_pos: int)[source]#
update_search_range(min_focus_pos, max_focus_pos) tuple[int, int][source]#

Update the search range for optimal focus position based on focus response curve.

Notes

This function updates the search range for the optimal focus position based on the focus response curve. It identifies the worst-performing positions in the current interval and adjusts the interval accordingly.

class astrafocus.autofocuser.AutofocuserBase(autofocus_device_manager: AutofocusDeviceManager, focus_measure_operator: FocusMeasureOperator, exposure_time: float, search_range: tuple[int, int] | None = None, initial_position: int | None = None, keep_images: bool = False, secondary_focus_measure_operators: dict | None = None, search_range_is_relative: bool = False, save_path: str | None = None)[source]#

Bases: ABC

Abstract base class for autofocusing algorithms.

Parameters:
  • autofocus_device_manager (AutofocusDeviceManager) – Interface to control the camera and its focuser.

  • focus_measure_operator (FocusMeasureOperator) – Operator to measure the focus of images.

  • exposure_time (float) – Exposure time for image acquisition.

  • search_range (Optional[Tuple[int, int]], optional) – Range of focus positions to search for the best focus (default is None, using the telescope’s allowed range).

  • initial_position (Optional[int], optional) – Initial focus position for the autofocus algorithm (default is None, using the telescope’s current position).

  • keep_images (bool, optional) – Whether to keep images for additional analysis (default is False).

  • secondary_focus_measure_operators (Optional[dict], optional) – Dictionary of additional focus measure operators for image analysis (default is an empty dictionary).

  • search_range_is_relative (bool, optional) – Whether the search range is relative to the initial position (default is False).

  • save_path (Optional[str], optional) – Path to save focus record to (default is None). If None, the focus record is not saved. If the path ends with ‘.csv’, the focus record is saved with that name. Otherwise, the focus record is saved as a csv file with a timestamp in the specified directory.

focus_record#

DataFrame containing focus positions and corresponding focus measures.

Type:

pd.DataFrame

best_focus_position#

Best focus position determined by the autofocus algorithm.

Type:

int or None

_image_record#

List to store images if ‘keep_images’ is True.

Type:

list

measure_focus(image: np.ndarray) float:[source]#

Measure the focus of a given image using the specified focus measure operator.

run():

Execute the autofocus algorithm. Handles exceptions and resets the focuser on failure.

_run():

Abstract method to be implemented by subclasses for the actual autofocus algorithm.

reset():

Reset the focuser to the initial position.

get_focus_record() Tuple[np.ndarray, np.ndarray]:[source]#

Retrieve the focus record as sorted arrays of focus positions and corresponding measures.

Examples

>>> autofocus_instance = AutofocuserBase(
...     autofocus_device_manager, focus_measure_operator, exposure_time
... )
>>> autofocus_instance.run()
property focus_record#
get_focus_record(threshold_nan_ratio: float = 0.25) tuple[ndarray, ndarray][source]#

Retrieve the focus record as sorted arrays of focus positions and corresponding measures.

Parameters:

threshold_nan_ratio (float, optional) – Threshold for the ratio of NaN values in the focus record to issue a warning about data quality (default is 0.25). If the ratio of NaN values exceeds this threshold, a warning is logged indicating that the results may be unreliable.

measure_focus(image: ndarray) float[source]#
reset()[source]#
run() bool[source]#
save_focus_log()[source]#
save_focus_record()[source]#
class astrafocus.autofocuser.NonParametricResponseAutofocuser(autofocus_device_manager, exposure_time, focus_measure_operator, extremum_estimator: RobustExtremumEstimator = <astrafocus.extremum_estimators.LOWESSExtremumEstimator object>, **kwargs)[source]#

Bases: SweepingAutofocuser

class astrafocus.autofocuser.SweepingAutofocuser(autofocus_device_manager: AutofocusDeviceManager, exposure_time: float, focus_measure_operator, n_steps: tuple[int] | int = (10,), n_exposures: int | ndarray = 1, search_range: tuple[int, int] | None = None, decrease_search_range=True, initial_position: int | None = None, **kwargs)[source]#

Bases: AutofocuserBase

Autofocuser implementation using a sweeping algorithm.

Parameters:
  • autofocus_device_manager (AutofocusDeviceManager) – Interface to control the camera and its focuser.

  • exposure_time (float) – Exposure time for image acquisition.

  • focus_measure_operator (FocusMeasureOperator) – Operator to measure the focus of images.

  • n_steps (Tuple[int], optional) – Number of steps for each sweep (default is (10,)). The length of this tuple determines the number of sweeps. The entries specify the number of steps for each sweep.

  • n_exposures (int | np.ndarray, optional) – Number of exposures at each focus position or an array specifying exposures for each sweep. If an integer is given, the same number of exposures is used for each sweep (default is 1). If an array is given, the length of the array must match the number of sweeps. (default is 1).

  • search_range (Optional[Tuple[int, int]], optional) – Range of focus positions to search for the best focus (default is None, using the telescope’s allowed range).

  • decrease_search_range (bool, optional) – Whether to decrease the search range after each sweep (default is True).

  • initial_position (Optional[int], optional) – Initial focus position for the autofocus algorithm (default is None, using the telescope’s current position).

  • **kwargs – Additional keyword arguments.

n_sweeps#

Number of sweeps to perform.

Type:

int

n_steps#

Number of steps for each sweep.

Type:

Tuple[int]

n_exposures#

Number of exposures at each focus position.

Type:

np.ndarray

decrease_search_range#

Whether to decrease the search range after each sweep.

Type:

bool

_run():

Execute the sweeping autofocus algorithm.

get_initial_direction(min_focus_pos, max_focus_pos) int:[source]#

Determine the initial direction of the sweep.

find_best_focus_position():

Find and set the best focus position based on the recorded focus measures.

_find_best_focus_position(focus_pos, focus_measure) Tuple[int, float]:[source]#

Abstract method to be implemented by subclasses for finding the best focus position.

_run_sweep(search_positions, n_exposures):

Perform a single sweep across the specified focus positions.

update_search_range(min_focus_pos, max_focus_pos) Tuple[int, int]:[source]#

Update the search range after each sweep.

integer_linspace(min_focus_pos, max_focus_pos, n_steps) np.ndarray:[source]#

Generate integer-spaced values within the specified range.

Examples

>>> from astrafocus.interface.simulation import CabaretDeviceSimulator
>>> from astrafocus.star_size_focus_measure_operators import HFRStarFocusMeasure
>>> from astrafocus.autofocuser import AnalyticResponseAutofocuser
>>> sim = CabaretDeviceSimulator.default()
>>> saf = AnalyticResponseAutofocuser(
...     autofocus_device_manager=sim,
...     exposure_time=1.0,
...     focus_measure_operator=HFRStarFocusMeasure,
...     n_steps=(20, 5),
...     n_exposures=1,
...     decrease_search_range=True,
...     percent_to_cut=50,
... )
>>> _ = saf.run()
>>> len(saf.focus_record) > 0
True
find_best_focus_position()[source]#
get_initial_direction(min_focus_pos, max_focus_pos)[source]#

Move upward if initial position is closer to min_focus_pos than max_focus_pos.

static integer_linspace(min_focus_pos, max_focus_pos, n_steps)[source]#

Notes

Search positions can be redundant >>> SweepingAutofocuser.integer_linspace(0, 1, 4) array([0, 0, 1, 1])

update_search_range(min_focus_pos, max_focus_pos)[source]#

Focus measure operators#

Image-based operators#

class astrafocus.focus_measure_operators.AbsoluteGradientFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Absolute Gradient'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.AnalyticResponseFocusedMeasureOperator(**kwargs)[source]#

Bases: FocusMeasureOperator

abstractmethod fit_focus_response_curve(measured_focus)[source]#
abstractmethod get_focus_response_curve_fit(focus_pos)[source]#
name = 'Analytic Response Focused Measure Operator'#
class astrafocus.focus_measure_operators.AutoCorrelationFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Auto Correlation'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.BrennerFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Brenner'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.FFTFocusMeasureTan2022(**kwargs)[source]#

Bases: FocusMeasureOperator

mathrm{FM} &= norm{bm{R phi}}_{1} \ mathrm{FFT}(x, y) &= R(x, y) expqty(-iphi(x,y)) \

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'FFT Tan 2022'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.FFTPhaseMagnitudeProductFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

mathrm{FM} &= norm{bm{R phi}}_{1} \ mathrm{FFT}(x, y) &= R(x, y) expqty(-iphi(x,y)) \

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'FFT Phase Magnitude Product'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.FFTPowerFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

mathrm{FM} &= norm{bm{R phi}}_{1} \ mathrm{FFT}(x, y) &= R(x, y) expqty(-iphi(x,y)) \

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'FFT Power'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.FocusMeasureOperator(**kwargs)[source]#

Bases: ABC

Abstract base class for focus measure operators.

smaller_is_better#

A class attribute indicating whether a smaller focus measure is considered better.

Type:

bool

name#

A class attribute representing the name of the focus measure operator.

Type:

str

__call__(image: np.ndarray, \*\*kwargs) float[source]#

Compute the focus measure of the input image.

measure_focus(image: np.ndarray, \*\*kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

convert_to_grayscale(image: np.ndarray) np.ndarray[source]#

Convert the input image to grayscale.

validate_image(image: np.ndarray)[source]#

Validate the input image for compatibility with focus measure algorithms.

static convert_to_grayscale(image: ndarray[tuple[Any, ...], dtype[floating | integer]]) ndarray[tuple[Any, ...], dtype[floating | integer]][source]#

Convert the input image to grayscale.

abstractmethod measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

smaller_is_better = True#
static validate_image(image)[source]#

Validate the input image for compatibility with focus measure algorithms.

class astrafocus.focus_measure_operators.LaplacianFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Laplacian'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.NormalizedVarianceFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Normalized Variance'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.SquaredGradientFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Squared Gradient'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.TenengradFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], ksize=1, **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Tenengrad'#
smaller_is_better = False#
class astrafocus.focus_measure_operators.VarianceOfLaplacianFocusMeasure(**kwargs)[source]#

Bases: FocusMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Variance Of Laplacian'#
smaller_is_better = False#

Star-size operators#

class astrafocus.star_size_focus_measure_operators.GaussianStarFocusMeasure(ref_image=None, fwhm=2.0, star_find_threshold=5.0, absolute_detection_limit=0.0, cutout_size: int = 15, saturation_threshold=None, max_stars=100, **kwargs)[source]#

Bases: StarSizeFocusMeasure

from astrafocus.utils.fits import load_fits_with_focus_pos_from_directory fits_directory = “path_to_fits_files” image_data, headers, focus_pos = load_fits_with_focus_pos_from_directory(fits_directory)

image = image_data[0] gsfm = GaussianStarFocusMeasure(image, fwhm=2.0, star_find_threshold=8.0) gsfm.star_finder.selected_stars

import matplotlib.pyplot as plt fm_vals = [gsfm.measure_focus(image) for image in image_data] plt.plot(focus_pos, fm_vals, ls=’’, marker=’.’); plt.show() # doctest: +SKIP

plot_focus_response_curve(gsfm, image_data, focus_pos, plot_name=’gaussian_star.pdf’)

fit_focus_response_curve(focus_pos, measured_focus)[source]#
static fit_hyperbola(x, y)[source]#
get_focus_response_curve_fit(focus_pos)[source]#
static hyperbola(x, a=1, b=1, x_0=0, y_0=0)[source]#

Notes

If we want the hyperbola to be north-south opening, we need to use the parametrisation (y-y_0)**2/b**2 - (x-x_0)**2/a**2 = 1

Resulting in the following parametrisation of the north opening hyperbola y = b * sqrt(1 + ((x-x_0)/a)**2) + y_0

Examples

>>> x = np.linspace(-1, 2)
>>> plt.plot(x, hyperbola(x=x, a=1, b=1, x_0=0, y_0=-1)); plt.show()
name = 'Gaussian'#
class astrafocus.star_size_focus_measure_operators.HFRStarFocusMeasure(ref_image=None, fwhm=2.0, star_find_threshold=5.0, absolute_detection_limit=0.0, cutout_size: int = 15, saturation_threshold=None, max_stars=100, **kwargs)[source]#

Bases: StarSizeFocusMeasure

from astrafocus.utils.fits import load_fits_with_focus_pos_from_directory fits_directory = “path_to_fits_files” image_data, headers, focus_pos = load_fits_with_focus_pos_from_directory(fits_directory)

image = image_data[0] hfrfm = HFRStarFocusMeasure(image, fwhm=2.0, star_find_threshold=8.0) hfrfm.star_finder.selected_stars

import matplotlib.pyplot as plt fm_vals = [hfrfm.measure_focus(image) for image in image_data] plt.plot(focus_pos, fm_vals/np.min(fm_vals), ls=’’, marker=’.’) plt.plot(

focus_pos, hfrfm.linear_V_curve(focus_pos, *(-0.003, 0.003, np.mean(focus_pos), np.min(fm_vals/np.min(fm_vals))))

) plt.show() # doctest: +SKIP

plot_focus_response_curve(hfrfm, image_data, focus_pos, plot_name=’HFR_star.pdf’)

hfrfm.fit_focus_response_curve(focus_pos, fm_vals/np.min(fm_vals)) hfrfm.get_focus_response_curve_fit(focus_pos) focus_pos_fine = np.linspace(np.min(focus_pos), np.max(focus_pos))

fit_focus_response_curve(focus_pos, measured_focus)[source]#
static fit_linear_V_curve(x, y)[source]#
get_focus_response_curve_fit(focus_pos)[source]#
static linear_V_curve(x, slope_left=-1, slope_right=1, x_centre=0, intercept=0)[source]#

Examples

>>> x = np.linspace(-2, 2, 200)
>>> plt.plot(x, linear_V_curve(x=x)); plt.show()

y_min = slope_left*v_centre + intercept_left = slope_right*v_centre + intercept_right (slope_left-slope_right)*v_centre = intercept_right - intercept_left v_centre = (intercept_right-intercept_left)/(slope_left - slope_right)

intercept_diff = (intercept_right - intercept_left)

v_centre = intercept_diff /(slope_left - slope_right)

slope_left*(x-v_centre) + b = slope_left*x + (b - slope_left**v_centre) slope_right*(x-v_centre) + b = slope_right*x + (b - v_centre*slope_right) intercept_diff = v_centre*(slope_left-slope_right)

v_centre = (intercept_right - intercept_left) / (slope_left - slope_right)

static linear_V_curve_prime(x, slope_left=-1, slope_right=1, intercept_left=0, intercept_right=0)[source]#

Examples

>>> x = np.linspace(-2, 2, 200)
>>> plt.plot(x, linear_V_curve(x=x)); plt.show()

slope_left*v_centre + intercept_left = slope_right*v_centre + intercept_right (slope_left-slope_right)*v_centre = intercept_right - intercept_left v_centre = (intercept_right-intercept_left)/(slope_left - slope_right)

name = 'HFR'#
class astrafocus.star_size_focus_measure_operators.StarSizeFocusMeasure(model, ref_image=None, fwhm=2.0, star_find_threshold=5.0, absolute_detection_limit=0.0, cutout_size: int = 15, saturation_threshold=None, max_stars=100, **kwargs)[source]#

Bases: AnalyticResponseFocusedMeasureOperator

measure_focus(image: ndarray[tuple[Any, ...], dtype[floating | integer]], cutout_size: int | None = None, **kwargs) float[source]#

Abstract method to be implemented by subclasses. Compute the focus measure of the input image.

Parameters:

image (np.ndarray) – Input image.

Returns:

Computed focus measure.

Return type:

float

name = 'Size'#

Extremum estimators#

Robust Extremum Estimation Module

This module provides a framework for robustly estimating the minimum or maximum values in a set of measurements using various techniques. It includes a base class and several subclasses, each implementing a different extremum estimation technique.

Classes#

  • RobustExtremumEstimator: Abstract base class providing methods for estimating extrema.

  • MedianFilterExtremumEstimation: Estimation using a median filter for robustness.

  • LOWESSExtremumEstimator: Estimation using Locally Weighted Scatterplot Smoothing (LOWESS).

  • SplineExtremumEstimator: Estimation using Univariate Spline interpolation.

  • RBFExtremumEstimator: Estimation using Radial Basis Function (RBF) interpolation.

- `argmin(x, y, return_value=True)`: Returns the x-value corresponding to the minimum estimated y-value.

If return_value is True, also returns the estimated y-value at the minimum.

- `argmax(x, y, return_value=True)`: Returns the x-value corresponding to the maximum estimated y-value.

If return_value is True, also returns the estimated y-value at the maximum.

- `estimate_robust_signal(x, y)`: Abstract method to be implemented by subclasses for specific

extremum estimation techniques. Returns the x and y values of a smoothed version of the curve.

class astrafocus.extremum_estimators.ExtremumEstimatorRegistry[source]#

Bases: object

Dictionary mapping string keys to extremum estimator classes.

Examples

>>> from astrafocus.extremum_estimators import ExtremumEstimatorRegistry
>>> ExtremumEstimatorRegistry.list()
['lowess', 'median', 'rbf', 'spline']
>>> ExtremumEstimatorRegistry.from_name("spline")
<class 'astrafocus.extremum_estimators.SplineExtremumEstimator'>
classmethod from_name(key: str)[source]#

Get an ExtremumEstimator by fuzzy matching the key. Returns lowess if not found.

classmethod get(key: str, default=<class 'astrafocus.extremum_estimators.LOWESSExtremumEstimator'>)[source]#

Get a focus measure operator class by fuzzy matching the key. Returns hfr if not found.

classmethod list()[source]#

List all available extremum estimators.

class astrafocus.extremum_estimators.LOWESSExtremumEstimator(frac=0.5, it=3, **kwargs)[source]#

Bases: RobustExtremumEstimator

estimate_robust_signal(x, y)[source]#

Estimates the robust signal using Locally Weighted Scatterplot Smoothing (LOWESS).

Parameters:
  • x (np.ndarray) – The input x values.

  • y (np.ndarray) – The input y values.

Returns:

Tuple of x and y values of the smoothed curve.

Return type:

Tuple[np.ndarray, np.ndarray]

name = 'LOWESS'#
class astrafocus.extremum_estimators.MedianFilterExtremumEstimation(size=10, **kwargs)[source]#

Bases: RobustExtremumEstimator

estimate_robust_signal(x, y)[source]#

Estimates the robust signal using a median filter.

Parameters:
  • x (np.ndarray) – The input x values.

  • y (np.ndarray) – The input y values.

Returns:

Tuple of x and y values of the smoothed curve.

Return type:

Tuple[np.ndarray, np.ndarray]

name = 'Median Filter Extremum Estimation'#
class astrafocus.extremum_estimators.RBFExtremumEstimator(kernel='linear', smoothing=20, **kwargs)[source]#

Bases: RobustExtremumEstimator

estimate_robust_signal(x, y)[source]#

Estimates the robust signal using Radial Basis Function (RBF) interpolation.

Parameters:
  • x (np.ndarray) – The input x values.

  • y (np.ndarray) – The input y values.

Returns:

Tuple of x and y values of the smoothed curve.

Return type:

Tuple[np.ndarray, np.ndarray]

name = 'RBF'#
class astrafocus.extremum_estimators.RobustExtremumEstimator[source]#

Bases: ABC

argmax(x: ndarray, y: ndarray, return_value=True) float | tuple[float, float][source]#

Returns the x-value corresponding to the maximum estimated noise-resistant y-value.

Parameters:
  • x (np.ndarray) – The input x values.

  • y (np.ndarray) – The input y values.

  • return_value (bool) – Whether to return the estimated y-value at the maximum. Defaults to True.

Returns:

The x-value corresponding to the maximum estimated noise-resistant y-value. If return_value is True, also returns the estimated y-value at the maximum.

Return type:

float | Tuple[float, float]

argmin(x: ndarray, y: ndarray, return_value=True) float | tuple[float, float][source]#

Returns the x-value corresponding to the minimum estimated noise-resistant y-value.

Parameters:
  • x (np.ndarray) – The input x values.

  • y (np.ndarray) – The input y values.

  • return_value (bool) – Whether to return the estimated y-value at the minimum. Defaults to True.

Returns:

The x-value corresponding to the minimum estimated noise-resistant y-value. If return_value is True, also returns the estimated y-value at the minimum.

Return type:

float | Tuple[float, float]

abstractmethod estimate_robust_signal(x: ndarray, y: ndarray) tuple[ndarray, ndarray][source]#

Abstract method to be implemented by subclasses for robust extremum signal estimation.

registry = {}#

Abstract base class for robust extremum estimation.

This class provides methods to estimate the minimum or maximum values in a set of measurements using various techniques. Subclasses must implement the estimate_robust_signal method.

name#

The name of the extremum estimator, derived from the class name if not explicitly set.

Type:

str

Examples

>>> from astrafocus.extremum_estimators import MedianFilterExtremumEstimation
>>> estimator = MedianFilterExtremumEstimation(size=2)
>>> y = np.array([10, 11, 8, 5, 4, 6, 9, 11, 11])
>>> x = np.arange(len(y))
>>> x_min, y_min = estimator.argmin(x, y)
>>> print(f"{x_min}, {y_min}")
4, 5
static sort(x: ndarray, y: ndarray) tuple[ndarray, ndarray][source]#

Sorts the x and y values by the x values.

unknown_kwargs(kwargs: dict)[source]#
class astrafocus.extremum_estimators.SplineExtremumEstimator(k=2, **kwargs)[source]#

Bases: RobustExtremumEstimator

estimate_robust_signal(x, y)[source]#

Estimates the robust signal using Univariate Spline interpolation.

Parameters:
  • x (np.ndarray) – The input x values.

  • y (np.ndarray) – The input y values.

Returns:

Tuple of x and y values of the smoothed curve.

Return type:

Tuple[np.ndarray, np.ndarray]

name = 'Spline'#

Star detection#

class astrafocus.star_finder.StarFinder(ref_image: ndarray[tuple[Any, ...], dtype[floating | integer]], fwhm: float = 3.0, star_find_threshold: float = 4.0, absolute_detection_limit: float = 0.0, saturation_threshold: float | None = None, max_stars: int = 50)[source]#

Bases: object

Examples

TargetFinder(ref_image)

FALLBACK_THRESHOLDS = array([4. , 3. , 2.5])#
classmethod find_sources(ref_image: ndarray[tuple[Any, ...], dtype[floating | integer]], fwhm: float = 3.0, threshold: float = 4.0, std=None, background=None, saturation_threshold=None, absolute_detection_limit: float = 0.0, max_stars: int = 50)[source]#

Detect and locate stars using a tiered-threshold DAOFIND approach.

This method performs an initial search at the specified threshold. If no sources are found, it automatically falls back to searching at progressively lower thresholds defined in FALLBACK_THRESHOLDS. This is designed to maintain autofocus reliability even when stars are blurred (out-of-focus) or sky transparency drops.

Parameters:
  • ref_image (2D array_like) – The background-subtracted or raw image array.

  • fwhm (float, optional) – Full-Width at Half-Maximum (pixels) of the Gaussian kernel. Standard ground-based telescopes typically use 2.5 to 4.0.

  • threshold (float, optional) – Initial detection threshold in units of background standard deviation (sigma). Default is 4.0.

  • std (float, optional) – Background noise standard deviation. If None, estimated via sigma-clipped statistics.

  • background (float, optional) – Median background level. If None, estimated via sigma-clipped statistics.

  • saturation_threshold (float, optional) – Maximum allowed pixel value. Peaks above this are rejected (useful for excluding saturated stars that bias centroids).

  • absolute_detection_limit (float, optional) – The hard floor for detection in ADU/counts. The effective threshold is max(absolute_limit, std * threshold).

  • max_stars (int, optional) – Capping limit for returned sources to optimize downstream processing speed. Sorted by brightness.

Returns:

A table of detected sources with centroids and photometry, or None if no sources meet the criteria even after fallbacks.

Return type:

astropy.table.QTable or None

Notes

The tiered approach prevents the “Noise Explosion” problem. Searching immediately at 2.0 sigma on a noisy CMOS sensor can result in thousands of false positives, slowing down the characterization phase significantly. By starting higher and falling back only when necessary, we balance speed with sensitivity.

classmethod get_fallback_thresholds(threshold: float | None) ndarray[source]#
class astrafocus.star_fitter.StarFitter(model: _ModelMeta, scale_factor: int = 1, fitter: <module 'astropy.modeling.fitting' from '/home/runner/work/astrafocus/astrafocus/.venv/lib/python3.11/site-packages/astropy/modeling/fitting.py'> = <astropy.modeling.fitting.LevMarLSQFitter object>)[source]#

Bases: object

A class for fitting astropy-like models to star data to calculate the size of stars.

Parameters:

model (astropy.modeling.Model) – The astropy model to fit to the star data.

model#

The astropy model used for fitting.

Type:

astropy.modeling.Model

result#

The result of the fitting process.

Type:

astropy.modeling.Model

fwhm#

The full-width at half-maximum (FWHM) of the fitted model.

Type:

float

fit(star_data, \*args, \*\*kwargs)[source]#

Fit the specified model to the given star_data.

calculate_avg_fwhm(result)#

Calculate the average FWHM from the fitted model parameters.

Raises:

ValueError – If the object has not been fitted yet or if FWHM calculation is not supported for the model type.

Examples

>>> from astropy.modeling import models
>>> from astrafocus.interface.simulation import CabaretDeviceSimulator
>>> from astrafocus.star_size_focus_measure_operators import GaussianStarFocusMeasure
>>> image = CabaretDeviceSimulator.generate_image()
>>> gsfm = GaussianStarFocusMeasure(image)
>>> star = gsfm.star_finder.selected_stars[0]
>>> star_fitter = StarFitter(models.Gaussian2D)
>>> _ = star_fitter.fit_source(image, star=star, cutout_size=15)
>>> bool(star_fitter.star_size > 0)
True
calculate_star_sizes_of_selection(images, selected_stars, cutout_size, *args, **kwargs)[source]#
fit(star_data, *args, **kwargs)[source]#

Fit the specified model to the window around the star provided in star_data.

Parameters:
  • star_data (numpy.ndarray) – A small section of the full image containing the intensity profile of the star.

  • *args (additional arguments and keyword arguments) – Additional arguments to pass to the model constructor.

  • **kwargs (additional arguments and keyword arguments) – Additional arguments to pass to the model constructor.

fit_source(image, star, cutout_size=15, *args, **kwargs)[source]#
fitter(model, x, y, star_data, *args, **kwargs)[source]#
static get_bounds(parameters, star_data) dict[source]#
static get_masked_star(image, star, cutout_size)[source]#
static get_parameter_init(parameters, star_data, expected_star_size=5) dict[source]#
integrate_source(image, star, cutout_size=15, fitter=<astropy.modeling.fitting.LevMarLSQFitter object>, *args, **kwargs)[source]#
property parameter_dict#
rescale_result()[source]#
property result#
property star_size#

Calculate the full-width at half-maximum (FWHM) of the fitted model. In the case of a 2d Gaussian, return the average FWHM.

Registries#

class astrafocus.AnalyticResponseAutofocuser(autofocus_device_manager: AutofocusDeviceManager, exposure_time: float, focus_measure_operator: type[AnalyticResponseFocusedMeasureOperator], percent_to_cut: float = 50.0, focus_measure_operator_kwargs: dict | None = None, **kwargs)[source]

Bases: SweepingAutofocuser

Autofocuser that fits a curve to the focus response curve and finds the best focus position.

Parameters:
  • autofocus_device_manager (AutofocusDeviceManager) – Interface to control the telescope and its focuser.

  • exposure_time (float) – Exposure time for image acquisition.

  • focus_measure_operator (AnalyticResponseFocusedMeasureOperator) – Operator to measure the focus of images using an analytic response curve.

  • percent_to_cut (float, optional) – Percentage of worst-performing focus positions to exclude when updating the search range (default is 50.0).

  • **kwargs – Additional keyword arguments.

Examples

>>> from astrafocus.interface.simulation import CabaretDeviceSimulator
>>> from astrafocus.star_size_focus_measure_operators import HFRStarFocusMeasure
>>> from astrafocus.autofocuser import AnalyticResponseAutofocuser
>>> sim = CabaretDeviceSimulator.default()
>>> araf = AnalyticResponseAutofocuser(
...     autofocus_device_manager=sim,
...     exposure_time=1.0,
...     focus_measure_operator=HFRStarFocusMeasure,
...     n_steps=(20, 5),
...     n_exposures=1,
...     decrease_search_range=True,
...     percent_to_cut=50,
... )
>>> _ = araf.run()
>>> abs(araf.best_focus_position - 10000) < 500
True
fit_focus_response_curve(focus_pos: ndarray, focus_measure: ndarray)[source]
get_focus_response_curve_fit(focus_pos: int)[source]
update_search_range(min_focus_pos, max_focus_pos) tuple[int, int][source]

Update the search range for optimal focus position based on focus response curve.

Notes

This function updates the search range for the optimal focus position based on the focus response curve. It identifies the worst-performing positions in the current interval and adjusts the interval accordingly.

class astrafocus.ExtremumEstimatorRegistry[source]

Bases: object

Dictionary mapping string keys to extremum estimator classes.

Examples

>>> from astrafocus.extremum_estimators import ExtremumEstimatorRegistry
>>> ExtremumEstimatorRegistry.list()
['lowess', 'median', 'rbf', 'spline']
>>> ExtremumEstimatorRegistry.from_name("spline")
<class 'astrafocus.extremum_estimators.SplineExtremumEstimator'>
classmethod from_name(key: str)[source]

Get an ExtremumEstimator by fuzzy matching the key. Returns lowess if not found.

classmethod get(key: str, default=<class 'astrafocus.extremum_estimators.LOWESSExtremumEstimator'>)[source]

Get a focus measure operator class by fuzzy matching the key. Returns hfr if not found.

classmethod list()[source]

List all available extremum estimators.

class astrafocus.FocusMeasureOperatorRegistry[source]

Bases: object

Registry mapping string keys to focus measure operator classes.

Examples

>>> from astrafocus import FocusMeasureOperatorRegistry
>>> FocusMeasureOperatorRegistry.list()
['hfr', ..., 'auto_correlation']
>>> FocusMeasureOperatorRegistry.from_name("fft")
<class 'astrafocus.focus_measure_operators.FFTFocusMeasureTan2022'>
classmethod from_name(key: str)[source]

Get a focus measure operator class by fuzzy matching the key. Returns hfr if not found.

classmethod get(key: str, default=<class 'astrafocus.star_size_focus_measure_operators.HFRStarFocusMeasure'>)[source]

Get a focus measure operator class by fuzzy matching the key. Returns hfr if not found.

classmethod list()[source]

List all available focus measure operators.

class astrafocus.FocusMeasureScan(operators: list[FocusMeasureOperator])[source]

Bases: object

Run one or more focus measure operators over a sequence of FITS images.

Parameters:

operators (list[FocusMeasureOperator]) – One or more focus measure operator instances to apply to each image.

Examples

>>> from astrafocus import FocusMeasureOperatorRegistry
>>> from astrafocus.focus_measure_scan import FocusMeasureScan
>>> scan = FocusMeasureScan.from_names(["Brenner", "Tenengrad", "FFT"])
>>> scan = FocusMeasureScan.from_names(["FFT"])
>>> scan.operators.append(FocusMeasureOperatorRegistry.from_name("HFR")(fwhm=4.0))
classmethod from_names(names: list[str]) FocusMeasureScan[source]

Construct a FocusMeasureScan from a list of operator name strings.

Parameters:

names (list[str]) – Operator names as accepted by FocusMeasureOperatorRegistry.from_name (e.g. ["brenner", "tenengrad"]).

plot_all(df, plot_kwargs={}, log_scale=False, axes=None)[source]
run(directory: str | Path, pattern: str = '*.fits', header_fields: list[str] | None = ['DATE-OBS', 'FOCUSPOS']) DataFrame[source]

Compute focus measures for all FITS files in a directory.

Parameters:
  • directory (str or Path) – Directory containing FITS files.

  • pattern (str) – Glob pattern used to find FITS files. Default is "*.fits".

  • header_fields (list[str], optional) – FITS header keywords to extract into the result DataFrame (e.g. ["FOCPOS"]).

Returns:

One row per file. Columns: file, any requested header_fields, and one column per operator named by operator.name.

Return type:

pd.DataFrame

Examples

>>> import tempfile
>>> import cabaret
>>> from cabaret.sources import Sources
>>> import numpy as np
>>> from astrafocus.focus_measure_scan import FocusMeasureScan
>>> sources = Sources.from_arrays(
...     ra=np.array([10.684]), dec=np.array([41.269]), fluxes=np.array([1e5])
... )
>>> with tempfile.TemporaryDirectory() as tmpdir:
...     obs = cabaret.Observatory(focuser={"position": 10000, "best_position": 10000})
...     _ = obs.generate_fits_image(
...         ra=10.684, dec=41.269, sources=sources, exp_time=10, seed=42,
...         file_path=f"{tmpdir}/focus_10000.fits",
...         user_header={"FOCUSPOS": 10000},
...     )
...     scan = FocusMeasureScan.from_names(["Brenner"])
...     df = scan.run(tmpdir, header_fields=["FOCUSPOS"])
...     list(df.columns)
['file', 'FOCUSPOS', 'Brenner']
class astrafocus.NonParametricResponseAutofocuser(autofocus_device_manager, exposure_time, focus_measure_operator, extremum_estimator: RobustExtremumEstimator = <astrafocus.extremum_estimators.LOWESSExtremumEstimator object>, **kwargs)[source]

Bases: SweepingAutofocuser

Hardware interface#

class astrafocus.interface.AutofocusDeviceManager(camera: CameraInterface, focuser: FocuserInterface, telescope: TelescopeInterface | None = TelescopeInterface())[source]

Bases: ABC

Abstract base class representing a telescope interface.

Parameters:
perform_exposure_at(focus_position: int, texp: float) ImageType[source]

Take an observation at a specific focus position with a given exposure time.

move_focuser_to_position(desired_position)[source]

Move the focuser to a desired position.

check_conditions() bool[source]

Check if observation conditions are good enough to take exposures. Default is True. The implementation of this method is optional, although it is recommended to provide one. This function will be used to interrupt the autofocus process if the conditions get bad.

Examples

Create a trivial autofocus device manager.

>>> from astrafocus.interface.device_manager import AutofocusDeviceManager
>>> from astrafocus.interface.camera import TrivialCamera
>>> from astrafocus.interface.focuser import TrivialFocuser
>>> from astrafocus.interface.telescope import TrivialTelescope
>>> autofocus_device_manager = AutofocusDeviceManager(
...     camera=TrivialCamera(),
...     focuser=TrivialFocuser(current_position=0, allowed_range=(0, 1000)),
...     telescope=TrivialTelescope(),
... )
check_conditions() bool[source]
move_focuser_to_position(desired_position: int)[source]
perform_exposure_at(focus_position: int, texp: float) ndarray[tuple[Any, ...], dtype[floating | integer]][source]

Take an observation at a specific focus position with a given exposure time.

Parameters:
  • focus_position (int) – Desired focus position.

  • texp (float) – Exposure time.

Returns:

Resulting image.

Return type:

ImageType

class astrafocus.interface.CameraInterface[source]

Bases: ABC

Abstract base class representing a telescope camera interface.

perform_exposure(texp)[source]

Take an observation with a specified exposure time.

abstractmethod perform_exposure(texp: float) ndarray[tuple[Any, ...], dtype[floating | integer]][source]

Abstract method to take an observation with a specified exposure time.

Parameters:

texp (float) – Exposure time.

Returns:

Resulting image.

Return type:

ImageType

class astrafocus.interface.FocuserInterface(current_position, allowed_range: tuple[int, int])[source]

Bases: ABC

A class to manage the focus of a telescope.

position

The current focuser position in steps.

Type:

int

allowed_range

The range of allowed focuser steps (min_step, max_step).

Type:

tuple

move_by_steps(steps_to_move)[source]

Move the focuser relative to the current position by n steps.

is_within_range(desired_position: int)[source]

Check whether a desired focuser position is within the allowed range.

Parameters:

desired_position (int) – The desired focuser position to check.

Returns:

True if the desired position is within the allowed range, False otherwise.

Return type:

bool

move_by_steps(steps_to_move: int)[source]

Move the focuser relative to the current position by n steps.

Parameters:

steps_to_move (int) – The number of steps to move relative to the current position.

Raises:

ValueError – If moving relative exceeds the allowed range.

abstractmethod move_focuser_to_position(new_position: int)[source]
property position
validate_allowed_range()[source]

Validate the allowed range provided during initialization.

Raises:

ValueError – If the allowed range is not a tuple, list, or numpy array, or if any items in the range are not integers, or if the range does not consist of two integers.

validate_desired_position(desired_position: int)[source]
class astrafocus.interface.TelescopeInterface[source]

Bases: ABC

A calss to interface the pointing of the telescope to a specific coordinate in the equatorial coordinate system.

point_to(coordinates)[source]

Point the telescope to a specific coordinate in the equatorial coordinate system.

Parameters:

coordinates (~astropy.coordinates.SkyCoord) – The ICRS coordinates that should be in the centre of the CCD.

abstractmethod set_telescope_position(coordinates: SkyCoord)[source]

Point the telescope to a specific coordinate in the equatorial coordinate system.

Parameters:

coordinates (~astropy.coordinates.SkyCoord) – The ICRS coordinates that should be in the centre of the CCD.

validate_arguments(coordinates: SkyCoord)[source]
class astrafocus.interface.device_manager.AutofocusDeviceManager(camera: CameraInterface, focuser: FocuserInterface, telescope: TelescopeInterface | None = TelescopeInterface())[source]#

Bases: ABC

Abstract base class representing a telescope interface.

Parameters:
perform_exposure_at(focus_position: int, texp: float) ImageType[source]#

Take an observation at a specific focus position with a given exposure time.

move_focuser_to_position(desired_position)[source]#

Move the focuser to a desired position.

check_conditions() bool[source]#

Check if observation conditions are good enough to take exposures. Default is True. The implementation of this method is optional, although it is recommended to provide one. This function will be used to interrupt the autofocus process if the conditions get bad.

Examples

Create a trivial autofocus device manager.

>>> from astrafocus.interface.device_manager import AutofocusDeviceManager
>>> from astrafocus.interface.camera import TrivialCamera
>>> from astrafocus.interface.focuser import TrivialFocuser
>>> from astrafocus.interface.telescope import TrivialTelescope
>>> autofocus_device_manager = AutofocusDeviceManager(
...     camera=TrivialCamera(),
...     focuser=TrivialFocuser(current_position=0, allowed_range=(0, 1000)),
...     telescope=TrivialTelescope(),
... )
check_conditions() bool[source]#
move_focuser_to_position(desired_position: int)[source]#
perform_exposure_at(focus_position: int, texp: float) ndarray[tuple[Any, ...], dtype[floating | integer]][source]#

Take an observation at a specific focus position with a given exposure time.

Parameters:
  • focus_position (int) – Desired focus position.

  • texp (float) – Exposure time.

Returns:

Resulting image.

Return type:

ImageType

class astrafocus.interface.device_manager.TrivialAutofocusDeviceManager(camera: CameraInterface = TrivialCamera(), focuser: FocuserInterface = FocuserInterface(current_position=0, allowed_range=(0, 1000)), telescope: TelescopeInterface = TelescopeInterface())[source]#

Bases: AutofocusDeviceManager

A trivial telescope interface for testing purposes.

class astrafocus.interface.camera.CameraInterface[source]#

Bases: ABC

Abstract base class representing a telescope camera interface.

perform_exposure(texp)[source]#

Take an observation with a specified exposure time.

abstractmethod perform_exposure(texp: float) ndarray[tuple[Any, ...], dtype[floating | integer]][source]#

Abstract method to take an observation with a specified exposure time.

Parameters:

texp (float) – Exposure time.

Returns:

Resulting image.

Return type:

ImageType

class astrafocus.interface.camera.TrivialCamera[source]#

Bases: CameraInterface

A trivial camera interface for testing purposes.

perform_exposure(texp: float) ndarray[tuple[Any, ...], dtype[floating | integer]] | None[source]#

Abstract method to take an observation with a specified exposure time.

Parameters:

texp (float) – Exposure time.

Returns:

Resulting image.

Return type:

ImageType

class astrafocus.interface.focuser.FocuserInterface(current_position, allowed_range: tuple[int, int])[source]#

Bases: ABC

A class to manage the focus of a telescope.

position#

The current focuser position in steps.

Type:

int

allowed_range#

The range of allowed focuser steps (min_step, max_step).

Type:

tuple

move_by_steps(steps_to_move)[source]#

Move the focuser relative to the current position by n steps.

is_within_range(desired_position: int)[source]#

Check whether a desired focuser position is within the allowed range.

Parameters:

desired_position (int) – The desired focuser position to check.

Returns:

True if the desired position is within the allowed range, False otherwise.

Return type:

bool

move_by_steps(steps_to_move: int)[source]#

Move the focuser relative to the current position by n steps.

Parameters:

steps_to_move (int) – The number of steps to move relative to the current position.

Raises:

ValueError – If moving relative exceeds the allowed range.

abstractmethod move_focuser_to_position(new_position: int)[source]#
property position#
validate_allowed_range()[source]#

Validate the allowed range provided during initialization.

Raises:

ValueError – If the allowed range is not a tuple, list, or numpy array, or if any items in the range are not integers, or if the range does not consist of two integers.

validate_desired_position(desired_position: int)[source]#
class astrafocus.interface.focuser.TrivialFocuser(current_position, allowed_range=tuple[int, int])[source]#

Bases: FocuserInterface

Trivial implementation to set the telescope focuser for testing purposes.

move_focuser_to_position(new_position: int)[source]#
class astrafocus.interface.telescope.TelescopeInterface[source]#

Bases: ABC

A calss to interface the pointing of the telescope to a specific coordinate in the equatorial coordinate system.

point_to(coordinates)[source]#

Point the telescope to a specific coordinate in the equatorial coordinate system.

Parameters:

coordinates (~astropy.coordinates.SkyCoord) – The ICRS coordinates that should be in the centre of the CCD.

abstractmethod set_telescope_position(coordinates: SkyCoord)[source]#

Point the telescope to a specific coordinate in the equatorial coordinate system.

Parameters:

coordinates (~astropy.coordinates.SkyCoord) – The ICRS coordinates that should be in the centre of the CCD.

validate_arguments(coordinates: SkyCoord)[source]#
class astrafocus.interface.telescope.TrivialTelescope[source]#

Bases: TelescopeInterface

Trivial implementation to set the telescope position for testing purposes.

static set_telescope_position(coordinates: SkyCoord)[source]#

Point the telescope to a specific coordinate in the equatorial coordinate system.

Parameters:

coordinates (~astropy.coordinates.SkyCoord) – The ICRS coordinates that should be in the centre of the CCD.

class astrafocus.interface.simulation.ObservationBasedDeviceSimulator(current_position: int | None = None, allowed_range: tuple[int, int] | None = None, sleep_flag: bool = False, seconds_per_step: float = 0.5, image_data: list | None = None, headers: list[ndarray[tuple[Any, ...], dtype[floating | integer]]] | None = None, focus_pos: ndarray | None = None, fits_path: str | None = None, save_path: str | None = None)[source]#

Bases: AutofocusDeviceSimulator

Sky targeting#

class astrafocus.targeting.ZenithNeighbourhoodQuery(db_path: str, zenith_neighbourhood: ZenithNeighbourhood, maximal_number_of_stars: int | None = 1000000)[source]

Bases: object

Class for querying a database based on a zenith neighbourhood.

Parameters:
  • db_path (str) – Path to the database.

  • zenith_neighbourhood (ZenithNeighbourhood) – Zenith neighbourhood object.

  • maximal_number_of_stars (int, optional) – Maximum number of stars to be considered in the query (default is 1 000 000). This parameter is needed to prevent excessive queries that could lead to memory issues or long processing times.

Examples

zenith_neighbourhood_query = ZenithNeighbourhoodQuery(

db_path=”path_to/database.db”, zenith_neighbourhood=zenith_neighbourhood

)

classmethod create_from_location_and_angle(db_path: str, observatory_location: EarthLocation, maximal_zenith_angle: float | int | Angle, maximal_number_of_stars: int | None = 1000000, observation_time: Time | None = None) ZenithNeighbourhoodQuery[source]

Create an instance of the ZenithNeighbourhoodQuery class with specified parameters.

This class method is an alternative constructor that creates a ZenithNeighbourhoodQuery instance based on the provided observatory location, maximal zenith angle, and optional observation time.

Parameters:
  • db_path (str) – The path to the database.

  • observatory_location (EarthLocation) – Location of the observatory.

  • maximal_zenith_angle (float, int, or Angle) – Maximum zenith angle for the neighbourhood in degrees.

  • maximal_number_of_stars (int, optional) – Maximum number of stars to be considered in the query (default is 1 000 000).

  • observation_time (Optional[Time], optional) – Observation time specified using astropy’s Time. (default is None, resulting to now)

Example

>>> db_path = '/path/to/database'
>>> observatory_location = EarthLocation(lat=30.0, lon=-70.0, height=1000.0)
>>> maximal_zenith_angle = 15.0
>>> observation_time = Time('2023-12-01T12:00:00')
>>> zenith_neighbourhood_query = ZenithNeighbourhoodQuery.create_from_location_and_angle(
...     db_path, observatory_location, maximal_zenith_angle, observation_time
... )
filter_df_by_zenith_angle(df) ZenithNeighbourhoodQueryResult[source]

Filter DataFrame based on zenith angle.

Parameters:

df (pd.DataFrame) – DataFrame to be filtered.

Returns:

Result of the filtered DataFrame.

Return type:

ZenithNeighbourhoodQueryResult

static find_airmass_threshold_crossover(airmass_threshold: float | None = 1.2, airmass_model: Callable = <function plane_parallel_atmosphere>)[source]
classmethod from_telescope_specs(telescope_specs, observation_time=None, maximal_zenith_angle=None, db_path=None) ZenithNeighbourhoodQuery[source]

Create an instance of the ZenithNeighbourhoodQuery class from an instance of the TelescopeSpecs class.

Parameters:
  • telescope_specs (TelescopeSpecs) – An instance of the TelescopeSpecs class.

  • db_path (str, optional) – The path to the database, by default None

Example

>>> telescope_specs = TelescopeSpecs.load_telescope_config(
...     file_path=path_to_config_file
... )
>>> zenith_neighbourhood_query = (
...     ZenithNeighbourhoodQuery.from_telescope_specs(telescope_specs)
... )
query_full(n_sub_div=20, zenith_angle_strict=True, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None) ZenithNeighbourhoodQueryResult[source]

Query the smallest rectangle that covers the whole patch.

Parameters:
  • n_sub_div (int, optional) – Number of subdivisions for approximation (default is 20).

  • zenith_angle_strict (bool, optional) – If True, filter results based on zenith angle (default is True).

  • min_phot_g_mean_mag (float, optional) – The minimum GAIA mean magnitude to query (default is None).

  • max_phot_g_mean_mag (float, optional) – The maximum GAIA mean magnitude to query (default is None).

  • min_j_m (float, optional) – The minimum J-band magnitude to query (default is None).

  • max_j_m (float, optional) – The maximum J-band magnitude to query (default is None).

Returns:

Result of the query.

Return type:

ZenithNeighbourhoodQueryResult

query_shardwise(n_sub_div=20, zenith_angle_strict=True, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None) ZenithNeighbourhoodQueryResult[source]

Query the database shard-wise, only searching each shard as far as needed.

Parameters:
  • n_sub_div (int, optional) – Number of subdivisions for approximation (default is 20).

  • zenith_angle_strict (bool, optional) – If True, filter results based on zenith angle (default is True).

  • min_phot_g_mean_mag (float, optional) – The minimum GAIA mean magnitude to query (default is None).

  • max_phot_g_mean_mag (float, optional) – The maximum GAIA mean magnitude to query (default is None).

  • min_j_m (float, optional) – The minimum J-band magnitude to query (default is None).

  • max_j_m (float, optional) – The maximum J-band magnitude to query (default is None).

Returns:

Result of the query.

Return type:

ZenithNeighbourhoodQueryResult

astrafocus.targeting.find_airmass_threshold_crossover(airmass_threshold: float | None = 1.2, airmass_model: Callable = <function plane_parallel_atmosphere>)[source]

Find the zenith angle cutoff based on an airmass model and a threshold value.

Parameters:
  • airmass_threshold (float, optional) – The threshold value, above which the airmass is considered too high.

  • airmass_model (Callable) – A callable function that takes zenith angles and returns airmass values.

Returns:

The zenith angle cutoff that corresponds to the airmass falling below the threshold.

Return type:

float

Notes

This function assumes that airmass_model increases monotonically as a function of zenith angle.

Examples

>>> float(find_airmass_threshold_crossover(
... airmass_threshold=1.2, airmass_model=plane_parallel_atmosphere
... ))
0.5846852994181003
class astrafocus.targeting.zenith_neighbourhood_query.ZenithNeighbourhoodQuery(db_path: str, zenith_neighbourhood: ZenithNeighbourhood, maximal_number_of_stars: int | None = 1000000)[source]#

Bases: object

Class for querying a database based on a zenith neighbourhood.

Parameters:
  • db_path (str) – Path to the database.

  • zenith_neighbourhood (ZenithNeighbourhood) – Zenith neighbourhood object.

  • maximal_number_of_stars (int, optional) – Maximum number of stars to be considered in the query (default is 1 000 000). This parameter is needed to prevent excessive queries that could lead to memory issues or long processing times.

Examples

zenith_neighbourhood_query = ZenithNeighbourhoodQuery(

db_path=”path_to/database.db”, zenith_neighbourhood=zenith_neighbourhood

)

classmethod create_from_location_and_angle(db_path: str, observatory_location: EarthLocation, maximal_zenith_angle: float | int | Angle, maximal_number_of_stars: int | None = 1000000, observation_time: Time | None = None) ZenithNeighbourhoodQuery[source]#

Create an instance of the ZenithNeighbourhoodQuery class with specified parameters.

This class method is an alternative constructor that creates a ZenithNeighbourhoodQuery instance based on the provided observatory location, maximal zenith angle, and optional observation time.

Parameters:
  • db_path (str) – The path to the database.

  • observatory_location (EarthLocation) – Location of the observatory.

  • maximal_zenith_angle (float, int, or Angle) – Maximum zenith angle for the neighbourhood in degrees.

  • maximal_number_of_stars (int, optional) – Maximum number of stars to be considered in the query (default is 1 000 000).

  • observation_time (Optional[Time], optional) – Observation time specified using astropy’s Time. (default is None, resulting to now)

Example

>>> db_path = '/path/to/database'
>>> observatory_location = EarthLocation(lat=30.0, lon=-70.0, height=1000.0)
>>> maximal_zenith_angle = 15.0
>>> observation_time = Time('2023-12-01T12:00:00')
>>> zenith_neighbourhood_query = ZenithNeighbourhoodQuery.create_from_location_and_angle(
...     db_path, observatory_location, maximal_zenith_angle, observation_time
... )
filter_df_by_zenith_angle(df) ZenithNeighbourhoodQueryResult[source]#

Filter DataFrame based on zenith angle.

Parameters:

df (pd.DataFrame) – DataFrame to be filtered.

Returns:

Result of the filtered DataFrame.

Return type:

ZenithNeighbourhoodQueryResult

static find_airmass_threshold_crossover(airmass_threshold: float | None = 1.2, airmass_model: Callable = <function plane_parallel_atmosphere>)[source]#
classmethod from_telescope_specs(telescope_specs, observation_time=None, maximal_zenith_angle=None, db_path=None) ZenithNeighbourhoodQuery[source]#

Create an instance of the ZenithNeighbourhoodQuery class from an instance of the TelescopeSpecs class.

Parameters:
  • telescope_specs (TelescopeSpecs) – An instance of the TelescopeSpecs class.

  • db_path (str, optional) – The path to the database, by default None

Example

>>> telescope_specs = TelescopeSpecs.load_telescope_config(
...     file_path=path_to_config_file
... )
>>> zenith_neighbourhood_query = (
...     ZenithNeighbourhoodQuery.from_telescope_specs(telescope_specs)
... )
query_full(n_sub_div=20, zenith_angle_strict=True, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None) ZenithNeighbourhoodQueryResult[source]#

Query the smallest rectangle that covers the whole patch.

Parameters:
  • n_sub_div (int, optional) – Number of subdivisions for approximation (default is 20).

  • zenith_angle_strict (bool, optional) – If True, filter results based on zenith angle (default is True).

  • min_phot_g_mean_mag (float, optional) – The minimum GAIA mean magnitude to query (default is None).

  • max_phot_g_mean_mag (float, optional) – The maximum GAIA mean magnitude to query (default is None).

  • min_j_m (float, optional) – The minimum J-band magnitude to query (default is None).

  • max_j_m (float, optional) – The maximum J-band magnitude to query (default is None).

Returns:

Result of the query.

Return type:

ZenithNeighbourhoodQueryResult

query_shardwise(n_sub_div=20, zenith_angle_strict=True, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None) ZenithNeighbourhoodQueryResult[source]#

Query the database shard-wise, only searching each shard as far as needed.

Parameters:
  • n_sub_div (int, optional) – Number of subdivisions for approximation (default is 20).

  • zenith_angle_strict (bool, optional) – If True, filter results based on zenith angle (default is True).

  • min_phot_g_mean_mag (float, optional) – The minimum GAIA mean magnitude to query (default is None).

  • max_phot_g_mean_mag (float, optional) – The maximum GAIA mean magnitude to query (default is None).

  • min_j_m (float, optional) – The minimum J-band magnitude to query (default is None).

  • max_j_m (float, optional) – The maximum J-band magnitude to query (default is None).

Returns:

Result of the query.

Return type:

ZenithNeighbourhoodQueryResult

class astrafocus.targeting.zenith_neighbourhood.ZenithNeighbourhood(observatory_location: EarthLocation, observation_time: Time | None = None, maximal_zenith_angle: Angle | float = <Angle 10. deg>)[source]#

Bases: object

Class representing a zenith neighbourhood for astronomical observations.

Parameters:
  • observatory_location (Optional[EarthLocation], optional) – Location of the observatory specified using astropy’s EarthLocation.

  • observation_time (Optional[Time], optional) – Observation time specified using astropy’s Time.

  • maximal_zenith_angle (float, optional) – Maximum zenith angle for the neighbourhood in degrees (default is DEFAULT_MAXIMAL_ZENITH_ANGLE).

Examples

# Zenith neighbourhood now >>> from astropy.coordinates import EarthLocation >>> import astropy.units as u >>> speculoos_geo_coords = { … “lat”: -24.627222 * u.deg, “lon”: -70.404167 * u.deg, “height”: 2635 * u.m … } >>> zn = ZenithNeighbourhood( … observatory_location=EarthLocation(**speculoos_geo_coords), … maximal_zenith_angle=10 * u.deg … )

# Zenith neighbourhood at a specific time, crossing the 0, 360 deg boundary >>> import astropy >>> from astrafocus.targeting.zenith_neighbourhood import ( … ZenithNeighbourhood, DEFAULT_MAXIMAL_ZENITH_ANGLE … ) >>> zenith_neighbourhood = ZenithNeighbourhood( … maximal_zenith_angle=DEFAULT_MAXIMAL_ZENITH_ANGLE, … observatory_location=EarthLocation(**speculoos_geo_coords), … observation_time=astropy.time.Time(“2023-11-23 00:35:54.5018”) … )

static check_maximal_zenith_angle(maximal_zenith_angle) Angle[source]#
static constant_approximation_ra_bounds(phi_bound_coords)[source]#
delta_phi_of_theta(dec)[source]#
classmethod from_telescope_specs(telescope_specs: TelescopeSpecs, observation_time=None, maximal_zenith_angle=None)[source]#
get_constant_approximation(n_approx=3, n_sub_div=20, south=None, north=None)[source]#

Calculate constant approximation.

get_constant_approximation_shards(n_sub_div=20)[source]#

Calculate constant shard-wise RA bounds.

get_constant_approximation_shards_deg(n_sub_div)[source]#

Calculate constant approximation in degrees.

Parameters:

n_sub_div (int) – Number of subdivisions for approximation.

Returns:

Constant declinations and corresponding RA bounds in degrees.

Return type:

Tuple[np.ndarray, np.ndarray]

get_ra_bounds(n: int, south: float | None = None, north: float | None = None) tuple[ndarray, ndarray][source]#

Calculate exact RA bounds between specified declinations.

Parameters:
  • n (int) – Number of subdivisions.

  • south (Optional[float], optional) – Southern declination in radians (default is None).

  • north (Optional[float], optional) – Northern declination in radians (default is None).

Returns:

Declinations and corresponding RA bounds.

Return type:

Tuple[np.ndarray, np.ndarray]

validate_observation_time()[source]#
validate_observatory_location()[source]#
class astrafocus.targeting.zenith_neighbourhood_query_result.ZenithNeighbourhoodQueryResult(*args, **kwargs)[source]#

Bases: DataFrame

Class representing the result of a zenith neighbourhood query.

Examples

>>> from astrafocus.targeting.zenith_neighbourhood import ZenithNeighbourhood
>>> from astrafocus.targeting.zenith_neighbourhood_query import ZenithNeighbourhoodQuery
>>> speculoos_geo_coords = {
...     "lat": -24.627222 * u.deg, "lon": -70.404167 * u.deg, "height": 2635 * u.m
... }
>>> zn = ZenithNeighbourhood(
...     observatory_location=EarthLocation(**speculoos_geo_coords),
...     maximal_zenith_angle=10 * u.deg,
... )
>>> zenith_neighbourhood_query = ZenithNeighbourhoodQuery(
...     db_path="path_to/database.db", zenith_neighbourhood=zn
... )
>>> df = zenith_neighbourhood_query.query_shardwise(n_sub_div=20)
add_cartesian_coordinates()[source]#

Add Cartesian coordinates to the DataFrame.

add_zenith_angle_and_cartesian_coordinates(zenith_neighbourhood)[source]#

Add zenith angle and Cartesian coordinates to the DataFrame.

Parameters:

zenith_neighbourhood (ZenithNeighbourhood) – Zenith neighbourhood object.

add_zenith_angle_fast(zenith_neighbourhood)[source]#
angular_distance(ra, deg)[source]#
determine_stars_in_neighbourhood(height=0.19444443333333333, width=0.19444444333333333)[source]#

Determine the number of stars in the neighbourhood of all stars in the DataFrame.

determine_stars_in_neighbourhood_of_star(ind, height=0.19444443333333333, width=0.19444444333333333)[source]#

Determine the number of stars in the neighbourhood of a single stars.

static find_bounds_star(phi_c, theta_c, height=0.19444443333333333, width=0.19444444333333333)[source]#
find_stars_in_neighbourhood(ind, height=0.19444443333333333, width=0.19444444333333333)[source]#

Find all stars in the neighbourhood of a specific stars.

get_sky_coord_of_select_star(ind)[source]#
static inverse_ra_dec(delta_tau, theta)[source]#
mask_by_magnitude(g_mag_range=(6, 12), j_mag_range=(6, 12))[source]#

Mask the DataFrame based on magnitude ranges.

Parameters:
  • g_mag_range (Tuple[float, float], optional) – Range for the G magnitude (default is G_MAG_RANGE).

  • j_mag_range (Tuple[float, float], optional) – Range for the J magnitude (default is J_MAG_RANGE).

Returns:

Masked result based on magnitude ranges.

Return type:

ZenithNeighbourhoodQueryResult

static spherical_to_cartesian(ra, dec)[source]#

Convert spherical coordinates to Cartesian coordinates.

Astronomical Airmass Calculation Module

This module provides functions for calculating astronomical airmass and related parameters.

Functions#

find_airmass_threshold_crossover(airmass_threshold=1.2, airmass_model)

Find the zenith angle cutoff based on an airmass model and a threshold value.

zenith_angle(altitude)

Calculate the zenith angle from the given altitude.

plane_parallel_atmosphere(zenith_angle)

Calculate airmass in a plane-parallel atmosphere.

pickering_interpolative(zenith_angle)

Calculate airmass using the Pickering interpolative formula.

rosenberg_interpolative(zenith_angle)

Calculate airmass using the Rosenberg interpolative formula.

Example Usage#

>>> from astrafocus.targeting import airmass_models
>>> cutoff = airmass_models.find_airmass_threshold_crossover(
...     airmass_threshold=1.2, airmass_model=airmass_models.plane_parallel_atmosphere
... )
>>> zenith = airmass_models.zenith_angle(0.5)
>>> airmass_pickering = airmass_models.pickering_interpolative(zenith)
>>> airmass_rosenberg = airmass_models.rosenberg_interpolative(zenith)

Sources#

https://en.wikipedia.org/wiki/Air_mass_(astronomy)

astrafocus.targeting.airmass_models.find_airmass_threshold_crossover(airmass_threshold: float | None = 1.2, airmass_model: Callable = <function plane_parallel_atmosphere>)[source]#

Find the zenith angle cutoff based on an airmass model and a threshold value.

Parameters:
  • airmass_threshold (float, optional) – The threshold value, above which the airmass is considered too high.

  • airmass_model (Callable) – A callable function that takes zenith angles and returns airmass values.

Returns:

The zenith angle cutoff that corresponds to the airmass falling below the threshold.

Return type:

float

Notes

This function assumes that airmass_model increases monotonically as a function of zenith angle.

Examples

>>> float(find_airmass_threshold_crossover(
... airmass_threshold=1.2, airmass_model=plane_parallel_atmosphere
... ))
0.5846852994181003
astrafocus.targeting.airmass_models.pickering_interpolative(zenith_angle)[source]#

Calculate the airmass using the Pickering interpolative formula.

Parameters:

zenith_angle (float) – Zenith angle in radians.

Returns:

The airmass value calculated using the Pickering formula.

Return type:

float

Examples

>>> float(pickering_interpolative(0))
1.000000196171337
astrafocus.targeting.airmass_models.plane_parallel_atmosphere(zenith_angle)[source]#

Calculate the airmass in a plane-parallel atmosphere model.

Parameters:

zenith_angle (float) – Zenith angle in radians.

Returns:

The airmass value for the given zenith angle.

Return type:

float

Examples

>>> float(plane_parallel_atmosphere(0))
1.0
astrafocus.targeting.airmass_models.rosenberg_interpolative(zenith_angle)[source]#

Calculate the airmass using the Rosenberg interpolative formula.

Parameters:

zenith_angle (float) – Zenith angle in radians.

Returns:

The airmass value calculated using the Rosenberg formula.

Return type:

float

Examples

>>> float(rosenberg_interpolative(0))
0.9999995824576546
astrafocus.targeting.airmass_models.zenith_angle(altitude)[source]#

Calculate the zenith angle from the given altitude.

Parameters:

altitude (float) – Altitude angle in radians.

Returns:

Zenith angle in radians.

Return type:

float

Examples

>>> zenith_angle(0) * 180/np.pi
90.0
class astrafocus.targeting.celestial_bounds_calculator.CelestialBoundsCalculatorInterface[source]#

Bases: ABC

static crosses_equator(dec_bounds)[source]#
get_bounds(pole_tolerance=1)[source]#
get_pole_dec_bounds(dec_bounds, pole_tolerance)[source]#

Create an SQLite-style query based on Declination bounds.

get_query(pole_tolerance=1)[source]#
get_ra_bounds(ra_bounds, dec_bounds)[source]#

Create an SQLite-style query based on Right Ascension bounds.

abstractmethod get_ra_dec_bounds_at_edges() tuple[source]#
static get_ra_query(ra_bounds)[source]#
static is_near_poles(dec_bounds, pole_tolerance)[source]#
abstractmethod ra_bounds_at_equator() ndarray[source]#
class astrafocus.targeting.celestial_bounds_calculator.CelestialBoundsCalculatorWCS(wcs)[source]#

Bases: CelestialBoundsCalculatorInterface

cbc = CelestialBoundsCalculatorWCS(wcs) cbc.get_query()

CelestialBoundsCalculatorWCS(wcs).get_query()

get_ra_dec_bounds_at_edges()[source]#
ra_bounds_at_equator()[source]#
class astrafocus.targeting.tangential_plane_projector.TangentialPlaneProjector(df, wcs, mask_first=False)[source]#

Bases: object

Examples

central_star = central_star centre = SkyCoord(ra=central_star.ra, dec=central_star.dec, unit=”deg”, frame=”icrs”)

centre = SkyCoord(ra=0, dec=0, unit=”deg”, frame=”icrs”) wcs = create_basic_wcs(center_ra=0, center_dec=-45, pixel_scale_arcsec=0.35) tangential_plane_projector = TangentialPlaneProjector(znqr, wcs) tangential_plane_projector.project(centre)

static create_basic_wcs(center_ra: float = 0.0, center_dec: float = 0.0, pixel_scale_arcsec: float = 0.35, pixel_shape: tuple[int, int] = (2000, 2000))[source]#

Create a basic WCS object.

Parameters:
  • center_ra (float) – The right ascension of the center of the field of view in degrees.

  • center_dec (float) – The declination of the center of the field of view in degrees.

  • pixel_scale_arcsec (float) – The pixel scale in arcseconds.

  • pixel_shape (tuple of int) – The shape of the ccd in pixels, i.e. (num_pixels_x_axis, num_pixels_y_axis). This is the equivalent to flip(image_shape). See WCS documentation for more information.

create_ra_mask(ra_bounds, dec_bounds)[source]#

Create a mask based on Right Ascension bounds.

static crosses_equator(dec_bounds)[source]#
filter_coordinates_on_ccd(pixel_coords: ndarray) ndarray[source]#
flatten_on_sky(central_star: SkyCoord)[source]#
classmethod from_telescope_specs(df, TelescopeSpecs, center_ra=None, center_dec=None, **kwargs)[source]#
get_mask(pole_tolerance=1)[source]#
get_ra_dec_bounds()[source]#
static is_near_poles(dec_bounds, pole_tolerance)[source]#
num_stars_in_mask(centre)[source]#
num_stars_on_ccd(centre: SkyCoord)[source]#
project(centre: SkyCoord)[source]#
project_on_ccd(centre: SkyCoord)[source]#
ra_bound_at_equator()[source]#

Models#

class astrafocus.models.EllipticalMoffat2D(amplitude=1, background=0, x_0=0, y_0=0, sigma_x=1, sigma_y=1, alpha=1, theta=0.0, **kwargs)[source]

Bases: Fittable2DModel

Two dimensional Moffat model.

Parameters:
  • amplitude (float) – Amplitude of the model.

  • x_0 (float) – x position of the maximum of the Moffat model.

  • y_0 (float) – y position of the maximum of the Moffat model.

  • sigma_x (float) – Core width of the Moffat model.

  • sigma_y (float) – Core height of the Moffat model.

  • alpha (float) – Power index of the Moffat model.

See also

Gaussian2D, Box2D

Notes

Model formula:

\[f(x, y) = B + A \left(1 + \mathrm{amplitude} \cdot \frac{\left(x - x_{0}\right)^{2} + \left(y - y_{0}\right)^{2}}{\gamma^{2}}\right)^{- \alpha}\]

Examples

>>> import numpy as np
>>> from astropy.modeling import fitting
>>> from astrafocus.models.elliptical_moffat_2D import EllipticalMoffat2D
>>> y, x = np.indices((21, 21))
>>> star_data = EllipticalMoffat2D(amplitude=1000, x_0=10.0, y_0=10.0)(x, y)
>>> model = EllipticalMoffat2D(amplitude=np.max(star_data), x_0=10.0, y_0=10.0)
>>> fitter = fitting.LevMarLSQFitter()
>>> fit = fitter(model, x, y, star_data, estimate_jacobian=True)
>>> bool(abs(fit.x_0 - 10) < 0.1)
True
alpha = Parameter('alpha', value=1.0)
amplitude = Parameter('amplitude', value=1.0)
background = Parameter('background', value=0.0)
static evaluate(x, y, amplitude, background, x_0, y_0, sigma_x, sigma_y, alpha, theta)[source]

Two dimensional Moffat model function.

static fit_deriv(x, y, amplitude, background, x_0, y_0, sigma_x, sigma_y, alpha, theta)[source]

Two dimensional Moffat model derivative with respect to parameters.

property fwhm_x

Moffat full width at half maximum. Derivation of the formula is available in this notebook by Yoonsoo Bach.

property input_units

This property is used to indicate what units or sets of units the evaluate method expects, and returns a dictionary mapping inputs to units (or None if any units are accepted).

Model sub-classes can also use function annotations in evaluate to indicate valid input units, in which case this property should not be overridden since it will return the input units based on the annotations.

param_names = ('amplitude', 'background', 'x_0', 'y_0', 'sigma_x', 'sigma_y', 'alpha', 'theta')

Names of the parameters that describe models of this type.

The parameters in this tuple are in the same order they should be passed in when initializing a model of a specific type. Some types of models, such as polynomial models, have a different number of parameters depending on some other property of the model, such as the degree.

When defining a custom model class the value of this attribute is automatically set by the ~astropy.modeling.Parameter attributes defined in the class body.

sigma_x = Parameter('sigma_x', value=1.0)
sigma_y = Parameter('sigma_y', value=1.0)
theta = Parameter('theta', value=0.0)
x_0 = Parameter('x_0', value=0.0)
y_0 = Parameter('y_0', value=0.0)
class astrafocus.models.HalfFluxRadius2D(amplitude=1, x_0=0, y_0=0, R_0=1, scale_factor=1, **kwargs)[source]

Bases: Disk2D

Examples

>>> hfr_2D = HalfFluxRadius2D()
>>> star_data=np.random.uniform(size=(20, 20))
>>> x, y = np.indices(star_data.shape)
>>> _ = hfr_2D.fit(star_data, x, y, scale_factor=5)
fit(x, y, star_data, *args, **kwargs)[source]

Source https://en.wikipedia.org/wiki/Half_flux_diameter#cite_note-4 https://www.lost-infinity.com/the-half-flux-diameter-hfd-for-a-perfectly-normal-distributed-star/

static optimize_hfr(high_res_image, distances, hfr)[source]
class astrafocus.models.elliptical_moffat_2D.EllipticalMoffat2D(amplitude=1, background=0, x_0=0, y_0=0, sigma_x=1, sigma_y=1, alpha=1, theta=0.0, **kwargs)[source]#

Bases: Fittable2DModel

Two dimensional Moffat model.

Parameters:
  • amplitude (float) – Amplitude of the model.

  • x_0 (float) – x position of the maximum of the Moffat model.

  • y_0 (float) – y position of the maximum of the Moffat model.

  • sigma_x (float) – Core width of the Moffat model.

  • sigma_y (float) – Core height of the Moffat model.

  • alpha (float) – Power index of the Moffat model.

See also

Gaussian2D, Box2D

Notes

Model formula:

\[f(x, y) = B + A \left(1 + \mathrm{amplitude} \cdot \frac{\left(x - x_{0}\right)^{2} + \left(y - y_{0}\right)^{2}}{\gamma^{2}}\right)^{- \alpha}\]

Examples

>>> import numpy as np
>>> from astropy.modeling import fitting
>>> from astrafocus.models.elliptical_moffat_2D import EllipticalMoffat2D
>>> y, x = np.indices((21, 21))
>>> star_data = EllipticalMoffat2D(amplitude=1000, x_0=10.0, y_0=10.0)(x, y)
>>> model = EllipticalMoffat2D(amplitude=np.max(star_data), x_0=10.0, y_0=10.0)
>>> fitter = fitting.LevMarLSQFitter()
>>> fit = fitter(model, x, y, star_data, estimate_jacobian=True)
>>> bool(abs(fit.x_0 - 10) < 0.1)
True
alpha = Parameter('alpha', value=1.0)#
amplitude = Parameter('amplitude', value=1.0)#
background = Parameter('background', value=0.0)#
static evaluate(x, y, amplitude, background, x_0, y_0, sigma_x, sigma_y, alpha, theta)[source]#

Two dimensional Moffat model function.

static fit_deriv(x, y, amplitude, background, x_0, y_0, sigma_x, sigma_y, alpha, theta)[source]#

Two dimensional Moffat model derivative with respect to parameters.

property fwhm_x#

Moffat full width at half maximum. Derivation of the formula is available in this notebook by Yoonsoo Bach.

property input_units#

This property is used to indicate what units or sets of units the evaluate method expects, and returns a dictionary mapping inputs to units (or None if any units are accepted).

Model sub-classes can also use function annotations in evaluate to indicate valid input units, in which case this property should not be overridden since it will return the input units based on the annotations.

param_names = ('amplitude', 'background', 'x_0', 'y_0', 'sigma_x', 'sigma_y', 'alpha', 'theta')#

Names of the parameters that describe models of this type.

The parameters in this tuple are in the same order they should be passed in when initializing a model of a specific type. Some types of models, such as polynomial models, have a different number of parameters depending on some other property of the model, such as the degree.

When defining a custom model class the value of this attribute is automatically set by the ~astropy.modeling.Parameter attributes defined in the class body.

sigma_x = Parameter('sigma_x', value=1.0)#
sigma_y = Parameter('sigma_y', value=1.0)#
theta = Parameter('theta', value=0.0)#
x_0 = Parameter('x_0', value=0.0)#
y_0 = Parameter('y_0', value=0.0)#
astrafocus.models.elliptical_moffat_2D.elliptical_moffat_model(x, y)[source]#
class astrafocus.models.half_flux_radius_2D.HalfFluxRadius2D(amplitude=1, x_0=0, y_0=0, R_0=1, scale_factor=1, **kwargs)[source]#

Bases: Disk2D

Examples

>>> hfr_2D = HalfFluxRadius2D()
>>> star_data=np.random.uniform(size=(20, 20))
>>> x, y = np.indices(star_data.shape)
>>> _ = hfr_2D.fit(star_data, x, y, scale_factor=5)
fit(x, y, star_data, *args, **kwargs)[source]#

Source https://en.wikipedia.org/wiki/Half_flux_diameter#cite_note-4 https://www.lost-infinity.com/the-half-flux-diameter-hfd-for-a-perfectly-normal-distributed-star/

static optimize_hfr(high_res_image, distances, hfr)[source]#

Database (SQL)#

class astrafocus.sql.LocalGaiaDatabaseQuery(db_path)[source]

Bases: object

Perform queries on the Gaia-2MASS Local Catalogue.

Parameters:

db_path (str) – The path to the SQLite database file.

Examples

>>> from astrafocus.sql.local_gaia_database_query import LocalGaiaDatabaseQuery
>>> lgdbq = LocalGaiaDatabaseQuery("path/to/db")
>>> lgdbq.count_query(10, 20, 30, 40, max_phot_g_mean_mag=12)
count_query(min_dec: float, max_dec: float, min_ra: float, max_ra: float, *, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None)[source]

Counts the number of entries in the local Gaia database within a specified range of declination and right ascension.

Parameters:
  • min_dec (float) – The minimum declination value to query.

  • max_dec (float) – The maximum declination value to query.

  • min_ra (float) – The minimum right ascension value to query.

  • max_ra (float) – The maximum right ascension value to query.

  • min_phot_g_mean_mag (float, optional) – The minimum GAIA mean magnitude to filter results (default is None).

  • max_phot_g_mean_mag (float, optional) – The maximum GAIA mean magnitude to filter results (default is None).

  • min_j_m (float, optional) – The minimum J-band magnitude to filter results (default is None).

  • max_j_m (float, optional) – The maximum J-band magnitude to filter results (default is None).

Returns:

The count of entries matching the query criteria.

Return type:

int

Raises:
  • TypeError – If any of the input values is not of type float or int.

  • ValueError – If any of the input values is not within the specified range, or if the order of range borders is incorrect.

query(min_dec: float, max_dec: float, min_ra: float, max_ra: float, *, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None)[source]

Queries the local Gaia database for astronomical data within a specified range of declination and right ascension. If min_ra < max_ra, the right ascension range is assumed to cross the 0/360 degree border.

Parameters:
  • min_dec (float) – The minimum declination value to query.

  • max_dec (float) – The maximum declination value to query.

  • min_ra (float) – The minimum right ascension value to query.

  • max_ra (float) – The maximum right ascension value to query.

  • min_phot_g_mean_mag (float, optional) – The minimum GAIA mean magnitude to query (default is None).

  • max_phot_g_mean_mag (float, optional) – The maximum GAIA mean magnitude to query (default is None).

  • min_j_m (float, optional) – The minimum J-band magnitude to query (default is None).

  • max_j_m (float, optional) – The maximum J-band magnitude to query (default is None).

Returns:

A pandas DataFrame containing the queried astronomical data.

Return type:

pd.DataFrame

Raises:
  • TypeError – If any of the input values is not of type float or int.

  • ValueError – If any of the input values is not within the specified range, or if the order of range borders is incorrect.]

Examples

>>> from astrafocus.sql.local_gaia_database_query import LocalGaiaDatabaseQuery
>>> lgdbq = LocalGaiaDatabaseQuery("path/to/db")
>>> lgdbq.count_query(10, 20, 30, 40, max_phot_g_mean_mag=12)
class astrafocus.sql.ShardwiseQuery(db_path)[source]

Bases: LocalGaiaDatabaseQuery

count_query_with_shard_array(dec_arr, ra_arr, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None)[source]

Count the number of stars in the Gaia-2MASS Local Catalogue using shardwise queries.

query_with_shard_array(dec_arr, ra_arr, min_phot_g_mean_mag: float | None = None, max_phot_g_mean_mag: float | None = None, min_j_m: float | None = None, max_j_m: float | None = None)[source]

Perform shardwise queries on the Gaia-2MASS Local Catalogue.