Skip to content

Python API Reference

FLITS is usable as a library as well as through the browser. Everything the interface does goes through the same objects documented here, so an analysis scripted against this API and one performed interactively produce the same numbers.

For reproducing a saved analysis without writing code, see Headless Replay.

A minimal analysis

import json

from flits import BurstSession

session = BurstSession.from_file(
    "data/GBT-L/burst.fil",
    dm=527.65,
    sefd_jy=10.0,
    sefd_fractional_uncertainty=0.1,
)

session.set_crop_ms(80.0, 200.0)
session.set_event_ms(120.0, 128.0)
session.add_offpulse_ms(0.0, 80.0)
session.auto_mask_jess("auto")

measurements = session.compute_properties()
print(measurements.fluence_jyms, measurements.snr)

# Persist everything needed to reproduce this later.
with open("burst_flits_session.json", "w") as handle:
    json.dump(session.snapshot_dict(), handle, indent=2)

Session

flits.session.BurstSession dataclass

One burst, loaded and under analysis.

A session owns the dedispersed dynamic spectrum together with every decision made about it: the crop, the event window, the off-pulse regions used as the noise reference, the channel mask, the spectral extent, and the calibration inputs. Analyses are computed against that state and cached on the session, and are discarded automatically when a change invalidates them.

The whole of that state round-trips through to_snapshot and from_snapshot, which is what makes an analysis reproducible: the snapshot plus the original file and a compatible software environment permit supported calculations to be repeated, either in the interface or through flits replay. Software provenance identifies the environment of each cached analysis independently of the environment saving the snapshot.

Examples:

>>> session = BurstSession.from_file("burst.fil", dm=527.65, sefd_jy=10.0)
>>> session.set_event_ms(120.0, 128.0)
>>> session.add_offpulse_ms(0.0, 80.0)
>>> measurements = session.compute_properties()
>>> measurements.fluence_jyms is not None
True
Notes

A session is not thread-safe and is not intended to be shared between concurrent requests.

burst_file property

burst_file: str

Path of the burst file this session was opened from.

dm property

dm: float

Dispersion measure the data is currently dedispersed to, in pc cm^-3.

telescope property

telescope: str

Key of the telescope preset in use.

detected_telescope property

detected_telescope: str

Telescope preset detected from the file header, before any override.

tsamp property

tsamp: float

Sample interval of the loaded data, in seconds.

freqres property

freqres: float

Channel width of the loaded data, in MHz.

start_mjd property

start_mjd: float

MJD of the first sample in the read window.

plus_mjd_sec property

plus_mjd_sec: float

Offset of the read window from the file start, in seconds.

sefd property

sefd: float | None

System-equivalent flux density in Jy, or None when uncalibrated.

bw property

bw: float

Total bandwidth of the loaded data, in MHz.

npol property

npol: int

Number of polarizations used for calibration, after any override.

header_npol property

header_npol: int

Number of polarizations declared by the file header.

polarization_order property

polarization_order: str | None

Polarization ordering declared by the file header, if any.

freqs property

freqs: ndarray

Channel centre frequencies, in MHz.

total_time_bins property

total_time_bins: int

Number of time samples in the read window.

total_channels property

total_channels: int

Number of frequency channels.

tsamp_ms property

tsamp_ms: float

Sample interval of the loaded data, in milliseconds.

software_provenance

software_provenance() -> dict[str, Any]

Snapshot of software identities; unknown historical origins stay unknown.

from_file classmethod

from_file(bfile: str, dm: float, telescope: str | None = None, *, sefd_jy: float | None = None, npol_override: int | None = None, read_start_sec: float | None = None, read_end_sec: float | None = None, auto_mask_profile: str | None = 'auto', distance_mpc: float | None = None, redshift: float | None = None, sefd_fractional_uncertainty: float | None = None, distance_fractional_uncertainty: float | None = None, source_ra_deg: float | None = None, source_dec_deg: float | None = None, time_scale: str | None = None, observatory_longitude_deg: float | None = None, observatory_latitude_deg: float | None = None, observatory_height_m: float | None = None, polarization_basis: str | None = None) -> BurstSession

Open a burst file and build a session at the given DM.

The file's format is detected automatically and a matching telescope preset is applied unless telescope names one explicitly. The data is dedispersed to dm on load.

from_snapshot classmethod

from_snapshot(snapshot: AnalysisSessionSnapshot | dict[str, Any], *, loader: Callable[..., BurstSession] | None = None) -> BurstSession

Rebuild a session from a snapshot produced by to_snapshot.

Reopens the burst the snapshot names and restores every recorded selection. Pass loader to control how the burst file is opened -- the web layer uses this to enforce data-directory containment.

invalidate_results

invalidate_results() -> None

Discard cached burst measurements.

clear_width_analysis

clear_width_analysis() -> None

Discard the cached width analysis.

clear_dm_optimization

clear_dm_optimization() -> None

Discard the cached DM sweep result.

clear_spectral_analysis

clear_spectral_analysis() -> None

Discard the cached spectral analysis.

clear_temporal_structure

clear_temporal_structure() -> None

Discard the cached temporal-structure analysis.

clear_drift_analysis

clear_drift_analysis() -> None

Discard the cached sub-burst drift measurement.

Component regions and manual peaks feed the centroid estimator, so editing them invalidates a cached drift result even though they leave the other measurements alone.

clear_polarization

clear_polarization() -> None

Discard the cached polarization analysis.

The loaded Stokes cube is kept: it is expensive to read and stays valid under every selection change, because the session re-applies its own dispersion shift to it on use.

invalidate_analysis_state

invalidate_analysis_state() -> None

Discard every cached analysis, after a change that invalidates them all.

