Metadata-Version: 2.1
Name: dynos-adaptive-resampling
Version: 0.1.3
Summary: Adaptive resampling extension for DYNOS Sentry surveys
License: Apache-2.0
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: dynos-client>=0.1.3
Requires-Dist: dynos-sentry-domain==0.1.3
Requires-Dist: numpy
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"

# dynos-adaptive-resampling

A worked example: chain an ML-driven resampling step onto a Sentry survey. This
package is scaffolding: it ships a stub `AdaptiveResampler` that you
replace with your model, plus the wiring (`runnable.py`, `mission.py`) that
lets `dynos connect` find it and that lets the planner sequence "survey,
resample, resurvey".

The package contributes one new transition (`resample_zone`) and one new
mission template (`build_resample_mission`). Everything else is for you to
edit.

## Install

You will be modifying the source, so install editable:

```bash
pip install -e dynos-adaptive-resampling
```

This pulls in `dynos-client`, `dynos-sentry-domain`, and `numpy`.

## What's in this repository

### `domain.py`

Declares `resample_zone` (transition), `ResampleZoneParams` (its parameter
dataclass), `resampling_complete` (a fluent the transition adds). Edit if
you need new fluents or parameters.

### `resampler.py`

Provides `AdaptiveResampler` class with `@Action(transition=resample_zone)`.
Edit `_gather_historic_data` and `_predict_new_zone` or whatever else you
need to get your model integrated.

### `mission.py`

Provides `build_resample_mission(zone_name)` returns a `Mission` that does
survey, resample, resurvey. Edit If you want a different sequence.

### `runnable.py`

Provides `AdaptiveResamplingNode`, the `dynos connect`-compatible entry point.
You probably don't need to edit this.

## How the round-trip works

1. You launch the resampler as a servant: `dynos connect
   dynos_adaptive_resampling.runnable:AdaptiveResamplingNode`. The servant
   idles, polling the backend for assignments.
2. From a second terminal you build the mission and call `execute_blocks`. The
   first plan block surveys the source zone end-to-end.
3. The planner reaches `resample_zone(source_zone=site_alpha)`. It dispatches
   the assignment over HTTP to your servant.
4. Your `AdaptiveResampler` runs `_gather_historic_data`, then
   `_predict_new_zone`, then writes the resulting `ZoneProposal` back as a new
   `Zone` object on the backend.
5. The next plan block surveys the new zone. The mission ends with abort +
   recover.

## Replacing the stub

You will edit two methods in `resampler.py`:

`_gather_historic_data(zone_name)`: return whatever your model needs. The
shipped stub returns an empty dict; you replace it with calls to a shared
database, ROS bag, the backend API (`orch.get_objects(type="zone")`), an
offline NetCDF, etc.

`_predict_new_zone(historic_data)`: return a `ZoneProposal` with
`vertices`, `altitude`, `speed`, and `confidence`:

```python
def _predict_new_zone(self, historic_data):
    features = self._extract_features(historic_data["sonar_readings"])
    prediction = self._model.predict(features)
    return ZoneProposal(
        vertices=self._prediction_to_polygon(prediction),
        altitude=70.0,
        speed=0.8,
        confidence=float(prediction.confidence),
    )
```

You can add fields to `ResampleZoneParams` (e.g. a confidence threshold, or
whatever else you want), but don't change the `Zone` parameter: the backend's
coverage planner reads `Zone.vertices`, `Zone.coverage_width`, etc. to draw
tracklines, and it expects the public schema.

## Test offline

Before pointing at the real backend, run the package's own tests:

```bash
pip install -e "dynos-adaptive-resampling/[dev]"
pytest dynos-adaptive-resampling/tests/ -v
```

These exercise `_gather_historic_data` and `_predict_new_zone` against a stub
backend. No network or session needed.

## Run it for real

Two terminals.

Terminal 1 is your resampler. This can be your laptop, a lab server, or the
same machine as the backend; the backend dispatches each assignment to
whichever servant is currently registered for `resample_zone`.

```bash
dynos login  # logins persist across terminals but expire after an hour
dynos session create --robot sentry-mock # Omitting '--robot sentry_mock' is valid but will omit important robot knowledge like coordinates, which is probably not what you want
dynos connect dynos_adaptive_resampling.runnable:AdaptiveResamplingNode
```

Leave it running. It executes `resample_zone` whenever the backend reaches that
step.

Terminal 2 is mission. Create the source zone and run the adaptive mission:

```bash
dynos call create zone.json
```

```python
from dynos_client import RemoteOrchestrator
from dynos_adaptive_resampling.mission import build_resample_mission

orch = RemoteOrchestrator.from_config(timeout_s=3600)
results = orch.execute_blocks(build_resample_mission("site_alpha"))
for r in results:
    print(r)
```

While it runs, monitor from a third terminal:

```bash
dynos call state --scope public      # current symbolic state, pruned only for the symbols you're expecting
dynos call goal                      # current goal
dynos call objects --type zone       # source + proposed zones
```

## What the mission does, step by step

1. Init. Takeover, descent, mode setup.
2. Survey the source zone (`full_coverage_of(site_alpha)`).
3. Resample. Plan dispatches `resample_zone(source_zone=site_alpha)` to your servant; your model creates `resampled_site_alpha`.
4. Resurvey. `full_coverage_of(resampled_site_alpha)`.
5. Recover. Abort and ascend.

## Public API

| Symbol                              | From                                  | Purpose                                        |
|-------------------------------------|---------------------------------------|------------------------------------------------|
| `resample_zone`                     | `dynos_adaptive_resampling.domain`    | The transition that fires your `@Action`.      |
| `ResampleZoneParams`                | `dynos_adaptive_resampling.domain`    | Its parameter dataclass (`source_zone: Zone`). |
| `resampling_complete`               | `dynos_adaptive_resampling.domain`    | Fluent the transition adds.                    |
| `AdaptiveResampler`                 | `dynos_adaptive_resampling.resampler` | The class you edit.                            |
| `AdaptiveResamplingNode`            | `dynos_adaptive_resampling.runnable`  | The `dynos connect` entry point.               |
| `build_resample_mission(zone_name)` | `dynos_adaptive_resampling.mission`   | Survey, resample, resurvey.                    |

## Troubleshooting

Resampler never gets an assignment: The mission hasn't reached `resample_zone`
yet. Check `dynos call state --scope public`.

`No plan found`: The source zone doesn't exist, has no `vertices`, or has
`coverage_width <= 0`. Check `dynos call objects --type zone`.

Resurvey failed: The proposed zone is missing fields. `resampled_*` needs
`vertices` (3+), `coverage_width > 0`, `robot_width > 0`, and
`coordinate_frame`. The `ZoneProposal` dataclass populates these; if you
bypassed it, double-check.

For cross-package issues (login, session, connection), see `user_guide.md` or
`dynos-client`'s README.
