Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ output/
# Build artifacts
dist/
build/
site/
*.egg-info/

# Test artifacts
Expand Down
50 changes: 33 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,45 @@ DATA_DIR=./data

## 🚀 Quick Start

**📓 [Tutorial Notebook](notebooks/ONC_Data_Download_Tutorial.ipynb)** - The best way to get started with interactive examples.
For the guided beginner path, start with the
**[online documentation](https://spiffical.github.io/onc-hydrophone-data/)**.
An extended [tutorial notebook](notebooks/ONC_Data_Download_Tutorial.ipynb) is
also available for interactive exploration.

### Python API

```python
from onc_hydrophone_data.onc.common import load_config
from onc_hydrophone_data.data import HydrophoneDownloader
from datetime import datetime, timezone
from pathlib import Path

from onc_hydrophone_data.audio import SpectrogramGenerator
from onc_hydrophone_data.data import HydrophoneDownloader
from onc_hydrophone_data.onc.common import load_config

# Load credentials from .env file
onc_token, data_dir = load_config()

# Download spectrograms using intelligent sampling
downloader = HydrophoneDownloader(onc_token, data_dir)
downloader.download_spectrograms_with_sampling_schedule(
deviceCode="ICLISTENHF6020",
start_date=(2021, 1, 1),
threshold_num=100

# Download a short, verified ONC audio range.
downloader.download_audio_for_range(
device_code="ICLISTENHF6324",
start_dt=datetime(2024, 4, 1, 12, 0, tzinfo=timezone.utc),
end_dt=datetime(2024, 4, 1, 12, 10, tzinfo=timezone.utc),
)

# Generate custom spectrograms from audio files
generator = SpectrogramGenerator(win_dur=2.0, overlap=0.75)
generator.process_directory("data/DEVICE/audio/", "output/spectrograms/")
# Generate PNG and MAT spectrograms locally from the downloaded audio.
audio_dir = Path(downloader.audio_path)
generator = SpectrogramGenerator(
win_dur=0.5,
overlap=0.75,
freq_lims=(20, 10_000),
crop_freq_lims=True,
)
generator.process_directory(
audio_dir,
audio_dir.parent / "custom_spectrograms",
save_plot=True,
save_mat=True,
)
```

### Command Line
Expand All @@ -81,11 +97,11 @@ python scripts/download_hydrophone_data.py

# Download spectrograms with specific parameters
python scripts/download_hydrophone_data.py --mode sampling \
--device ICLISTENHF6020 --start-date 2021 1 1 --threshold 500
--device ICLISTENHF6324 --start-date 2024 4 1 --threshold 500

# Include FLAC audio files
python scripts/download_hydrophone_data.py --mode sampling \
--device ICLISTENHF6020 --start-date 2021 1 1 --threshold 100 --download-audio
--device ICLISTENHF6324 --start-date 2024 4 1 --threshold 100 --download-audio

# Generate custom spectrograms
python scripts/generate_spectrograms.py --input-dir data/DEVICE/audio/ --win-dur 2.0
Expand Down Expand Up @@ -126,8 +142,8 @@ Downloads are organized in a clean, flat structure:

```
data/
└── ICLISTENHF6020/
└── sampling_2021-01-01_to_2021-01-31/
└── ICLISTENHF6324/
└── audio_range_2024-04-01_to_2024-04-01/
├── onc_spectrograms/ # ONC-downloaded spectrograms (MAT/PNG)
│ ├── *.mat # Spectrogram data files
│ └── anomaly_report.txt # Any validation issues (if found)
Expand Down
20 changes: 15 additions & 5 deletions docs/audio_downloads.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ without querying ONC again.

## Start with a short range

Run this setup first. The remaining examples on this page reuse `dl`, `DEVICE`,
`start`, and `end` from this block.

```python
from datetime import datetime, timezone

Expand All @@ -16,10 +19,14 @@ from onc_hydrophone_data.onc.common import load_config
onc_token, data_dir = load_config()
dl = HydrophoneDownloader(onc_token, data_dir)

DEVICE = "ICLISTENHF6324"
start = datetime(2024, 4, 1, 12, 0, tzinfo=timezone.utc)
end = datetime(2024, 4, 1, 12, 10, tzinfo=timezone.utc)

dl.download_audio_for_range(
device_code="ICLISTENHF6324",
start_dt=datetime(2024, 4, 1, 12, 0, tzinfo=timezone.utc),
end_dt=datetime(2024, 4, 1, 12, 10, tzinfo=timezone.utc),
device_code=DEVICE,
start_dt=start,
end_dt=end,
)

print("Audio directory:", dl.audio_path)
Expand Down Expand Up @@ -70,10 +77,13 @@ To explore seasonal or long-term variation without downloading every file,
request a uniform sample:

```python
sample_start = datetime(2024, 4, 1, tzinfo=timezone.utc)
sample_end = datetime(2024, 4, 8, tzinfo=timezone.utc)

result = dl.download_sampled_audio(
device_code=DEVICE,
start_dt=start,
end_dt=end,
start_dt=sample_start,
end_dt=sample_end,
total_audio_files=24,
files_per_request=4,
)
Expand Down
6 changes: 6 additions & 0 deletions docs/custom_spectrograms.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ For many labeled events, one workflow can download the needed audio context,
clip each event, and generate local spectrograms:

```python
from onc_hydrophone_data.data import HydrophoneDownloader
from onc_hydrophone_data.onc.common import load_config

onc_token, data_dir = load_config()
dl = HydrophoneDownloader(onc_token, data_dir)

results = dl.create_custom_spectrograms_from_json(
"custom_requests.json",
save_mat=True,
Expand Down
30 changes: 28 additions & 2 deletions docs/downloads.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,26 @@ Options](onc_spectrogram_options.md).

![Parallel ONC request pipeline](assets/parallel_pipeline.svg){: width="100%" }

## Shared setup for the examples

Run this block once before the Python examples below:

```python
from datetime import datetime, timezone

from onc_hydrophone_data.data import HydrophoneDownloader
from onc_hydrophone_data.onc.common import load_config

onc_token, data_dir = load_config()
dl = HydrophoneDownloader(onc_token, data_dir)

DEVICE = "ICLISTENHF6324"
start = datetime(2024, 4, 1, 12, 0, tzinfo=timezone.utc)
end = datetime(2024, 4, 1, 14, 0, tzinfo=timezone.utc)
```

## Server-generated spectrograms for a range

```python
result = dl.download_spectrograms_for_range(
device_code=DEVICE,
Expand All @@ -28,6 +47,7 @@ result = dl.download_spectrograms_for_range(
```

### Include matching audio

```python
result = dl.download_spectrograms_for_range(
device_code=DEVICE,
Expand All @@ -39,17 +59,22 @@ result = dl.download_spectrograms_for_range(
```

## Sample uniformly across a range

```python
sample_start = datetime(2024, 4, 1, tzinfo=timezone.utc)
sample_end = datetime(2024, 4, 8, tzinfo=timezone.utc)

result = dl.download_sampled_spectrograms(
device_code=DEVICE,
start_dt=start,
end_dt=end,
start_dt=sample_start,
end_dt=sample_end,
total_spectrograms=24,
spectrograms_per_request=6,
)
```

## Download around event timestamps

```python
events = [
datetime(2024, 4, 1, 12, 5, tzinfo=timezone.utc),
Expand All @@ -76,6 +101,7 @@ result = dl.download_audio_for_range(
```

## JSON/CSV request files

```python
results = dl.download_requests_from_json("/path/to/requests.json")
results = dl.download_requests_from_csv("/path/to/requests.csv")
Expand Down
11 changes: 10 additions & 1 deletion docs/onc_spectrogram_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,19 @@ specific downsampling guide recommends no more than one month per request.

### Python examples

Set a default for every request made by a downloader:
Run this setup once before the Python examples on this page. The two-hour range is
small enough for learning; begin with fewer windows for full-resolution output.

```python
from datetime import datetime, timezone

from onc_hydrophone_data.data import HydrophoneDownloader
from onc_hydrophone_data.onc.common import load_config

ONC_TOKEN, DATA_DIR = load_config()
DEVICE = "ICLISTENHF6324"
start = datetime(2024, 4, 1, 12, 0, tzinfo=timezone.utc)
end = datetime(2024, 4, 1, 14, 0, tzinfo=timezone.utc)

# Use ONC's compact, pre-generated one-minute MAT product by default.
dl = HydrophoneDownloader(
Expand Down
5 changes: 4 additions & 1 deletion docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ workflow.

## I cannot find the downloaded files

Print the active paths immediately after a download:
Print the active paths immediately after a download, using the same `dl`
instance created in the [Download Audio](audio_downloads.md) setup block:

```python
print("Audio:", dl.audio_path)
Expand Down Expand Up @@ -62,6 +63,8 @@ for long ranges. See **[Choose ONC Server Spectrograms](onc_spectrogram_options.
Use the SciPy backend to separate backend installation from data problems:

```python
from onc_hydrophone_data.audio import SpectrogramGenerator

generator = SpectrogramGenerator(backend="scipy")
```

Expand Down
16 changes: 11 additions & 5 deletions onc_hydrophone_data/data/downloader/onc_downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ def download_audio_files(
False so rerunning a request resumes instead of wasting bandwidth.

Returns:
Summary dict with files found/downloaded and extension used.
Summary dict with files found, downloaded during this call, skipped,
available locally after the call, errors, and the extension used.
"""
self.logger.info(f'Finding audio files for {deviceCode} from {start_time} to {end_time}')
try:
Expand All @@ -194,6 +195,7 @@ def download_audio_files(
'files_found': 0,
'files_downloaded': 0,
'files_skipped': 0,
'files_available': 0,
'errors': 0,
}

Expand Down Expand Up @@ -240,7 +242,7 @@ def download_audio_files(
attempts = {f: 0 for f in audio_files}
max_attempts = 6
attempt_round = 0
downloaded = len(existing_files)
downloaded = 0
errors = 0

if existing_files:
Expand Down Expand Up @@ -302,15 +304,19 @@ def download_audio_files(
time.sleep(5)

self.logger.info(
f"{extension.upper()} files downloaded in {time.time() - download_start:.2f}s"
f"{extension.upper()} download pass completed in "
f"{time.time() - download_start:.2f}s "
f"({downloaded} downloaded, {len(existing_files)} skipped)"
)
available = len(existing_files) + downloaded
summary.update({
'extension_used': extension if downloaded else None,
'extension_used': extension if available else None,
'files_downloaded': downloaded,
'files_skipped': len(existing_files),
'files_available': available,
'errors': errors,
})
if downloaded:
if available:
return summary

self.logger.warning(
Expand Down
9 changes: 5 additions & 4 deletions onc_hydrophone_data/utils/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,14 +449,15 @@ def plot_availability_calendar(
raise ValueError("plot_availability_calendar requires bin_size='day'")

date_to_bin = {b['start'].date(): b for b in bins if b.get('start') is not None}
start_dt = availability.get('start') or bins[0]['start']
end_dt = availability.get('end') or bins[-1]['end']
if start_dt is None or end_dt is None:
first_bin_start = bins[0].get('start')
last_bin_start = bins[-1].get('start')
start_dt = availability.get('start') or first_bin_start
if start_dt is None or last_bin_start is None:
print("Availability window is empty.")
return None

start_date = start_dt.date()
end_date = bins[-1]['start'].date()
end_date = last_bin_start.date()
week0_start = start_date - timedelta(days=start_date.weekday())
total_days = (end_date - start_date).days
num_weeks = ((end_date - week0_start).days // 7) + 1
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ where = ["."]
include = ["onc_hydrophone_data*"]

[tool.pytest.ini_options]
addopts = "-m 'not integration'"
addopts = ["-m", "not integration"]
markers = [
"integration: makes live ONC API requests and may download data",
]
Loading