Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added near meridian constraint #589

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
49 changes: 48 additions & 1 deletion astroplan/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"LocalTimeConstraint", "PrimaryEclipseConstraint",
"SecondaryEclipseConstraint", "Constraint", "TimeConstraint",
"observability_table", "months_observable", "max_best_rescale",
"min_best_rescale", "PhaseConstraint", "is_event_observable"]
"min_best_rescale", "PhaseConstraint", "is_event_observable",
"MeridianSeparationConstraint"]

_current_year = time.localtime().tm_year # needed for backward compatibility
_current_year_time_range = Time( # needed for backward compatibility
Expand Down Expand Up @@ -938,6 +939,52 @@ def compute_constraint(self, times, observer=None, targets=None):
return mask


class MeridianSeparationConstraint(Constraint):
"""
Constraint on angular separation from the meridian.
"""
def __init__(self, min=None, max=None, boolean_constraint=True):
"""
Parameters
----------
min : `~astropy.units.Quantity` or `None`, optional
Minimum acceptable angular distance from the meridian.
`None` indicates no lower limit.

max : `~astropy.units.Quantity` or `None`, optional
Maximum acceptable angular distance from the meridian.
`None` indicates no upper limit.

boolean_constraint : bool

Examples
--------
Constrain observations to targets that are between 3 and 35 degrees
away from the meridian.
>>> import astropy.units as u
>>> constraint = MeridianSeparationConstraint(min=3*u.deg, max=35*u.deg)

This can be useful for observations using German-Equatorial Mounts, to avoid
flipping the side of the pier during exposures.
"""
self.min = min if min is not None else 0*u.deg
self.max = max if max is not None else 180*u.deg
self.boolean_constraint = boolean_constraint

def compute_constraint(self, times, observer, targets):
lst = observer.local_sidereal_time(times)
meridian = SkyCoord(ra=lst, dec=targets.dec)

meridian_separation = meridian.separation(targets)

if self.boolean_constraint:
mask = (self.min < meridian_separation) & (meridian_separation < self.max)
return mask
else:
rescale = min_best_rescale(meridian_separation, self.min, self.max, less_than_min=0)
return rescale


def is_always_observable(constraints, observer, targets, times=None,
time_range=None, time_grid_resolution=0.5*u.hour):
"""
Expand Down
17 changes: 15 additions & 2 deletions astroplan/tests/test_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np
import astropy.units as u
from astropy.time import Time
from astropy.coordinates import Galactic, SkyCoord, get_sun, get_body
from astropy.coordinates import Galactic, SkyCoord, EarthLocation, get_sun, get_body
from astropy.utils import minversion
import pytest

Expand All @@ -20,7 +20,7 @@
TimeConstraint, LocalTimeConstraint, months_observable,
max_best_rescale, min_best_rescale, PhaseConstraint,
PrimaryEclipseConstraint, SecondaryEclipseConstraint,
is_event_observable)
is_event_observable, MeridianSeparationConstraint)
from ..periodic import EclipsingSystem
from ..exceptions import MissingConstraintWarning

Expand Down Expand Up @@ -200,6 +200,18 @@ def test_sun_separation():
assert np.all(is_constraint_met == [False, True, True])


def test_meridian_separation():
time_range = Time(["2024-10-08 21:00", "2024-10-08 23:00"])
target = FixedTarget(coord=SkyCoord(ra=19.75*u.hour, dec=-22.50*u.deg), name="name")

# Pico dos Dias Observatory (Brazil)
opd = Observer(location=EarthLocation(lat=-22.53, lon=-45.58, height=1864))
constraint = MeridianSeparationConstraint(min=3*u.deg, max=35*u.deg)

results = constraint(opd, target, times=time_grid_from_range(time_range))
assert np.all(results == [True, False, True, True, True])


def test_moon_separation():
time = Time('2003-04-05 06:07:08')
apo = Observer.at_site("APO")
Expand Down Expand Up @@ -419,6 +431,7 @@ def test_rescale_minmax():
AtNightConstraint(),
SunSeparationConstraint(min=90*u.deg),
MoonSeparationConstraint(min=20*u.deg),
MeridianSeparationConstraint(min=3*u.deg),
LocalTimeConstraint(min=dt.time(23, 50), max=dt.time(4, 8)),
TimeConstraint(*Time(["2015-08-28 03:30", "2015-09-05 10:30"]))
]
Expand Down
Loading