Skip to content

Commit

Permalink
New version 0.0.2
Browse files Browse the repository at this point in the history
Improved the SpectrumSharingDApp by exposing the noise floor value instead of the ota flag and a classifier that will label the values on the Dashboard

Renamed DemoGui to Dashboard
  • Loading branch information
Thecave3 authored Feb 3, 2025
2 parents de6b372 + 554dc0c commit 39aa992
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 33 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.1
0.0.2
4 changes: 1 addition & 3 deletions docs/gui_uml.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class IQPlotter {
+ process_iq_data(iq_data)
}

class DemoGui {
class Dashboard {
-- Attributes --
- BUFFER_SIZE : int
- iq_size : int
Expand All @@ -49,6 +49,4 @@ class DemoGui {
+ stop()
}

DemoGui --> IQPlotter : "contains"
DemoGui --> EnergyPlotter : "contains"
@enduml
14 changes: 11 additions & 3 deletions examples/spectrum_dapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@ def stop_program(time_to_wait, dapp: SpectrumSharingDApp):
def main(args, time_to_wait: float = 60.0):
# with open(f"{LOG_DIR}/busy.txt", "w") as f:
# f.close()

dapp = SpectrumSharingDApp(ota=args.ota, save_iqs=args.save_iqs, control=args.control, link=args.link, transport=args.transport,
energyGui=args.energy_gui, iqPlotterGui=args.iq_plotter_gui, demoGui=args.demo_gui)

if args.ota:
print(f'Using OTA configuration')
noise_floor_threshold = 20 # this really depends on the RF conditions and should be carefully calibrated
else: # Colosseum
print(f'Using Colosseum configuration')
noise_floor_threshold = 53

print(f'Threshold {noise_floor_threshold}')

dapp = SpectrumSharingDApp(noise_floor_threshold=noise_floor_threshold, save_iqs=args.save_iqs, control=args.control, link=args.link, transport=args.transport,
energyGui=args.energy_gui, iqPlotterGui=args.iq_plotter_gui, dashboard=args.demo_gui)
dapp.setup_connection()

if args.timed:
Expand Down
36 changes: 14 additions & 22 deletions src/spectrum/spectrum_dapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class SpectrumSharingDApp(DApp):
# Noise floor threshold needs to be calibrated
# We receive the symbols and average them over some frames, and do thresholding.

def __init__(self, id: int = 1, ota: bool = False, save_iqs: bool = False, control: bool = False, link: str = 'posix', transport:str = 'uds', **kwargs):
def __init__(self, id: int = 1, noise_floor_threshold: int = 53, save_iqs: bool = False, control: bool = False, link: str = 'posix', transport:str = 'uds', **kwargs):
super().__init__(link=link, transport=transport, **kwargs)

self.bw = 40.08e6 # Bandwidth in Hz
Expand All @@ -35,14 +35,7 @@ def __init__(self, id: int = 1, ota: bool = False, save_iqs: bool = False, contr
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)
self.FFT_SIZE = 1536
self.Average_over_frames = 63

if ota:
dapp_logger.info(f'Using OTA configuration')
self.Noise_floor_threshold = 20 # this really depends on the RF conditions and should be carefully calibrated
else: # Colosseum
dapp_logger.info(f'Using Colosseum configuration')
self.Noise_floor_threshold = 53

self.noise_floor_threshold = noise_floor_threshold
self.save_iqs = save_iqs
self.e3_interface.add_callback(self.get_iqs_from_ran)
if self.save_iqs:
Expand All @@ -54,7 +47,7 @@ def __init__(self, id: int = 1, ota: bool = False, save_iqs: bool = False, contr

self.energyGui = kwargs.get('energyGui', False)
self.iqPlotterGui = kwargs.get('iqPlotterGui', False)
self.demoGui = kwargs.get('demoGui', False)
self.dashboard = kwargs.get('dashboard', False)

self.control_count = 1
self.abs_iq_av = np.zeros(self.FFT_SIZE)
Expand All @@ -70,11 +63,12 @@ def __init__(self, id: int = 1, ota: bool = False, save_iqs: bool = False, contr
iq_size = self.FFT_SIZE * 2 # double the size of ofdm_symbol_size since real and imaginary parts are interleaved
self.iqPlotter = IQPlotter(buffer_size=500, iq_size=iq_size, bw=self.bw, center_freq=self.center_freq)

if self.demoGui:
from visualization.dashboard import DemoGui
if self.dashboard:
from visualization.dashboard import Dashboard
self.demo_queue = multiprocessing.Queue()
iq_size = self.FFT_SIZE * 2 # double the size of ofdm_symbol_size since real and imaginary parts are interleaved
self.demo = DemoGui(buffer_size=100, iq_size=iq_size)
classifier = kwargs.get('classifier', None)
self.demo = Dashboard(buffer_size=100, iq_size=iq_size, classifier=classifier)

def get_iqs_from_ran(self, data):
dapp_logger.debug(f'Triggered callback')
Expand All @@ -93,7 +87,7 @@ def get_iqs_from_ran(self, data):
if self.iqPlotterGui:
self.iq_queue.put(iq_arr)

if self.demoGui:
if self.dashboard:
self.demo_queue.put(("iq_data", iq_arr))

if self.control:
Expand All @@ -106,9 +100,6 @@ def get_iqs_from_ran(self, data):
self.control_count += 1
dapp_logger.debug(f"Control count is: {self.control_count}")

if self.iqPlotterGui:
self.iq_queue.put(iq_arr)

if self.control_count == self.Average_over_frames:
abs_iq_av_db = 20 * np.log10(1 + (self.abs_iq_av/(self.Average_over_frames)))
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])
Expand All @@ -119,10 +110,9 @@ def get_iqs_from_ran(self, data):
# last_5_max_abs_iq_av_db_offset_correct = np.sort(abs_iq_av_db_offset_correct)[-20:]
# dapp_logger.info(f'Last 20 max values (abs_iq_av_db_offset_correct): {last_5_max_abs_iq_av_db_offset_correct}')


# PRB blocking based on the noise floor threshold
f_ind = np.arange(self.FFT_SIZE)
blklist_sub_carrier = f_ind[abs_iq_av_db_offset_correct > self.Noise_floor_threshold]
blklist_sub_carrier = f_ind[abs_iq_av_db_offset_correct > self.noise_floor_threshold]
np.sort(blklist_sub_carrier)
dapp_logger.info(f'blklist_sub_carrier: {blklist_sub_carrier}')
prb_blk_list = np.unique((np.floor(blklist_sub_carrier/self.Num_car_prb))).astype(np.uint16)
Expand All @@ -143,7 +133,7 @@ def get_iqs_from_ran(self, data):
if self.energyGui:
self.sig_queue.put(abs_iq_av_db)

if self.demoGui:
if self.dashboard:
self.demo_queue.put(("prb_list", prb_blk_list))

# Reset the variables
Expand All @@ -154,16 +144,18 @@ def _control_loop(self):
if self.energyGui:
abs_iq_av_db = self.sig_queue.get()
self.energyPlotter.process_iq_data(abs_iq_av_db)

if self.iqPlotterGui:
iq_data = self.iq_queue.get()
self.iqPlotter.process_iq_data(iq_data)
if self.demoGui:

if self.dashboard:
message = self.demo_queue.get()
self.demo.process_iq_data(message)

def _stop(self):
if self.save_iqs:
self.iq_save_file.close()

if self.demoGui:
if self.dashboard:
self.demo.stop()
14 changes: 11 additions & 3 deletions src/visualization/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
exit(-1)
import numpy as np

class DemoGui:
def __init__(self, buffer_size=100, iq_size=1536, bw=46.08e6, center_freq=3.6192e9):
class Dashboard:
def __init__(self, buffer_size=100, iq_size=1536, bw=46.08e6, center_freq=3.6192e9, classifier=None):
# TODO double check killing conditions
# Flask app setup
self.app = Flask(__name__)
Expand All @@ -30,6 +30,8 @@ def __init__(self, buffer_size=100, iq_size=1536, bw=46.08e6, center_freq=3.6192
# Route setup
self.app.add_url_rule("/", view_func=self.index)

self.classifier = classifier if classifier is not None else None

# SocketIO event handlers
self.socketio.on_event("connect", self.handle_initial_connection)
self._initialize_plot()
Expand Down Expand Up @@ -63,6 +65,7 @@ def handle_initial_connection(self):
data_buffer = {
"magnitude": np.zeros((self.BUFFER_SIZE, iqs_to_show)).tolist(),
"num_prbs": num_prbs,
"predicted_label": self.classifier is not None
}
self.socketio.emit("initialize_plot", data_buffer)

Expand All @@ -88,6 +91,11 @@ def process_iq_data(self, message):
if self.sampling_counter >= self.sampling_threshold:
magnitude_dB = self._process_iq_data(iq_data)[::-1].tolist() # visualization processing is delegated to client
self.socketio.emit("update_plot", {"magnitude": magnitude_dB})

if self.classifier:
label = self.classifier.predict(iq_data)
self.socketio.emit("update_plot", {"predicted_label": label})

self.sampling_counter = 0

elif plot == "prb_list":
Expand All @@ -101,4 +109,4 @@ def stop(self):
self.run_thread.join()

if __name__ == "__main__":
server_app = DemoGui()
server_app = Dashboard()
21 changes: 20 additions & 1 deletion src/visualization/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<body>
<div id="prbs"></div>
<div id="magnitude"></div>
<div id="predicted_label"></div>

<script>
var waterfall = [{
Expand All @@ -22,7 +23,7 @@
var waterfallLayout = {
title: "Magnitude of the Sensed Spectrum",
xaxis: {
title: "OFDM Symbols"
title: "Subcarriers of the OFDM Symbol"
},
yaxis: {
title: "Time samples",
Expand Down Expand Up @@ -152,8 +153,18 @@
}
};

var classifierLayout = {
title: {
text: "Predicted Label: N/A",
font: { size: 24 }
},
xaxis: { visible: false },
yaxis: { visible: false }
};

var waterfall_buffer;
var prbs_buffer;
var predictedLabel;

var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('initialize_plot', function (data) {
Expand All @@ -163,6 +174,9 @@
prbs[0].z = prbs_buffer;
Plotly.newPlot('magnitude', waterfall, waterfallLayout);
Plotly.newPlot('prbs', prbs, prbsLayout);
if(data.predicted_label){
Plotly.newPlot("predicted_label", [], classifierLayout);
}
});

socket.on('update_plot', function (data) {
Expand All @@ -184,6 +198,11 @@
prbs_buffer[0].fill(0);
data.prb_list.forEach(index => prbs_buffer[0][index] = 1);
Plotly.restyle('prbs', { z: [prbs_buffer] });
} else if ('predicted_label' in data) {
console.log("Predicted label is: ", data.predicted_label);
Plotly.relayout('predicted_label', {
'title.text': `Predicted Label: ${data.predicted_label}`
});
} else {
console.error('Error unknown values in data ', data);
}
Expand Down

0 comments on commit 39aa992

Please sign in to comment.