bin_to_ms

bin_to_ms(time_bin: int | float) -> float

Convert a time-sample index to milliseconds from the start of the window.

ms_to_bin

ms_to_bin(time_ms: float) -> int

Convert milliseconds from the start of the window to a time-sample index.

clamp_bin

clamp_bin(value: int, is_end: bool = False) -> int

Clamp a time-sample index into the valid range.

Pass is_end=True for an exclusive end index.

clamp_channel

clamp_channel(value: int) -> int

Clamp a channel index into the valid range.

freq_to_channel

freq_to_channel(freq_mhz: float) -> int

Return the channel index nearest to a frequency in MHz.

get_masked_crop

get_masked_crop(data: ndarray | None = None) -> np.ndarray

Return the cropped dynamic spectrum with masked channels removed.

get_display_crop

get_display_crop(data: ndarray | None = None) -> np.ndarray

Return the cropped dynamic spectrum at the current display decimation.

get_view

get_view() -> dict[str, Any]

Return the full session state the interface renders, as plain data.

reset_view

reset_view() -> None

Reset crop, selections and decimation to their initial state.

set_time_factor

set_time_factor(factor: int) -> None

Set the time decimation factor used for display and analysis.

set_freq_factor

set_freq_factor(factor: int) -> None

Set the frequency decimation factor used for display and analysis.

set_crop_ms

set_crop_ms(start_ms: float, end_ms: float) -> None

Set the crop window, in milliseconds from the start of the read window.

set_event_ms

set_event_ms(start_ms: float, end_ms: float) -> None

Set the event window that measurements integrate over.

add_region_ms

add_region_ms(start_ms: float, end_ms: float) -> None

Add a burst sub-region, used to separate components.

clear_regions

clear_regions() -> None

Remove every burst sub-region.

add_offpulse_ms

add_offpulse_ms(start_ms: float, end_ms: float) -> None

Add an off-pulse region, used as the noise reference.

clear_offpulse

clear_offpulse() -> None

Remove every off-pulse region.

add_peak_ms

add_peak_ms(time_ms: float) -> None

Mark a component peak at the given time, switching to manual peaks.

remove_peak_ms

remove_peak_ms(time_ms: float, tolerance_bins: int = 40) -> None

Remove the manually marked peak nearest the given time.

mask_channel_freq

mask_channel_freq(freq_mhz: float) -> None

Mask the channel nearest the given frequency.

mask_range_freq

mask_range_freq(low_freq_mhz: float, high_freq_mhz: float) -> None

Mask every channel in a frequency range.

undo_mask

undo_mask() -> None

Undo the most recent masking step.

reset_mask

reset_mask() -> None

Clear the channel mask entirely.

set_spectral_extent_freq

set_spectral_extent_freq(low_freq_mhz: float, high_freq_mhz: float) -> None

Set the frequency extent measurements are restricted to.

set_notes

set_notes(notes: str | None) -> None

Attach free-text notes, carried through snapshots and exports.

set_timing_metadata

set_timing_metadata(*, source_ra_deg: float | None = None, source_dec_deg: float | None = None, time_scale: str | None = None, observatory_longitude_deg: float | None = None, observatory_latitude_deg: float | None = None, observatory_height_m: float | None = None) -> None

Set the source position and observatory location used for barycentring.

auto_localize

auto_localize(*, detection_snr_threshold: float = 6.0, search_window_bins: tuple[int, int] | None = None, apply: bool = True, **kwargs: Any) -> BurstLocalization

Automatically localize the burst inside the current crop.

Runs the iterative time/frequency search on the masked crop at native resolution and, when a detection is found and apply is True, updates the event window, spectral extent, off-pulse windows, and peak selection. The crop itself is unchanged.

auto_mask_jess

auto_mask_jess(profile: str | None = None) -> None

Mask interference automatically using the jess statistical tests.

set_dm

set_dm(new_dm: float) -> None

Retune the dispersion measure of the loaded data.

The shift is computed as an absolute solution relative to the DM the data was loaded at, and only the difference from the currently applied shift is moved. Rounding therefore happens once per target DM rather than once per step, so stepping out along a DM sweep and back returns the data to its original alignment exactly.

apply_best_dm

apply_best_dm() -> DmOptimizationResult

Adopt the best DM found by the last sweep and re-dedisperse to it.

compute_widths

compute_widths() -> WidthAnalysisSummary

Measure the burst width by every supported method.

accept_width_result

accept_width_result(method: str) -> WidthAnalysisSummary

Adopt one width method's result as the reported width.

optimize_dm

optimize_dm(center_dm: float, half_range: float, step: float, metric: str = 'integrated_event_snr') -> DmOptimizationResult

Sweep DM around a centre value and score each trial.

metric selects the criterion: integrated event signal-to-noise, or the structure-maximizing DM_phase score.

compute_properties

compute_properties() -> BurstMeasurements

Measure the burst and cache the result.

Produces width, peak flux, fluence and signal-to-noise, together with the provenance and uncertainty classification of each quantity.

fit_model

fit_model(config_data: dict[str, Any] | None = None) -> BurstMeasurements

Fit a burst model to the selected event using fitburst.

run_temporal_structure_analysis

run_temporal_structure_analysis(segment_length_ms: float) -> TemporalStructureResult

Run temporal-structure and power-spectral analysis on the selection.

run_spectral_analysis

run_spectral_analysis(segment_length_ms: float) -> SpectralAnalysisResult

Run averaged spectral analysis on the selection.

run_drift_analysis

run_drift_analysis(*, settings: DriftAnalysisSettings | None = None, dm_uncertainty_pc_cm3: float | None = None) -> DriftAnalysisResult

