remoteRF-server 1.0.2__tar.gz → 1.0.4__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-1.0.2/src/remoteRF_server.egg-info → remoterf_server-1.0.4}/PKG-INFO +117 -5
- remoterf_server-1.0.2/PKG-INFO → remoterf_server-1.0.4/README.md +113 -16
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/pyproject.toml +4 -4
- remoterf_server-1.0.4/src/remoteRF_server/common/grpc/grpc_pb2.py +101 -0
- remoterf_server-1.0.4/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +462 -0
- remoterf_server-1.0.4/src/remoteRF_server/common/grpc/v2_codec.py +131 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/idl/__init__.py +20 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/idl/schema.py +2 -2
- remoterf_server-1.0.4/src/remoteRF_server/common/idl/v2.py +276 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/utils/process_arg.py +3 -0
- remoterf_server-1.0.4/src/remoteRF_server/drivers/usrp/policy.py +363 -0
- remoterf_server-1.0.4/src/remoteRF_server/drivers/usrp/uhd_manifest.py +1649 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/drivers/usrp/usrp_schema.py +278 -5
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/cert_provider.py +1 -1
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/device_manager.py +60 -15
- remoterf_server-1.0.4/src/remoteRF_server/server/dynamic_v2.py +1473 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/grpc_server.py +227 -94
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/reservation.py +23 -4
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/rpc_manager.py +23 -0
- remoterf_server-1.0.4/src/remoteRF_server/server/v2_errors.py +99 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/serverrf_cli.py +193 -62
- remoterf_server-1.0.4/src/remoteRF_server/tools/introspect_uhd.py +127 -0
- remoterf_server-1.0.2/README.md → remoterf_server-1.0.4/src/remoteRF_server.egg-info/PKG-INFO +129 -2
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server.egg-info/SOURCES.txt +16 -1
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server.egg-info/requires.txt +2 -2
- remoterf_server-1.0.4/tests/test_cert_provider.py +88 -0
- remoterf_server-1.0.4/tests/test_default_usrp_schema.py +129 -0
- remoterf_server-1.0.4/tests/test_dynamic_v2.py +983 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/tests/test_idl_schema.py +43 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/tests/test_pluto_compat.py +15 -0
- remoterf_server-1.0.4/tests/test_reservation_timestamps.py +30 -0
- remoterf_server-1.0.4/tests/test_usrp_client_server_hardware.py +224 -0
- remoterf_server-1.0.4/tests/test_usrp_client_server_localhost.py +259 -0
- remoterf_server-1.0.4/tests/test_usrp_remoterf_rx_hardware.py +337 -0
- remoterf_server-1.0.4/tests/test_usrp_uhd410_hardware.py +158 -0
- remoterf_server-1.0.2/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -59
- remoterf_server-1.0.2/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -97
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/setup.cfg +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/idl/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/drivers/usrp/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/tools/gist_status.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/src/remoteRF_server.egg-info/top_level.txt +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/tests/test_process_arg.py +0 -0
- {remoterf_server-1.0.2 → remoterf_server-1.0.4}/tests/test_rpc_manager_calln.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: RemoteRF server-side control package
|
|
5
|
-
Requires-Python: >=3.
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: grpcio
|
|
8
|
-
Requires-Dist: protobuf
|
|
7
|
+
Requires-Dist: grpcio<2.0.0,>=1.78.1
|
|
8
|
+
Requires-Dist: protobuf<7.0.0,>=6.31.1
|
|
9
9
|
Requires-Dist: numpy
|
|
10
10
|
Requires-Dist: prompt_toolkit
|
|
11
11
|
Requires-Dist: python-dotenv
|
|
@@ -173,6 +173,19 @@ Start the server:
|
|
|
173
173
|
```bash
|
|
174
174
|
serverrf -s
|
|
175
175
|
```
|
|
176
|
+
|
|
177
|
+
For a loopback-only development server, explicitly bind both services to
|
|
178
|
+
localhost:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
REMOTERF_BIND_HOST=127.0.0.1 GRPC_PORT=55051 CERT_PORT=55052 \
|
|
182
|
+
serverrf --serve --headless
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Headless and interactive shutdown both stop accepting RPCs, close all Dynamic
|
|
186
|
+
v2 sessions/handles and native devices, stop the certificate provider, and
|
|
187
|
+
terminate the reservation-update worker.
|
|
188
|
+
|
|
176
189
|
Take note of the Local IP and Local Port, for example, if you see:
|
|
177
190
|
|
|
178
191
|
```bash
|
|
@@ -226,7 +239,9 @@ Some distros come with default firewall settings that block traffic on specific
|
|
|
226
239
|
|
|
227
240
|
## Server Device Configuration
|
|
228
241
|
|
|
229
|
-
|
|
242
|
+
ADALM-Pluto and local UHD B200-family inventories are supported. USRP devices
|
|
243
|
+
remain registered as device type `usrp`; runtime capabilities distinguish
|
|
244
|
+
`usrp2901` and `b200`.
|
|
230
245
|
|
|
231
246
|
To connect plutos to the server:
|
|
232
247
|
|
|
@@ -283,6 +298,103 @@ serverrf -d --edit-name 0 "New Name"
|
|
|
283
298
|
|
|
284
299
|
Note that all device and server config parameters need a 'restart' to take affect (ctrl + c -> serverrf -s).
|
|
285
300
|
|
|
301
|
+
### NI USRP-2901 / B200-family
|
|
302
|
+
|
|
303
|
+
The server automatically installs and registers its complete USRP v1/v2
|
|
304
|
+
schema on first startup, so clients can discover the API even when UHD is not
|
|
305
|
+
installed locally. Existing custom `usrp` drivers in
|
|
306
|
+
`~/.config/remoterf/drivers/` take precedence over that default.
|
|
307
|
+
|
|
308
|
+
Opening a real USRP session requires the machine that owns the hardware to
|
|
309
|
+
have the exact supported UHD Python API, UHD `4.10.0.0`, installed. The
|
|
310
|
+
server does not open the device while loading inventory. It opens the
|
|
311
|
+
reservation's exact `type=b200,serial=...` device only when the authenticated
|
|
312
|
+
client opens a v2 session.
|
|
313
|
+
|
|
314
|
+
Add a serial-bound device:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
serverrf --device --add --usrp 7:usrp2901_lab:31A2B3C
|
|
318
|
+
serverrf --device --show
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The canonical inventory is `~/.config/remoterf/devices.yml`:
|
|
322
|
+
|
|
323
|
+
```yaml
|
|
324
|
+
devices:
|
|
325
|
+
- device_id: 7
|
|
326
|
+
device_type: usrp
|
|
327
|
+
name: usrp2901_lab
|
|
328
|
+
init:
|
|
329
|
+
serial: 31A2B3C
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Transmit is denied if `~/.config/remoterf/usrp_policy.yml` is missing,
|
|
333
|
+
unreadable, invalid, or does not explicitly enable and bound the requested
|
|
334
|
+
operation. Low-level writes are also denied unless both globally enabled and
|
|
335
|
+
method/path/address allowlisted. A starting policy looks like:
|
|
336
|
+
|
|
337
|
+
```yaml
|
|
338
|
+
defaults:
|
|
339
|
+
tx_enabled: false
|
|
340
|
+
low_level_write_enabled: false
|
|
341
|
+
|
|
342
|
+
devices:
|
|
343
|
+
"31A2B3C":
|
|
344
|
+
tx_enabled: true
|
|
345
|
+
tx_frequency_ranges_hz:
|
|
346
|
+
- [902000000, 928000000]
|
|
347
|
+
max_tx_gain_db: 20
|
|
348
|
+
max_tx_normalized_gain: 0.5
|
|
349
|
+
max_tx_power_dbm: -10
|
|
350
|
+
max_tx_bandwidth_hz: 2000000
|
|
351
|
+
max_tx_sample_rate_sps: 2000000
|
|
352
|
+
allowed_tx_antennas: [TX/RX]
|
|
353
|
+
max_burst_seconds: 1.0
|
|
354
|
+
max_duty_cycle: 0.1
|
|
355
|
+
duty_cycle_window_seconds: 60
|
|
356
|
+
low_level_write_enabled: false
|
|
357
|
+
low_level_write_methods: []
|
|
358
|
+
gpio_write_masks: {}
|
|
359
|
+
property_write_paths: []
|
|
360
|
+
register_write_addresses: []
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
On every qualified target, generate the introspection record and enforce the
|
|
364
|
+
pinned API gate:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
python -m remoteRF_server.tools.introspect_uhd \
|
|
368
|
+
--output uhd-4.10.0.0-target.json
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
The command exits nonzero for a version mismatch, a missing required member,
|
|
372
|
+
an unclassified public `MultiUSRP` member, or any still-deferred schema entry.
|
|
373
|
+
Hardware differential,
|
|
374
|
+
two-channel/full-duplex, USB 2/3, external clock/PPS, policy-bypass, and
|
|
375
|
+
qualified-LAN performance tests remain mandatory release gates; they cannot
|
|
376
|
+
be completed on a checkout without UHD and a physical NI USRP-2901.
|
|
377
|
+
|
|
378
|
+
To qualify the actual two-channel RX path through both checkouts on the Linux
|
|
379
|
+
USRP host, run the opt-in localhost client/server test from the server repo:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
REMOTERF_TEST_USRP_SERIAL=31A2B3C \
|
|
383
|
+
REMOTERF_CLIENT_SRC=../RemoteRF-Client/src \
|
|
384
|
+
python -m unittest discover -s tests \
|
|
385
|
+
-p test_usrp_client_server_hardware.py -v
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
This opens the serial-bound device, negotiates the v2 schema over a real
|
|
389
|
+
localhost gRPC socket, configures both RX channels through the client API,
|
|
390
|
+
receives raw binary IQ, restores the prior RX settings, and closes the remote
|
|
391
|
+
stream/session. It is RX-only and does not enable transmit.
|
|
392
|
+
|
|
393
|
+
Dynamic v2 control and `SampleDataV1.SampleStream` run on the existing TLS
|
|
394
|
+
gRPC channel. IQ samples are carried as bounded raw binary payloads with
|
|
395
|
+
dtype/shape/channel metadata; JSON/base64 and repeated-float sample transport
|
|
396
|
+
are not used.
|
|
397
|
+
|
|
286
398
|
## Server Runtime Configuration
|
|
287
399
|
|
|
288
400
|
After running `serverrf --serve`, you’ll enter the **RemoteRF Server Shell**. This interactive shell is used for runtime administration: users, devices, reservations, user groups, enrollment codes, and live host-tunnel state.
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: remoteRF-server
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: RemoteRF server-side control package
|
|
5
|
-
Requires-Python: >=3.8
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: grpcio
|
|
8
|
-
Requires-Dist: protobuf
|
|
9
|
-
Requires-Dist: numpy
|
|
10
|
-
Requires-Dist: prompt_toolkit
|
|
11
|
-
Requires-Dist: python-dotenv
|
|
12
|
-
Requires-Dist: grpcio-tools
|
|
13
|
-
Requires-Dist: requests
|
|
14
|
-
Requires-Dist: pyyaml
|
|
15
|
-
|
|
16
1
|
# Remote RF Server Guide (Linux)
|
|
17
2
|
|
|
18
3
|
## Environment and setup
|
|
@@ -173,6 +158,19 @@ Start the server:
|
|
|
173
158
|
```bash
|
|
174
159
|
serverrf -s
|
|
175
160
|
```
|
|
161
|
+
|
|
162
|
+
For a loopback-only development server, explicitly bind both services to
|
|
163
|
+
localhost:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
REMOTERF_BIND_HOST=127.0.0.1 GRPC_PORT=55051 CERT_PORT=55052 \
|
|
167
|
+
serverrf --serve --headless
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Headless and interactive shutdown both stop accepting RPCs, close all Dynamic
|
|
171
|
+
v2 sessions/handles and native devices, stop the certificate provider, and
|
|
172
|
+
terminate the reservation-update worker.
|
|
173
|
+
|
|
176
174
|
Take note of the Local IP and Local Port, for example, if you see:
|
|
177
175
|
|
|
178
176
|
```bash
|
|
@@ -226,7 +224,9 @@ Some distros come with default firewall settings that block traffic on specific
|
|
|
226
224
|
|
|
227
225
|
## Server Device Configuration
|
|
228
226
|
|
|
229
|
-
|
|
227
|
+
ADALM-Pluto and local UHD B200-family inventories are supported. USRP devices
|
|
228
|
+
remain registered as device type `usrp`; runtime capabilities distinguish
|
|
229
|
+
`usrp2901` and `b200`.
|
|
230
230
|
|
|
231
231
|
To connect plutos to the server:
|
|
232
232
|
|
|
@@ -283,6 +283,103 @@ serverrf -d --edit-name 0 "New Name"
|
|
|
283
283
|
|
|
284
284
|
Note that all device and server config parameters need a 'restart' to take affect (ctrl + c -> serverrf -s).
|
|
285
285
|
|
|
286
|
+
### NI USRP-2901 / B200-family
|
|
287
|
+
|
|
288
|
+
The server automatically installs and registers its complete USRP v1/v2
|
|
289
|
+
schema on first startup, so clients can discover the API even when UHD is not
|
|
290
|
+
installed locally. Existing custom `usrp` drivers in
|
|
291
|
+
`~/.config/remoterf/drivers/` take precedence over that default.
|
|
292
|
+
|
|
293
|
+
Opening a real USRP session requires the machine that owns the hardware to
|
|
294
|
+
have the exact supported UHD Python API, UHD `4.10.0.0`, installed. The
|
|
295
|
+
server does not open the device while loading inventory. It opens the
|
|
296
|
+
reservation's exact `type=b200,serial=...` device only when the authenticated
|
|
297
|
+
client opens a v2 session.
|
|
298
|
+
|
|
299
|
+
Add a serial-bound device:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
serverrf --device --add --usrp 7:usrp2901_lab:31A2B3C
|
|
303
|
+
serverrf --device --show
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The canonical inventory is `~/.config/remoterf/devices.yml`:
|
|
307
|
+
|
|
308
|
+
```yaml
|
|
309
|
+
devices:
|
|
310
|
+
- device_id: 7
|
|
311
|
+
device_type: usrp
|
|
312
|
+
name: usrp2901_lab
|
|
313
|
+
init:
|
|
314
|
+
serial: 31A2B3C
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Transmit is denied if `~/.config/remoterf/usrp_policy.yml` is missing,
|
|
318
|
+
unreadable, invalid, or does not explicitly enable and bound the requested
|
|
319
|
+
operation. Low-level writes are also denied unless both globally enabled and
|
|
320
|
+
method/path/address allowlisted. A starting policy looks like:
|
|
321
|
+
|
|
322
|
+
```yaml
|
|
323
|
+
defaults:
|
|
324
|
+
tx_enabled: false
|
|
325
|
+
low_level_write_enabled: false
|
|
326
|
+
|
|
327
|
+
devices:
|
|
328
|
+
"31A2B3C":
|
|
329
|
+
tx_enabled: true
|
|
330
|
+
tx_frequency_ranges_hz:
|
|
331
|
+
- [902000000, 928000000]
|
|
332
|
+
max_tx_gain_db: 20
|
|
333
|
+
max_tx_normalized_gain: 0.5
|
|
334
|
+
max_tx_power_dbm: -10
|
|
335
|
+
max_tx_bandwidth_hz: 2000000
|
|
336
|
+
max_tx_sample_rate_sps: 2000000
|
|
337
|
+
allowed_tx_antennas: [TX/RX]
|
|
338
|
+
max_burst_seconds: 1.0
|
|
339
|
+
max_duty_cycle: 0.1
|
|
340
|
+
duty_cycle_window_seconds: 60
|
|
341
|
+
low_level_write_enabled: false
|
|
342
|
+
low_level_write_methods: []
|
|
343
|
+
gpio_write_masks: {}
|
|
344
|
+
property_write_paths: []
|
|
345
|
+
register_write_addresses: []
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
On every qualified target, generate the introspection record and enforce the
|
|
349
|
+
pinned API gate:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
python -m remoteRF_server.tools.introspect_uhd \
|
|
353
|
+
--output uhd-4.10.0.0-target.json
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
The command exits nonzero for a version mismatch, a missing required member,
|
|
357
|
+
an unclassified public `MultiUSRP` member, or any still-deferred schema entry.
|
|
358
|
+
Hardware differential,
|
|
359
|
+
two-channel/full-duplex, USB 2/3, external clock/PPS, policy-bypass, and
|
|
360
|
+
qualified-LAN performance tests remain mandatory release gates; they cannot
|
|
361
|
+
be completed on a checkout without UHD and a physical NI USRP-2901.
|
|
362
|
+
|
|
363
|
+
To qualify the actual two-channel RX path through both checkouts on the Linux
|
|
364
|
+
USRP host, run the opt-in localhost client/server test from the server repo:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
REMOTERF_TEST_USRP_SERIAL=31A2B3C \
|
|
368
|
+
REMOTERF_CLIENT_SRC=../RemoteRF-Client/src \
|
|
369
|
+
python -m unittest discover -s tests \
|
|
370
|
+
-p test_usrp_client_server_hardware.py -v
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
This opens the serial-bound device, negotiates the v2 schema over a real
|
|
374
|
+
localhost gRPC socket, configures both RX channels through the client API,
|
|
375
|
+
receives raw binary IQ, restores the prior RX settings, and closes the remote
|
|
376
|
+
stream/session. It is RX-only and does not enable transmit.
|
|
377
|
+
|
|
378
|
+
Dynamic v2 control and `SampleDataV1.SampleStream` run on the existing TLS
|
|
379
|
+
gRPC channel. IQ samples are carried as bounded raw binary payloads with
|
|
380
|
+
dtype/shape/channel metadata; JSON/base64 and repeated-float sample transport
|
|
381
|
+
are not used.
|
|
382
|
+
|
|
286
383
|
## Server Runtime Configuration
|
|
287
384
|
|
|
288
385
|
After running `serverrf --serve`, you’ll enter the **RemoteRF Server Shell**. This interactive shell is used for runtime administration: users, devices, reservations, user groups, enrollment codes, and live host-tunnel state.
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "remoteRF-server"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.4"
|
|
8
8
|
description = "RemoteRF server-side control package"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
10
|
dependencies = [
|
|
11
|
-
"grpcio",
|
|
12
|
-
"protobuf",
|
|
11
|
+
"grpcio>=1.78.1,<2.0.0",
|
|
12
|
+
"protobuf>=6.31.1,<7.0.0",
|
|
13
13
|
"numpy",
|
|
14
14
|
"prompt_toolkit",
|
|
15
15
|
"python-dotenv",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: grpc.proto
|
|
5
|
+
# Protobuf Python Version: 6.31.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
6,
|
|
15
|
+
31,
|
|
16
|
+
1,
|
|
17
|
+
'',
|
|
18
|
+
'grpc.proto'
|
|
19
|
+
)
|
|
20
|
+
# @@protoc_insertion_point(imports)
|
|
21
|
+
|
|
22
|
+
_sym_db = _symbol_database.Default()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ngrpc.proto\x12\tremote_rf\"\xa2\x01\n\x11GenericRPCRequest\x12\x15\n\rfunction_name\x18\x01 \x01(\t\x12\x34\n\x04\x61rgs\x18\x02 \x03(\x0b\x32&.remote_rf.GenericRPCRequest.ArgsEntry\x1a@\n\tArgsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.remote_rf.Argument:\x02\x38\x01\"\x96\x01\n\x12GenericRPCResponse\x12;\n\x07results\x18\x01 \x03(\x0b\x32*.remote_rf.GenericRPCResponse.ResultsEntry\x1a\x43\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.remote_rf.Argument:\x02\x38\x01\"\x19\n\nArrayShape\x12\x0b\n\x03\x64im\x18\x01 \x03(\x05\"+\n\rComplexNumber\x12\x0c\n\x04real\x18\x01 \x01(\x02\x12\x0c\n\x04imag\x18\x02 \x01(\x02\"a\n\x11\x43omplexNumpyArray\x12$\n\x05shape\x18\x01 \x01(\x0b\x32\x15.remote_rf.ArrayShape\x12&\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x18.remote_rf.ComplexNumber\"D\n\x0eRealNumpyArray\x12$\n\x05shape\x18\x01 \x01(\x0b\x32\x15.remote_rf.ArrayShape\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x02\"\xed\x01\n\x08\x41rgument\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x15\n\x0bint64_value\x18\x02 \x01(\x03H\x00\x12\x15\n\x0b\x66loat_value\x18\x03 \x01(\x02H\x00\x12\x14\n\nbool_value\x18\x04 \x01(\x08H\x00\x12\x35\n\rcomplex_array\x18\x05 \x01(\x0b\x32\x1c.remote_rf.ComplexNumpyArrayH\x00\x12/\n\nreal_array\x18\x06 \x01(\x0b\x32\x19.remote_rf.RealNumpyArrayH\x00\x12\x14\n\njson_value\x18\x07 \x01(\tH\x00\x42\x07\n\x05value\":\n\x0cNDArrayValue\x12\r\n\x05\x64type\x18\x01 \x01(\t\x12\r\n\x05shape\x18\x02 \x03(\x03\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\"\xe9\x01\n\x0c\x44ynamicValue\x12\x14\n\nnull_value\x18\x01 \x01(\x08H\x00\x12\x14\n\nbool_value\x18\x02 \x01(\x08H\x00\x12\x15\n\x0bint64_value\x18\x03 \x01(\x12H\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12\x16\n\x0cstring_value\x18\x05 \x01(\tH\x00\x12\x15\n\x0b\x62ytes_value\x18\x06 \x01(\x0cH\x00\x12\x14\n\njson_value\x18\x07 \x01(\tH\x00\x12\x30\n\rndarray_value\x18\x08 \x01(\x0b\x32\x17.remote_rf.NDArrayValueH\x00\x42\x07\n\x05value\"B\n\nNamedValue\x12\x0c\n\x04name\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.remote_rf.DynamicValue\"B\n\x08Mutation\x12\x0e\n\x06target\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.remote_rf.DynamicValue\"\xb0\x01\n\rErrorEnvelope\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x0e\n\x06method\x18\x04 \x01(\t\x12\x18\n\x10native_exception\x18\x05 \x01(\t\x12\x13\n\x0buhd_version\x18\x06 \x01(\t\x12\x11\n\tretryable\x18\x07 \x01(\x08\x12\x18\n\x10\x66\x61tal_to_session\x18\x08 \x01(\x08\"s\n\x10NegotiateRequest\x12\x17\n\x0fschema_versions\x18\x01 \x03(\t\x12!\n\x19\x63ontrol_protocol_versions\x18\x02 \x03(\t\x12#\n\x1bstreaming_protocol_versions\x18\x03 \x03(\t\"\x9a\x01\n\x11NegotiateResponse\x12\x16\n\x0eschema_version\x18\x01 \x01(\t\x12 \n\x18\x63ontrol_protocol_version\x18\x02 \x01(\t\x12\"\n\x1astreaming_protocol_version\x18\x03 \x01(\t\x12\'\n\x05\x65rror\x18\x04 \x01(\x0b\x32\x18.remote_rf.ErrorEnvelope\":\n\x10GetSchemaRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x17\n\x0fschema_versions\x18\x02 \x03(\t\"~\n\x11GetSchemaResponse\x12\x16\n\x0eschema_version\x18\x01 \x01(\t\x12\x13\n\x0bschema_json\x18\x02 \x01(\t\x12\x13\n\x0bschema_hash\x18\x03 \x01(\t\x12\'\n\x05\x65rror\x18\x04 \x01(\x0b\x32\x18.remote_rf.ErrorEnvelope\"\x88\x01\n\x12OpenSessionRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x1d\n\x15requested_schema_hash\x18\x02 \x01(\t\x12 \n\x18\x63ontrol_protocol_version\x18\x03 \x01(\t\x12\"\n\x1astreaming_protocol_version\x18\x04 \x01(\t\"\xee\x01\n\x13OpenSessionResponse\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x15\n\rdevice_handle\x18\x02 \x01(\t\x12\x13\n\x0bschema_json\x18\x03 \x01(\t\x12\x13\n\x0bschema_hash\x18\x04 \x01(\t\x12\x19\n\x11\x63\x61pabilities_json\x18\x05 \x01(\t\x12\x13\n\x0buhd_version\x18\x06 \x01(\t\x12\x0f\n\x07uhd_abi\x18\x07 \x01(\t\x12\x18\n\x10hardware_profile\x18\x08 \x01(\t\x12\'\n\x05\x65rror\x18\t \x01(\x0b\x32\x18.remote_rf.ErrorEnvelope\"}\n\rInvokeRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0e\n\x06handle\x18\x02 \x01(\t\x12\x0e\n\x06method\x18\x03 \x01(\t\x12\x13\n\x0boverload_id\x18\x04 \x01(\t\x12#\n\x04\x61rgs\x18\x05 \x03(\x0b\x32\x15.remote_rf.NamedValue\"\x8a\x01\n\x0eInvokeResponse\x12\'\n\x06result\x18\x01 \x01(\x0b\x32\x17.remote_rf.DynamicValue\x12&\n\tmutations\x18\x02 \x03(\x0b\x32\x13.remote_rf.Mutation\x12\'\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x18.remote_rf.ErrorEnvelope\"8\n\x12\x43loseHandleRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0e\n\x06handle\x18\x02 \x01(\t\")\n\x13\x43loseSessionRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"H\n\rCloseResponse\x12\x0e\n\x06\x63losed\x18\x01 \x01(\x08\x12\'\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x18.remote_rf.ErrorEnvelope\"\xea\x03\n\x0bSampleFrame\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0e\n\x06handle\x18\x02 \x01(\t\x12\x12\n\ngeneration\x18\x03 \x01(\x04\x12\x14\n\x0coperation_id\x18\x04 \x01(\t\x12\x10\n\x08sequence\x18\x05 \x01(\x04\x12-\n\tdirection\x18\x06 \x01(\x0e\x32\x1a.remote_rf.SampleDirection\x12(\n\x04kind\x18\x07 \x01(\x0e\x32\x1a.remote_rf.SampleFrameKind\x12\r\n\x05\x64type\x18\x08 \x01(\t\x12\r\n\x05shape\x18\t \x03(\x03\x12\x10\n\x08\x63hannels\x18\n \x03(\r\x12\x14\n\x0csample_count\x18\x0b \x01(\x04\x12\x0f\n\x07payload\x18\x0c \x01(\x0c\x12\x15\n\rmetadata_json\x18\r \x01(\t\x12\x18\n\x10\x64\x65vice_time_secs\x18\x0e \x01(\x01\x12\x0f\n\x07\x63redits\x18\x0f \x01(\r\x12\x13\n\x0btimeout_sec\x18\x10 \x01(\x01\x12\x12\n\none_packet\x18\x11 \x01(\x08\x12\x15\n\rend_of_stream\x18\x12 \x01(\x08\x12\x11\n\tcancelled\x18\x13 \x01(\x08\x12\'\n\x05\x65rror\x18\x14 \x01(\x0b\x32\x18.remote_rf.ErrorEnvelope\x12\r\n\x05\x66lags\x18\x15 \x01(\x04*e\n\x0fSampleDirection\x12 \n\x1cSAMPLE_DIRECTION_UNSPECIFIED\x10\x00\x12\x17\n\x13SAMPLE_DIRECTION_RX\x10\x01\x12\x17\n\x13SAMPLE_DIRECTION_TX\x10\x02*\xf2\x01\n\x0fSampleFrameKind\x12\x1c\n\x18SAMPLE_FRAME_UNSPECIFIED\x10\x00\x12\x15\n\x11SAMPLE_FRAME_OPEN\x10\x01\x12\x18\n\x14SAMPLE_FRAME_REQUEST\x10\x02\x12\x15\n\x11SAMPLE_FRAME_DATA\x10\x03\x12\x17\n\x13SAMPLE_FRAME_RESULT\x10\x04\x12\x17\n\x13SAMPLE_FRAME_CREDIT\x10\x05\x12\x17\n\x13SAMPLE_FRAME_CANCEL\x10\x06\x12\x16\n\x12SAMPLE_FRAME_CLOSE\x10\x07\x12\x16\n\x12SAMPLE_FRAME_ERROR\x10\x08\x32Q\n\nGenericRPC\x12\x43\n\x04\x43\x61ll\x12\x1c.remote_rf.GenericRPCRequest\x1a\x1d.remote_rf.GenericRPCResponse2\xc1\x03\n\x10\x44ynamicControlV2\x12\x46\n\tNegotiate\x12\x1b.remote_rf.NegotiateRequest\x1a\x1c.remote_rf.NegotiateResponse\x12\x46\n\tGetSchema\x12\x1b.remote_rf.GetSchemaRequest\x1a\x1c.remote_rf.GetSchemaResponse\x12L\n\x0bOpenSession\x12\x1d.remote_rf.OpenSessionRequest\x1a\x1e.remote_rf.OpenSessionResponse\x12=\n\x06Invoke\x12\x18.remote_rf.InvokeRequest\x1a\x19.remote_rf.InvokeResponse\x12\x46\n\x0b\x43loseHandle\x12\x1d.remote_rf.CloseHandleRequest\x1a\x18.remote_rf.CloseResponse\x12H\n\x0c\x43loseSession\x12\x1e.remote_rf.CloseSessionRequest\x1a\x18.remote_rf.CloseResponse2R\n\x0cSampleDataV1\x12\x42\n\x0cSampleStream\x12\x16.remote_rf.SampleFrame\x1a\x16.remote_rf.SampleFrame(\x01\x30\x01\x42\x1e\n\x10\x63om.example.demoB\nDemoProtosb\x06proto3')
|
|
28
|
+
|
|
29
|
+
_globals = globals()
|
|
30
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'grpc_pb2', _globals)
|
|
32
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
+
_globals['DESCRIPTOR']._loaded_options = None
|
|
34
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n\020com.example.demoB\nDemoProtos'
|
|
35
|
+
_globals['_GENERICRPCREQUEST_ARGSENTRY']._loaded_options = None
|
|
36
|
+
_globals['_GENERICRPCREQUEST_ARGSENTRY']._serialized_options = b'8\001'
|
|
37
|
+
_globals['_GENERICRPCRESPONSE_RESULTSENTRY']._loaded_options = None
|
|
38
|
+
_globals['_GENERICRPCRESPONSE_RESULTSENTRY']._serialized_options = b'8\001'
|
|
39
|
+
_globals['_SAMPLEDIRECTION']._serialized_start=3213
|
|
40
|
+
_globals['_SAMPLEDIRECTION']._serialized_end=3314
|
|
41
|
+
_globals['_SAMPLEFRAMEKIND']._serialized_start=3317
|
|
42
|
+
_globals['_SAMPLEFRAMEKIND']._serialized_end=3559
|
|
43
|
+
_globals['_GENERICRPCREQUEST']._serialized_start=26
|
|
44
|
+
_globals['_GENERICRPCREQUEST']._serialized_end=188
|
|
45
|
+
_globals['_GENERICRPCREQUEST_ARGSENTRY']._serialized_start=124
|
|
46
|
+
_globals['_GENERICRPCREQUEST_ARGSENTRY']._serialized_end=188
|
|
47
|
+
_globals['_GENERICRPCRESPONSE']._serialized_start=191
|
|
48
|
+
_globals['_GENERICRPCRESPONSE']._serialized_end=341
|
|
49
|
+
_globals['_GENERICRPCRESPONSE_RESULTSENTRY']._serialized_start=274
|
|
50
|
+
_globals['_GENERICRPCRESPONSE_RESULTSENTRY']._serialized_end=341
|
|
51
|
+
_globals['_ARRAYSHAPE']._serialized_start=343
|
|
52
|
+
_globals['_ARRAYSHAPE']._serialized_end=368
|
|
53
|
+
_globals['_COMPLEXNUMBER']._serialized_start=370
|
|
54
|
+
_globals['_COMPLEXNUMBER']._serialized_end=413
|
|
55
|
+
_globals['_COMPLEXNUMPYARRAY']._serialized_start=415
|
|
56
|
+
_globals['_COMPLEXNUMPYARRAY']._serialized_end=512
|
|
57
|
+
_globals['_REALNUMPYARRAY']._serialized_start=514
|
|
58
|
+
_globals['_REALNUMPYARRAY']._serialized_end=582
|
|
59
|
+
_globals['_ARGUMENT']._serialized_start=585
|
|
60
|
+
_globals['_ARGUMENT']._serialized_end=822
|
|
61
|
+
_globals['_NDARRAYVALUE']._serialized_start=824
|
|
62
|
+
_globals['_NDARRAYVALUE']._serialized_end=882
|
|
63
|
+
_globals['_DYNAMICVALUE']._serialized_start=885
|
|
64
|
+
_globals['_DYNAMICVALUE']._serialized_end=1118
|
|
65
|
+
_globals['_NAMEDVALUE']._serialized_start=1120
|
|
66
|
+
_globals['_NAMEDVALUE']._serialized_end=1186
|
|
67
|
+
_globals['_MUTATION']._serialized_start=1188
|
|
68
|
+
_globals['_MUTATION']._serialized_end=1254
|
|
69
|
+
_globals['_ERRORENVELOPE']._serialized_start=1257
|
|
70
|
+
_globals['_ERRORENVELOPE']._serialized_end=1433
|
|
71
|
+
_globals['_NEGOTIATEREQUEST']._serialized_start=1435
|
|
72
|
+
_globals['_NEGOTIATEREQUEST']._serialized_end=1550
|
|
73
|
+
_globals['_NEGOTIATERESPONSE']._serialized_start=1553
|
|
74
|
+
_globals['_NEGOTIATERESPONSE']._serialized_end=1707
|
|
75
|
+
_globals['_GETSCHEMAREQUEST']._serialized_start=1709
|
|
76
|
+
_globals['_GETSCHEMAREQUEST']._serialized_end=1767
|
|
77
|
+
_globals['_GETSCHEMARESPONSE']._serialized_start=1769
|
|
78
|
+
_globals['_GETSCHEMARESPONSE']._serialized_end=1895
|
|
79
|
+
_globals['_OPENSESSIONREQUEST']._serialized_start=1898
|
|
80
|
+
_globals['_OPENSESSIONREQUEST']._serialized_end=2034
|
|
81
|
+
_globals['_OPENSESSIONRESPONSE']._serialized_start=2037
|
|
82
|
+
_globals['_OPENSESSIONRESPONSE']._serialized_end=2275
|
|
83
|
+
_globals['_INVOKEREQUEST']._serialized_start=2277
|
|
84
|
+
_globals['_INVOKEREQUEST']._serialized_end=2402
|
|
85
|
+
_globals['_INVOKERESPONSE']._serialized_start=2405
|
|
86
|
+
_globals['_INVOKERESPONSE']._serialized_end=2543
|
|
87
|
+
_globals['_CLOSEHANDLEREQUEST']._serialized_start=2545
|
|
88
|
+
_globals['_CLOSEHANDLEREQUEST']._serialized_end=2601
|
|
89
|
+
_globals['_CLOSESESSIONREQUEST']._serialized_start=2603
|
|
90
|
+
_globals['_CLOSESESSIONREQUEST']._serialized_end=2644
|
|
91
|
+
_globals['_CLOSERESPONSE']._serialized_start=2646
|
|
92
|
+
_globals['_CLOSERESPONSE']._serialized_end=2718
|
|
93
|
+
_globals['_SAMPLEFRAME']._serialized_start=2721
|
|
94
|
+
_globals['_SAMPLEFRAME']._serialized_end=3211
|
|
95
|
+
_globals['_GENERICRPC']._serialized_start=3561
|
|
96
|
+
_globals['_GENERICRPC']._serialized_end=3642
|
|
97
|
+
_globals['_DYNAMICCONTROLV2']._serialized_start=3645
|
|
98
|
+
_globals['_DYNAMICCONTROLV2']._serialized_end=4094
|
|
99
|
+
_globals['_SAMPLEDATAV1']._serialized_start=4096
|
|
100
|
+
_globals['_SAMPLEDATAV1']._serialized_end=4178
|
|
101
|
+
# @@protoc_insertion_point(module_scope)
|