futuresdr 0.0.41

An Experimental Async SDR Runtime for Heterogeneous Architectures.
Documentation
# Remote Interaction

It is possible to interact with a running flowgraph through the control port REST API, which
can be used as the base for web UIs or any other tool supporting REST (e.g., Curl, Python requests).

## REST API

*Control port* provides a REST API to expose the flowgraph structure and enable remote interaction.
It is enabled by default, but you can configure it explicitly through the
[configuration](running_apps.md#configuration), for example:

```toml
ctrlport_enable = true
ctrlport_bind = "127.0.0.1:1337"
```

To allow remote hosts to access control port, bind it to a public interface or
an unrestricted address:

```toml
ctrlport_enable = true
ctrlport_bind = "0.0.0.0:1337"
```

Alternatively, configure control port through environment variables, which
always take precedence:

```bash
export FUTURESDR_CTRLPORT_ENABLE="true"
export FUTURESDR_CTRLPORT_BIND="0.0.0.0:1337"
```

Control port can be accessed with a browser or programmatically (e.g., using
`curl`, the Python `requests` library, etc.).
FutureSDR also provides a [support library](https://crates.io/crates/futuresdr-remote) to ease remote interaction from Rust.

To get a JSON description of the first flowgraph executed on a runtime, open
`127.0.0.1:1337/api/fg/0/` in your browser or use `curl`:

```bash
curl http://127.0.0.1:1337/api/fg/0/ | jq
{
  "blocks": [
    {
      "id": 0,
      "type_name": "Encoder",
      "instance_name": "Encoder-0",
      "stream_inputs": [],
      "stream_outputs": [
        "output"
      ],
      "message_inputs": [
        "tx"
      ],
      "message_outputs": [],
      "blocking": false
    },
    {
      "id": 1,
      "type_name": "Mac",
      "instance_name": "Mac-1",
      "stream_inputs": [],
      "stream_outputs": [],
      "message_inputs": [
        "tx"
      ],
      "message_outputs": [
        "tx"
      ],
      "blocking": false
    }
  ],
  "stream_edges": [
    [
      0,
      "output",
      2,
      "input"
    ]
  ],
  "message_edges": [
    [
      1,
      "tx",
      0,
      "tx"
    ]
  ]
}
```

It is also possible to get information about a particular block.

```bash
curl http://127.0.0.1:1337/api/fg/0/block/0/ | jq
{
  "id": 0,
  "type_name": "Encoder",
  "instance_name": "Encoder-0",
  "stream_inputs": [],
  "stream_outputs": [
    "output"
  ],
  "message_inputs": [
    "tx"
  ],
  "message_outputs": [],
  "blocking": false
}
```

All message handlers of a block are exposed automatically through the REST API.
Assuming block `0` is the SDR source or sink, you can set the frequency by
posting a JSON-serialized [PMT](https://docs.rs/futuresdr-types/latest/futuresdr_types/enum.Pmt.html) to the corresponding message handler:

```bash
curl -X POST -H "Content-Type: application/json" -d '{ "U32": 123 }'  http://127.0.0.1:1337/api/fg/0/block/0/call/freq/
```

Here are some more examples of serialized PMTs:

```json
{ "U32": 123 }
{ "U64": 5}
{ "F32": 123 }
{ "Bool": true }
{ "VecU64": [ 1, 2, 3] }
"Ok"
"Null"
{ "String": "foo" }
```

### Endpoints

The control port interface is associated with the runtime. By default, it comes with the following endpoints:

- **GET /api/fg/**: Array of flowgraph IDs of the flowgraphs spawned on the runtime.
- **GET /api/fg/0/**: JSON description of flowgraph with ID 0.
- **GET /api/fg/0/block/0/**: JSON description of the block with ID 0.
- **GET /api/fg/0/block/0/call/freq**: Call message handler `freq` of the block with `Pmt::Null` as argument.
- **POST /api/fg/0/block/0/call/freq**: Call message handler `freq` with JSON-serialized `Pmt` as input.

### Example: Frequency Hopping

The following Python script uses the REST API to find the first block in
flowgraph `0` whose type or instance name contains `seify`, then repeatedly
calls its `freq` message handler with a list of frequencies.

It requires the Python `requests` package:

```python
#!/usr/bin/env python3

import itertools
import requests
import time


BASE_URL = "http://127.0.0.1:1337"
FREQUENCIES = [
    100.0e6,
    101.0e6,
    102.0e6,
]
DWELL_TIME = 1.0


description = requests.get(f"{BASE_URL}/api/fg/0/").json()

seify_block = next(
    block
    for block in description["blocks"]
    if "seify" in block["type_name"].lower()
    or "seify" in block["instance_name"].lower()
)

freq_url = f"{BASE_URL}/api/fg/0/block/{seify_block['id']}/call/freq/"

for freq in itertools.cycle(FREQUENCIES):
    # Pmt::F64(freq), serialized as JSON.
    requests.post(freq_url, json={"F64": freq})
    print(f"set frequency to {freq / 1e6:.3f} MHz")
    time.sleep(DWELL_TIME)
```


## Web UI

FutureSDR comes with a minimal, work-in-progress web UI, implemented in the *prophecy* crate.
It comes pre-compiled at `crates/prophecy/dist`.
When FutureSDR is started with control port enabled, you can specify the
`frontend_path` [configuration](running_apps.md#configuration) option to serve a custom
frontend at the root path of the control-port URL (e.g., `127.0.0.1:1337`).

Using the REST API, it is straightforward to build custom UIs.

- A web UI served by an independent server
- A web UI served through FutureSDR control port (see the WLAN and ADS-B examples)
- A UI using arbitrary technology (GTK, Qt, etc.) running as a separate process
  (see the Egui example)