Measure the sub-burst drift rate of the current selection.

Parameters:

Name Type Description Default
settings DriftAnalysisSettings | None

Fit-region and Monte-Carlo controls. When omitted the session's stored drift_settings are used; when supplied they replace them.

None
dm_uncertainty_pc_cm3 float | None

1-sigma DM uncertainty to fold into the drift uncertainty. It is stored in drift_settings so a snapshot replay reproduces the same classification. When neither this nor the stored value is set, the DM sweep's own uncertainty is used if it still describes the applied DM; pass settings with dm_uncertainty_pc_cm3=None to go back to that fallback.

None

polarization_capability

polarization_capability() -> dict[str, Any]

Report whether this burst can support an in-session RM measurement.

Answers the three questions separately -- does the reader have a full-Stokes path, does the file carry four products, and is it known which four -- because they call for different responses: a different file, a different file, and a stated basis.

load_stokes_cube

load_stokes_cube(*, reload: bool = False) -> np.ndarray

Return the burst as a (4, channels, time) Stokes I/Q/U/V cube.

The cube is read once, at the DM the file was opened with, and cached. The session's accumulated dispersion shift is applied on every call, so the returned cube is aligned with data even after the DM has been retuned -- and retuning does not force a re-read.

set_polarization_settings

set_polarization_settings(settings: PolarizationSettings | dict[str, Any]) -> PolarizationSettings

Replace the polarization settings, discarding any stale result.

run_polarization_analysis

run_polarization_analysis(settings: PolarizationSettings | dict[str, Any] | None = None) -> PolarizationAnalysisResult

Measure the rotation measure from this session's own selections.

Integrates Stokes Q and U over the session's event window, takes the noise from its off-pulse regions, drops the channels its mask and spectral extent exclude, and runs RM synthesis on what is left. Nothing is imported and nothing is re-selected: the RM is measured against the same burst definition as every other number in the session.

to_snapshot

to_snapshot() -> AnalysisSessionSnapshot

Capture the full session state as a snapshot object.

snapshot_dict

snapshot_dict() -> dict[str, Any]

Capture the full session state as a JSON-compatible dict.

export_results

export_results(*, session_id: str, include: list[str] | tuple[str, ...] | None = None, plot_formats: list[str] | tuple[str, ...] | None = None, window_formats: list[str] | tuple[str, ...] | None = None, window_resolutions: list[str] | tuple[str, ...] | None = None) -> ExportManifest

Build an export bundle and retain it for download.

preview_export_results

preview_export_results(*, include: list[str] | tuple[str, ...] | None = None, plot_formats: list[str] | tuple[str, ...] | None = None, window_formats: list[str] | tuple[str, ...] | None = None, window_resolutions: list[str] | tuple[str, ...] | None = None) -> ExportPreview

Describe the bundle export_results would build, without building it.

get_export_manifest

get_export_manifest(export_id: str) -> ExportManifest

Return the manifest of a previously built export bundle.

get_export_artifact

get_export_artifact(export_id: str, artifact_name: str) -> tuple[ExportArtifact, bytes]

Return one artifact from a previously built bundle, as (metadata, bytes).

Input

flits.io

Input layer: format detection and burst loading.

Readers are discovered through flits.readers entry points, so a third party can add a format without modifying FLITS. See flits.io.reader for the protocol a reader must satisfy.

inspect_filterbank

inspect_filterbank(path: str | Path) -> FilterbankInspection

load_filterbank_data

load_filterbank_data(path: str | Path, config: ObservationConfig, inspection: FilterbankInspection | None = None) -> tuple[np.ndarray, FilterbankMetadata]

load_stokes_data

load_stokes_data(path: str | Path, config: ObservationConfig, inspection: FilterbankInspection | None = None) -> tuple[np.ndarray, FilterbankMetadata]

Load a full-Stokes I/Q/U/V cube for path.

Raises PolarizationUnavailableError if the matching reader has no full-Stokes path, or if the file itself cannot provide one.

reader_supports_stokes

reader_supports_stokes(reader: object) -> bool

True if reader implements the optional full-Stokes path.

list_readers

list_readers() -> list[BurstReader]

flits.io.reader.BurstReader

Bases: Protocol

A reader for one or more input formats.

Implementations must be cheap to instantiate (zero-arg constructor) because they may be created once per process. All I/O happens inside inspect() / load() — no state is retained between calls.

sniff

sniff(path: Path) -> bool

Return True if this reader can handle the given file.

Must be fast (magic-byte / root-attr check only) and must not raise on unrelated files — return False instead.

flits.io.reader.StokesBurstReader

Bases: BurstReader, Protocol

A reader that can additionally return full-Stokes data.

load_stokes returns (cube, metadata) where cube has shape (4, channels, time) in I/Q/U/V order and shares the time and frequency axes of the Stokes I array load returns for the same file and config -- same dedispersion, same read window, same channel order. That equivalence is what lets a session apply its event window, off-pulse regions and channel mask to the cube without re-deriving any of them.

Implementations raise :class:~flits.io.errors.PolarizationUnavailableError when the file has fewer than four polarization products or when the polarization basis is unknown, rather than guessing.

flits.io.reader.FilterbankInspection dataclass

stokes_available property

stokes_available: bool

True when this file can yield a full Stokes I/Q/U/V cube.

flits.stokes

Conversion of raw four-product polarization data into Stokes I/Q/U/V.

Burst files carry four polarization products, but which four is not something FLITS can infer from the numbers alone. A backend may write Stokes directly, or it may write the coherency products of a linear or a circular feed. The three cases differ only by which pair of products carries the linear polarization, so guessing wrong silently swaps Stokes V into Q and produces an RM that is wrong without looking wrong.

