dapps 0.0.1__py3-none-any.whl

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.
@@ -0,0 +1,295 @@
1
+ import struct
2
+ from abc import ABC, abstractmethod
3
+ from enum import Enum
4
+ import os
5
+ import socket
6
+ import zmq
7
+ from .e3_logging import e3_logger, LOG_DIR
8
+
9
+
10
+ class E3LinkLayer(Enum):
11
+ ZMQ = "zmq"
12
+ POSIX = "posix"
13
+
14
+ def __str__(self):
15
+ return self.value
16
+
17
+ @staticmethod
18
+ def from_string(link_layer_str: str):
19
+ try:
20
+ return E3LinkLayer(link_layer_str.lower())
21
+ except ValueError:
22
+ raise ValueError(f"Invalid link layer: '{link_layer_str}'. Must be one of {[e.value for e in E3LinkLayer]}")
23
+
24
+ class E3TransportLayer(Enum):
25
+ SCTP = "sctp"
26
+ TCP = "tcp"
27
+ IPC = "ipc"
28
+
29
+ def __str__(self):
30
+ return self.value
31
+
32
+ @staticmethod
33
+ def from_string(transport_layer_str: str):
34
+ try:
35
+ return E3TransportLayer(transport_layer_str.lower())
36
+ except ValueError:
37
+ raise ValueError(f"Invalid transport layer: '{transport_layer_str}'. Must be one of {[e.value for e in E3TransportLayer]}")
38
+
39
+ class E3Connector(ABC):
40
+ # List of known valid configurations
41
+ VALID_CONFIGURATIONS = [
42
+ (E3LinkLayer.ZMQ, E3TransportLayer.IPC),
43
+ (E3LinkLayer.ZMQ, E3TransportLayer.TCP),
44
+ (E3LinkLayer.POSIX, E3TransportLayer.TCP),
45
+ (E3LinkLayer.POSIX, E3TransportLayer.SCTP),
46
+ (E3LinkLayer.POSIX, E3TransportLayer.IPC)
47
+ ]
48
+
49
+ IPC_BASE_DIR = "/tmp/dapps"
50
+ E3_IPC_SETUP_PATH = f"{IPC_BASE_DIR}/setup"
51
+ E3_IPC_SOCKET_PATH = f"{IPC_BASE_DIR}/e3_socket" # inbound
52
+ DAPP_IPC_SOCKET_PATH = f"{IPC_BASE_DIR}/dapp_socket" # outbound
53
+
54
+ @staticmethod
55
+ def setup_connector(link_layer: str, transport_layer: str):
56
+ link_layer = E3LinkLayer.from_string(link_layer)
57
+ transport_layer = E3TransportLayer.from_string(transport_layer)
58
+
59
+ # Check if the configuration is part of the valid configurations
60
+ if (link_layer, transport_layer) not in E3Connector.VALID_CONFIGURATIONS:
61
+ raise ValueError(
62
+ f"Invalid configuration: Link Layer={link_layer}, Transport Layer={transport_layer}. "
63
+ f"Must be one of {E3Connector.VALID_CONFIGURATIONS}"
64
+ )
65
+
66
+ if link_layer == E3LinkLayer.POSIX:
67
+ return POSIXConnector(transport_layer)
68
+ else:
69
+ return ZMQConnector(transport_layer)
70
+
71
+ @abstractmethod
72
+ def send_setup_request(self, payload):
73
+ pass
74
+
75
+ @abstractmethod
76
+ def setup_inbound_connection(self):
77
+ pass
78
+
79
+ @abstractmethod
80
+ def receive(self) -> bytes:
81
+ """Receive a byte-encoded payload based on the configuration."""
82
+ pass
83
+
84
+ @abstractmethod
85
+ def setup_outbound_connection(self):
86
+ pass
87
+
88
+ @abstractmethod
89
+ def send(self, payload: bytes):
90
+ pass
91
+
92
+ @abstractmethod
93
+ def dispose(self):
94
+ pass
95
+
96
+ class ZMQConnector(E3Connector):
97
+ setup_context: zmq.Context
98
+ inbound_context: zmq.Context
99
+ outbound_context: zmq.Context
100
+
101
+ def __init__(self, transport_layer: E3TransportLayer):
102
+ self.setup_context = zmq.Context()
103
+
104
+ match transport_layer:
105
+ case E3TransportLayer.SCTP | E3TransportLayer.TCP:
106
+ self.setup_endpoint = f"{transport_layer}://127.0.0.1:9990"
107
+ self.inbound_endpoint = f"{transport_layer}://127.0.0.1:9991"
108
+ self.outbound_endpoint = f"{transport_layer}://127.0.0.1:9999"
109
+
110
+ case E3TransportLayer.IPC:
111
+ self.setup_endpoint = f"{transport_layer}://{self.E3_IPC_SETUP_PATH}"
112
+ self.inbound_endpoint = f"{transport_layer}://{self.E3_IPC_SOCKET_PATH}"
113
+ self.outbound_endpoint = f"{transport_layer}://{self.DAPP_IPC_SOCKET_PATH}"
114
+
115
+ case _:
116
+ raise ValueError(f'Unknown/Unsupported value for transport layer {transport_layer}')
117
+
118
+ self.transport_layer = transport_layer
119
+
120
+ def send_setup_request(self, payload):
121
+ # Lazy pirate pattern, i.e., reliability on the client side
122
+ request_timeout = 1000
123
+ request_retries = 5
124
+
125
+ while request_retries > 0:
126
+ setup_socket = self.setup_context.socket(zmq.REQ)
127
+ setup_socket.connect(self.setup_endpoint)
128
+ e3_logger.debug("Send E3 Setup request")
129
+ setup_socket.send(payload)
130
+
131
+ if (setup_socket.poll(request_timeout) & zmq.POLLIN) != 0:
132
+ reply = setup_socket.recv()
133
+ e3_logger.debug('ZMQ setup socket replied')
134
+ return reply
135
+
136
+ request_retries -= 1
137
+ e3_logger.error("ZMQ setup did not reply")
138
+ setup_socket.setsockopt(zmq.LINGER, 0)
139
+ setup_socket.close()
140
+ e3_logger.debug('Retrying to connect')
141
+
142
+ raise ConnectionRefusedError('E3 Setup request procedure did not went through')
143
+
144
+
145
+ def setup_inbound_connection(self):
146
+ self.inbound_context = zmq.Context()
147
+ self.inbound_socket = self.inbound_context.socket(zmq.SUB)
148
+ self.inbound_socket.setsockopt_string(zmq.SUBSCRIBE, "") # subscribe to all the messages
149
+ self.inbound_socket.setsockopt(zmq.CONFLATE, 1) # Keep only last message
150
+ self.inbound_socket.connect(self.inbound_endpoint)
151
+
152
+ self.poller = zmq.Poller()
153
+ self.poller.register(self.inbound_socket, zmq.POLLIN)
154
+
155
+ def receive(self) -> bytes:
156
+ # The commented part should be decommented for measuring the effective performance of the control loop
157
+ # start_time = time.perf_counter()
158
+ # while True:
159
+
160
+ # # Poll the socket without blocking
161
+ # events = dict(self.poller.poll(0))
162
+
163
+ # if self.inbound_socket in events and events[self.inbound_socket] == zmq.POLLIN:
164
+ # with open(f"{LOG_DIR}/busy.txt", "a") as f:
165
+ # print(f"{time.perf_counter() - start_time}", file=f)
166
+ return self.inbound_socket.recv()
167
+
168
+
169
+ def setup_outbound_connection(self):
170
+ self.outbound_context = zmq.Context()
171
+ self.outbound_socket = self.outbound_context.socket(zmq.PUB)
172
+ self.outbound_socket.bind(self.outbound_endpoint)
173
+ if self.transport_layer == E3TransportLayer.IPC:
174
+ os.chmod(self.DAPP_IPC_SOCKET_PATH, 0o666)
175
+
176
+ def send(self, payload: bytes):
177
+ self.outbound_socket.send(payload)
178
+
179
+ def dispose(self):
180
+ if hasattr(self, "setup_context"):
181
+ self.setup_context.destroy()
182
+ if hasattr(self, "inbound_context"):
183
+ self.inbound_context.destroy()
184
+ if hasattr(self, "outbound_context"):
185
+ self.outbound_context.destroy()
186
+
187
+ class POSIXConnector(E3Connector):
188
+ CHUNK_SIZE = 8192
189
+
190
+ def __init__(self, transport_layer: E3TransportLayer):
191
+ match transport_layer:
192
+ case E3TransportLayer.SCTP | E3TransportLayer.TCP:
193
+ self.setup_endpoint = ("127.0.0.1", 9990)
194
+ self.inbound_endpoint = ("127.0.0.1", 9991)
195
+ self.outbound_endpoint = ("127.0.0.1", 9999)
196
+
197
+ case E3TransportLayer.IPC:
198
+ self.setup_endpoint = self.E3_IPC_SETUP_PATH
199
+ self.inbound_endpoint = self.E3_IPC_SOCKET_PATH
200
+ self.outbound_endpoint = self.DAPP_IPC_SOCKET_PATH
201
+
202
+ case _:
203
+ raise ValueError(f'Unknown/Unsupported value for transport layer {transport_layer}')
204
+
205
+ self.transport_layer = transport_layer
206
+
207
+ def _create_socket(self):
208
+ match self.transport_layer:
209
+ case E3TransportLayer.SCTP:
210
+ try:
211
+ import sctp
212
+ except ModuleNotFoundError:
213
+ e3_logger.critical(
214
+ "SCTP selected as transport layer, but the optional dependency 'pysctp' is not installed.\n"
215
+ "Fix this by running:\n\n"
216
+ " pip install 'dApps[network]' # OR\n"
217
+ " pip install 'dApps[all]'\n",
218
+ exc_info=True
219
+ )
220
+ exit(-1)
221
+ sock = sctp.sctpsocket_tcp(socket.AF_INET)
222
+ case E3TransportLayer.TCP:
223
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
224
+ case E3TransportLayer.IPC:
225
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
226
+ case _:
227
+ raise ValueError(f'Unknown/Unsupported value for transport layer {self.transport_layer}')
228
+ return sock
229
+
230
+ def send_setup_request(self, payload):
231
+ setup_socket = self._create_socket()
232
+
233
+ try:
234
+ setup_socket.connect(self.setup_endpoint)
235
+ setup_socket.send(payload)
236
+ reply = self.receive_in_chunks(setup_socket)
237
+ finally:
238
+ setup_socket.close()
239
+
240
+ return reply
241
+
242
+ def setup_inbound_connection(self):
243
+ self.inbound_socket = self._create_socket()
244
+ self.inbound_socket.bind(self.inbound_endpoint)
245
+ self.inbound_socket.listen(5)
246
+ self.inbound_connection, _ = self.inbound_socket.accept()
247
+
248
+ def receive_in_chunks(self, conn):
249
+ data = bytearray()
250
+ chunks = 0
251
+
252
+ # Receive the size of the buffer first
253
+ raw_size = conn.recv(4)
254
+ if len(raw_size) < 4:
255
+ e3_logger.error("Failed to receive buffer size")
256
+ return None
257
+ buffer_size = struct.unpack("!I", raw_size)[0]
258
+ e3_logger.debug(f"buffer size is {buffer_size}")
259
+
260
+ # Receive the buffer in chunks
261
+ while len(data) < buffer_size:
262
+ remaining = buffer_size - len(data)
263
+ chunk = conn.recv(min(self.CHUNK_SIZE, remaining))
264
+ if not chunk:
265
+ e3_logger.error("Connection closed unexpectedly")
266
+ return None # Connection closed unexpectedly
267
+ data.extend(chunk)
268
+ chunks += 1
269
+
270
+ e3_logger.debug(f"Chunks recv {chunks}")
271
+ e3_logger.debug(f"Total size recv {len(data)}")
272
+ return bytes(data)
273
+
274
+ def receive(self) -> bytes:
275
+ return self.receive_in_chunks(self.inbound_connection)
276
+
277
+ def setup_outbound_connection(self):
278
+ self.outbound_socket = self._create_socket()
279
+ self.outbound_socket.connect(self.outbound_endpoint)
280
+
281
+ def send(self, payload: bytes):
282
+ self.outbound_socket.send(payload)
283
+
284
+ def dispose(self):
285
+ if hasattr(self, "outbound_socket"):
286
+ self.outbound_socket.close()
287
+ if hasattr(self, "inbound_connection"):
288
+ self.inbound_connection.close()
289
+ if hasattr(self, "inbound_socket"):
290
+ self.inbound_socket.close()
291
+
292
+ if self.transport_layer == E3TransportLayer.IPC:
293
+ os.remove(self.E3_IPC_SETUP_PATH)
294
+ os.remove(self.E3_IPC_SOCKET_PATH)
295
+ os.remove(self.DAPP_IPC_SOCKET_PATH)
@@ -0,0 +1,187 @@
1
+ import os
2
+ import multiprocessing
3
+ import queue
4
+ import threading
5
+ from .e3_connector import E3Connector
6
+ from .e3_logging import e3_logger
7
+ import asn1tools
8
+
9
+ class E3Interface:
10
+ _instance = None
11
+ _lock = threading.Lock()
12
+
13
+ def __new__(cls, *args, **kwargs):
14
+ with cls._lock:
15
+ if not cls._instance:
16
+ cls._instance = super(E3Interface, cls).__new__(cls)
17
+ return cls._instance
18
+
19
+ def __init__(self, *args, **kwargs):
20
+ if not hasattr(self, "initialized"):
21
+ self.callbacks = []
22
+ self.stop_event = multiprocessing.Event()
23
+ self.initialized = True
24
+
25
+ # Create an E3Connector instance based on the configuration
26
+ self.e3_connector = E3Connector.setup_connector(kwargs.get('link', ''), kwargs.get('transport', ''))
27
+
28
+ e3_logger.info(f"Endpoint setup {self.e3_connector.setup_endpoint}")
29
+ e3_logger.info(f"Endpoint inbound {self.e3_connector.inbound_endpoint}")
30
+ e3_logger.info(f"Endpoint outbound {self.e3_connector.outbound_endpoint}")
31
+
32
+ self.defs = asn1tools.compile_files(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./defs/e3.asn"), codec="per")
33
+ self.outbound_queue = multiprocessing.Queue()
34
+
35
+ def send_setup_request(self, dappId: int = 1) -> bool:
36
+ e3_logger.info("Start setup request")
37
+ payload = self.create_setup_request(dappId)
38
+ try:
39
+ response = self.e3_connector.send_setup_request(payload)
40
+ except ConnectionRefusedError as e:
41
+ e3_logger.error(f"Unable to connect to E3 setup endpoint, connection refused: {e}")
42
+ return False
43
+
44
+ e3_logger.info("Setup response received")
45
+ pdu = self.defs.decode('E3-PDU', response)
46
+ if pdu[0] == "setupResponse":
47
+ e3_setup_response = pdu[1]
48
+ e3_logger.info(e3_setup_response)
49
+ # Either here or in setup connections we need E3 sub request and response
50
+ self.setup_connections()
51
+ return e3_setup_response['responseCode'] == 'positive'
52
+ else:
53
+ return False
54
+
55
+ def setup_connections(self):
56
+ # Two connections, one for the inbound the other for the outbound
57
+ self.inbound_process = multiprocessing.Process(target=self._inbound_connection)
58
+ self.outbound_process = multiprocessing.Process(target=self._outbound_connection)
59
+
60
+ self.inbound_process.start()
61
+ self.outbound_process.start()
62
+
63
+ def _inbound_connection(self):
64
+ """
65
+ Inbound is for all the messages that are coming from the RAN after the initial setup
66
+ """
67
+ e3_logger.info(f'Start inbound connection')
68
+ self.e3_connector.setup_inbound_connection()
69
+ e3_logger.info(f'Start inbound loop')
70
+
71
+ try:
72
+ while not self.stop_event.is_set():
73
+ data = self.e3_connector.receive()
74
+ if not data:
75
+ e3_logger.error(f'No data received, connection closed, end')
76
+ break
77
+ e3_logger.debug(f'Received data size: {len(data)}')
78
+ e3_logger.debug(data.hex())
79
+ pdu = self.defs.decode("E3-PDU", data)
80
+ e3_logger.debug(f"Data decoded")
81
+ match pdu[0]:
82
+ case "indicationMessage":
83
+ e3_indication_message = pdu[1]
84
+ protocolData = e3_indication_message['protocolData']
85
+ e3_logger.debug(protocolData)
86
+ e3_logger.debug(f"Indication message protocolData {len(protocolData)}")
87
+ self._handle_incoming_data(protocolData)
88
+
89
+ case "xAppControlAction":
90
+ e3_xapp_control_action = pdu[1]
91
+ raise NotImplementedError()
92
+
93
+ case _:
94
+ raise ValueError("Unrecognized value ", pdu)
95
+
96
+ except Exception as e:
97
+ e3_logger.error(f"Error in inbound thread: {e}")
98
+ self.stop_event.set()
99
+ finally:
100
+ e3_logger.info("Close inbound connection")
101
+
102
+ def _outbound_connection(self):
103
+ """
104
+ Outbound is for all the messages that should go to the RAN after the initial setup
105
+ Messages are dApp Control Action and dApp Report Message
106
+ """
107
+ e3_logger.info(f'Start outbound connection')
108
+ self.e3_connector.setup_outbound_connection()
109
+
110
+ e3_logger.info(f'Start outbound loop')
111
+ try:
112
+ while not self.stop_event.is_set():
113
+ try:
114
+ msg, data = self.outbound_queue.get(timeout=0.5)
115
+ except queue.Empty:
116
+ continue
117
+
118
+ e3_logger.debug(f"Outbound queue has got '{msg}', {data}")
119
+
120
+ match msg:
121
+ case "control":
122
+ payload = self.create_control_action(data)
123
+ case "report":
124
+ payload = self.create_dapp_report(data)
125
+
126
+ case _:
127
+ raise ValueError("Unrecognized value ", msg)
128
+
129
+ e3_logger.debug(f"Send the pdu encoded {payload}")
130
+ self.e3_connector.send(payload)
131
+
132
+ except Exception as e:
133
+ e3_logger.error(f"Error outbound thread: {e}")
134
+ self.stop_event.set()
135
+ finally:
136
+ e3_logger.info("Close outbound connection")
137
+
138
+ def _handle_incoming_data(self, data):
139
+ for callback in self.callbacks:
140
+ e3_logger.debug("Launch callback")
141
+ callback(data)
142
+
143
+ def schedule_control(self, payload: bytes):
144
+ self.outbound_queue.put(('control', payload))
145
+
146
+ def schedule_report(self, payload: bytes):
147
+ self.outbound_queue.put(('report', payload))
148
+
149
+ def add_callback(self, callback):
150
+ if callback not in self.callbacks:
151
+ e3_logger.debug("Add callback")
152
+ self.callbacks.append(callback)
153
+
154
+ def remove_callback(self, callback):
155
+ if callback in self.callbacks:
156
+ e3_logger.debug("Remove callback")
157
+ self.callbacks.remove(callback)
158
+
159
+ def terminate_connections(self):
160
+ e3_logger.info("Stop event")
161
+ self.stop_event.set()
162
+
163
+ if hasattr(self, "inbound_process"):
164
+ self.inbound_process.join()
165
+ if hasattr(self, "outbound_process"):
166
+ self.outbound_process.join()
167
+
168
+ self.e3_connector.dispose()
169
+
170
+ def create_control_action(self, actionData: bytes):
171
+ control_message = ("controlAction", {"actionData": actionData})
172
+ payload = self.defs.encode("E3-PDU", control_message)
173
+ return payload
174
+
175
+ def create_dapp_report(self, reportData: bytes):
176
+ dapp_report_message = ("dAppReport", {"reportData": reportData})
177
+ payload = self.defs.encode("E3-PDU", dapp_report_message)
178
+ return payload
179
+
180
+ def create_setup_request(self, ranId: int = 1, ranFunctions: list = []):
181
+ setup_request_message = ("setupRequest", {"ranIdentifier": ranId, "ranFunctionsList": ranFunctions})
182
+ payload = self.defs.encode("E3-PDU", setup_request_message)
183
+ return payload
184
+
185
+ def __del__(self):
186
+ if not self.stop_event.is_set():
187
+ self.terminate_connections()
@@ -0,0 +1,23 @@
1
+ import logging
2
+ import os
3
+
4
+ LOG_DIR = ('.' if os.geteuid() != 0 else '') + '/logs/'
5
+
6
+ def setup_logger(name, log_file, level=logging.INFO, format_str="[%(name)s] [%(created)f] %(levelname)s - %(message)s"):
7
+ logger = logging.getLogger(name)
8
+ logger.setLevel(level)
9
+
10
+ handler = logging.FileHandler(log_file)
11
+ handler.setLevel(level)
12
+
13
+ formatter = logging.Formatter(format_str)
14
+ handler.setFormatter(formatter)
15
+
16
+ # Avoid adding multiple handlers if logger is reused
17
+ if not logger.handlers:
18
+ logger.addHandler(handler)
19
+
20
+ return logger
21
+
22
+ e3_logger = setup_logger("e3_logger", f"{LOG_DIR}/e3.log", format_str="[E3] [%(created)f] %(levelname)s - %(message)s")
23
+ dapp_logger = setup_logger("dapp_logger", f"{LOG_DIR}/dapp.log", format_str="[dApp] [%(created)f] %(levelname)s - %(message)s")
spectrum/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ import os
2
+ import sys
3
+
4
+ sys.path.append(os.path.realpath(os.getcwd()))
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ dApp for Spectrum Sharing
4
+ """
5
+
6
+ __author__ = "Andrea Lacava"
7
+
8
+ import multiprocessing
9
+ import time
10
+ import numpy as np
11
+ # np.set_printoptions(threshold=sys.maxsize)
12
+
13
+ from dapp.dapp import DApp
14
+ from e3interface.e3_logging import dapp_logger, LOG_DIR
15
+
16
+ class SpectrumSharingDApp(DApp):
17
+
18
+ ### Configuration ###
19
+ # gNB runs with BW = 40 MHz, with -E (3/4 sampling)
20
+ # Center frequency = 3.6192 GHz
21
+ # OAI sampling frequency 46.08e6
22
+ # No SRS for now, hence in gNB config file set do_SRS = 0
23
+ # gNB->frame_parms.ofdm_symbol_size = 1536
24
+ # gNB->frame_parms.first_carrier_offset = 900
25
+ # Noise floor threshold needs to be calibrated
26
+ # We receive the symbols and average them over some frames, and do thresholding.
27
+
28
+ def __init__(self, id: int = 1, ota: bool = False, save_iqs: bool = False, control: bool = False, link: str = 'posix', transport:str = 'uds', **kwargs):
29
+ super().__init__(link=link, transport=transport, **kwargs)
30
+
31
+ self.bw = 40.08e6 # Bandwidth in Hz
32
+ self.center_freq = 3.6192e9 # Center frequency in Hz
33
+ self.First_carrier_offset = 900
34
+ self.Num_car_prb = 12
35
+ self.prb_thrs = 75 # This avoids blacklisting PRBs where the BWP is scheduled (it’s a workaround bc the UE and gNB would not be able to communicate anymore, a cleaner fix is to move the BWP if needed or things like that)
36
+ self.FFT_SIZE = 1536
37
+ self.Average_over_frames = 63
38
+
39
+ if ota:
40
+ dapp_logger.info(f'Using OTA configuration')
41
+ self.Noise_floor_threshold = 20 # this really depends on the RF conditions and should be carefully calibrated
42
+ else: # Colosseum
43
+ dapp_logger.info(f'Using Colosseum configuration')
44
+ self.Noise_floor_threshold = 53
45
+
46
+ self.save_iqs = save_iqs
47
+ self.e3_interface.add_callback(self.get_iqs_from_ran)
48
+ if self.save_iqs:
49
+ self.iq_save_file = open(f"{LOG_DIR}/iqs_{int(time.time())}.bin", "ab")
50
+ self.save_counter = 0
51
+ self.limit_per_file = 200
52
+ self.control = control
53
+ dapp_logger.info(f"Control is {'not ' if not self.control else ''}active")
54
+
55
+ self.energyGui = kwargs.get('energyGui', False)
56
+ self.iqPlotterGui = kwargs.get('iqPlotterGui', False)
57
+ self.demoGui = kwargs.get('demoGui', False)
58
+
59
+ self.control_count = 1
60
+ self.abs_iq_av = np.zeros(self.FFT_SIZE)
61
+
62
+ if self.energyGui:
63
+ from visualization.energy import EnergyPlotter
64
+ self.sig_queue = multiprocessing.Queue()
65
+ self.energyPlotter = EnergyPlotter(self.FFT_SIZE, bw=self.bw, center_freq=self.center_freq)
66
+
67
+ if self.iqPlotterGui:
68
+ from visualization.iq import IQPlotter
69
+ self.iq_queue = multiprocessing.Queue()
70
+ iq_size = self.FFT_SIZE * 2 # double the size of ofdm_symbol_size since real and imaginary parts are interleaved
71
+ self.iqPlotter = IQPlotter(buffer_size=500, iq_size=iq_size, bw=self.bw, center_freq=self.center_freq)
72
+
73
+ if self.demoGui:
74
+ from visualization.dashboard import DemoGui
75
+ self.demo_queue = multiprocessing.Queue()
76
+ iq_size = self.FFT_SIZE * 2 # double the size of ofdm_symbol_size since real and imaginary parts are interleaved
77
+ self.demo = DemoGui(buffer_size=100, iq_size=iq_size)
78
+
79
+ def get_iqs_from_ran(self, data):
80
+ dapp_logger.debug(f'Triggered callback')
81
+ if self.save_iqs:
82
+ dapp_logger.debug("I will write on the logfile iqs")
83
+ self.save_counter += 1
84
+ self.iq_save_file.write(data)
85
+ self.iq_save_file.flush()
86
+ if self.save_counter > self.limit_per_file:
87
+ self.iq_save_file.close()
88
+ self.iq_save_file = open(f"{LOG_DIR}/iqs_{int(time.time())}.bin", "ab")
89
+
90
+ iq_arr = np.frombuffer(data, dtype=np.int16)
91
+ dapp_logger.debug(f"Shape of iq_arr {iq_arr.shape}")
92
+
93
+ if self.iqPlotterGui:
94
+ self.iq_queue.put(iq_arr)
95
+
96
+ if self.demoGui:
97
+ self.demo_queue.put(("iq_data", iq_arr))
98
+
99
+ if self.control:
100
+ dapp_logger.debug("Start control operations")
101
+ iq_comp = iq_arr[::2] + iq_arr[1::2] * 1j
102
+ dapp_logger.debug(f"Shape of iq_comp {iq_comp.shape}")
103
+ abs_iq = np.abs(iq_comp).astype(float)
104
+ dapp_logger.debug(f"After iq division self.abs_iq_av: {self.abs_iq_av.shape} abs_iq: {abs_iq.shape}")
105
+ self.abs_iq_av += abs_iq
106
+ self.control_count += 1
107
+ dapp_logger.debug(f"Control count is: {self.control_count}")
108
+
109
+ if self.iqPlotterGui:
110
+ self.iq_queue.put(iq_arr)
111
+
112
+ if self.control_count == self.Average_over_frames:
113
+ abs_iq_av_db = 20 * np.log10(1 + (self.abs_iq_av/(self.Average_over_frames)))
114
+ abs_iq_av_db_offset_correct = np.append(abs_iq_av_db[self.First_carrier_offset:self.FFT_SIZE],abs_iq_av_db[0:self.First_carrier_offset])
115
+ dapp_logger.info(f'--- AVG VALUES ----')
116
+ dapp_logger.info(f'abs_iq_av_db_offset_correct: {abs_iq_av_db_offset_correct.mean()}')
117
+ dapp_logger.info(f'--- MAX VALUES ----')
118
+ dapp_logger.info(f'abs_iq_av_db_offset_correct: {abs_iq_av_db_offset_correct.max()}')
119
+ # last_5_max_abs_iq_av_db_offset_correct = np.sort(abs_iq_av_db_offset_correct)[-20:]
120
+ # dapp_logger.info(f'Last 20 max values (abs_iq_av_db_offset_correct): {last_5_max_abs_iq_av_db_offset_correct}')
121
+
122
+
123
+ # PRB blocking based on the noise floor threshold
124
+ f_ind = np.arange(self.FFT_SIZE)
125
+ blklist_sub_carrier = f_ind[abs_iq_av_db_offset_correct > self.Noise_floor_threshold]
126
+ np.sort(blklist_sub_carrier)
127
+ dapp_logger.info(f'blklist_sub_carrier: {blklist_sub_carrier}')
128
+ prb_blk_list = np.unique((np.floor(blklist_sub_carrier/self.Num_car_prb))).astype(np.uint16)
129
+ dapp_logger.info(f'prb_blk_list: {prb_blk_list}')
130
+ prb_blk_list = prb_blk_list[prb_blk_list > self.prb_thrs]
131
+ # prb_blk_list = np.array([76, 77, 78, 79, 80, 81, 82, 83], dtype=np.uint16) # 76, 77, 78, 79, 80, 81, 82, 83
132
+ # prb_blk_list = np.arange(start=76, stop=140, dtype=np.uint16)
133
+ dapp_logger.info(f"Blacklisted prbs ({prb_blk_list.size}): {prb_blk_list}")
134
+ prb_new = prb_blk_list.view(prb_blk_list.dtype.newbyteorder('>'))
135
+
136
+ # Create the payload
137
+ size = prb_blk_list.size.to_bytes(2,'little')
138
+ prbs_to_send = prb_new.tobytes(order="C")
139
+
140
+ # Schedule the delivery
141
+ self.e3_interface.schedule_control(size+prbs_to_send)
142
+
143
+ if self.energyGui:
144
+ self.sig_queue.put(abs_iq_av_db)
145
+
146
+ if self.demoGui:
147
+ self.demo_queue.put(("prb_list", prb_blk_list))
148
+
149
+ # Reset the variables
150
+ self.abs_iq_av = np.zeros(self.FFT_SIZE)
151
+ self.control_count = 1
152
+
153
+ def _control_loop(self):
154
+ if self.energyGui:
155
+ abs_iq_av_db = self.sig_queue.get()
156
+ self.energyPlotter.process_iq_data(abs_iq_av_db)
157
+ if self.iqPlotterGui:
158
+ iq_data = self.iq_queue.get()
159
+ self.iqPlotter.process_iq_data(iq_data)
160
+ if self.demoGui:
161
+ message = self.demo_queue.get()
162
+ self.demo.process_iq_data(message)
163
+
164
+ def _stop(self):
165
+ if self.save_iqs:
166
+ self.iq_save_file.close()
167
+
168
+ if self.demoGui:
169
+ self.demo.stop()
@@ -0,0 +1,4 @@
1
+ import os
2
+ import sys
3
+
4
+ sys.path.append(os.path.realpath(os.getcwd()))