> ## Documentation Index
> Fetch the complete documentation index at: https://docs.atomscale.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Poll Similarity Trajectory

> Monitor similarity trajectory updates from streaming analysis

Similarity trajectory polling tracks how the RHEED pattern evolves over time relative to reference frames. It follows the same patterns as [time series polling](/sdk/poll-timeseries) but uses dedicated functions from `atomscale.similarity`.

| Function                          | Use case                                       |
| --------------------------------- | ---------------------------------------------- |
| `iter_poll_trajectory`            | Synchronous loop that blocks between polls     |
| `start_polling_trajectory_thread` | Background thread for GUI or acquisition loops |
| `aiter_poll_trajectory`           | Async iterator for `asyncio` applications      |
| `start_polling_trajectory_task`   | Fire-and-forget asyncio task                   |

## When to use trajectory polling

Use trajectory polling when you need to:

* Track how the diffraction pattern changes during growth
* Detect when the pattern stabilizes or diverges from a reference
* Implement automatic stop conditions based on similarity thresholds

## Setup

```python theme={null}
from atomscale import Client
from atomscale.similarity import (
    iter_poll_trajectory,
    start_polling_trajectory_thread,
    aiter_poll_trajectory,
    start_polling_trajectory_task,
)

client = Client(api_key="YOUR_API_KEY")
source_id = "YOUR_DATA_OR_SAMPLE_ID"
```

<Note>
  The `source_id` parameter accepts either a data ID or a physical sample ID.
</Note>

## Synchronous polling

```python theme={null}
def latest_similarity(df):
    """Extract the latest similarity score for deduplication."""
    if df.empty or "Similarity" not in df.columns:
        return None
    return df.iloc[-1]["Similarity"]


for result in iter_poll_trajectory(
    client,
    source_id=source_id,
    interval=5.0,
    last_n=10,
    distinct_by=latest_similarity,
    max_polls=20,
):
    if not result.empty and "Similarity" in result.columns:
        latest = result.iloc[-1]["Similarity"]
        print(f"Current similarity: {latest:.3f}")
```

## Auto-stop on threshold

Use the `until` parameter to stop polling when a condition is met. By default, trajectory polling stops automatically when no trajectory is active.

```python theme={null}
def similarity_stabilized(df):
    """Stop when similarity exceeds 0.95."""
    if df.empty or "Similarity" not in df.columns:
        return False
    return df.iloc[-1]["Similarity"] > 0.95


for result in iter_poll_trajectory(
    client,
    source_id=source_id,
    interval=5.0,
    distinct_by=latest_similarity,
    until=similarity_stabilized,
):
    print(f"Similarity: {result.iloc[-1]['Similarity']:.3f}")

print("Growth stabilized!")
```

## Background thread polling

For integration with instrument control software:

```python theme={null}
def on_trajectory_update(result):
    if not result.empty and "Similarity" in result.columns:
        similarity = result.iloc[-1]["Similarity"]
        if similarity > 0.95:
            print("Pattern stabilized - consider stopping growth")


stop_event = start_polling_trajectory_thread(
    client,
    source_id=source_id,
    interval=5.0,
    last_n=10,
    on_result=on_trajectory_update,
)

# Stop polling when done:
# stop_event.set()
```

## Async polling

```python theme={null}
import asyncio


async def monitor_trajectory():
    async for result in aiter_poll_trajectory(
        client,
        source_id=source_id,
        interval=5.0,
        last_n=10,
        max_polls=20,
    ):
        if not result.empty and "Similarity" in result.columns:
            print(f"Similarity: {result.iloc[-1]['Similarity']:.3f}")


asyncio.run(monitor_trajectory())
```

## Error handling

Pass an `on_error` handler to continue polling through transient failures:

```python theme={null}
def handle_error(exc):
    print(f"Poll failed: {exc}")


for result in iter_poll_trajectory(
    client,
    source_id=source_id,
    interval=5.0,
    max_polls=10,
    on_error=handle_error,
):
    print(f"Received {len(result)} rows")
```

## Result structure

Each poll yields a DataFrame with a multi-level index and the following columns:

| Column                 | Description                                |
| ---------------------- | ------------------------------------------ |
| `Reference ID` (index) | ID of the reference trajectory             |
| `Time` (index)         | Real time in seconds                       |
| `Similarity`           | Similarity score (float)                   |
| `Reference Name`       | Display name of the reference              |
| `UNIX Timestamp`       | Unix epoch timestamp                       |
| `Active`               | Whether the trajectory is currently active |
| `Averaged Count`       | Number of data points averaged             |

## Next steps

<CardGroup cols={2}>
  <Card title="Poll Time Series" icon="clock" href="/sdk/poll-timeseries">
    Monitor raw time series updates.
  </Card>

  <Card title="Stream RHEED" icon="video" href="/sdk/stream-rheed">
    Set up RHEED streaming from your instrument.
  </Card>
</CardGroup>
