Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added modules for Shannon Capacity, BER, PEP, ETX and Path calculations using Dijkstra's #6

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions examples/overview/inputs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"P_t": 2.14,
"n": 2,
"K": 0.0004,
"d_0": 1,
"sigma": 0.8,
"snr_threshold" : 0.5,
"Bandwidth": 4000000,
"packet_size": 800,
"error_threshold": 100,
"source": "unit_3",
"destination": "unit_4"
}
30 changes: 16 additions & 14 deletions examples/overview/instructions.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
from mobscript import *

load_map("map.json")
import pygame
graph=load_map("map.json")

set_attribute("time_duration", 10)
set_attribute("time_step", 1)

p = create_units("pedestrian", "type_a", 1)
u = create_units("UAV", "type_b", 1)
v = create_units("vehicle", "type_c", 2)
c = create_units("compute", "type_a", 3)
group_1 = create_group(p, c)
#p = create_units("pedestrian", "type_a", 1, False)
u = create_units("UAV", "type_b", 1, True)
u_2 = create_units("UAV", "type_b", 1, True)
u_3 = create_units("UAV", "type_b", 1, True)
v = create_units("vehicle", "type_c", 1, True)
c = create_units("compute", "type_a", 1, True)
#group_1 = create_group(p, c)

home = (15, 25)
set_starting_position(p, home)
set_waypoints(v, [(20,20), (50,50)], 7, 9)
set_waypoints(v, [(0,0), (10,0)], 0, 4)
home = (10,0)
set_starting_position(u, home)
set_waypoints(v, [(20,20), (50,50)], 0,1)
set_waypoints(u_2, [(15,25), (20,15)], 0,1)
set_waypoints(u_3, [(35,30), (30,32)], 0,2)
set_waypoints(v, [(40,15), (10,0)],0,2)
set_waypoints(c, [(0,0), (10,10)],1,3)
stop_movement(v, 3)
change_equipment_at_time(u, 3.0, True, "standard_radio")
change_equipment_at_time(v, 5.0, False, "standard_radio")

create_cellular_region([(0,0), (10,0), (20,10), (10,30), (0,30)])
34 changes: 32 additions & 2 deletions mobscript/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import math
import contextlib
import os
import json

from shapely.geometry.polygon import Polygon
from mobscript.data_structures.global_attributes import GlobalAttributes
Expand All @@ -13,6 +14,8 @@
from mobscript.map_controller import MapController
from mobscript.units_controller import UnitsController
from mobscript.util.util import set_equipment
from mobscript.log_normal_fading import ShannonCapacity
from mobscript.log_normal_fading import CalculateMetrics

thisdir = pathlib.Path(__file__).resolve().parent

Expand All @@ -31,6 +34,8 @@ class Instance:
global_attributes = GlobalAttributes(thisdir.joinpath("input_data_defaults", "global_attributes.json") )
delayed_instructions = DelayedInstructions()
script_count = 0
shannon_cap = ShannonCapacity()
error_prob = CalculateMetrics()