This module therefore keeps the basis explicit. Readers resolve it from the file header where the format records it (PSRFITS POL_TYPE plus FD_POLN), from the telescope preset where the instrument is known, or from an operator override -- and refuse to build a Stokes cube when none of those settle the question.

normalize_polarization_basis

normalize_polarization_basis(value: object) -> str | None

Return the canonical basis name for value, or None if unset.

Raises PolarizationBasisError for a non-empty value that names no known basis, so a typo in a preset or an API request fails loudly.

basis_from_psrfits

basis_from_psrfits(pol_type: object, feed_polarization: object = None) -> str | None

Map a PSRFITS POL_TYPE (with FD_POLN) onto a FLITS basis.

AABBCRCI names the coherency products without saying which feed produced them, so the receiver's FD_POLN decides. A file that declares neither is left unresolved rather than assumed.

stokes_from_products

stokes_from_products(products: ndarray, basis: str) -> np.ndarray

Convert four polarization products into a Stokes I/Q/U/V cube.

products must have the four products on its first axis; the remaining axes are carried through untouched. The returned array is float32 and has the same trailing shape.

The coherency conversions follow the PSRFITS/PSRCHIVE convention: a linear feed gives I=AA+BB, Q=AA-BB, U=2CR, V=2CI and a circular feed gives I=AA+BB, V=AA-BB, Q=2CR, U=2CI.

Analysis

flits.analysis.localization.localize_burst

localize_burst(masked: ndarray, *, detection_snr_threshold: float = DETECTION_SNR_THRESHOLD, extent_exit_sn: float = EXTENT_EXIT_SN, spectrum_exit_sn: float = SPECTRUM_EXIT_SN, full_band_fraction: float = FULL_BAND_FRACTION, max_iterations: int = MAX_ITERATIONS, max_width_bins: int | None = None, min_offpulse_bins: int = MIN_OFFPULSE_BINS, search_window_bins: tuple[int, int] | None = None) -> BurstLocalization

Localize a burst in a masked dynamic spectrum (channels x time).

Masked channels must be NaN rows. Returns bins relative to the input array. search_window_bins optionally restricts the matched-filter peak location to a known half-open time interval; event boundaries may extend outside it so burst wings are retained. When nothing crosses detection_snr_threshold the status is "no_detection".

flits.measurements.compute_burst_measurements

compute_burst_measurements(*, burst_name: str, dm: float, start_mjd: float, read_start_sec: float, crop_start_bin: int, tsamp_ms: float, freqres_mhz: float, freqs_mhz: ndarray, masked: ndarray, event_rel_start: int, event_rel_end: int, spec_lo: int, spec_hi: int, peak_bins_abs: Sequence[int], burst_regions_abs: Sequence[tuple[int, int]], manual_selection: bool, manual_peak_selection: bool, sefd_jy: float | None, npol: int, distance_mpc: float | None, redshift: float | None, sefd_fractional_uncertainty: float | None, distance_fractional_uncertainty: float | None, masked_channels: Sequence[int], offpulse_regions_rel: Sequence[tuple[int, int]] | None = None, noise_settings: NoiseEstimateSettings | None = None, width_results: Sequence[WidthResult] | None = None, accepted_width: AcceptedWidthSelection | None = None, time_axis_ms: ndarray | None = None, timing_context: TimingContext | None = None) -> BurstMeasurements

Measure a burst from a prepared selection.

Integrates the event window against the off-pulse noise reference and returns width, peak flux, fluence, signal-to-noise and, when a distance is supplied, isotropic energy.

Every quantity carries its own uncertainty classification. A value is reported as formal_1sigma only when the systematic inputs it needs were supplied -- notably an SEFD fractional uncertainty for flux-like quantities. Without them the value is statistical_only and is flagged as not publishable, rather than being presented as a complete uncertainty.

Returns:

Type Description
BurstMeasurements

Measured quantities together with their provenance, uncertainty detail and any diagnostic flags raised during measurement.

flits.analysis.dm_optimization.optimize_dm_trials

optimize_dm_trials(*, data: ndarray, current_dm: float, freqs_mhz: ndarray, tsamp_sec: float, score_data: ndarray | None = None, score_current_dm: float | None = None, score_freqs_mhz: ndarray | None = None, score_tsamp_sec: float | None = None, center_dm: float, half_range: float, step: float, metric_input_builder: Callable[[ndarray], DMMetricInput], residuals: Callable[[ndarray], tuple[ndarray, ndarray, ndarray, str]], provenance: DmOptimizationProvenance, metric: str = 'integrated_event_snr') -> DmOptimizationResult

Run a DM trial sweep and return the optimized DM result.

Parameters:

Name Type Description Default
data ndarray

Native-resolution dynamic spectrum with shape (n_channels, n_time). This array is used for residual diagnostics at the applied and best-fit DMs.

required
current_dm float

DM currently applied to data, in pc cm^-3.

required
freqs_mhz ndarray

Native frequency axis in MHz matching data rows.

required
tsamp_sec float

Native sampling interval in seconds.

required
score_data ndarray | None

Optional waterfall used only for scoring. DMphase passes the current reduced grid here so the score respects the user's analysis resolution. Metrics that should score native data leave this as None.

None
score_current_dm float | None

Applied DM for score_data. Defaults to current_dm.

None
score_freqs_mhz ndarray | None

Frequency axis in MHz matching score_data rows. Defaults to freqs_mhz.

None
score_tsamp_sec float | None

Sampling interval in seconds for score_data. Defaults to tsamp_sec.

None
center_dm float

Trial grid controls, all in pc cm^-3.

required
half_range float

Trial grid controls, all in pc cm^-3.

