dapps 0.1.0__tar.gz → 0.2.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.
- {dapps-0.1.0 → dapps-0.2.0}/.github/ISSUE_TEMPLATE/new_sm.yml +1 -1
- {dapps-0.1.0 → dapps-0.2.0}/.github/PULL_REQUEST_TEMPLATE.md +3 -3
- {dapps-0.1.0 → dapps-0.2.0}/.github/workflows/tag-and-release.yml +8 -3
- {dapps-0.1.0 → dapps-0.2.0}/.github/workflows/test.yml +8 -3
- dapps-0.2.0/CMakeLists.txt +22 -0
- {dapps-0.1.0 → dapps-0.2.0}/CONTRIBUTING.md +4 -4
- dapps-0.2.0/MANIFEST.in +6 -0
- {dapps-0.1.0 → dapps-0.2.0}/PKG-INFO +56 -23
- {dapps-0.1.0 → dapps-0.2.0}/README.md +32 -1
- dapps-0.2.0/VERSION +1 -0
- {dapps-0.1.0 → dapps-0.2.0}/docs/ground_truth_labeling.md +4 -4
- {dapps-0.1.0 → dapps-0.2.0}/examples/spectrum_dapp.py +112 -30
- dapps-0.2.0/libiqsaver/CMakeLists.txt +274 -0
- dapps-0.2.0/libiqsaver/cmake/libiqsaver.pc.in +10 -0
- dapps-0.2.0/libiqsaver/cmake/libiqsaverConfig.cmake.in +9 -0
- dapps-0.2.0/libiqsaver/include/iqsaver/annotation.hpp +6 -0
- dapps-0.2.0/libiqsaver/include/iqsaver/iq_saver_writer.hpp +72 -0
- dapps-0.2.0/libiqsaver/include/iqsaver/iqsaver.hpp +17 -0
- dapps-0.2.0/libiqsaver/include/iqsaver/types.hpp +31 -0
- dapps-0.2.0/libiqsaver/include/iqsaver/version.hpp.in +10 -0
- dapps-0.2.0/libiqsaver/src/iq_saver_writer.cpp +536 -0
- dapps-0.2.0/libiqsaver/swig/iqsaver.i +38 -0
- dapps-0.2.0/libiqsaver/swig/iqsaver_swig.cpp +107 -0
- dapps-0.2.0/libiqsaver/swig/iqsaver_swig.hpp +85 -0
- dapps-0.2.0/pyproject.toml +89 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/dapp/dapp.py +5 -2
- {dapps-0.1.0 → dapps-0.2.0}/src/e3interface/defs/e3.json +63 -141
- {dapps-0.1.0 → dapps-0.2.0}/src/e3interface/e3_connector.py +39 -4
- {dapps-0.1.0 → dapps-0.2.0}/src/e3interface/e3_encoder.py +125 -28
- {dapps-0.1.0 → dapps-0.2.0}/src/e3interface/e3_interface.py +241 -144
- dapps-0.2.0/src/iq_saver/iq_saver.py +279 -0
- dapps-0.2.0/src/simple/defs/e3sm_simple.asn +111 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/simple/simple_dapp.py +52 -6
- dapps-0.2.0/src/spectrum/README.md +56 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/spectrum/adaptive_noise_floor.py +22 -3
- dapps-0.2.0/src/spectrum/defs/e3sm_oai_l1_kpm.asn +97 -0
- dapps-0.2.0/src/spectrum/defs/e3sm_spectrum.asn +156 -0
- dapps-0.2.0/src/spectrum/defs/e3sm_spectrum.json +250 -0
- dapps-0.2.0/src/spectrum/e3_l2_sensing_reader.py +155 -0
- dapps-0.2.0/src/spectrum/e3_ran_buffers_reader.py +338 -0
- dapps-0.2.0/src/spectrum/spectrum_dapp.py +2055 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/spectrum/threshold_detector.py +32 -2
- dapps-0.2.0/src/visualization/subcarrier/insi_white.png +0 -0
- dapps-0.2.0/src/visualization/subcarrier/start_subcarrier_visualizer.sh +51 -0
- dapps-0.2.0/src/visualization/subcarrier/subcarrier_visualizer.py +2504 -0
- dapps-0.2.0/src/visualization/subcarrier_pub.py +192 -0
- dapps-0.2.0/start_dapp.sh +2 -0
- dapps-0.2.0/tests/test_e3_concurrency.py +68 -0
- dapps-0.2.0/tests/test_encoder_roundtrip.py +186 -0
- dapps-0.2.0/tests/test_indication_dispatch.py +182 -0
- {dapps-0.1.0 → dapps-0.2.0}/tests/test_iq_saver.py +215 -12
- dapps-0.2.0/tests/test_sensing_policy.py +253 -0
- dapps-0.2.0/tests/test_spectrum_telemetry.py +87 -0
- dapps-0.1.0/.github/workflows/publish-docker-image.yml +0 -54
- dapps-0.1.0/MANIFEST.in +0 -5
- dapps-0.1.0/VERSION +0 -1
- dapps-0.1.0/docker/Dockerfile-oai-dapp +0 -130
- dapps-0.1.0/docker/Dockerfile-uhd-ubuntu24 +0 -63
- dapps-0.1.0/pyproject.toml +0 -72
- dapps-0.1.0/src/iq_saver/iq_saver.py +0 -503
- dapps-0.1.0/src/simple/defs/e3sm_simple.asn +0 -54
- dapps-0.1.0/src/spectrum/defs/e3sm_spectrum.asn +0 -76
- dapps-0.1.0/src/spectrum/defs/e3sm_spectrum.json +0 -137
- dapps-0.1.0/src/spectrum/spectrum_dapp.py +0 -743
- dapps-0.1.0/src/visualization/dashboard.py +0 -185
- dapps-0.1.0/src/visualization/templates/index.html +0 -508
- dapps-0.1.0/tests/test_dashboard.py +0 -43
- {dapps-0.1.0 → dapps-0.2.0}/.github/CODEOWNERS +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/.github/ISSUE_TEMPLATE/documentation.yml +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/.github/ISSUE_TEMPLATE/question.yml +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/.github/workflows/mirror.yml +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/.gitignore +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/CONTRIBUTORS +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/LICENSE +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/SECURITY.md +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/docs/dapp_uml.txt +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/docs/gui_uml.txt +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/examples/nvidia_dapp.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/examples/simple_dapp.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/dapp/__init__.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/e3interface/__init__.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/e3interface/defs/e3.asn +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/e3interface/e3_logging.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/iq_saver/__init__.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/nvidia/__init__.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/nvidia/defs/e3sm_nvidia.asn +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/nvidia/defs/e3sm_nvidia.json +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/nvidia/nvidia_dapp.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/simple/__init__.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/spectrum/__init__.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/spectrum/embargo_manager.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/visualization/__init__.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/visualization/energy.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/visualization/grafana.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/visualization/influx.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/src/visualization/iq.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/tools/README.md +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/tools/asntojson.py +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/tools/generate_sm.sh +0 -0
- {dapps-0.1.0 → dapps-0.2.0}/tools/read_recording.py +0 -0
|
@@ -30,8 +30,8 @@ Closes #
|
|
|
30
30
|
|
|
31
31
|
These mirror what CI (`.github/workflows/test.yml`) enforces. **All boxes must be ticked before review.**
|
|
32
32
|
|
|
33
|
-
- [ ] `
|
|
34
|
-
- [ ] `
|
|
33
|
+
- [ ] `pytest tests/ -v` passes locally on Python 3.12 (after `pip install -e ".[all]"`)
|
|
34
|
+
- [ ] `python -m build` succeeds (verifies packaging metadata and the C++/SWIG build)
|
|
35
35
|
- [ ] `VERSION` file bumped per [SemVer](https://semver.org/) if the public API or wire protocol changed
|
|
36
36
|
- [ ] `README.md` updated if interface, environment variables, or workflow changed
|
|
37
37
|
- [ ] `CONTRIBUTING.md` updated if contributor-facing rules changed
|
|
@@ -43,7 +43,7 @@ These mirror what CI (`.github/workflows/test.yml`) enforces. **All boxes must b
|
|
|
43
43
|
|
|
44
44
|
## Twin-repo coordination
|
|
45
45
|
|
|
46
|
-
dapps is paired with [`libe3`](https://github.com/wineslab/
|
|
46
|
+
dapps is paired with [`libe3`](https://github.com/wineslab/libe3) and [`dApp-openairinterface5g`](https://github.com/wineslab/dApp-openairinterface5g). Breaking changes to the twin repos are not accepted.
|
|
47
47
|
|
|
48
48
|
- [ ] This PR does not break the E3 wire protocol, OR a paired PR exists in the affected twin repo (link below).
|
|
49
49
|
|
|
@@ -63,8 +63,14 @@ jobs:
|
|
|
63
63
|
python-version: "3.8"
|
|
64
64
|
- name: Install pypa/build
|
|
65
65
|
run: python3 -m pip install build --user
|
|
66
|
-
- name: Build a
|
|
67
|
-
|
|
66
|
+
- name: Build a source tarball (sdist)
|
|
67
|
+
# sdist only: the compiled extension produces a non-portable
|
|
68
|
+
# linux_x86_64 wheel that PyPI rejects. `pip install dapps` builds the
|
|
69
|
+
# sdist on the user's machine; scikit-build-core + FetchContent pull
|
|
70
|
+
# cmake/swig/ninja/nlohmann automatically, so no system prereqs are
|
|
71
|
+
# required. Building the sdist does not compile anything, so this job
|
|
72
|
+
# needs no C++ toolchain or nlohmann/json either.
|
|
73
|
+
run: python3 -m build --sdist
|
|
68
74
|
- name: Store the distribution packages
|
|
69
75
|
uses: actions/upload-artifact@v4
|
|
70
76
|
with:
|
|
@@ -113,7 +119,6 @@ jobs:
|
|
|
113
119
|
with:
|
|
114
120
|
inputs: |
|
|
115
121
|
./dist/*.tar.gz
|
|
116
|
-
./dist/*.whl
|
|
117
122
|
- name: Create GitHub Release
|
|
118
123
|
env:
|
|
119
124
|
GITHUB_TOKEN: ${{ github.token }}
|
|
@@ -17,8 +17,13 @@ jobs:
|
|
|
17
17
|
with:
|
|
18
18
|
python-version: "3.12"
|
|
19
19
|
|
|
20
|
-
- name: Install
|
|
21
|
-
run:
|
|
20
|
+
- name: Install build prerequisites
|
|
21
|
+
run: |
|
|
22
|
+
sudo apt-get update
|
|
23
|
+
sudo apt-get install -y cmake swig nlohmann-json3-dev
|
|
24
|
+
|
|
25
|
+
- name: Install package (editable, with test deps)
|
|
26
|
+
run: pip install -e ".[all]" pytest
|
|
22
27
|
|
|
23
28
|
- name: Run tests
|
|
24
|
-
run:
|
|
29
|
+
run: pytest tests/ -v
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Root CMake — drives the Python wheel build via scikit-build-core.
|
|
2
|
+
#
|
|
3
|
+
# scikit-build-core invokes this file when `pip install .` / `python -m
|
|
4
|
+
# build` is run. We turn on SWIG and direct the SWIG outputs into the
|
|
5
|
+
# wheel's `iq_saver/` package directory. The standalone C++ install of
|
|
6
|
+
# libiqsaver (pkg-config, headers under /usr/local) is driven directly
|
|
7
|
+
# from the libiqsaver/ subdirectory CMake; see libiqsaver/CMakeLists.txt.
|
|
8
|
+
#
|
|
9
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
10
|
+
|
|
11
|
+
cmake_minimum_required(VERSION 3.16)
|
|
12
|
+
|
|
13
|
+
# Tell libiqsaver/CMakeLists.txt to: (1) build SWIG bindings, (2) skip
|
|
14
|
+
# the system-wide install logic, (3) install SWIG outputs into the
|
|
15
|
+
# wheel's iq_saver/ package directory.
|
|
16
|
+
set(LIBIQSAVER_ENABLE_SWIG ON CACHE BOOL "" FORCE)
|
|
17
|
+
set(LIBIQSAVER_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
18
|
+
set(LIBIQSAVER_PY_INSTALL_DIR "iq_saver" CACHE STRING "" FORCE)
|
|
19
|
+
|
|
20
|
+
project(iqsaver_pywheel LANGUAGES CXX)
|
|
21
|
+
|
|
22
|
+
add_subdirectory(libiqsaver)
|
|
@@ -49,16 +49,16 @@ The following are **mandatory** for every contribution. PRs that do not meet the
|
|
|
49
49
|
|
|
50
50
|
- All PRs must use `.github/PULL_REQUEST_TEMPLATE.md` and complete every checklist item.
|
|
51
51
|
- The following CI workflow must be green on the latest commit before review:
|
|
52
|
-
- **`Run tests`** (`.github/workflows/test.yml`) —
|
|
52
|
+
- **`Run tests`** (`.github/workflows/test.yml`) — installs the package via `pip install -e ".[all]"` and runs `pytest tests/ -v` on Python 3.12.
|
|
53
53
|
- The following local checks must be reported in the PR description as run by the contributor (mirroring CI):
|
|
54
|
-
- `
|
|
55
|
-
- `
|
|
54
|
+
- `pytest tests/ -v` passes (after `pip install -e ".[all]"`).
|
|
55
|
+
- `python -m build` succeeds (the build backend is scikit-build-core; this also verifies the C++/SWIG extension compiles).
|
|
56
56
|
- `VERSION` file bumped per [SemVer](https://semver.org/) when public API or wire protocol changes.
|
|
57
57
|
- `README.md` and/or `CONTRIBUTING.md` updated when contributor- or user-facing behavior changes.
|
|
58
58
|
|
|
59
59
|
### Service Models and twin-repo coordination
|
|
60
60
|
|
|
61
|
-
This repository is part of a framework that includes [`libe3`](https://github.com/wineslab/
|
|
61
|
+
This repository is part of a framework that includes [`libe3`](https://github.com/wineslab/libe3) (the gNB-side E3 agent) and [`dApp-openairinterface5g`](https://github.com/wineslab/dApp-openairinterface5g) (the OAI fork). PRs that change the E3 wire protocol or add a new Service Model must come with a paired PR in the affected twin repo, linked from the PR description.
|
|
62
62
|
|
|
63
63
|
## Code of Conduct
|
|
64
64
|
|
dapps-0.2.0/MANIFEST.in
ADDED
|
@@ -1,44 +1,46 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dapps
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A library for creating and connecting dApps to Open RAN units.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Project-URL: Source, https://github.com/wineslab/dApp-library
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/wineslab/dApp-library/issues
|
|
9
|
-
Author-email: Andrea Lacava <thecave003@gmail.com>
|
|
5
|
+
Keywords: dApps,O-RAN,5G,cellular,networks
|
|
6
|
+
Author-Email: Andrea Lacava <thecave003@gmail.com>
|
|
10
7
|
License-Expression: Apache-2.0
|
|
11
8
|
License-File: LICENSE
|
|
12
|
-
Keywords: 5G,O-RAN,cellular,dApps,networks
|
|
13
9
|
Classifier: Development Status :: 4 - Beta
|
|
14
10
|
Classifier: Programming Language :: Python
|
|
15
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
12
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
17
13
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
14
|
+
Project-URL: Homepage, https://openrangym.com/o-ran-frameworks/dapps
|
|
15
|
+
Project-URL: Package, https://pypi.org/project/dapps/
|
|
16
|
+
Project-URL: Source, https://github.com/wineslab/dApp-library
|
|
17
|
+
Project-URL: Bug Tracker, https://github.com/wineslab/dApp-library/issues
|
|
18
18
|
Requires-Python: >=3.12
|
|
19
19
|
Requires-Dist: asn1tools
|
|
20
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: typing_extensions
|
|
21
21
|
Requires-Dist: numpy
|
|
22
22
|
Requires-Dist: pyzmq
|
|
23
23
|
Requires-Dist: sigmf
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Provides-Extra: all
|
|
26
|
-
Requires-Dist: flask; extra == 'all'
|
|
27
|
-
Requires-Dist: flask-socketio; extra == 'all'
|
|
28
|
-
Requires-Dist: influxdb-client; extra == 'all'
|
|
29
|
-
Requires-Dist: libiq; extra == 'all'
|
|
30
|
-
Requires-Dist: matplotlib; extra == 'all'
|
|
31
|
-
Requires-Dist: pysctp; extra == 'all'
|
|
24
|
+
Requires-Dist: jsonschema>=4.18.0
|
|
32
25
|
Provides-Extra: api
|
|
33
|
-
Requires-Dist:
|
|
26
|
+
Requires-Dist: influxdb_client; extra == "api"
|
|
34
27
|
Provides-Extra: cnn
|
|
35
|
-
Requires-Dist: libiq; extra ==
|
|
28
|
+
Requires-Dist: libiq; extra == "cnn"
|
|
36
29
|
Provides-Extra: gui
|
|
37
|
-
Requires-Dist: flask; extra ==
|
|
38
|
-
Requires-Dist: flask-
|
|
39
|
-
Requires-Dist:
|
|
30
|
+
Requires-Dist: flask; extra == "gui"
|
|
31
|
+
Requires-Dist: flask-sock; extra == "gui"
|
|
32
|
+
Requires-Dist: flask-cors; extra == "gui"
|
|
33
|
+
Requires-Dist: matplotlib; extra == "gui"
|
|
40
34
|
Provides-Extra: network
|
|
41
|
-
Requires-Dist: pysctp; extra ==
|
|
35
|
+
Requires-Dist: pysctp; extra == "network"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: pysctp; extra == "all"
|
|
38
|
+
Requires-Dist: flask; extra == "all"
|
|
39
|
+
Requires-Dist: flask-sock; extra == "all"
|
|
40
|
+
Requires-Dist: flask-cors; extra == "all"
|
|
41
|
+
Requires-Dist: matplotlib; extra == "all"
|
|
42
|
+
Requires-Dist: influxdb_client; extra == "all"
|
|
43
|
+
Requires-Dist: libiq; extra == "all"
|
|
42
44
|
Description-Content-Type: text/markdown
|
|
43
45
|
|
|
44
46
|
# dApp Library
|
|
@@ -60,10 +62,41 @@ The dApp can be found in the `examples` directory or it can be downloaded from t
|
|
|
60
62
|
Clone the repository and install it manually with:
|
|
61
63
|
|
|
62
64
|
```
|
|
63
|
-
|
|
65
|
+
pip3 install -e ".[all]"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Building a distributable sdist / wheel:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
pip install build
|
|
72
|
+
python -m build # produces dist/*.tar.gz and dist/*.whl
|
|
64
73
|
pip3 install "dist/*.tar.gz[all]"
|
|
65
74
|
```
|
|
66
75
|
|
|
76
|
+
The wheel build uses [scikit-build-core](https://scikit-build-core.readthedocs.io/) to compile the bundled C++ extension (see *libiqsaver* below). System prerequisites: a C++17 compiler, CMake ≥ 3.16, SWIG ≥ 4.0, and [`nlohmann/json`](https://github.com/nlohmann/json) (≥ 3.11). On Debian/Ubuntu: `apt install cmake swig nlohmann-json3-dev`.
|
|
77
|
+
|
|
78
|
+
### libiqsaver — standalone C++ library
|
|
79
|
+
|
|
80
|
+
The `iq_saver` Python package wraps **libiqsaver**, a SigMF-compliant IQ-sample writer written in C++. The same library is also usable directly from C++ projects. To install it system-wide:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
cd libiqsaver
|
|
84
|
+
mkdir build && cd build
|
|
85
|
+
cmake .. -DCMAKE_BUILD_TYPE=Release
|
|
86
|
+
sudo make install
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
After install:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
pkg-config --cflags --libs libiqsaver
|
|
93
|
+
# or, in CMake:
|
|
94
|
+
find_package(libiqsaver REQUIRED)
|
|
95
|
+
target_link_libraries(my_app PRIVATE iqsaver::iqsaver)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The standalone install (headers under `${CMAKE_INSTALL_PREFIX}/include/iqsaver`, library and pkg-config under `${CMAKE_INSTALL_LIBDIR}`) is independent of `pip install dapps` — both can coexist on the same machine.
|
|
99
|
+
|
|
67
100
|
### Launch the Spectrum Sharing dApp example
|
|
68
101
|
|
|
69
102
|
OAI should start _before_ running the dApp
|
|
@@ -17,10 +17,41 @@ The dApp can be found in the `examples` directory or it can be downloaded from t
|
|
|
17
17
|
Clone the repository and install it manually with:
|
|
18
18
|
|
|
19
19
|
```
|
|
20
|
-
|
|
20
|
+
pip3 install -e ".[all]"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Building a distributable sdist / wheel:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
pip install build
|
|
27
|
+
python -m build # produces dist/*.tar.gz and dist/*.whl
|
|
21
28
|
pip3 install "dist/*.tar.gz[all]"
|
|
22
29
|
```
|
|
23
30
|
|
|
31
|
+
The wheel build uses [scikit-build-core](https://scikit-build-core.readthedocs.io/) to compile the bundled C++ extension (see *libiqsaver* below). System prerequisites: a C++17 compiler, CMake ≥ 3.16, SWIG ≥ 4.0, and [`nlohmann/json`](https://github.com/nlohmann/json) (≥ 3.11). On Debian/Ubuntu: `apt install cmake swig nlohmann-json3-dev`.
|
|
32
|
+
|
|
33
|
+
### libiqsaver — standalone C++ library
|
|
34
|
+
|
|
35
|
+
The `iq_saver` Python package wraps **libiqsaver**, a SigMF-compliant IQ-sample writer written in C++. The same library is also usable directly from C++ projects. To install it system-wide:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
cd libiqsaver
|
|
39
|
+
mkdir build && cd build
|
|
40
|
+
cmake .. -DCMAKE_BUILD_TYPE=Release
|
|
41
|
+
sudo make install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
After install:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
pkg-config --cflags --libs libiqsaver
|
|
48
|
+
# or, in CMake:
|
|
49
|
+
find_package(libiqsaver REQUIRED)
|
|
50
|
+
target_link_libraries(my_app PRIVATE iqsaver::iqsaver)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The standalone install (headers under `${CMAKE_INSTALL_PREFIX}/include/iqsaver`, library and pkg-config under `${CMAKE_INSTALL_LIBDIR}`) is independent of `pip install dapps` — both can coexist on the same machine.
|
|
54
|
+
|
|
24
55
|
### Launch the Spectrum Sharing dApp example
|
|
25
56
|
|
|
26
57
|
OAI should start _before_ running the dApp
|
dapps-0.2.0/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.0
|
|
@@ -63,7 +63,7 @@ The detector flags any PRB whose power exceeds a fixed absolute threshold.
|
|
|
63
63
|
|
|
64
64
|
**Interactive:**
|
|
65
65
|
```bash
|
|
66
|
-
|
|
66
|
+
python3 examples/spectrum_dapp.py \
|
|
67
67
|
--link zmq --transport ipc \
|
|
68
68
|
--save-iqs \
|
|
69
69
|
--control \
|
|
@@ -77,7 +77,7 @@ hatch run python examples/spectrum_dapp.py \
|
|
|
77
77
|
|
|
78
78
|
**CI/CD:**
|
|
79
79
|
```bash
|
|
80
|
-
|
|
80
|
+
python3 examples/spectrum_dapp.py \
|
|
81
81
|
--link zmq --transport ipc \
|
|
82
82
|
--save-iqs \
|
|
83
83
|
--control \
|
|
@@ -96,7 +96,7 @@ after the last detection before being released.
|
|
|
96
96
|
|
|
97
97
|
**Interactive:**
|
|
98
98
|
```bash
|
|
99
|
-
|
|
99
|
+
python3 examples/spectrum_dapp.py \
|
|
100
100
|
--link zmq --transport ipc \
|
|
101
101
|
--save-iqs \
|
|
102
102
|
--control \
|
|
@@ -112,7 +112,7 @@ hatch run python examples/spectrum_dapp.py \
|
|
|
112
112
|
|
|
113
113
|
**CI/CD:**
|
|
114
114
|
```bash
|
|
115
|
-
|
|
115
|
+
python3 examples/spectrum_dapp.py \
|
|
116
116
|
--link zmq --transport ipc \
|
|
117
117
|
--save-iqs \
|
|
118
118
|
--control \
|
|
@@ -9,7 +9,11 @@ import time
|
|
|
9
9
|
import logging
|
|
10
10
|
|
|
11
11
|
from e3interface.e3_connector import E3LinkLayer, E3TransportLayer
|
|
12
|
-
from spectrum.spectrum_dapp import
|
|
12
|
+
from spectrum.spectrum_dapp import (
|
|
13
|
+
SpectrumSharingDApp,
|
|
14
|
+
compute_fft_size,
|
|
15
|
+
make_periodic_toggle_callback,
|
|
16
|
+
)
|
|
13
17
|
from spectrum.threshold_detector import StaticThresholdDetector, AdaptiveThresholdDetector
|
|
14
18
|
|
|
15
19
|
LOG_DIR = '/tmp/'
|
|
@@ -23,17 +27,15 @@ def stop_program(time_to_wait, dapp: SpectrumSharingDApp):
|
|
|
23
27
|
|
|
24
28
|
def main(args):
|
|
25
29
|
if args.model:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
36
|
-
exit(-1)
|
|
30
|
+
# CNN signal classification is not wired into SpectrumSharingDApp; a
|
|
31
|
+
# classifier= kwarg would fall through to **kwargs and be dropped, so
|
|
32
|
+
# --model (and --time-window/--moving-avg-window/--extraction-window)
|
|
33
|
+
# would silently do nothing. Fail loudly instead.
|
|
34
|
+
raise SystemExit(
|
|
35
|
+
"--model (CNN signal classification) is not supported by "
|
|
36
|
+
"SpectrumSharingDApp; remove --model and the associated "
|
|
37
|
+
"--time-window/--moving-avg-window/--extraction-window flags."
|
|
38
|
+
)
|
|
37
39
|
|
|
38
40
|
# This value really depends on the RF conditions and the RU used and
|
|
39
41
|
# should be carefully calibrated.
|
|
@@ -81,16 +83,6 @@ def main(args):
|
|
|
81
83
|
f" | window: {args.average_over_frames} frames"
|
|
82
84
|
)
|
|
83
85
|
|
|
84
|
-
classifier = None
|
|
85
|
-
if args.model:
|
|
86
|
-
classifier = Classifier(
|
|
87
|
-
time_window=args.time_window,
|
|
88
|
-
input_vector=fft_size,
|
|
89
|
-
moving_avg_window=args.moving_avg_window,
|
|
90
|
-
extraction_window=args.extraction_window,
|
|
91
|
-
model_path=args.model,
|
|
92
|
-
)
|
|
93
|
-
|
|
94
86
|
dapp = SpectrumSharingDApp(
|
|
95
87
|
detector=detector,
|
|
96
88
|
save_iqs=args.save_iqs,
|
|
@@ -100,14 +92,21 @@ def main(args):
|
|
|
100
92
|
energyGui=args.energy_gui,
|
|
101
93
|
iqPlotterGui=args.iq_plotter_gui,
|
|
102
94
|
dashboard=args.demo_gui,
|
|
103
|
-
|
|
95
|
+
viz_web_port=args.viz_web_port,
|
|
96
|
+
viz_zmq_port=args.viz_zmq_port,
|
|
97
|
+
external_viz=args.external_viz,
|
|
104
98
|
center_freq=args.center_freq,
|
|
105
99
|
num_prbs=args.num_prbs,
|
|
106
100
|
e_sampling=args.e,
|
|
107
101
|
num_subcarrier_spacing=args.num_subcarrier_spacing,
|
|
108
102
|
sampling_threshold=args.sampling_threshold,
|
|
103
|
+
max_samples_per_file=args.max_samples_per_file,
|
|
104
|
+
fp16_beta=args.fp16_beta,
|
|
105
|
+
sensing_only=args.sensing_only,
|
|
106
|
+
strict_sensing=args.strict_sensing,
|
|
107
|
+
min_sensing_symbols=args.min_sensing_symbols,
|
|
108
|
+
encoding_method=args.encoding_method,
|
|
109
109
|
ground_truth=args.ground_truth,
|
|
110
|
-
show_controls=args.show_controls,
|
|
111
110
|
dapp_name="SpectrumSharing",
|
|
112
111
|
dapp_version="1.0.0",
|
|
113
112
|
vendor="WinesLab",
|
|
@@ -137,7 +136,29 @@ def main(args):
|
|
|
137
136
|
)
|
|
138
137
|
time.sleep(1)
|
|
139
138
|
|
|
140
|
-
dapp.send_subscription_request()
|
|
139
|
+
if not dapp.send_subscription_request():
|
|
140
|
+
# send_subscription_request returns True only if the gNB accepted both
|
|
141
|
+
# the L1-KPM (RF=2) and Spectrum (RF=1) subscriptions; anything else
|
|
142
|
+
# means the dApp would run deaf, so abort with a non-zero exit.
|
|
143
|
+
dapp.stop()
|
|
144
|
+
raise SystemExit("[FATAL] gNB did not accept the dApp subscriptions; aborting")
|
|
145
|
+
|
|
146
|
+
# Optional sensing-policy toggle. When --toggle-period > 0, installs
|
|
147
|
+
# a periodic on/off toggle that flips the gNB's masked UL TDA
|
|
148
|
+
# selector via spectrum_sm ctrl_id=2. Operator must have configured
|
|
149
|
+
# additional_ul_tdas in the gNB (e.g. "0:7") for the scheduler to
|
|
150
|
+
# have a short TDA to switch to. See examples/README or the gNB
|
|
151
|
+
# boot log ([additional] tag) to verify.
|
|
152
|
+
if args.toggle_period > 0:
|
|
153
|
+
print(f"[INFO] Installing sensing-policy toggle: "
|
|
154
|
+
f"period={args.toggle_period}s "
|
|
155
|
+
f"n_slots={args.toggle_n_slots} "
|
|
156
|
+
f"mask_when_on=0x{args.toggle_mask:04x}")
|
|
157
|
+
dapp.set_sensing_policy_logic(make_periodic_toggle_callback(
|
|
158
|
+
period_s=args.toggle_period,
|
|
159
|
+
n_slots=args.toggle_n_slots,
|
|
160
|
+
mask_when_on=args.toggle_mask,
|
|
161
|
+
))
|
|
141
162
|
|
|
142
163
|
if args.timed:
|
|
143
164
|
timer = threading.Thread(target=stop_program, args=(args.timed, dapp), daemon=False)
|
|
@@ -167,7 +188,8 @@ if __name__ == "__main__":
|
|
|
167
188
|
parser.add_argument('--save-iqs', action='store_true', default=False,
|
|
168
189
|
help="Save I/Q samples to SigMF files")
|
|
169
190
|
parser.add_argument('--control', action='store_true', default=False,
|
|
170
|
-
help="Send PRB
|
|
191
|
+
help="Send PRB-block control messages to the gNB when "
|
|
192
|
+
"PRBs are detected above the noise threshold")
|
|
171
193
|
parser.add_argument('--noise-floor-threshold', type=int, default=None,
|
|
172
194
|
help="Detection threshold in dB (static) or dB above noise floor (adaptive)")
|
|
173
195
|
parser.add_argument('--use-adaptive-noise-floor', action='store_true', default=False,
|
|
@@ -184,7 +206,13 @@ if __name__ == "__main__":
|
|
|
184
206
|
parser.add_argument('--iq-plotter-gui', action='store_true', default=False,
|
|
185
207
|
help="Enable IQ time-domain plotter")
|
|
186
208
|
parser.add_argument('--demo-gui', action='store_true', default=False,
|
|
187
|
-
help="
|
|
209
|
+
help="Publish per-slot subcarrier power to the visualizer")
|
|
210
|
+
parser.add_argument('--viz-web-port', type=int, default=5001,
|
|
211
|
+
help="Visualizer web UI port (default 5001)")
|
|
212
|
+
parser.add_argument('--viz-zmq-port', type=int, default=5559,
|
|
213
|
+
help="ZMQ port the dApp publishes on / the visualizer reads (default 5559)")
|
|
214
|
+
parser.add_argument('--external-viz', action='store_true', default=False,
|
|
215
|
+
help="Don't spawn the visualizer; publish to an already-running one")
|
|
188
216
|
parser.add_argument('--num-prbs', type=int, default=106,
|
|
189
217
|
help="Number of PRBs")
|
|
190
218
|
parser.add_argument('--num-subcarrier-spacing', type=int, default=30,
|
|
@@ -205,14 +233,68 @@ if __name__ == "__main__":
|
|
|
205
233
|
help="Samples to retain after CNN energy peak detection")
|
|
206
234
|
parser.add_argument('--sampling-threshold', type=int, default=5,
|
|
207
235
|
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,
|
|
237
|
+
help="Rotate the SigMF capture file once a segment reaches "
|
|
238
|
+
"this many true IQ samples (default 46080000). Each "
|
|
239
|
+
"indication is kept whole, so a segment may exceed the "
|
|
240
|
+
"threshold by up to one indication's worth of samples. "
|
|
241
|
+
"Segment wall-clock duration depends on the capture rate. "
|
|
242
|
+
"Only used with --save-iqs.")
|
|
208
243
|
parser.add_argument('--ground-truth', type=str, default='', metavar='LABEL',
|
|
209
244
|
help="Initial ground truth label written into IQ annotations "
|
|
210
245
|
"(only used with --save-iqs). "
|
|
211
246
|
"Updatable at runtime via the dashboard GUI "
|
|
212
247
|
"when --demo-gui is also set.")
|
|
213
|
-
parser.add_argument('--
|
|
214
|
-
help="
|
|
215
|
-
"
|
|
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).")
|
|
253
|
+
parser.add_argument('--encoding-method', type=str, default='asn1',
|
|
254
|
+
choices=['asn1', 'json'],
|
|
255
|
+
help="Wire encoding for Spectrum-* envelopes (default: asn1).")
|
|
256
|
+
parser.add_argument('--no-sensing-only', dest='sensing_only',
|
|
257
|
+
action='store_false', default=True,
|
|
258
|
+
help="Disable the sensing-window filter. By default the dApp uses the "
|
|
259
|
+
"sensing ranges from the Spectrum SM (RF=1) to slice the detector "
|
|
260
|
+
"input to sensing-PUSCH cells; passing this flag uses every cell.")
|
|
261
|
+
parser.add_argument('--strict-sensing', dest='strict_sensing',
|
|
262
|
+
action='store_true', default=False,
|
|
263
|
+
help="Stricter sensing filter: drop any slot whose sensing window "
|
|
264
|
+
"doesn't cover the full slot (i.e. any UE PUSCH was granted "
|
|
265
|
+
"in that slot). Eliminates UE CP/spectral bleed at the cost "
|
|
266
|
+
"of a sparser waterfall under heavy UE traffic. Requires "
|
|
267
|
+
"--sensing-only (the default).")
|
|
268
|
+
parser.add_argument('--min-sensing-symbols', type=int, default=1,
|
|
269
|
+
help="Minimum number of kept symbols required to emit the slot to "
|
|
270
|
+
"the dashboard. Default 1: display every slot that has any "
|
|
271
|
+
"sensing symbol — keeps the waterfall scrolling even under "
|
|
272
|
+
"heavy UE UL where the MAC scheduler leaves only one free "
|
|
273
|
+
"symbol per slot. Set to 2 to drop the typical \"only sym 13 "
|
|
274
|
+
"survived\" partial-sensing slots (evens out brightness at "
|
|
275
|
+
"the cost of dashboard freezes under load). Set to 14 to "
|
|
276
|
+
"require fully-clean slots (equivalent to "
|
|
277
|
+
"--strict-sensing).")
|
|
278
|
+
|
|
279
|
+
# --- Optional sensing-policy toggle ----------------------------- #
|
|
280
|
+
# Drives the gNB's masked UL TDA selector via spectrum_sm ctrl_id=2.
|
|
281
|
+
# Disabled by default (toggle-period=0); set --toggle-period to a
|
|
282
|
+
# positive value to enable. Requires additional_ul_tdas (e.g. "0:7")
|
|
283
|
+
# on the gNB so the scheduler has a short TDA to switch to.
|
|
284
|
+
parser.add_argument('--toggle-period', type=float, default=0.0,
|
|
285
|
+
metavar='SECONDS',
|
|
286
|
+
help="Toggle the gNB sensing policy on/off every N seconds "
|
|
287
|
+
"(0 = disabled, default). When > 0, alternates between "
|
|
288
|
+
"installing the mask (active) and clearing it (deactivate). "
|
|
289
|
+
"Requires additional_ul_tdas configured on the gNB.")
|
|
290
|
+
parser.add_argument('--toggle-n-slots', type=int, default=20,
|
|
291
|
+
help="numb_slots_frame of the gNB for the per-slot mask "
|
|
292
|
+
"(mu=1 -> 20). Must match the gNB exactly or the gNB "
|
|
293
|
+
"will NACK the policy with an n_slots mismatch.")
|
|
294
|
+
parser.add_argument('--toggle-mask', type=lambda s: int(s, 0), default=0x3F80,
|
|
295
|
+
metavar='HEX',
|
|
296
|
+
help="14-bit symbol bitmap applied uniformly to every slot "
|
|
297
|
+
"when the toggle is active (default 0x3F80 = syms 7..13).")
|
|
216
298
|
|
|
217
299
|
args = parser.parse_args()
|
|
218
300
|
print("Start dApp")
|