RemoteRF 2.0.11__tar.gz → 2.0.13__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.
Files changed (65) hide show
  1. {remoterf-2.0.11/src/remoterf.egg-info → remoterf-2.0.13}/PKG-INFO +1 -1
  2. {remoterf-2.0.11 → remoterf-2.0.13}/setup.py +1 -1
  3. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/utils/banner.py +2 -2
  4. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/dynamic_device.py +5 -1
  5. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/hackrf/hackrf_remote.py +58 -10
  6. {remoterf-2.0.11 → remoterf-2.0.13/src/remoterf.egg-info}/PKG-INFO +1 -1
  7. {remoterf-2.0.11 → remoterf-2.0.13}/tests/test_banner.py +3 -3
  8. {remoterf-2.0.11 → remoterf-2.0.13}/tests/test_dynamic_device_codegen.py +11 -0
  9. {remoterf-2.0.11 → remoterf-2.0.13}/LICENSE +0 -0
  10. {remoterf-2.0.11 → remoterf-2.0.13}/MANIFEST.in +0 -0
  11. {remoterf-2.0.11 → remoterf-2.0.13}/README.md +0 -0
  12. {remoterf-2.0.11 → remoterf-2.0.13}/setup.cfg +0 -0
  13. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/__init__.py +0 -0
  14. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/__init__.py +0 -0
  15. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/grpc/__init__.py +0 -0
  16. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/grpc/grpc_pb2.py +0 -0
  17. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
  18. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/grpc/v2_codec.py +0 -0
  19. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/tos_notice.txt +0 -0
  20. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/utils/__init__.py +0 -0
  21. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/utils/ansi_codes.py +0 -0
  22. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/utils/api_token.py +0 -0
  23. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/utils/list_string.py +0 -0
  24. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/common/utils/process_arg.py +0 -0
  25. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/config/__init__.py +0 -0
  26. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/config/cert_fetcher.py +0 -0
  27. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/config/config.py +0 -0
  28. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/__init__.py +0 -0
  29. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/acc_login.py +0 -0
  30. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/app.py +0 -0
  31. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/banner_note.txt +0 -0
  32. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/dynamic_v2_transport.py +0 -0
  33. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/grpc_acc.py +0 -0
  34. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/grpc_client.py +0 -0
  35. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/core/v2_errors.py +0 -0
  36. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/__init__.py +0 -0
  37. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/_virtual.py +0 -0
  38. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
  39. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
  40. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/dynamic_v2.py +0 -0
  41. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/hackrf/__init__.py +0 -0
  42. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/rtl_sdr/__init__.py +0 -0
  43. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/rtl_sdr/rtl_sdr_remote.py +0 -0
  44. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/support/__init__.py +0 -0
  45. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/support/ti_mmwave.py +0 -0
  46. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/support/uhd.py +0 -0
  47. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/support/uhd_dsp.py +0 -0
  48. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/support/uhd_v2.py +0 -0
  49. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/ti_mmwave/__init__.py +0 -0
  50. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/ti_mmwave/ti_mmwave_remote.py +0 -0
  51. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/usrp/__init__.py +0 -0
  52. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/usrp/usrp_remote.py +0 -0
  53. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/drivers/usrp/usrp_remote.pyi +0 -0
  54. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/remoterf_cli.py +0 -0
  55. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoteRF/version.py +0 -0
  56. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoterf.egg-info/SOURCES.txt +0 -0
  57. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoterf.egg-info/dependency_links.txt +0 -0
  58. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoterf.egg-info/entry_points.txt +0 -0
  59. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoterf.egg-info/requires.txt +0 -0
  60. {remoterf-2.0.11 → remoterf-2.0.13}/src/remoterf.egg-info/top_level.txt +0 -0
  61. {remoterf-2.0.11 → remoterf-2.0.13}/tests/test_dynamic_v2.py +0 -0
  62. {remoterf-2.0.11 → remoterf-2.0.13}/tests/test_pluto_virtual.py +0 -0
  63. {remoterf-2.0.11 → remoterf-2.0.13}/tests/test_process_arg.py +0 -0
  64. {remoterf-2.0.11 → remoterf-2.0.13}/tests/test_remoterf_cli.py +0 -0
  65. {remoterf-2.0.11 → remoterf-2.0.13}/tests/test_ti_mmwave.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 2.0.11