required
step float

Trial grid controls, all in pc cm^-3.

required
metric_input_builder Callable[[ndarray], DMMetricInput]

Callback that converts each trial-dedispersed scoring waterfall into a :class:DMMetricInput for the selected metric.

required
residuals Callable[[ndarray], tuple[ndarray, ndarray, ndarray, str]]

Callback that computes subband arrival residuals for a waterfall and returns (freqs_mhz, arrivals_ms, residuals_ms, status).

required
provenance DmOptimizationProvenance

Serialized context describing the selection and effective analysis resolution used for the sweep.

required
metric str

Registered metric key. Supported values are defined by DM_METRIC_REGISTRY.

'integrated_event_snr'

Returns:

Type Description
DmOptimizationResult

Score curve, sampled and refined DM estimates, residual diagnostics, settings, and provenance.

Raises:

Type Description
ValueError

If the metric key is unsupported, the trial grid is invalid, a metric returns an invalid score shape, or no finite score can be computed for the current selection.

flits.analysis.morphology.compute_width_analysis

compute_width_analysis(*, selected_profile: ndarray, time_axis_ms: ndarray, event_rel_start: int, event_rel_end: int, tsamp_ms: float, noise_summary: NoiseEstimateSummary, offpulse_bins: ndarray, settings: WidthAnalysisSettings | None = None, event_window_ms: list[float], spectral_extent_mhz: list[float], offpulse_windows_ms: list[list[float]], masked_channels: list[int], effective_bandwidth_mhz: float | None, existing_accepted_method: str | None = None, extra_flags: Sequence[str] = ()) -> WidthAnalysisSummary

Compute the full set of morphology-based burst-width measurements.

Parameters:

Name Type Description Default
selected_profile ndarray

Selected-band time profile for the current crop on the same scale used to define the off-pulse baseline.

required
time_axis_ms ndarray

Time axis in milliseconds matching selected_profile.

required
event_rel_start int

Half-open event window in profile-bin coordinates.

required
event_rel_end int

Half-open event window in profile-bin coordinates.

required
tsamp_ms float

Effective time resolution of the profile in milliseconds.

required
noise_summary NoiseEstimateSummary

Off-pulse noise estimate and warning flags associated with the current selection.

required
offpulse_bins ndarray

Off-pulse bins used to bootstrap per-trial baseline/noise estimates.

required
settings WidthAnalysisSettings | None

Width-analysis settings. If omitted, defaults from :class:WidthAnalysisSettings are used.

None
event_window_ms list[float]

Event-window bounds in milliseconds for provenance.

required
spectral_extent_mhz list[float]

Selected spectral extent in MHz for provenance.

required
offpulse_windows_ms list[list[float]]

Off-pulse windows in milliseconds for provenance.

required
masked_channels list[int]

Masked channel indices for provenance.

required
effective_bandwidth_mhz float | None

Effective unmasked bandwidth in MHz, if known.

required
existing_accepted_method str | None

Previously user-accepted width method. If present and still available, it is preserved as the accepted width.

None
extra_flags Sequence[str]

Additional quality flags propagated to every method result.

()

Returns:

Type Description
WidthAnalysisSummary

Structured width results for all registered methods plus the accepted width selection and the noise summary used to derive uncertainties.

Notes

All width values and uncertainties are reported in milliseconds.

flits.analysis.temporal.core.run_temporal_structure_analysis

run_temporal_structure_analysis(*, event_series: ndarray, tsamp_ms: float, segment_length_ms: float, noise_sigma: float, event_window_ms: tuple[float, float], spectral_extent_mhz: tuple[float, float], model_fit_widths_ms: ndarray | None = None, offpulse_series_runs: Sequence[ndarray] | None = None, backend_loader: Callable[[], tuple[type[Any] | None, type[Any] | None, str | None]] = _load_stingray_backend) -> TemporalStructureResult

Run temporal-structure analysis on a selected event series.

Parameters:

Name Type Description Default
event_series ndarray

One-dimensional selected-band event series for the current event window.

required
tsamp_ms float

Sampling interval in milliseconds.

required
segment_length_ms float

Requested segment length for the averaged PSD, in milliseconds.

required
noise_sigma float

Off-pulse noise standard deviation used to normalize matched-filter and wavelet responses.

required
event_window_ms tuple[float, float]

Event-window bounds in milliseconds for provenance.

required
spectral_extent_mhz tuple[float, float]

Selected spectral extent in MHz for provenance.

required
model_fit_widths_ms ndarray | None

Optional model-fit component widths in milliseconds. When provided, the smallest positive width is recorded as a model-based cross-check.

None
offpulse_series_runs Sequence[ndarray] | None

Optional contiguous off-pulse time-series runs used to estimate a noise PSD on the same segment grid.

None
backend_loader Callable[[], tuple[type[Any] | None, type[Any] | None, str | None]]

Dependency-injection hook for loading Stingray-compatible backend classes.

_load_stingray_backend

Returns:

Type Description
TemporalStructureResult

Structured result containing the raw periodogram, averaged PSD, minimum-timescale scan products, optional power-law fit, crossover frequency estimate, and optional noise PSD. status is "ok" on success and otherwise one of "insufficient_data", "insufficient_time_bins", "invalid_segment_length", "stingray_unavailable", or "stingray_failed".

Notes

The primary minimum-structure estimate comes from the first matched-filter scale whose boxcar or Gaussian response exceeds the familywise-corrected threshold. The wavelet estimate is reported separately from the Mexican-hat scan.

flits.analysis.spectral.core.run_averaged_spectral_analysis

