Node v1 includes aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/skyrobot804/node_v1/llms.txt
Use this file to discover all available pages before exploring further.
LiveStacker class that aligns incoming sub-frames to a reference using a RANSAC-style asterism vote over detected stars and accumulates them into a running-average image. This mirrors the Seestar S50’s signature live-stacking feature: as each sub-frame arrives the displayed image grows progressively cleaner, with SNR improving proportionally to the square root of the number of frames stacked. LiveStacker is entirely self-contained — it accepts raw image arrays from any source (ALPACA responses, FITS files, NumPy arrays) and has no hardware dependencies.
How Alignment Works
Alignment is translation-only: each incoming frame is shifted in X and Y to register it onto the first accepted reference frame. Rotation is not corrected. Detection: Stars are detected usingDAOStarFinder from photutils after sigma-clipped background subtraction. Up to max_stars (default 60) brightest sources are retained for matching.
RANSAC vote: For each candidate pair of a reference star and a current-frame star, a translation hypothesis (dx, dy) is formed. All current-frame stars are shifted by that hypothesis and tested against the reference catalogue; the hypothesis with the most agreeing pairs (inliers within match_tolerance_px) wins.
Sub-pixel refinement: Once the winning hypothesis is identified, inlier pairs are collected and the final (dx, dy) is taken as the median of their individual residuals, giving sub-pixel accuracy.
Warping: The frame is shifted using scipy.ndimage.shift with bilinear interpolation (order=1), then added to the float64 running accumulator.
SNR Improvement
For shot-noise-limited images, stacking N frames of equal exposure improves the signal-to-noise ratio by a factor of √N compared with a single frame. Thesnr_gain() method returns this theoretical factor based on the current frame count:
stacking.py
Configuration
The stacking parameters used by the dashboard’s automatic stacking session are set under thestacking key in config.yaml:
config.yaml
Total number of sub-frames to accumulate before the stacking session ends. The session stops automatically when this count is reached; call
POST /api/stack/start again to begin a new session.Exposure duration in seconds for each sub-frame, passed to the camera when the dashboard drives exposures. Has no effect when frames are supplied externally (e.g., via
ImageWatcher).How often the dashboard preview PNG is regenerated. Set to
1 to update after every stacked frame; set to 5 to update every five frames and reduce CPU usage on slower hardware.Dashboard Integration
The live stacker is accessible from the dashboard via two API endpoints:| Method | Endpoint | Description |
|---|---|---|
POST | /api/stack/start | Reset the accumulator and begin a new stacking session using the current stacking config. |
POST | /api/stack/stop | Abort the current session and stop accumulating frames. |
preview_every accepted frames. The preview is served as a base64-encoded string embedded directly in the /api/status response, so no separate image endpoint is needed.
Programmatic Use
LiveStacker can be used directly in any Python script. All constructor parameters are optional; the defaults work well for the Seestar S50’s typical output:
stacking.py
add_frame() accepts any input that can be coerced to a 2-D NumPy array. Colour images in ALPACA (plane, x, y) or (x, y, plane) layout are automatically collapsed to luminance by averaging the smallest axis.
add_frame() Return Dict
Every call toadd_frame() returns a status dict, regardless of whether the frame was accepted:
True if the frame was aligned and added to the stack; False if it was rejected.Human-readable reason for the outcome. For accepted frames:
"reference frame" (first frame) or "stacked". For rejected frames: the failure reason, e.g. "alignment failed (too few matching stars)", "offset out of range (350.2,12.1)", "frame size differs from reference", or "reference frame has too few stars".Number of frames successfully added to the accumulator so far (including this one if accepted).
Total number of frames submitted to
add_frame(), including rejected ones.Number of frames rejected for any reason since the last
reset().The
(dx, dy) translation in pixels applied to this frame to register it onto the reference, rounded to 2 decimal places. (0.0, 0.0) for the reference frame and for rejected frames.Number of star pairs that agreed with the winning alignment hypothesis (RANSAC inliers). Useful for gauging alignment quality — more inliers means a more confident registration.
Current √N SNR gain factor relative to a single frame, rounded to 2 decimal places. For example
4.47 after 20 frames.Preview Rendering
preview_png_b64() renders the current mean stack into a viewable 8-bit PNG using the following pipeline:
- Percentile clip: Low and high levels are set to the 1st and 99.5th percentile of the stack, clamping extreme hot pixels and the sky background before stretching.
- Asinh stretch: After normalising to [0, 1], a mild
arcsinh(x × 10) / arcsinh(10)stretch is applied. This lifts faint nebulosity and dim stars while preventing bright cores from washing out — the same perceptual approach used by the Seestar app’s own live view. - Downscale: If either dimension exceeds
max_px(default 720 pixels), the image is downscaled proportionally using Pillow’s resize, keeping the aspect ratio intact. - Encode: The result is saved as a PNG into a
BytesIObuffer and base64-encoded for embedding directly in JSON API responses ordata:URIs.
