Exoplanet Transit Detector
Full differential-photometry pipeline for ground-based exoplanet transit detection and variable-star monitoring — calibration, alignment, ensemble photometry, Lomb-Scargle + BLS period search, phase folding, SIMBAD/VSX identification, and AAVSO-compliant export in single or multi-band form.
The amateur-to-professional bridge. This is the tool for a dark-sky amateur rig running differential photometry on candidate transit events and variable stars — the detection and vetting step whose output goes to professional databases, professional follow-up, or published papers where the final publication-grade fits are done in dedicated professional astronomy tools. Load a stack of light frames (raw or already-aligned), get differentially-calibrated light curves for every star in the field, run Lomb-Scargle and Box Least Squares to search for periodic signals, phase-fold candidate transits, identify the target via SIMBAD/VSX, and — the headline feature — export it all directly to AAVSO.
AAVSO integration — the bridge, out of the box. Export → AAVSO produces WebObs-compliant Extended Format files directly from your light curves. This is the signature feature of this tool: the mechanism by which amateur photometry contributes to professional databases of variable stars and exoplanet transits.
The implementation does the hard parts right: a three-tier photometric calibration cascade (ensemble zero point when ≥3 catalog comparisons are available, single-comparison fallback, check-star last resort), MAD-clipped sigma rejection to drop bad catalog magnitudes before they poison the zero point, APASS DR9 and UCAC4 catalog integration for Johnson B/V and Sloan r' (acting as R proxy, with honest NOTES annotation when it does), MAD-based SEM for the ZP uncertainty, systematic error floor added in quadrature (0.10 mag default, configurable), per-filter interleaved TB/TG/TR output for OSC (DSLR / color CMOS) data, and automatic MERR > 1.0 → "na" handling so a single out-of-range row doesn't bounce the whole file at upload. The full math and tier logic are in the Technical section (§18.15-18.17); the single-click workflow is in §15.
Published science from the amateur side. The Backyard Eclipsing Exoplanet Project (BEEP) is a citizen-science collaboration whose contributors use SASpro for amateur-side detection and candidate vetting. Their forthcoming paper "Detection of a Long-Period Gas-Giant Candidate With Citizen Scientists" (K. Lynch, D. Anderson, D. Chaton, N. Fritz, F. Thill, F. Marek, T. Traub) reports a transit candidate around TIC 358943696.01 with P = 13.5627337 ± 0.0000204 days, central transit duration T14 = 8.847 ± 0.110 hours, and Rp = 1.82 ± 0.08 RJup, seen in TESS Sectors 17, 57, and 84.
SASpro's role in that work is the amateur-detection side — ground-based follow-up frames from citizen-science contributors going through differential photometry here to flag the candidate for the professional team. The publication-grade fits in the paper are done in dedicated professional astronomy tools; SASpro's job is bringing amateur data up to the quality bar where it's usable alongside those fits, and getting it there via AAVSO submission is the standard handoff.
1. The two modes
At the top of the dialog, pick which stage you're starting from:
| Mode | Input | Workflow |
| Aligned Subs (default) | Frames already calibrated and star-aligned (e.g. output from Stacking Suite or Star Registration) | One-click Load, Measure & Photometry button — loads, detects stars, runs full photometry pipeline. |
| Raw Subs | Uncalibrated raw light frames | Three-step: Load Raw Subs → load Master Dark / Master Flat → Calibrate & Align Subs → Measure & Photometry. |
Mode switching shows and hides the relevant controls. Raw mode exposes the master-dark / master-flat loaders and calibration button; aligned mode hides them because the frames are assumed already-processed.
Why 2× 2 binning on load. Every frame — raw or aligned — is 2×2 binned the moment it's loaded. This is a deliberate design choice: binning improves per-pixel SNR for faint comparison stars (critical for ensemble photometry), halves memory consumption on long sequences, and roughly quadruples throughput at every subsequent step with no measurable loss of positional accuracy at typical ground-based plate scales. The WCS is tracked with a _wcs_bin_factor so pixel-to-sky conversions apply the correct scaling regardless of whether the WCS came from a header (full-resolution) or the solver (binned).
2. Loading frames
Both modes present a multi-select file picker (FITS, FIT, XISF, TIFF, TIF supported). Files are read in parallel for header metadata, then sorted chronologically before loading pixel data. The time parsing hierarchy is:
UT-OBS if present (already in UTC)
DATE-OBS (treated as ISO-UTC; timezone offsets like -0700 are auto-normalized to -07:00)
DATE-END as fallback
MJD-OBS as the most robust final fallback
XISF files have their FITSKeywords block queried for the same keys. Any frame that fails to parse a time ends up at the end of the sort, after all time-stamped frames. The resulting ordered list drives everything downstream — the light curve x-axis, the reference frame selection, and the AAVSO JD field.
Each loaded frame also harvests two more fields:
- Exposure time from
EXPOSURE or EXPTIME. Needed for AAVSO export (per-frame integration).
- Airmass from
AIRMASS directly, or computed from altitude keys (OBJCTALT, ALT, ALTITUDE, EL) via X = 1/sin(h). Defaults to 1.0 if nothing is found.
3. Calibration (Raw mode)
In Raw mode, two buttons let you load Master Dark and Master Flat files. Status labels flip from ❌ to ✅ when a master is successfully loaded. The dark and flat shapes must be compatible (after binning); mismatches surface as a warning and refuse the load. Both masters are themselves 2× 2 binned on load so they align with the (binned) light frames.
Calibrate & Align Subs runs the full raw-to-aligned conversion on every loaded frame:
- Subtract master dark (if loaded).
- Divide by master flat (if loaded) via Numba-accelerated per-pixel division.
- Expand mono to RGB-equivalent if the input is 2D.
- Use the first processed frame as the reference, then astroalign-align every subsequent frame to it with an affine transform. See Stellar Alignment for the alignment engine details.
Save Aligned Frames writes the calibrated and aligned stack back to disk — useful if you want to inspect intermediate frames or feed them to a different downstream tool.
4. The Settings dialog (wrench icon)
The orange wrench button at top-right opens the full configuration panel, organized into three sections:
Photometry
• SEP detection σ (default 5.0) — threshold for star detection. Lower → more stars (including faint). Higher → only bright, well-detected sources.
• Border fraction (default 0.10) — fraction of each frame edge to exclude from detection. Stars within this border are dropped to avoid PSF truncation at frame edges and aperture-at-edge artifacts.
Ensemble Normalization
• Comparison stars (k) (default 10) — number of nearest-flux companion stars used to build each target's ensemble reference. Smaller k = more targeted comparison; larger k = more robust to individual noisy comparisons.
Analysis (period search)
• LS samples / peak (default 10) — Lomb-Scargle frequency grid density.
• BLS min / max period (defaults 0.05 / 2.0 days) — period search range for Box Least Squares. The max is capped at 95% of the observational baseline internally.
• BLS # periods (default 1000) — number of periods in the search grid.
• BLS min / max duration fraction (defaults 0.01 / 0.5) — transit duration grid, as fraction of trial period.
• BLS # durations (default 20) — number of duration samples.
5. Measure & Photometry — the main pipeline
Once frames are loaded (and calibrated if raw), Measure & Photometry runs the full detection, aperture photometry, plate-solving, and ensemble normalization pipeline. In aligned mode, Load, Measure & Photometry combines the load and run into one click.
Progress updates stream in real-time. The pipeline:
- Pass 1: SEP detection on every frame in parallel (80% of CPU cores). Measures star count, mean background, FWHM, and ellipticity per frame.
- Picks the best reference frame by quality metric (highest star count / FWHM / background).
- Plate-solves the reference frame to establish WCS. If a WCS already exists in the header, you're asked whether to use it or re-solve. XISF files always get re-solved because the embedded WCS is PixInsight's TPS approximation and can be off by 10-30" at frame corners.
- Filters the reference-frame SEP catalog to the interior (dropping stars within the border fraction).
- Pass 2: aperture + annulus photometry on every star across every frame, with per-star adaptive apertures based on each star's own FWHM in the reference.
- Ensemble normalization — each star's light curve is divided by the median flux of its k nearest-flux companions.
- Detrending (if configured) and 2σ outlier flagging.
- Drops stars with fewer than 75% good frames.
- For OSC color inputs, runs a supplementary per-channel R/G/B photometry pass.
- Populates the star list and opens the overlay dialog.
6. The reference overlay
A second window appears showing the reference frame with every detected star circled. Click any circle to select that star in the main dialog's list. Shift-click adds to the selection. Click the main-dialog list and watch the overlay highlight update in real time (red = currently selected, yellow = flagged for dip, blue = normal).
Overlay controls
• Zoom In / Zoom Out / Reset Zoom / Fit to Window — standard zoom controls. Pan with left-click drag anywhere outside a star circle.
• Stretch: ON/OFF — toggle an automatic log-based stretch for viewing faint stars. The pixel data isn't modified; only the preview.
7. The star list and light curve plot
Each detected star appears in the left-column list showing its index, x,y pixel position, median relative flux, and the reference-frame FWHM. Click one or more entries (Ctrl-click to multi-select) and the right-side plot updates with the selected light curves in distinct colors.
The plot shows:
- Raw points (translucent, flux-colored dots) — the per-frame relative-flux measurements, with flagged frames omitted.
- Moving average line (dashed, matching color) — a 5-frame boxcar average, giving a smoothed view of the trend.
The x-axis shows hours since first observation when timestamps are available, or frame index when they aren't. Segments separated by gaps larger than one hour are plotted as separate connected regions, preventing fake "transit" shapes from being drawn across nightly gaps.
8. Detrending
The Detrend combo in the top-right offers three options:
- No Detrend — raw relative-flux curves.
- Linear — divide each curve by its best-fit line. Removes slow systematic trends from airmass change, mild transparency drift, or focus changes across the night.
- Quadratic (default) — divide each curve by its best-fit parabola. Captures the typical "frown" from early-rising + late-setting airmass trajectories across a long observing session.
Detrending happens on each star's light curve independently, after ensemble normalization. The curves are then re-centered to unit median so comparisons across stars remain apples-to-apples.
9. Dip detection — threshold and coherence
Transit detection in the main view works via two sliders working together:
Dip threshold (ppt) — minimum peak deviation of the 5-frame moving average from 1.0, in parts per thousand. 20 ppt = 2% dip. Set lower to catch shallower candidates; higher to filter noise-driven false positives.
Temporal SNR min (default 2.0) — the coherence check: the peak dip must be at least N × the MAD of residuals between the raw points and the moving average. A noisy curve where the MA randomly wanders doesn't pass; a real coherent dip does. Set to 0 to disable.
Stars that pass both thresholds get highlighted yellow in both the list and the overlay. The status bar reports the count. The design catches real transits while filtering the noise-driven false positives that would otherwise swamp any amateur-sized dataset.
Why coherence matters. A naive "any 2% dip is a candidate" filter will fire on every noisy comparison star in your field — stars where the raw scatter is 1-2% per frame will produce 5-frame-averaged "dips" of similar magnitude just from random fluctuations. The coherence check requires the MA excursion to be larger than the MAD of the individual-point residuals: real transits have smooth, coherent, non-random dip shapes; noise doesn't.
10. Identify Star — SIMBAD / VSX lookup
Select exactly one star, click Identify Star…. The tool projects the star's pixel position through the WCS to RA/Dec, queries SIMBAD within 15" of that position, and returns the best match. For contested regions where multiple catalog entries fall nearby, a scoring system prefers:
- Variable-star otypes (V*, Mi*, EB*, EA*, EW*, RR*, Ce*, dS*, SX*, Sr*, SR*, LP*, LPV, Ir*, Pu*, Ro*, RS*, BY*, Er*, FU*, TT*, Y*O, and more) — +100 to score
- Named catalog designations (V*, V, NSV, HD, HR, HIP, TYC, BD, CD, ASAS, ASASSN, ZTF, OGLE, GSC, PPM, SAO, WDS, WISE) — +30
- Generic Gaia designations (Gaia DR3, DR2, DR1, EDR3) — –30
Angular offset is subtracted from the score as a tiebreaker. If the best SIMBAD match still isn't flagged as variable, a VSX cone search within the same radius looks for a variable-star designation to upgrade to. The result dialog shows the ID, otype, V magnitude, and offset, with a one-click button to open the full SIMBAD page in your browser.
11. Find Star by name
The reverse operation: enter any SIMBAD-resolvable name (e.g. "V336 Aps", "HD 12345", "TYC 9256-617-1", "Gaia DR3 ...") and the tool resolves the name to RA/Dec, projects back through the WCS to detector pixel coordinates, and selects the nearest detected star in the list.
The matching tolerance adapts to the data: it's the maximum of 2× the median FWHM in detector pixels, 10 arcseconds converted to detector pixels using the WCS plate scale, and a 6-pixel floor. If no star is within tolerance, you get a specific diagnostic — "resolves to pixel (X,Y) but closest detected star is N pixels away" — so you know whether SEP dropped the star (faint, border, saturated) or the target isn't in frame at all.
12. Show Ensemble Members
Each star's light curve is normalized against its k nearest-flux comparison stars (default k=10, configurable in Settings). Show Ensemble Members highlights those comparisons in the list in light blue, letting you see which stars are being used as reference for your target. Useful for diagnosing noisy curves — if the "comparisons" are a mix of variable stars and the target itself, the normalization will drag the target toward whatever those are doing.
13. Analyze Star — the period search
Select exactly one star, click Analyze Star…. A tabbed window opens with four panels:
| Tab | Shows |
| Lomb-Scargle | LS periodogram covering frequencies from 1/Tspan to the Nyquist limit. Peak marked in yellow. Best for continuous variability (sine-like signals, pulsators). |
| BLS Periodogram | Box Least Squares periodogram, whitened (running-median background subtracted) and plotted on a per-period axis. Best peak marked in red. Best for sharp transit-shaped dips. |
| Phase Fold | Light curve folded at the best BLS period, phase from –0.5 to +0.5. Raw points in grey, binned points with error bars in blue, box-model prediction in red. |
| Light Curve | Raw calibrated curve with predicted transit centers marked by vertical dashed lines at integer multiples of the found period from t₀. |
The window title summarizes the result: Analysis: Star #42 | P = 1.4237 d | SDE = 8.3. A detail line underneath shows duration (hours), depth (ppt), SDE, alias penalty, and number of per-night dips detected.
Reading the SDE. Signal Detection Efficiency: how many sigma above the noise floor the BLS peak sits, after whitening. SDE > 7 is a strong candidate; 5-7 is marginal (worth phase-folding to inspect); below 5 is noise-dominated. The reported SDE already has the alias penalty subtracted — alias-contaminated peaks near 0.5, 1.0, 2.0, or 3.0 days get downweighted so true planetary signals win over nightly-cadence artifacts.
14. Export CSV / FITS
Standard data export of every star's light curve across every frame. The file format follows the dialog's suffix: .csv gets Pandas-formatted CSV with one row per frame, columns for MJD then STAR_i / FLAG_i / STAR_i_RA / STAR_i_DEC per detected star. .fits gets a proper multi-HDU FITS with a LIGHTCURVE table (MJD + per-star flux + flag columns) and a STARS table (index, x, y, RA, Dec per star), plus original-header metadata (OBJECT, TELESCOP, DATE-OBS, EXPTIME, FILTER) and pipeline parameters (SEP threshold, border fraction, reference frame index, median FWHM).
15. Export → AAVSO — the real deliverable
Produces an AAVSO Extended Format text file ready for WebObs submission. This is where the tool stops being an analysis plaything and starts producing data someone else can use. Two export paths:
Single-band export
For mono data or when you want one filter at a time. The flow:
- Select one target star in the list.
- Target name auto-resolved via SIMBAD/VSX (editable).
- AAVSO observer code (persisted across sessions).
- Submission format (Variable-Star Photometry or Exoplanet Report).
- Check star auto-identified from the target's ensemble members (first with a known SIMBAD V magnitude). Fallback to manual entry if auto-ID fails.
- Filter code chosen from the full AAVSO band list (Johnson-Cousins, DSLR, unfiltered CCD, Sloan, Near-IR, narrowband).
Multi-band export (OSC color data)
When OSC per-channel photometry is available, you're offered a one-click option to export all three channels interleaved in a single AAVSO Extended file: TB (Blue), TG (Green standardized to V), TR (Red). This is the biggest workflow win for DSLR / OSC observers: one upload, three bands, standardized against catalog B / V / R magnitudes.
For each band, the calibration pipeline tries three tiers in order:
- Ensemble zero point (preferred) — requires ≥3 comparison stars with catalog magnitudes in that band. Computes ZP(t) = mediani(Vcat,i + 2.5 log10 Fi(t)) per frame with MAD-based 3σ clipping. Delivers a proper AAVSO ENSEMBLE entry.
- Single-comparison — when only 1-2 catalog comparisons are available in that band. Picks the brightest of them for best SNR and does classic differential photometry.
- Check-star fallback — last resort when no catalog comparisons exist in that band. The check star doubles as the comparison; AAVSO can still accept the row but the CMAG and KMAG fields reflect that compromise.
Catalogs are tried in order: MagTool cache (if available from a prior Magnitude Tool session on this frame), APASS DR9 (Johnson V/B + Sloan r'), UCAC4 (same with slightly different coverage). When Sloan r' is used as the R anchor, the NOTES field includes "R anchor = Sloan r' (APASS/UCAC)" so a reviewer knows exactly what was done.
MERR > 1.0 handling. AAVSO Extended Format rejects any row with MERR > 1.0 at upload — the whole file bounces. When a frame produces a valid MAG but an uncertainty outside this limit, the tool emits MERR=na rather than dropping the row entirely. The summary dialog at export completion reports the count per filter so you know how many rows got the na-flag treatment.
16. Query TESScut
If TESS has observed your target during one of its sectors, click Query TESScut after selecting a star. The tool downloads the target pixel file (10 arcminute cutout centered on the selected RA/Dec), builds a 2.5-pixel-radius circular aperture, extracts the light curve, clips extreme outliers, and shows the normalized flux via Lightkurve. Results are cached locally so re-querying the same target skips the download.
Useful for cross-checking ground-based variability against the much higher-cadence TESS data — if your 20-ppt dip shows up in TESS at the same period, that's independent confirmation; if TESS shows a flat line, your "candidate" is probably your own systematics.
17. Workflow recipes
Variable-star monitoring across a session. A session's worth of frames of a known-variable target. Mode: Aligned (frames already through Stacking). Load, Measure & Photometry → the target star shows up in the list → select it → the light curve plots → set Dip threshold to 20 ppt (if looking for subtle variations) or 50 ppt (bright variables), coherence 2.0 → run Analyze Star to get LS period → compare to AAVSO VSX historical data → Export AAVSO to contribute your night's data.
TESS candidate follow-up. A known TESS Object of Interest (TOI) with a predicted transit tonight. Image through the predicted window. Run through the full pipeline → Find Star by TOI name to select it → look at the light curve directly for the predicted dip. If seen: Export AAVSO with the Exoplanet Report format for submission. If not seen: a null result is still useful; export anyway with your limiting-magnitude notes. Query TESScut after your observation for cross-validation.
Blind survey for new variables. A deep stack of ordinary field frames. Load, Measure & Photometry → crank Dip threshold down to 15-20 ppt to catch marginal candidates → coherence 2.5+ to reject noise → every yellow-highlighted star gets a sanity-check Analyze → the real variables show clean LS peaks and coherent phase folds; the false positives show nothing in LS and messy phase diagrams → Identify Star on each real detection → cross-check against VSX for existing designation → report any genuinely new ones via AAVSO.
Long-period transit vetting (BEEP-style workflow). Multiple nights of a known candidate, from a known TESS TOI, with a predicted period and expected transit time. Combine all nights' aligned frames → Measure & Photometry on the merged stack → Find Star by TOI name → Analyze Star with BLS min period set near the TESS-reported value → verify the detected period matches TESS to within the expected uncertainty → phase-fold → compare duration to TESS → Export AAVSO. This is the workflow that produced the Lynch et al. (2025) long-period gas-giant paper — the citizen-science confirmation of TIC 358943696.01 at P = 13.56 days.
Multi-band DSLR / OSC color photometry. OSC imager (DSLR, color CMOS). Mode: Raw with masters, or Aligned if already processed. Load, Measure & Photometry runs the per-channel R/G/B pass automatically. Select target → Export AAVSO → opt in to multi-band export → get TB + TG + TR rows in one file, calibrated against APASS Johnson B / V and Sloan r' (acting as R proxy with honest NOTES annotation). One upload = three bands.
18. Technical details
Star detection, aperture photometry, ensemble normalization, the period search algorithms, the AAVSO calibration tiers, and the plate-solve integration.
18.1 SEP detection pipeline
Every frame is run through SEP (Barbary 2016, port of SExtractor by Bertin & Arnouts 1996) in parallel across a thread pool sized to 80% of available CPU cores. Per frame:
- Sigma-clipped background estimation via
sigma_clipped_stats on the gray plane.
- Background subtraction, then SEP background map and RMS map computation.
- Source extraction with the user's σ threshold (default 5.0), minarea 16, deblending threshold 32.
- Per-source FWHM via FWHM = 2.3548 · √(a·b) from SEP's semi-axis measurements:
\mathrm{FWHM} = 2.3548 \cdot \sqrt{a \cdot b}
And ellipticity:
e = \sqrt{1 - (b/a)^2}
Per-frame stats recorded: star count, mean background, median FWHM, median ellipticity.
18.2 Reference frame selection
The best reference frame is chosen by a quality metric:
Q_i = \frac{N_{\mathrm{stars}}}{\mathrm{FWHM} \cdot \langle I \rangle}
which rewards frames with many detections, small PSFs, and low background. The frame with highest Q becomes the detection template for the rest of the pipeline — its SEP catalog defines which stars get photometered, its WCS gets plate-solved, and all other frames compute photometry at the positions found here.
18.3 Plate-solving integration
The reference frame goes to the plate solver. Three paths:
- Existing WCS in header, non-XISF, not forcing → user asked whether to use the header WCS (fast) or re-solve (fresh). Default is "yes, use it".
- XISF source → always re-solve. The XISF format embeds a TPS (thin-plate spline) WCS approximation computed by PixInsight that can be off by 10-30 arcseconds at frame corners — too imprecise for star-matching tolerances. SIP and CD/CDELT headers are stripped from the seed, keeping only CRVAL / OBJCTRA / OBJCTDEC / FOCALLEN / XPIXSZ as hints.
- Force blind solve enabled or no existing WCS → straight plate solve from scratch.
The _wcs_bin_factor is tracked throughout: header WCS is full-resolution (so bin_factor = 2 when applied to our binned coordinates), solver WCS is already in binned coordinates (so bin_factor = 1). Every pixel-to-sky conversion downstream respects this.
18.4 Adaptive aperture photometry
Per-star aperture radii adapt to each star's actual FWHM in the reference frame rather than using a global constant. The three radii:
r_{\mathrm{ap}} = \max(2.5,\; 1.5 \cdot \mathrm{FWHM})
r_{\mathrm{in}} = \max(r_{\mathrm{ap}} + 1,\; 3 \cdot \mathrm{FWHM})
r_{\mathrm{out}} = \max(r_{\mathrm{in}} + 2,\; 5 \cdot \mathrm{FWHM})
rap at 1.5·FWHM captures ~95% of a Gaussian PSF — close to the SNR-optimal aperture (which sits around 1.4·FWHM for a Gaussian + flat sky). rin at 3·FWHM puts the inner annulus edge well beyond the PSF wings. rout at 5·FWHM gives enough annulus pixels for a stable median sky estimate.
Per-star FWHM is clamped to [0.5× median, 3× median] to prevent any single bad detection from producing wildly out-of-range apertures.
18.5 Annulus sky subtraction with error propagation
For each (star, frame) pair:
sep.sum_circle gives the aperture flux Fap and uncertainty σap.
sep.sum_circann gives the annulus flux Fann and uncertainty σann.
- Compute sky-per-pixel from the annulus (median behavior via SEP's robust mode).
- Subtract the implied sky total from the aperture:
F_{\mathrm{net}} = F_{\mathrm{ap}} - \frac{F_{\mathrm{ann}}}{A_{\mathrm{ann}}} \cdot A_{\mathrm{ap}}
Propagate the uncertainties in quadrature with the aperture-area ratio:
\sigma_F = \sqrt{\sigma_{\mathrm{ap}}^2 + \left(\frac{A_{\mathrm{ap}}}{A_{\mathrm{ann}}}\right)^2 \sigma_{\mathrm{ann}}^2}
If annulus extraction fails (star near edge, insufficient annulus pixels), the pipeline falls back to the global-background-subtracted aperture flux rather than failing the measurement outright.
18.6 Ensemble normalization
For each target star T, find its k nearest-flux companions (default k=10) based on median flux:
F_{\mathrm{ens}}(t) = \mathrm{median}_i\{F_i(t)\}, \quad i \in \mathcal{N}_k(T)
Then divide:
f_T(t) = \frac{F_T(t)}{F_{\mathrm{ens}}(t)}
Error propagation per frame:
\sigma_{f_T} = f_T \sqrt{\left(\frac{\sigma_T}{F_T}\right)^2 + \left(\frac{\sigma_{\mathrm{ens}}}{F_{\mathrm{ens}}}\right)^2}
Finally the whole curve is renormalized to unit median so curves across stars are directly comparable. This is the standard differential-photometry workflow — any multiplicative systematic that affects target and ensemble members similarly (airmass, transparency, mild clouds, focus) cancels in the ratio, leaving astrophysical variability.
18.7 Detrending
Linear or quadratic polynomial fit per curve to the time-indexed flux (after ensemble normalization), then divide out:
curve_detrended = curve / polyfit_trend(curve)
The fit respects per-point validity (finite + positive). After detrending, each curve is re-centered to unit median.
18.8 Outlier flagging and star rejection
Per-curve 2σ-MAD outlier flagging:
\sigma_{\mathrm{rob}} = 1.4826 \cdot \mathrm{MAD}
|f_i - \mathrm{median}| > 2 \sigma_{\mathrm{rob}}
Flagged points are excluded from downstream plots and period search but kept in the data arrays. Stars with fewer than 75% unflagged frames are dropped entirely from the list — too noisy to be useful.
After dropping, the ensemble_map is remapped from pre-filter to post-filter indices so every downstream reference to "star #N" still points at the right curve.
18.9 Moving average dip detection
A 5-frame boxcar moving average smooths the curve:
\mathrm{MA}(t) = \frac{1}{w} \sum_{k=-w/2}^{w/2} f(t+k)
The peak excursion from unit median (converted to parts per thousand):
\mathrm{peak}_{\mathrm{ppt}} = 1000 \cdot \max_t |1 - \mathrm{MA}(t)|
Must exceed the user's dip threshold. Then the coherence check:
\mathrm{SNR}_{\mathrm{temp}} = \frac{\mathrm{peak}_{\mathrm{ppt}}}{1000 \cdot 1.4826 \cdot \mathrm{MAD}(f - \mathrm{MA})}
Must exceed the Temporal SNR min. Only stars passing both get flagged. The temporal SNR requirement is what makes dip detection useful on real amateur data where per-point scatter is often comparable to the dip magnitudes you're hunting.
18.10 Lomb-Scargle periodogram
Frequency grid from 1/baseline to Nyquist:
\nu \in [1/T_{\mathrm{span}},\; 0.5/\Delta t]
Via astropy's LombScargle.autopower with configurable samples-per-peak density (default 10). Peak period marked on the display. Best for sinusoidal variability — pulsators, eclipsing binaries at frequency-doubled signatures, rotational signals.
18.11 BLS pipeline with whitening and alias penalties
Box Least Squares is where the real transit search happens. The pipeline:
- Clean light curve: sort, positive-only, median-normalize, 6σ upper-outlier clip, re-normalize.
- Duration grid: linear spacing between min and max duration fractions of the minimum period:
d \in [d_{\min}\, p_{\min},\; d_{\max}\, p_{\min}]
- BLS with SNR objective, 10× oversampling, min_n_transit = 2.
- Whiten the raw power spectrum by subtracting a running median:
P_w(T) = P(T) - \mathrm{medfilt}(P(T),\, w)
This removes slow trends in the periodogram baseline that would otherwise dominate SDE calculations.
- Find the top 12 local maxima in the whitened spectrum. Peaks within 1% of each other in period are merged (same signal at different grid points).
- For each candidate peak, do a local refinement BLS on a dense grid ±20% around that peak, picking the best SNR point.
- Compute SDE for each refined candidate:
\mathrm{SDE} = \frac{P_w - \mathrm{median}(P_w)}{\sigma_{\mathrm{MAD}}(P_w)}
- Compute the alias penalty — downweighting periods near 0.5, 1.0, 2.0, 3.0 days (the diurnal cadence aliases that plague ground-based observing):
\pi_{\mathrm{alias}} = \max_{a \in \{0.5, 1, 2, 3\}} \max\left(0,\; 1 - \frac{|P/a - 1|}{\tau}\right)
with tolerance τ = 0.02 (within 2% of an alias).
- Final adjusted SDE:
\mathrm{SDE}_{\mathrm{adj}} = \mathrm{SDE} - 1.5 \cdot \pi_{\mathrm{alias}}
The best SDE_adj across all candidates wins. This is the design that prevents the "every ground-based BLS peaks at 1.0 day" problem — a true planetary signal at some real period beats a 1-day sidereal alias even if the raw BLS power at 1.0 day happens to be slightly higher.
18.12 Phase folding and binned curves
At the chosen period P and epoch t0, fold to [-0.5, +0.5]:
\phi = \left(\frac{t - t_0}{P} + 0.5\right) \,\mathrm{mod}\, 1 - 0.5
Raw folded points are plotted as grey dots. A sigma-clipped binned curve (adaptive bin width ≈ transit_duration/3) overlays with error bars from MAD-based SEM. A box-model prediction (constant flux outside transit duration, constant depth inside) overlays in red for visual comparison. The transit duration half-phase determines the box width.
18.13 Night dip event detection
Independent of BLS, a per-night dip detector segments the light curve by observational gaps (default > 0.2 days):
\delta = \mathrm{OOT} - \min(\mathrm{smooth}(f))
where OOT is out-of-transit (segment median of the smoothed curve). Required to be > max(2σsegment, 0.0008). Transit-shaped dips (down then up) get event entries. Reports per-event center time, depth, SNR, and duration. The summary line in the Analyze window reports the count — if your "candidate" shows 0 night-dip events but has a strong BLS peak, that peak is probably an alias; if it shows 5 events across 5 nights at consistent timing, you've got something real.
18.14 Multi-band R/G/B photometry (OSC inputs)
For color inputs, a secondary photometry pass runs the same aperture math (fixed global aperture from median FWHM, no per-star adaptation needed for color consistency) on each R, G, B channel separately. The result is a 3D array raw_flux_rgb shaped (3, n_stars, n_frames) with corresponding errors. Both are set to None when the input is mono, and the AAVSO exporter falls through to single-band mode.
18.15 AAVSO calibration — ensemble zero point
When ≥3 comparison stars have catalog magnitudes in the target band, compute a per-frame ensemble zero point:
\mathrm{ZP}(t) = \mathrm{median}_i\{V_{\mathrm{cat},i} + 2.5 \log_{10} F_i(t)\}
Target magnitude:
m_T(t) = -2.5 \log_{10} F_T(t) + \mathrm{ZP}(t)
The comparison set is first MAD-clipped (robust 3σ on each star's run-median ZP, with a 0.05 mag floor to prevent over-clipping tight ensembles): comparison stars with discrepant catalog magnitudes get dropped before the per-frame ZP is computed. SEM per frame:
\mathrm{SEM} = \frac{1.4826 \cdot \mathrm{MAD}}{\sqrt{N}}
Final MERR combines target instrumental uncertainty, ZP SEM, and a systematic floor:
\sigma_m = \sqrt{\left(\frac{2.5}{\ln 10} \cdot \frac{\sigma_F}{F}\right)^2 + \mathrm{SEM}^2 + \sigma_{\mathrm{floor}}^2}
Default floor is 0.10 magnitudes — a defensive estimate of unmodeled systematics (color terms, atmospheric variations, aperture scatter). Can be reduced to 0.05 or 0.0 if you've characterized your rig and know your actual systematic contribution. The ensemble zero point approach is standard practice for professional transit surveys and gives photometric accuracy within 0.01-0.03 magnitudes under good conditions, limited mainly by catalog magnitude accuracy.
18.16 AAVSO calibration — single-comparison fallback
When only 1-2 comparison stars have catalog magnitudes, pick the brightest (highest median flux — best SNR, most stable anchor):
m_T(t) = c_mag - 2.5·log10(F_T(t) / F_C(t))
Error propagated through both flux measurements plus systematic floor. The AAVSO row's CNAME field points at the chosen comparison; KMAG field holds the independent check-star value for post-upload sanity checking.
18.17 AAVSO calibration — check-star fallback
Last resort when no comparison stars have catalog magnitudes in the target band. The check star doubles as the comparison. CNAME points at the check star, KMAG field is "na" because there's no independent check anymore. The NOTES field documents the tier used so a reviewer can see exactly what was done.
18.18 SIMBAD / VSX name resolution scoring
Variable-star otype prefixes get +100 to the match score:
V*, Mi*, EB*, EA*, EW*, RR*, Ce*, dS*, SX*, Sr*, SR*, LP*, LPV,
Ir*, IR*, Pu*, Ro*, RS*, BY*, Er*, FU*, GC*, Ta*, TT*, Y*O
Named catalog designations get +30:
V*, V, NSV, HD, HR, HIP, TYC, BD, CD, ASAS, ASASSN, ZTF,
OGLE, GSC, PPM, SAO, WDS, WISE
Generic Gaia designations get -30:
Gaia DR3, Gaia DR2, Gaia DR1, Gaia EDR3
Angular offset is subtracted (closer wins). This solves a real problem: without scoring, SIMBAD's "nearest match" would often return a Gaia DR3 identifier for a star that's actually catalogued as, say, V336 Aps — Gaia has an entry for literally everything, and a plain nearest-neighbor return pollutes the exported #NAME field. The scoring plus the VSX fallback produces the right answer (the variable-star designation) when one exists.
18.19 Thread parallelism
Both SEP detection (pass 1) and aperture photometry (pass 2) run in parallel across a thread pool sized to 80% of available CPU cores. SEP itself is single-threaded internally so this gives real linear speedup. Per-frame tasks are completely independent (no shared state), so there's no lock contention. On an 8-core machine, a 200-frame session processes in roughly the time it takes one frame × 32 — a 6× speedup over serial execution.
Credits. SASpro Exoplanet Transit Detector — the two-pass detection + adaptive-aperture photometry pipeline, the k-nearest-flux ensemble normalization with error propagation, the moving-average + coherence dip filter, the three-tier AAVSO calibration cascade (ensemble ZP → single-comparison → check-star fallback) with per-tier NOTES annotation, the Sloan r' → R proxy flag for honesty on OSC multi-band exports, the SIMBAD/VSX scoring system that prefers variable-star designations over generic Gaia IDs, the XISF plate-solve re-solve policy, and the BLS whitening + multi-peak scanning + alias penalty workflow — are original work by Franklin Marek for SetiAstro Suite Pro. The night-dip event detection, phase-fold binning, and BLS multi-peak merging patterns derive from Kyle Lynch's "exoplanet_ground_telescope_script" (BEEP collaboration), ported and extended for the SASpro integration. Standard components credited: SEP (Barbary 2016, Python port of Source Extractor by Bertin & Arnouts 1996); astropy for WCS, Time, Lomb-Scargle, BoxLeastSquares, and SkyCoord; astroquery for SIMBAD and VSX queries; Lightkurve + MAST Tesscut for the TESS cross-reference path; APASS DR9 and UCAC4 catalogs via VizieR; and the AAVSO Extended Format specification. The Lynch, Anderson, Chaton, Fritz, Thill, Marek, Traub "Detection of a Long-Period Gas-Giant Candidate With Citizen Scientists" paper documenting TIC 358943696.01 (P = 13.5627 d, Rp = 1.82 RJup) is where SASpro contributes on the amateur-detection side through the Backyard Eclipsing Exoplanet Project (BEEP); the publication-grade fits in that paper are done in dedicated professional astronomy tools.