vlcSim 0.4.0__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.
- vlcsim/__init__.py +25 -0
- vlcsim/controller.py +743 -0
- vlcsim/scene.py +1793 -0
- vlcsim/simulator.py +797 -0
- vlcsim-0.4.0.dist-info/METADATA +164 -0
- vlcsim-0.4.0.dist-info/RECORD +8 -0
- vlcsim-0.4.0.dist-info/WHEEL +4 -0
- vlcsim-0.4.0.dist-info/licenses/LICENSE.md +17 -0
vlcsim/scene.py
ADDED
|
@@ -0,0 +1,1793 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module manages the physical substrate for Visible Light Communication (VLC) simulations.
|
|
3
|
+
It provides classes for Access Points (VLEDs and RF), Receivers, and the Scenario configuration.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import math as m
|
|
8
|
+
from enum import Enum
|
|
9
|
+
import warnings
|
|
10
|
+
from typing import List, Optional, Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AccessPoint:
|
|
14
|
+
"""
|
|
15
|
+
Represents a generic access point in the VLC scenario.
|
|
16
|
+
This class manages the physical substrate, including position, state, and time slicing.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
x (float): x coordinate of the access point.
|
|
20
|
+
y (float): y coordinate of the access point.
|
|
21
|
+
z (float): z coordinate of the access point.
|
|
22
|
+
state (Enum): Current state (IDLE or BUSY).
|
|
23
|
+
sliceTime (float): Time of the slice in seconds.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
stateap = Enum("state", "IDLE BUSY")
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
x: float,
|
|
31
|
+
y: float,
|
|
32
|
+
z: float,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""
|
|
35
|
+
VLed constructor
|
|
36
|
+
|
|
37
|
+
:param x: **x** coordinate of the position of this VLed.
|
|
38
|
+
:type x: float
|
|
39
|
+
:param y: **y** coordinate of the position of this VLed.
|
|
40
|
+
:type y: float
|
|
41
|
+
:param z: **z** coordinate of the position of this VLed.
|
|
42
|
+
:type z: float
|
|
43
|
+
:param nLedsX: Number of rows in the led.
|
|
44
|
+
:type nLedsX: int
|
|
45
|
+
:param nLedsY: Number of cols in the led.
|
|
46
|
+
:type nLedsY: int
|
|
47
|
+
:param ledPower: Power of the led in [mW]
|
|
48
|
+
:type ledPower: float
|
|
49
|
+
:param theta: semi-angle median ilumination (degrees)
|
|
50
|
+
:type theta: float
|
|
51
|
+
"""
|
|
52
|
+
self._x: float = x
|
|
53
|
+
self._y: float = y
|
|
54
|
+
self._z: float = z
|
|
55
|
+
self._state = AccessPoint.stateap.IDLE
|
|
56
|
+
self._sliceTime: float = 0.1
|
|
57
|
+
self._slicesInFrame: int = 10
|
|
58
|
+
|
|
59
|
+
self._ID: Optional[int] = None
|
|
60
|
+
self._position: np.ndarray[Any, np.dtype[np.float64]] = np.array([x, y, z])
|
|
61
|
+
self._B: float = 1e6
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def B(self) -> float:
|
|
65
|
+
"""
|
|
66
|
+
Gets the Bandwith of this AccessPoint
|
|
67
|
+
|
|
68
|
+
:return: The B of this AccessPoint
|
|
69
|
+
:rtype: float
|
|
70
|
+
"""
|
|
71
|
+
return self._B
|
|
72
|
+
|
|
73
|
+
@B.setter
|
|
74
|
+
def B(self, value: float) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Sets the bandwidth of this AccessPoint.
|
|
77
|
+
|
|
78
|
+
:param value: The bandwidth value to set
|
|
79
|
+
:type value: float
|
|
80
|
+
"""
|
|
81
|
+
self._B = value
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def ID(self) -> Optional[int]:
|
|
85
|
+
"""
|
|
86
|
+
Identification of VLed
|
|
87
|
+
|
|
88
|
+
:return: VLed ID.
|
|
89
|
+
:rtype: int
|
|
90
|
+
"""
|
|
91
|
+
return self._ID
|
|
92
|
+
|
|
93
|
+
@ID.setter
|
|
94
|
+
def ID(self, value: int) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Sets the ID of this AccessPoint.
|
|
97
|
+
|
|
98
|
+
:param value: The ID value to set
|
|
99
|
+
:type value: int
|
|
100
|
+
"""
|
|
101
|
+
self._ID = value
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def x(self) -> float:
|
|
105
|
+
"""
|
|
106
|
+
x Coordinate of this VLed
|
|
107
|
+
|
|
108
|
+
:return: x coordinate
|
|
109
|
+
:rtype: float
|
|
110
|
+
"""
|
|
111
|
+
return self._x
|
|
112
|
+
|
|
113
|
+
@x.setter
|
|
114
|
+
def x(self, value: float) -> None:
|
|
115
|
+
"""
|
|
116
|
+
Sets the x coordinate of this AccessPoint.
|
|
117
|
+
|
|
118
|
+
:param value: The x coordinate value to set
|
|
119
|
+
:type value: float
|
|
120
|
+
"""
|
|
121
|
+
self._x = value
|
|
122
|
+
self._position = np.array([self._x, self.y, self.z])
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def y(self) -> float:
|
|
126
|
+
"""
|
|
127
|
+
y Coordinate of this VLed
|
|
128
|
+
|
|
129
|
+
:return: y coordinate.
|
|
130
|
+
:rtype: float
|
|
131
|
+
"""
|
|
132
|
+
return self._y
|
|
133
|
+
|
|
134
|
+
@y.setter
|
|
135
|
+
def y(self, value: float) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Sets the y coordinate of this AccessPoint.
|
|
138
|
+
|
|
139
|
+
:param value: The y coordinate value to set
|
|
140
|
+
:type value: float
|
|
141
|
+
"""
|
|
142
|
+
self._y = value
|
|
143
|
+
self._position = np.array([self.x, self._y, self.z])
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def z(self) -> float:
|
|
147
|
+
"""
|
|
148
|
+
z Coordinate of this VLed
|
|
149
|
+
|
|
150
|
+
:return: z coordinate
|
|
151
|
+
:rtype: float
|
|
152
|
+
"""
|
|
153
|
+
return self._z
|
|
154
|
+
|
|
155
|
+
@z.setter
|
|
156
|
+
def z(self, value: float) -> None:
|
|
157
|
+
"""
|
|
158
|
+
Sets the z coordinate of this AccessPoint.
|
|
159
|
+
|
|
160
|
+
:param value: The z coordinate value to set
|
|
161
|
+
:type value: float
|
|
162
|
+
"""
|
|
163
|
+
self._z = value
|
|
164
|
+
self._position = np.array([self.x, self.y, self._z])
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def position(self) -> np.ndarray[Any, np.dtype[np.float64]]:
|
|
168
|
+
"""
|
|
169
|
+
The coordinates of the position of this VLed
|
|
170
|
+
|
|
171
|
+
:return: Coordinates of the position
|
|
172
|
+
:rtype: np.ndarray[Any, np.dtype[np.float64]]
|
|
173
|
+
"""
|
|
174
|
+
return self._position
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def state(self) -> stateap:
|
|
178
|
+
"""
|
|
179
|
+
VLed state
|
|
180
|
+
|
|
181
|
+
:return: This access point state. Could be IDLE or BUSY.
|
|
182
|
+
:rtype: :class:`vlcsim.scene.AccessPoint.stateap`
|
|
183
|
+
"""
|
|
184
|
+
return self._state
|
|
185
|
+
|
|
186
|
+
def setIDLE(self) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Set the state of this access point to IDLE
|
|
189
|
+
"""
|
|
190
|
+
self._state = AccessPoint.stateap.IDLE
|
|
191
|
+
|
|
192
|
+
def setBUSY(self) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Set the state of this VLed to BUSY
|
|
195
|
+
"""
|
|
196
|
+
self._state = AccessPoint.stateap.BUSY
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def sliceTime(self) -> float:
|
|
200
|
+
"""
|
|
201
|
+
The slice time correspond to the time that a connection will be using this VLed.
|
|
202
|
+
|
|
203
|
+
:return: Slice time
|
|
204
|
+
:rtype: float
|
|
205
|
+
"""
|
|
206
|
+
return self._sliceTime
|
|
207
|
+
|
|
208
|
+
@sliceTime.setter
|
|
209
|
+
def sliceTime(self, value: float) -> None:
|
|
210
|
+
"""
|
|
211
|
+
Sets the slice time for this AccessPoint.
|
|
212
|
+
|
|
213
|
+
:param value: The slice time value to set
|
|
214
|
+
:type value: float
|
|
215
|
+
"""
|
|
216
|
+
self._sliceTime = value
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def slicesInFrame(self) -> int:
|
|
220
|
+
"""
|
|
221
|
+
The number of slices in the frame. This is used to determine how many slices will be in the frame.
|
|
222
|
+
|
|
223
|
+
:return: The number of slices in the frame
|
|
224
|
+
:rtype: int
|
|
225
|
+
"""
|
|
226
|
+
return self._slicesInFrame
|
|
227
|
+
|
|
228
|
+
@slicesInFrame.setter
|
|
229
|
+
def slicesInFrame(self, value: int) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Sets the number of slices in the frame for this AccessPoint.
|
|
232
|
+
|
|
233
|
+
:param value: The number of slices to set
|
|
234
|
+
:type value: int
|
|
235
|
+
"""
|
|
236
|
+
self._slicesInFrame = value
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class VLed(AccessPoint):
|
|
240
|
+
"""
|
|
241
|
+
Represents a Visual Light LED (VLED) access point in the scenario.
|
|
242
|
+
Inherits from AccessPoint and adds LED-specific parameters.
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
numberOfVLeds = 0
|
|
246
|
+
"""
|
|
247
|
+
number of VLeds created
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
def __init__(
|
|
251
|
+
self,
|
|
252
|
+
x: float,
|
|
253
|
+
y: float,
|
|
254
|
+
z: float,
|
|
255
|
+
nLedsX: int,
|
|
256
|
+
nLedsY: int,
|
|
257
|
+
ledPower: float,
|
|
258
|
+
theta: float,
|
|
259
|
+
) -> None:
|
|
260
|
+
"""
|
|
261
|
+
VLed constructor
|
|
262
|
+
|
|
263
|
+
:param x: **x** coordinate of the position of this VLed.
|
|
264
|
+
:type x: float
|
|
265
|
+
:param y: **y** coordinate of the position of this VLed.
|
|
266
|
+
:type y: float
|
|
267
|
+
:param z: **z** coordinate of the position of this VLed.
|
|
268
|
+
:type z: float
|
|
269
|
+
:param nLedsX: Number of rows in the led.
|
|
270
|
+
:type nLedsX: int
|
|
271
|
+
:param nLedsY: Number of cols in the led.
|
|
272
|
+
:type nLedsY: int
|
|
273
|
+
:param ledPower: Power of the led in [mW]
|
|
274
|
+
:type ledPower: float
|
|
275
|
+
:param theta: semi-angle median ilumination (degrees)
|
|
276
|
+
:type theta: float
|
|
277
|
+
"""
|
|
278
|
+
AccessPoint.__init__(self, x, y, z)
|
|
279
|
+
self.__nLedsX = nLedsX
|
|
280
|
+
self.__nLedsY = nLedsY
|
|
281
|
+
self.__ledPower = ledPower
|
|
282
|
+
self.__theta = theta
|
|
283
|
+
self._state = AccessPoint.stateap.IDLE
|
|
284
|
+
|
|
285
|
+
self.__numberOfLeds = self.__nLedsX * self.__nLedsY
|
|
286
|
+
self.__totalPower = self.__numberOfLeds * self.__ledPower
|
|
287
|
+
self.__ml = -m.log10(2) / m.log10(m.cos(m.radians(theta)))
|
|
288
|
+
|
|
289
|
+
self._ID = VLed.numberOfVLeds
|
|
290
|
+
VLed.numberOfVLeds += 1
|
|
291
|
+
self._position = np.array([x, y, z])
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def nLedsX(self) -> int:
|
|
295
|
+
"""
|
|
296
|
+
Number of leds in each row.
|
|
297
|
+
|
|
298
|
+
:return: leds on each row
|
|
299
|
+
:rtype: int
|
|
300
|
+
"""
|
|
301
|
+
return self.__nLedsX
|
|
302
|
+
|
|
303
|
+
@nLedsX.setter
|
|
304
|
+
def nLedsX(self, value: int) -> None:
|
|
305
|
+
"""
|
|
306
|
+
Sets the number of LEDs in each row.
|
|
307
|
+
|
|
308
|
+
:param value: Number of LEDs in each row
|
|
309
|
+
:type value: int
|
|
310
|
+
"""
|
|
311
|
+
self.__nLedsX = value
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def nLedsY(self) -> int:
|
|
315
|
+
"""
|
|
316
|
+
Number of leds in each col.
|
|
317
|
+
|
|
318
|
+
:return: leds on each col
|
|
319
|
+
:rtype: int
|
|
320
|
+
"""
|
|
321
|
+
return self.__nLedsY
|
|
322
|
+
|
|
323
|
+
@nLedsY.setter
|
|
324
|
+
def nLedsY(self, value: int) -> None:
|
|
325
|
+
"""
|
|
326
|
+
Sets the number of LEDs in each column.
|
|
327
|
+
|
|
328
|
+
:param value: Number of LEDs in each column
|
|
329
|
+
:type value: int
|
|
330
|
+
"""
|
|
331
|
+
self.__nLedsY = value
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def ledPower(self) -> float:
|
|
335
|
+
"""
|
|
336
|
+
Power of each lead in [mW]
|
|
337
|
+
|
|
338
|
+
:return: power in [mW]
|
|
339
|
+
:rtype: float
|
|
340
|
+
"""
|
|
341
|
+
return self.__ledPower
|
|
342
|
+
|
|
343
|
+
@ledPower.setter
|
|
344
|
+
def ledPower(self, value: float) -> None:
|
|
345
|
+
"""
|
|
346
|
+
Sets the power of each LED in [mW].
|
|
347
|
+
|
|
348
|
+
:param value: Power of each LED
|
|
349
|
+
:type value: float
|
|
350
|
+
"""
|
|
351
|
+
self.__ledPower = value
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
def theta(self) -> float:
|
|
355
|
+
"""
|
|
356
|
+
semi-angle median ilumination (degrees)
|
|
357
|
+
|
|
358
|
+
:return: angle in degrees
|
|
359
|
+
:rtype: float
|
|
360
|
+
"""
|
|
361
|
+
return self.__theta
|
|
362
|
+
|
|
363
|
+
@theta.setter
|
|
364
|
+
def theta(self, value: float) -> None:
|
|
365
|
+
"""
|
|
366
|
+
Sets the semi-angle median illumination (degrees).
|
|
367
|
+
|
|
368
|
+
:param value: Semi-angle median illumination
|
|
369
|
+
:type value: float
|
|
370
|
+
"""
|
|
371
|
+
self.__theta = value
|
|
372
|
+
|
|
373
|
+
@property
|
|
374
|
+
def numberOfLeds(self) -> int:
|
|
375
|
+
"""
|
|
376
|
+
Number of leds in this VLed
|
|
377
|
+
|
|
378
|
+
:return: number of leds
|
|
379
|
+
:rtype: int
|
|
380
|
+
"""
|
|
381
|
+
return self.__numberOfLeds
|
|
382
|
+
|
|
383
|
+
@property
|
|
384
|
+
def totalPower(self) -> float:
|
|
385
|
+
"""
|
|
386
|
+
The Power of this VLed in [mW]. Corresponds to the multiplication of the number of cols in leds with the number of rows and the power of each led.
|
|
387
|
+
|
|
388
|
+
:return: total power in [mW]
|
|
389
|
+
:rtype: float
|
|
390
|
+
"""
|
|
391
|
+
return self.__totalPower
|
|
392
|
+
|
|
393
|
+
@totalPower.setter
|
|
394
|
+
def totalPower(self, value: float):
|
|
395
|
+
"""
|
|
396
|
+
Sets the total power of this VLed in [mW].
|
|
397
|
+
|
|
398
|
+
:param value: Total power value
|
|
399
|
+
:type value: float
|
|
400
|
+
"""
|
|
401
|
+
self.__totalPower = value
|
|
402
|
+
|
|
403
|
+
@property
|
|
404
|
+
def ml(self) -> float:
|
|
405
|
+
"""
|
|
406
|
+
Lambertian emission order is given by:
|
|
407
|
+
|
|
408
|
+
.. math::
|
|
409
|
+
\\begin{eqnarray}
|
|
410
|
+
ml = -\\frac{\\log_{10} 2}{\\log_{10} (\\cos(\\theta))}
|
|
411
|
+
\\end{eqnarray}
|
|
412
|
+
|
|
413
|
+
:return: Lambertian emission order
|
|
414
|
+
:rtype: float
|
|
415
|
+
"""
|
|
416
|
+
return self.__ml
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class RF(AccessPoint):
|
|
420
|
+
"""
|
|
421
|
+
Represents a Radio Frequency (RF) access point in the scenario.
|
|
422
|
+
Inherits from AccessPoint and adds RF-specific parameters.
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
numberOfRFs = 0
|
|
426
|
+
"""
|
|
427
|
+
Number of RFs created
|
|
428
|
+
"""
|
|
429
|
+
|
|
430
|
+
def __init__(
|
|
431
|
+
self,
|
|
432
|
+
x: float,
|
|
433
|
+
y: float,
|
|
434
|
+
z: float,
|
|
435
|
+
bf: float = 5e6,
|
|
436
|
+
pf: float = 40,
|
|
437
|
+
BERf: float = 10e-5,
|
|
438
|
+
Af: float = 10.0,
|
|
439
|
+
Ef: float = 1.0,
|
|
440
|
+
R_awgn: float = 1 * 10 ** (-174 / 10),
|
|
441
|
+
N_rf: int = 10,
|
|
442
|
+
nFactor_rf: float = 1.0,
|
|
443
|
+
A: float = 0.1e6,
|
|
444
|
+
) -> None:
|
|
445
|
+
AccessPoint.__init__(self, x, y, z)
|
|
446
|
+
self.__bf = bf
|
|
447
|
+
self.__pf = pf
|
|
448
|
+
self.__BERf = BERf
|
|
449
|
+
self.__Af = Af
|
|
450
|
+
self.__Ef = Ef
|
|
451
|
+
self.__R_awgn = R_awgn
|
|
452
|
+
self.__N_rf = N_rf
|
|
453
|
+
self.__nFactor_rf = nFactor_rf
|
|
454
|
+
self.__A = A
|
|
455
|
+
self._state = AccessPoint.stateap.IDLE
|
|
456
|
+
|
|
457
|
+
self.__pif = -1.5 / m.log(5 * self.__BERf)
|
|
458
|
+
self._ID = RF.numberOfRFs
|
|
459
|
+
RF.numberOfRFs += 1
|
|
460
|
+
|
|
461
|
+
@property
|
|
462
|
+
def bf(self) -> float:
|
|
463
|
+
"""
|
|
464
|
+
femtocell bandwidth
|
|
465
|
+
|
|
466
|
+
:return: Value of BF property
|
|
467
|
+
:rtype: float
|
|
468
|
+
"""
|
|
469
|
+
return self.__bf
|
|
470
|
+
|
|
471
|
+
@bf.setter
|
|
472
|
+
def bf(self, value: float) -> None:
|
|
473
|
+
"""
|
|
474
|
+
Sets the femtocell bandwidth.
|
|
475
|
+
|
|
476
|
+
:param value: Bandwidth value
|
|
477
|
+
:type value: float
|
|
478
|
+
"""
|
|
479
|
+
self.__bf = value
|
|
480
|
+
|
|
481
|
+
@property
|
|
482
|
+
def pf(self) -> float:
|
|
483
|
+
"""
|
|
484
|
+
Femtobase power transmission
|
|
485
|
+
|
|
486
|
+
:return: Femtobase power transmission
|
|
487
|
+
:rtype: float
|
|
488
|
+
|
|
489
|
+
"""
|
|
490
|
+
return self.__pf
|
|
491
|
+
|
|
492
|
+
@pf.setter
|
|
493
|
+
def pf(self, value: float) -> None:
|
|
494
|
+
"""
|
|
495
|
+
Sets the femtobase power transmission.
|
|
496
|
+
|
|
497
|
+
:param value: Power transmission value
|
|
498
|
+
:type value: float
|
|
499
|
+
"""
|
|
500
|
+
self.__pf = value
|
|
501
|
+
|
|
502
|
+
@property
|
|
503
|
+
def BERf(self) -> float:
|
|
504
|
+
"""
|
|
505
|
+
BER desired in femto network
|
|
506
|
+
|
|
507
|
+
:return: BER
|
|
508
|
+
:rtype: float
|
|
509
|
+
"""
|
|
510
|
+
return self.__BERf
|
|
511
|
+
|
|
512
|
+
@BERf.setter
|
|
513
|
+
def BERf(self, value: float) -> None:
|
|
514
|
+
"""
|
|
515
|
+
Sets the BER desired in femto network.
|
|
516
|
+
|
|
517
|
+
:param value: BER value
|
|
518
|
+
:type value: float
|
|
519
|
+
"""
|
|
520
|
+
self.__BERf = value
|
|
521
|
+
|
|
522
|
+
@property
|
|
523
|
+
def Af(self) -> float:
|
|
524
|
+
"""
|
|
525
|
+
Pathloss constant
|
|
526
|
+
|
|
527
|
+
:return: Pathloss constant
|
|
528
|
+
:rtype: float
|
|
529
|
+
"""
|
|
530
|
+
return self.__Af
|
|
531
|
+
|
|
532
|
+
@Af.setter
|
|
533
|
+
def Af(self, value: float) -> None:
|
|
534
|
+
"""
|
|
535
|
+
Sets the pathloss constant.
|
|
536
|
+
|
|
537
|
+
:param value: Pathloss constant value
|
|
538
|
+
:type value: float
|
|
539
|
+
"""
|
|
540
|
+
self.__Af = value
|
|
541
|
+
|
|
542
|
+
@property
|
|
543
|
+
def Ef(self) -> float:
|
|
544
|
+
"""
|
|
545
|
+
Pathloss exponent
|
|
546
|
+
|
|
547
|
+
:return: Pathloss exponent
|
|
548
|
+
:rtype: float
|
|
549
|
+
"""
|
|
550
|
+
return self.__Ef
|
|
551
|
+
|
|
552
|
+
@Ef.setter
|
|
553
|
+
def Ef(self, value: float) -> None:
|
|
554
|
+
"""
|
|
555
|
+
Sets the pathloss exponent.
|
|
556
|
+
|
|
557
|
+
:param value: Pathloss exponent value
|
|
558
|
+
:type value: float
|
|
559
|
+
"""
|
|
560
|
+
self.__Ef = value
|
|
561
|
+
|
|
562
|
+
@property
|
|
563
|
+
def R_awgn(self) -> float:
|
|
564
|
+
"""
|
|
565
|
+
Normalized AWGN noise power
|
|
566
|
+
|
|
567
|
+
:return: Normalized AWGN noise power
|
|
568
|
+
:rtype: float
|
|
569
|
+
"""
|
|
570
|
+
return self.__R_awgn
|
|
571
|
+
|
|
572
|
+
@R_awgn.setter
|
|
573
|
+
def R_awgn(self, value: float) -> None:
|
|
574
|
+
"""
|
|
575
|
+
Sets the normalized AWGN noise power.
|
|
576
|
+
|
|
577
|
+
:param value: AWGN noise power value
|
|
578
|
+
:type value: float
|
|
579
|
+
"""
|
|
580
|
+
self.__R_awgn = value
|
|
581
|
+
|
|
582
|
+
@property
|
|
583
|
+
def pif(self) -> float:
|
|
584
|
+
"""
|
|
585
|
+
The received SNR discrepancy from the continuous input memoryless channel’s capacity at the target BER. (pif=-1.5log2-1(5 BERf))
|
|
586
|
+
|
|
587
|
+
.. math::
|
|
588
|
+
\\begin{eqnarray}
|
|
589
|
+
pif = -1.5 \\log_2^{-1}(5\\cdot BERf)
|
|
590
|
+
\\end{eqnarray}
|
|
591
|
+
|
|
592
|
+
:return: SNR discrepancy
|
|
593
|
+
:rtype: float
|
|
594
|
+
"""
|
|
595
|
+
return self.__pif
|
|
596
|
+
|
|
597
|
+
@pif.setter
|
|
598
|
+
def pif(self, value: float) -> None:
|
|
599
|
+
"""
|
|
600
|
+
Sets the SNR discrepancy value.
|
|
601
|
+
|
|
602
|
+
:param value: SNR discrepancy value
|
|
603
|
+
:type value: float
|
|
604
|
+
"""
|
|
605
|
+
self.__pif = value
|
|
606
|
+
|
|
607
|
+
@property
|
|
608
|
+
def N_rf(self) -> int:
|
|
609
|
+
"""
|
|
610
|
+
Number of cells using this femtocell
|
|
611
|
+
|
|
612
|
+
:return: Number of cells using this femtocell
|
|
613
|
+
:rtype: int
|
|
614
|
+
"""
|
|
615
|
+
return self.__N_rf
|
|
616
|
+
|
|
617
|
+
@N_rf.setter
|
|
618
|
+
def N_rf(self, value: int) -> None:
|
|
619
|
+
"""
|
|
620
|
+
Sets the number of cells using this femtocell.
|
|
621
|
+
|
|
622
|
+
:param value: Number of cells
|
|
623
|
+
:type value: int
|
|
624
|
+
"""
|
|
625
|
+
self.__N_rf = value
|
|
626
|
+
|
|
627
|
+
@property
|
|
628
|
+
def nFactor_rf(self) -> float:
|
|
629
|
+
"""
|
|
630
|
+
RF constant
|
|
631
|
+
|
|
632
|
+
:return: RF constant
|
|
633
|
+
:rtype: float
|
|
634
|
+
"""
|
|
635
|
+
return self.__nFactor_rf
|
|
636
|
+
|
|
637
|
+
@nFactor_rf.setter
|
|
638
|
+
def nFactor_rf(self, value: float) -> None:
|
|
639
|
+
"""
|
|
640
|
+
Sets the RF constant.
|
|
641
|
+
|
|
642
|
+
:param value: RF constant value
|
|
643
|
+
:type value: float
|
|
644
|
+
"""
|
|
645
|
+
self.__nFactor_rf = value
|
|
646
|
+
|
|
647
|
+
@property
|
|
648
|
+
def A(self) -> float:
|
|
649
|
+
"""
|
|
650
|
+
Rayleigh distributed channel gain.
|
|
651
|
+
|
|
652
|
+
:return: Rayleigh distributed channel gain.
|
|
653
|
+
:rtype: float
|
|
654
|
+
"""
|
|
655
|
+
return self.__A
|
|
656
|
+
|
|
657
|
+
@A.setter
|
|
658
|
+
def A(self, value: float) -> None:
|
|
659
|
+
"""
|
|
660
|
+
Sets the Rayleigh distributed channel gain.
|
|
661
|
+
|
|
662
|
+
:param value: Channel gain value
|
|
663
|
+
:type value: float
|
|
664
|
+
"""
|
|
665
|
+
self.__A = value
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
class Receiver:
|
|
669
|
+
"""
|
|
670
|
+
Represents a receiver device in the VLC scenario.
|
|
671
|
+
Contains all physical and electrical parameters required for simulation.
|
|
672
|
+
"""
|
|
673
|
+
|
|
674
|
+
receiversCreated = 0
|
|
675
|
+
"""
|
|
676
|
+
Number of receivers created by this class.
|
|
677
|
+
"""
|
|
678
|
+
|
|
679
|
+
def __init__(
|
|
680
|
+
self,
|
|
681
|
+
x: float,
|
|
682
|
+
y: float,
|
|
683
|
+
z: float,
|
|
684
|
+
aDet: float,
|
|
685
|
+
ts: float,
|
|
686
|
+
index: float,
|
|
687
|
+
fov: float,
|
|
688
|
+
q: float = 1.6e-19,
|
|
689
|
+
s: float = 0.54,
|
|
690
|
+
b: float = 10e6,
|
|
691
|
+
ibg: float = 5.1e-3,
|
|
692
|
+
cb: float = 1.380649e-23,
|
|
693
|
+
tk: float = 298.0,
|
|
694
|
+
a: float = 1.0,
|
|
695
|
+
gv: float = 10.0,
|
|
696
|
+
n: float = 1.12e-6,
|
|
697
|
+
fr: float = 1.5,
|
|
698
|
+
gm: float = 3e-2,
|
|
699
|
+
i1: float = 0.562,
|
|
700
|
+
i2: float = 0.0868,
|
|
701
|
+
) -> None:
|
|
702
|
+
"""
|
|
703
|
+
Initializes the object with the values. This is the constructor for the Schrodinger model
|
|
704
|
+
|
|
705
|
+
:param x: x coordinate of the object
|
|
706
|
+
:type x: float
|
|
707
|
+
:param y: y coordinate of the object's coordinate.
|
|
708
|
+
:type y: float
|
|
709
|
+
:param z: z coordinate of the object's coordinate.
|
|
710
|
+
:type z: float
|
|
711
|
+
:param aDet: aDet value of the object's coordinate.
|
|
712
|
+
:type aDet: float
|
|
713
|
+
:param ts: time of the object's coordinate. Default is 10 seconds.
|
|
714
|
+
:type ts: float
|
|
715
|
+
:param index: order of Lambertian emission that depends on the half-angle at half illumination of the LED.
|
|
716
|
+
:type index: float
|
|
717
|
+
:param fov: width of the field of view.
|
|
718
|
+
:type fov: float
|
|
719
|
+
:param q: electronic load. Default is -1.602e-19 culombios
|
|
720
|
+
:type q: float
|
|
721
|
+
:param s: ibg b - parameter of the Ib - Galactic model. Default is 5 seconds.
|
|
722
|
+
:type s: float
|
|
723
|
+
:param b: c b - parameter of the Ib - Galactic model. Default is 5 seconds.
|
|
724
|
+
:type b: float
|
|
725
|
+
:param ibg: background current. Default is 5.1e-3 Amperes.
|
|
726
|
+
:type ibg: float
|
|
727
|
+
:param cb: Boltzmann constant. Default is 1.380649e-23 J/K.
|
|
728
|
+
:type cb: float
|
|
729
|
+
:param tk: 380649
|
|
730
|
+
:type tk: float
|
|
731
|
+
:param a: n n - prameter of the B - parameter. Default is 10 seconds.
|
|
732
|
+
:type a: float
|
|
733
|
+
:param gv: Open-loop voltage gain. Default is 10
|
|
734
|
+
:type gv: float
|
|
735
|
+
:param n: Fixed capacitance of the PD per unit area. Default is 1.12e-6.
|
|
736
|
+
:type n: float
|
|
737
|
+
:param fr: FET channel noise factor. Default is 1.5
|
|
738
|
+
:type fr: float
|
|
739
|
+
:param gm: FET transconductance. Default is 3e-2.
|
|
740
|
+
:type gm: float
|
|
741
|
+
:param i1: constant experimental value. Default is 0.562.
|
|
742
|
+
:type i1: float
|
|
743
|
+
:param i2: constant experimental value. Default is 0.0868.
|
|
744
|
+
:type i2: float
|
|
745
|
+
"""
|
|
746
|
+
self.__x = x
|
|
747
|
+
self.__y = y
|
|
748
|
+
self.__z = z
|
|
749
|
+
self.__aDet = aDet
|
|
750
|
+
self.__ts = ts
|
|
751
|
+
self.__index = index
|
|
752
|
+
self.__fov = fov
|
|
753
|
+
self.__q = q
|
|
754
|
+
self.__s = s
|
|
755
|
+
self.__b = b
|
|
756
|
+
self.__ibg = ibg
|
|
757
|
+
self.__cb = cb
|
|
758
|
+
self.__tk = tk
|
|
759
|
+
self.__a = a
|
|
760
|
+
self.__gv = gv
|
|
761
|
+
self.__n = n
|
|
762
|
+
self.__fr = fr
|
|
763
|
+
self.__gm = gm
|
|
764
|
+
self.__i1 = i1
|
|
765
|
+
self.__i2 = i2
|
|
766
|
+
|
|
767
|
+
self.__timeFirstConnected: Optional[float] = None
|
|
768
|
+
self.__goalTime: Optional[float] = None
|
|
769
|
+
self.__timeActive: float = 0.0
|
|
770
|
+
self.__timeFinished: Optional[float] = None
|
|
771
|
+
self.__capacityFromAP: Optional[float] = None
|
|
772
|
+
|
|
773
|
+
self.__gCon = (index**2) / (m.sin(m.radians(fov)) ** 2)
|
|
774
|
+
self.__position = np.array([x, y, z])
|
|
775
|
+
self.__ID = Receiver.receiversCreated
|
|
776
|
+
Receiver.receiversCreated += 1
|
|
777
|
+
|
|
778
|
+
@property
|
|
779
|
+
def capacityFromAP(self) -> Optional[float]:
|
|
780
|
+
"""
|
|
781
|
+
Get the capacity from AP.
|
|
782
|
+
|
|
783
|
+
:return: capacity from AP
|
|
784
|
+
:rtype: float
|
|
785
|
+
"""
|
|
786
|
+
return self.__capacityFromAP
|
|
787
|
+
|
|
788
|
+
@capacityFromAP.setter
|
|
789
|
+
def capacityFromAP(self, value: float) -> None:
|
|
790
|
+
"""
|
|
791
|
+
Sets the capacity from AP.
|
|
792
|
+
|
|
793
|
+
:param value: Capacity value
|
|
794
|
+
:type value: float
|
|
795
|
+
"""
|
|
796
|
+
self.__capacityFromAP = value
|
|
797
|
+
|
|
798
|
+
@property
|
|
799
|
+
def ID(self) -> int:
|
|
800
|
+
"""
|
|
801
|
+
Return the ID of the object. This is used to distinguish between different objects that are part of the same object group.
|
|
802
|
+
|
|
803
|
+
:return: The ID of the object
|
|
804
|
+
:rtype: int
|
|
805
|
+
"""
|
|
806
|
+
return self.__ID
|
|
807
|
+
|
|
808
|
+
@ID.setter
|
|
809
|
+
def ID(self, value: int) -> None:
|
|
810
|
+
"""
|
|
811
|
+
Sets the ID of this Receiver.
|
|
812
|
+
|
|
813
|
+
:param value: The ID value to set
|
|
814
|
+
:type value: int
|
|
815
|
+
"""
|
|
816
|
+
self.__ID = value
|
|
817
|
+
|
|
818
|
+
@property
|
|
819
|
+
def x(self) -> float:
|
|
820
|
+
"""
|
|
821
|
+
Get the x coordinate.
|
|
822
|
+
|
|
823
|
+
:return: The x coordinate
|
|
824
|
+
:rtype: float
|
|
825
|
+
"""
|
|
826
|
+
return self.__x
|
|
827
|
+
|
|
828
|
+
@x.setter
|
|
829
|
+
def x(self, value: float) -> None:
|
|
830
|
+
"""
|
|
831
|
+
Sets the x coordinate of this Receiver.
|
|
832
|
+
|
|
833
|
+
:param value: The x coordinate value to set
|
|
834
|
+
:type value: float
|
|
835
|
+
"""
|
|
836
|
+
self.__x = value
|
|
837
|
+
|
|
838
|
+
@property
|
|
839
|
+
def y(self) -> float:
|
|
840
|
+
"""
|
|
841
|
+
Gets the y coordinate .
|
|
842
|
+
|
|
843
|
+
:return: The y coordinate
|
|
844
|
+
:rtype: float
|
|
845
|
+
"""
|
|
846
|
+
return self.__y
|
|
847
|
+
|
|
848
|
+
@y.setter
|
|
849
|
+
def y(self, value: float) -> None:
|
|
850
|
+
"""
|
|
851
|
+
Sets the y coordinate of this Receiver.
|
|
852
|
+
|
|
853
|
+
:param value: The y coordinate value to set
|
|
854
|
+
:type value: float
|
|
855
|
+
"""
|
|
856
|
+
self.__y = value
|
|
857
|
+
|
|
858
|
+
@property
|
|
859
|
+
def z(self) -> float:
|
|
860
|
+
"""
|
|
861
|
+
Z coordinate .
|
|
862
|
+
|
|
863
|
+
:return: the z coordinate
|
|
864
|
+
:rtype: float
|
|
865
|
+
"""
|
|
866
|
+
return self.__z
|
|
867
|
+
|
|
868
|
+
@z.setter
|
|
869
|
+
def z(self, value: float) -> None:
|
|
870
|
+
"""
|
|
871
|
+
Sets the z coordinate of this Receiver.
|
|
872
|
+
|
|
873
|
+
:param value: The z coordinate value to set
|
|
874
|
+
:type value: float
|
|
875
|
+
"""
|
|
876
|
+
self.__z = value
|
|
877
|
+
|
|
878
|
+
@property
|
|
879
|
+
def aDet(self) -> float:
|
|
880
|
+
"""
|
|
881
|
+
aDet value of the object's coordinate.
|
|
882
|
+
|
|
883
|
+
:return: aDet value of the object's coordinate.
|
|
884
|
+
:rtype: float
|
|
885
|
+
"""
|
|
886
|
+
return self.__aDet
|
|
887
|
+
|
|
888
|
+
@aDet.setter
|
|
889
|
+
def aDet(self, value: float) -> None:
|
|
890
|
+
"""
|
|
891
|
+
Sets the aDet value of this Receiver.
|
|
892
|
+
|
|
893
|
+
:param value: The aDet value to set
|
|
894
|
+
:type value: float
|
|
895
|
+
"""
|
|
896
|
+
self.__aDet = value
|
|
897
|
+
|
|
898
|
+
@property
|
|
899
|
+
def ts(self) -> float:
|
|
900
|
+
"""
|
|
901
|
+
time of the object's coordinate. Default is 10 seconds.
|
|
902
|
+
|
|
903
|
+
:return: time of the object's coordinate.
|
|
904
|
+
:rtype: float
|
|
905
|
+
"""
|
|
906
|
+
return self.__ts
|
|
907
|
+
|
|
908
|
+
@ts.setter
|
|
909
|
+
def ts(self, value: float) -> None:
|
|
910
|
+
"""
|
|
911
|
+
Sets the ts value of this Receiver.
|
|
912
|
+
|
|
913
|
+
:param value: The ts value to set
|
|
914
|
+
:type value: float
|
|
915
|
+
"""
|
|
916
|
+
self.__ts = value
|
|
917
|
+
|
|
918
|
+
@property
|
|
919
|
+
def index(self) -> float:
|
|
920
|
+
"""
|
|
921
|
+
order of Lambertian emission that depends on the half-angle at half illumination of the LED.
|
|
922
|
+
|
|
923
|
+
:return: order of Lambertian emission
|
|
924
|
+
:rtype: float
|
|
925
|
+
"""
|
|
926
|
+
return self.__index
|
|
927
|
+
|
|
928
|
+
@index.setter
|
|
929
|
+
def index(self, value: float) -> None:
|
|
930
|
+
"""
|
|
931
|
+
Sets the index value of this Receiver.
|
|
932
|
+
|
|
933
|
+
:param value: The index value to set
|
|
934
|
+
:type value: float
|
|
935
|
+
"""
|
|
936
|
+
self.__index = value
|
|
937
|
+
|
|
938
|
+
@property
|
|
939
|
+
def fov(self) -> float:
|
|
940
|
+
"""
|
|
941
|
+
width of the field of view.
|
|
942
|
+
|
|
943
|
+
:return: width of the field of view.
|
|
944
|
+
:rtype: float
|
|
945
|
+
"""
|
|
946
|
+
return self.__fov
|
|
947
|
+
|
|
948
|
+
@fov.setter
|
|
949
|
+
def fov(self, value: float) -> None:
|
|
950
|
+
"""
|
|
951
|
+
Sets the field of view (fov) of this Receiver.
|
|
952
|
+
|
|
953
|
+
:param value: The fov value to set
|
|
954
|
+
:type value: float
|
|
955
|
+
"""
|
|
956
|
+
self.__fov = value
|
|
957
|
+
|
|
958
|
+
@property
|
|
959
|
+
def gCon(self) -> float:
|
|
960
|
+
return self.__gCon
|
|
961
|
+
|
|
962
|
+
@property
|
|
963
|
+
def position(self) -> np.ndarray[Any, np.dtype[np.float64]]:
|
|
964
|
+
"""
|
|
965
|
+
Return the position of the object as a numpy array (x, y, z).
|
|
966
|
+
|
|
967
|
+
:return: the position of the object
|
|
968
|
+
:rtype: np.ndarray[Any, np.dtype[np.float64]]
|
|
969
|
+
"""
|
|
970
|
+
return self.__position
|
|
971
|
+
|
|
972
|
+
@property
|
|
973
|
+
def q(self) -> float:
|
|
974
|
+
"""
|
|
975
|
+
electronic load. Default is -1.602e-19 culombios
|
|
976
|
+
|
|
977
|
+
:return: electronic load.
|
|
978
|
+
:rtype: _type_
|
|
979
|
+
"""
|
|
980
|
+
return self.__q
|
|
981
|
+
|
|
982
|
+
@q.setter
|
|
983
|
+
def q(self, value: float) -> None:
|
|
984
|
+
"""
|
|
985
|
+
Sets the electronic load (q) of this Receiver.
|
|
986
|
+
|
|
987
|
+
:param value: The q value to set
|
|
988
|
+
:type value: float
|
|
989
|
+
"""
|
|
990
|
+
self.__q = value
|
|
991
|
+
|
|
992
|
+
@property
|
|
993
|
+
def s(self) -> float:
|
|
994
|
+
"""
|
|
995
|
+
ibg b - parameter of the Ib - Galactic model. Default is 5 seconds.
|
|
996
|
+
|
|
997
|
+
:return: ibg b - parameter of the Ib - Galactic model.
|
|
998
|
+
"""
|
|
999
|
+
return self.__s
|
|
1000
|
+
|
|
1001
|
+
@s.setter
|
|
1002
|
+
def s(self, value: float) -> None:
|
|
1003
|
+
"""
|
|
1004
|
+
Sets the s parameter of this Receiver.
|
|
1005
|
+
|
|
1006
|
+
:param value: The s value to set
|
|
1007
|
+
:type value: float
|
|
1008
|
+
"""
|
|
1009
|
+
self.__s = value
|
|
1010
|
+
|
|
1011
|
+
@property
|
|
1012
|
+
def b(self) -> float:
|
|
1013
|
+
"""
|
|
1014
|
+
c b - parameter of the Ib - Galactic model.
|
|
1015
|
+
|
|
1016
|
+
:return: c b - parameter of the Ib - Galactic model.
|
|
1017
|
+
"""
|
|
1018
|
+
return self.__b
|
|
1019
|
+
|
|
1020
|
+
@b.setter
|
|
1021
|
+
def b(self, value: float) -> None:
|
|
1022
|
+
"""
|
|
1023
|
+
Sets the b parameter of this Receiver.
|
|
1024
|
+
|
|
1025
|
+
:param value: The b value to set
|
|
1026
|
+
:type value: float
|
|
1027
|
+
"""
|
|
1028
|
+
self.__b = value
|
|
1029
|
+
|
|
1030
|
+
@property
|
|
1031
|
+
def ibg(self) -> float:
|
|
1032
|
+
"""
|
|
1033
|
+
background current. Default is 5.1e-3 Amperes.
|
|
1034
|
+
|
|
1035
|
+
:return: background current.
|
|
1036
|
+
:rtype: float
|
|
1037
|
+
"""
|
|
1038
|
+
return self.__ibg
|
|
1039
|
+
|
|
1040
|
+
@ibg.setter
|
|
1041
|
+
def ibg(self, value: float) -> None:
|
|
1042
|
+
"""
|
|
1043
|
+
Sets the background current (ibg) of this Receiver.
|
|
1044
|
+
|
|
1045
|
+
:param value: The ibg value to set
|
|
1046
|
+
:type value: float
|
|
1047
|
+
"""
|
|
1048
|
+
self.__ibg = value
|
|
1049
|
+
|
|
1050
|
+
@property
|
|
1051
|
+
def cb(self) -> float:
|
|
1052
|
+
"""
|
|
1053
|
+
Boltzmann constant. Default is 1.380649e-23 J/K.
|
|
1054
|
+
|
|
1055
|
+
:return: Boltzmann constant.
|
|
1056
|
+
:rtype: float
|
|
1057
|
+
"""
|
|
1058
|
+
return self.__cb
|
|
1059
|
+
|
|
1060
|
+
@cb.setter
|
|
1061
|
+
def cb(self, value: float) -> None:
|
|
1062
|
+
"""
|
|
1063
|
+
Sets the Boltzmann constant (cb) of this Receiver.
|
|
1064
|
+
|
|
1065
|
+
:param value: The cb value to set
|
|
1066
|
+
:type value: float
|
|
1067
|
+
"""
|
|
1068
|
+
self.__cb = value
|
|
1069
|
+
|
|
1070
|
+
@property
|
|
1071
|
+
def tk(self) -> float:
|
|
1072
|
+
return self.__tk
|
|
1073
|
+
|
|
1074
|
+
@tk.setter
|
|
1075
|
+
def tk(self, value: float) -> None:
|
|
1076
|
+
"""
|
|
1077
|
+
Sets the temperature (tk) of this Receiver.
|
|
1078
|
+
|
|
1079
|
+
:param value: The tk value to set
|
|
1080
|
+
:type value: float
|
|
1081
|
+
"""
|
|
1082
|
+
self.__tk = value
|
|
1083
|
+
|
|
1084
|
+
@property
|
|
1085
|
+
def a(self) -> float:
|
|
1086
|
+
"""
|
|
1087
|
+
n n - parameter of the B - parameter. Default is 10 seconds.
|
|
1088
|
+
|
|
1089
|
+
:return: n n - B - parameter
|
|
1090
|
+
:rtype: float
|
|
1091
|
+
"""
|
|
1092
|
+
return self.__a
|
|
1093
|
+
|
|
1094
|
+
@a.setter
|
|
1095
|
+
def a(self, value: float) -> None:
|
|
1096
|
+
"""
|
|
1097
|
+
Sets the a parameter of this Receiver.
|
|
1098
|
+
|
|
1099
|
+
:param value: The a value to set
|
|
1100
|
+
:type value: float
|
|
1101
|
+
"""
|
|
1102
|
+
self.__a = value
|
|
1103
|
+
|
|
1104
|
+
@property
|
|
1105
|
+
def gv(self) -> float:
|
|
1106
|
+
"""
|
|
1107
|
+
Open-loop voltage gain. Default is 10
|
|
1108
|
+
|
|
1109
|
+
:return: Open-loop voltage gain.
|
|
1110
|
+
:rtype: float
|
|
1111
|
+
"""
|
|
1112
|
+
return self.__gv
|
|
1113
|
+
|
|
1114
|
+
@gv.setter
|
|
1115
|
+
def gv(self, value: float) -> None:
|
|
1116
|
+
"""
|
|
1117
|
+
Sets the open-loop voltage gain (gv) of this Receiver.
|
|
1118
|
+
|
|
1119
|
+
:param value: The gv value to set
|
|
1120
|
+
:type value: float
|
|
1121
|
+
"""
|
|
1122
|
+
self.__gv = value
|
|
1123
|
+
|
|
1124
|
+
@property
|
|
1125
|
+
def n(self) -> float:
|
|
1126
|
+
"""
|
|
1127
|
+
Fixed capacitance of the PD per unit area. Default is 1.12e-6.
|
|
1128
|
+
|
|
1129
|
+
:return: Fixed capacitance of the PD per unit area.
|
|
1130
|
+
:rtype: float
|
|
1131
|
+
"""
|
|
1132
|
+
return self.__n
|
|
1133
|
+
|
|
1134
|
+
@n.setter
|
|
1135
|
+
def n(self, value: float) -> None:
|
|
1136
|
+
"""
|
|
1137
|
+
Sets the fixed capacitance (n) of this Receiver.
|
|
1138
|
+
|
|
1139
|
+
:param value: The n value to set
|
|
1140
|
+
:type value: float
|
|
1141
|
+
"""
|
|
1142
|
+
self.__n = value
|
|
1143
|
+
|
|
1144
|
+
@property
|
|
1145
|
+
def fr(self) -> float:
|
|
1146
|
+
"""
|
|
1147
|
+
FET channel noise factor. Default is 1.5
|
|
1148
|
+
|
|
1149
|
+
:return: FET channel noise factor.
|
|
1150
|
+
:rtype: float
|
|
1151
|
+
"""
|
|
1152
|
+
return self.__fr
|
|
1153
|
+
|
|
1154
|
+
@fr.setter
|
|
1155
|
+
def fr(self, value: float) -> None:
|
|
1156
|
+
"""
|
|
1157
|
+
Sets the FET channel noise factor (fr) of this Receiver.
|
|
1158
|
+
|
|
1159
|
+
:param value: The fr value to set
|
|
1160
|
+
:type value: float
|
|
1161
|
+
"""
|
|
1162
|
+
self.__fr = value
|
|
1163
|
+
|
|
1164
|
+
@property
|
|
1165
|
+
def gm(self) -> float:
|
|
1166
|
+
"""
|
|
1167
|
+
FET transconductance. Default is 3e-2.
|
|
1168
|
+
|
|
1169
|
+
:return: FET transconductance.
|
|
1170
|
+
:rtype: float
|
|
1171
|
+
"""
|
|
1172
|
+
return self.__gm
|
|
1173
|
+
|
|
1174
|
+
@gm.setter
|
|
1175
|
+
def gm(self, value: float) -> None:
|
|
1176
|
+
"""
|
|
1177
|
+
Sets the FET transconductance (gm) of this Receiver.
|
|
1178
|
+
|
|
1179
|
+
:param value: The gm value to set
|
|
1180
|
+
:type value: float
|
|
1181
|
+
"""
|
|
1182
|
+
self.__gm = value
|
|
1183
|
+
|
|
1184
|
+
@property
|
|
1185
|
+
def i1(self) -> float:
|
|
1186
|
+
"""
|
|
1187
|
+
constant experimental value. Default is 0.562.
|
|
1188
|
+
|
|
1189
|
+
:return: constant experimental value.
|
|
1190
|
+
:rtype: float
|
|
1191
|
+
"""
|
|
1192
|
+
return self.__i1
|
|
1193
|
+
|
|
1194
|
+
@i1.setter
|
|
1195
|
+
def i1(self, value: float) -> None:
|
|
1196
|
+
"""
|
|
1197
|
+
Sets the constant experimental value i1 of this Receiver.
|
|
1198
|
+
|
|
1199
|
+
:param value: The i1 value to set
|
|
1200
|
+
:type value: float
|
|
1201
|
+
"""
|
|
1202
|
+
self.__i1 = value
|
|
1203
|
+
|
|
1204
|
+
@property
|
|
1205
|
+
def i2(self) -> float:
|
|
1206
|
+
"""
|
|
1207
|
+
constant experimental value. Default is 0.0868.
|
|
1208
|
+
|
|
1209
|
+
:return: constant experimental value.
|
|
1210
|
+
:rtype: float
|
|
1211
|
+
"""
|
|
1212
|
+
return self.__i2
|
|
1213
|
+
|
|
1214
|
+
@i2.setter
|
|
1215
|
+
def i2(self, value: float) -> None:
|
|
1216
|
+
"""
|
|
1217
|
+
Sets the constant experimental value i2 of this Receiver.
|
|
1218
|
+
|
|
1219
|
+
:param value: The i2 value to set
|
|
1220
|
+
:type value: float
|
|
1221
|
+
"""
|
|
1222
|
+
self.__i2 = value
|
|
1223
|
+
|
|
1224
|
+
@property
|
|
1225
|
+
def timeFirstConnected(self) -> Optional[float]:
|
|
1226
|
+
"""
|
|
1227
|
+
time point at which this receiver was first connected
|
|
1228
|
+
|
|
1229
|
+
:return: time point at which this receiver was first connected
|
|
1230
|
+
:rtype: float
|
|
1231
|
+
"""
|
|
1232
|
+
return self.__timeFirstConnected
|
|
1233
|
+
|
|
1234
|
+
@timeFirstConnected.setter
|
|
1235
|
+
def timeFirstConnected(self, value: float) -> None:
|
|
1236
|
+
"""
|
|
1237
|
+
Sets the time point at which this receiver was first connected.
|
|
1238
|
+
|
|
1239
|
+
:param value: The time value to set
|
|
1240
|
+
:type value: float
|
|
1241
|
+
"""
|
|
1242
|
+
self.__timeFirstConnected = value
|
|
1243
|
+
|
|
1244
|
+
@property
|
|
1245
|
+
def goalTime(self) -> Optional[float]:
|
|
1246
|
+
"""
|
|
1247
|
+
Amount of time that this receiver will be connected. Depends on tha SNR.
|
|
1248
|
+
|
|
1249
|
+
:return: Amount of time that this receiver will be connected.
|
|
1250
|
+
:rtype: float
|
|
1251
|
+
"""
|
|
1252
|
+
return self.__goalTime
|
|
1253
|
+
|
|
1254
|
+
@goalTime.setter
|
|
1255
|
+
def goalTime(self, value: float) -> None:
|
|
1256
|
+
"""
|
|
1257
|
+
Sets the amount of time that this receiver will be connected.
|
|
1258
|
+
|
|
1259
|
+
:param value: The goal time value to set
|
|
1260
|
+
:type value: float
|
|
1261
|
+
"""
|
|
1262
|
+
self.__goalTime = value
|
|
1263
|
+
|
|
1264
|
+
@property
|
|
1265
|
+
def timeActive(self) -> float:
|
|
1266
|
+
"""
|
|
1267
|
+
Effective time connected.
|
|
1268
|
+
|
|
1269
|
+
:return: Effective time connected
|
|
1270
|
+
:rtype: float
|
|
1271
|
+
"""
|
|
1272
|
+
return self.__timeActive
|
|
1273
|
+
|
|
1274
|
+
@timeActive.setter
|
|
1275
|
+
def timeActive(self, value: float) -> None:
|
|
1276
|
+
"""
|
|
1277
|
+
Sets the effective time connected for this receiver.
|
|
1278
|
+
|
|
1279
|
+
:param value: The time active value to set
|
|
1280
|
+
:type value: float
|
|
1281
|
+
"""
|
|
1282
|
+
self.__timeActive = value
|
|
1283
|
+
|
|
1284
|
+
@property
|
|
1285
|
+
def timeFinished(self) -> Optional[float]:
|
|
1286
|
+
"""
|
|
1287
|
+
point in time when this receiver wil be finished.
|
|
1288
|
+
|
|
1289
|
+
:return: point in time when this receiver wil be finished.
|
|
1290
|
+
:rtype: float
|
|
1291
|
+
"""
|
|
1292
|
+
return self.__timeFinished
|
|
1293
|
+
|
|
1294
|
+
@timeFinished.setter
|
|
1295
|
+
def timeFinished(self, value: float) -> None:
|
|
1296
|
+
"""
|
|
1297
|
+
Sets the point in time when this receiver will be finished.
|
|
1298
|
+
|
|
1299
|
+
:param value: The time finished value to set
|
|
1300
|
+
:type value: float
|
|
1301
|
+
"""
|
|
1302
|
+
self.__timeFinished = value
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
class Scenario:
|
|
1306
|
+
"""
|
|
1307
|
+
Represents the characteristics and configuration of the simulation scenario.
|
|
1308
|
+
Manages the room dimensions, access points, receivers, and channel calculations.
|
|
1309
|
+
"""
|
|
1310
|
+
|
|
1311
|
+
numberOfAPs = 0
|
|
1312
|
+
"""
|
|
1313
|
+
Number of access points in the scenario.
|
|
1314
|
+
|
|
1315
|
+
:return: Number of access points in the scenario.
|
|
1316
|
+
:rtype: int
|
|
1317
|
+
"""
|
|
1318
|
+
|
|
1319
|
+
warnings.filterwarnings(
|
|
1320
|
+
"ignore", message="invalid value encountered in double_scalars"
|
|
1321
|
+
)
|
|
1322
|
+
|
|
1323
|
+
def __init__(
|
|
1324
|
+
self, width: float, length: float, height: float, nGrids: int, rho: float
|
|
1325
|
+
) -> None:
|
|
1326
|
+
"""Scenario Constructor
|
|
1327
|
+
|
|
1328
|
+
:param width: Width of the room
|
|
1329
|
+
:type width: float
|
|
1330
|
+
:param length: Length of the room
|
|
1331
|
+
:type length: float
|
|
1332
|
+
:param height: Height of the room
|
|
1333
|
+
:type height: float
|
|
1334
|
+
:param nGrids: Number of grids of each VLED
|
|
1335
|
+
:type nGrids: int
|
|
1336
|
+
:param rho: Reflexion coefficient
|
|
1337
|
+
:type rho: float
|
|
1338
|
+
"""
|
|
1339
|
+
|
|
1340
|
+
self.__length: float = length # x
|
|
1341
|
+
self.__width: float = width # y
|
|
1342
|
+
self.__height: float = height # z
|
|
1343
|
+
|
|
1344
|
+
self.__rho: float = rho
|
|
1345
|
+
|
|
1346
|
+
self.__start_x: float = -self.__width / 2
|
|
1347
|
+
self.__start_y: float = -self.__length / 2
|
|
1348
|
+
self.__end_x: float = self.__width / 2
|
|
1349
|
+
self.__end_y: float = self.__length / 2
|
|
1350
|
+
|
|
1351
|
+
self.__mobile_terminals: List[Receiver] = []
|
|
1352
|
+
self.__vleds: List[VLed] = []
|
|
1353
|
+
self.__femtocells: List[RF] = []
|
|
1354
|
+
|
|
1355
|
+
self.__vledsPositions: List[int] = []
|
|
1356
|
+
self.__rfsPositions: List[int] = []
|
|
1357
|
+
|
|
1358
|
+
self.__nx = round(self.__length * nGrids)
|
|
1359
|
+
self.__ny = round(self.__width * nGrids)
|
|
1360
|
+
self.__nz = round(self.__height * nGrids)
|
|
1361
|
+
|
|
1362
|
+
self.__g_x = np.linspace(self.__start_x, self.__end_x, self.__nx)
|
|
1363
|
+
self.__g_y = np.linspace(self.__start_y, self.__end_y, self.__ny)
|
|
1364
|
+
self.__g_z = np.linspace(0, self.__height, self.__nz)
|
|
1365
|
+
|
|
1366
|
+
self.__g_xyz = np.array([self.__g_x, self.__g_y, self.__g_z], dtype=object)
|
|
1367
|
+
|
|
1368
|
+
def addVLed(self, vled: "VLed") -> None:
|
|
1369
|
+
"""
|
|
1370
|
+
Add a VLED to this Scenario
|
|
1371
|
+
|
|
1372
|
+
:param vled: the VLED to be added
|
|
1373
|
+
:type vled: VLed
|
|
1374
|
+
:rtype: None
|
|
1375
|
+
"""
|
|
1376
|
+
self.__vleds.append(vled)
|
|
1377
|
+
self.__vledsPositions.append(Scenario.numberOfAPs)
|
|
1378
|
+
Scenario.numberOfAPs += 1
|
|
1379
|
+
|
|
1380
|
+
def addRF(self, rf: "RF") -> None:
|
|
1381
|
+
"""
|
|
1382
|
+
Add an RF to this Scenario
|
|
1383
|
+
|
|
1384
|
+
:param rf: The RF to be added
|
|
1385
|
+
:type rf: RF
|
|
1386
|
+
:rtype: None
|
|
1387
|
+
"""
|
|
1388
|
+
self.__femtocells.append(rf)
|
|
1389
|
+
self.__rfsPositions.append(Scenario.numberOfAPs)
|
|
1390
|
+
Scenario.numberOfAPs += 1
|
|
1391
|
+
|
|
1392
|
+
def getPowerInPointFromWalls(
|
|
1393
|
+
self, receiver: "Receiver", vledID: Optional[int]
|
|
1394
|
+
) -> float:
|
|
1395
|
+
"""
|
|
1396
|
+
Get the incident power in an specific point in the room, received from the walls.
|
|
1397
|
+
|
|
1398
|
+
:param receiver: The receiver which is in the point
|
|
1399
|
+
:type receiver: Receiver
|
|
1400
|
+
:param vledID: The VLED that is sending the signal
|
|
1401
|
+
:type vledID: Optional[int]
|
|
1402
|
+
:return: Power from walls
|
|
1403
|
+
:rtype: float
|
|
1404
|
+
"""
|
|
1405
|
+
if vledID is None or vledID < 0 or vledID >= len(self.__vleds):
|
|
1406
|
+
return 0.0
|
|
1407
|
+
vled = self.__vleds[vledID]
|
|
1408
|
+
h1 = self.__channelGainWall(receiver, vled, 1, 0)
|
|
1409
|
+
h2 = self.__channelGainWall(receiver, vled, 0, 1)
|
|
1410
|
+
h3 = self.__channelGainWall(receiver, vled, 1, 2)
|
|
1411
|
+
h4 = self.__channelGainWall(receiver, vled, 0, 3)
|
|
1412
|
+
power = (h1 + h2 + h3 + h4) * vled.totalPower * receiver.ts * receiver.gCon
|
|
1413
|
+
return float(power)
|
|
1414
|
+
|
|
1415
|
+
def getPowerInPointFromVled(
|
|
1416
|
+
self, receiver: "Receiver", vledID: Optional[int]
|
|
1417
|
+
) -> float:
|
|
1418
|
+
"""
|
|
1419
|
+
Get the power from the VLED in LOS
|
|
1420
|
+
|
|
1421
|
+
:param receiver: The receiver which is in the point
|
|
1422
|
+
:type receiver: Receiver
|
|
1423
|
+
:param vledID: The VLED that is sending the signal
|
|
1424
|
+
:type vledID: Optional[int]
|
|
1425
|
+
:return: Power from VLED
|
|
1426
|
+
:rtype: float
|
|
1427
|
+
"""
|
|
1428
|
+
if vledID is None or vledID < 0 or vledID >= len(self.__vleds):
|
|
1429
|
+
return 0.0
|
|
1430
|
+
vled = self.__vleds[vledID]
|
|
1431
|
+
D_los = m.sqrt(
|
|
1432
|
+
(receiver.x - vled.x) ** 2
|
|
1433
|
+
+ (receiver.y - vled.y) ** 2
|
|
1434
|
+
+ (receiver.z - vled.z) ** 2
|
|
1435
|
+
)
|
|
1436
|
+
cosphi = (vled.z - receiver.z) / D_los
|
|
1437
|
+
# print(vled.x, vled.y, vled.z)
|
|
1438
|
+
# print(receiver.x, receiver.y, receiver.z)
|
|
1439
|
+
# print(cosphi)
|
|
1440
|
+
r_angle = m.degrees(m.acos(cosphi))
|
|
1441
|
+
H = (
|
|
1442
|
+
(vled.ml + 1)
|
|
1443
|
+
* receiver.aDet
|
|
1444
|
+
* cosphi ** (vled.ml + 1)
|
|
1445
|
+
/ (2 * m.pi * D_los**2)
|
|
1446
|
+
)
|
|
1447
|
+
power = (
|
|
1448
|
+
vled.totalPower * H * receiver.ts * receiver.gCon
|
|
1449
|
+
if abs(r_angle) <= receiver.fov
|
|
1450
|
+
else 0
|
|
1451
|
+
)
|
|
1452
|
+
return power
|
|
1453
|
+
|
|
1454
|
+
def __channelGainWall(
|
|
1455
|
+
self, receiver: Receiver, vled: VLed, posVar: int, posFixed: int
|
|
1456
|
+
) -> float:
|
|
1457
|
+
"""
|
|
1458
|
+
Gain in channel from walls
|
|
1459
|
+
|
|
1460
|
+
:param receiver: The receiver which is in the point
|
|
1461
|
+
:type receiver: Receiver
|
|
1462
|
+
:param vled: The VLED that is sending the signal
|
|
1463
|
+
:type vled: VLed
|
|
1464
|
+
:param posVar: Auxiliar variable
|
|
1465
|
+
:type posVar: int
|
|
1466
|
+
:param posFixed: Auxiliar variable
|
|
1467
|
+
:type posFixed: int
|
|
1468
|
+
:return: Gain in channel
|
|
1469
|
+
:rtype: float
|
|
1470
|
+
"""
|
|
1471
|
+
wall = None
|
|
1472
|
+
dA = self.__height
|
|
1473
|
+
if posFixed == 0:
|
|
1474
|
+
wall = (self.__start_x, 0)
|
|
1475
|
+
dA *= self.__length / (self.__nx * self.__nz)
|
|
1476
|
+
elif posFixed == 1:
|
|
1477
|
+
wall = (self.__start_y, 1)
|
|
1478
|
+
dA *= self.__width / (self.__ny * self.__nz)
|
|
1479
|
+
elif posFixed == 2:
|
|
1480
|
+
wall = (self.__end_x, 0)
|
|
1481
|
+
dA *= self.__length / (self.__nx * self.__nz)
|
|
1482
|
+
elif posFixed == 3:
|
|
1483
|
+
wall = (self.__end_y, 1)
|
|
1484
|
+
dA *= self.__width / (self.__ny * self.__nz)
|
|
1485
|
+
|
|
1486
|
+
if wall is None:
|
|
1487
|
+
return 0.0
|
|
1488
|
+
|
|
1489
|
+
h = 0
|
|
1490
|
+
wp = np.full(3, wall[0])
|
|
1491
|
+
g = self.__g_xyz[posVar]
|
|
1492
|
+
for i in g:
|
|
1493
|
+
wp[posVar] = i
|
|
1494
|
+
for j in self.__g_z:
|
|
1495
|
+
wp[2] = j
|
|
1496
|
+
D1 = m.sqrt(np.dot(vled.position - wp, vled.position - wp))
|
|
1497
|
+
cosphi = abs(wp[2] - vled.position[2]) / D1
|
|
1498
|
+
cosalpha = abs(vled.position[wall[1]] - wp[wall[1]]) / D1
|
|
1499
|
+
D2 = m.sqrt(np.dot(wp - receiver.position, wp - receiver.position))
|
|
1500
|
+
cosbeta = abs(wp[wall[1]] - receiver.position[wall[1]]) / D2
|
|
1501
|
+
cospsi = abs(wp[2] - receiver.position[2]) / D2
|
|
1502
|
+
if abs(m.degrees(m.acos(cospsi))) <= receiver.fov:
|
|
1503
|
+
h = h + (vled.ml + 1) * receiver.aDet * self.__rho * dA * (
|
|
1504
|
+
cosphi**vled.ml
|
|
1505
|
+
) * cosalpha * cosbeta * cospsi / (
|
|
1506
|
+
2 * (m.pi**2) * (D1**2) * (D2**2)
|
|
1507
|
+
)
|
|
1508
|
+
return h
|
|
1509
|
+
|
|
1510
|
+
@property
|
|
1511
|
+
def numberOfVLeds(self) -> int:
|
|
1512
|
+
"""
|
|
1513
|
+
number of VLeds in this scenario
|
|
1514
|
+
|
|
1515
|
+
:return: number of vleds
|
|
1516
|
+
:rtype: int
|
|
1517
|
+
"""
|
|
1518
|
+
return len(self.__vleds)
|
|
1519
|
+
|
|
1520
|
+
@property
|
|
1521
|
+
def numberOfRFs(self) -> int:
|
|
1522
|
+
"""
|
|
1523
|
+
Number of RFs in this scenario
|
|
1524
|
+
|
|
1525
|
+
:return: number of RFs
|
|
1526
|
+
:rtype: int
|
|
1527
|
+
"""
|
|
1528
|
+
return len(self.__femtocells)
|
|
1529
|
+
|
|
1530
|
+
@property
|
|
1531
|
+
def vleds(self) -> List[VLed]:
|
|
1532
|
+
"""
|
|
1533
|
+
set of VLEDs in this scenario.
|
|
1534
|
+
|
|
1535
|
+
:return: set of VLEDs
|
|
1536
|
+
:rtype: List[VLed]
|
|
1537
|
+
"""
|
|
1538
|
+
return self.__vleds
|
|
1539
|
+
|
|
1540
|
+
@property
|
|
1541
|
+
def rfs(self) -> List[RF]:
|
|
1542
|
+
"""
|
|
1543
|
+
List of RFs in this scenario
|
|
1544
|
+
|
|
1545
|
+
:return: List of RFs
|
|
1546
|
+
:rtype: List[RF]
|
|
1547
|
+
"""
|
|
1548
|
+
return self.__femtocells
|
|
1549
|
+
|
|
1550
|
+
@property
|
|
1551
|
+
def start_x(self) -> float:
|
|
1552
|
+
"""
|
|
1553
|
+
Auxiliar variable determining the starting X coordinate of the room
|
|
1554
|
+
|
|
1555
|
+
:return: Starting X coordinate
|
|
1556
|
+
:rtype: float
|
|
1557
|
+
"""
|
|
1558
|
+
return self.__start_x
|
|
1559
|
+
|
|
1560
|
+
@start_x.setter
|
|
1561
|
+
def start_x(self, value: float) -> None:
|
|
1562
|
+
"""
|
|
1563
|
+
Sets the starting X coordinate of the room.
|
|
1564
|
+
|
|
1565
|
+
:param value: The starting X coordinate value to set
|
|
1566
|
+
:type value: float
|
|
1567
|
+
"""
|
|
1568
|
+
self.__start_x = value
|
|
1569
|
+
|
|
1570
|
+
@property
|
|
1571
|
+
def start_y(self) -> float:
|
|
1572
|
+
"""
|
|
1573
|
+
Auxiliar variable determining the starting Y coordinate of the room
|
|
1574
|
+
|
|
1575
|
+
:return: Starting Y coordinate
|
|
1576
|
+
:rtype: float
|
|
1577
|
+
"""
|
|
1578
|
+
return self.__start_y
|
|
1579
|
+
|
|
1580
|
+
@start_y.setter
|
|
1581
|
+
def start_y(self, value: float) -> None:
|
|
1582
|
+
"""
|
|
1583
|
+
Sets the starting Y coordinate of the room.
|
|
1584
|
+
|
|
1585
|
+
:param value: The starting Y coordinate value to set
|
|
1586
|
+
:type value: float
|
|
1587
|
+
"""
|
|
1588
|
+
self.__start_y = value
|
|
1589
|
+
|
|
1590
|
+
@property
|
|
1591
|
+
def end_x(self) -> float:
|
|
1592
|
+
"""
|
|
1593
|
+
Auxiliar variable determining the ending X coordinate of the room
|
|
1594
|
+
|
|
1595
|
+
:return: Ending X coordinate
|
|
1596
|
+
:rtype: float
|
|
1597
|
+
"""
|
|
1598
|
+
return self.__end_x
|
|
1599
|
+
|
|
1600
|
+
@end_x.setter
|
|
1601
|
+
def end_x(self, value: float) -> None:
|
|
1602
|
+
"""
|
|
1603
|
+
Sets the ending X coordinate of the room.
|
|
1604
|
+
|
|
1605
|
+
:param value: The ending X coordinate value to set
|
|
1606
|
+
:type value: float
|
|
1607
|
+
"""
|
|
1608
|
+
self.__end_x = value
|
|
1609
|
+
|
|
1610
|
+
@property
|
|
1611
|
+
def end_y(self) -> float:
|
|
1612
|
+
"""
|
|
1613
|
+
Auxiliar variable determining the ending Y coordinate of the room
|
|
1614
|
+
|
|
1615
|
+
:return: Ending Y coordinate
|
|
1616
|
+
:rtype: float
|
|
1617
|
+
"""
|
|
1618
|
+
return self.__end_y
|
|
1619
|
+
|
|
1620
|
+
@end_y.setter
|
|
1621
|
+
def end_y(self, value: float) -> None:
|
|
1622
|
+
"""
|
|
1623
|
+
Sets the ending Y coordinate of the room.
|
|
1624
|
+
|
|
1625
|
+
:param value: The ending Y coordinate value to set
|
|
1626
|
+
:type value: float
|
|
1627
|
+
"""
|
|
1628
|
+
self.__end_y = value
|
|
1629
|
+
|
|
1630
|
+
@property
|
|
1631
|
+
def height(self) -> float:
|
|
1632
|
+
"""
|
|
1633
|
+
Scenario height
|
|
1634
|
+
|
|
1635
|
+
:return: Scenario height
|
|
1636
|
+
:rtype: float
|
|
1637
|
+
"""
|
|
1638
|
+
return self.__height
|
|
1639
|
+
|
|
1640
|
+
@height.setter
|
|
1641
|
+
def height(self, value: float) -> None:
|
|
1642
|
+
"""
|
|
1643
|
+
Sets the scenario height.
|
|
1644
|
+
|
|
1645
|
+
:param value: The height value to set
|
|
1646
|
+
:type value: float
|
|
1647
|
+
"""
|
|
1648
|
+
self.__height = value
|
|
1649
|
+
|
|
1650
|
+
@property
|
|
1651
|
+
def length(self) -> float:
|
|
1652
|
+
"""
|
|
1653
|
+
Scenario Length
|
|
1654
|
+
|
|
1655
|
+
:return: Scenario Length
|
|
1656
|
+
:rtype: float
|
|
1657
|
+
"""
|
|
1658
|
+
return self.__length
|
|
1659
|
+
|
|
1660
|
+
@length.setter
|
|
1661
|
+
def length(self, value: float) -> None:
|
|
1662
|
+
"""
|
|
1663
|
+
Sets the scenario length.
|
|
1664
|
+
|
|
1665
|
+
:param value: The length value to set
|
|
1666
|
+
:type value: float
|
|
1667
|
+
"""
|
|
1668
|
+
self.__length = value
|
|
1669
|
+
|
|
1670
|
+
@property
|
|
1671
|
+
def width(self) -> float:
|
|
1672
|
+
"""
|
|
1673
|
+
Scenario width
|
|
1674
|
+
|
|
1675
|
+
:return: Scenario width
|
|
1676
|
+
:rtype: float
|
|
1677
|
+
"""
|
|
1678
|
+
return self.__width
|
|
1679
|
+
|
|
1680
|
+
@width.setter
|
|
1681
|
+
def width(self, value: float) -> None:
|
|
1682
|
+
"""
|
|
1683
|
+
Sets the scenario width.
|
|
1684
|
+
|
|
1685
|
+
:param value: The width value to set
|
|
1686
|
+
:type value: float
|
|
1687
|
+
"""
|
|
1688
|
+
self.__width = value
|
|
1689
|
+
|
|
1690
|
+
@property
|
|
1691
|
+
def vledsPositions(self) -> List[int]:
|
|
1692
|
+
"""
|
|
1693
|
+
Vleds positions in room
|
|
1694
|
+
|
|
1695
|
+
:return: Vleds position
|
|
1696
|
+
:rtype: List[int]
|
|
1697
|
+
"""
|
|
1698
|
+
return self.__vledsPositions
|
|
1699
|
+
|
|
1700
|
+
@property
|
|
1701
|
+
def rfsPositions(self) -> List[int]:
|
|
1702
|
+
"""
|
|
1703
|
+
RFs positions
|
|
1704
|
+
|
|
1705
|
+
:return: RFs positions
|
|
1706
|
+
:rtype: List[int]
|
|
1707
|
+
"""
|
|
1708
|
+
return self.__rfsPositions
|
|
1709
|
+
|
|
1710
|
+
def snrVled(self, receiver: Receiver, vled: VLed) -> float:
|
|
1711
|
+
"""
|
|
1712
|
+
Total SNR in an specific point from a VLED, considering walls and positions.
|
|
1713
|
+
|
|
1714
|
+
:param receiver: Receiver device
|
|
1715
|
+
:type receiver: Receiver
|
|
1716
|
+
:param vled: VLED
|
|
1717
|
+
:type vled: VLed
|
|
1718
|
+
:return: Total SNR
|
|
1719
|
+
:rtype: float
|
|
1720
|
+
"""
|
|
1721
|
+
vled_id = vled.ID if vled.ID is not None else -1
|
|
1722
|
+
powerReceived = self.getPowerInPointFromVled(
|
|
1723
|
+
receiver, vled_id
|
|
1724
|
+
) + self.getPowerInPointFromWalls(receiver, vled_id)
|
|
1725
|
+
rd = (2 * receiver.q * receiver.s * powerReceived * receiver.b) + (
|
|
1726
|
+
2 * receiver.q * receiver.ibg * receiver.i1 * receiver.b
|
|
1727
|
+
)
|
|
1728
|
+
rt = (
|
|
1729
|
+
8
|
|
1730
|
+
* m.pi
|
|
1731
|
+
* receiver.cb
|
|
1732
|
+
* receiver.tk
|
|
1733
|
+
* receiver.n
|
|
1734
|
+
* receiver.a
|
|
1735
|
+
* receiver.b**2
|
|
1736
|
+
* (
|
|
1737
|
+
(receiver.i1 / receiver.gv)
|
|
1738
|
+
+ (2 * m.pi)
|
|
1739
|
+
* receiver.fr
|
|
1740
|
+
/ receiver.gm
|
|
1741
|
+
* receiver.n
|
|
1742
|
+
* receiver.a
|
|
1743
|
+
* receiver.i2
|
|
1744
|
+
* receiver.b
|
|
1745
|
+
)
|
|
1746
|
+
)
|
|
1747
|
+
rg = rd + rt
|
|
1748
|
+
return (receiver.s * powerReceived) ** 2 / rg if rg != 0 else 0.0
|
|
1749
|
+
|
|
1750
|
+
def snrRf(self, receiver: Receiver, rf: RF) -> float:
|
|
1751
|
+
"""
|
|
1752
|
+
Total SNR in an specific point from an RF.
|
|
1753
|
+
|
|
1754
|
+
:param receiver: Receiver device
|
|
1755
|
+
:type receiver: Receiver
|
|
1756
|
+
:param rf: RF device
|
|
1757
|
+
:type rf: RF
|
|
1758
|
+
:return: Total SNR
|
|
1759
|
+
:rtype: float
|
|
1760
|
+
"""
|
|
1761
|
+
df = m.sqrt(
|
|
1762
|
+
(receiver.x - rf.x) ** 2
|
|
1763
|
+
+ (receiver.y - rf.y) ** 2
|
|
1764
|
+
+ (receiver.z - rf.z) ** 2
|
|
1765
|
+
)
|
|
1766
|
+
|
|
1767
|
+
return rf.pf * rf.Af * df ** (-rf.Ef)
|
|
1768
|
+
|
|
1769
|
+
def capacityVled(self, receiver: Receiver, vled: VLed) -> float:
|
|
1770
|
+
"""
|
|
1771
|
+
Capacity of the VLED channel considering receiver devices in an specific point.
|
|
1772
|
+
|
|
1773
|
+
:param receiver: Receiver device
|
|
1774
|
+
:type receiver: Receiver
|
|
1775
|
+
:param vled: VLED
|
|
1776
|
+
:type vled: VLed
|
|
1777
|
+
:return: Capacity
|
|
1778
|
+
:rtype: float
|
|
1779
|
+
"""
|
|
1780
|
+
return vled.B * m.log2(1 + self.snrVled(receiver, vled))
|
|
1781
|
+
|
|
1782
|
+
def capacityRf(self, receiver: Receiver, rf: RF) -> float:
|
|
1783
|
+
"""
|
|
1784
|
+
Capacity of the RF channel considering receiver devices in an specific point.
|
|
1785
|
+
|
|
1786
|
+
:param receiver: Receiver device
|
|
1787
|
+
:type receiver: Receiver
|
|
1788
|
+
:param rf: RF
|
|
1789
|
+
:type rf: RF
|
|
1790
|
+
:return: Capacity
|
|
1791
|
+
:rtype: float
|
|
1792
|
+
"""
|
|
1793
|
+
return rf.B * m.log2(1 + self.snrRf(receiver, rf))
|