Skip to content

Commit d2b074a

Browse files
authored
Merge pull request #3 from NET-BYU/dev
Dev
2 parents e4132b1 + b731ddc commit d2b074a

File tree

3 files changed

+278
-2
lines changed

3 files changed

+278
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,4 @@ After the Python code has been run, you can expect to have the following:
177177
## Thank You!
178178
A lot of work has gone into this project, and it means a lot to have it reviewed and used. Please feel free to clone it and try your own modifications. Best of luck!
179179

180-
A big thanks to Panasonic - Cirrus for funding our work!
180+
A big thanks to Panasonic - Cirrus for funding our work!

auto_capture.py

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#----------------------------------------------------------------------------
4+
# Created By : Bryson Schiel - @schielb (GitHub)
5+
# Created Date: May 18, 2023
6+
# Latest Update: May 18, 2023
7+
# version ='1.1'
8+
# ---------------------------------------------------------------------------
9+
"""
10+
This file tries to "zoom in" on the knee for each RSU during testing.
11+
"""
12+
# ---------------------------------------------------------------------------
13+
14+
import pyshark
15+
from yaml import safe_load
16+
from resources.mesh_class import MeshClass
17+
import copy
18+
from threading import Event, Thread
19+
import time
20+
import os
21+
from datetime import datetime
22+
import matplotlib.pyplot as plt
23+
24+
def call_repeatedly(interval, func, *args):
25+
stopped = Event()
26+
def loop():
27+
while not stopped.wait(interval): # the first call is in `interval` secs
28+
func(*args)
29+
Thread(target=loop).start()
30+
return stopped.set
31+
32+
33+
with open("./cv2x.yml") as f:
34+
yaml_data = safe_load(f)
35+
36+
mesh = MeshClass(yaml_data["mesh_ip"])
37+
38+
rsus : dict = copy.deepcopy(yaml_data["rsus"])
39+
40+
data = {}
41+
cur_time : int
42+
43+
def display_data():
44+
global cur_time
45+
cur_time += 1
46+
print("\r Time: %d // " % cur_time, end='')
47+
for rsu in data.keys():
48+
print(rsu + " ≈ " + str(data[rsu]) + " // ", end='')
49+
50+
51+
def clear_data():
52+
global cur_time
53+
cur_time = 0
54+
for name in rsus.keys():
55+
data[name] = 0
56+
57+
def handle_paket(block):
58+
#print("Got something!")
59+
i = 0
60+
for rsu in rsus.keys():
61+
if (hasattr(block, 'ip')
62+
and str(block['ip'].src) == rsus[rsu]['ip'] # RSU IP Address
63+
and str(block['ip'].dst) == yaml_data['host_ip'] # This IP address ### CHANGE MANUALLY IN YAML! ###
64+
and str(block['ip'].proto) == '17' # UDP Protocol number
65+
and hasattr(block, 'udp')
66+
and str(block['udp'].dstport) == str(rsus[rsu]['dst_port']) # This UDP reception port
67+
and hasattr(block, 'DATA')
68+
):
69+
# If we got in here, then this packet is a forwarded C-V2X packet from the specified RSU
70+
data[rsu] += 1
71+
#print ("Packet received: ", rsu)
72+
73+
def uniquify(path):
74+
filename, extension = os.path.splitext(path)
75+
counter = 1
76+
77+
while os.path.exists(path):
78+
path = filename + "(" + str(counter) + ")" + extension
79+
counter += 1
80+
81+
return path
82+
83+
cap_folder_name = "Packet_Captures/" + datetime.now().strftime("%b-%d-%H:%M:%S")
84+
res_folder_name = "Results/" + datetime.now().strftime("%b-%d-%H:%M:%S")
85+
86+
#See if we need to create a new folder and csv file for today for today
87+
for newpath in [cap_folder_name, res_folder_name]:
88+
if not os.path.exists(newpath):
89+
os.makedirs(newpath)
90+
91+
# This is where we see some tricky stuff. We set a bottom and a top range for attenuation values, and then
92+
# we need to sort through each one for each RSU to find the knee of the RSU (where, for one dB value, we
93+
# get a reception rate above the critical safrety limit, and for the next dB, that RSU performs below the
94+
# critical safety limit).
95+
#
96+
# I think that in this case, it is fine to keep gathering from each RSU, even if we have already found a
97+
# knee for the other RSUs. I think the best way is to find a middle sort option for each RSU, and in
98+
# reality we need to focus on 1 RSU at a time. We will probably wind up having to go back and forth quite
99+
# a bit to make it work out, but that should be okay.
100+
bottom_att = yaml_data["att_low"]
101+
top_att = yaml_data["att_high"]
102+
attenuation = bottom_att
103+
104+
critical_safety_limit = 0.9
105+
106+
knee_finders = {}
107+
for rsu in rsus:
108+
knee_finders[rsu] = {"bottom": bottom_att, "top": top_att, "knee_found": False}
109+
110+
# This set of numbers lets us make sure that we find knees iteratively, focusing on one and then the other
111+
num_knees_found = 0
112+
num_rsus = len(rsus)
113+
114+
results = {}
115+
116+
117+
def midway(bottom, top):
118+
return int(((top - bottom) / 2) + bottom)
119+
120+
while num_knees_found < num_rsus:
121+
# Clear the data and declare the start of the trial
122+
clear_data()
123+
print("\x1B[32mSetting up trial on attenuation value\x1B[35m %d\x1B[32m db for\x1B[35m %d\x1B[32m seconds...\x1B[37m"
124+
% (attenuation, yaml_data["trial_length"]))
125+
126+
cap_file_name = uniquify(cap_folder_name + "/attenuation_%d.pcap" % attenuation)
127+
128+
cap = pyshark.LiveCapture(interface=yaml_data["wireshark_interface"],
129+
bpf_filter="udp and not src host %s" % yaml_data['host_ip'],
130+
output_file=cap_file_name)
131+
132+
133+
134+
# Go through and set all the attenuation values we need
135+
for tx_port in yaml_data["static_mesh_ports"]:
136+
for rsu in rsus.keys():
137+
rx_port = rsus[rsu]["mesh_port"]
138+
partial_att = rsus[rsu]["att_offset"]
139+
diff_att = attenuation - partial_att
140+
diff_att = round(diff_att * 4) / 4 # needs a multiple of 0.25
141+
142+
#print('dbg: mesh.set_att(%s, %s, %f)' % (tx_port, rx_port, diff_att))
143+
mesh.set_att(tx_port, rx_port, diff_att)
144+
145+
time.sleep(10) # Delay to allow new setup to settle
146+
print("Starting trial:")
147+
148+
# Start the repeating timer
149+
end_timer = call_repeatedly(1, display_data)
150+
151+
# Start the packet capture
152+
try:
153+
cap.apply_on_packets(handle_paket, timeout=int(yaml_data["trial_length"]))
154+
155+
except Exception as e:
156+
if e is TimeoutError:
157+
# This just means that a packet was caught mid-exit; not fatal to the experiment
158+
pass
159+
finally:
160+
end_timer()
161+
#timer.cancel()
162+
print()
163+
print("\x1B[32mEnding trial for\x1B[35m %d\x1B[32m db\x1B[37m" % attenuation, end="\n")
164+
165+
print("Saving data from trial...")
166+
# Save the results in their own files
167+
cap.close()
168+
169+
results[attenuation] = {}
170+
171+
for rsu in rsus:
172+
173+
174+
total_time_gap = yaml_data["trial_length"]
175+
num_packets = data[rsu]
176+
177+
estimated_num_spaced = int(total_time_gap * 10)
178+
percent_reception = float(float(num_packets) / float(estimated_num_spaced))
179+
180+
summaries_file_name = '%s/summaries_%s.txt' % (res_folder_name, rsu)
181+
182+
print("Saving %s data in %s" % (rsu, summaries_file_name))
183+
184+
print(file=open(summaries_file_name, 'a'))
185+
print("Attenuation ", attenuation, file=open(summaries_file_name, 'a'))
186+
print("Number of packets: ", num_packets, file=open(summaries_file_name, 'a'))
187+
print('Total time gap: ', total_time_gap, file=open(summaries_file_name, 'a'))
188+
print('Total expected packets: ', estimated_num_spaced, file=open(summaries_file_name, 'a'))
189+
print("Calculated missed packets: ", estimated_num_spaced - num_packets, file=open(summaries_file_name, 'a'))
190+
print("Percent reception: ", percent_reception, file=open(summaries_file_name, 'a'))
191+
192+
# Now we focus on taking the current recpetion rate and trying to find the knee
193+
if percent_reception > critical_safety_limit:
194+
if attenuation > knee_finders[rsu]["bottom"]:
195+
knee_finders[rsu]["bottom"] = attenuation
196+
else:
197+
# Crucially, we don't want to record a slightly low value if a higher attenuation, for
198+
# whatever reason, gives us a better recpetion rate.
199+
if attenuation < knee_finders[rsu]["top"] and attenuation > knee_finders[rsu]["bottom"]:
200+
knee_finders[rsu]["top"] = attenuation
201+
202+
results[attenuation][rsu] = percent_reception
203+
204+
205+
206+
print("Data saved 👍\n")
207+
# Here, we reset each knee as needed
208+
for rsu in list(rsus)[num_knees_found:]:
209+
if knee_finders[rsu]["top"] - knee_finders[rsu]["bottom"] == 1:
210+
knee_finders[rsu]["knee_found"] = True
211+
num_knees_found += 1
212+
else:
213+
if knee_finders[rsu]["top"] - knee_finders[rsu]["bottom"] < 0:
214+
# Error, top and bottom are switched, move top back
215+
knee_finders[rsu]["top"] = top_att
216+
# No matter what, if the current index doesn't check out, break
217+
break
218+
219+
# Here, we need to set the "attenuation" variable for the next loop
220+
if num_knees_found < num_rsus:
221+
nkf = list(knee_finders)[num_knees_found]
222+
attenuation = midway(knee_finders[nkf]["bottom"], knee_finders[nkf]["top"])
223+
224+
print("New attenuation: %d" % attenuation)
225+
226+
##############################################################################################
227+
# At this point, all attenuations have been gathered, and we are ready to display the results.
228+
##############################################################################################
229+
for rsu in rsus:
230+
231+
values = []
232+
233+
num_vals = 0
234+
att = 0
235+
rec = 0.0
236+
237+
for attenuation in results:
238+
values.append((attenuation, results[attenuation][rsu]))
239+
values.sort()
240+
241+
attenuations = []
242+
reception_rates = []
243+
for i in values:
244+
attenuations.append(i[0])
245+
reception_rates.append(i[1] * 100.0)
246+
247+
data['att'] = attenuations
248+
data[rsu] = {}
249+
data[rsu]['rate'] = reception_rates
250+
251+
252+
plt.plot(attenuations, reception_rates, marker = 'o')
253+
plt.xlabel("Attenuations (dB)")
254+
plt.ylabel("Packet Reception Rate (%)")
255+
plt.title("Reception Rate per Attenuation (%s)" % rsu)
256+
plt.ylim(-4, 104)
257+
plt.axhline(y=90, color='red', linestyle='--', label='Critical Safety Limit: 90%')
258+
plt.show()
259+
plt.savefig(res_folder_name + '/Attenuations-%s.png' % rsu)
260+
plt.clf()
261+
262+
for rsu in rsus:
263+
plt.plot(data['att'], data[rsu]['rate'], label=rsu, marker = 'o')
264+
265+
plt.xlabel("Attenuation (db)")
266+
plt.ylabel("Packet Reception Rate (%)")
267+
plt.ylim(-4, 104)
268+
plt.title("Reception Rate per Attenuation (All RSU Comparison)")
269+
plt.axhline(y=90, color='red', linestyle='--', label='Critical Safety Limit: 90%')
270+
plt.legend()
271+
plt.savefig(res_folder_name + '/Comparison-Attenuations.png')
272+
plt.show()
273+
plt.clf()
274+

resources/clear_mesh.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@
4444
for port1 in port1s:
4545
for port2 in port2s:
4646
if port1 != port2:
47-
mesh.set_att(port1, port2, att_to_set)
47+
mesh.set_att(port1, port2, att_to_set)
48+
49+

0 commit comments

Comments
 (0)