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/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))