@staticmethod
def load_script(path: pathlib.Path) -> None:
Expand All @@ -45,9 +50,9 @@ def load_script(path: pathlib.Path) -> None:
def create_units(unit_name: str,
unit_type: str,
unit_count: int,
has_standard_radio: bool,
starting_position=(0, 0),
waypoints=[],
has_standard_radio=False,
has_cellular_radio=False,
has_satellite_link=False) -> Dict[str, Unit]:
return Instance.units_controller.create_unit(
Expand All @@ -56,7 +61,7 @@ def create_units(unit_name: str,
unit_count,
starting_position=(0, 0),
waypoints=[],
has_standard_radio=False,
has_standard_radio= has_standard_radio,
has_cellular_radio=False,
has_satellite_link=False
)
Expand Down Expand Up @@ -106,3 +111,28 @@ def change_equipment_at_time(units_list: Iterable[Unit],
change_time, turn_on, equipment_name=="standard_radio",
equipment_name=="cellular_radio", equipment_name=="satellite_link"
)


def shannon_calculation(links, P_t, K, n, d, d_0, sigma, bandwidth):
return Instance.shannon_cap.capacity_calculation(P_t, K, n, d, d_0, sigma, bandwidth)


def read_json_file(file_name):
with open(file_name, 'r') as f:
data = json.load(f)
# Extract arguments
P_t = data['P_t']
K = data['K']
n = data['n']
d = data['d']
d_0 = data['d_0']
sigma = data['sigma']
snr_threshold = data['snr_threshold']
bandwidth = data['Bandwidth']
packet_size = data['packet_size']
error_threshold = data['error_threshold']

return P_t, K, n, d_0, sigma, snr_threshold, bandwidth, packet_size, error_threshold

def calculate_dist_between_points(point_1: Tuple[Union[int, float], Union[int, float]],point_2: Tuple[Union[int, float], Union[int, float]]) -> float:
return math.sqrt((point_1[0] - point_2[0])**2 + (point_1[1] - point_2[1])**2)
169 changes: 160 additions & 9 deletions mobscript/display_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@
from .data_structures.unit import Unit
from .map_controller import MapController
from .units_controller import UnitsController
from .log_normal_fading import ShannonCapacity
from .log_normal_fading import CalculateMetrics
from .__init__ import read_json_file
from .__init__ import calculate_dist_between_points
import itertools
import json
import heapq
import matplotlib.pyplot as plt

def distance_point_to_line_segment(point, start, end):
"""Calculate the distance between a point and a line segment."""
dx = end[0] - start[0]
dy = end[1] - start[1]
if dx == dy == 0: # the segment is just a point
return math.hypot(point[0] - start[0], point[1] - start[1])

t = ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy) / (dx**2 + dy**2)
t = max(0, min(1, t)) # limit t to the range [0, 1]
closest_point = start[0] + t * dx, start[1] + t * dy
return math.hypot(point[0] - closest_point[0], point[1] - closest_point[1])

