-
Notifications
You must be signed in to change notification settings - Fork 0
/
wsignal.py
152 lines (121 loc) · 4.68 KB
/
wsignal.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""
Some helper function for dealing with wireless signal transmission.
"""
from math import inf, log, sqrt
def subtract_dbm(dbm1: float, dbm2: float):
"""Adds two decibel values"""
watt1 = dbm_to_watt(dbm1)
watt2 = dbm_to_watt(dbm2)
return watt_to_dbm(watt1 - watt2)
def dbm_to_watt(dbm: float):
"""
Bell is a logarithmic scale to express the ratio of two
quantities. It is defined as the logarithm of the ratio to base
10.
Q = lg P_1/P_2 Bell = 10 * lg P_1/P_2 deciBell
So if we have a power of 50 dBm (= decibel with respect to 1 mW),
that means
50 dBm = 10 * lg P/1mW
=> 5 dBm = lg P - lg 1mW
=> 5 dBm + lg 1mW = lg P
=> 10^(5 dBm + lg 1mW) = P
=> 10^(5 dBm) * 10^(lg 1mW) = P
=> 10^(5 dBm) * 1mW = P
=> P = 10^5 mW = 10^5 * 10^(-3) W
"""
# 0 cannot be represented as a ratio to 1mW
if dbm == -inf:
return 0
bell = dbm / 10.0
milliwatts = 10.0 ** bell
watts = milliwatts / 1000.0
return watts
def watt_to_dbm(watts: float):
"""
Converts an amount in watt to a ratio to 1mW, represented in dBm.
"""
# 0 cannot be represented as a ratio to 1mW
if watts <= 0:
return -inf
milliwatts = watts * 1000.0
bell = log(milliwatts, 10.0)
decibel = bell * 10.0
return decibel
def log_path_loss(
distance_meters: float, loss_exponent: int = 2, system_loss: float = 0
):
"""
Returns the approximated path loss (in dB) over a certain distance
using the simple log-distance model. This approximation is dependent
on the "loss exponent", which depends on the environment and usually
is a value between 2 (indicating free space) and 4 (indicating a
noisy environment like a building). Wikipedia has a more detailed
description:
https://en.wikipedia.org/wiki/Path_loss#Loss_exponent
Comparison to a linear model:
s = signal_dbm
signal_watt = 10**(s/10 + 3)
d = distance_meter
e = loss exponent
loss_dbm = e * 10 * log d
received_dbm = s - loss_dbm = s - 10e * log d
received_watt = 10**( (s - 10e * log d) / 10 + 3)
= 10**( s/10 - (10e * log d)/10 + 3)
= 10**( s/10 + 3 - e * log d )
= 10**( s/10 + 3 ) / 10 ** ( e * log d )
= signal_watt / (10 ** log d) ** e
= signal_watt / d**e
=: signal_watt * loss_linear
=> loss_linear = 1/d**e
"""
# decibels relative to 1m
if distance_meters != 0:
distance_decibel = 10 * log(distance_meters, 10)
else:
# it is mathematically convenient here to define log(0) = -inf
distance_decibel = -inf
# loss_exponent = loss increase (decibels) per increase in order of
# magnitude. E.g. loss_exponent = 3dB means that the signal loses half
# of its strength for each order of magnitude increase in distance.
loss = loss_exponent * distance_decibel + system_loss
# May actually be negative when the distance is <1m. This could be
# explained by constructive interference, or could be taken as a
# failure of the path loss model. Either way, since only the
# resulting signal really matters for our purposes (the path loss
# function could easily be adjusted without changes to the model)
# and this is what the marvelo instance generation script uses, we
# accept that the loss may be negative.
return loss
def distance(x1: float, y1: float, x2: float, y2: float):
"""
Calculates the euclidean distance between two points in 2d space.
"""
return sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
def power_received(distance_meter: float, transmit_power_dbm: float):
"""
Calculates the power received at target when source sends at
full transmission power (based on log path loss model).
"""
path_loss = log_path_loss(distance_meter)
# path loss is in dB, so this subtraction is "actually" a division
return transmit_power_dbm - path_loss
def sinr(
received_signal_dbm: float,
received_interference_dbm: float,
noise_floor_dbm: float,
):
"""
Calculate the Signal Interference Noise Ratio in decibels.
That means that
$10^{sinr/10} . (noise+interference) = signal$
Should always hold.
"""
# We need to convert to watts for addition (log scale can only multiply)
received_noise_watt = dbm_to_watt(received_interference_dbm) + dbm_to_watt(
noise_floor_dbm
)
received_noise_dbm = watt_to_dbm(received_noise_watt)
# Even though it is called Signal Interference Noise *Ratio*, here we have
# to subtract (not divide) since we are calculating in dBm, which is a
# logarithmic scale.
return received_signal_dbm - received_noise_dbm