Observation Plans Module
- seestarpy.plan.create_mosaic_plan(plan_name, center_ra, center_dec, width, height, delta_ra, delta_dec, t_total, start_min, lp_filter=False, target_name_prefix='Mosaic')
Create an observation plan that tiles a rectangular sky region.
The Seestar S50 FOV is roughly 0.75 x 1.33 degrees — too small for many extended objects. This function generates a plan dictionary with individual pointings arranged in a grid, ready to be sent with
set_view_plan(). No network I/O is performed.Panels are traversed in boustrophedon (snake) order: even Dec rows go left-to-right in RA, odd rows go right-to-left. This minimises slew distance between consecutive panels.
- Parameters:
plan_name (str) – Human-readable name for the plan.
center_ra (float) – Right ascension of the mosaic centre in decimal hours [0, 24).
center_dec (float) – Declination of the mosaic centre in decimal degrees [-90, 90].
width (float) – Angular extent in RA on the sky, in degrees. Use 0 for a single column of panels.
height (float) – Angular extent in Dec, in degrees. Use 0 for a single row.
delta_ra (float) – Panel spacing in the RA direction, in degrees (> 0).
delta_dec (float) – Panel spacing in the Dec direction, in degrees (> 0).
t_total (float) – Total observation time in minutes, divided equally among panels.
start_min (int) – Start time as minutes since local midnight (same convention as
set_view_plan()).lp_filter (bool, optional) – Enable the light-pollution filter for every panel (default False).
target_name_prefix (str, optional) – Prefix for panel names (default
"Mosaic"). Panels are named"{prefix}_01","{prefix}_02", etc.
- Returns:
A plan dictionary suitable for
set_view_plan().- Return type:
dict
- Raises:
ValueError – If any parameter is out of range.
Examples
>>> from seestarpy import plan >>> mosaic = plan.create_mosaic_plan( ... plan_name="NGC 7000 Mosaic", ... center_ra=20.99, # ~20h 59m ... center_dec=44.53, ... width=3.0, ... height=2.0, ... delta_ra=1.0, ... delta_dec=1.0, ... t_total=180, ... start_min=1320, # 22:00 ... ) >>> len(mosaic["list"]) 6 >>> plan.set_view_plan(mosaic)
- seestarpy.plan.create_named_plan(plan_name, targets, end_time, lp_filter=False)
Create an observation plan from a list of targets.
Targets can be specified by name (resolved automatically via the CDS Sesame service — SIMBAD/NED/VizieR) or by explicit RA/Dec coordinates. Each target observes from its start time until the next target begins; the last target observes until end_time.
- Parameters:
plan_name (str) – Human-readable name for the plan.
targets (list of tuple) –
Each element is
(target, start_time)or(target, start_time, lp_filter), where:target — either a string (object designation such as
"M42","NGC 884","HD 126675", resolved via Sesame) or a (ra_hours, dec_deg) tuple/list giving explicit coordinates.start_time (str) — local start time as
"hh:mm"(24-hour format).lp_filter (bool, optional) — per-target light-pollution filter override. If omitted, the function-level lp_filter default is used.
Times that are earlier than the previous target’s start time are assumed to be after midnight (next calendar day).
end_time (str) – When the last target should stop observing, as
"hh:mm". If earlier than the last target’s start, it is treated as post-midnight.lp_filter (bool, optional) – Default light-pollution filter setting for targets that don’t specify one (default
False).
- Returns:
A plan dictionary suitable for
set_view_plan().- Return type:
dict
- Raises:
ValueError – If targets is empty or times are malformed.
LookupError – If a target name string cannot be resolved.
ConnectionError – If the CDS Sesame service is unreachable.
Examples
>>> from seestarpy import plan >>> p = plan.create_named_plan( ... plan_name="Evening Session", ... targets=[ ... ("M42", "21:00"), # resolved via Sesame ... ("NGC 884", "22:30", True), # with LP filter ... ((0.712, 41.27), "23:45"), # explicit RA/Dec ... ], ... end_time="01:00", ... ) >>> plan.set_view_plan(p)
- seestarpy.plan.create_polygon_plan(plan_name, corners, delta_ra, delta_dec, t_total, start_min, lp_filter=False, target_name_prefix='Poly')
Create an observation plan filling an arbitrary polygon on the sky.
Unlike
create_mosaic_plan()which tiles an axis-aligned rectangle, this function accepts 3 or more RA/Dec corner pairs defining a simple (non-self-intersecting) polygon that may be non-rectangular or tilted relative to the equatorial grid. A gnomonic tangent-plane projection is used internally, so cos(dec) correction and RA wraparound near 0 h/24 h are handled automatically.Panels are traversed in boustrophedon (snake) order within the tangent-plane grid. No network I/O is performed.
- Parameters:
plan_name (str) – Human-readable name for the plan.
corners (list of (float, float)) – Three or more
(ra_hours, dec_deg)tuples defining the polygon boundary, ordered either clockwise or counter-clockwise. Edges must not cross each other (simple polygon).delta_ra (float) – Grid spacing in the RA (xi) direction of the tangent plane, in degrees (> 0).
delta_dec (float) – Grid spacing in the Dec (eta) direction of the tangent plane, in degrees (> 0).
t_total (float) – Total observation time in minutes, divided equally among panels.
start_min (int) – Start time as minutes since local midnight (same convention as
set_view_plan()).lp_filter (bool, optional) – Enable the light-pollution filter for every panel (default False).
target_name_prefix (str, optional) – Prefix for panel names (default
"Poly").
- Returns:
A plan dictionary suitable for
set_view_plan().- Return type:
dict
- Raises:
ValueError – If
cornershas fewer than 3 elements, spacings are not positive, or the polygon is degenerate (zero area).
Examples
>>> from seestarpy import plan >>> # Quadrilateral >>> quad = plan.create_polygon_plan( ... plan_name="Veil Nebula Quad", ... corners=[ ... (20.75, 30.0), ... (20.95, 30.0), ... (20.95, 32.0), ... (20.75, 32.0), ... ], ... delta_ra=0.5, ... delta_dec=0.5, ... t_total=120, ... start_min=1320, ... ) >>> # Pentagon >>> penta = plan.create_polygon_plan( ... plan_name="Five-sided region", ... corners=[ ... (12.0, 0.0), (12.1, -0.5), (12.2, 0.0), ... (12.15, 0.5), (12.05, 0.5), ... ], ... delta_ra=0.3, ... delta_dec=0.3, ... t_total=90, ... start_min=1320, ... ) >>> plan.set_view_plan(quad)
- seestarpy.plan.create_quadrilateral_plan(plan_name, corners, delta_ra, delta_dec, t_total, start_min, lp_filter=False, target_name_prefix='Poly')
Alias — the original name before generalisation to arbitrary polygons.
- seestarpy.plan.get_running_plan()
Return the currently running observation plan, or
Noneif no plan is active.This queries
iscope_get_app_stateand extracts theViewPlankey, which is how the official Seestar app checks plan status.Note
Confirmed via traffic capture from the official Seestar app v3.0.2 on 2026-02-24.
- Returns:
The
ViewPlanstate dict when a plan is loaded (even if stopped/cancelled), orNoneif no plan data is present. The dict includesstate("working","cancel", etc.) andplanwith the full plan payload including per-targetstate,lapse_ms, andskipfields added by the firmware.- Return type:
dict or None
Examples
>>> from seestarpy import plan >>> vp = plan.get_running_plan() >>> if vp and vp["state"] == "working": ... print(f"Running: {vp['plan']['plan_name']}")
- seestarpy.plan.plot_mosaic_plan(plan, fov_width=0.75, fov_height=1.33, ax=None)
Plot panel borders of a mosaic plan on a Mollweide projection.
The view is zoomed to 150 % of the area covered by the panel footprints, with Mollweide grid lines (RA meridians and Dec parallels) drawn for reference. Each panel is drawn as a closed rectangle in RA/Dec space, assuming an equatorial mount (panels axis-aligned).
Because matplotlib’s built-in Mollweide axes do not support zooming, coordinates are projected manually and drawn on a standard rectilinear axes.
- Parameters:
plan (dict) – A plan dictionary (from
create_mosaic_plan()or hand-built) containing"plan_name"and"list"with target dicts that each have"target_ra_dec"and"target_name".fov_width (float, optional) – Field-of-view width in degrees (RA direction on the sky). Default is 0.75 (Seestar S50).
fov_height (float, optional) – Field-of-view height in degrees (Dec direction). Default is 1.33 (Seestar S50).
ax (matplotlib.axes.Axes or None, optional) – A rectilinear axes to plot on. If
None, a new figure and axes are created.
- Returns:
The axes with panel outlines, labels, and grid lines plotted.
- Return type:
matplotlib.axes.Axes
Examples
>>> from seestarpy import plan >>> mosaic = plan.create_mosaic_plan( ... "NGC 7000", 20.99, 44.53, 3.0, 2.0, 1.0, 1.0, 180, 1320, ... ) >>> ax = plan.plot_mosaic_plan(mosaic)
- seestarpy.plan.resolve_name(name)
Resolve an astronomical object name to RA/Dec using the CDS Sesame name resolver.
Sesame queries SIMBAD, NED, and VizieR in sequence, so most common designations work (Messier, NGC, IC, HD, HIP, Bayer, etc.).
- Parameters:
name (str) – Object designation, e.g.
"M42","NGC 884","HD 126675".- Returns:
(ra_hours, dec_deg)in the same convention used byset_view_plan()— RA as decimal hours [0, 24), Dec as decimal degrees [-90, 90].- Return type:
(float, float)
- Raises:
LookupError – If the name cannot be resolved (not found in any catalogue).
ConnectionError – If the Sesame service is unreachable.
Examples
>>> from seestarpy.plan import resolve_name >>> ra, dec = resolve_name("M42") >>> print(f"RA={ra:.4f}h Dec={dec:.2f}°") RA=5.5881h Dec=-5.39°
- seestarpy.plan.set_view_plan(plan)
Send an observation plan to the Seestar and start executing it.
- Parameters:
plan (dict) –
A plan dictionary with keys
plan_name(str),update_time_seestar(str, format"yyyy.MM.dd"), andlist(list of target dicts). Each target dict has:target_id(int): 9-digit unique identifier.target_name(str): Display name (e.g."M 31").alias_name(str): Alias or empty string"".target_ra_dec(list[float, float]):[RA_hours, Dec_degrees].lp_filter(bool): Enable the light-pollution filter for this target.start_min(int): Start time as minutes since local midnight.duration_min(int): Observation duration in minutes.
- Return type:
dict
Notes
Note
Payload format confirmed via traffic capture from the official Seestar app v3.0.2 on 2026-02-24.
RA convention:
target_ra_decuses the same convention asiscope_start_view()— RA in decimal hour-angle [0, 24) and Dec in decimal degrees [-90, 90]. The decompiledSetViewPlanCmd.javadoes not divide RA by 15 because the app already stores RA in hour-angle.start_min: This is not a relative offset from when the plan starts. It is the absolute minutes since local midnight, using the Seestar’s internal clock (which is synced to the phone/host local time via
pi_set_time()). For example, 22:30 =1350, 23:00 =1380. For targets after midnight, use values above 1440 (e.g. 01:30 AM =1530).duration_min: Observation duration in minutes. The official app displays plans on a chart with 10-minute resolution, but the firmware receives the raw integer – sub-10-minute durations are valid at the protocol level.
Examples
>>> from seestarpy import plan >>> # M42 at 22:30 for 30 min, then M31 at 23:00 for 45 min >>> my_plan = { ... "plan_name": "Evening Session", ... "update_time_seestar": "2026.02.23", ... "list": [ ... { ... "target_id": 123456789, ... "target_name": "M42", ... "alias_name": "Orion Nebula", ... "target_ra_dec": [5.588, -5.39], ... "lp_filter": True, ... "start_min": 1350, ... "duration_min": 30, ... }, ... { ... "target_id": 123456790, ... "target_name": "M31", ... "alias_name": "Andromeda Galaxy", ... "target_ra_dec": [0.712, 41.27], ... "lp_filter": False, ... "start_min": 1380, ... "duration_min": 45, ... }, ... ], ... } >>> plan.set_view_plan(my_plan)
- seestarpy.plan.stop_view_plan()
Stop the currently executing observation plan.
This is the method the official Seestar app uses to stop a running plan. It sends
stop_funcwith{"name": "ViewPlan"}.Note
Confirmed via traffic capture from the official Seestar app v3.0.2 on 2026-02-24.
- Return type:
dict
Examples
>>> from seestarpy import plan >>> plan.stop_view_plan()