Metadata-Version: 2.4
Name: salsa-hi-toolkit
Version: 0.1.0
Summary: Unofficial beginner-friendly tools for SALSA 21-cm neutral-hydrogen spectra and Milky Way mapping
Author: Oliver
License: MIT
Keywords: SALSA,radio astronomy,21 cm,HI,Milky Way,amateur astronomy
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Education
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Astronomy
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: matplotlib>=3.7
Requires-Dist: scipy>=1.10
Dynamic: license-file

# SALSA-HI Toolkit

An **unofficial**, beginner-friendly Python toolkit for analysing 21-cm neutral-hydrogen (HI) spectra exported by the SALSA radio telescope and turning detected velocity components into a simple kinematic map of the Milky Way.

It is intended for students, schools and amateur radio astronomers who want to go from a folder of SALSA CSV files to useful plots and coordinates without rewriting the same pandas/scipy/matplotlib code every time.

> This project is not an official SALSA package and is not affiliated with the SALSA telescope team.

## What it does

- reads SALSA CSV files while preserving `#` metadata
- extracts Galactic longitude and latitude automatically
- converts VLSR from m/s to km/s
- smooths spectra with a Savitzky-Golay filter
- estimates noise robustly with a MAD-based estimator
- detects multiple significant HI peaks with prominence filtering
- batch-processes entire folders
- exports detected peaks to CSV
- estimates galactocentric radius using a simple flat rotation curve
- handles the inner-Galaxy near/far distance ambiguity explicitly
- converts cloud positions to Cartesian `(x, y, z)` coordinates
- plots individual spectra and a top-down Milky Way map
- offers both a Python API and a command-line interface

## Installation from source

```bash
git clone <your-repository-url>
cd salsa_hi_toolkit
python -m pip install -e .
```

Dependencies are installed automatically: NumPy, pandas, SciPy and Matplotlib.

## Fastest possible Python workflow

```python
from salsahi import map_folder

peaks, clouds = map_folder(
    "my_salsa_data",
    "results"
)
```

This creates:

```text
results/
├── detected_peaks.csv
├── hi_cloud_coordinates.csv
├── milky_way_hi_map.png
└── spectra/
    ├── observation_1_peaks.png
    ├── observation_2_peaks.png
    └── ...
```

## More control over peak detection

```python
from salsahi import PeakConfig, map_folder

config = PeakConfig(
    velocity_min_kms=-150,
    velocity_max_kms=100,
    smooth_window=11,
    polyorder=3,
    noise_factor=5.0,
    height_factor=3.0,
    min_distance_kms=8.0,
)

peaks, clouds = map_folder(
    "my_salsa_data",
    "results",
    config=config,
    show_spectra=True,
)
```

### The most useful settings

- `velocity_min_kms`, `velocity_max_kms`: velocity region where the detector is allowed to search.
- `noise_factor`: required peak prominence in estimated noise sigmas. Increase it to reject more weak/noisy peaks.
- `height_factor`: required height above the median baseline.
- `min_distance_kms`: minimum velocity separation between two accepted peaks.
- `smooth_window`: Savitzky-Golay window length. Avoid making this so large that neighbouring physical components merge.

## Analyse one file

```python
import matplotlib.pyplot as plt
from salsahi import load_spectrum, detect_peaks, plot_spectrum

spectrum = load_spectrum("observation.csv")
peaks = detect_peaks(spectrum)

print(peaks)
plot_spectrum(spectrum, peaks)
plt.show()
```

## Inspect a folder before analysing it

```python
from salsahi import summarize_folder

observations = summarize_folder("my_salsa_data")
print(observations)
```

This produces one row per observation with useful metadata such as Galactic longitude, latitude, integration time, telescope, azimuth, elevation and number of spectral samples.

## Batch analysis without mapping

```python
from salsahi import analyze_folder

peaks = analyze_folder(
    "my_salsa_data",
    show_plots=True,
)

peaks.to_csv("detected_peaks.csv", index=False)
```

## Command-line use

After installation:

```bash
salsahi my_salsa_data --output results --show
```

Custom detection settings:

```bash
salsahi my_salsa_data \
    --output results \
    --vmin -150 \
    --vmax 100 \
    --noise-factor 5 \
    --min-distance 8
```

## Coordinate convention

The top-down Cartesian map uses:

- Galactic Centre: `(0, 0, 0)`
- Sun: `(0, R0, 0)`
- positive `x`: direction corresponding to Galactic longitude `l = 90°` as viewed from the Sun

Default constants are:

```python
R0 = 8.5   # kpc
V0 = 220   # km/s
```

They can be changed:

```python
from salsahi import GalacticConstants

constants = GalacticConstants(
    r0_kpc=8.2,
    v0_kms=236,
)
```

## Important scientific limitation

The kinematic conversion is deliberately simple. It assumes a **flat Galactic rotation curve and circular gas motion**. Real HI contains streaming motions, expanding structures, turbulence and departures from circular rotation. Kinematic distances should therefore be treated as estimates, not direct geometric measurements.

### Inner-Galaxy ambiguity

For many sight lines inside the Solar circle, one measured radial velocity can correspond to two heliocentric distances. By default, this package does **not** silently choose one. It keeps both in:

- `distance_near_kpc`
- `distance_far_kpc`

and marks the row with:

- `distance_ambiguous = True`
- `kinematic_status = "near_far_ambiguous"`

The main `x_kpc`, `y_kpc` map coordinates are left blank until a choice is made. The toolkit also uses `kinematic_status = "no_geometric_solution"` when a measured velocity is incompatible with the simple circular flat-rotation model for that sight line. This is useful rather than an error: local gas motions, noise and non-circular motions can produce such measurements. If you knowingly want one branch:

```python
peaks, clouds = map_folder(
    "my_salsa_data",
    "results",
    ambiguity="near",
)
```

or:

```python
ambiguity="far"
```

## Expected SALSA file format

The loader accepts the normal SALSA format where metadata is stored as comment lines above the table:

```text
# Origin: SALSA
# Telescope: vale
# Date: 2026-05-13T16:32:58+00:00
# Coordinate system: galactic
# Target: 90.0000, 0.0000 deg
# Columns: frequency_hz,amplitude,vlsr_mps
frequency_hz,amplitude,vlsr_mps
1419150000,1.86,294049.8
...
```

Required data columns are:

- `frequency_hz`
- `amplitude`
- `vlsr_mps`

## Package layout

```text
src/salsahi/
├── __init__.py      public API
├── models.py        configuration/data classes
├── io.py            reading metadata and spectra
├── peaks.py         smoothing, noise estimation, peak detection
├── kinematics.py    radius, distance and coordinate calculations
├── plotting.py      spectrum and Milky Way plots
├── pipeline.py      simple end-to-end workflows
└── cli.py           command-line interface
```

## Contributing

Good beginner-friendly additions would include:

- interactive manual approval/rejection of automatically detected peaks
- uncertainty propagation
- alternative Galactic rotation curves
- tangent-point handling
- support for additional telescope export formats
- overlays of published spiral-arm models
- notebooks/tutorials for schools

Keep physical assumptions visible to the user rather than hiding them behind automatic choices.

## License

MIT. See `LICENSE`.