run_averaged_spectral_analysis(*, event_series: ndarray, tsamp_ms: float, segment_length_ms: float, event_window_ms: tuple[float, float], spectral_extent_mhz: tuple[float, float], offpulse_series_runs: Sequence[ndarray] | None = None, backend_loader: Callable[[], tuple[type[Any] | None, type[Any] | None, str | None]] = _load_stingray_backend) -> SpectralAnalysisResult

Run averaged spectral analysis on a selected event time series.

Parameters:

Name Type Description Default
event_series ndarray

One-dimensional selected-band event series. The analysis uses the provided samples as a uniformly sampled light curve.

required
tsamp_ms float

Sampling interval in milliseconds.

required
segment_length_ms float

Requested segment length for the averaged power spectrum, in milliseconds.

required
event_window_ms tuple[float, float]

Event-window bounds in milliseconds for provenance.

required
spectral_extent_mhz tuple[float, float]

Selected spectral extent in MHz for provenance.

required
offpulse_series_runs Sequence[ndarray] | None

Optional contiguous off-pulse time-series runs. When provided, FLITS computes a noise PSD using the same segment size and backend.

None
backend_loader Callable[[], tuple[type[Any] | None, type[Any] | None, str | None]]

Dependency-injection hook for loading Stingray-compatible Lightcurve and AveragedPowerspectrum classes.

_load_stingray_backend

Returns:

Type Description
SpectralAnalysisResult

Structured PSD result. status is "ok" on success and otherwise one of "insufficient_data", "insufficient_time_bins", "invalid_segment_length", "stingray_unavailable", or "stingray_failed".

Notes

The returned frequency axis is in Hz, the PSD uses Stingray's norm="none" convention, and any fitted power-law parameters describe the averaged event PSD rather than the off-pulse noise estimate.

flits.analysis.temporal.multiscale.haar_excess_power

haar_excess_power(event_profile: ndarray | Sequence[float], offpulse_runs: Sequence[ndarray | Sequence[float]], *, tsamp_ms: float, scales_bins: Sequence[int] | ndarray | None = None, min_coefficients: int = MIN_COEFFICIENTS) -> HaarExcessPowerResult

Measure temporal signal power above an empirical off-pulse floor.

Parameters:

Name Type Description Default
event_profile ndarray | Sequence[float]

One-dimensional on-burst time profile.

required
offpulse_runs Sequence[ndarray | Sequence[float]]

Contiguous off-pulse profile sections. Runs are never joined across a gap, so artificial Haar coefficients are not created at boundaries.

required
tsamp_ms float

Native or effective sampling interval in milliseconds.

required
scales_bins Sequence[int] | ndarray | None

Optional positive scales. By default FLITS uses dyadic scales up to a quarter of the event length.

None
min_coefficients int

Minimum event and pooled off-pulse coefficient count retained at each scale.

MIN_COEFFICIENTS
Notes

characteristic_scale_ms is the excess-power-weighted geometric mean scale. It is an aggregate descriptor, not a minimum resolvable timescale. Its selection dependence should still be assessed with injection/recovery tests when comparing a burst population.

flits.analysis.temporal.multiscale.excess_power_fraction_below

excess_power_fraction_below(result: HaarExcessPowerResult, max_scale_ms: float) -> float | None

Fraction of positive excess power at scales no larger than a limit.

flits.analysis.drift.run_drift_analysis

run_drift_analysis(inputs: DriftAnalysisInputs, settings: DriftAnalysisSettings | None = None) -> DriftAnalysisResult

Measure the sub-burst drift rate of the selected event window.

Parameters:

Name Type Description Default
inputs DriftAnalysisInputs

Waterfall, axes, selection bounds and DM state; see :class:DriftAnalysisInputs.

required
settings DriftAnalysisSettings | None

Fit-region, Monte-Carlo and seeding controls. Defaults are used when omitted.

None

Returns:

Type Description
DriftAnalysisResult

status is "ok" when the 2D-ACF fit converged, and otherwise one of "insufficient_time_bins", "insufficient_channels", "insufficient_signal", "invalid_axes" or "fit_failed". The component-centroid estimator reports separately through component_drift_status and never blocks the primary result.

Notes

The statistical uncertainty comes from a seeded Monte Carlo: each trial adds an independent noise realisation at the measured per-channel off-pulse sigma and repeats the autocorrelation and the fit. That propagates the correlation between neighbouring autocorrelation pixels, which the fit covariance alone does not; the covariance value is kept alongside as a diagnostic. The result is publishable only when a DM uncertainty was supplied, because the DM systematic is usually the larger term.

flits.analysis.drift.DriftAnalysisInputs dataclass

Everything the drift measurement needs from the session layer.

Parameters:

Name Type Description Default
waterfall ndarray

Dedispersed dynamic spectrum on the reduced analysis grid, shaped (n_channels, n_time). Masked channels and samples are NaN.

required
time_axis_ms ndarray

Time axis of waterfall in milliseconds.

required
freqs_mhz ndarray

Channel centre frequencies in MHz. Either ordering is accepted; the signed channel spacing is what fixes the sign of the drift rate.

required
event_rel_start int

Half-open event-window bounds as column indices into waterfall.

required
event_rel_end int

Half-open event-window bounds as column indices into waterfall.

required
spec_lo int

Inclusive selected-channel bounds as row indices into waterfall.

required
spec_hi int

Inclusive selected-channel bounds as row indices into waterfall.

required
offpulse_bins ndarray

Column indices used as the off-pulse reference for per-channel baselines and per-channel noise.

required
component_windows Sequence[tuple[str, tuple[int, int]]]

(label, (start_bin, end_bin)) pairs for the component-centroid estimator, as half-open column indices into waterfall.

()
dm_pc_cm3 float

DM the waterfall is dedispersed at, in pc cm^-3.