3
+ Version: 2.0.13
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -21,7 +21,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
21
21
 
22
22
  setup(
23
23
  name="remoterf",
24
- version="2.0.11",
24
+ version="2.0.13",
25
25
  author="Ethan Ge",
26
26
  author_email="ethoGalaxy@gmail.com",
27
27
  description="A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.",
@@ -47,7 +47,7 @@ COMPACT_BANNER = "RemoteRF"
47
47
  ASCII_WORDMARK_WIDTH = max(len(line) for line in ASCII_BANNER.splitlines())
48
48
  ASCII_BANNER_WIDTH = max(len(line) for line in BRANDED_ASCII_BANNER.splitlines())
49
49
  ASCII_BANNER_MIN_COLUMNS = ASCII_BANNER_WIDTH + 1
50
- PANEL_CONTENT_WIDTH = 31
50
+ PANEL_CONTENT_WIDTH = 35
51
51
  PANEL_INNER_WIDTH = PANEL_CONTENT_WIDTH + 2
52
52
  PANEL_WIDTH = PANEL_INNER_WIDTH + 2
53
53
  PANEL_GAP = 1
@@ -113,7 +113,7 @@ def _panel_lines(
113
113
  top = f"{top_left}{label}{horizontal * (PANEL_INNER_WIDTH - len(label))}{top_right}"
114
114
  content = (
115
115
  f"{marker} www.remoterf.net",
116
- "Created by E. Ge and I. Roberts",
116
+ "Created by the Wireless Lab at UCLA",
117
117
  f"VERSION {version}",
118
118
  f"SERVER {server or 'NA'}",
119
119
  )
@@ -1070,6 +1070,8 @@ def _emit_client_object_bindings(client_objects: dict, *, class_name: str) -> li
1070
1070
  # Regular string (not f-string) — {_PREFIX}/{prop}/{e} are literal text
1071
1071
  # that become f-strings inside the *generated* file.
1072
1072
  _HELPERS = '''\
1073
+ import os
1074
+
1073
1075
  from importlib import import_module as _import_module
1074
1076
 
1075
1077
  from ...common.utils import map_arg, unmap_arg
@@ -1230,7 +1232,9 @@ def _codegen(schema: dict) -> str:
1230
1232
  f' self.token = make_virtual_token(token, device_type="{device_type}")',
1231
1233
  " return",
1232
1234
  ' if token is None:',
1233
- ' raise ValueError("A reservation token is required unless virtual=True")',
1235
+ ' token = os.getenv("REMOTERF_TOKEN")',
1236
+ ' if not token:',
1237
+ ' raise ValueError("A reservation token is required; pass token= or set REMOTERF_TOKEN")',
1234
1238
  " self.token = token",
1235
1239
  " from ..dynamic_device import install_driver_if_stale",
1236
1240
  " install_driver_if_stale(token=token, current_hash=_SCHEMA_HASH)",
@@ -14,21 +14,35 @@
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
15
 
16
16
  # Auto-generated from IDL schema — do not edit by hand.
17
- # device_type: hackrf driver_version: 0.1.1 schema_hash: sha256:1d6e935e2e33959e753b89d943a1a5696d814ec28aac52a7c39142b3fce89693
17
+ # device_type: hackrf driver_version: 0.1.1 schema_hash: sha256:6fb625ce99aaf7e190de013ad3e66276265020a5464d9d55d4c0e076dae28f3c
18
18
 
19
19
  _PREFIX = "Hackrf"
20
- _SCHEMA_HASH = "sha256:1d6e935e2e33959e753b89d943a1a5696d814ec28aac52a7c39142b3fce89693"
20
+ _SCHEMA_HASH = "sha256:6fb625ce99aaf7e190de013ad3e66276265020a5464d9d55d4c0e076dae28f3c"
21
21
  _CLIENT_MODULES = {}
22
22
  _CLIENT_OBJECTS = {}
23
23
 
24
+ import os
25
+
24
26
  from importlib import import_module as _import_module
25
27
 
26
- from ...core.grpc_client import rpc_client
27
28
  from ...common.utils import map_arg, unmap_arg
29
+ from .._virtual import (
30
+ is_virtual_token,
31
+ make_virtual_token,
32
+ virtual_call,
33
+ virtual_get,
34
+ virtual_set,
35
+ )
28
36
 
29
37
  _NO_ARG = object()
30
38
 
31
39
 
40
+ def _rpc_client(*, function_name, args):
41
+ from ...core.grpc_client import rpc_client
42
+
43
+ return rpc_client(function_name=function_name, args=args)
44
+
45
+
32
46
  for _alias, _module_path in _CLIENT_MODULES.items():
33
47
  globals()[_alias] = _import_module(_module_path)
34
48
 
@@ -71,27 +85,34 @@ def _wrap_client_return(spec, self_obj, result):
71
85
 
72
86
 
73
87
  def _try_get(prop, token):
74
- return unmap_arg(rpc_client(
88
+ if is_virtual_token(token):
89
+ return virtual_get(token, prop)
90
+ return unmap_arg(_rpc_client(
75
91
  function_name=f"{_PREFIX}:{prop}:GET",
76
92
  args={'a': map_arg(token)},
77
93
  ).results[prop])
78
94
 
79
95
 
80
96
  def _try_set(prop, value, token):
81
- rpc_client(
97
+ if is_virtual_token(token):
98
+ virtual_set(token, prop, value)
99
+ return None
100
+ _rpc_client(
82
101
  function_name=f"{_PREFIX}:{prop}:SET",
83
102
  args={prop: map_arg(value), 'a': map_arg(token)},
84
103
  )
85
104
 
86
105
 
87
106
  def _try_call(prop, token, arg=_NO_ARG):
107
+ if is_virtual_token(token):
108
+ return virtual_call(token, prop, arg, has_arg=arg is not _NO_ARG)
88
109
  if arg is _NO_ARG:
89
- resp = rpc_client(
110
+ resp = _rpc_client(
90
111
  function_name=f"{_PREFIX}:{prop}:CALL0",
91
112
  args={'a': map_arg(token)},
92
113
  )
93
114
  else:
94
- resp = rpc_client(
115
+ resp = _rpc_client(
95
116
  function_name=f"{_PREFIX}:{prop}:CALL1",
96
117
  args={'a': map_arg(token), 'arg1': map_arg(arg)},
97
118
  )
@@ -100,12 +121,15 @@ def _try_call(prop, token, arg=_NO_ARG):
100
121
 
101
122
 
102
123
  def _try_calln(prop, token, kwargs):
124
+ if is_virtual_token(token):
125
+ filtered = {key: value for key, value in dict(kwargs).items() if value is not _NO_ARG}
126
+ return virtual_call(token, prop, filtered, has_arg=True)
103
127
  payload = {'a': map_arg(token)}
104
128
  for key, value in dict(kwargs).items():
105
129
  if value is _NO_ARG:
106
130
  continue
107
131
  payload[str(key)] = map_arg(value)
108
- resp = rpc_client(
132
+ resp = _rpc_client(
109
133
  function_name=f"{_PREFIX}:{prop}:CALLN",
110
134
  args=payload,
111
135
  )
@@ -114,7 +138,15 @@ def _try_calln(prop, token, kwargs):
114
138
 
115
139
  class HackRF:
116
140
 
117
- def __init__(self, token: str):
141
+ def __init__(self, token: str = None, *, virtual: bool = False):
142
+ self.virtual = bool(virtual)
143
+ if self.virtual:
144
+ self.token = make_virtual_token(token, device_type="hackrf")
145
+ return
146
+ if token is None:
147
+ token = os.getenv("REMOTERF_TOKEN")
148
+ if not token:
149
+ raise ValueError("A reservation token is required; pass token= or set REMOTERF_TOKEN")
118
150
  self.token = token
119
151
  from ..dynamic_device import install_driver_if_stale
120
152
  install_driver_if_stale(token=token, current_hash=_SCHEMA_HASH)
@@ -204,6 +236,10 @@ class HackRF:
204
236
  'Clear any captured or queued IQ bytes while the radio is idle.'
205
237
  return _try_call("clear_buffer", self.token)
206
238
 
239
+ def close(self):
240
+ 'Safely end this remote TX use without closing the server-owned radio.'
241
+ return _try_call("close", self.token)
242
+
207
243
  def enumerate(self):
208
244
  'Return attached serials as a JSON array for broad client compatibility.'
209
245
  return _try_call("enumerate", self.token)
@@ -213,7 +249,7 @@ class HackRF:
213
249
  return _try_call("get_serial_no", self.token)
214
250
 
215
251
  def load_tx_iq(self, samples):
216
- 'Convert normalized IQ samples and load the native transmit buffer.'
252
+ 'Load normalized complex or native interleaved signed-int8 TX IQ.'
217
253
  return _try_call("load_tx_iq", self.token, samples)
218
254
 
219
255
  def read_samples(self, num_samples=_NO_ARG):
@@ -222,6 +258,18 @@ class HackRF:
222
258
  "num_samples": num_samples,
223
259
  })
224
260
 
261
+ def set_freq(self, value):
262
+ 'Compatibility alias for setting the center frequency in Hz.'
263
+ return _try_call("set_freq", self.token, value)
264
+
265
+ def set_sample_rate(self, value):
266
+ 'Compatibility alias for setting the complex sample rate in Hz.'
267
+ return _try_call("set_sample_rate", self.token, value)
268
+
269
+ def setup(self):
270
+ 'Confirm the already-managed remote HackRF is ready for use.'
271
+ return _try_call("setup", self.token)
272
+
225
273
  def start_tx(self):
226
274
  'Start transmitting the IQ bytes previously loaded with load_tx_iq().'
227
275
  return _try_call("start_tx", self.token)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 2.0.11
3
+ Version: 2.0.13
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -47,11 +47,11 @@ class BannerTests(unittest.TestCase):
47
47
  index for index, line in enumerate(lines) if "╭─ REMOTERF CLIENT" in line
48
48
  )
49
49
  self.assertIn("◆ www.remoterf.net", lines[panel_top + 1])
50
- self.assertIn("Created by E. Ge and I. Roberts", lines[panel_top + 2])
50
+ self.assertIn("Created by the Wireless Lab at UCLA", lines[panel_top + 2])
51
51
  self.assertIn("VERSION 2.0.9 (latest)", lines[panel_top + 3])
52
52
  self.assertIn("SERVER 192.0.2.10:61005", lines[panel_top + 4])
53
53
  self.assertNotIn("STATUS", text)
54
- self.assertNotIn("UCLA", text)
54
+ self.assertIn("UCLA", text)
55
55
  self.assertNotIn("TIME", text)
56
56
  self.assertTrue(all(len(line) < SIDE_BY_SIDE_MIN_COLUMNS for line in lines))
57
57
 
@@ -81,7 +81,7 @@ class BannerTests(unittest.TestCase):
81
81
  self.assertIn(ASCII_COMPAT_BANNER.splitlines()[0], text)
82
82
  self.assertNotIn("█", text)
83
83
  self.assertIn("* www.remoterf.net", text)
84
- self.assertIn("Created by E. Ge and I. Roberts", text)
84
+ self.assertIn("Created by the Wireless Lab at UCLA", text)
85
85
  self.assertIn("SERVER rrf.example:61005", text)
86
86
  self.assertNotIn("TIME", text)
87
87
 
@@ -13,6 +13,7 @@
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
15
 
16
+ import os
16
17
  import unittest
17
18
  import sys
18
19
  import tempfile
@@ -413,6 +414,15 @@ class DynamicDeviceCodegenTests(unittest.TestCase):
413
414
  exec(code, module.__dict__)
414
415
 
415
416
  device = module.FakeDevice("token")
417
+ old_token = os.environ.get("REMOTERF_TOKEN")
418
+ os.environ["REMOTERF_TOKEN"] = "token-from-env"
419
+ try:
420
+ self.assertEqual(module.FakeDevice().token, "token-from-env")
421
+ finally:
422
+ if old_token is None:
423
+ os.environ.pop("REMOTERF_TOKEN", None)
424
+ else:
425
+ os.environ["REMOTERF_TOKEN"] = old_token
416
426
  self.assertEqual(device.status, {"raw": True})
417
427
  self.assertEqual(device.echo({"payload": 3}), {"payload": 3})
418
428
  self.assertEqual(device.configure(1.0), {"configured": True})
@@ -423,6 +433,7 @@ class DynamicDeviceCodegenTests(unittest.TestCase):
423
433
  self.assertEqual(
424
434
  [item[0] for item in seen],
425
435
  [
436
+ "Fake_device:ip:CALL0",
426
437
  "Fake_device:ip:CALL0",
427
438
  "Fake_device:status:GET",
428
439
  "Fake_device:echo:CALL1",
File without changes
File without changes
File without changes
File without changes