Lingrui Lin 林令瑞← Tools & visualizations

FISH / Sun17_A_01 spectral workflow

FISH spectral workflow tutorial

A practical guide to the complete spectral workflow using a compact real Sun17_A_01 / M01 dataset: load inputs, calibrate, inspect RFI, fit and subtract baselines, align velocities, and save a resampled spectral table. Only spatial gridding is omitted.

Sun17_A_01 · M01 · Real dataAA + BBNo server requiredPre-release tutorial snapshot · 25 Oct 2026

The download contains the Python source, notebook, real SCI/CAL/Tcal extracts, and numerical regression results. It is a fixed teaching snapshot, not a claim that the entire pipeline is ready for general release.

01 / Know the inputs

Sun17_A_01 input dataset

The example contains actual Sun17_A_01 observations from 10 October 2021: M01, 32 consecutive science integrations, and both auto-polarizations. Original zero-based SCI rows 1040–1071 are retained from N_0001. The native band keeps all 65,536 channels over 31.25 MHz.

The selection contains 6.442450944 seconds of exposure. Matching ON/OFF and diode-reference extracts retain context for the two default 10-timestamp filters and the 16-frequency-channel Tcal filter.

These are observed spectra, with no simulated signal or truth array. The original files are preserved. The complete example runs through baseline subtraction and Doppler-aligned resampling. Actual saved pointings and frequency factors are included; there is no need to download full telemetry. Spatial gridding is the only omitted production stage.

science.fits · 16,796,160 bytes

32 times × 65,536 channels × AA/BB; original counts and scalar metadata.

on_off.fits · 279,360 bytes

64 times × OFF/ON × AA/BB × 261 channels.

tcal_reference.fits · 106,560 bytes

M01, 4,197 diode-reference channels including smoothing context.

provenance.json

Original basenames and slices, midpoint ranges, exact sizes, SHA256 hashes, and extraction comparison.

Data volume. The three FITS files total 17,182,080 bytes (16.39 MiB). All 19 beams of Sun17_A_01 would require 82,037,318,400 bytes of narrow-band SCI and 10,288,290,240 bytes of extracted CAL; the whole scan is unnecessary for this calibration example.

02 / Run it locally

Installation and execution

Use a Python environment with Python ≥3.8 and a compatible scientific stack. The normal package installation includes the full pipeline dependencies; the tutorial itself uses NumPy, SciPy, Astropy, and Matplotlib. No server connection is required. The unattended workflow needs no desktop display; the Qt viewer does.

1

Download and unpack

Save the source archive using the download above. In the folder where you saved it:

tar -xzf fish-sun17-a01-source-20261025.tar.gz
cd fastfish-0.1.0
2

Install in a separate environment

An isolated environment keeps this teaching snapshot separate from an existing FISH installation.

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[notebook,qt]'
Installation does not finish?

Read the first dependency error. The full package includes cygrid, whose installation depends on your platform and available compiled dependencies. Use a compatible scientific Python environment rather than replacing your working system Python. If using a notebook, select the kernel of the environment where FISH is installed.

3

Run the readable script

python examples/minimal_calibration/run_workflow.py \
  --output "$HOME/fish-suna01-workflow"

The runner verifies inputs and executes the normal FISH run/wait sequence for reference, calibration/RFI, baseline and resampling. Use a new empty workspace. It saves fit records, a spectral table and diagnostic figures; no gridding is started.

Prefer to inspect one cell at a time?
cd examples/minimal_calibration
jupyter notebook walkthrough.ipynb

The notebook explains each intermediate array and equation. Its final cell explicitly saves results to a temporary folder. Download the notebook separately if you already have this source snapshot and its data.

03 / Desktop interface

Interactive calibration with the Qt viewer

Use the downloaded Sun17 example to run the actual FISH desktop viewer on your own computer. The instructions below create a session with 32 real spectra and their measured sky positions; no connection to the original data server is required. The screenshots show the calibration and baseline pages.