required
dm_uncertainty_pc_cm3 float | None

1-sigma DM uncertainty, when one is known. Supplying it is what turns the drift rate from statistical-only into a publishable measurement.

None

flits.models.DriftAnalysisSettings dataclass

Inputs that control the sub-burst drift measurement.

normalized

normalized() -> DriftAnalysisSettings

Return settings that are finite and safe to execute or replay.

These values can arrive through the actions API or an imported session snapshot. Normalizing them in the shared model keeps the browser, headless replay, and direct Python entry point on the same bounded behaviour.

flits.models.DriftAnalysisResult dataclass

Sub-burst drift rate measured from the selected event window.

The drift rate is dnu/dt in MHz per millisecond: negative for the downward "sad trombone" drift seen in repeaters. drift_rate_mhz_per_ms comes from a rotated two-dimensional Gaussian fitted to the mask-corrected 2D autocorrelation of the dynamic spectrum. component_* fields hold the independent component-centroid regression, which is only populated when the session defines at least two burst components.

flits.analysis.drift.time_frequency_slope

time_frequency_slope(sigma_time_ms: float, sigma_freq_mhz: float, correlation: float) -> float

Return the time-on-frequency regression slope dt/dnu in ms/MHz.

This is rho sigma_t / sigma_nu, not the reciprocal of the drift rate. The two agree only for a perfectly correlated ridge, and it is this one -- not the drift rate -- that a DM error displaces linearly.

flits.analysis.drift.dm_slope_sensitivity

dm_slope_sensitivity(reference_frequency_mhz: float) -> float

Return d(dt/dnu)/d(DM) in ms/MHz per pc cm^-3.

Differentiating the dispersion delay t(nu) = k DM nu^-2 gives -2 k nu^-3. This is the one quantity a DM error moves linearly: a dedispersion error adds g (nu - nu_ref) to every arrival time, which shifts the time-on-frequency regression slope by exactly g and leaves the frequency spread untouched.

flits.analysis.drift.drift_dm_sensitivity

drift_dm_sensitivity(sigma_time_ms: float, sigma_freq_mhz: float, correlation: float, reference_frequency_mhz: float) -> float

Return d(drift)/d(DM) in MHz/ms per pc cm^-3.

A DM error shears the burst covariance, Sigma_{t,nu} -> Sigma_{t,nu} + g Sigma_{nu,nu} and Sigma_{t,t} -> Sigma_{t,t} + 2 g Sigma_{t,nu} + g^2 Sigma_{nu,nu}. Differentiating drift = Sigma_{t,nu} / Sigma_{t,t} through that shear leaves (sigma_nu / sigma_t)^2 (1 - 2 rho^2) times the slope sensitivity. The factor (1 - 2 rho^2) is why the naive d(1/drift)/d(DM) answer is wrong for anything short of a perfectly correlated ridge: it changes sign at rho^2 = 1/2, where the drift rate is momentarily insensitive to the DM.

flits.analysis.drift.dm_equivalent_of_slope

dm_equivalent_of_slope(slope_ms_per_mhz: float, reference_frequency_mhz: float) -> float

Return the DM error that would on its own produce this dt/dnu.

This is the honest statement of the drift/DM degeneracy: if this number is smaller than the DM uncertainty, the measured drift is not distinguishable from a dedispersion error. It is positive when the burst is tilted the way an under-dedispersed burst is, so adding it to the applied DM is what removes the tilt.

Polarization

flits.models.PolarizationSettings dataclass

Operator-controlled inputs to an in-session polarization analysis.

calibration_confirmed is deliberately not a default-true convenience: an RM measured from uncalibrated Q/U is a number, not a measurement, and the workflow refuses to call it calibrated until someone states that the polarization calibration of this data has been established.

flits.models.PolarizationAnalysisResult dataclass

A polarization analysis run against a session's own selections.

Holds both halves of the analysis: the channelized Q/U spectrum integrated over the session's event window against its off-pulse regions, and the RM synthesis computed from that spectrum. Keeping them together is what makes the RM reproducible -- the spectrum records which channels survived the mask and the S/N cut, and the provenance records which four products the file held and what established that.

flits.analysis.polarization.extract_normalized_linear_spectrum

extract_normalized_linear_spectrum(*, stokes_iquv: ndarray, freqs_mhz: ndarray, event_bins: tuple[int, int], offpulse_regions: Iterable[tuple[int, int]], channel_mask: ndarray | None = None, spectral_channels: tuple[int, int] | None = None, channel_width_mhz: float | None = None, min_linear_snr: float = 5.0, calibration_status: str = 'unknown') -> IntegratedPolarizationSpectrum

Integrate a burst and prepare weighted Q/L and U/L spectra.

Noise is estimated from non-overlapping, event-length integrations within the explicit off-pulse regions. This carries native time correlation into the channel uncertainties more faithfully than dividing a per-sample RMS by sqrt(N). The normalization supports calibrated linear-polarization Q/U fitting without assuming a per-sample white-noise correction.

flits.analysis.polarization.run_rm_synthesis

run_rm_synthesis(*, freqs_mhz: ndarray, stokes_q: ndarray, stokes_u: ndarray, sigma_q: ndarray | float | None = None, sigma_u: ndarray | float | None = None, channel_width_mhz: ndarray | float | None = None, phi_min_rad_m2: float | None = None, phi_max_rad_m2: float | None = None, phi_step_rad_m2: float | None = None, clean: bool = False, clean_gain: float = 0.1, clean_threshold_sigma: float = 3.0, clean_max_iterations: int = 1000) -> RMSynthesisResult

Compute a weighted dirty Faraday spectrum and optional RM-CLEAN product.

