dapps 0.2.0__tar.gz → 0.2.2__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 (114) hide show
  1. {dapps-0.2.0 → dapps-0.2.2}/PKG-INFO +42 -8
  2. {dapps-0.2.0 → dapps-0.2.2}/README.md +38 -6
  3. dapps-0.2.2/VERSION +1 -0
  4. {dapps-0.2.0 → dapps-0.2.2}/docs/ground_truth_labeling.md +3 -3
  5. dapps-0.2.2/examples/__init__.py +3 -0
  6. {dapps-0.2.0 → dapps-0.2.2}/examples/simple_dapp.py +2 -2
  7. {dapps-0.2.0 → dapps-0.2.2}/examples/spectrum_dapp.py +62 -38
  8. {dapps-0.2.0 → dapps-0.2.2}/pyproject.toml +17 -3
  9. {dapps-0.2.0 → dapps-0.2.2}/src/dapp/dapp.py +17 -12
  10. dapps-0.2.2/src/e3interface/e3_interface.py +624 -0
  11. dapps-0.2.2/src/e3interface/e3_transport.py +23 -0
  12. dapps-0.2.2/src/e3interface/latrec.py +78 -0
  13. dapps-0.2.2/src/e3interface/libe3_agent.py +304 -0
  14. dapps-0.2.2/src/e3interface/sm_helpers.py +85 -0
  15. dapps-0.2.2/src/simple/defs/__init__.py +0 -0
  16. dapps-0.2.2/src/simple/defs/e3sm_simple.proto +38 -0
  17. dapps-0.2.2/src/simple/defs/e3sm_simple_pb2.py +33 -0
  18. dapps-0.2.2/src/simple/simple_dapp.py +226 -0
  19. dapps-0.2.2/src/spectrum/defs/__init__.py +0 -0
  20. dapps-0.2.2/src/spectrum/defs/e3sm_oai_l1_kpm.proto +36 -0
  21. dapps-0.2.2/src/spectrum/defs/e3sm_oai_l1_kpm_pb2.py +29 -0
  22. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/defs/e3sm_spectrum.asn +77 -3
  23. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/defs/e3sm_spectrum.json +26 -0
  24. dapps-0.2.2/src/spectrum/defs/e3sm_spectrum.proto +89 -0
  25. dapps-0.2.2/src/spectrum/defs/e3sm_spectrum_pb2.py +43 -0
  26. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/e3_ran_buffers_reader.py +45 -6
  27. dapps-0.2.2/src/spectrum/ru_profiles.py +130 -0
  28. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/spectrum_dapp.py +169 -46
  29. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/subcarrier/subcarrier_visualizer.py +2 -1
  30. {dapps-0.2.0 → dapps-0.2.2}/tests/test_indication_dispatch.py +70 -16
  31. dapps-0.2.2/tests/test_latrec.py +66 -0
  32. dapps-0.2.2/tests/test_latrec_stamps.py +211 -0
  33. dapps-0.2.2/tests/test_libe3_agent.py +55 -0
  34. dapps-0.2.2/tests/test_outbound_send_result.py +55 -0
  35. dapps-0.2.2/tests/test_protobuf_sm.py +114 -0
  36. dapps-0.2.2/tests/test_ru_profiles.py +98 -0
  37. dapps-0.2.2/tests/test_spectrum_sm_timestamps_mask.py +80 -0
  38. dapps-0.2.2/tools/regen_proto.sh +20 -0
  39. dapps-0.2.0/VERSION +0 -1
  40. dapps-0.2.0/examples/nvidia_dapp.py +0 -43
  41. dapps-0.2.0/src/e3interface/defs/e3.asn +0 -105
  42. dapps-0.2.0/src/e3interface/defs/e3.json +0 -416
  43. dapps-0.2.0/src/e3interface/e3_connector.py +0 -339
  44. dapps-0.2.0/src/e3interface/e3_encoder.py +0 -473
  45. dapps-0.2.0/src/e3interface/e3_interface.py +0 -564
  46. dapps-0.2.0/src/nvidia/defs/e3sm_nvidia.asn +0 -48
  47. dapps-0.2.0/src/nvidia/defs/e3sm_nvidia.json +0 -40
  48. dapps-0.2.0/src/nvidia/nvidia_dapp.py +0 -74
  49. dapps-0.2.0/src/simple/defs/e3sm_simple.asn +0 -111
  50. dapps-0.2.0/src/simple/simple_dapp.py +0 -285
  51. dapps-0.2.0/src/visualization/__init__.py +0 -4
  52. dapps-0.2.0/tests/test_encoder_roundtrip.py +0 -186
  53. {dapps-0.2.0 → dapps-0.2.2}/.github/CODEOWNERS +0 -0
  54. {dapps-0.2.0 → dapps-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  55. {dapps-0.2.0 → dapps-0.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  56. {dapps-0.2.0 → dapps-0.2.2}/.github/ISSUE_TEMPLATE/documentation.yml +0 -0
  57. {dapps-0.2.0 → dapps-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  58. {dapps-0.2.0 → dapps-0.2.2}/.github/ISSUE_TEMPLATE/new_sm.yml +0 -0
  59. {dapps-0.2.0 → dapps-0.2.2}/.github/ISSUE_TEMPLATE/question.yml +0 -0
  60. {dapps-0.2.0 → dapps-0.2.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  61. {dapps-0.2.0 → dapps-0.2.2}/.github/workflows/mirror.yml +0 -0
  62. {dapps-0.2.0 → dapps-0.2.2}/.github/workflows/tag-and-release.yml +0 -0
  63. {dapps-0.2.0 → dapps-0.2.2}/.github/workflows/test.yml +0 -0
  64. {dapps-0.2.0 → dapps-0.2.2}/.gitignore +0 -0
  65. {dapps-0.2.0 → dapps-0.2.2}/CMakeLists.txt +0 -0
  66. {dapps-0.2.0 → dapps-0.2.2}/CONTRIBUTING.md +0 -0
  67. {dapps-0.2.0 → dapps-0.2.2}/CONTRIBUTORS +0 -0
  68. {dapps-0.2.0 → dapps-0.2.2}/LICENSE +0 -0
  69. {dapps-0.2.0 → dapps-0.2.2}/MANIFEST.in +0 -0
  70. {dapps-0.2.0 → dapps-0.2.2}/SECURITY.md +0 -0
  71. {dapps-0.2.0 → dapps-0.2.2}/docs/dapp_uml.txt +0 -0
  72. {dapps-0.2.0 → dapps-0.2.2}/docs/gui_uml.txt +0 -0
  73. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/CMakeLists.txt +0 -0
  74. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/cmake/libiqsaver.pc.in +0 -0
  75. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/cmake/libiqsaverConfig.cmake.in +0 -0
  76. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/include/iqsaver/annotation.hpp +0 -0
  77. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/include/iqsaver/iq_saver_writer.hpp +0 -0
  78. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/include/iqsaver/iqsaver.hpp +0 -0
  79. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/include/iqsaver/types.hpp +0 -0
  80. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/include/iqsaver/version.hpp.in +0 -0
  81. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/src/iq_saver_writer.cpp +0 -0
  82. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/swig/iqsaver.i +0 -0
  83. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/swig/iqsaver_swig.cpp +0 -0
  84. {dapps-0.2.0 → dapps-0.2.2}/libiqsaver/swig/iqsaver_swig.hpp +0 -0
  85. {dapps-0.2.0 → dapps-0.2.2}/src/dapp/__init__.py +0 -0
  86. {dapps-0.2.0 → dapps-0.2.2}/src/e3interface/__init__.py +0 -0
  87. {dapps-0.2.0 → dapps-0.2.2}/src/e3interface/e3_logging.py +0 -0
  88. {dapps-0.2.0 → dapps-0.2.2}/src/iq_saver/__init__.py +0 -0
  89. {dapps-0.2.0 → dapps-0.2.2}/src/iq_saver/iq_saver.py +0 -0
  90. {dapps-0.2.0 → dapps-0.2.2}/src/simple/__init__.py +0 -0
  91. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/README.md +0 -0
  92. {dapps-0.2.0/src/nvidia → dapps-0.2.2/src/spectrum}/__init__.py +0 -0
  93. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/adaptive_noise_floor.py +0 -0
  94. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/defs/e3sm_oai_l1_kpm.asn +0 -0
  95. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/e3_l2_sensing_reader.py +0 -0
  96. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/embargo_manager.py +0 -0
  97. {dapps-0.2.0 → dapps-0.2.2}/src/spectrum/threshold_detector.py +0 -0
  98. {dapps-0.2.0/src/spectrum → dapps-0.2.2/src/visualization}/__init__.py +0 -0
  99. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/energy.py +0 -0
  100. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/grafana.py +0 -0
  101. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/influx.py +0 -0
  102. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/iq.py +0 -0
  103. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/subcarrier/insi_white.png +0 -0
  104. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/subcarrier/start_subcarrier_visualizer.sh +0 -0
  105. {dapps-0.2.0 → dapps-0.2.2}/src/visualization/subcarrier_pub.py +0 -0
  106. {dapps-0.2.0 → dapps-0.2.2}/start_dapp.sh +0 -0
  107. {dapps-0.2.0 → dapps-0.2.2}/tests/test_e3_concurrency.py +0 -0
  108. {dapps-0.2.0 → dapps-0.2.2}/tests/test_iq_saver.py +0 -0
  109. {dapps-0.2.0 → dapps-0.2.2}/tests/test_sensing_policy.py +0 -0
  110. {dapps-0.2.0 → dapps-0.2.2}/tests/test_spectrum_telemetry.py +0 -0
  111. {dapps-0.2.0 → dapps-0.2.2}/tools/README.md +0 -0
  112. {dapps-0.2.0 → dapps-0.2.2}/tools/asntojson.py +0 -0
  113. {dapps-0.2.0 → dapps-0.2.2}/tools/generate_sm.sh +0 -0
  114. {dapps-0.2.0 → dapps-0.2.2}/tools/read_recording.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dapps
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A library for creating and connecting dApps to Open RAN units.
5
5
  Keywords: dApps,O-RAN,5G,cellular,networks
6
6
  Author-Email: Andrea Lacava <thecave003@gmail.com>
@@ -19,9 +19,9 @@ Requires-Python: >=3.12
19
19
  Requires-Dist: asn1tools
20
20
  Requires-Dist: typing_extensions
21
21
  Requires-Dist: numpy
22
- Requires-Dist: pyzmq
23
22
  Requires-Dist: sigmf
24
23
  Requires-Dist: jsonschema>=4.18.0
24
+ Requires-Dist: protobuf>=4.21
25
25
  Provides-Extra: api
26
26
  Requires-Dist: influxdb_client; extra == "api"
27
27
  Provides-Extra: cnn
@@ -31,6 +31,7 @@ Requires-Dist: flask; extra == "gui"
31
31
  Requires-Dist: flask-sock; extra == "gui"
32
32
  Requires-Dist: flask-cors; extra == "gui"
33
33
  Requires-Dist: matplotlib; extra == "gui"
34
+ Requires-Dist: pyzmq; extra == "gui"
34
35
  Provides-Extra: network
35
36
  Requires-Dist: pysctp; extra == "network"
36
37
  Provides-Extra: all
@@ -39,6 +40,7 @@ Requires-Dist: flask; extra == "all"
39
40
  Requires-Dist: flask-sock; extra == "all"
40
41
  Requires-Dist: flask-cors; extra == "all"
41
42
  Requires-Dist: matplotlib; extra == "all"
43
+ Requires-Dist: pyzmq; extra == "all"
42
44
  Requires-Dist: influxdb_client; extra == "all"
43
45
  Requires-Dist: libiq; extra == "all"
44
46
  Description-Content-Type: text/markdown
@@ -49,6 +51,35 @@ A complete tutorial on how to deploy a dApp can be found on the [OpenRAN Gym web
49
51
 
50
52
  ## Installation
51
53
 
54
+ ### Prerequisite: libe3 Python bindings (`libe3py`)
55
+
56
+ All **E3AP** operations (transport, setup handshake, subscribe, indication/control
57
+ framing, and the ASN.1/JSON/Protobuf wire encoding) are handled by
58
+ [**libe3**](https://github.com/wineslab/libe3); this library keeps only the
59
+ **E3SM** (service-model) encode/decode in Python — the same split OAI uses on the
60
+ RAN side. You must therefore install libe3 with its Python bindings **into the
61
+ same interpreter/venv** that runs the dApp, *before* installing this package:
62
+
63
+ ```bash
64
+ cd ~/libe3
65
+ ./build_libe3 --install --enable-swig \
66
+ --cmake-opt "-DLIBE3_ENABLE_ASN1=ON -DLIBE3_ENABLE_JSON=ON"
67
+ python3 -c "import libe3py; print('libe3py OK')"
68
+ ```
69
+
70
+ The dApp's `--link` / `--transport` / `--encoding` must match the gNB's
71
+ `E3Configuration`. See [libe3's `swig/README.md`](https://github.com/wineslab/libe3/blob/main/swig/README.md)
72
+ for the binding architecture.
73
+
74
+ #### Latency recording (optional)
75
+
76
+ Per-stage latency records (latrec) come from libe3 itself: build the bindings with
77
+ `--cmake-opt "-DLIBE3_ENABLE_LATREC=ON"` and the dApp stamps its E3SM decode,
78
+ processing, output creation, E3SM encode and xApp-policy apply stages into libe3's
79
+ rings, joinable with libe3's own by `tools/latrec2csv.py`. There is no runtime
80
+ switch; against a binding built without it every stamp is a no-op. See libe3's
81
+ `docs/latrec.md`.
82
+
52
83
  ### Python package installation (recommended)
53
84
 
54
85
  ```
@@ -121,8 +152,8 @@ This dApp implements a spectrum sharing use case discussed in [our paper](https:
121
152
 
122
153
  **Spectrum Sensing Configuration:**
123
154
 
124
- - `--noise-floor-threshold` - Set the noise floor threshold (in dB) for determining the presence of incumbents and for detecting the PRBs affected. If not specified, will be auto-detected based on environment
125
- - `--ota` - Specify if the setup is Over-The-Air (OTA) or on Colosseum testbed for using a precalculated noise floor threshold. If `--noise-floor-threshold` is specified, this parameter is ignored
155
+ - `--noise-floor-threshold` - Set the noise floor threshold (in dB) for determining the presence of incumbents and for detecting the PRBs affected. If not specified, the `--ru` profile value is used
156
+ - `--ru` / `-ru` - Radio-unit profile (default: `usrp`) seeding the defaults of `--num-prbs`, `--num-subcarrier-spacing`, `--center-freq`, `--noise-floor-threshold`, `--fp16-beta`, `--sample-rate` and `--max-samples-per-file`. Explicit flags always win. Profiles: `usrp` (alias `x310`), `x410`, `x310-3p58`, `colosseum`, `foxconn`, `foxconn-273`, `benetel`, `rfsim`; see `src/spectrum/ru_profiles.py`. Settings a profile does not calibrate fall back to generic defaults with a warning. This replaces the former `--ota` flag
126
157
 
127
158
  **Visualization Options:**
128
159
 
@@ -134,10 +165,13 @@ This dApp implements a spectrum sharing use case discussed in [our paper](https:
134
165
 
135
166
  **Radio Configuration:**
136
167
 
137
- - `--num-prbs` - Number of Physical Resource Blocks in the channel (default: 106)
138
- - `--num-subcarrier-spacing` - Subcarrier spacing in kHz. Use 30 for FR1 (sub-6 GHz) (default: 30)
168
+ - `--num-prbs` - Number of Physical Resource Blocks in the channel (default: from `--ru`, 106 for `usrp`)
169
+ - `--num-subcarrier-spacing` - Subcarrier spacing in kHz. Use 30 for FR1 (sub-6 GHz) (default: from `--ru`, 30)
139
170
  - `--e` - Enable 3/4 FFT sampling mode. Set this flag if the gNB was started with the `-E` option
140
- - `--center-freq` - Center frequency in Hz (default: 3.6192e9, which is 3.6192 GHz)
171
+ - `--center-freq` - Center frequency in Hz (default: from `--ru`, 3.6192e9 for `usrp`)
172
+ - `--sample-rate` - Front-end (ADC) sample rate in Hz, written as the SigMF `core:sample_rate` (default: from `--ru`; the occupied bandwidth when the profile has none)
173
+ - `--fp16-beta` - FP16 IQ rescale factor, must match the gNB build/config (default: from `--ru`, 1/128 for USRPs and 1/2048 otherwise)
174
+ - `--max-samples-per-file` - SigMF segment length in samples (default: one second of the write rate, `num_prbs * 12 * SCS`)
141
175
 
142
176
  **Execution Control:**
143
177
 
@@ -237,7 +271,7 @@ spear-openairinterface5g/openair2/E3AP/service_models/<sm_name>_sm/
237
271
 
238
272
  For dApp-side (when `--target dapp` or `both`):
239
273
  ```
240
- spear-dApp/src/<sm_name>/
274
+ dApp-library/src/<sm_name>/
241
275
  ├── __init__.py
242
276
  ├── <sm_name>_dapp.py # Main dApp implementation
243
277
  └── defs/
@@ -4,6 +4,35 @@ A complete tutorial on how to deploy a dApp can be found on the [OpenRAN Gym web
4
4
 
5
5
  ## Installation
6
6
 
7
+ ### Prerequisite: libe3 Python bindings (`libe3py`)
8
+
9
+ All **E3AP** operations (transport, setup handshake, subscribe, indication/control
10
+ framing, and the ASN.1/JSON/Protobuf wire encoding) are handled by
11
+ [**libe3**](https://github.com/wineslab/libe3); this library keeps only the
12
+ **E3SM** (service-model) encode/decode in Python — the same split OAI uses on the
13
+ RAN side. You must therefore install libe3 with its Python bindings **into the
14
+ same interpreter/venv** that runs the dApp, *before* installing this package:
15
+
16
+ ```bash
17
+ cd ~/libe3
18
+ ./build_libe3 --install --enable-swig \
19
+ --cmake-opt "-DLIBE3_ENABLE_ASN1=ON -DLIBE3_ENABLE_JSON=ON"
20
+ python3 -c "import libe3py; print('libe3py OK')"
21
+ ```
22
+
23
+ The dApp's `--link` / `--transport` / `--encoding` must match the gNB's
24
+ `E3Configuration`. See [libe3's `swig/README.md`](https://github.com/wineslab/libe3/blob/main/swig/README.md)
25
+ for the binding architecture.
26
+
27
+ #### Latency recording (optional)
28
+
29
+ Per-stage latency records (latrec) come from libe3 itself: build the bindings with
30
+ `--cmake-opt "-DLIBE3_ENABLE_LATREC=ON"` and the dApp stamps its E3SM decode,
31
+ processing, output creation, E3SM encode and xApp-policy apply stages into libe3's
32
+ rings, joinable with libe3's own by `tools/latrec2csv.py`. There is no runtime
33
+ switch; against a binding built without it every stamp is a no-op. See libe3's
34
+ `docs/latrec.md`.
35
+
7
36
  ### Python package installation (recommended)
8
37
 
9
38
  ```
@@ -76,8 +105,8 @@ This dApp implements a spectrum sharing use case discussed in [our paper](https:
76
105
 
77
106
  **Spectrum Sensing Configuration:**
78
107
 
79
- - `--noise-floor-threshold` - Set the noise floor threshold (in dB) for determining the presence of incumbents and for detecting the PRBs affected. If not specified, will be auto-detected based on environment
80
- - `--ota` - Specify if the setup is Over-The-Air (OTA) or on Colosseum testbed for using a precalculated noise floor threshold. If `--noise-floor-threshold` is specified, this parameter is ignored
108
+ - `--noise-floor-threshold` - Set the noise floor threshold (in dB) for determining the presence of incumbents and for detecting the PRBs affected. If not specified, the `--ru` profile value is used
109
+ - `--ru` / `-ru` - Radio-unit profile (default: `usrp`) seeding the defaults of `--num-prbs`, `--num-subcarrier-spacing`, `--center-freq`, `--noise-floor-threshold`, `--fp16-beta`, `--sample-rate` and `--max-samples-per-file`. Explicit flags always win. Profiles: `usrp` (alias `x310`), `x410`, `x310-3p58`, `colosseum`, `foxconn`, `foxconn-273`, `benetel`, `rfsim`; see `src/spectrum/ru_profiles.py`. Settings a profile does not calibrate fall back to generic defaults with a warning. This replaces the former `--ota` flag
81
110
 
82
111
  **Visualization Options:**
83
112
 
@@ -89,10 +118,13 @@ This dApp implements a spectrum sharing use case discussed in [our paper](https:
89
118
 
90
119
  **Radio Configuration:**
91
120
 
92
- - `--num-prbs` - Number of Physical Resource Blocks in the channel (default: 106)
93
- - `--num-subcarrier-spacing` - Subcarrier spacing in kHz. Use 30 for FR1 (sub-6 GHz) (default: 30)
121
+ - `--num-prbs` - Number of Physical Resource Blocks in the channel (default: from `--ru`, 106 for `usrp`)
122
+ - `--num-subcarrier-spacing` - Subcarrier spacing in kHz. Use 30 for FR1 (sub-6 GHz) (default: from `--ru`, 30)
94
123
  - `--e` - Enable 3/4 FFT sampling mode. Set this flag if the gNB was started with the `-E` option
95
- - `--center-freq` - Center frequency in Hz (default: 3.6192e9, which is 3.6192 GHz)
124
+ - `--center-freq` - Center frequency in Hz (default: from `--ru`, 3.6192e9 for `usrp`)
125
+ - `--sample-rate` - Front-end (ADC) sample rate in Hz, written as the SigMF `core:sample_rate` (default: from `--ru`; the occupied bandwidth when the profile has none)
126
+ - `--fp16-beta` - FP16 IQ rescale factor, must match the gNB build/config (default: from `--ru`, 1/128 for USRPs and 1/2048 otherwise)
127
+ - `--max-samples-per-file` - SigMF segment length in samples (default: one second of the write rate, `num_prbs * 12 * SCS`)
96
128
 
97
129
  **Execution Control:**
98
130
 
@@ -192,7 +224,7 @@ spear-openairinterface5g/openair2/E3AP/service_models/<sm_name>_sm/
192
224
 
193
225
  For dApp-side (when `--target dapp` or `both`):
194
226
  ```
195
- spear-dApp/src/<sm_name>/
227
+ dApp-library/src/<sm_name>/
196
228
  ├── __init__.py
197
229
  ├── <sm_name>_dapp.py # Main dApp implementation
198
230
  └── defs/
dapps-0.2.2/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.2
@@ -44,9 +44,9 @@ I/Q recording is only active when `--save-iqs` is passed. Without it, `--ground-
44
44
  ## Commands
45
45
 
46
46
  > [!NOTE]
47
- > `--ota` sets the noise floor to 20 dB, a value calibrated for the X310 USRP. That default
48
- > is **ignored** whenever `--noise-floor-threshold` is explicitly provided — the explicit value
49
- > always takes precedence regardless of `--ota`.
47
+ > `--ru usrp` (the default) sets the noise floor to 20 dB, a value calibrated for the X310 USRP.
48
+ > That default is **ignored** whenever `--noise-floor-threshold` is explicitly provided — the
49
+ > explicit value always takes precedence regardless of `--ru`.
50
50
 
51
51
  The commands below use 53 dB as the noise floor threshold, which matches both the Colosseum
52
52
  testbed and the Foxconn RU calibration. Center frequency is 3.75 GHz with 106 PRBs.
@@ -0,0 +1,3 @@
1
+ """Runnable dApp examples, shipped in the wheel so they can be launched with
2
+ ``python -m examples.spectrum_dapp`` / ``python -m examples.simple_dapp`` from an
3
+ installed ``dapps`` package (e.g. the OAI rfsim CI driving the real dApp)."""
@@ -13,7 +13,7 @@ import argparse
13
13
  import threading
14
14
  import time
15
15
 
16
- from e3interface.e3_connector import E3LinkLayer, E3TransportLayer
16
+ from e3interface.e3_transport import E3LinkLayer, E3TransportLayer
17
17
  from simple.simple_dapp import SimpleDApp
18
18
 
19
19
 
@@ -101,7 +101,7 @@ if __name__ == "__main__":
101
101
  "--encoding",
102
102
  type=str,
103
103
  default="asn1",
104
- choices=["asn1", "json"],
104
+ choices=["asn1", "json", "protobuf"],
105
105
  help="Encoding format (default: asn1)",
106
106
  )
107
107
  parser.add_argument(
@@ -8,12 +8,18 @@ import threading
8
8
  import time
9
9
  import logging
10
10
 
11
- from e3interface.e3_connector import E3LinkLayer, E3TransportLayer
11
+ from e3interface.e3_transport import E3LinkLayer, E3TransportLayer
12
12
  from spectrum.spectrum_dapp import (
13
13
  SpectrumSharingDApp,
14
14
  compute_fft_size,
15
15
  make_periodic_toggle_callback,
16
16
  )
17
+ from spectrum.ru_profiles import (
18
+ DEFAULT_RU,
19
+ RU_CHOICES,
20
+ resolve_ru_settings,
21
+ uncalibrated_keys,
22
+ )
17
23
  from spectrum.threshold_detector import StaticThresholdDetector, AdaptiveThresholdDetector
18
24
 
19
25
  LOG_DIR = '/tmp/'
@@ -37,18 +43,27 @@ def main(args):
37
43
  "--time-window/--moving-avg-window/--extraction-window flags."
38
44
  )
39
45
 
40
- # This value really depends on the RF conditions and the RU used and
41
- # should be carefully calibrated.
42
- if args.noise_floor_threshold:
43
- print('Using custom configuration')
44
- noise_floor_threshold = args.noise_floor_threshold
45
- else:
46
- if args.ota:
47
- print('Using OTA configuration')
48
- noise_floor_threshold = 20
49
- else: # Colosseum
50
- print('Using Colosseum configuration')
51
- noise_floor_threshold = 53
46
+ settings = resolve_ru_settings(args.ru, {
47
+ 'num_prbs': args.num_prbs,
48
+ 'num_subcarrier_spacing': args.num_subcarrier_spacing,
49
+ 'center_freq': args.center_freq,
50
+ 'noise_floor_threshold': args.noise_floor_threshold,
51
+ 'fp16_beta': args.fp16_beta,
52
+ 'front_end_sample_rate': args.sample_rate,
53
+ 'max_samples_per_file': args.max_samples_per_file,
54
+ })
55
+ print(f"Using radio-unit profile '{args.ru}': {settings}")
56
+ uncalibrated = [
57
+ key for key in uncalibrated_keys(args.ru)
58
+ if getattr(args, key, None) is None
59
+ ]
60
+ if uncalibrated:
61
+ print(f"[WARNING] Profile '{args.ru}' has no calibrated value for "
62
+ f"{', '.join(uncalibrated)}; using generic defaults")
63
+
64
+ # The noise floor depends on the RF conditions and the RU and should be
65
+ # carefully calibrated.
66
+ noise_floor_threshold = settings['noise_floor_threshold']
52
67
 
53
68
  print(f'Threshold is {noise_floor_threshold}')
54
69
 
@@ -56,7 +71,7 @@ def main(args):
56
71
  # Build the detection strategy explicitly so the example is the
57
72
  # authoritative place for detector configuration.
58
73
  # ------------------------------------------------------------------
59
- fft_size = compute_fft_size(args.num_prbs, args.e)
74
+ fft_size = compute_fft_size(settings['num_prbs'], args.e)
60
75
 
61
76
  if args.use_adaptive_noise_floor:
62
77
  detector = AdaptiveThresholdDetector(
@@ -95,13 +110,14 @@ def main(args):
95
110
  viz_web_port=args.viz_web_port,
96
111
  viz_zmq_port=args.viz_zmq_port,
97
112
  external_viz=args.external_viz,
98
- center_freq=args.center_freq,
99
- num_prbs=args.num_prbs,
113
+ center_freq=settings['center_freq'],
114
+ num_prbs=settings['num_prbs'],
100
115
  e_sampling=args.e,
101
- num_subcarrier_spacing=args.num_subcarrier_spacing,
116
+ num_subcarrier_spacing=settings['num_subcarrier_spacing'],
102
117
  sampling_threshold=args.sampling_threshold,
103
- max_samples_per_file=args.max_samples_per_file,
104
- fp16_beta=args.fp16_beta,
118
+ max_samples_per_file=settings['max_samples_per_file'],
119
+ fp16_beta=settings['fp16_beta'],
120
+ front_end_sample_rate=settings['front_end_sample_rate'],
105
121
  sensing_only=args.sensing_only,
106
122
  strict_sensing=args.strict_sensing,
107
123
  min_sensing_symbols=args.min_sensing_symbols,
@@ -191,16 +207,21 @@ if __name__ == "__main__":
191
207
  help="Send PRB-block control messages to the gNB when "
192
208
  "PRBs are detected above the noise threshold")
193
209
  parser.add_argument('--noise-floor-threshold', type=int, default=None,
194
- help="Detection threshold in dB (static) or dB above noise floor (adaptive)")
210
+ help="Detection threshold in dB (static) or dB above noise floor (adaptive); "
211
+ "default: from --ru")
195
212
  parser.add_argument('--use-adaptive-noise-floor', action='store_true', default=False,
196
213
  help="Use per-bin median noise floor estimation instead of a fixed threshold")
197
214
  parser.add_argument('--embargo-timeout-secs', type=float, default=10.1,
198
215
  help="Hold time in seconds for embargoed PRBs after last detection (adaptive mode)")
199
216
  parser.add_argument('--average-over-frames', type=int, default=64,
200
217
  help="Number of frames to average before each decision")
201
- parser.add_argument('--ota', action='store_true', default=False,
202
- help="Use OTA threshold (20 dB) instead of Colosseum (53 dB). "
203
- "Ignored when --noise-floor-threshold is set.")
218
+ parser.add_argument('--ru', '-ru', type=str, default=DEFAULT_RU,
219
+ choices=RU_CHOICES,
220
+ help="Radio-unit profile seeding the defaults of --num-prbs, "
221
+ "--num-subcarrier-spacing, --center-freq, "
222
+ "--noise-floor-threshold, --fp16-beta, --sample-rate and "
223
+ "--max-samples-per-file. Explicit flags always win. "
224
+ f"Default: {DEFAULT_RU}.")
204
225
  parser.add_argument('--energy-gui', action='store_true', default=False,
205
226
  help="Enable energy spectrum visualization")
206
227
  parser.add_argument('--iq-plotter-gui', action='store_true', default=False,
@@ -213,14 +234,18 @@ if __name__ == "__main__":
213
234
  help="ZMQ port the dApp publishes on / the visualizer reads (default 5559)")
214
235
  parser.add_argument('--external-viz', action='store_true', default=False,
215
236
  help="Don't spawn the visualizer; publish to an already-running one")
216
- parser.add_argument('--num-prbs', type=int, default=106,
217
- help="Number of PRBs")
218
- parser.add_argument('--num-subcarrier-spacing', type=int, default=30,
219
- help="Subcarrier spacing in kHz (FR1 = 30)")
237
+ parser.add_argument('--num-prbs', type=int, default=None,
238
+ help="Number of PRBs (default: from --ru)")
239
+ parser.add_argument('--num-subcarrier-spacing', type=int, default=None,
240
+ help="Subcarrier spacing in kHz (FR1 = 30; default: from --ru)")
220
241
  parser.add_argument('--e', action='store_true', default=False,
221
242
  help="Enable 3/4 FFT sampling (OAI -E flag for USRPs)")
222
- parser.add_argument('--center-freq', type=float, default=3.6192e9,
223
- help="RF center frequency in Hz")
243
+ parser.add_argument('--center-freq', type=float, default=None,
244
+ help="RF center frequency in Hz (default: from --ru)")
245
+ parser.add_argument('--sample-rate', type=float, default=None, metavar='HZ',
246
+ help="Front-end (ADC) sample rate in Hz, written as the SigMF "
247
+ "core:sample_rate (default: from --ru; when the profile has "
248
+ "none, the occupied bandwidth)")
224
249
  parser.add_argument('--timed', type=int, default=0, metavar='SECONDS',
225
250
  help="Stop automatically after SECONDS (0 = run indefinitely)")
226
251
  parser.add_argument('--model', type=str, default='',
@@ -233,25 +258,24 @@ if __name__ == "__main__":
233
258
  help="Samples to retain after CNN energy peak detection")
234
259
  parser.add_argument('--sampling-threshold', type=int, default=5,
235
260
  help="Render a new dashboard frame every N IQ batches (visualization only, does not affect IQ delivery or recording)")
236
- parser.add_argument('--max-samples-per-file', type=int, default=46_080_000,
261
+ parser.add_argument('--max-samples-per-file', type=int, default=None,
237
262
  help="Rotate the SigMF capture file once a segment reaches "
238
- "this many true IQ samples (default 46080000). Each "
263
+ "this many true IQ samples (default: one second of the "
264
+ "dApp's write rate, num_prbs * 12 * SCS). Each "
239
265
  "indication is kept whole, so a segment may exceed the "
240
266
  "threshold by up to one indication's worth of samples. "
241
- "Segment wall-clock duration depends on the capture rate. "
242
267
  "Only used with --save-iqs.")
243
268
  parser.add_argument('--ground-truth', type=str, default='', metavar='LABEL',
244
269
  help="Initial ground truth label written into IQ annotations "
245
270
  "(only used with --save-iqs). "
246
271
  "Updatable at runtime via the dashboard GUI "
247
272
  "when --demo-gui is also set.")
248
- parser.add_argument('--fp16-beta', type=float, default=1.0 / 2048.0,
249
- help="FP16 IQ rescale factor; MUST match the gNB "
250
- "E3Configuration.fp16_beta (the reader rescales by "
251
- "1/beta). Default 1/2048 matches the gNB code default; "
252
- "the X410 sample conf overrides it to 0.0078125 (1/128).")
273
+ parser.add_argument('--fp16-beta', type=float, default=None,
274
+ help="FP16 IQ rescale factor; MUST match the gNB build/config "
275
+ "(the reader rescales by 1/beta). Default from --ru: "
276
+ "1/128 for USRPs, 1/2048 otherwise.")
253
277
  parser.add_argument('--encoding-method', type=str, default='asn1',
254
- choices=['asn1', 'json'],
278
+ choices=['asn1', 'json', 'protobuf'],
255
279
  help="Wire encoding for Spectrum-* envelopes (default: asn1).")
256
280
  parser.add_argument('--no-sensing-only', dest='sensing_only',
257
281
  action='store_false', default=True,
@@ -19,13 +19,23 @@ classifiers = [
19
19
  ]
20
20
  license = "Apache-2.0"
21
21
  license-files = ["LICENSE"]
22
+ # NOTE: E3AP transport is provided by libe3's Python bindings (`libe3py`),
23
+ # which are not on PyPI. **libe3 >= 0.2.0 is required**: 0.2.0 added the
24
+ # correlation id that DAppSession.send_report() now takes as a mandatory
25
+ # argument, so an older binding fails at the E3 setup handshake against a
26
+ # matching gNB. Install them into this environment with:
27
+ # cd ~/libe3 && ./build_libe3 --install --enable-swig \
28
+ # --cmake-opt "-DLIBE3_ENABLE_ASN1=ON -DLIBE3_ENABLE_JSON=ON"
29
+ # (asn1tools is still required for the in-Python service-model codecs.)
22
30
  dependencies = [
23
31
  "asn1tools",
24
32
  "typing_extensions",
25
33
  "numpy",
26
- "pyzmq",
27
34
  "sigmf",
28
35
  "jsonschema>=4.18.0",
36
+ # protobuf E3SM codec path (spectrum/simple SMs); >=4.21 for FieldDescriptor
37
+ # .has_presence and the current generated-code format.
38
+ "protobuf>=4.21",
29
39
  ]
30
40
 
31
41
  authors = [
@@ -43,7 +53,7 @@ Source = "https://github.com/wineslab/dApp-library"
43
53
  [project.optional-dependencies]
44
54
  api = ["influxdb_client"]
45
55
  cnn = ["libiq"]
46
- gui = ["flask", "flask-sock", "flask-cors", "matplotlib"]
56
+ gui = ["flask", "flask-sock", "flask-cors", "matplotlib", "pyzmq"]
47
57
  network = ["pysctp"]
48
58
  all = [
49
59
  "pysctp",
@@ -51,6 +61,7 @@ all = [
51
61
  "flask-sock",
52
62
  "flask-cors",
53
63
  "matplotlib",
64
+ "pyzmq",
54
65
  "influxdb_client",
55
66
  "libiq",
56
67
  ]
@@ -68,10 +79,13 @@ wheel.packages = [
68
79
  "src/dapp",
69
80
  "src/e3interface",
70
81
  "src/spectrum",
71
- "src/nvidia",
72
82
  "src/simple",
73
83
  "src/iq_saver",
74
84
  "src/visualization",
85
+ # Ship the runnable examples so they can be launched from the installed
86
+ # wheel via `python -m examples.<name>` (e.g. the OAI rfsim CI driving the
87
+ # real spectrum dApp) instead of a hand-rolled launcher.
88
+ "examples",
75
89
  ]
76
90
 
77
91
  # Include the libiqsaver C++ tree and the root CMake in the sdist so
@@ -10,7 +10,6 @@ from abc import ABC, abstractmethod
10
10
  import threading
11
11
  import time
12
12
  from e3interface.e3_interface import E3Interface
13
- from e3interface.e3_encoder import AsnE3Encoder, JsonE3Encoder
14
13
  from e3interface.e3_logging import dapp_logger
15
14
 
16
15
  class DApp(ABC):
@@ -30,16 +29,14 @@ class DApp(ABC):
30
29
  self.dapp_version = dapp_version
31
30
  self.vendor = vendor
32
31
  self.e3ap_protocol_version = e3ap_protocol_version
33
- self.encoding_method = encoding_method
34
- match self.encoding_method:
35
- case "asn1":
36
- encoder = AsnE3Encoder()
37
- case "json":
38
- encoder = JsonE3Encoder()
39
- case _:
40
- raise ValueError(f"Unsupported encoding method: {self.encoding_method}")
41
-
42
- self.e3_interface = E3Interface(encoder=encoder, link=link, transport=transport)
32
+ self.encoding_method = encoding_method
33
+ if encoding_method not in ("asn1", "json", "protobuf"):
34
+ raise ValueError(f"Unsupported encoding method: {encoding_method}")
35
+
36
+ # libe3 owns E3AP (transport + wire encoding); the encoding string is
37
+ # passed straight through and must match the gNB E3Configuration.
38
+ self.e3_interface = E3Interface(
39
+ link=link, transport=transport, encoding=encoding_method)
43
40
  self.stop_event = threading.Event()
44
41
 
45
42
  dapp_logger.info(f'Using {link} and {transport}')
@@ -146,7 +143,15 @@ class DApp(ABC):
146
143
  return scheduled
147
144
 
148
145
  @abstractmethod
149
- def _handle_xapp_control(self, dapp_identifier: int, data: bytes):
146
+ def _handle_xapp_control(self, dapp_identifier: int, data: bytes, sequence_id: int = 0):
147
+ """Handle a control addressed to this dApp by an xApp.
148
+
149
+ ``sequence_id`` is the correlation id of the xApp procedure. Echo it on
150
+ the control re-issued to the RAN so the RAN can report back when the
151
+ decision reached the air. Implementations written before the id existed
152
+ may still take two arguments; E3Interface detects that and calls them
153
+ the old way.
154
+ """
150
155
  # This in the future might become a class
151
156
  pass
152
157