Compare commits
No commits in common. "414597e9406c12f38877250ae9b8091884b9075c" and "27049f00eaaa3676794cd98e1f8dcfa13fa44e3b" have entirely different histories.
414597e940
...
27049f00ea
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -52,7 +52,6 @@ tests/sdr/
|
||||||
|
|
||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/build/
|
docs/build/
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
# Jupyter Notebook
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
|
|
|
||||||
1083
docs/_build/html/_sources/intro/getting_started.rst.txt
vendored
1083
docs/_build/html/_sources/intro/getting_started.rst.txt
vendored
File diff suppressed because it is too large
Load Diff
|
|
@ -1,29 +0,0 @@
|
||||||
/* Change the hex values below to customize heading colours */
|
|
||||||
|
|
||||||
.rst-content h1 { color: #2c3e50; }
|
|
||||||
.rst-content h2,
|
|
||||||
.rst-content h2 a { color: #ffffff !important; font-size: 22px !important; }
|
|
||||||
|
|
||||||
.rst-content h3,
|
|
||||||
.rst-content h3 a { color: #ffffff !important; font-size: 16px !important; }
|
|
||||||
|
|
||||||
.rst-content h3 code { font-size: inherit !important; }
|
|
||||||
|
|
||||||
.rst-content .admonition.warning {
|
|
||||||
background: #1a1a2e !important;
|
|
||||||
border-left: 4px solid #c0392b !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rst-content .admonition.warning .admonition-title {
|
|
||||||
background: #c0392b !important;
|
|
||||||
color: #ffffff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rst-content .admonition.warning p {
|
|
||||||
color: #ffffff !important;
|
|
||||||
}
|
|
||||||
.rst-content h4 { color: #404040; }
|
|
||||||
|
|
||||||
.highlight * { color: #ffffff !important; }
|
|
||||||
|
|
||||||
.ria-cmd { color: #2980b9 !important; }
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
document.querySelectorAll('.highlight pre').forEach(function (pre) {
|
|
||||||
pre.innerHTML = pre.innerHTML.replace(
|
|
||||||
/((?:^|\n|>))(ria)(?=[ \t]|<)/g,
|
|
||||||
'$1<span class="ria-cmd">$2</span>'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -14,7 +14,7 @@ sys.path.insert(0, os.path.abspath(os.path.join('..', '..')))
|
||||||
project = 'ria-toolkit-oss'
|
project = 'ria-toolkit-oss'
|
||||||
copyright = '2025, Qoherent Inc'
|
copyright = '2025, Qoherent Inc'
|
||||||
author = 'Qoherent Inc.'
|
author = 'Qoherent Inc.'
|
||||||
release = '0.1.5'
|
release = '0.1.4'
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
@ -73,6 +73,3 @@ def setup(app):
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||||
|
|
||||||
html_theme = 'sphinx_rtd_theme'
|
html_theme = 'sphinx_rtd_theme'
|
||||||
html_static_path = ['_static']
|
|
||||||
html_css_files = ['custom.css']
|
|
||||||
html_js_files = ['custom.js']
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
901
poetry.lock
generated
901
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
2
poetry.toml
Normal file
2
poetry.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[virtualenvs.options]
|
||||||
|
system-site-packages = true
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "ria-toolkit-oss"
|
name = "ria-toolkit-oss"
|
||||||
version = "0.1.5"
|
version = "0.1.4"
|
||||||
description = "An open-source version of the RIA Toolkit, including the fundamental tools to get started developing, testing, and deploying radio intelligence applications"
|
description = "An open-source version of the RIA Toolkit, including the fundamental tools to get started developing, testing, and deploying radio intelligence applications"
|
||||||
license = { text = "AGPL-3.0-only" }
|
license = { text = "AGPL-3.0-only" }
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -49,8 +49,7 @@ dependencies = [
|
||||||
"pyzmq (>=27.1.0,<28.0.0)",
|
"pyzmq (>=27.1.0,<28.0.0)",
|
||||||
"pyyaml (>=6.0.3,<7.0.0)",
|
"pyyaml (>=6.0.3,<7.0.0)",
|
||||||
"click (>=8.1.0,<9.0.0)",
|
"click (>=8.1.0,<9.0.0)",
|
||||||
"matplotlib (>=3.8.0,<4.0.0)",
|
"matplotlib (>=3.8.0,<4.0.0)"
|
||||||
"paramiko (>=4.0.0)"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# [project.optional-dependencies] Commented out to prevent Tox tests from failing
|
# [project.optional-dependencies] Commented out to prevent Tox tests from failing
|
||||||
|
|
@ -88,7 +87,7 @@ pytest = "^8.0.0"
|
||||||
tox = "^4.19.0"
|
tox = "^4.19.0"
|
||||||
fastapi = ">=0.111,<1.0"
|
fastapi = ">=0.111,<1.0"
|
||||||
uvicorn = {version = ">=0.29,<1.0", extras = ["standard"]}
|
uvicorn = {version = ">=0.29,<1.0", extras = ["standard"]}
|
||||||
onnxruntime = {version = ">=1.17,<2.0", python = ">=3.11"}
|
onnxruntime = ">=1.17,<2.0"
|
||||||
httpx = ">=0.27,<1.0"
|
httpx = ">=0.27,<1.0"
|
||||||
|
|
||||||
[tool.poetry.group.docs.dependencies]
|
[tool.poetry.group.docs.dependencies]
|
||||||
|
|
@ -122,7 +121,7 @@ ria-agent = "ria_toolkit_oss.agent:main"
|
||||||
[tool.poetry.group.server.dependencies]
|
[tool.poetry.group.server.dependencies]
|
||||||
fastapi = ">=0.111,<1.0"
|
fastapi = ">=0.111,<1.0"
|
||||||
uvicorn = {version = ">=0.29,<1.0", extras = ["standard"]}
|
uvicorn = {version = ">=0.29,<1.0", extras = ["standard"]}
|
||||||
onnxruntime = {version = ">=1.17,<2.0", python = ">=3.11"}
|
onnxruntime = ">=1.17,<2.0"
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 119
|
line-length = 119
|
||||||
|
|
|
||||||
|
|
@ -40,19 +40,15 @@ class RemoteTransmitter:
|
||||||
try:
|
try:
|
||||||
if radio_str in ("pluto", "plutosdr"):
|
if radio_str in ("pluto", "plutosdr"):
|
||||||
from ria_toolkit_oss.sdr.pluto import Pluto
|
from ria_toolkit_oss.sdr.pluto import Pluto
|
||||||
|
|
||||||
self._sdr = Pluto(identifier)
|
self._sdr = Pluto(identifier)
|
||||||
elif radio_str in ("usrp",):
|
elif radio_str in ("usrp",):
|
||||||
from ria_toolkit_oss.sdr.usrp import USRP
|
from ria_toolkit_oss.sdr.usrp import USRP
|
||||||
|
|
||||||
self._sdr = USRP(identifier)
|
self._sdr = USRP(identifier)
|
||||||
elif radio_str in ("hackrf", "hackrf_one"):
|
elif radio_str in ("hackrf", "hackrf_one"):
|
||||||
from ria_toolkit_oss.sdr.hackrf import HackRF
|
from ria_toolkit_oss.sdr.hackrf import HackRF
|
||||||
|
|
||||||
self._sdr = HackRF(identifier)
|
self._sdr = HackRF(identifier)
|
||||||
elif radio_str in ("bladerf", "blade"):
|
elif radio_str in ("bladerf", "blade"):
|
||||||
from ria_toolkit_oss.sdr.blade import Blade
|
from ria_toolkit_oss.sdr.blade import Blade
|
||||||
|
|
||||||
self._sdr = Blade(identifier)
|
self._sdr = Blade(identifier)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown SDR type: {radio_str!r}")
|
raise ValueError(f"Unknown SDR type: {radio_str!r}")
|
||||||
|
|
@ -81,7 +77,6 @@ class RemoteTransmitter:
|
||||||
if self._sdr is None:
|
if self._sdr is None:
|
||||||
raise RuntimeError("Call set_radio() and init_tx() before transmit()")
|
raise RuntimeError("Call set_radio() and init_tx() before transmit()")
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# Transmit in a loop until duration has elapsed
|
# Transmit in a loop until duration has elapsed
|
||||||
end = time.monotonic() + duration_s
|
end = time.monotonic() + duration_s
|
||||||
while time.monotonic() < end:
|
while time.monotonic() < end:
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ import logging
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import paramiko
|
|
||||||
import zmq
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_STARTUP_WAIT_S = 2.0 # seconds to wait for remote ZMQ server to bind
|
_STARTUP_WAIT_S = 2.0 # seconds to wait for remote ZMQ server to bind
|
||||||
|
|
@ -161,21 +158,16 @@ class RemoteTransmitterController:
|
||||||
"""
|
"""
|
||||||
logger.info(
|
logger.info(
|
||||||
"init_tx: fc=%.3f MHz, fs=%.3f MHz, gain=%.1f dB, ch=%d",
|
"init_tx: fc=%.3f MHz, fs=%.3f MHz, gain=%.1f dB, ch=%d",
|
||||||
center_frequency / 1e6,
|
center_frequency / 1e6, sample_rate / 1e6, gain, channel,
|
||||||
sample_rate / 1e6,
|
|
||||||
gain,
|
|
||||||
channel,
|
|
||||||
)
|
)
|
||||||
self._send(
|
self._send({
|
||||||
{
|
|
||||||
"function_name": "init_tx",
|
"function_name": "init_tx",
|
||||||
"center_frequency": center_frequency,
|
"center_frequency": center_frequency,
|
||||||
"sample_rate": sample_rate,
|
"sample_rate": sample_rate,
|
||||||
"gain": gain,
|
"gain": gain,
|
||||||
"channel": channel,
|
"channel": channel,
|
||||||
"gain_mode": gain_mode,
|
"gain_mode": gain_mode,
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
def transmit_async(self, duration_s: float) -> None:
|
def transmit_async(self, duration_s: float) -> None:
|
||||||
"""Start a timed CW transmission in a background thread.
|
"""Start a timed CW transmission in a background thread.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import pytest
|
||||||
|
|
||||||
from ria_toolkit_oss.remote_control.remote_transmitter import RemoteTransmitter
|
from ria_toolkit_oss.remote_control.remote_transmitter import RemoteTransmitter
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -62,18 +63,14 @@ class TestSetRadio:
|
||||||
def test_hackrf_alias(self):
|
def test_hackrf_alias(self):
|
||||||
tx = RemoteTransmitter()
|
tx = RemoteTransmitter()
|
||||||
mock_sdr = _make_mock_sdr()
|
mock_sdr = _make_mock_sdr()
|
||||||
mock_module = MagicMock()
|
with patch("ria_toolkit_oss.sdr.hackrf.HackRF", return_value=mock_sdr):
|
||||||
mock_module.HackRF = MagicMock(return_value=mock_sdr)
|
|
||||||
with patch.dict("sys.modules", {"ria_toolkit_oss.sdr.hackrf": mock_module}):
|
|
||||||
tx.set_radio("hackrf", "")
|
tx.set_radio("hackrf", "")
|
||||||
assert tx._sdr is mock_sdr
|
assert tx._sdr is mock_sdr
|
||||||
|
|
||||||
def test_hackrf_one_alias(self):
|
def test_hackrf_one_alias(self):
|
||||||
tx = RemoteTransmitter()
|
tx = RemoteTransmitter()
|
||||||
mock_sdr = _make_mock_sdr()
|
mock_sdr = _make_mock_sdr()
|
||||||
mock_module = MagicMock()
|
with patch("ria_toolkit_oss.sdr.hackrf.HackRF", return_value=mock_sdr):
|
||||||
mock_module.HackRF = MagicMock(return_value=mock_sdr)
|
|
||||||
with patch.dict("sys.modules", {"ria_toolkit_oss.sdr.hackrf": mock_module}):
|
|
||||||
tx.set_radio("hackrf_one", "")
|
tx.set_radio("hackrf_one", "")
|
||||||
assert tx._sdr is mock_sdr
|
assert tx._sdr is mock_sdr
|
||||||
|
|
||||||
|
|
@ -244,40 +241,34 @@ class TestRunFunction:
|
||||||
|
|
||||||
def test_init_tx_without_radio_returns_failure(self):
|
def test_init_tx_without_radio_returns_failure(self):
|
||||||
tx = RemoteTransmitter()
|
tx = RemoteTransmitter()
|
||||||
resp = tx.run_function(
|
resp = tx.run_function({
|
||||||
{
|
|
||||||
"function_name": "init_tx",
|
"function_name": "init_tx",
|
||||||
"center_frequency": 2.4e9,
|
"center_frequency": 2.4e9,
|
||||||
"sample_rate": 20e6,
|
"sample_rate": 20e6,
|
||||||
"gain": 0,
|
"gain": 0,
|
||||||
}
|
})
|
||||||
)
|
|
||||||
assert resp["status"] is False
|
assert resp["status"] is False
|
||||||
assert resp["error_message"]
|
assert resp["error_message"]
|
||||||
|
|
||||||
def test_init_tx_with_radio_success(self):
|
def test_init_tx_with_radio_success(self):
|
||||||
tx = self._tx_with_mock_sdr()
|
tx = self._tx_with_mock_sdr()
|
||||||
resp = tx.run_function(
|
resp = tx.run_function({
|
||||||
{
|
|
||||||
"function_name": "init_tx",
|
"function_name": "init_tx",
|
||||||
"center_frequency": 2.4e9,
|
"center_frequency": 2.4e9,
|
||||||
"sample_rate": 20e6,
|
"sample_rate": 20e6,
|
||||||
"gain": 30,
|
"gain": 30,
|
||||||
}
|
})
|
||||||
)
|
|
||||||
assert resp["status"] is True
|
assert resp["status"] is True
|
||||||
|
|
||||||
def test_transmit_runs_for_short_duration(self):
|
def test_transmit_runs_for_short_duration(self):
|
||||||
tx = self._tx_with_mock_sdr()
|
tx = self._tx_with_mock_sdr()
|
||||||
tx._sdr.init_tx = MagicMock()
|
tx._sdr.init_tx = MagicMock()
|
||||||
resp = tx.run_function(
|
resp = tx.run_function({
|
||||||
{
|
|
||||||
"function_name": "init_tx",
|
"function_name": "init_tx",
|
||||||
"center_frequency": 2.4e9,
|
"center_frequency": 2.4e9,
|
||||||
"sample_rate": 20e6,
|
"sample_rate": 20e6,
|
||||||
"gain": 0,
|
"gain": 0,
|
||||||
}
|
})
|
||||||
)
|
|
||||||
resp = tx.run_function({"function_name": "transmit", "duration_s": 0.02})
|
resp = tx.run_function({"function_name": "transmit", "duration_s": 0.02})
|
||||||
assert resp["status"] is True
|
assert resp["status"] is True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ sys.modules so they run regardless of whether the packages are installed.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
@ -197,11 +199,15 @@ class TestErrorHandling:
|
||||||
|
|
||||||
def test_missing_paramiko_raises_runtime_error(self):
|
def test_missing_paramiko_raises_runtime_error(self):
|
||||||
"""If paramiko is absent, connecting gives a clear RuntimeError."""
|
"""If paramiko is absent, connecting gives a clear RuntimeError."""
|
||||||
|
import importlib
|
||||||
|
|
||||||
import ria_toolkit_oss.remote_control.remote_transmitter_controller as mod
|
import ria_toolkit_oss.remote_control.remote_transmitter_controller as mod
|
||||||
|
|
||||||
with patch.dict("sys.modules", {"paramiko": None}):
|
with patch.dict("sys.modules", {"paramiko": None}):
|
||||||
with pytest.raises((RuntimeError, ImportError)):
|
with pytest.raises((RuntimeError, ImportError)):
|
||||||
mod.RemoteTransmitterController(host="h", ssh_user="u", ssh_key_path="/k")
|
mod.RemoteTransmitterController(
|
||||||
|
host="h", ssh_user="u", ssh_key_path="/k"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, call, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
@ -12,6 +12,7 @@ from ria_toolkit_oss.orchestration.campaign import (
|
||||||
TransmitterConfig,
|
TransmitterConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -178,7 +179,9 @@ class TestInitRemoteTxControllers:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
executor = _make_executor(d)
|
executor = _make_executor(d)
|
||||||
with patch("ria_toolkit_oss.remote_control.RemoteTransmitterController") as mock_cls:
|
with patch(
|
||||||
|
"ria_toolkit_oss.remote_control.RemoteTransmitterController"
|
||||||
|
) as mock_cls:
|
||||||
executor._init_remote_tx_controllers()
|
executor._init_remote_tx_controllers()
|
||||||
mock_cls.assert_not_called()
|
mock_cls.assert_not_called()
|
||||||
assert executor._remote_tx_controllers == {}
|
assert executor._remote_tx_controllers == {}
|
||||||
|
|
@ -261,7 +264,7 @@ class TestStartTransmitterSdrRemote:
|
||||||
tx = executor.config.transmitters[0]
|
tx = executor.config.transmitters[0]
|
||||||
step = CaptureStep(duration=5.0, label="nochan")
|
step = CaptureStep(duration=5.0, label="nochan")
|
||||||
executor._start_transmitter(tx, step)
|
executor._start_transmitter(tx, step)
|
||||||
_, kwargs = ctrl.init_tx.call_args
|
_, kwargs = mock_ctrl_kwarg = ctrl.init_tx.call_args
|
||||||
assert kwargs["channel"] == 0
|
assert kwargs["channel"] == 0
|
||||||
|
|
||||||
def test_missing_controller_raises(self):
|
def test_missing_controller_raises(self):
|
||||||
|
|
@ -378,11 +381,7 @@ class TestRunWithSdrRemote:
|
||||||
),
|
),
|
||||||
patch.object(executor, "_close_sdr"),
|
patch.object(executor, "_close_sdr"),
|
||||||
patch.object(executor, "_close_remote_tx_controllers"),
|
patch.object(executor, "_close_remote_tx_controllers"),
|
||||||
patch.object(
|
patch.object(executor, "_execute_step", return_value=MagicMock(error=None, qa=MagicMock(flagged=False, snr_db=20.0, duration_s=10.0))),
|
||||||
executor,
|
|
||||||
"_execute_step",
|
|
||||||
return_value=MagicMock(error=None, qa=MagicMock(flagged=False, snr_db=20.0, duration_s=10.0)),
|
|
||||||
),
|
|
||||||
):
|
):
|
||||||
executor.run()
|
executor.run()
|
||||||
|
|
||||||
|
|
@ -402,7 +401,6 @@ class TestTransmitBufferAndTimeout:
|
||||||
|
|
||||||
def _executor_with_ctrl(self):
|
def _executor_with_ctrl(self):
|
||||||
from ria_toolkit_oss.orchestration.executor import CampaignExecutor
|
from ria_toolkit_oss.orchestration.executor import CampaignExecutor
|
||||||
|
|
||||||
cfg = CampaignConfig.from_dict(_FULL_CAMPAIGN_DICT)
|
cfg = CampaignConfig.from_dict(_FULL_CAMPAIGN_DICT)
|
||||||
executor = CampaignExecutor(cfg)
|
executor = CampaignExecutor(cfg)
|
||||||
ctrl = MagicMock()
|
ctrl = MagicMock()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user