class UnitSprite():
def __init__(self,
Expand All @@ -32,14 +52,58 @@ def update(self, curr_time: Union[int, float]) -> None:
self.curr_pos = self.positions_history[time_index][self.unit_key]
self.curr_pos = (self.curr_pos[1], self.curr_pos[0])

def display(self,
convert_coord_func: Callable[[Tuple[int, int], Tuple[int, int], int], Tuple[int, int]],

global my_dict
my_dict = {}

def display(self,
convert_coord_func: Callable[[Tuple[int, int], Tuple[int, int], int], Tuple[int, int]],
origin: Tuple[int, int]) -> None:
screen_width, screen_height = self.screen.get_size()

converted_coord = convert_coord_func(self.curr_pos, origin, screen_height)

pygame.draw.circle(self.screen, (0, 0, 255), converted_coord, self.radius)

if self.unit_data.has_standard_radio == True:
my_dict[self.unit_key] = converted_coord
with open('mobile_network_scripting/mobscript/input_data_defaults/global_attributes.json', 'r') as f:
data = json.load(f)
min_distance = data.get('standard_radio_radius')

for key1, key2 in itertools.combinations(my_dict.keys(), 2):
coord1, coord2 = my_dict[key1], my_dict[key2]
distance = calculate_dist_between_points(coord1, coord2)

if distance < min_distance:
pygame.draw.line(self.screen, (255, 0, 0), coord1, coord2, width=1)
mouse_pos = pygame.mouse.get_pos()
line_start = coord1
line_end = coord2
line_thickness = 3
distance_to_line = distance_point_to_line_segment(mouse_pos, line_start, line_end)

if distance_to_line < line_thickness: # Display capacity only if mouse is over the line segment
P_t, K, n, d_0, sigma, snr_threshold, bandwidth, packet_size, error_threshold = read_json_file("mobile_network_scripting/examples/overview/inputs.json")
capacity = ShannonCapacity()
error_prob = CalculateMetrics()
capacity.capacity_calculation(P_t, K, n, distance, d_0, sigma, bandwidth)
error_prob.bit_error_probability( P_t, snr_threshold, distance ,d_0, K, sigma, bandwidth,n)

error_prob.packet_error_probability(snr_threshold, error_threshold, packet_size, P_t, distance ,d_0, K, sigma, bandwidth,n)

error_prob.calculate_ETX()
details_text = "Shannon Cap: " + str(capacity.shannon_capacity) + ", " + "BER: " + str(error_prob.bit_error_prob) + ", PEP: " + str(error_prob.pep) + ", ETX: " + str(error_prob.etx)


font = pygame.font.Font(None, 20)
details_surface = font.render(details_text, True, (255, 255, 255))
text_rect = details_surface.get_rect()
background_rect = pygame.Rect(mouse_pos, (text_rect.width + 40, text_rect.height + 30))
pygame.draw.rect(self.screen, (0, 0, 0), background_rect)
self.screen.blit(details_surface, (mouse_pos[0] + 10, mouse_pos[1] + 10))


class DisplayController:
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
Expand All @@ -49,15 +113,16 @@ def __init__(self,
units_controller: UnitsController,
map_controller: MapController,
positions_history: List[Dict[str, Tuple[Union[int, float], Union[int, float]]]],
global_attributes: GlobalAttributes) -> None:
global_attributes: GlobalAttributes
) -> None:
self.units_controller = units_controller
self.map_controller = map_controller
self.positions_history = positions_history
self.global_attributes = global_attributes
self.scale = self.map_controller.get_scale()
self.length_to_pixels = self.global_attributes.length_to_pixels
self.multiplier = self.global_attributes.length_to_pixels * self.scale

self.char_to_image['^'] = pygame.image.load("mobile_network_scripting/images/mountain.png")
self.char_to_image['_'] = pygame.image.load("mobile_network_scripting/images/ground.png")
self.char_to_image['r'] = pygame.image.load("mobile_network_scripting/images/road.png")
Expand All @@ -81,12 +146,10 @@ def draw_map(self, screen: pygame.Surface, origin: Tuple[int, int]) -> None:
pos_rect = self.convert_coord((self.scale * col, self.scale * row + self.scale), origin, screen_height)
pos_char = self.convert_coord((self.scale * col + self.scale / 2, self.scale * row + self.scale / 2),
origin, screen_height)
# pygame.draw.rect(screen, color, [pos_rect[0], pos_rect[1], self.multiplier, self.multiplier])
rect = [pos_rect[0], pos_rect[1], self.multiplier, self.multiplier]
curr_char = map[row][col]
screen.blit(pygame.transform.smoothscale(
self.char_to_image[curr_char], (self.multiplier, self.multiplier)), rect)
# screen.blit(font.render(map[row][col], True, (255, 0, 0)), pos_char)

def display(self) -> None:
pygame.init()
Expand All @@ -98,8 +161,11 @@ def display(self) -> None:

slider = Slider(screen, 100, 30, self.SCREEN_WIDTH - 310, 20, step=.001, initial=0, min=0,
max=self.global_attributes.time_duration)
button = Button(screen, 40, 27, 30, 30,
onClick=lambda: (paused := not paused))

def on_button_click():
nonlocal paused
paused = not paused
button = Button(screen, 40, 27, 30, 30, onClick=on_button_click)

time_duration_text = TextBox(screen, self.SCREEN_WIDTH - 180, 10, 180, 30,
fontSize=24, borderThickness=0, textColour=(50, 50, 50))
Expand All @@ -110,6 +176,91 @@ def display(self) -> None:
current_time_text.setText("Current Time: {}s".format(current_time))
current_time_text.disable()

min_distance = 150

def on_button_click_calculate_dijkstra():
nonlocal paused
paused = not paused

last_positions = {}
for unit, position in self.positions_history[-1].items():
last_positions[unit] = position

last_positions = {node: (pos[0] * 10, pos[1] * 10) for node, pos in last_positions.items()}
# Define the edges between the nodes
edges = {}
for node1, pos1 in last_positions.items():
edges[node1] = []
for node2, pos2 in last_positions.items():
if node1 != node2:
distance = ((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2) ** 0.5
if distance > min_distance:
edges[node1].append((node2, distance))


shortest_path = dijkstra(edges, 'unit_1', 'unit_4')
fig, ax = plt.subplots(figsize=(8, 8))
for node, pos in last_positions.items():
circle = plt.Circle(pos, 15, color=(1, 1, 1), fill=True)
ax.add_artist(circle)
plt.text(pos[0], pos[1], node, color=(0, 0, 0), ha='center', va='center', fontsize=10)
for neighbor, weight in edges[node]:
plt.plot([pos[0], last_positions[neighbor][0]], [pos[1], last_positions[neighbor][1]], color=(0, 0, 1), linewidth=2)

source_node = 'unit_1'
destination_node = 'unit_4'

shortest_path = dijkstra(edges, source_node, destination_node)

for i in range(len(shortest_path) - 1):
node_1 = shortest_path[i]
node_2 = shortest_path[i+1]
pos_1 = last_positions[node_1]
pos_2 = last_positions[node_2]
plt.plot([pos_1[0], pos_2[0]], [pos_1[1], pos_2[1]], color=(0, 1, 0), linewidth=3)
plt.plot(pos_1[0], pos_1[1], 'o', color=(0, 1, 0), markersize=10)
plt.plot(pos_2[0], pos_2[1], 'o', color=(0, 1, 0), markersize=10)

plt.title(f'Shortest path from {source_node} to {destination_node}')
plt.axis('equal')
plt.show()

button_path = Button(screen, 100, 100, 30, 30, onClick=on_button_click_calculate_dijkstra)
button_det = TextBox(screen, self.SCREEN_WIDTH - 650, 100, 180, 30,
fontSize=24, borderThickness=0, backgroundColor=(0, 0, 0, 0))
button_det.setText("Dijkstra's Path")
button_det.disable()

# Define the Dijkstra's shortest path algorithm
def dijkstra(graph, start, end):
dist = {node: math.inf for node in graph}
dist[start] = 0
heap = [(0, start)]
path = {}

while heap:
(current_dist, current_node) = heapq.heappop(heap)

if current_node == end:
break

for (neighbor, weight) in graph[current_node]:
distance = current_dist + weight

if distance < dist[neighbor]:
dist[neighbor] = distance
heapq.heappush(heap, (distance, neighbor))
if(distance > min_distance):
path[neighbor] = current_node

shortest_path = [end]
while end != start:
shortest_path.append(path.get(end))
end = path.get(end)
shortest_path.reverse()

return shortest_path

instructions_text_1 = TextBox(screen, self.SCREEN_WIDTH - 180, 80, 180, 30,
fontSize=24, borderThickness=0, textColour=(50, 50, 50))
instructions_text_1.setText("i - Toggle Instructions")
Expand All @@ -125,7 +276,6 @@ def display(self) -> None:
instructions_text_3.setText("space - toggle pause")
instructions_text_3.disable()


unit_sprites = {}
units_data = self.units_controller.get_units_data()
for unit_key in units_data:
Expand Down Expand Up @@ -189,4 +339,5 @@ def display(self) -> None:
instructions_text_2.show()
instructions_text_3.show()
pygame_widgets.update(events)
mouse_pos = pygame.mouse.get_pos()
pygame.display.flip()
2 changes: 1 addition & 1 deletion mobscript/input_data_defaults/global_attributes.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"time_duration": 20,
"time_step": 0.05,
"standard_radio_radius": 3,
"standard_radio_radius": 150,
"length_to_pixels": 5
}
Loading