remoteRF-server 0.2.3__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/PKG-INFO +3 -1
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/pyproject.toml +3 -1
- remoterf_server-1.0.0/src/remoteRF_server/common/idl/pluto_mimo_schema.py +6 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/idl/pluto_schema.py +66 -11
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/idl/schema.py +8 -2
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/process_arg.py +20 -11
- remoterf_server-1.0.0/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +181 -0
- remoterf_server-1.0.0/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +229 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/host/host_tunnel_server.py +45 -1
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/device_manager.py +266 -82
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/grpc_server.py +1 -1
- remoterf_server-1.0.0/src/remoteRF_server/server/rpc_manager.py +288 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/user_group_cli.py +3 -3
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/user_group_handler.py +13 -12
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/serverrf_cli.py +159 -26
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/tools/gist_status.py +14 -22
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server.egg-info/PKG-INFO +3 -1
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server.egg-info/SOURCES.txt +3 -0
- remoterf_server-0.2.3/src/remoteRF_server/server/rpc_manager.py +0 -104
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/README.md +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/setup.cfg +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/idl/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server.egg-info/requires.txt +0 -0
- {remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server.egg-info/top_level.txt +0 -0
{remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/idl/pluto_schema.py
RENAMED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
# Admin-written schema for ADALM-Pluto devices.
|
|
4
|
-
# Drop this file into the drivers folder, restart the server. Done.
|
|
1
|
+
# SAMPLE schema for ADI Pluto SDR.
|
|
2
|
+
# Place into -> ~/.config/remoterf/drivers and restart server to utilize!
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
|
|
5
|
+
from remoteRF_server.common.idl import DeviceSchema, idl_register, idl_expose
|
|
7
6
|
|
|
8
7
|
import subprocess
|
|
9
8
|
import re
|
|
10
9
|
import adi
|
|
11
10
|
|
|
12
|
-
#
|
|
11
|
+
# == PLUTO Connection Logic ==
|
|
13
12
|
|
|
14
13
|
def connect_pluto(*, serial: str):
|
|
15
14
|
serial = (serial or "").strip()
|
|
@@ -46,10 +45,11 @@ def connect_pluto(*, serial: str):
|
|
|
46
45
|
|
|
47
46
|
# ========================= PLUTO Schema =========================
|
|
48
47
|
|
|
49
|
-
@idl_register("
|
|
48
|
+
@idl_register("adalm_pluto")
|
|
50
49
|
class PlutoSchema(DeviceSchema):
|
|
51
50
|
|
|
52
|
-
device_type = "
|
|
51
|
+
device_type = "adalm_pluto"
|
|
52
|
+
client_class = "Pluto"
|
|
53
53
|
driver_version = "0.0.1"
|
|
54
54
|
|
|
55
55
|
@staticmethod
|
|
@@ -85,11 +85,13 @@ class PlutoSchema(DeviceSchema):
|
|
|
85
85
|
|
|
86
86
|
@idl_expose(kind="get")
|
|
87
87
|
def get_rx_hardwaregain_chan0(self):
|
|
88
|
-
|
|
88
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
89
|
+
return self.device.hardwaregain_chan0
|
|
89
90
|
|
|
90
91
|
@idl_expose(kind="set")
|
|
91
92
|
def set_rx_hardwaregain_chan0(self, value):
|
|
92
|
-
|
|
93
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
94
|
+
self.device.hardwaregain_chan0 = value
|
|
93
95
|
|
|
94
96
|
@idl_expose(kind="get")
|
|
95
97
|
def get_tx_hardwaregain_chan0(self):
|
|
@@ -171,4 +173,57 @@ class PlutoSchema(DeviceSchema):
|
|
|
171
173
|
|
|
172
174
|
@idl_expose(kind="call")
|
|
173
175
|
def call_tx_destroy_buffer(self):
|
|
174
|
-
self.device.tx_destroy_buffer()
|
|
176
|
+
self.device.tx_destroy_buffer()
|
|
177
|
+
|
|
178
|
+
@idl_expose(kind="call")
|
|
179
|
+
def call_disable_dds(self):
|
|
180
|
+
"""Disable the internal DDS tone generator."""
|
|
181
|
+
self.device.disable_dds()
|
|
182
|
+
|
|
183
|
+
# endregion
|
|
184
|
+
|
|
185
|
+
# region ip / repr
|
|
186
|
+
|
|
187
|
+
@idl_expose(kind="call")
|
|
188
|
+
def call_ip(self):
|
|
189
|
+
"""Return a connection identifier for this device (ping / verify)."""
|
|
190
|
+
try:
|
|
191
|
+
return str(self.device.uri)
|
|
192
|
+
except AttributeError:
|
|
193
|
+
return str(self.device._uri_auto)
|
|
194
|
+
|
|
195
|
+
@idl_expose(kind="get")
|
|
196
|
+
def get_repr(self):
|
|
197
|
+
"""Return repr() of the underlying device object."""
|
|
198
|
+
return repr(self.device)
|
|
199
|
+
|
|
200
|
+
# endregion
|
|
201
|
+
|
|
202
|
+
# region _dec_int_fpga_filter
|
|
203
|
+
|
|
204
|
+
@idl_expose(kind="get")
|
|
205
|
+
def get_rx_dec8_filter_en(self):
|
|
206
|
+
"""rx_dec8_filter_en: Enable decimate-by-8 filter in FPGA"""
|
|
207
|
+
return bool(self.device.rx_dec8_filter_en)
|
|
208
|
+
|
|
209
|
+
@idl_expose(kind="set")
|
|
210
|
+
def set_rx_dec8_filter_en(self, value):
|
|
211
|
+
"""rx_dec8_filter_en: Enable decimate-by-8 filter in FPGA"""
|
|
212
|
+
self.device.rx_dec8_filter_en = value
|
|
213
|
+
|
|
214
|
+
@idl_expose(kind="get")
|
|
215
|
+
def get_tx_int8_filter_en(self):
|
|
216
|
+
"""tx_int8_filter_en: Enable interpolate-by-8 filter in FPGA"""
|
|
217
|
+
return bool(self.device.tx_int8_filter_en)
|
|
218
|
+
|
|
219
|
+
@idl_expose(kind="set")
|
|
220
|
+
def set_tx_int8_filter_en(self, value):
|
|
221
|
+
"""tx_int8_filter_en: Enable interpolate-by-8 filter in FPGA"""
|
|
222
|
+
self.device.tx_int8_filter_en = value
|
|
223
|
+
|
|
224
|
+
@idl_expose(kind="get")
|
|
225
|
+
def get_rates(self):
|
|
226
|
+
"""Get decimation/interpolation rates from the FPGA filter block."""
|
|
227
|
+
return self.device._get_rates(self.device._rxadc, False)
|
|
228
|
+
|
|
229
|
+
# endregion
|
|
@@ -263,7 +263,7 @@ class DeviceSchema:
|
|
|
263
263
|
elif ex.kind == "call":
|
|
264
264
|
calls[name] = entry
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
body = {
|
|
267
267
|
"schema_version": "1.0",
|
|
268
268
|
"device_type": self.device_type,
|
|
269
269
|
"driver_version": self.driver_version,
|
|
@@ -272,6 +272,12 @@ class DeviceSchema:
|
|
|
272
272
|
"calls": calls,
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
client_class = getattr(self, "client_class", "")
|
|
276
|
+
if client_class:
|
|
277
|
+
body["client_class"] = str(client_class)
|
|
278
|
+
|
|
279
|
+
return body
|
|
280
|
+
|
|
275
281
|
# ── Dispatch (server RPC layer calls this) ────────────────────
|
|
276
282
|
|
|
277
283
|
def dispatch(self, method_name: str, args: dict) -> Any:
|
|
@@ -355,4 +361,4 @@ class _Exposed:
|
|
|
355
361
|
self.name = name
|
|
356
362
|
self.fn = fn
|
|
357
363
|
self.kind = kind
|
|
358
|
-
self.doc = doc
|
|
364
|
+
self.doc = doc
|
{remoterf_server-0.2.3 → remoterf_server-1.0.0}/src/remoteRF_server/common/utils/process_arg.py
RENAMED
|
@@ -23,26 +23,36 @@ def unmap_arg(arg):
|
|
|
23
23
|
def map_arg(value):
|
|
24
24
|
arg = grpc_pb2.Argument()
|
|
25
25
|
|
|
26
|
-
if isinstance(value,
|
|
27
|
-
arg.
|
|
28
|
-
elif isinstance(value,
|
|
29
|
-
arg.
|
|
26
|
+
if isinstance(value, (bool, np.bool_)):
|
|
27
|
+
arg.bool_value = bool(value)
|
|
28
|
+
elif isinstance(value, (int, np.integer)):
|
|
29
|
+
arg.int64_value = int(value)
|
|
30
|
+
elif isinstance(value, (float, np.floating)):
|
|
31
|
+
arg.float_value = float(value)
|
|
30
32
|
elif isinstance(value, str):
|
|
31
33
|
arg.string_value = value
|
|
32
|
-
elif isinstance(value,
|
|
33
|
-
|
|
34
|
+
elif isinstance(value, (list, tuple)):
|
|
35
|
+
array = np.asarray(value)
|
|
36
|
+
if array.dtype == object:
|
|
37
|
+
try:
|
|
38
|
+
array = np.stack(value)
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
raise ValueError(f"Cannot map ragged/object array value: {value}") from exc
|
|
41
|
+
return map_arg(array)
|
|
34
42
|
elif isinstance(value, np.ndarray):
|
|
43
|
+
if value.dtype == object:
|
|
44
|
+
raise ValueError(f"Cannot map object-dtype array: {value!r}")
|
|
35
45
|
if np.iscomplexobj(value):
|
|
36
46
|
complex_array = arg.complex_array
|
|
37
47
|
complex_array.shape.dim.extend(value.shape)
|
|
38
|
-
for num in value.ravel():
|
|
48
|
+
for num in np.asarray(value, dtype=np.complex64).ravel():
|
|
39
49
|
complex_num = complex_array.data.add()
|
|
40
|
-
complex_num.real = num.real
|
|
41
|
-
complex_num.imag = num.imag
|
|
50
|
+
complex_num.real = float(num.real)
|
|
51
|
+
complex_num.imag = float(num.imag)
|
|
42
52
|
else:
|
|
43
53
|
float_array = arg.real_array
|
|
44
54
|
float_array.shape.dim.extend(value.shape)
|
|
45
|
-
float_array.data.extend(value.ravel())
|
|
55
|
+
float_array.data.extend(np.asarray(value, dtype=np.float32).ravel())
|
|
46
56
|
else:
|
|
47
57
|
raise ValueError(f"Unknown argument type during mapping: {value}")
|
|
48
58
|
return arg
|
|
@@ -77,4 +87,3 @@ def unmap_array_proto(arg):
|
|
|
77
87
|
return np.array(arg.float_array.data, dtype=np.float32)
|
|
78
88
|
else:
|
|
79
89
|
raise ValueError("Argument does not contain a recognizable array.")
|
|
80
|
-
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# SAMPLE schema for ADI Pluto SDR in AD9361 MIMO / 2R2T mode.
|
|
2
|
+
# Place into -> ~/.config/remoterf/drivers and restart server to utilize!
|
|
3
|
+
|
|
4
|
+
from remoteRF_server.common.idl import idl_register, idl_expose
|
|
5
|
+
from remoteRF_server.common.idl.pluto_schema import PlutoSchema
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
import re
|
|
9
|
+
import adi
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _normalize_channels(value, default):
|
|
13
|
+
if value is None:
|
|
14
|
+
return list(default)
|
|
15
|
+
if isinstance(value, str):
|
|
16
|
+
if not value.strip():
|
|
17
|
+
return list(default)
|
|
18
|
+
return [int(part.strip()) for part in value.split(",") if part.strip()]
|
|
19
|
+
return [int(ch) for ch in value]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _get_attr(device, *names):
|
|
23
|
+
for name in names:
|
|
24
|
+
if hasattr(device, name):
|
|
25
|
+
return getattr(device, name)
|
|
26
|
+
raise AttributeError(f"{device!r} has none of: {', '.join(names)}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _set_attr(device, value, *names):
|
|
30
|
+
for name in names:
|
|
31
|
+
if hasattr(device, name):
|
|
32
|
+
setattr(device, name, value)
|
|
33
|
+
return
|
|
34
|
+
raise AttributeError(f"{device!r} has none of: {', '.join(names)}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _apply_initial_settings(device, settings):
|
|
38
|
+
int_attrs = (
|
|
39
|
+
"sample_rate",
|
|
40
|
+
"rx_lo",
|
|
41
|
+
"tx_lo",
|
|
42
|
+
"rx_rf_bandwidth",
|
|
43
|
+
"tx_rf_bandwidth",
|
|
44
|
+
"rx_buffer_size",
|
|
45
|
+
)
|
|
46
|
+
for attr in int_attrs:
|
|
47
|
+
value = settings.get(attr)
|
|
48
|
+
if value is not None:
|
|
49
|
+
setattr(device, attr, int(value))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# == PLUTO MIMO Connection Logic ==
|
|
53
|
+
|
|
54
|
+
def connect_pluto_mimo(
|
|
55
|
+
*,
|
|
56
|
+
serial,
|
|
57
|
+
rx_enabled_channels=None,
|
|
58
|
+
tx_enabled_channels=None,
|
|
59
|
+
initial_settings=None,
|
|
60
|
+
):
|
|
61
|
+
serial = (serial or "").strip()
|
|
62
|
+
if not serial:
|
|
63
|
+
print("An adalm_pluto_mimo serial must be provided")
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
out = subprocess.check_output(
|
|
68
|
+
["iio_info", "-s"],
|
|
69
|
+
text=True,
|
|
70
|
+
stderr=subprocess.STDOUT,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
usb = None
|
|
74
|
+
for line in out.splitlines():
|
|
75
|
+
if (f"serial={serial}" in line) or (f"hw_serial={serial}" in line):
|
|
76
|
+
m = re.search(r"\[usb:([^\]]+)\]", line)
|
|
77
|
+
if m:
|
|
78
|
+
usb = m.group(1).strip()
|
|
79
|
+
break
|
|
80
|
+
|
|
81
|
+
if not usb:
|
|
82
|
+
print(f"No adalm_pluto_mimo device found with serial {serial}")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
dev = adi.ad9361(f"usb:{usb}")
|
|
86
|
+
dev.rx_enabled_channels = _normalize_channels(rx_enabled_channels, [0, 1])
|
|
87
|
+
dev.tx_enabled_channels = _normalize_channels(tx_enabled_channels, [0, 1])
|
|
88
|
+
_apply_initial_settings(dev, initial_settings or {})
|
|
89
|
+
print(f"Connected to adalm_pluto_mimo serial={serial} via usb:{usb}")
|
|
90
|
+
return dev
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"Failed to connect to adalm_pluto_mimo serial={serial}: {e}")
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ========================= PLUTO MIMO Schema =========================
|
|
98
|
+
|
|
99
|
+
@idl_register("adalm_pluto_mimo")
|
|
100
|
+
class PlutoMIMOSchema(PlutoSchema):
|
|
101
|
+
|
|
102
|
+
device_type = "adalm_pluto_mimo"
|
|
103
|
+
client_class = "PlutoMIMO"
|
|
104
|
+
driver_version = "0.0.1"
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def make_device(**kwargs):
|
|
108
|
+
return connect_pluto_mimo(
|
|
109
|
+
serial=kwargs.get("serial"),
|
|
110
|
+
rx_enabled_channels=kwargs.get("rx_enabled_channels"),
|
|
111
|
+
tx_enabled_channels=kwargs.get("tx_enabled_channels"),
|
|
112
|
+
initial_settings=kwargs,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# region MIMO channel selection
|
|
116
|
+
|
|
117
|
+
@idl_expose(kind="get")
|
|
118
|
+
def get_mode(self):
|
|
119
|
+
return "mimo"
|
|
120
|
+
|
|
121
|
+
@idl_expose(kind="get")
|
|
122
|
+
def get_rx_enabled_channels(self):
|
|
123
|
+
return list(self.device.rx_enabled_channels)
|
|
124
|
+
|
|
125
|
+
@idl_expose(kind="set")
|
|
126
|
+
def set_rx_enabled_channels(self, value):
|
|
127
|
+
self.device.rx_enabled_channels = _normalize_channels(value, [0, 1])
|
|
128
|
+
|
|
129
|
+
@idl_expose(kind="get")
|
|
130
|
+
def get_tx_enabled_channels(self):
|
|
131
|
+
return list(self.device.tx_enabled_channels)
|
|
132
|
+
|
|
133
|
+
@idl_expose(kind="set")
|
|
134
|
+
def set_tx_enabled_channels(self, value):
|
|
135
|
+
self.device.tx_enabled_channels = _normalize_channels(value, [0, 1])
|
|
136
|
+
|
|
137
|
+
# endregion
|
|
138
|
+
|
|
139
|
+
# region channel 1 controls
|
|
140
|
+
|
|
141
|
+
@idl_expose(kind="get")
|
|
142
|
+
def get_gain_control_mode_chan1(self):
|
|
143
|
+
return self.device.gain_control_mode_chan1
|
|
144
|
+
|
|
145
|
+
@idl_expose(kind="set")
|
|
146
|
+
def set_gain_control_mode_chan1(self, value):
|
|
147
|
+
self.device.gain_control_mode_chan1 = value
|
|
148
|
+
|
|
149
|
+
@idl_expose(kind="get")
|
|
150
|
+
def get_rx_hardwaregain_chan1(self):
|
|
151
|
+
"""RX channel 1 hardware gain (dB)."""
|
|
152
|
+
return _get_attr(self.device, "rx_hardwaregain_chan1", "hardwaregain_chan1")
|
|
153
|
+
|
|
154
|
+
@idl_expose(kind="set")
|
|
155
|
+
def set_rx_hardwaregain_chan1(self, value):
|
|
156
|
+
"""RX channel 1 hardware gain (dB)."""
|
|
157
|
+
_set_attr(self.device, value, "rx_hardwaregain_chan1", "hardwaregain_chan1")
|
|
158
|
+
|
|
159
|
+
@idl_expose(kind="get")
|
|
160
|
+
def get_tx_hardwaregain_chan1(self):
|
|
161
|
+
return self.device.tx_hardwaregain_chan1
|
|
162
|
+
|
|
163
|
+
@idl_expose(kind="set")
|
|
164
|
+
def set_tx_hardwaregain_chan1(self, value):
|
|
165
|
+
self.device.tx_hardwaregain_chan1 = value
|
|
166
|
+
|
|
167
|
+
# endregion
|
|
168
|
+
|
|
169
|
+
# region AD9361 gain compatibility
|
|
170
|
+
|
|
171
|
+
@idl_expose(kind="get")
|
|
172
|
+
def get_rx_hardwaregain_chan0(self):
|
|
173
|
+
"""RX channel 0 hardware gain (dB)."""
|
|
174
|
+
return _get_attr(self.device, "rx_hardwaregain_chan0", "hardwaregain_chan0")
|
|
175
|
+
|
|
176
|
+
@idl_expose(kind="set")
|
|
177
|
+
def set_rx_hardwaregain_chan0(self, value):
|
|
178
|
+
"""RX channel 0 hardware gain (dB)."""
|
|
179
|
+
_set_attr(self.device, value, "rx_hardwaregain_chan0", "hardwaregain_chan0")
|
|
180
|
+
|
|
181
|
+
# endregion
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# SAMPLE schema for ADI Pluto SDR.
|
|
2
|
+
# Place into -> ~/.config/remoterf/drivers and restart server to utilize!
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from remoteRF_server.common.idl import DeviceSchema, idl_register, idl_expose
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
import re
|
|
9
|
+
import adi
|
|
10
|
+
|
|
11
|
+
# == PLUTO Connection Logic ==
|
|
12
|
+
|
|
13
|
+
def connect_pluto(*, serial: str):
|
|
14
|
+
serial = (serial or "").strip()
|
|
15
|
+
if not serial:
|
|
16
|
+
print("A Pluto serial must be provided")
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
out = subprocess.check_output(
|
|
21
|
+
["iio_info", "-s"],
|
|
22
|
+
text=True,
|
|
23
|
+
stderr=subprocess.STDOUT
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
usb = None
|
|
27
|
+
for line in out.splitlines():
|
|
28
|
+
if (f"serial={serial}" in line) or (f"hw_serial={serial}" in line):
|
|
29
|
+
m = re.search(r"\[usb:([^\]]+)\]", line)
|
|
30
|
+
if m:
|
|
31
|
+
usb = m.group(1).strip()
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
if not usb:
|
|
35
|
+
print(f"No device found with serial {serial}")
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
dev = adi.Pluto(f"usb:{usb}")
|
|
39
|
+
print(f"Connected to Pluto serial={serial} via usb:{usb}")
|
|
40
|
+
return dev
|
|
41
|
+
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"Failed to connect to Pluto serial={serial}: {e}")
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
# ========================= PLUTO Schema =========================
|
|
47
|
+
|
|
48
|
+
@idl_register("adalm_pluto")
|
|
49
|
+
class PlutoSchema(DeviceSchema):
|
|
50
|
+
|
|
51
|
+
device_type = "adalm_pluto"
|
|
52
|
+
client_class = "Pluto"
|
|
53
|
+
driver_version = "0.0.1"
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def make_device(**kwargs):
|
|
57
|
+
serial = kwargs.get("serial")
|
|
58
|
+
return connect_pluto(serial=serial)
|
|
59
|
+
|
|
60
|
+
# region ad9364
|
|
61
|
+
|
|
62
|
+
@idl_expose(kind="get")
|
|
63
|
+
def get_filter(self):
|
|
64
|
+
return self.device.filter
|
|
65
|
+
|
|
66
|
+
@idl_expose(kind="set")
|
|
67
|
+
def set_filter(self, value):
|
|
68
|
+
self.device.filter = value
|
|
69
|
+
|
|
70
|
+
@idl_expose(kind="get")
|
|
71
|
+
def get_loopback(self):
|
|
72
|
+
return self.device.loopback
|
|
73
|
+
|
|
74
|
+
@idl_expose(kind="set")
|
|
75
|
+
def set_loopback(self, value):
|
|
76
|
+
self.device.loopback = value
|
|
77
|
+
|
|
78
|
+
@idl_expose(kind="get")
|
|
79
|
+
def get_gain_control_mode_chan0(self):
|
|
80
|
+
return self.device.gain_control_mode_chan0
|
|
81
|
+
|
|
82
|
+
@idl_expose(kind="set")
|
|
83
|
+
def set_gain_control_mode_chan0(self, value):
|
|
84
|
+
self.device.gain_control_mode_chan0 = value
|
|
85
|
+
|
|
86
|
+
@idl_expose(kind="get")
|
|
87
|
+
def get_rx_hardwaregain_chan0(self):
|
|
88
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
89
|
+
return self.device.hardwaregain_chan0
|
|
90
|
+
|
|
91
|
+
@idl_expose(kind="set")
|
|
92
|
+
def set_rx_hardwaregain_chan0(self, value):
|
|
93
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
94
|
+
self.device.hardwaregain_chan0 = value
|
|
95
|
+
|
|
96
|
+
@idl_expose(kind="get")
|
|
97
|
+
def get_tx_hardwaregain_chan0(self):
|
|
98
|
+
return self.device.tx_hardwaregain_chan0
|
|
99
|
+
|
|
100
|
+
@idl_expose(kind="set")
|
|
101
|
+
def set_tx_hardwaregain_chan0(self, value):
|
|
102
|
+
self.device.tx_hardwaregain_chan0 = value
|
|
103
|
+
|
|
104
|
+
@idl_expose(kind="get")
|
|
105
|
+
def get_rx_rf_bandwidth(self):
|
|
106
|
+
return self.device.rx_rf_bandwidth
|
|
107
|
+
|
|
108
|
+
@idl_expose(kind="set")
|
|
109
|
+
def set_rx_rf_bandwidth(self, value):
|
|
110
|
+
self.device.rx_rf_bandwidth = value
|
|
111
|
+
|
|
112
|
+
@idl_expose(kind="get")
|
|
113
|
+
def get_tx_rf_bandwidth(self):
|
|
114
|
+
return self.device.tx_rf_bandwidth
|
|
115
|
+
|
|
116
|
+
@idl_expose(kind="set")
|
|
117
|
+
def set_tx_rf_bandwidth(self, value):
|
|
118
|
+
self.device.tx_rf_bandwidth = value
|
|
119
|
+
|
|
120
|
+
@idl_expose(kind="get")
|
|
121
|
+
def get_sample_rate(self):
|
|
122
|
+
return self.device.sample_rate
|
|
123
|
+
|
|
124
|
+
@idl_expose(kind="set")
|
|
125
|
+
def set_sample_rate(self, value):
|
|
126
|
+
self.device.sample_rate = value
|
|
127
|
+
|
|
128
|
+
@idl_expose(kind="get")
|
|
129
|
+
def get_rx_lo(self):
|
|
130
|
+
return self.device.rx_lo
|
|
131
|
+
|
|
132
|
+
@idl_expose(kind="set")
|
|
133
|
+
def set_rx_lo(self, value):
|
|
134
|
+
self.device.rx_lo = value
|
|
135
|
+
|
|
136
|
+
@idl_expose(kind="get")
|
|
137
|
+
def get_tx_lo(self):
|
|
138
|
+
return self.device.tx_lo
|
|
139
|
+
|
|
140
|
+
@idl_expose(kind="set")
|
|
141
|
+
def set_tx_lo(self, value):
|
|
142
|
+
self.device.tx_lo = value
|
|
143
|
+
|
|
144
|
+
@idl_expose(kind="get")
|
|
145
|
+
def get_tx_cyclic_buffer(self):
|
|
146
|
+
return self.device.tx_cyclic_buffer
|
|
147
|
+
|
|
148
|
+
@idl_expose(kind="set")
|
|
149
|
+
def set_tx_cyclic_buffer(self, value):
|
|
150
|
+
self.device.tx_cyclic_buffer = value
|
|
151
|
+
|
|
152
|
+
# region RX/TX
|
|
153
|
+
|
|
154
|
+
@idl_expose(kind="call")
|
|
155
|
+
def call_rx(self):
|
|
156
|
+
return self.device.rx()
|
|
157
|
+
|
|
158
|
+
@idl_expose(kind="get")
|
|
159
|
+
def get_rx_buffer_size(self):
|
|
160
|
+
return self.device.rx_buffer_size
|
|
161
|
+
|
|
162
|
+
@idl_expose(kind="set")
|
|
163
|
+
def set_rx_buffer_size(self, value):
|
|
164
|
+
self.device.rx_buffer_size = value
|
|
165
|
+
|
|
166
|
+
@idl_expose(kind="call")
|
|
167
|
+
def call_rx_destroy_buffer(self):
|
|
168
|
+
self.device.rx_destroy_buffer()
|
|
169
|
+
|
|
170
|
+
@idl_expose(kind="call")
|
|
171
|
+
def call_tx(self, value):
|
|
172
|
+
self.device.tx(value)
|
|
173
|
+
|
|
174
|
+
@idl_expose(kind="call")
|
|
175
|
+
def call_tx_destroy_buffer(self):
|
|
176
|
+
self.device.tx_destroy_buffer()
|
|
177
|
+
|
|
178
|
+
@idl_expose(kind="call")
|
|
179
|
+
def call_disable_dds(self):
|
|
180
|
+
"""Disable the internal DDS tone generator."""
|
|
181
|
+
self.device.disable_dds()
|
|
182
|
+
|
|
183
|
+
# endregion
|
|
184
|
+
|
|
185
|
+
# region ip / repr
|
|
186
|
+
|
|
187
|
+
@idl_expose(kind="call")
|
|
188
|
+
def call_ip(self):
|
|
189
|
+
"""Return a connection identifier for this device (ping / verify)."""
|
|
190
|
+
try:
|
|
191
|
+
return str(self.device.uri)
|
|
192
|
+
except AttributeError:
|
|
193
|
+
return str(self.device._uri_auto)
|
|
194
|
+
|
|
195
|
+
@idl_expose(kind="get")
|
|
196
|
+
def get_repr(self):
|
|
197
|
+
"""Return repr() of the underlying device object."""
|
|
198
|
+
return repr(self.device)
|
|
199
|
+
|
|
200
|
+
# endregion
|
|
201
|
+
|
|
202
|
+
# region _dec_int_fpga_filter
|
|
203
|
+
|
|
204
|
+
@idl_expose(kind="get")
|
|
205
|
+
def get_rx_dec8_filter_en(self):
|
|
206
|
+
"""rx_dec8_filter_en: Enable decimate-by-8 filter in FPGA"""
|
|
207
|
+
return bool(self.device.rx_dec8_filter_en)
|
|
208
|
+
|
|
209
|
+
@idl_expose(kind="set")
|
|
210
|
+
def set_rx_dec8_filter_en(self, value):
|
|
211
|
+
"""rx_dec8_filter_en: Enable decimate-by-8 filter in FPGA"""
|
|
212
|
+
self.device.rx_dec8_filter_en = value
|
|
213
|
+
|
|
214
|
+
@idl_expose(kind="get")
|
|
215
|
+
def get_tx_int8_filter_en(self):
|
|
216
|
+
"""tx_int8_filter_en: Enable interpolate-by-8 filter in FPGA"""
|
|
217
|
+
return bool(self.device.tx_int8_filter_en)
|
|
218
|
+
|
|
219
|
+
@idl_expose(kind="set")
|
|
220
|
+
def set_tx_int8_filter_en(self, value):
|
|
221
|
+
"""tx_int8_filter_en: Enable interpolate-by-8 filter in FPGA"""
|
|
222
|
+
self.device.tx_int8_filter_en = value
|
|
223
|
+
|
|
224
|
+
@idl_expose(kind="get")
|
|
225
|
+
def get_rates(self):
|
|
226
|
+
"""Get decimation/interpolation rates from the FPGA filter block."""
|
|
227
|
+
return self.device._get_rates(self.device._rxadc, False)
|
|
228
|
+
|
|
229
|
+
# endregion
|