Actual FISH Qt Tsys calibration page for Sun17_A_01 M01 row 1040, showing smoothing controls, footprint and raw versus smoothed temperature references.
Tsys calibration: compare raw and filtered Tpsr, then adjust the two smoothing controls. Click the screenshot to enlarge it.
The upper-right panel compares raw and smoothed Tpsr. The lower-right panel shows the smoothed curve separately. Both summarize the temperature reference used for the selected spectrum.

1. Install and open the downloaded example

From the unpacked fastfish-0.1.0 source directory, in the same Python environment used above:

python -m pip install -e '.[qt]'
python examples/minimal_calibration/prepare_viewer.py \
  --output "$HOME/fish-suna01-viewer"
fish-viewer --session-state "$HOME/fish-suna01-viewer/fish_session.json"

Run the viewer on a machine with a desktop display; a headless Linux server needs a configured desktop/display session. The preparation command requires a new empty output directory and leaves the released data unchanged. To reopen a session and retain your edits, run only the last command. In the viewer, raw rows 0–31 refer to this compact extract and correspond to original Sun17 SCI rows 1040–1071.

2. Inspect the temperature reference

  1. Select Tsys calibration in the Stage menu. Click one of the 32 points in the footprint; Previous / Next also changes the selected spectrum.
  2. Keep Tpsr smoothing = 10, change ON−OFF smoothing from 10 to 5, then click Update preview. Compare the reference curves. This first filter changes the ON−OFF used to derive Tpsr.
  3. Restore ON−OFF smoothing to 10, change Tpsr smoothing to 5 and update again. This second filter acts on Tpsr before matching SCI timestamps. The two changes need not have identical effects.
  4. Use Reset edits to discard pending, unapplied edits. Once an edit has been applied, restore a value explicitly and click Update preview. Use values 1–10; larger filters require a longer CAL extract and are rejected in this teaching session.

3. Inspect calibrated spectra

  1. Choose Apply calibration. The upper plot is raw SCI counts; the lower plot is temperature in K for the same observation. No baseline has yet been subtracted.
  2. Switch between AA and BB. Compare their gain-calibrated spectra; do not expect the backend counts or bandpass to be identical.
  3. Use Z for horizontal zoom, Shift+Z for both axes, B for the previous view, F for full extent, and Escape to cancel selection. For exact limits, use View → Plot ranges….
  4. Open View → Display tools to switch between centered steps and lines, or adjust display averaging. These settings affect drawing, not the calibration arrays.

4. Try baseline and spectral-output previews

  1. Choose Baseline. Inspect the observed spectrum, fitted components and residual. The initial list contains sin/cos plus a local degree-1 polynomial. Each component fits and applies only within its listed range; protected H I and detected RFI do not anchor the fit.
  2. Use + Add component, choose a function and range, then Update preview. Change one parameter at a time. If a period reaches its search limit, open Details… and inspect the residual rather than assuming the fit is correct.
  3. Choose Table. Set rebinning n_b or the Gaussian target n_g, and the output velocity limits. Update the preview. Select a region on the footprint with R (rectangle) or P (polygon), then use Region mean to inspect its mean spectrum.

What this download supports. All 32 positions, midpoint times and LSRK frequency factors are real values saved from the verified Sun17 reduction. No sky coordinates are invented. The desktop viewer and complete workflow use these LSRK factors. The separate calibration-only sub-example uses topocentric frequencies. This short, one-beam extract supports saving all four spectral stages, but is unsuitable for a meaningful spatial cube. Only gridding is disabled. The full raw telemetry and full scan are not needed to run it.

5. Save the processing stages with your chosen settings

After inspecting and applying settings in the viewer, open FISH in the same environment and restore that session:

fish --viewer qt
restore ~/fish-suna01-viewer/fish_session.json
tsyscal run
wait
applycal run
wait
baseline run
wait
table run
wait
status

Run the commands one stage at a time and inspect any failure before proceeding. These commands process the compact dataset itself. Run commands… copies this same sequence, ending at Table. If editing settings in the terminal while the viewer is open, refresh the viewer before making further edits. Different settings receive different stage fingerprints; previous results are retained.