Q and U must be channelized spectra on the supplied frequency centres. If per-channel uncertainties are available they drive both inverse-variance weighting and the propagated Faraday-spectrum noise. Otherwise FLITS uses equal weights and estimates noise from the residual of the best thin model.

Signal primitives

flits.signal

Signal-processing primitives: dedispersion, normalization and decimation.

dedisperse

dedisperse(data: ndarray, dm: float, freqs_mhz: ndarray, tsamp_sec: float, *, fill_value: float | None = None) -> np.ndarray

Dedisperse a dynamic spectrum to the top of the band.

Parameters:

Name Type Description Default
data ndarray

Dynamic spectrum with shape (n_channels, n_time).

required
dm float

Dispersion measure in pc cm^-3, relative to the current dedispersion of data.

required
freqs_mhz ndarray

Channel centre frequencies in MHz.

required
tsamp_sec float

Sample interval in seconds.

required
fill_value float | None

What to put in samples vacated by the shift. None (the default) wraps them around the time axis, which is lossless and exactly reversible; that suits repeated in-place retuning of an already-loaded session. Pass a value (readers pass 0.0) for a one-shot dedispersion, where wrapped samples would otherwise fold burst power into the opposite edge of the read window and contaminate the off-pulse statistics computed there.

None

Returns:

Type Description
ndarray

The dedispersed dynamic spectrum, same shape and dtype as data.

dedispersion_shift_bins

dedispersion_shift_bins(dm: float, freqs_mhz: ndarray, tsamp_sec: float) -> np.ndarray

Return the per-channel sample shift that dedisperses to the top of band.

Shifts are rounded to whole samples, so the achievable DM resolution is limited by the time resolution of the data. dedispersion_bin_resolution reports the smallest DM step that moves the band edge by one sample.

Parameters:

Name Type Description Default
dm float

Dispersion measure in pc cm^-3, relative to the current dedispersion of data.

required
freqs_mhz ndarray

Channel centre frequencies in MHz.

required
tsamp_sec float

Sample interval in seconds.

required

Returns:

Type Description
ndarray

Integer sample shift for each channel, positive towards later times.

dedispersion_edge_bins

dedispersion_edge_bins(dm: float, freqs_mhz: ndarray, tsamp_sec: float) -> tuple[int, int]

Return how many samples at each end are affected by the dedispersion shift.

These are the samples whose content came from outside the read window. They are either wrapped-around data or fill values depending on how dedisperse was called, and in both cases they are not valid burst samples.

Returns:

Type Description
tuple of int

(leading, trailing) sample counts to treat as invalid.

dedispersion_bin_resolution

dedispersion_bin_resolution(freqs_mhz: ndarray, tsamp_sec: float) -> float

Return the smallest DM step that shifts the lowest channel by one sample.

DM trials finer than this produce bit-identical dedispersed data, so a sweep stepped below it wastes work and reports a spuriously smooth score curve.

shift_channels

shift_channels(data: ndarray, shift_bins: ndarray, *, fill_value: float | None = None) -> np.ndarray

Apply a per-channel integer sample shift to a dynamic spectrum.

Separated from dedisperse so a session can retune its dispersion by the exact difference between two absolute shift solutions, rather than by re-rounding an incremental DM step each time.

normalize

normalize(ds: ndarray, offpulse: ndarray) -> np.ndarray

normalize_stokes

normalize_stokes(cube: ndarray, offpulse: ndarray) -> np.ndarray

Baseline and scale a Stokes cube without distorting its polarization.

Each Stokes parameter has its own off-pulse median removed, but all four are divided by the same per-channel scale, taken from the off-pulse standard deviation of Stokes I. Scaling the four independently would rescale Q/I and U/I channel by channel and corrupt every polarization fraction and every Faraday rotation measured from them; using one scale per channel only flattens the bandpass, exactly as normalize does for Stokes I alone.

Parameters:

Name Type Description Default
cube ndarray

Stokes data with shape (4, n_channels, n_time) in I/Q/U/V order.

required
offpulse ndarray

Off-pulse samples with shape (4, n_channels, n_offpulse).

required

block_reduce_mean

block_reduce_mean(arr: ndarray, tfac: int = 1, ffac: int = 1) -> np.ndarray

radiometer

radiometer(tsamp_ms: float, bw_mhz: float, npol: int, sefd_jy: float) -> float

Configuration

flits.settings

Observation configuration and telescope presets.

Presets carry the instrument-specific values -- SEFD, polarization count, observatory location -- that calibrated measurements depend on, and are matched against file headers automatically where possible.

ObservationConfig dataclass

TelescopePreset dataclass

available_presets

available_presets() -> list[TelescopePreset]

detect_preset

detect_preset(telescope_id: int | None, machine_id: int | None, *, telescope_name: str | None = None, schema_version: str | None = None, freq_lo_mhz: float | None = None, freq_hi_mhz: float | None = None) -> tuple[str, str]

get_preset

get_preset(preset_key: str | None = None) -> TelescopePreset

Command line

flits.cli

Command line entry point for FLITS.

flits starts the browser interface. flits replay re-runs a saved session snapshot without a browser, which is what makes the reproducibility claim checkable: the snapshot records the selected analysis state, and replaying it recomputes the measurements from the original data file.

Invoking flits with server options and no subcommand still starts the server, so existing commands and container entrypoints keep working.

main

main(argv: Sequence[str] | None = None) -> int

Dispatch a FLITS subcommand, defaulting to the server.

replay

replay(argv: Sequence[str]) -> int

Run the flits replay command. Returns the process exit code.

serve

serve(argv: Sequence[str]) -> int

Run the flits serve command (also the default). Returns the exit code.