Use the same workflow with your own full observation project

Prepare a project with org_spec/, org_cal/, and matching feed_info/ telemetry, plus its appropriate Tcal reference. In FISH, select the files before calculating their footprint:

fish --viewer qt
project /path/to/your/project
file in org_spec/your_scan/your_date/*.fits
list
go where /workers 4
tsyscal

Inspect Tsys, Apply calibration, Baseline and Table previews, applying the settings you intend to use. Only then run the batch stages in the FISH prompt, waiting and checking each stage before continuing:

tsyscal run
wait
applycal run
wait
baseline run
wait
table run
wait
status

Run commands… copies stage commands for a full project; it does not execute them. This guide ends at the spectral table. Finite output does not establish true baseline quality or absolute calibration accuracy. Do not use a project path or Tcal reference from another observation without checking compatibility.

04 / Follow the calculation

Calibration procedure

The example calls the production calibration functions. It does not maintain a second estimator just for the tutorial. Open each step to see its meaning and code location.

ON − OFFTpsrGainTemperature
01 Measure the noise-diode increment

D = PON − POFF20480 × 20480 + 286722

Smooth this increment along time using n_t_cal (default 10 timestamps). The original code uses ON=20480 and OFF=28672. The official FAST backend manual, Section 4 (p. 4) defines their unit as 4 ns. These settings give unequal diode durations: 81.92 μs ON and 114.688 μs OFF.

Timing values used by the original calibration
QuantityCalculationDuration
Diode ON20480 × 4 ns81.92 μs
Diode OFF28672 × 4 ns114.688 μs
Cycle(20480 + 28672) × 4 ns196.608 μs
PSR sampleSun17 CAL header TBIN98.304 μs

The extractor averages alternating equal-length PSR sample bins separately. With the assumed phase alignment, the ON-labelled bin receives injection for only 81.92/98.304 = 5/6 of its duration. The required multiplier is therefore tsample/tON = (20480 + 28672)/(2 × 20480) = 1.2. It is not the inverse of the ON fraction of the entire cycle.

The CRAFTS pipeline paper, Section II.1 also documents the unequal diode durations. The numerical settings here come from the original calibration code; the FAST manual establishes the units, not these observation-specific values. This explanation assumes the pulse is confined to the ON-labelled bin. Different settings or phase alignment require a corresponding timing calculation.

Production: src/fastfish/calibration/extracted.py → rebuild_reference

02 Build and align the temperature reference

Tpsr = Tcal × POFF / smooth(D)

The reader smooths the diode-temperature reference over 16 frequency channels and interpolates it to the CAL grid. It retains the production float32 reference rounding. Then n_t_apply independently smooths Tpsr in time before interpolation to SCI midpoints.

The reader converts original SCI UTOBS start times to midpoints using EXPOSURE/2. CAL timestamps already denote midpoints. Time extrapolation is disabled. Smoothing Tpsr does not time-average the science spectra.

Production: rebuild_reference; time matching: scipy.interpolate.interp1d(..., bounds_error=True)

03 Estimate the frequency-dependent gain

Gain anchors = overlap-averaged SCI counts / Tpsr

SCI is averaged into fully covered CAL frequency bins for comparison. A cubic log-gain is fitted independently at every time and for AA/BB, then evaluated on the native SCI grid. Anchors within ±700 km/s of H I are excluded here; this is a gain-fit exclusion, not a baseline-protection setting.

The example calls the production calibration wrapper, including its chunk-level catalogue-guided RFI search. Only observed detections become exclusions. No intervals were detected for this selected chunk at the current thresholds; this does not establish that every channel is clean.

Production: processing/preview.py → calibrate and processing/spectral.py → frequency_gain

04 Apply gain to native science channels

TSCI = PSCI / Gain

The final spectrum retains native channels. Frequencies outside the CAL support remain NaN; they are not extrapolated. The measured background remains because no continuum or baseline has been removed.

Tutorial: calibrate.py → apply_reference

Array dimensions and units. SCI is (time, channel, polarization); Tpsr is (time, polarization, channel). Frequencies supplied to the gain estimator are GHz, diode-file frequencies are MHz, temperature is K, and gain is counts/K.

05 / RFI exclusions and baseline removal

Fit and inspect the baseline separately in AA and BB

Calibration converts backend counts to temperature; it does not remove the spectral baseline. The baseline stage fits each polarization independently, subtracts its model on native channels, and only then averages the residual polarizations.

  1. Inspect RFI and H I first. In Apply calibration, zoom through the native band and compare AA/BB. Red bands mark detected RFI; gray marks protected H I. Catalogue priors are not automatic masks. The selected sample currently has no catalogue-guided detections, which does not prove that all channels are clean.
  2. Choose Baseline. The component table initially has a full-band sin/cos component and a degree-1 polynomial over −1500:1500 km/s. Components fit sequential residuals in list order. Use the arrows to change order or × to remove a component.
  3. Protect H I. The default protection is ±250 km/s. Protected channels and detected RFI are excluded from fitting anchors, but the model is subtracted beneath the protected line. Choose a different interval only after inspecting the actual profile.
  4. Adjust one component. Choose polynomial and its degree, or sin/cos with automatic, fixed or bounded period in MHz. Set the velocity fit/apply range, then click Update preview. Each component is applied only within that range; a local model is not extrapolated outside it.
  5. Inspect the residual. Check both polarizations, especially offsets and coherent structure outside H I and excluded RFI. Compare plausible models. A flat-looking average or low residual outside H I does not establish the true baseline under the protected interval.
  6. Save the result. After applying settings, run baseline run and wait in the restored FISH session. If calibration settings changed too, complete the upstream reference/calibration stages first.
Saved AA and BB baseline components above, with after-first-component spectra, local models and final residuals below. Protected H I is shaded; its peak is clipped for baseline inspection.
Reconstructed from the production stage’s saved coefficients, not a separate presentation fit. The upper panels show calibrated spectra, the first component and total baseline; the lower panels show the local model and residual. Display means of 32 channels and clipped H I peaks expose smaller offsets. Steps at ±1500 km/s follow the component’s explicit application boundary.
Extra exclusions and period-search warnings

Additional baseline-only intervals can be entered in Exclude km/s. For example, the FISH syntax set baseline_exclusions_kms [[800,850]] excludes that interval from fitting; it does not identify an RFI there. Use exclusions only when justified by the observed data.

The default demonstration reports a period at its allowed search boundary in one of two processing chunks. Open Details to identify the component/polarization and compare its residual with alternatives. This is a diagnostic, not an automatic pass/fail verdict. The initial model is illustrative and is not asserted to be scientifically optimal.

Inside protected ±250 km/s, the baseline is inferred from outside anchors and the chosen function. The fit alone cannot measure its true error there. Native-channel scatter also contains radiometer noise; the plotted channel means have lower scatter and should not be confused with native noise.

06 / Doppler alignment and resampling

Generate the final spectral table

The Table stage uses the actual per-spectrum LSRK frequency factors bundled with the example. It aligns baseline-subtracted spectra onto a common output grid and retains one row for each selected timestamp. This is a spectral table, not a spatially gridded cube.

  1. Choose Table in Stage. Keep rebin mode with n_b=2 and output velocity limits −250 to +250 km/s for the default example.
  2. To compare responses, change the rebin factor or select Gaussian with a target n_g in native-channel FWHM units. Update preview and inspect the result; display averaging is a separate plotting control.
  3. Optionally select points with R (rectangle) or P (polygon) on the footprint and use Region mean. This previews a mean without spatial gridding.
  4. Run table run, then wait and status in FISH. The default saved table has 32 rows × 2,485 channels. There is no need to run xy_map for this example.
Mean of 32 baseline-subtracted, Doppler-aligned spectra on the final output velocity grid, with H I preserved and zero reference line.
Mean of the saved 32-row spectral table, n_b=2. The table retains individual spectra; averaging here is for this diagnostic figure.
Read the saved table in Python

Replace the workspace path if you chose another output folder:

from pathlib import Path
import json
import numpy as np
root = Path.home() / "fish-suna01-workflow"
manifest = json.loads((root / "table_manifest.json").read_text())
processed = json.loads(Path(manifest["processed"]).read_text())
product = processed[0]["products"][0]
spectra = np.load(product["data"], mmap_mode="r")
frequency_ghz = np.asarray(manifest["frequency_ghz"])
print(spectra.shape)  # (32, 2485) for the defaults

07 / Check your answer

Saved outputs and verification

Real Sun17_A_01 M01 diagnostics: noisy and filtered ON-OFF increments, temperature references, separate AA/BB gain curves, and observed H I spectra.
Sun17_A_01 / M01. Faint time curves show raw band medians; solid time curves summarize channel-wise filtered data. Spectral panels use display-only means of 32 channels; native calibration arrays are unchanged.
0 KCropped vs original calibrated-spectrum difference on the server
26.43 / 27.35 KMedian Tpsr in AA / BB
32 × 65,536 × 2Native calibrated output shape

The compact inputs were compared with three surrounding original CAL files and the full original Tcal file. At the default smoothing settings, the effective Tpsr and all calibrated SCI values were exactly equal on the server (maximum difference 0 K). The separate calibration-only script and notebook additionally compare selected native temperatures and all gain coefficients with frozen original-input results. Small floating-point differences across platforms are allowed.

workflow_summary.json

Four completed stages, table dimensions, residual statistics and warnings.

table_manifest.json

Settings, output frequencies and paths to the saved spectral table.

stages/

Reference arrays, gain coefficients, RFI intervals, baseline fits/reports and disk-backed resampled spectra.

The unattended runner also saves calibration.png, baseline.png and table-spectrum.png. All four stages process 32 spectra. The default table is fully finite; RFI-fraction fields remain within [0,1]. The workflow does not run spatial gridding.

Reference quality. A few nonpositive Tpsr values occur at 1420.28808594 MHz (about 0.084% of AA and 0.048% of BB reference samples). That channel is inside the excluded H I gain interval and is not used as a gain anchor. These values are reported, not changed or automatically labelled RFI.

Real-data repeatability does not establish the true astronomical spectrum or absolute calibration accuracy. The calibration diagnostic plots native frequency as topocentric velocity. The baseline plots, viewer and final spectral table use the bundled real LSRK factors. Baseline and RFI quality still require scientific assessment.

08 / Parameter changes

Parameter tests and code modification

Time-smoothing parameters

python examples/minimal_calibration/calibrate.py \
  --output "$HOME/fish-calibration-no-smoothing" \
  --n-t-cal 1 --n-t-apply 1

Compare the reference curves, gain coefficients and spectra with the default result. Real references fluctuate, so changing the two filters can change the answer. The default regression comparison is skipped for non-default windows; the change is reported instead. With both filters disabled, a few nonfinite references appear at 1425.415 and 1426.514 MHz; the production fit rejects invalid anchors and the summary reports them. This does not make reference-quality checks optional.

Relevant source functions

Read rebuild_reference for the two time filters. Read frequency_gain for overlap averaging, exclusions, log fitting, clipping, and support checks. The package’s src/README.md maps the remaining modules and explains how to extend them.

The compact extract supports time-filter windows from 1 to 10. Larger windows need more surrounding CAL timestamps and must not be forced by disabling support checks. Preserve the released data and numerical reference when experimenting.

Scope of the full reduction workflow

Real reductions add input convention and reference-quality checks, observed RFI detection, consistent pointing/time/Doppler calculations, configurable baseline components, spectral resampling, and spatial gridding. This compact tutorial demonstrates the spectral stages; spatial gridding and full-observation quality assessment require a larger dataset. Native channel spacing is not a measurement of the instrumental line-spread function.