From 7e1fe7903e9257a1d3ed5fa92e759418ec62fa07 Mon Sep 17 00:00:00 2001 From: Dizhi Zhou Date: Sun, 21 Oct 2012 21:47:17 -0300 Subject: [PATCH 1/4] GSoC 2012 NS-3 LTE MAC schedulers --- AUTHORS | 1 + RELEASE_NOTES | 2 + src/lte/AUTHORS | 13 + src/lte/doc/source/lte-design.rst | 170 ++ src/lte/doc/source/lte-references.rst | 6 + src/lte/doc/source/lte-testing.rst | 191 ++ src/lte/doc/source/lte-user.rst | 55 + src/lte/model/eps-bearer.cc | 5 + src/lte/model/eps-bearer.h | 2 + src/lte/model/fdbet-ff-mac-scheduler.cc | 1334 ++++++++++++++ src/lte/model/fdbet-ff-mac-scheduler.h | 216 +++ src/lte/model/fdmt-ff-mac-scheduler.cc | 1292 +++++++++++++ src/lte/model/fdmt-ff-mac-scheduler.h | 205 +++ src/lte/model/fdtbfq-ff-mac-scheduler.cc | 1526 ++++++++++++++++ src/lte/model/fdtbfq-ff-mac-scheduler.h | 235 +++ src/lte/model/pss-ff-mac-scheduler.cc | 1624 +++++++++++++++++ src/lte/model/pss-ff-mac-scheduler.h | 225 +++ src/lte/model/tdbet-ff-mac-scheduler.cc | 1261 +++++++++++++ src/lte/model/tdbet-ff-mac-scheduler.h | 216 +++ src/lte/model/tdmt-ff-mac-scheduler.cc | 1232 +++++++++++++ src/lte/model/tdmt-ff-mac-scheduler.h | 205 +++ src/lte/model/tdtbfq-ff-mac-scheduler.cc | 1322 ++++++++++++++ src/lte/model/tdtbfq-ff-mac-scheduler.h | 235 +++ src/lte/model/tta-ff-mac-scheduler.cc | 1308 +++++++++++++ src/lte/model/tta-ff-mac-scheduler.h | 205 +++ .../test/lte-test-fdbet-ff-mac-scheduler.cc | 530 ++++++ .../test/lte-test-fdbet-ff-mac-scheduler.h | 89 + .../test/lte-test-fdmt-ff-mac-scheduler.cc | 347 ++++ src/lte/test/lte-test-fdmt-ff-mac-scheduler.h | 67 + .../test/lte-test-fdtbfq-ff-mac-scheduler.cc | 764 ++++++++ .../test/lte-test-fdtbfq-ff-mac-scheduler.h | 90 + src/lte/test/lte-test-pss-ff-mac-scheduler.cc | 791 ++++++++ src/lte/test/lte-test-pss-ff-mac-scheduler.h | 89 + .../test/lte-test-tdbet-ff-mac-scheduler.cc | 548 ++++++ .../test/lte-test-tdbet-ff-mac-scheduler.h | 89 + .../test/lte-test-tdmt-ff-mac-scheduler.cc | 347 ++++ src/lte/test/lte-test-tdmt-ff-mac-scheduler.h | 67 + .../test/lte-test-tdtbfq-ff-mac-scheduler.cc | 760 ++++++++ .../test/lte-test-tdtbfq-ff-mac-scheduler.h | 90 + src/lte/test/lte-test-tta-ff-mac-scheduler.cc | 347 ++++ src/lte/test/lte-test-tta-ff-mac-scheduler.h | 66 + src/lte/wscript | 32 + 42 files changed, 18199 insertions(+) create mode 100644 src/lte/model/fdbet-ff-mac-scheduler.cc create mode 100644 src/lte/model/fdbet-ff-mac-scheduler.h create mode 100644 src/lte/model/fdmt-ff-mac-scheduler.cc create mode 100644 src/lte/model/fdmt-ff-mac-scheduler.h create mode 100644 src/lte/model/fdtbfq-ff-mac-scheduler.cc create mode 100644 src/lte/model/fdtbfq-ff-mac-scheduler.h create mode 100644 src/lte/model/pss-ff-mac-scheduler.cc create mode 100644 src/lte/model/pss-ff-mac-scheduler.h create mode 100644 src/lte/model/tdbet-ff-mac-scheduler.cc create mode 100644 src/lte/model/tdbet-ff-mac-scheduler.h create mode 100644 src/lte/model/tdmt-ff-mac-scheduler.cc create mode 100644 src/lte/model/tdmt-ff-mac-scheduler.h create mode 100644 src/lte/model/tdtbfq-ff-mac-scheduler.cc create mode 100644 src/lte/model/tdtbfq-ff-mac-scheduler.h create mode 100644 src/lte/model/tta-ff-mac-scheduler.cc create mode 100644 src/lte/model/tta-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-fdbet-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-fdmt-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-pss-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-pss-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-tdbet-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-tdmt-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h create mode 100644 src/lte/test/lte-test-tta-ff-mac-scheduler.cc create mode 100644 src/lte/test/lte-test-tta-ff-mac-scheduler.h diff --git a/AUTHORS b/AUTHORS index 89a23619d..66fb528a5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -104,3 +104,4 @@ Mitch Watrous (watrous@u.washington.edu) Florian Westphal (fw@strlen.de) He Wu (mdzz@u.washington.edu) Yoshihiko Yazawa (yoshiyaz@gmail.com) +Dizhi Zhou (dizhi.zhou@gmail.com) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 3ae80338e..4f01a0056 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -21,6 +21,8 @@ Supported platforms New user-visible features ------------------------- +-Support several new LTE MAC schedulers developed in GSoC 2012 project. Those schedulers include FD-MT, TD-MT, TTA, FD-BET, TD-BET, FD-TBFQ, TD-TBFQ, PSS. Here, FD and TD mean frequency domain and time domain respectively. + Bugs fixed ---------- diff --git a/src/lte/AUTHORS b/src/lte/AUTHORS index c72746a72..c2d6ac6b1 100644 --- a/src/lte/AUTHORS +++ b/src/lte/AUTHORS @@ -1,6 +1,19 @@ The ns-3 LTE module is the result of the development effort carried out by different people/institutions/projects. The main contributors are listed below. + +==================================================== + Google Summer of Code (GSoC) (2012) +==================================================== + +Dizhi Zhou, University of New Brunswick, Canada +develop several LTE MAC schedulers + +Nicola Baldo, CTTC +Marco Miozzo, CTTC +supervision and GSoC project mentorship + + ==================================================== Google Summer of Code (GSoC) (2010) ==================================================== diff --git a/src/lte/doc/source/lte-design.rst b/src/lte/doc/source/lte-design.rst index 67ad80ad2..da3adc3e2 100644 --- a/src/lte/doc/source/lte-design.rst +++ b/src/lte/doc/source/lte-design.rst @@ -652,6 +652,176 @@ where :math:`|\cdot|` indicates the cardinality of the set; finally, \right)}{\tau} +Maximum Throughput (MT) Scheduler +---------------------------------- + +The Maximum Throughput (MT) scheduler [FCapo2012]_ aims to maximize the overall throughput of eNB. +It allocates each RB to the user that can achieve the maximum achievable rate in the current TTI. +Currently, MT scheduler in LENA has two versions: frequency domain (FDMT) and time domain (TDMT). +In FDMT, every TTI, MAC scheduler allocates RBGs to the UE who has highest achievable rate calculated +by subband CQI. In TDMT, every TTI, MAC scheduler selects one UE which has highest achievable rate +calculated by wideband CQI. Then MAC scheduler allocates all RBGs to this UE in current TTI. +The calculation of achievable rate in FDMT and TDMT is as same as the one in PF. +Let :math:`i,j` denote generic users; let :math:`t` be the +subframe index, and :math:`k` be the resource block index; let :math:`M_{i,k}(t)` be MCS +usable by user :math:`i` on resource block :math:`k` according to what reported by the AMC +model (see `Adaptive Modulation and Coding`_); finally, let :math:`S(M, B)` be the TB +size in bits as defined in [TS36.213]_ for the case where a number :math:`B` of +resource blocks is used. The achievable rate :math:`R_{i}(k,t)` in bit/s for user :math:`i` +on resource block :math:`k` at subframe :math:`t` is defined as + +.. math:: + + R_{i}(k,t) = \frac{S\left( M_{i,k}(t), 1\right)}{\tau} + +where :math:`\tau` is the TTI duration. +At the start of each subframe :math:`t`, each RB is assigned to a certain user. +In detail, the index :math:`\widehat{i}_{k}(t)` to which RB :math:`k` is assigned at time +:math:`t` is determined as + +.. math:: + + \widehat{i}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}} + \left( { R_{j}(k,t) } \right) + +When there are several UEs having the same achievable rate, current implementation always selects +the first UE created in script. Although MT can maximize cell throughput, it cannot provide +fairness to UEs in poor channel condition. + + +Throughput to Average (TTA) Scheduler +-------------------------------------- + +The Throughput to Average (TTA) scheduler [FCapo2012]_ can be considered as an intermediate between MT and PF. +The metric used in TTA is calculated as follows: + +.. math:: + + \widehat{i}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}} + \left( \frac{ R_{j}(k,t) }{ R_{j}(t) } \right) + +Here, :math:`R_{i}(k,t)` in bit/s represents the achievable rate for user :math:`i` +on resource block :math:`k` at subframe :math:`t`. The +calculation method already is shown in MT and PF. Meanwhile, :math:`R_{i}(t)` in bit/s stands +for the achievable rate for :math:`i` at subframe :math:`t`. The difference between those two +achievable rates is how to get MCS. For :math:`R_{i}(k,t)`, MCS is calculated by subband CQI while +:math:`R_{i}(t)` is calculated by wideband CQI. TTA scheduler can only be implemented in frequency domain (FD) because +the achievable rate of particular RBG is only related to FD scheduling. + +Blind Average Throughput Scheduler +---------------------------------- + +The Blind Average Throughput scheduler [FCapo2012]_ aims to provide equal throughput to all UEs under eNB. The metric +used in TTA is calculated as follows: + +.. math:: + + \widehat{i}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}} + \left( \frac{ 1 }{ T_\mathrm{j}(t) } \right) + +where :math:`T_{j}(t)` is the past throughput performance perceived by the user :math:`j` and can be calculated by the +same method in PF scheduler. In the time domain blind average throughput (TD-BET), the scheduler selects the UE +with largest priority metric and allocates all RBGs to this UE. On the other hand, in the frequency domain blind +average throughput (FD-BET), every TTI, the scheduler first selects one UE with lowest pastAverageThroughput (largest +priority metric). Then scheduler assigns one RBG to this UE, it calculates expected throughput of this UE and uses it +to compare with past average throughput :math:`T_{j}(t)` of other UEs. The scheduler continues +to allocate RBG to this UE until its expected throughput is not the smallest one among past average throughput :math:`T_{j}(t)` +of all UE. Then the scheduler will use the same way to allocate RBG for a new UE which has the +lowest past average throughput :math:`T_{j}(t)` until all RBGs are allocated to UEs. The principle behind this is +that, in every TTI, the scheduler tries the best to achieve the equal throughput among all UEs. + +Token Bank Fair Queue Scheduler +------------------------------- + +Token Band Fair Queue (TBFQ) is a QoS aware scheduler which derives from the leaky-bucket mechanism. In TBFQ, +a traffic flow of user :math:`i` is characterized by following parameters: + + * :math:`t_{i}`: packet arrival rate (byte/sec ) + * :math:`r_{i}`: token generation rate (byte/sec) + * :math:`p_{i}`: token pool size (byte) + * :math:`E_{i}`: counter that records the number of token borrowed from or given to the token bank by flow :math:`i` ; + :math:`E_{i}` can be smaller than zero + +Each K bytes data consumes k tokens. Also, TBFQ maintains a shared token bank (:math:`B`) so as to balance the traffic +between different flows. If token generation rate :math:`r_{i}` is bigger than packet arrival rate :math:`t_{i}`, then tokens +overflowing from token pool are added to the token bank, and :math:`E_{i}` is increased by the same amount. Otherwise, +flow :math:`i` needs to withdraw tokens from token bank based on a priority metric :math:`frac{E_{i}}{r_{i}}`, and :math:`E_{i}` is decreased. +Obviously, the user contributes more on token bank has higher priority to borrow tokens; on the other hand, the +user borrows more tokens from bank has lower priority to continue to withdraw tokens. Therefore, in case of several +users having the same token generation rate, traffic rate and token pool size, user suffers from higher interference +has more opportunity to borrow tokens from bank. In addition, TBFQ can police the traffic by setting the token +generation rate to limit the throughput. Additionally, TBFQ also maintains following three parameters for each flow: + + * Debt limit :math:`d_{i}`: if :math:`E_{i}` belows this threshold, user i cannot further borrow tokens from bank. This is for + preventing malicious UE to borrow too much tokens. + * Credit limit :math:`c_{i}`: the maximum number of tokens UE i can borrow from the bank in one time. + * Credit threshold :math:`C`: once :math:`E_{i}` reaches debt limit, UE i must store :math:`C` tokens to bank in order to further + borrow token from bank. + +LTE in NS-3 has two versions of TBFQ scheduler: frequency domain TBFQ (FD-TBFQ) and time domain TBFQ (TD-TBFQ). +In FD-TBFQ, the scheduler always select UE with highest metric and allocates RBG with highest subband CQI until +there are no packets within UE's RLC buffer or all RBGs are allocated [FABokhari2009]_. In TD-TBFQ, after selecting +UE with maximum metric, it allocates all RBGs to this UE by using wideband CQI [WKWong2004]_. + +Priority Set Scheduler +---------------------- + +Priority set scheduler (PSS) is a QoS aware scheduler which combines time domain (TD) and frequency domain (FD) +packet scheduling operations into one scheduler [GMonghal2008]_. It controls the fairness among UEs by a specified +Target Bit Rate (TBR). + +In TD scheduler part, PSS first selects UEs with non-empty RLC buffer and then divide them into two sets based +on the TBR: + +* set 1: UE whose past average throughput is smaller than TBR; TD scheduler calculates their priority metric in + Blind Equal Throughput (BET) style: + +.. math:: + + \widehat{i}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}} + \left( \frac{ 1 }{ T_\mathrm{j}(t) } \right) + +* set 2: UE whose past average throughput is larger (or equal) than TBR; TD scheduler calculates their priority + metric in Proportional Fair (PF) style: + +.. math:: + + \widehat{i}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}} + \left( \frac{ R_{j}(k,t) }{ T_\mathrm{j}(t) } \right) + +UEs belonged to set 1 have higher priority than ones in set 2. Then PSS will select :math:`N_{mux}` UEs with +highest metric in two sets and forward those UE to FD scheduler. In PSS, FD scheduler allocates RBG k to UE n +that maximums the chosen metric. Two PF schedulers are used in PF scheduler: + +* Proportional Fair scheduled (PFsch) + +.. math:: + + \widehat{Msch}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}} + \left( \frac{ R_{j}(k,t) }{ Tsch_\mathrm{j}(t) } \right) + + +* Carrier over Interference to Average (CoIta) + +.. math:: + + \widehat{Mcoi}_{k}(t) = \underset{j=1,...,N}{\operatorname{argmax}} + \left( \frac{ CoI[j,k] }{ \sum_{k=0}^{N_{RBG}} CoI[j,k] } \right) + +where :math:`Tsch_{j}(t)` is similar past throughput performance perceived by the user :math:`j`, with the +difference that it is updated only when the i-th user is actually served. :math:`CoI[j,k]` is an +estimation of the SINR on the RBG :math:`k` of UE :math:`j`. Both PFsch and CoIta is for decoupling +FD metric from TD scheduler. In addition, PSS FD scheduler also provide a weight metric W[n] for helping +controlling fairness in case of low number of UEs. + +.. math:: + + W[n] = max (1, \frac{TBR}{ T_{j}(t) }) + +where :math:`T_{j}(t)` is the past throughput performance perceived by the user :math:`j` . Therefore, on +RBG k, the FD scheduler selects the UE :math:`j` that maximizes the product of the frequency domain +metric (:math:`Msch`, :math:`MCoI`) by weight :math:`W[n]`. This strategy will guarantee the throughput of lower +quality UE tend towards the TBR. Transport Blocks ---------------- diff --git a/src/lte/doc/source/lte-references.rst b/src/lte/doc/source/lte-references.rst index 490be17f7..a7d46c8b7 100644 --- a/src/lte/doc/source/lte-references.rst +++ b/src/lte/doc/source/lte-references.rst @@ -86,5 +86,11 @@ References .. [R4-081920] 3GPP R4-081920 `LTE PDCCH/PCFICH Demodulation Performance Results with Implementation Margin `_ +.. [FCapo2012] F.Capozzi, G.Piro L.A.Grieco, G.Boggia, P.Camarda, "Downlink Packet Scheduling in LTE Cellular Networks: Key Design Issues and a Survey", IEEE Comm. Surveys and Tutorials, to appear +.. [FABokhari2009] F.A. Bokhari, H. Yanikomeroglu, W.K. Wong, M. Rahman, "Cross-Layer Resource Scheduling for Video Traffic in the Downlink of OFDMA-Based Wireless 4G Networks",EURASIP J. Wirel. Commun. Netw., vol.2009, no.3, pp. 1-10, Jan. 2009. + +.. [WKWong2004] W.K. Wong, H.Y. Tang, V.C.M, Leung, "Token bank fair queuing: a new scheduling algorithm for wireless multimedia services", Int. J. Commun. Syst., vol.17, no.6, pp.591-614, Aug.2004. + +.. [GMonghal2008] G.Mongha, K.I. Pedersen, I.Z. Kovacs, P.E. Mogensen, " QoS Oriented Time and Frequency Domain Packet Schedulers for The UTRAN Long Term Evolution", In Proc. IEEE VTC, 2008. diff --git a/src/lte/doc/source/lte-testing.rst b/src/lte/doc/source/lte-testing.rst index 3ddbd1ade..147df733d 100644 --- a/src/lte/doc/source/lte-testing.rst +++ b/src/lte/doc/source/lte-testing.rst @@ -347,6 +347,197 @@ more resources to the users that use a higher MCS index. where the UEs have MCS index :math:`28, 24, 16, 12, 6` +Maximum Throughput scheduler performance +---------------------------------------- + +Test suites ``lte-fdmt-ff-mac-scheduler`` and ``lte-tdmt-ff-mac-scheduler`` +create different test cases with a single eNB and several UEs, all having the same +Radio Bearer specification, using the Frequency Domain Maximum Throughput (FDMT) +scheduler and Time Domain Maximum Throughput (TDMT) scheduler respectively. +In other words, UEs are all placed at the +same distance from the eNB, and hence all placed in order to have the +same SNR. Different test cases are implemented by using a different +SNR values and a different number of UEs. The test consists on +checking that the obtained throughput performance matches with the +known reference throughput up to a given tolerance.The expected +behavior of both FDMT and TDMT scheduler when all UEs have the same SNR is that +scheduler allocates all RBGs to the first UE defined in script. This is because +the current FDMT and TDMT implementation always select the first UE to serve when there are +multiple UEs having the same SNR value. We calculate the reference +throughput value for first UE by the throughput achievable of a single UE +at the given SNR, while reference throughput value for other UEs by zero. +Let :math:`\tau` be the TTI duration, :math:`B` the transmission +bandwidth configuration in number of RBs, :math:`M` the modulation and +coding scheme in use at the given SNR and :math:`S(M, B)` be the +transport block size as defined in [TS36.213]_. The reference +throughput :math:`T` in bit/s achieved by each UE is calculated as + +.. math:: + + T = \frac{S(M,B)}{\tau} + +Throughput to Average scheduler performance +------------------------------------------- + +Test suites ``lte-tta-ff-mac-scheduler`` +create different test cases with a single eNB and several UEs, all having the same +Radio Bearer specification using TTA scheduler. Network topology and configurations in +TTA test case are as the same as the test for MT scheduler. More complex test case needs to be +developed to show the fairness feature of TTA scheduler. + + +Blind Average Throughput scheduler performance +---------------------------------------------- + +Test suites ``lte-tdbet-ff-mac-scheduler`` and ``lte-fdbet-ff-mac-scheduler`` create different +test cases with a single eNB and several UEs, all having the same Radio Bearer specification. + +In the first test case of ``lte-tdbet-ff-mac-scheduler`` and ``lte-fdbet-ff-mac-scheduler``, +the UEs are all placed at the same distance from the eNB, and hence all placed in order to +have the same SNR. Different test cases are implemented by using a different SNR value and +a different number of UEs. The test consists on checking that the obtained throughput performance +matches with the known reference throughput up to a given tolerance. In long term, the expected +behavior of both TD-BET and FD-BET when all UEs have the same SNR is that each UE should get an +equal throughput. However, the exact throughput value of TD-BET and FD-BET in this test case is not +the same. + +When all UEs have the same SNR, TD-BET can be seen as a specific case of PF where achievable rate equals +to 1. Therefore, the throughput obtained by TD-BET is equal to that of PF. On the other hand, FD-BET performs +very similar to the round robin (RR) scheduler in case of that all UEs have the same SNR and the number of UE( or RBG) +is an integer multiple of the number of RBG( or UE). In this case, FD-BET always allocate the same number of RBGs +to each UE. For example, if eNB has 12 RBGs and there are 6 UEs, then each UE will get 2 RBGs in each TTI. +Or if eNB has 12 RBGs and there are 24 UEs, then each UE will get 2 RBGs per two TTIs. When the number of +UE (RBG) is not an integer multiple of the number of RBG (UE), FD-BET will not follow the RR behavior because +it will assigned different number of RBGs to some UEs, while the throughput of each UE is still the same. + +The second category of tests aims at verifying the fairness of the both TD-BET and FD-BET schedulers in a more realistic +simulation scenario where the UEs have a different SNR (constant for the whole simulation). In this case, +both scheduler should give the same amount of averaged throughput to each user. + +Specifically, for TD-BET, let :math:`F_i` be the fraction of time allocated to user i in total simulation time, +:math:`R^{fb}_i` be the the full bandwidth achievable rate for user i and :math:`T_i` be the achieved throughput of +user i. Then we have: + +.. math:: + + T_i = F_i R^{fb}_i + +In TD-BET, the sum of :math:`F_i` for all user equals one. In long term, all UE has the same :math:`T_i` so that we replace +:math:`T_i` by :math:`T`. Then we have: + +.. math:: + + T = \frac{1}{ \sum_{i=1}^{N} \frac{1}{R^{fb}_i} } + +Token Band Fair Queue scheduler performance +------------------------------------------- + +Test suites ``lte-fdtbfq-ff-mac-scheduler`` and ``lte-tdtbfq-ff-mac-scheduler`` create different +test cases for testing three key features of TBFQ scheduler: traffic policing, fairness and traffic +balance. Constant Bit Rate UDP traffic is used in both downlink and uplink in all test cases. +The packet interval is set to 1ms to keep the RLC buffer non-empty. Different traffic rate is +achieved by setting different packet size. Specifically, two classes of flows are created in the +testsuites: + + * Homogeneous flow: flows with the same token generation rate and packet arrival rate + * Heterogeneous flow: flows with different packet arrival rate, but with the same token generation rate + +In test case 1 verifies traffic policing and fairness features for the scenario that all UEs are +placed at the same distance from the eNB. In this case, all Ues have the same SNR value. Different +test cases are implemented by using a different SNR value and a different number of UEs. Because each +flow have the same traffic rate and token generation rate, TBFQ scheduler will guarantee the same +throughput among UEs without the constraint of token generation rate. In addition, the exact value +of UE throughput is depended on the total traffic rate: + + * If total traffic rate <= maximum throughput, UE throughput = traffic rate + + * If total traffic rate > maximum throughput, UE throughput = maximum throughput / N + +Here, N is the number of UE connected to eNodeB. The maximum throughput in this case equals to the rate +that all RBGs are assigned to one UE(e.g., when distance equals 0, maximum throughput is 2196000 byte/sec). +When the traffic rate is smaller than max bandwidth, TBFQ can police the traffic by token generation rate +so that the UE throughput equals its actual traffic rate (token generation rate is set to traffic +generation rate); On the other hand, when total traffic rate is bigger than the max throughput, eNodeB +cannot forward all traffic to UEs. Therefore, in each TTI, TBFQ will allocate all RBGs to one UE due to +the large packets buffered in RLC buffer. When a UE is scheduled in current TTI, its token counter is decreased +so that it will not be scheduled in the next TTI. Because each UE has the same traffic generation rate, +TBFQ will serve each UE in turn and only serve one UE in each TTI (both in TD TBFQ and FD TBFQ). +Therefore, the UE throughput in the second condition equals to the evenly share of maximum throughput. + +Test case 2 verifies traffic policing and fairness features for the scenario that each UE is placed at +the different distance from the eNB. In this case, each UE has the different SNR value. Similar to test +case 1, UE throughput in test case 2 is also depended on the total traffic rate but with a different +maximum throughput. Suppose all UEs have a high traffic load. Then the traffic will saturate the RLC buffer +in eNodeB. In each TTI, after selecting one UE with highest metric, TBFQ will allocate all RBGs to this +UE due to the large RLC buffer size. On the other hand, once RLC buffer is saturated, the total throughput +of all UEs cannot increase any more. In addition, as we discussed in test case 1, for homogeneous flows +which have the same t_i and r_i, each UE will achieve the same throughput in long term. Therefore, we +can use the same method in TD BET to calculate the maximum throughput: + +.. math:: + + T = \frac{N}{ \sum_{i=1}^{N} \frac{1}{R^{fb}_i} } + +Here, :math:`T` is the maximum throughput. :math:`R^{fb}_i` be the the full bandwidth achievable rate +for user i. :math:`N` is the number of UE. + +When the totol traffic rate is bigger than :math:`T`, the UE throughput equals to :math:`\frac{T}{N}` . Otherwise, UE throughput +equals to its traffic generation rate. + +In test case 3, three flows with different traffic rate are created. Token generation rate for each +flow is the same and equals to the average traffic rate of three flows. Because TBFQ use a shared token +bank, tokens contributed by UE with lower traffic load can be utilized by UE with higher traffic load. +In this way, TBFQ can guarantee the traffic rate for each flow. Although we use heterogeneous flow here, +the calculation of maximum throughput is as same as that in test case 2. In calculation max throughput +of test case 2, we assume that all UEs suffer high traffic load so that scheduler always assign all RBGs +to one UE in each TTI. This assumes is also true in heterogeneous flow case. In other words, whether +those flows have the same traffic rate and token generation rate, if their traffic rate is bigger enough, +TBFQ performs as same as it in test case 2. Therefore, the maximum bandwidth in test case 3 is as +same as it in test case 2. + +In test case 3, in some flows, token generate rate does not equal to MBR, although all flows are CBR +traffic. This is not accorded with our parameter setting rules. Actually, the traffic balance feature +is used in VBR traffic. Because different UE's peak rate may occur in different time, TBFQ use shared +token bank to balance the traffic among those VBR traffics. Test case 3 use CBR traffic to verify this +feature. But in the real simulation, it is recommended to set token generation rate to MBR. + +Priority Set scheduler performance +---------------------------------- + +Test suites ``lte-pss-ff-mac-scheduler`` create different test cases with a single eNB and several UEs. +In all test cases, we select PFsch in FD scheduler. Same testing results can also be obtained by using CoItA +scheduler. In addition, all test cases do not define nMux so that TD scheduler in PSS will always select half +of total UE. + +In the first class test case of ``lte-pss-ff-mac-scheduler``, the UEs are all placed at the same distance from +the eNB, and hence all placed in order to have the same SNR. Different test cases are implemented +by using a different TBR for each UEs. In each test cases, all UEs have the same +Target Bit Rate configured by GBR in EPS bear setting. The expected behavior of PSS is to guarantee that +each UE's throughput at least equals its TBR if the total flow rate is blow maximum throughput. Similar +to TBFQ, the maximum throughput in this case equals to the rate that all RBGs are assigned to one UE. +When the traffic rate is smaller than max bandwidth, the UE throughput equals its actual traffic rate; +On the other hand, UE throughput equals to the evenly share of the maximum throughput. + +In the first class of test cases, each UE has the same SNR. Therefore, the priority metric in PF scheduler will be +determined by past average throughput :math:`T_{j}(t)` because each UE has the same achievable throughput +:math:`R_{j}(k,t)` in PFsch or same :math:`CoI[k,n]` in CoItA. This means that PSS will performs like a +TD-BET which allocates all RBGs to one UE in each TTI. Then the maximum value of UE throughput equals to +the achievable rate that all RBGs are allocated to this UE. + +In the second class of test case of ``lte-pss-ff-mac-scheduler``, the UEs are all placed at the same distance from +the eNB, and hence all placed in order to have the same SNR. Different TBR values are assigned to each UE. +There also exist an maximum throughput in this case. Once total traffic rate is bigger than this threshold, +there will be some UEs that cannot achieve their TBR. Because there is no fading, subband CQIs for each +RBGs frequency are the same. Therefore, in FD scheduler,in each TTI, priority metrics of UE for all RBGs +are the same. This means that FD scheduler will always allocate all RBGs to one user. Therefore, in the +maximum throughput case, PSS performs like a TD-BET. Then we have: + +.. math:: + + T = \frac{N}{ \sum_{i=1}^N \frac{1}{R^{fb}_i} } + +Here, :math:`T` is the maximum throughput. :math:`R^{fb}_i` be the the full bandwidth achievable rate +for user i. :math:`N` is the number of UE. Building Propagation Loss Model ------------------------------- diff --git a/src/lte/doc/source/lte-user.rst b/src/lte/doc/source/lte-user.rst index a8ca8fefa..f92e79fe5 100644 --- a/src/lte/doc/source/lte-user.rst +++ b/src/lte/doc/source/lte-user.rst @@ -189,6 +189,61 @@ note that the above will put in the file ``input-defaults.txt`` *all* the default values that are registered in your particular build of the simulator, including lots of non-LTE attributes. +Configure LTE MAC Scheduler +--------------------------- + +There are several types of LTE MAC scheduler user can choose here. User can use following codes to define scheduler type:: + + Ptr lteHelper = CreateObject (); + lteHelper->SetSchedulerType ("ns3::FdMtFfMacScheduler"); // FD-MT scheduler + lteHelper->SetSchedulerType ("ns3::TdMtFfMacScheduler"); // TD-MT scheduler + lteHelper->SetSchedulerType ("ns3::TtaFfMacScheduler"); // TTA scheduler + lteHelper->SetSchedulerType ("ns3::FdBetFfMacScheduler"); // FD-BET scheduler + lteHelper->SetSchedulerType ("ns3::TdBetFfMacScheduler"); // TD-BET scheduler + lteHelper->SetSchedulerType ("ns3::FdTbfqFfMacScheduler"); // FD-TBFQ scheduler + lteHelper->SetSchedulerType ("ns3::TdTbfqFfMacScheduler"); // TD-TBFQ scheduler + lteHelper->SetSchedulerType ("ns3::PssFfMacScheduler"); //PSS scheduler + +TBFQ and PSS have more parameters than other schedulers. Users can define those parameters in following way:: + + * TBFQ scheduler:: + + Ptr lteHelper = CreateObject (); + lteHelper->SetSchedulerAttribute("DebtLimit", IntegerValue(yourvalue)); // default value -625000 bytes (-5Mb) + lteHelper->SetSchedulerAttribute("CreditLimit", UintegerValue(yourvalue)); // default value 625000 bytes (5Mb) + lteHelper->SetSchedulerAttribute("TokenPoolSize", UintegerValue(yourvalue)); // default value 1 byte + lteHelper->SetSchedulerAttribute("CreditableThreshold", UintegerValue(yourvalue)); // default value 0 + + * PSS scheduler:: + + Ptr lteHelper = CreateObject (); + lteHelper->SetSchedulerAttribute("nMux", UIntegerValue(yourvalue)); // the maximum number of UE selected by TD scheduler + lteHelper->SetSchedulerAttribute("PssFdSchedulerType", StringValue("CoItA")); // PF scheduler type in PSS + +In TBFQ, default values of debt limit and credit limit are set to -5Mb and 5Mb respectively based on paper [FABokhari2009]_. +Current implementation does not consider credit threshold (:math:`C` = 0). In PSS, if user does not define nMux, +PSS will set this value to half of total UE. The default FD scheduler is PFsch. + +In addition, token generation rate in TBFQ and target bit rate in PSS need to be configured by Guarantee Bit Rate (GBR) or +Maximum Bit Rate (MBR) in epc bearer QoS parameters. Users can use following codes to define GBR and MBR in both downlink and uplink:: + + Ptr lteHelper = CreateObject (); + enum EpsBearer::Qci q = EpsBearer::yourvalue; // define Qci type + GbrQosInformation qos; + qos.gbrDl = yourvalue; // Downlink GBR + qos.gbrUl = yourvalue; // Uplink GBR + qos.mbrDl = yourvalue; // Downlink MBR + qos.mbrUl = yourvalue; // Uplink MBR + EpsBearer bearer (q, qos); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + +In PSS, TBR is obtained from GBR in bearer level QoS parameters. In TBFQ, token generation rate is obtained from the MBR +setting in bearer level QoS parameters, which therefore needs to be configured consistently. +For constant bit rate (CBR) traffic, it is suggested to set MBR to GBR. For variance bit rate (VBR) traffic, +it is suggested to set MBR k times larger than GBR in order to cover the peak traffic rate. In current implementation, k is set to +three based on paper [FABokhari2009]_. In addition, current version of TBFQ does not consider RLC header and PDCP header length in +MBR and GBR. Another parameter in TBFQ is packet arrival rate. This parameter is calculated within scheduler and equals to the past +average throughput which is used in PF scheduler. Simulation Output ----------------- diff --git a/src/lte/model/eps-bearer.cc b/src/lte/model/eps-bearer.cc index 275bdb726..ae9e59be4 100644 --- a/src/lte/model/eps-bearer.cc +++ b/src/lte/model/eps-bearer.cc @@ -38,6 +38,11 @@ EpsBearer::EpsBearer (Qci x) { } +EpsBearer::EpsBearer (Qci x, struct GbrQosInformation y) + : qci (x), gbrQosInfo (y) +{ +} + bool EpsBearer::IsGbr () const { diff --git a/src/lte/model/eps-bearer.h b/src/lte/model/eps-bearer.h index 989ffce27..a4f5f0e34 100644 --- a/src/lte/model/eps-bearer.h +++ b/src/lte/model/eps-bearer.h @@ -88,6 +88,8 @@ struct EpsBearer */ EpsBearer (Qci x); + EpsBearer (Qci x, GbrQosInformation y); + /** * * @return true if the EPS Bearer is a Guaranteed Bit Rate bearer, false otherwise diff --git a/src/lte/model/fdbet-ff-mac-scheduler.cc b/src/lte/model/fdbet-ff-mac-scheduler.cc new file mode 100644 index 000000000..c477b0a15 --- /dev/null +++ b/src/lte/model/fdbet-ff-mac-scheduler.cc @@ -0,0 +1,1334 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("FdBetFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int FdBetType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + +NS_OBJECT_ENSURE_REGISTERED (FdBetFfMacScheduler); + +class FdBetSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + FdBetSchedulerMemberCschedSapProvider (FdBetFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + FdBetSchedulerMemberCschedSapProvider (); + FdBetFfMacScheduler* m_scheduler; +}; + +FdBetSchedulerMemberCschedSapProvider::FdBetSchedulerMemberCschedSapProvider () +{ +} + +FdBetSchedulerMemberCschedSapProvider::FdBetSchedulerMemberCschedSapProvider (FdBetFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +FdBetSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +FdBetSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +FdBetSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +FdBetSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +FdBetSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class FdBetSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + FdBetSchedulerMemberSchedSapProvider (FdBetFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + FdBetSchedulerMemberSchedSapProvider (); + FdBetFfMacScheduler* m_scheduler; +}; + + + +FdBetSchedulerMemberSchedSapProvider::FdBetSchedulerMemberSchedSapProvider () +{ +} + + +FdBetSchedulerMemberSchedSapProvider::FdBetSchedulerMemberSchedSapProvider (FdBetFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +FdBetSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +FdBetFfMacScheduler::FdBetFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_timeWindow (99.0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new FdBetSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new FdBetSchedulerMemberSchedSapProvider (this); +} + +FdBetFfMacScheduler::~FdBetFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +FdBetFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +FdBetFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::FdBetFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&FdBetFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +FdBetFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +FdBetFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +FdBetFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +FdBetFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +FdBetFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +FdBetFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +FdBetFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::map ::iterator it; + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + fdbetsFlowPerf_t flowStatsDl; + flowStatsDl.flowStart = Simulator::Now (); + flowStatsDl.totalBytesTransmitted = 0; + flowStatsDl.lastTtiBytesTransmitted = 0; + flowStatsDl.lastAveragedThroughput = 1; + m_flowStatsDl.insert (std::pair (params.m_rnti, flowStatsDl)); + fdbetsFlowPerf_t flowStatsUl; + flowStatsUl.flowStart = Simulator::Now (); + flowStatsUl.totalBytesTransmitted = 0; + flowStatsUl.lastTtiBytesTransmitted = 0; + flowStatsUl.lastAveragedThroughput = 1; + m_flowStatsUl.insert (std::pair (params.m_rnti, flowStatsUl)); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + } + + return; +} + +void +FdBetFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdBetFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +FdBetFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +FdBetFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdBetFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +FdBetFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < FdBetType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + + +int +FdBetFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + +void +FdBetFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + + RefreshDlCqiMaps (); + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + std::map ::iterator itFlow; + std::map estAveThr; // store expected average throughput for UE + std::map ::iterator itMax = estAveThr.end (); + std::map ::iterator it; + std::map rbgPerRntiLog; // record the number of RBG assigned to UE + double metricMax = 0.0; + + for (itFlow = m_flowStatsDl.begin (); itFlow != m_flowStatsDl.end (); itFlow++) + { + estAveThr.insert (std::pair ((*itFlow).first, (*itFlow).second.lastAveragedThroughput)); + } + + // Find UE with largest priority metric + for (it = estAveThr.begin (); it != estAveThr.end (); it++) + { + double metric = 1 / (*it).second; + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } + rbgPerRntiLog.insert (std::pair ((*it).first, 1)); + } + + // The scheduler tries the best to achieve the equal throughput among all UEs + int i = 0; + do + { + // allocate one RBG to current UE + std::map >::iterator itMap; + std::vector tempMap; + itMap = allocationMap.find ((*itMax).first); + if (itMap == allocationMap.end ()) + { + tempMap.push_back (i); + allocationMap.insert (std::pair > ((*itMax).first, tempMap)); + } + else + { + (*itMap).second.push_back (i); + } + + // caculate expected throughput for current UE + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find ((*itMax).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMax).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMax).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector mcs; + for (uint8_t j = 0; j < nLayer; j++) + { + if (itCqi == m_p10CqiRxed.end ()) + { + mcs.push_back (0); // no info on this user -> lowest MCS + } + else + { + mcs.push_back (m_amc->GetMcsFromCqi ((*itCqi).second)); + } + } + + std::map ::iterator itRbgPerRntiLog; + itRbgPerRntiLog = rbgPerRntiLog.find ((*itMax).first); + std::map ::iterator itPastAveThr; + itPastAveThr = m_flowStatsDl.find ((*itMax).first); + uint32_t bytesTxed = 0; + for (uint8_t j = 0; j < nLayer; j++) + { + int tbSize = (m_amc->GetTbSizeFromMcs (mcs.at (0), (*itRbgPerRntiLog).second * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + bytesTxed += tbSize; + } + double expectedAveThr = ((1.0 - (1.0 / m_timeWindow)) * (*itPastAveThr).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)(bytesTxed / 0.001)); + + int rbgPerRnti = (*itRbgPerRntiLog).second; + rbgPerRnti++; + rbgPerRntiLog[(*itMax).first] = rbgPerRnti; + estAveThr[(*itMax).first] = expectedAveThr; + + // find new UE with largest priority metric + metricMax = 0.0; + for (it = estAveThr.begin (); it != estAveThr.end (); it++) + { + double metric = 1 / (*it).second; + if (metric > metricMax) + { + itMax = it; + metricMax = metric; + } + } // end for estAveThr + + i++; + + } + while ( i < rbgNum ); // end for RBGs + + // reset TTI stats of users + std::map ::iterator itStats; + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + (*itStats).second.lastTtiBytesTransmitted = 0; + } + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + uint16_t rbgPerRnti = (*itMap).second.size (); + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find ((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + uint32_t bytesTxed = 0; + for (uint8_t i = 0; i < nLayer; i++) + { + if (itCqi == m_p10CqiRxed.end ()) + { + newDci.m_mcs.push_back (0); // no info on this user -> lowest MCS + } + else + { + newDci.m_mcs.push_back ( m_amc->GetMcsFromCqi ((*itCqi).second) ); + } + } + + for (uint8_t i = 0; i < nLayer; i++) + { + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (0), rbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + bytesTxed += tbSize; + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) && + (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + // update UE stats + std::map ::iterator it; + it = m_flowStatsDl.find ((*itMap).first); + if (it != m_flowStatsDl.end ()) + { + (*it).second.lastTtiBytesTransmitted = bytesTxed; + } + else + { + NS_LOG_DEBUG (this << " No Stats for this allocated UE"); + } + + itMap++; + + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + + // update UEs stats + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted; + // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term Evolution, Ed Wiley) + (*itStats).second.lastAveragedThroughput = ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); + (*itStats).second.lastTtiBytesTransmitted = 0; + + } + + m_schedSapUser->SchedDlConfigInd (ret); + + + return; +} + +void +FdBetFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdBetFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +FdBetFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +FdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + std::map ::iterator itStats; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + // update TTI UE stats + itStats = m_flowStatsUl.find ((*it).first); + if (itStats != m_flowStatsUl.end ()) + { + (*itStats).second.lastTtiBytesTransmitted = uldci.m_tbSize; +// NS_LOG_DEBUG (this << " UE bytes txed " << (*it).second.lastTtiBytesTransmitted); + + + } + else + { + NS_LOG_DEBUG (this << " No Stats for this allocated UE"); + } + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + + // Update global UE stats + // update UEs stats + for (itStats = m_flowStatsUl.begin (); itStats != m_flowStatsUl.end (); itStats++) + { + (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted; + // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term Evolution, Ed Wiley) + (*itStats).second.lastAveragedThroughput = ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); + // NS_LOG_DEBUG (this << " UE tot bytes " << (*itStats).second.totalBytesTransmitted); + // NS_LOG_DEBUG (this << " UE avg thr " << (*itStats).second.lastAveragedThroughput); + (*itStats).second.lastTtiBytesTransmitted = 0; + } + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +FdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdBetFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdBetFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +FdBetFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("FdBetFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +FdBetFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +FdBetFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +FdBetFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +FdBetFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +FdBetFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/fdbet-ff-mac-scheduler.h b/src/lte/model/fdbet-ff-mac-scheduler.h new file mode 100644 index 000000000..7efbf9496 --- /dev/null +++ b/src/lte/model/fdbet-ff-mac-scheduler.h @@ -0,0 +1,216 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef FDBET_FF_MAC_SCHEDULER_H +#define FDBET_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ns3 { + + +struct fdbetsFlowPerf_t +{ + Time flowStart; + unsigned long totalBytesTransmitted; + unsigned int lastTtiBytesTransmitted; + double lastAveragedThroughput; +}; + +/** + * \ingroup lte + + * \brief Implements the SCHED SAP and CSCHED SAP for a Frequency Domain Blind Equal Throughput scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class FdBetFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + FdBetFfMacScheduler (); + + /** + * Destructor + */ + virtual ~FdBetFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class FdBetSchedulerMemberCschedSapProvider; + friend class FdBetSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Map of UE statistics (per RNTI basis) in downlink + */ + std::map m_flowStatsDl; + + /* + * Map of UE statistics (per RNTI basis) + */ + std::map m_flowStatsUl; + + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + + double m_timeWindow; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs +}; + +} // namespace ns3 + +#endif /* FDBET_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/model/fdmt-ff-mac-scheduler.cc b/src/lte/model/fdmt-ff-mac-scheduler.cc new file mode 100644 index 000000000..6477dfc9a --- /dev/null +++ b/src/lte/model/fdmt-ff-mac-scheduler.cc @@ -0,0 +1,1292 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("FdMtFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int FdMtType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + + +NS_OBJECT_ENSURE_REGISTERED (FdMtFfMacScheduler); + + + +class FdMtSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + FdMtSchedulerMemberCschedSapProvider (FdMtFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + FdMtSchedulerMemberCschedSapProvider (); + FdMtFfMacScheduler* m_scheduler; +}; + +FdMtSchedulerMemberCschedSapProvider::FdMtSchedulerMemberCschedSapProvider () +{ +} + +FdMtSchedulerMemberCschedSapProvider::FdMtSchedulerMemberCschedSapProvider (FdMtFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +FdMtSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +FdMtSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +FdMtSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +FdMtSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +FdMtSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class FdMtSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + FdMtSchedulerMemberSchedSapProvider (FdMtFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + FdMtSchedulerMemberSchedSapProvider (); + FdMtFfMacScheduler* m_scheduler; +}; + + + +FdMtSchedulerMemberSchedSapProvider::FdMtSchedulerMemberSchedSapProvider () +{ +} + + +FdMtSchedulerMemberSchedSapProvider::FdMtSchedulerMemberSchedSapProvider (FdMtFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +FdMtSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +FdMtFfMacScheduler::FdMtFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new FdMtSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new FdMtSchedulerMemberSchedSapProvider (this); +} + +FdMtFfMacScheduler::~FdMtFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +FdMtFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +FdMtFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::FdMtFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&FdMtFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +FdMtFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +FdMtFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +FdMtFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +FdMtFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +FdMtFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +FdMtFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +FdMtFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::set::iterator it; + + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + m_flowStatsDl.insert (params.m_rnti); + m_flowStatsUl.insert (params.m_rnti); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + + } + + return; +} + + +void +FdMtFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdMtFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +FdMtFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +FdMtFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdMtFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +FdMtFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < FdMtType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + + +int +FdMtFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + + +void +FdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + + RefreshDlCqiMaps (); + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + for (int i = 0; i < rbgNum; i++) + { + std::set ::iterator it; + std::set ::iterator itMax = m_flowStatsDl.end (); + double rcqiMax = 0.0; + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++) + { + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*it)); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*it)); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it)); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector sbCqi; + if (itCqi == m_a30CqiRxed.end ()) + { + for (uint8_t k = 0; k < nLayer; k++) + { + sbCqi.push_back (1); // start with lowest value + } + } + else + { + sbCqi = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; + } + uint8_t cqi1 = sbCqi.at (0); + uint8_t cqi2 = 1; + if (sbCqi.size () > 1) + { + cqi2 = sbCqi.at (1); + } + + if ((cqi1 > 0)||(cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + { + if (LcActivePerFlow ((*it)) > 0) + { + // this UE has data to transmit + double achievableRate = 0.0; + for (uint8_t k = 0; k < nLayer; k++) + { + uint8_t mcs = 0; + if (sbCqi.size () > k) + { + mcs = m_amc->GetMcsFromCqi (sbCqi.at (k)); + } + else + { + // no info on this subband -> worst MCS + mcs = 0; + } + achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI + } + + double rcqi = achievableRate; + + if (rcqi > rcqiMax) + { + rcqiMax = rcqi; + itMax = it; + } + } + } // end if cqi + } // end for m_flowStatsDl + + if (itMax == m_flowStatsDl.end ()) + { + // no UE available for this RB + NS_LOG_DEBUG (this << " no UE found"); + } + else + { + std::map >::iterator itMap; + itMap = allocationMap.find ((*itMax)); + if (itMap == allocationMap.end ()) + { + // insert new element + std::vector tempMap; + tempMap.push_back (i); + allocationMap.insert (std::pair > ((*itMax), tempMap)); + } + else + { + (*itMap).second.push_back (i); + } + } + } // end for RBGs + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + uint16_t rbgPerRnti = (*itMap).second.size (); + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector worstCqi (2, 15); + if (itCqi != m_a30CqiRxed.end ()) + { + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + if ((*itCqi).second.m_higherLayerSelected.size () > (*itMap).second.at (k)) + { + for (uint8_t j = 0; j < nLayer; j++) + { + if ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.size () > j) + { + if (((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)) < worstCqi.at (j)) + { + worstCqi.at (j) = ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)); + } + } + else + { + // no CQI for this layer of this suband -> worst one + worstCqi.at (j) = 1; + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + uint32_t bytesTxed = 0; + for (uint8_t j = 0; j < nLayer; j++) + { + newDci.m_mcs.push_back (m_amc->GetMcsFromCqi (worstCqi.at (j))); + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (j), rbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + bytesTxed += tbSize; + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) && + (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + + itMap++; + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + m_schedSapUser->SchedDlConfigInd (ret); + + return; +} + +void +FdMtFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdMtFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +FdMtFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +FdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +FdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdMtFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdMtFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +FdMtFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("FdMtFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +FdMtFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +FdMtFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +FdMtFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +FdMtFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +FdMtFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/fdmt-ff-mac-scheduler.h b/src/lte/model/fdmt-ff-mac-scheduler.h new file mode 100644 index 000000000..90b5f1693 --- /dev/null +++ b/src/lte/model/fdmt-ff-mac-scheduler.h @@ -0,0 +1,205 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef FDMT_FF_MAC_SCHEDULER_H +#define FDMT_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace ns3 { + +/** + * \ingroup lte + * + * \brief Implements the SCHED SAP and CSCHED SAP for a Frequency Domain Maximum Throughput scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class FdMtFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + FdMtFfMacScheduler (); + + /** + * Destructor + */ + virtual ~FdMtFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class FdMtSchedulerMemberCschedSapProvider; + friend class FdMtSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Set of UE's RNTI in downlink + */ + std::set m_flowStatsDl; + + /* + * Set of UE's RNTI in uplink + */ + std::set m_flowStatsUl; + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs +}; + +} // namespace ns3 + +#endif /* FDMT_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/model/fdtbfq-ff-mac-scheduler.cc b/src/lte/model/fdtbfq-ff-mac-scheduler.cc new file mode 100644 index 000000000..4e5470304 --- /dev/null +++ b/src/lte/model/fdtbfq-ff-mac-scheduler.cc @@ -0,0 +1,1526 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("FdTbfqFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int FdTbfqType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + +NS_OBJECT_ENSURE_REGISTERED (FdTbfqFfMacScheduler); + +class FdTbfqSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + FdTbfqSchedulerMemberCschedSapProvider (FdTbfqFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + FdTbfqSchedulerMemberCschedSapProvider (); + FdTbfqFfMacScheduler* m_scheduler; +}; + +FdTbfqSchedulerMemberCschedSapProvider::FdTbfqSchedulerMemberCschedSapProvider () +{ +} + +FdTbfqSchedulerMemberCschedSapProvider::FdTbfqSchedulerMemberCschedSapProvider (FdTbfqFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +FdTbfqSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +FdTbfqSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +FdTbfqSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +FdTbfqSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +FdTbfqSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class FdTbfqSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + FdTbfqSchedulerMemberSchedSapProvider (FdTbfqFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + FdTbfqSchedulerMemberSchedSapProvider (); + FdTbfqFfMacScheduler* m_scheduler; +}; + + + +FdTbfqSchedulerMemberSchedSapProvider::FdTbfqSchedulerMemberSchedSapProvider () +{ +} + + +FdTbfqSchedulerMemberSchedSapProvider::FdTbfqSchedulerMemberSchedSapProvider (FdTbfqFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +FdTbfqSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +FdTbfqFfMacScheduler::FdTbfqFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_timeWindow (99.0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new FdTbfqSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new FdTbfqSchedulerMemberSchedSapProvider (this); +} + +FdTbfqFfMacScheduler::~FdTbfqFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +FdTbfqFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +FdTbfqFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::FdTbfqFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + .AddAttribute ("DebtLimit", + "Flow debt limit (default -625000 bytes)", + IntegerValue (-625000), + MakeIntegerAccessor (&FdTbfqFfMacScheduler::m_debtLimit), + MakeIntegerChecker ()) + .AddAttribute ("CreditLimit", + "Flow credit limit (default 625000 bytes)", + UintegerValue (625000), + MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_creditLimit), + MakeUintegerChecker ()) + .AddAttribute ("TokenPoolSize", + "The maximum value of flow token pool (default 1 bytes)", + UintegerValue (1), + MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_tokenPoolSize), + MakeUintegerChecker ()) + .AddAttribute ("CreditableThreshold", + "Threshold of flow credit (default 0 bytes)", + UintegerValue (0), + MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_creditableThreshold), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +FdTbfqFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +FdTbfqFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +FdTbfqFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +FdTbfqFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +FdTbfqFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +FdTbfqFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +FdTbfqFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::map ::iterator it; + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + uint64_t mbrDlInBytes = params.m_logicalChannelConfigList.at (i).m_eRabMaximulBitrateDl / 8; // byte/s + uint64_t mbrUlInBytes = params.m_logicalChannelConfigList.at (i).m_eRabMaximulBitrateUl / 8; // byte/s + + fdtbfqsFlowPerf_t flowStatsDl; + flowStatsDl.flowStart = Simulator::Now (); + flowStatsDl.packetArrivalRate = 0; + flowStatsDl.tokenGenerationRate = mbrDlInBytes; + flowStatsDl.tokenPoolSize = 0; + flowStatsDl.maxTokenPoolSize = m_tokenPoolSize; + flowStatsDl.counter = 0; + flowStatsDl.burstCredit = m_creditLimit; // bytes + flowStatsDl.debtLimit = m_debtLimit; // bytes + flowStatsDl.creditableThreshold = m_creditableThreshold; + m_flowStatsDl.insert (std::pair (params.m_rnti, flowStatsDl)); + fdtbfqsFlowPerf_t flowStatsUl; + flowStatsUl.flowStart = Simulator::Now (); + flowStatsUl.packetArrivalRate = 0; + flowStatsUl.tokenGenerationRate = mbrUlInBytes; + flowStatsUl.tokenPoolSize = 0; + flowStatsUl.maxTokenPoolSize = m_tokenPoolSize; + flowStatsUl.counter = 0; + flowStatsUl.burstCredit = m_creditLimit; // bytes + flowStatsUl.debtLimit = m_debtLimit; // bytes + flowStatsUl.creditableThreshold = m_creditableThreshold; + m_flowStatsUl.insert (std::pair (params.m_rnti, flowStatsUl)); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + } + + return; +} + + +void +FdTbfqFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdTbfqFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +FdTbfqFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +FdTbfqFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdTbfqFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +FdTbfqFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < FdTbfqType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + + +int +FdTbfqFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + +void +FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + RefreshDlCqiMaps (); + + // update token pool, counter and bank size + std::map ::iterator itStats; + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + if ( (*itStats).second.tokenGenerationRate / 1000 + (*itStats).second.tokenPoolSize > (*itStats).second.maxTokenPoolSize ) + { + (*itStats).second.counter += (*itStats).second.tokenGenerationRate / 1000 - ( (*itStats).second.maxTokenPoolSize - (*itStats).second.tokenPoolSize ); + (*itStats).second.tokenPoolSize = (*itStats).second.maxTokenPoolSize; + bankSize += (*itStats).second.tokenGenerationRate / 1000 - ( (*itStats).second.maxTokenPoolSize - (*itStats).second.tokenPoolSize ); + } + else + { + (*itStats).second.tokenPoolSize += (*itStats).second.tokenGenerationRate / 1000; + } + } + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + std::set allocatedRnti; // store UEs which are already assigned RBGs + std::set allocatedRbg; // store RBGs which are already allocated to UE + + int totalRbg = 0; + while (totalRbg < rbgNum) + { + // select UE with largest metric + std::map ::iterator it; + std::map ::iterator itMax = m_flowStatsDl.end (); + double metricMax; + bool firstRnti = true; + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++) + { + if (LcActivePerFlow ((*it).first) == 0) + { + continue; + } + + std::set ::iterator rnti; + rnti = allocatedRnti.find((*it).first); + if (rnti != allocatedRnti.end ()) // already allocated RBGs to this UE + { + continue; + } + + double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); + + if (firstRnti == true) + { + metricMax = metric; + itMax = it; + firstRnti = false; + continue; + } + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } + } // end for m_flowStatsDl + + if (itMax == m_flowStatsDl.end()) + { + // all UEs are allocated RBG + break; + } + + // mark this UE as "allocated" + allocatedRnti.insert((*itMax).first); + + // calculate the maximum number of byte that the scheduler can assigned to this UE + uint32_t budget = 0; + if ( bankSize > 0 ) + { + budget = (*itMax).second.counter - (*itMax).second.debtLimit; + if ( budget > (*itMax).second.burstCredit ) + budget = (*itMax).second.burstCredit; + if ( budget > bankSize ) + budget = bankSize; + } + budget = budget + (*itMax).second.tokenPoolSize; + + // calcualte how much bytes this UE actally need + if (budget == 0) + { + // there are no tokens for this UE + continue; + } + else + { + // calculate rlc buffer size + uint32_t rlcBufSize; + uint8_t lcid; + std::map::iterator itRlcBuf; + for (itRlcBuf = m_rlcBufferReq.begin (); itRlcBuf != m_rlcBufferReq.end (); itRlcBuf++) + { + if ( (*itRlcBuf).first.m_rnti == (*itMax).first ) + lcid = (*itRlcBuf).first.m_lcId; + } + LteFlowId_t flow ((*itMax).first, lcid); + itRlcBuf = m_rlcBufferReq.find (flow); + if (itRlcBuf!=m_rlcBufferReq.end ()) + rlcBufSize = (*itRlcBuf).second.m_rlcTransmissionQueueSize + (*itRlcBuf).second.m_rlcRetransmissionQueueSize + (*itRlcBuf).second.m_rlcStatusPduSize; + if ( budget > rlcBufSize ) + budget = rlcBufSize; + } + + // assign RBGs to this UE + uint32_t bytesTxed = 0; + uint32_t bytesTxedTmp = 0; + int rbgIndex; + while ( bytesTxed <= budget ) + { + totalRbg++; + + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*itMax).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMax).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + + // find RBG with largest achievableRate + double achievableRateMax = 0.0; + rbgIndex = rbgNum; + for (int k = 0; k < rbgNum; k++) + { + std::set ::iterator rbg; + rbg = allocatedRbg.find (k); + if (rbg != allocatedRbg.end ()) // RBGs are already allocated to this UE + continue; + + std::vector sbCqi; + if (itCqi == m_a30CqiRxed.end ()) + { + for (uint8_t k = 0; k < nLayer; k++) + { + sbCqi.push_back (1); // start with lowest value + } + } + else + { + sbCqi = (*itCqi).second.m_higherLayerSelected.at (k).m_sbCqi; + } + uint8_t cqi1 = sbCqi.at (0); + uint8_t cqi2 = 1; + if (sbCqi.size () > 1) + { + cqi2 = sbCqi.at (1); + } + + if ((cqi1 > 0)||(cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + { + if (LcActivePerFlow ((*itMax).first) > 0) + { + // this UE has data to transmit + double achievableRate = 0.0; + for (uint8_t j = 0; j < nLayer; j++) + { + uint8_t mcs = 0; + if (sbCqi.size () > j) + { + mcs = m_amc->GetMcsFromCqi (sbCqi.at (j)); + } + else + { + // no info on this subband -> worst MCS + mcs = 0; + } + achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI + } + + if ( achievableRate > achievableRateMax ) + { + achievableRateMax = achievableRate; + rbgIndex = k; + } + } // end of LcActivePerFlow + } // end of cqi + } // end of for rbgNum + + if ( rbgIndex == rbgNum) // impossible + { + // all RBGs are already assigned + totalRbg = rbgNum; + break; + } + else + { + // mark this UE as "allocated" + allocatedRbg.insert (rbgIndex); + } + + // assign this RBG to UE + std::map >::iterator itMap; + itMap = allocationMap.find ((*itMax).first); + uint16_t RbgPerRnti; + if (itMap == allocationMap.end ()) + { + // insert new element + std::vector tempMap; + tempMap.push_back (rbgIndex); + allocationMap.insert (std::pair > ((*itMax).first, tempMap)); + itMap = allocationMap.find ((*itMax).first); // point itMap to the first RBGs assigned to this UE + } + else + { + (*itMap).second.push_back (rbgIndex); + } + + RbgPerRnti = (*itMap).second.size(); + + // calculate tb size + std::vector worstCqi (2, 15); + if (itCqi != m_a30CqiRxed.end ()) + { + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + if ((*itCqi).second.m_higherLayerSelected.size () > (*itMap).second.at (k)) + { + for (uint8_t j = 0; j < nLayer; j++) + { + if ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.size () > j) + { + if (((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)) < worstCqi.at (j)) + { + worstCqi.at (j) = ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)); + } + } + else + { + // no CQI for this layer of this suband -> worst one + worstCqi.at (j) = 1; + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + + bytesTxedTmp = bytesTxed; + bytesTxed = 0; + for (uint8_t j = 0; j < nLayer; j++) + { + int tbSize = (m_amc->GetTbSizeFromMcs (m_amc->GetMcsFromCqi (worstCqi.at (j)), RbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + bytesTxed += tbSize; + } + + } // end of while() + + // remove and unmark last RBG assigned to UE + if ( bytesTxed > budget ) + { + std::map >::iterator itMap; + itMap = allocationMap.find ((*itMax).first); + (*itMap).second.pop_back (); + allocatedRbg.erase (rbgIndex); + bytesTxed = bytesTxedTmp; // recovery bytesTxed + totalRbg--; + } + + // update UE stats + if ( bytesTxed <= (*itMax).second.tokenPoolSize ) + { + (*itMax).second.tokenPoolSize -= bytesTxed; + } + else + { + (*itMax).second.counter = (*itMax).second.counter - ( bytesTxed - (*itMax).second.tokenPoolSize ); + (*itMax).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*itMax).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*itMax).second.tokenPoolSize ); + } + } // end of RBGs + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + uint16_t RgbPerRnti = (*itMap).second.size (); + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector worstCqi (2, 15); + if (itCqi != m_a30CqiRxed.end ()) + { + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + if ((*itCqi).second.m_higherLayerSelected.size () > (*itMap).second.at (k)) + { + for (uint8_t j = 0; j < nLayer; j++) + { + if ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.size () > j) + { + if (((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)) < worstCqi.at (j)) + { + worstCqi.at (j) = ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)); + } + } + else + { + // no CQI for this layer of this suband -> worst one + worstCqi.at (j) = 1; + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + uint32_t bytesTxed = 0; + for (uint8_t j = 0; j < nLayer; j++) + { + newDci.m_mcs.push_back (m_amc->GetMcsFromCqi (worstCqi.at (j))); + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (j), RgbPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + bytesTxed += tbSize; + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) && + (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + itMap++; + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + m_schedSapUser->SchedDlConfigInd (ret); + + return; +} + +void +FdTbfqFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdTbfqFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +FdTbfqFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +FdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +FdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdTbfqFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +FdTbfqFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +FdTbfqFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("FdTbfqFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +FdTbfqFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +FdTbfqFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +FdTbfqFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +FdTbfqFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +FdTbfqFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/fdtbfq-ff-mac-scheduler.h b/src/lte/model/fdtbfq-ff-mac-scheduler.h new file mode 100644 index 000000000..fc706a9f0 --- /dev/null +++ b/src/lte/model/fdtbfq-ff-mac-scheduler.h @@ -0,0 +1,235 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef FDTBFQ_FF_MAC_SCHEDULER_H +#define FDTBFQ_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ns3 { + +/** + * Flow information + */ +struct fdtbfqsFlowPerf_t +{ + Time flowStart; + uint64_t packetArrivalRate; /// packet arrival rate( byte/s) + uint64_t tokenGenerationRate; /// token generation rate ( byte/s ) + uint32_t tokenPoolSize; /// current size of token pool (byte) + uint32_t maxTokenPoolSize; /// maximum size of token pool (byte) + int counter; /// the number of token borrow or given to token bank + uint32_t burstCredit; /// the maximum number of tokens connection i can borrow from the bank each time + int debtLimit; /// counter threshold that the flow cannot further borrow tokens from bank + uint32_t creditableThreshold; /// the flow cannot borrow token from bank until the number of token it has deposited to bank reaches this threshold +}; + + +/** + * \ingroup lte + + * \brief Implements the SCHED SAP and CSCHED SAP for a Frequency Domain Token Bank Fair Queue scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class FdTbfqFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + FdTbfqFfMacScheduler (); + + /** + * Destructor + */ + virtual ~FdTbfqFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class FdTbfqSchedulerMemberCschedSapProvider; + friend class FdTbfqSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Map of UE statistics (per RNTI basis) in downlink + */ + std::map m_flowStatsDl; + + /* + * Map of UE statistics (per RNTI basis) + */ + std::map m_flowStatsUl; + + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + + double m_timeWindow; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs + + uint64_t bankSize; // the number of bytes in token bank + + int m_debtLimit; // flow debt limit (byte) + + uint32_t m_creditLimit; // flow credit limit (byte) + + uint32_t m_tokenPoolSize; // maximum size of token pool (byte) + + uint32_t m_creditableThreshold; // threshold of flow credit +}; + +} // namespace ns3 + +#endif /* FDTBFQ_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/model/pss-ff-mac-scheduler.cc b/src/lte/model/pss-ff-mac-scheduler.cc new file mode 100644 index 000000000..0a6283146 --- /dev/null +++ b/src/lte/model/pss-ff-mac-scheduler.cc @@ -0,0 +1,1624 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("PssFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int PssType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + +NS_OBJECT_ENSURE_REGISTERED (PssFfMacScheduler); + +class PssSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + PssSchedulerMemberCschedSapProvider (PssFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + PssSchedulerMemberCschedSapProvider (); + PssFfMacScheduler* m_scheduler; +}; + +PssSchedulerMemberCschedSapProvider::PssSchedulerMemberCschedSapProvider () +{ +} + +PssSchedulerMemberCschedSapProvider::PssSchedulerMemberCschedSapProvider (PssFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +PssSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +PssSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +PssSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +PssSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +PssSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class PssSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + PssSchedulerMemberSchedSapProvider (PssFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + PssSchedulerMemberSchedSapProvider (); + PssFfMacScheduler* m_scheduler; +}; + + + +PssSchedulerMemberSchedSapProvider::PssSchedulerMemberSchedSapProvider () +{ +} + + +PssSchedulerMemberSchedSapProvider::PssSchedulerMemberSchedSapProvider (PssFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +PssSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +PssSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +PssFfMacScheduler::PssFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_timeWindow (99.0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new PssSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new PssSchedulerMemberSchedSapProvider (this); +} + +PssFfMacScheduler::~PssFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +PssFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +PssFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::PssFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&PssFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + .AddAttribute ("PssFdSchedulerType", + "FD scheduler in PSS (default value is PFsch)", + StringValue ("PFsch"), + MakeStringAccessor (&PssFfMacScheduler::m_fdSchedulerType), + MakeStringChecker ()) + .AddAttribute ("nMux", + "The number of UE selected by TD scheduler (default value is 0)", + UintegerValue (0), + MakeUintegerAccessor (&PssFfMacScheduler::m_nMux), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +PssFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +PssFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +PssFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +PssFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +PssFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +PssFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +PssFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::map ::iterator it; + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + double tbrDlInBytes = params.m_logicalChannelConfigList.at (i).m_eRabGuaranteedBitrateDl / 8; // byte/s + double tbrUlInBytes = params.m_logicalChannelConfigList.at (i).m_eRabGuaranteedBitrateUl / 8; // byte/s + + pssFlowPerf_t flowStatsDl; + flowStatsDl.flowStart = Simulator::Now (); + flowStatsDl.totalBytesTransmitted = 0; + flowStatsDl.lastTtiBytesTransmitted = 0; + flowStatsDl.lastAveragedThroughput = 1; + flowStatsDl.secondLastAveragedThroughput = 1; + flowStatsDl.targetThroughput = tbrDlInBytes; + m_flowStatsDl.insert (std::pair (params.m_rnti, flowStatsDl)); + pssFlowPerf_t flowStatsUl; + flowStatsUl.flowStart = Simulator::Now (); + flowStatsUl.totalBytesTransmitted = 0; + flowStatsUl.lastTtiBytesTransmitted = 0; + flowStatsUl.lastAveragedThroughput = 1; + flowStatsUl.secondLastAveragedThroughput = 1; + flowStatsUl.targetThroughput = tbrUlInBytes; + m_flowStatsUl.insert (std::pair (params.m_rnti, flowStatsUl)); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + } + + return; +} + + +void +PssFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +PssFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +PssFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +PssFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +PssFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +PssFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < PssType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + + +int +PssFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + +void +PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + + RefreshDlCqiMaps (); + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + std::map ::iterator it; + + // schedulability check + std::map ueSet; + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++) + { + if( LcActivePerFlow ((*it).first) > 0 ) + { + ueSet.insert(std::pair ((*it).first, (*it).second)); + } + } + + if (ueSet.size() == 0) + { + // no data in RLC buffer + return; + } + + // Time Domain scheduler + std::vector > ueSet1; + std::vector > ueSet2; + for (it = ueSet.begin (); it != ueSet.end (); it++) + { + double metric; + if ((*it).second.lastAveragedThroughput < (*it).second.targetThroughput ) + { + // calculate TD BET metric + metric = 1 / (*it).second.lastAveragedThroughput; + ueSet1.push_back(std::pair (metric, (*it).first)); + } + else + { + // calculate TD PF metric + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find ((*it).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*it).first); + if (itTxMode == m_uesTxMode.end()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + uint8_t wbCqi = 0; + if (itCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } + else + { + wbCqi = (*itCqi).second; + } + + if (wbCqi > 0) + { + if (LcActivePerFlow ((*it).first) > 0) + { + // this UE has data to transmit + double achievableRate = 0.0; + for (uint8_t k = 0; k < nLayer; k++) + { + uint8_t mcs = 0; + mcs = m_amc->GetMcsFromCqi (wbCqi); + achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI + } + + metric = achievableRate / (*it).second.lastAveragedThroughput; + } + } // end of wbCqi + + ueSet2.push_back(std::pair (metric, (*it).first)); + } + }// end of ueSet + + // sorting UE in ueSet1 and ueSet1 in descending order based on their metric value + std::sort (ueSet1.rbegin (), ueSet1.rend ()); + std::sort (ueSet2.rbegin (), ueSet2.rend ()); + + std::map tdUeSet; + uint32_t nMux; + if ( m_nMux > 0) + nMux = m_nMux; + else + { + // select half number of UE + if (ueSet1.size() + ueSet2.size() <=2 ) + nMux = 1; + else + nMux = (int)((ueSet1.size() + ueSet2.size()) / 2) ; // TD scheduler only transfers half selected UE per RTT to TD scheduler + } + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it--) + { + std::vector >::iterator itSet; + for (itSet = ueSet1.begin (); itSet != ueSet1.end () && nMux != 0; itSet++) + { + std::map ::iterator itUe; + itUe = m_flowStatsDl.find((*itSet).second); + tdUeSet.insert(std::pair ( (*itUe).first, (*itUe).second ) ); + nMux--; + } + + if (nMux == 0) + break; + + for (itSet = ueSet2.begin (); itSet != ueSet2.end () && nMux != 0; itSet++) + { + std::map ::iterator itUe; + itUe = m_flowStatsDl.find((*itSet).second); + tdUeSet.insert(std::pair ( (*itUe).first, (*itUe).second ) ); + nMux--; + } + + if (nMux == 0) + break; + + } // end of m_flowStatsDl + + if ( m_fdSchedulerType.compare("CoItA") == 0) + { + // FD scheduler: Carrier over Interference to Average (CoItA) + std::map < uint16_t, uint8_t > sbCqiSum; + for (it = tdUeSet.begin (); it != tdUeSet.end (); it++) + { + uint8_t sum = 0; + for (int i = 0; i < rbgNum; i++) + { + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*it).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*it).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector sbCqis; + if (itCqi == m_a30CqiRxed.end ()) + { + for (uint8_t k = 0; k < nLayer; k++) + { + sbCqis.push_back (1); // start with lowest value + } + } + else + { + sbCqis = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; + } + + uint8_t cqi1 = sbCqis.at (0); + uint8_t cqi2 = 1; + if (sbCqis.size () > 1) + { + cqi2 = sbCqis.at (1); + } + + uint8_t sbCqi; + if ((cqi1 > 0)||(cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + { + for (uint8_t k = 0; k < nLayer; k++) + { + if (sbCqis.size () > k) + { + sbCqi = sbCqis.at(k); + } + else + { + // no info on this subband + sbCqi = 0; + } + sum += sbCqi; + } + } // end if cqi + }// end of rbgNum + + sbCqiSum.insert (std::pair ((*it).first, sum)); + }// end tdUeSet + + for (int i = 0; i < rbgNum; i++) + { + std::map ::iterator itMax = tdUeSet.end (); + double metricMax = 0.0; + for (it = tdUeSet.begin (); it != tdUeSet.end (); it++) + { + // calculate PF weigth + double weight = (*it).second.targetThroughput / (*it).second.lastAveragedThroughput; + if (weight < 1.0) + weight = 1.0; + + std::map < uint16_t, uint8_t>::iterator itSbCqiSum; + itSbCqiSum = sbCqiSum.find((*it).first); + + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*it).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*it).first); + if (itTxMode == m_uesTxMode.end()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector sbCqis; + if (itCqi == m_a30CqiRxed.end ()) + { + for (uint8_t k = 0; k < nLayer; k++) + { + sbCqis.push_back (1); // start with lowest value + } + } + else + { + sbCqis = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; + } + + uint8_t cqi1 = sbCqis.at( 0); + uint8_t cqi2 = 1; + if (sbCqis.size () > 1) + { + cqi2 = sbCqis.at(1); + } + + uint8_t sbCqi; + double colMetric = 0.0; + if ((cqi1 > 0)||(cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + { + for (uint8_t k = 0; k < nLayer; k++) + { + if (sbCqis.size () > k) + { + sbCqi = sbCqis.at(k); + } + else + { + // no info on this subband + sbCqi = 0; + } + colMetric += (double)sbCqi / (double)(*itSbCqiSum).second; + } + } // end if cqi + + double metric; + if (colMetric != 0) + metric= weight * colMetric; + else + metric = 1; + + if (metric > metricMax ) + { + metricMax = metric; + itMax = it; + } + } // end of tdUeSet + + if (itMax == m_flowStatsDl.end ()) + { + // no UE available for downlink + return; + } + else + { + // assign all RBGs to this UE + std::vector tempMap; + for (int i = 0; i < rbgNum; i++) + { + tempMap.push_back (i); + } + allocationMap.insert (std::pair > ((*itMax).first, tempMap)); + } + }// end of rbgNum + + }// end of CoIta + + if ( m_fdSchedulerType.compare("PFsch") == 0) + { + // FD scheduler: Proportional Fair scheduled (PFsch) + for (int i = 0; i < rbgNum; i++) + { + std::map ::iterator itMax = tdUeSet.end (); + double metricMax = 0.0; + for (it = tdUeSet.begin (); it != tdUeSet.end (); it++) + { + // calculate PF weigth + double weight = (*it).second.targetThroughput / (*it).second.lastAveragedThroughput; + if (weight < 1.0) + weight = 1.0; + + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*it).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*it).first); + if (itTxMode == m_uesTxMode.end()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector sbCqis; + if (itCqi == m_a30CqiRxed.end ()) + { + for (uint8_t k = 0; k < nLayer; k++) + { + sbCqis.push_back (1); // start with lowest value + } + } + else + { + sbCqis = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; + } + + uint8_t cqi1 = sbCqis.at(0); + uint8_t cqi2 = 1; + if (sbCqis.size () > 1) + { + cqi2 = sbCqis.at(1); + } + + double schMetric = 0.0; + if ((cqi1 > 0)||(cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + { + double achievableRate = 0.0; + for (uint8_t k = 0; k < nLayer; k++) + { + uint8_t mcs = 0; + if (sbCqis.size () > k) + { + mcs = m_amc->GetMcsFromCqi (sbCqis.at (k)); + } + else + { + // no info on this subband -> worst MCS + mcs = 0; + } + achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI + } + schMetric = achievableRate / (*it).second.secondLastAveragedThroughput; + } // end if cqi + + double metric; + metric= weight * schMetric; + + if (metric > metricMax ) + { + metricMax = metric; + itMax = it; + } + } // end of tdUeSet + + if (itMax == m_flowStatsDl.end ()) + { + // no UE available for downlink + return; + } + else + { + // assign all RBGs to this UE + std::vector tempMap; + for (int i = 0; i < rbgNum; i++) + { + tempMap.push_back (i); + } + allocationMap.insert (std::pair > ((*itMax).first, tempMap)); + } + + }// end of rbgNum + + } // end of PFsch + + + // reset TTI stats of users + std::map ::iterator itStats; + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + (*itStats).second.lastTtiBytesTransmitted = 0; + } + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + uint16_t rbgPerRnti = (*itMap).second.size (); + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector worstCqi (2, 15); + if (itCqi != m_a30CqiRxed.end ()) + { + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + if ((*itCqi).second.m_higherLayerSelected.size () > (*itMap).second.at (k)) + { + for (uint8_t j = 0; j < nLayer; j++) + { + if ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.size () > j) + { + if (((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)) < worstCqi.at (j)) + { + worstCqi.at (j) = ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)); + } + } + else + { + // no CQI for this layer of this suband -> worst one + worstCqi.at (j) = 1; + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + uint32_t bytesTxed = 0; + for (uint8_t j = 0; j < nLayer; j++) + { + newDci.m_mcs.push_back (m_amc->GetMcsFromCqi (worstCqi.at (j))); + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (j), rbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + bytesTxed += tbSize; + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) && + (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + // update UE stats + std::map ::iterator it; + it = m_flowStatsDl.find ((*itMap).first); + if (it != m_flowStatsDl.end ()) + { + (*it).second.lastTtiBytesTransmitted = bytesTxed; + } + else + { + NS_LOG_DEBUG (this << " No Stats for this allocated UE"); + } + + itMap++; + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + + // update UEs stats + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + std::map ::iterator itUeScheduleted = tdUeSet.end(); + itUeScheduleted = tdUeSet.find((*itStats).first); + if (itUeScheduleted != tdUeSet.end()) + { + (*itStats).second.secondLastAveragedThroughput = ((1.0 - (1 / m_timeWindow)) * (*itStats).second.secondLastAveragedThroughput) + ((1 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); + } + + (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted; + // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term Evolution, Ed Wiley) + (*itStats).second.lastAveragedThroughput = ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); + (*itStats).second.lastTtiBytesTransmitted = 0; + } + + m_schedSapUser->SchedDlConfigInd (ret); + + + return; +} + +void +PssFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +PssFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +PssFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +PssFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +PssFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +PssFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +PssFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +PssFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("PssFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +PssFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +PssFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +PssFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +PssFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +PssFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/pss-ff-mac-scheduler.h b/src/lte/model/pss-ff-mac-scheduler.h new file mode 100644 index 000000000..0a3701864 --- /dev/null +++ b/src/lte/model/pss-ff-mac-scheduler.h @@ -0,0 +1,225 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef PSS_FF_MAC_SCHEDULER_H +#define PSS_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ns3 { + +/** + * Flow information + */ +struct pssFlowPerf_t +{ + Time flowStart; + unsigned long totalBytesTransmitted; /// Total bytes send by eNb for this UE + unsigned int lastTtiBytesTransmitted; /// Total bytes send by eNB in last tti for this UE + double lastAveragedThroughput; /// Past average throughput + double secondLastAveragedThroughput; + double targetThroughput; /// Target throughput +}; + + +/** + * \ingroup lte + + * \brief Implements the SCHED SAP and CSCHED SAP for a Priority Set scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class PssFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + PssFfMacScheduler (); + + /** + * Destructor + */ + virtual ~PssFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class PssSchedulerMemberCschedSapProvider; + friend class PssSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Map of UE statistics (per RNTI basis) in downlink + */ + std::map m_flowStatsDl; + + /* + * Map of UE statistics (per RNTI basis) + */ + std::map m_flowStatsUl; + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + double m_timeWindow; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs + + std::string m_fdSchedulerType; + + uint32_t m_nMux; // TD scheduler selects nMux UEs and transfer them to FD scheduler + +}; + +} // namespace ns3 + +#endif /* PSS_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/model/tdbet-ff-mac-scheduler.cc b/src/lte/model/tdbet-ff-mac-scheduler.cc new file mode 100644 index 000000000..8cf5e3d2d --- /dev/null +++ b/src/lte/model/tdbet-ff-mac-scheduler.cc @@ -0,0 +1,1261 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("TdBetFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int TdBetType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + +NS_OBJECT_ENSURE_REGISTERED (TdBetFfMacScheduler); + +class TdBetSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + TdBetSchedulerMemberCschedSapProvider (TdBetFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + TdBetSchedulerMemberCschedSapProvider (); + TdBetFfMacScheduler* m_scheduler; +}; + +TdBetSchedulerMemberCschedSapProvider::TdBetSchedulerMemberCschedSapProvider () +{ +} + +TdBetSchedulerMemberCschedSapProvider::TdBetSchedulerMemberCschedSapProvider (TdBetFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +TdBetSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +TdBetSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +TdBetSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +TdBetSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +TdBetSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class TdBetSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + TdBetSchedulerMemberSchedSapProvider (TdBetFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + TdBetSchedulerMemberSchedSapProvider (); + TdBetFfMacScheduler* m_scheduler; +}; + + + +TdBetSchedulerMemberSchedSapProvider::TdBetSchedulerMemberSchedSapProvider () +{ +} + + +TdBetSchedulerMemberSchedSapProvider::TdBetSchedulerMemberSchedSapProvider (TdBetFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +TdBetSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +TdBetFfMacScheduler::TdBetFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_timeWindow (99.0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new TdBetSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new TdBetSchedulerMemberSchedSapProvider (this); +} + +TdBetFfMacScheduler::~TdBetFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +TdBetFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +TdBetFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TdBetFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&TdBetFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +TdBetFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +TdBetFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +TdBetFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +TdBetFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +TdBetFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +TdBetFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +TdBetFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::map ::iterator it; + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + tdbetsFlowPerf_t flowStatsDl; + flowStatsDl.flowStart = Simulator::Now (); + flowStatsDl.totalBytesTransmitted = 0; + flowStatsDl.lastTtiBytesTransmitted = 0; + flowStatsDl.lastAveragedThroughput = 1; + m_flowStatsDl.insert (std::pair (params.m_rnti, flowStatsDl)); + tdbetsFlowPerf_t flowStatsUl; + flowStatsUl.flowStart = Simulator::Now (); + flowStatsUl.totalBytesTransmitted = 0; + flowStatsUl.lastTtiBytesTransmitted = 0; + flowStatsUl.lastAveragedThroughput = 1; + m_flowStatsUl.insert (std::pair (params.m_rnti, flowStatsUl)); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + } + + return; +} + +void +TdBetFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdBetFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +TdBetFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +TdBetFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdBetFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +TdBetFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < TdBetType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + + +int +TdBetFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + +void +TdBetFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + + RefreshDlCqiMaps (); + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + std::map ::iterator it; + std::map ::iterator itMax = m_flowStatsDl.end (); + double metricMax = 0.0; + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++) + { + double metric = 1 / (*it).second.lastAveragedThroughput; + + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } + } // end for m_flowStatsDl + + if (itMax == m_flowStatsDl.end ()) + { + // no UE available for downlink + return; + } + else + { + // assign all RBGs to this UE + std::vector tempMap; + for (int i = 0; i < rbgNum; i++) + { + tempMap.push_back (i); + } + allocationMap.insert (std::pair > ((*itMax).first, tempMap)); + } + + // reset TTI stats of users + std::map ::iterator itStats; + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + (*itStats).second.lastTtiBytesTransmitted = 0; + } + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find ((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + uint32_t bytesTxed = 0; + for (uint8_t i = 0; i < nLayer; i++) + { + if (itCqi == m_p10CqiRxed.end ()) + { + newDci.m_mcs.push_back (0); // no info on this user -> lowest MCS + } + else + { + newDci.m_mcs.push_back ( m_amc->GetMcsFromCqi ((*itCqi).second) ); + } + } + + for (uint8_t i = 0; i < nLayer; i++) + { + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (0), rbgNum * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + bytesTxed += tbSize; + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) && + (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + // update UE stats + std::map ::iterator it; + it = m_flowStatsDl.find ((*itMap).first); + if (it != m_flowStatsDl.end ()) + { + (*it).second.lastTtiBytesTransmitted = bytesTxed; + } + else + { + NS_LOG_DEBUG (this << " No Stats for this allocated UE"); + } + + itMap++; + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + + // update UEs stats + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted; + // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term Evolution, Ed Wiley) + (*itStats).second.lastAveragedThroughput = ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); + (*itStats).second.lastTtiBytesTransmitted = 0; + } + + m_schedSapUser->SchedDlConfigInd (ret); + + + return; +} + +void +TdBetFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdBetFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +TdBetFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +TdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + std::map ::iterator itStats; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + // update TTI UE stats + itStats = m_flowStatsUl.find ((*it).first); + if (itStats != m_flowStatsUl.end ()) + { + (*itStats).second.lastTtiBytesTransmitted = uldci.m_tbSize; +// NS_LOG_DEBUG (this << " UE bytes txed " << (*it).second.lastTtiBytesTransmitted); + + + } + else + { + NS_LOG_DEBUG (this << " No Stats for this allocated UE"); + } + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + + // Update global UE stats + // update UEs stats + for (itStats = m_flowStatsUl.begin (); itStats != m_flowStatsUl.end (); itStats++) + { + (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted; + // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term Evolution, Ed Wiley) + (*itStats).second.lastAveragedThroughput = ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); + // NS_LOG_DEBUG (this << " UE tot bytes " << (*itStats).second.totalBytesTransmitted); + // NS_LOG_DEBUG (this << " UE avg thr " << (*itStats).second.lastAveragedThroughput); + (*itStats).second.lastTtiBytesTransmitted = 0; + } + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +TdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdBetFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdBetFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +TdBetFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("TdBetFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +TdBetFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +TdBetFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +TdBetFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +TdBetFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +TdBetFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/tdbet-ff-mac-scheduler.h b/src/lte/model/tdbet-ff-mac-scheduler.h new file mode 100644 index 000000000..16e7845bc --- /dev/null +++ b/src/lte/model/tdbet-ff-mac-scheduler.h @@ -0,0 +1,216 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef TDBET_FF_MAC_SCHEDULER_H +#define TDBET_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ns3 { + + +struct tdbetsFlowPerf_t +{ + Time flowStart; + unsigned long totalBytesTransmitted; + unsigned int lastTtiBytesTransmitted; + double lastAveragedThroughput; +}; + +/** + * \ingroup lte + + * \brief Implements the SCHED SAP and CSCHED SAP for a Time Domain Blind Equal Throughput scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class TdBetFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + TdBetFfMacScheduler (); + + /** + * Destructor + */ + virtual ~TdBetFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class TdBetSchedulerMemberCschedSapProvider; + friend class TdBetSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Map of UE statistics (per RNTI basis) in downlink + */ + std::map m_flowStatsDl; + + /* + * Map of UE statistics (per RNTI basis) + */ + std::map m_flowStatsUl; + + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + + double m_timeWindow; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs +}; + +} // namespace ns3 + +#endif /* TDBET_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/model/tdmt-ff-mac-scheduler.cc b/src/lte/model/tdmt-ff-mac-scheduler.cc new file mode 100644 index 000000000..db67cd9c1 --- /dev/null +++ b/src/lte/model/tdmt-ff-mac-scheduler.cc @@ -0,0 +1,1232 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("TdMtFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int TdMtType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + + +NS_OBJECT_ENSURE_REGISTERED (TdMtFfMacScheduler); + + + +class TdMtSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + TdMtSchedulerMemberCschedSapProvider (TdMtFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + TdMtSchedulerMemberCschedSapProvider (); + TdMtFfMacScheduler* m_scheduler; +}; + +TdMtSchedulerMemberCschedSapProvider::TdMtSchedulerMemberCschedSapProvider () +{ +} + +TdMtSchedulerMemberCschedSapProvider::TdMtSchedulerMemberCschedSapProvider (TdMtFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +TdMtSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +TdMtSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +TdMtSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +TdMtSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +TdMtSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class TdMtSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + TdMtSchedulerMemberSchedSapProvider (TdMtFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + TdMtSchedulerMemberSchedSapProvider (); + TdMtFfMacScheduler* m_scheduler; +}; + + + +TdMtSchedulerMemberSchedSapProvider::TdMtSchedulerMemberSchedSapProvider () +{ +} + + +TdMtSchedulerMemberSchedSapProvider::TdMtSchedulerMemberSchedSapProvider (TdMtFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +TdMtSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +TdMtFfMacScheduler::TdMtFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new TdMtSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new TdMtSchedulerMemberSchedSapProvider (this); +} + +TdMtFfMacScheduler::~TdMtFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +TdMtFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +TdMtFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TdMtFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&TdMtFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +TdMtFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +TdMtFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +TdMtFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +TdMtFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +TdMtFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +TdMtFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +TdMtFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::set::iterator it; + + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + m_flowStatsDl.insert (params.m_rnti); + m_flowStatsUl.insert (params.m_rnti); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + + } + + return; +} + + +void +TdMtFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdMtFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +TdMtFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +TdMtFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdMtFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +TdMtFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < TdMtType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + + +int +TdMtFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + + +void +TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + + RefreshDlCqiMaps (); + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + std::set ::iterator it; + std::set ::iterator itMax = m_flowStatsDl.end (); + double metricMax = 0.0; + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++) + { + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find ((*it)); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*it)); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it)); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + uint8_t wbCqi = 0; + if (itCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } + else + { + wbCqi = (*itCqi).second; + } + + if (wbCqi > 0) + { + if (LcActivePerFlow (*it) > 0) + { + // this UE has data to transmit + double achievableRate = 0.0; + for (uint8_t k = 0; k < nLayer; k++) + { + uint8_t mcs = 0; + mcs = m_amc->GetMcsFromCqi (wbCqi); + achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI + } + + double metric = achievableRate; + + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } + } + } // end of wbCqi + } // end for m_flowStatsDl + + if (itMax == m_flowStatsDl.end ()) + { + // no UE available for downlink + return; + } + else + { + // assign all RBGs to this UE + std::vector tempMap; + for (int i = 0; i < rbgNum; i++) + { + tempMap.push_back (i); + } + allocationMap.insert (std::pair > ((*itMax), tempMap)); + } + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + for (uint8_t j = 0; j < nLayer; j++) + { + if (itCqi == m_p10CqiRxed.end ()) + { + newDci.m_mcs.push_back (0); // no info on this user -> lowest MCS + } + else + { + newDci.m_mcs.push_back ( m_amc->GetMcsFromCqi ((*itCqi).second) ); + } + // allocate all RBGs to this UE + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (j), rbgNum * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) && + (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + + itMap++; + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + m_schedSapUser->SchedDlConfigInd (ret); + + return; +} + +void +TdMtFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdMtFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +TdMtFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +TdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +TdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdMtFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdMtFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +TdMtFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("TdMtFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +TdMtFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +TdMtFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +TdMtFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +TdMtFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +TdMtFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/tdmt-ff-mac-scheduler.h b/src/lte/model/tdmt-ff-mac-scheduler.h new file mode 100644 index 000000000..7bdfd344d --- /dev/null +++ b/src/lte/model/tdmt-ff-mac-scheduler.h @@ -0,0 +1,205 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef TDMT_FF_MAC_SCHEDULER_H +#define TDMT_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace ns3 { + +/** + * \ingroup lte + * + * \brief Implements the SCHED SAP and CSCHED SAP for a Time Domain Maximum Throughput scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class TdMtFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + TdMtFfMacScheduler (); + + /** + * Destructor + */ + virtual ~TdMtFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class TdMtSchedulerMemberCschedSapProvider; + friend class TdMtSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Set of UE's RNTI in downlink + */ + std::set m_flowStatsDl; + + /* + * Set of UE's RNTI in uplink + */ + std::set m_flowStatsUl; + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs +}; + +} // namespace ns3 + +#endif /* TDMT_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/model/tdtbfq-ff-mac-scheduler.cc b/src/lte/model/tdtbfq-ff-mac-scheduler.cc new file mode 100644 index 000000000..c14d93beb --- /dev/null +++ b/src/lte/model/tdtbfq-ff-mac-scheduler.cc @@ -0,0 +1,1322 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("TdTbfqFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int TdTbfqType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + +NS_OBJECT_ENSURE_REGISTERED (TdTbfqFfMacScheduler); + +class TdTbfqSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + TdTbfqSchedulerMemberCschedSapProvider (TdTbfqFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + TdTbfqSchedulerMemberCschedSapProvider (); + TdTbfqFfMacScheduler* m_scheduler; +}; + +TdTbfqSchedulerMemberCschedSapProvider::TdTbfqSchedulerMemberCschedSapProvider () +{ +} + +TdTbfqSchedulerMemberCschedSapProvider::TdTbfqSchedulerMemberCschedSapProvider (TdTbfqFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +TdTbfqSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +TdTbfqSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +TdTbfqSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +TdTbfqSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +TdTbfqSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class TdTbfqSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + TdTbfqSchedulerMemberSchedSapProvider (TdTbfqFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + TdTbfqSchedulerMemberSchedSapProvider (); + TdTbfqFfMacScheduler* m_scheduler; +}; + + + +TdTbfqSchedulerMemberSchedSapProvider::TdTbfqSchedulerMemberSchedSapProvider () +{ +} + + +TdTbfqSchedulerMemberSchedSapProvider::TdTbfqSchedulerMemberSchedSapProvider (TdTbfqFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +TdTbfqSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +TdTbfqFfMacScheduler::TdTbfqFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_timeWindow (99.0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new TdTbfqSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new TdTbfqSchedulerMemberSchedSapProvider (this); +} + +TdTbfqFfMacScheduler::~TdTbfqFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +TdTbfqFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +TdTbfqFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TdTbfqFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + .AddAttribute ("DebtLimit", + "Flow debt limit (default -625000 bytes)", + IntegerValue (-625000), + MakeIntegerAccessor (&TdTbfqFfMacScheduler::m_debtLimit), + MakeIntegerChecker ()) + .AddAttribute ("CreditLimit", + "Flow credit limit (default 625000 bytes)", + UintegerValue (625000), + MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_creditLimit), + MakeUintegerChecker ()) + .AddAttribute ("TokenPoolSize", + "The maximum value of flow token pool (default 1 bytes)", + UintegerValue (1), + MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_tokenPoolSize), + MakeUintegerChecker ()) + .AddAttribute ("CreditableThreshold", + "Threshold of flow credit (default 0 bytes)", + UintegerValue (0), + MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_creditableThreshold), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +TdTbfqFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +TdTbfqFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +TdTbfqFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +TdTbfqFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +TdTbfqFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +TdTbfqFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +TdTbfqFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::map ::iterator it; + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + uint64_t mbrDlInBytes = params.m_logicalChannelConfigList.at (i).m_eRabMaximulBitrateDl / 8; // byte/s + uint64_t mbrUlInBytes = params.m_logicalChannelConfigList.at (i).m_eRabMaximulBitrateUl / 8; // byte/s + + tdtbfqsFlowPerf_t flowStatsDl; + flowStatsDl.flowStart = Simulator::Now (); + flowStatsDl.packetArrivalRate = 0; + flowStatsDl.tokenGenerationRate = mbrDlInBytes; + flowStatsDl.tokenPoolSize = 0; + flowStatsDl.maxTokenPoolSize = m_tokenPoolSize; + flowStatsDl.counter = 0; + flowStatsDl.burstCredit = m_creditLimit; // bytes + flowStatsDl.debtLimit = m_debtLimit; // bytes + flowStatsDl.creditableThreshold = m_creditableThreshold; + m_flowStatsDl.insert (std::pair (params.m_rnti, flowStatsDl)); + tdtbfqsFlowPerf_t flowStatsUl; + flowStatsUl.flowStart = Simulator::Now (); + flowStatsUl.packetArrivalRate = 0; + flowStatsUl.tokenGenerationRate = mbrUlInBytes; + flowStatsUl.tokenPoolSize = 0; + flowStatsUl.maxTokenPoolSize = m_tokenPoolSize; + flowStatsUl.counter = 0; + flowStatsUl.burstCredit = m_creditLimit; // bytes + flowStatsUl.debtLimit = m_debtLimit; // bytes + flowStatsUl.creditableThreshold = m_creditableThreshold; + m_flowStatsUl.insert (std::pair (params.m_rnti, flowStatsUl)); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + } + + return; +} + + +void +TdTbfqFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdTbfqFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +TdTbfqFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +TdTbfqFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdTbfqFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +TdTbfqFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < TdTbfqType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + +int +TdTbfqFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + +void +TdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + RefreshDlCqiMaps (); + + // update token pool, counter and bank size + std::map ::iterator itStats; + for (itStats = m_flowStatsDl.begin (); itStats != m_flowStatsDl.end (); itStats++) + { + if ( (*itStats).second.tokenGenerationRate / 1000 + (*itStats).second.tokenPoolSize > (*itStats).second.maxTokenPoolSize ) + { + (*itStats).second.counter += (*itStats).second.tokenGenerationRate / 1000 - ( (*itStats).second.maxTokenPoolSize - (*itStats).second.tokenPoolSize ); + (*itStats).second.tokenPoolSize = (*itStats).second.maxTokenPoolSize; + bankSize += (*itStats).second.tokenGenerationRate / 1000 - ( (*itStats).second.maxTokenPoolSize - (*itStats).second.tokenPoolSize ); + } + else + { + (*itStats).second.tokenPoolSize += (*itStats).second.tokenGenerationRate / 1000; + } + } + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + + // select UE with largest metric + std::map ::iterator it; + std::map ::iterator itMax = m_flowStatsDl.end (); + double metricMax; + bool firstRnti = true; + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++) + { + if (LcActivePerFlow ((*it).first) == 0) + { + continue; + } + + double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); + + if (firstRnti == true) + { + metricMax = metric; + itMax = it; + firstRnti = false; + continue; + } + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } + } // end for m_flowStatsDl + + if (itMax == m_flowStatsDl.end ()) + { + // no UE available for downlink + return; + } + else + { + // assign all RBGs to this UE + std::vector tempMap; + for (int i = 0; i < rbgNum; i++) + { + tempMap.push_back (i); + } + allocationMap.insert (std::pair > ((*itMax).first, tempMap)); + } + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + uint16_t RgbPerRnti = (*itMap).second.size (); + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector worstCqi (2, 15); + if (itCqi != m_a30CqiRxed.end ()) + { + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + if ((*itCqi).second.m_higherLayerSelected.size () > (*itMap).second.at (k)) + { + for (uint8_t j = 0; j < nLayer; j++) + { + if ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.size () > j) + { + if (((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)) < worstCqi.at (j)) + { + worstCqi.at (j) = ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)); + } + } + else + { + // no CQI for this layer of this suband -> worst one + worstCqi.at (j) = 1; + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + uint32_t bytesTxed = 0; + for (uint8_t j = 0; j < nLayer; j++) + { + newDci.m_mcs.push_back (m_amc->GetMcsFromCqi (worstCqi.at (j))); + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (j), RgbPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + bytesTxed += tbSize; + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) + && (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + // update UE stats + std::map ::iterator it; + it = m_flowStatsDl.find ((*itMap).first); + if (it != m_flowStatsDl.end ()) + { + if ( bytesTxed <= (*it).second.tokenPoolSize ) + { + (*it).second.tokenPoolSize -= bytesTxed; + } + else + { + (*it).second.counter = (*it).second.counter - ( bytesTxed - (*it).second.tokenPoolSize ); + (*it).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*it).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*it).second.tokenPoolSize ); + } + } + else + { + NS_LOG_DEBUG (this << " No Stats for this allocated UE"); + } + + itMap++; + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + m_schedSapUser->SchedDlConfigInd (ret); + + + return; +} + +void +TdTbfqFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdTbfqFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +TdTbfqFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +TdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +TdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdTbfqFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TdTbfqFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +TdTbfqFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("TdTbfqFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +TdTbfqFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +TdTbfqFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +TdTbfqFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +TdTbfqFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +TdTbfqFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/tdtbfq-ff-mac-scheduler.h b/src/lte/model/tdtbfq-ff-mac-scheduler.h new file mode 100644 index 000000000..34aaae1c0 --- /dev/null +++ b/src/lte/model/tdtbfq-ff-mac-scheduler.h @@ -0,0 +1,235 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef TDTBFQ_FF_MAC_SCHEDULER_H +#define TDTBFQ_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ns3 { + +/** + * Flow information + */ +struct tdtbfqsFlowPerf_t +{ + Time flowStart; + uint64_t packetArrivalRate; /// packet arrival rate( byte/s) + uint64_t tokenGenerationRate; /// token generation rate ( byte/s ) + uint32_t tokenPoolSize; /// current size of token pool (byte) + uint32_t maxTokenPoolSize; /// maximum size of token pool (byte) + int counter; /// the number of token borrow or given to token bank + uint32_t burstCredit; /// the maximum number of tokens connection i can borrow from the bank each time + int debtLimit; /// counter threshold that the flow cannot further borrow tokens from bank + uint32_t creditableThreshold; /// the flow cannot borrow token from bank until the number of token it has deposited to bank reaches this threshold +}; + + +/** + * \ingroup lte + + * \brief Implements the SCHED SAP and CSCHED SAP for a Frequency Domain Token Bank Fair Queue scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class TdTbfqFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + TdTbfqFfMacScheduler (); + + /** + * Destructor + */ + virtual ~TdTbfqFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class TdTbfqSchedulerMemberCschedSapProvider; + friend class TdTbfqSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Map of UE statistics (per RNTI basis) in downlink + */ + std::map m_flowStatsDl; + + /* + * Map of UE statistics (per RNTI basis) + */ + std::map m_flowStatsUl; + + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + + double m_timeWindow; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs + + uint64_t bankSize; // the number of bytes in token bank + + int m_debtLimit; // flow debt limit (byte) + + uint32_t m_creditLimit; // flow credit limit (byte) + + uint32_t m_tokenPoolSize; // maximum size of token pool (byte) + + uint32_t m_creditableThreshold; // threshold of flow credit +}; + +} // namespace ns3 + +#endif /* TDTBFQ_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/model/tta-ff-mac-scheduler.cc b/src/lte/model/tta-ff-mac-scheduler.cc new file mode 100644 index 000000000..590bcb093 --- /dev/null +++ b/src/lte/model/tta-ff-mac-scheduler.cc @@ -0,0 +1,1308 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#include +#include +#include + +#include +#include +#include +#include + +NS_LOG_COMPONENT_DEFINE ("TtaFfMacScheduler"); + +// value for SINR outside the range defined by LTE, used to indicate that there +// is no CQI for this element +#define NO_SINR -5000 + +namespace ns3 { + +int TtaType0AllocationRbg[4] = { + 10, // RGB size 1 + 26, // RGB size 2 + 63, // RGB size 3 + 110 // RGB size 4 +}; // see table 7.1.6.1-1 of 36.213 + + +NS_OBJECT_ENSURE_REGISTERED (TtaFfMacScheduler); + + + +class TtaSchedulerMemberCschedSapProvider : public FfMacCschedSapProvider +{ +public: + TtaSchedulerMemberCschedSapProvider (TtaFfMacScheduler* scheduler); + + // inherited from FfMacCschedSapProvider + virtual void CschedCellConfigReq (const struct CschedCellConfigReqParameters& params); + virtual void CschedUeConfigReq (const struct CschedUeConfigReqParameters& params); + virtual void CschedLcConfigReq (const struct CschedLcConfigReqParameters& params); + virtual void CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params); + virtual void CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params); + +private: + TtaSchedulerMemberCschedSapProvider (); + TtaFfMacScheduler* m_scheduler; +}; + +TtaSchedulerMemberCschedSapProvider::TtaSchedulerMemberCschedSapProvider () +{ +} + +TtaSchedulerMemberCschedSapProvider::TtaSchedulerMemberCschedSapProvider (TtaFfMacScheduler* scheduler) : m_scheduler (scheduler) +{ +} + + +void +TtaSchedulerMemberCschedSapProvider::CschedCellConfigReq (const struct CschedCellConfigReqParameters& params) +{ + m_scheduler->DoCschedCellConfigReq (params); +} + +void +TtaSchedulerMemberCschedSapProvider::CschedUeConfigReq (const struct CschedUeConfigReqParameters& params) +{ + m_scheduler->DoCschedUeConfigReq (params); +} + + +void +TtaSchedulerMemberCschedSapProvider::CschedLcConfigReq (const struct CschedLcConfigReqParameters& params) +{ + m_scheduler->DoCschedLcConfigReq (params); +} + +void +TtaSchedulerMemberCschedSapProvider::CschedLcReleaseReq (const struct CschedLcReleaseReqParameters& params) +{ + m_scheduler->DoCschedLcReleaseReq (params); +} + +void +TtaSchedulerMemberCschedSapProvider::CschedUeReleaseReq (const struct CschedUeReleaseReqParameters& params) +{ + m_scheduler->DoCschedUeReleaseReq (params); +} + + + + +class TtaSchedulerMemberSchedSapProvider : public FfMacSchedSapProvider +{ +public: + TtaSchedulerMemberSchedSapProvider (TtaFfMacScheduler* scheduler); + + // inherited from FfMacSchedSapProvider + virtual void SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params); + virtual void SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params); + virtual void SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params); + virtual void SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params); + virtual void SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params); + virtual void SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params); + virtual void SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params); + virtual void SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params); + virtual void SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params); + virtual void SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params); + virtual void SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params); + + +private: + TtaSchedulerMemberSchedSapProvider (); + TtaFfMacScheduler* m_scheduler; +}; + + + +TtaSchedulerMemberSchedSapProvider::TtaSchedulerMemberSchedSapProvider () +{ +} + + +TtaSchedulerMemberSchedSapProvider::TtaSchedulerMemberSchedSapProvider (TtaFfMacScheduler* scheduler) + : m_scheduler (scheduler) +{ +} + +void +TtaSchedulerMemberSchedSapProvider::SchedDlRlcBufferReq (const struct SchedDlRlcBufferReqParameters& params) +{ + m_scheduler->DoSchedDlRlcBufferReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedDlPagingBufferReq (const struct SchedDlPagingBufferReqParameters& params) +{ + m_scheduler->DoSchedDlPagingBufferReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedDlMacBufferReq (const struct SchedDlMacBufferReqParameters& params) +{ + m_scheduler->DoSchedDlMacBufferReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedDlTriggerReq (const struct SchedDlTriggerReqParameters& params) +{ + m_scheduler->DoSchedDlTriggerReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedDlRachInfoReq (const struct SchedDlRachInfoReqParameters& params) +{ + m_scheduler->DoSchedDlRachInfoReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedDlCqiInfoReq (const struct SchedDlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedDlCqiInfoReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedUlTriggerReq (const struct SchedUlTriggerReqParameters& params) +{ + m_scheduler->DoSchedUlTriggerReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedUlNoiseInterferenceReq (const struct SchedUlNoiseInterferenceReqParameters& params) +{ + m_scheduler->DoSchedUlNoiseInterferenceReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedUlSrInfoReq (const struct SchedUlSrInfoReqParameters& params) +{ + m_scheduler->DoSchedUlSrInfoReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedUlMacCtrlInfoReq (const struct SchedUlMacCtrlInfoReqParameters& params) +{ + m_scheduler->DoSchedUlMacCtrlInfoReq (params); +} + +void +TtaSchedulerMemberSchedSapProvider::SchedUlCqiInfoReq (const struct SchedUlCqiInfoReqParameters& params) +{ + m_scheduler->DoSchedUlCqiInfoReq (params); +} + + + + + +TtaFfMacScheduler::TtaFfMacScheduler () + : m_cschedSapUser (0), + m_schedSapUser (0), + m_nextRntiUl (0) +{ + m_amc = CreateObject (); + m_cschedSapProvider = new TtaSchedulerMemberCschedSapProvider (this); + m_schedSapProvider = new TtaSchedulerMemberSchedSapProvider (this); +} + +TtaFfMacScheduler::~TtaFfMacScheduler () +{ + NS_LOG_FUNCTION (this); +} + +void +TtaFfMacScheduler::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_cschedSapProvider; + delete m_schedSapProvider; +} + +TypeId +TtaFfMacScheduler::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TtaFfMacScheduler") + .SetParent () + .AddConstructor () + .AddAttribute ("CqiTimerThreshold", + "The number of TTIs a CQI is valid (default 1000 - 1 sec.)", + UintegerValue (1000), + MakeUintegerAccessor (&TtaFfMacScheduler::m_cqiTimersThreshold), + MakeUintegerChecker ()) + ; + return tid; +} + + + +void +TtaFfMacScheduler::SetFfMacCschedSapUser (FfMacCschedSapUser* s) +{ + m_cschedSapUser = s; +} + +void +TtaFfMacScheduler::SetFfMacSchedSapUser (FfMacSchedSapUser* s) +{ + m_schedSapUser = s; +} + +FfMacCschedSapProvider* +TtaFfMacScheduler::GetFfMacCschedSapProvider () +{ + return m_cschedSapProvider; +} + +FfMacSchedSapProvider* +TtaFfMacScheduler::GetFfMacSchedSapProvider () +{ + return m_schedSapProvider; +} + +void +TtaFfMacScheduler::DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // Read the subset of parameters used + m_cschedCellConfig = params; + FfMacCschedSapUser::CschedUeConfigCnfParameters cnf; + cnf.m_result = SUCCESS; + m_cschedSapUser->CschedUeConfigCnf (cnf); + return; +} + +void +TtaFfMacScheduler::DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " RNTI " << params.m_rnti << " txMode " << (uint16_t)params.m_transmissionMode); + std::map ::iterator it = m_uesTxMode.find (params.m_rnti); + if (it == m_uesTxMode.end ()) + { + m_uesTxMode.insert (std::pair (params.m_rnti, params.m_transmissionMode)); + } + else + { + (*it).second = params.m_transmissionMode; + } + return; +} + +void +TtaFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params) +{ + NS_LOG_FUNCTION (this << " New LC, rnti: " << params.m_rnti); + + std::set::iterator it; + + for (uint16_t i = 0; i < params.m_logicalChannelConfigList.size (); i++) + { + it = m_flowStatsDl.find (params.m_rnti); + + if (it == m_flowStatsDl.end ()) + { + m_flowStatsDl.insert (params.m_rnti); + m_flowStatsUl.insert (params.m_rnti); + } + else + { + NS_LOG_ERROR ("RNTI already exists"); + } + + } + + return; +} + + +void +TtaFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TtaFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + + +void +TtaFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this << params.m_rnti << (uint32_t) params.m_logicalChannelIdentity); + // API generated by RLC for updating RLC parameters on a LC (tx and retx queues) + + std::map ::iterator it; + + LteFlowId_t flow (params.m_rnti, params.m_logicalChannelIdentity); + + it = m_rlcBufferReq.find (flow); + + if (it == m_rlcBufferReq.end ()) + { + m_rlcBufferReq.insert (std::pair (flow, params)); + } + else + { + (*it).second = params; + } + + return; +} + +void +TtaFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TtaFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +int +TtaFfMacScheduler::GetRbgSize (int dlbandwidth) +{ + for (int i = 0; i < 4; i++) + { + if (dlbandwidth < TtaType0AllocationRbg[i]) + { + return (i + 1); + } + } + + return (-1); +} + + +int +TtaFfMacScheduler::LcActivePerFlow (uint16_t rnti) +{ + std::map ::iterator it; + int lcActive = 0; + for (it = m_rlcBufferReq.begin (); it != m_rlcBufferReq.end (); it++) + { + if (((*it).first.m_rnti == rnti) && (((*it).second.m_rlcTransmissionQueueSize > 0) + || ((*it).second.m_rlcRetransmissionQueueSize > 0) + || ((*it).second.m_rlcStatusPduSize > 0) )) + { + lcActive++; + } + if ((*it).first.m_rnti > rnti) + { + break; + } + } + return (lcActive); + +} + + +void +TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + // API generated by RLC for triggering the scheduling of a DL subframe + + + // evaluate the relative channel quality indicator for each UE per each RBG + // (since we are using allocation type 0 the small unit of allocation is RBG) + // Resource allocation type 0 (see sec 7.1.6.1 of 36.213) + + RefreshDlCqiMaps (); + + int rbgSize = GetRbgSize (m_cschedCellConfig.m_dlBandwidth); + int rbgNum = m_cschedCellConfig.m_dlBandwidth / rbgSize; + std::map > allocationMap; + for (int i = 0; i < rbgNum; i++) + { + std::set ::iterator it; + std::set ::iterator itMax = m_flowStatsDl.end (); + double metricMax = 0.0; + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++) + { + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*it)); + std::map ::iterator itWbCqi; + itWbCqi = m_p10CqiRxed.find ((*it)); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*it)); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it)); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector sbCqi; + if (itCqi == m_a30CqiRxed.end ()) + { + for (uint8_t k = 0; k < nLayer; k++) + { + sbCqi.push_back (1); // start with lowest value + } + } + else + { + sbCqi = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; + } + + uint8_t wbCqi = 0; + if (itWbCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } + else + { + wbCqi = (*itWbCqi).second; + } + + uint8_t cqi1 = sbCqi.at(0); + uint8_t cqi2 = 1; + if (sbCqi.size () > 1) + { + cqi2 = sbCqi.at(1); + } + + if ((cqi1 > 0)||(cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + { + if (LcActivePerFlow (*it) > 0) + { + // this UE has data to transmit + uint8_t sbMcs = 0; + uint8_t wbMcs = 0; + double achievableSbRate = 1.0; + double achievableWbRate = 1.0; + for (uint8_t k = 0; k < nLayer; k++) + { + if (sbCqi.size () > k) + { + sbMcs = m_amc->GetMcsFromCqi (sbCqi.at (k)); + } + else + { + // no info on this subband -> worst MCS + sbMcs = 0; + } + achievableSbRate += ((m_amc->GetTbSizeFromMcs (sbMcs, rbgSize) / 8) / 0.001); // = TB size / TTI + wbMcs = m_amc->GetMcsFromCqi (wbCqi); + achievableWbRate = ((m_amc->GetTbSizeFromMcs (wbMcs, rbgSize) / 8) / 0.001); // = TB size / TTI + } + + double metric = achievableSbRate / achievableWbRate; + + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } + } + } // end if cqi + } // end for m_flowStatsDl + + if (itMax == m_flowStatsDl.end ()) + { + // no UE available for this RB + NS_LOG_DEBUG (this << " no UE found"); + } + else + { + std::map >::iterator itMap; + itMap = allocationMap.find ((*itMax)); + if (itMap == allocationMap.end ()) + { + // insert new element + std::vector tempMap; + tempMap.push_back (i); + allocationMap.insert (std::pair > ((*itMax), tempMap)); + } + else + { + (*itMap).second.push_back (i); + } + } + } // end for RBGs + + + // generate the transmission opportunities by grouping the RBGs of the same RNTI and + // creating the correspondent DCIs + FfMacSchedSapUser::SchedDlConfigIndParameters ret; + std::map >::iterator itMap = allocationMap.begin (); + while (itMap != allocationMap.end ()) + { + // create new BuildDataListElement_s for this LC + BuildDataListElement_s newEl; + newEl.m_rnti = (*itMap).first; + // create the DlDciListElement_s + DlDciListElement_s newDci; + std::vector newRlcPduLe; + newDci.m_rnti = (*itMap).first; + + uint16_t lcActives = LcActivePerFlow ((*itMap).first); + uint16_t rbgPerRnti = (*itMap).second.size (); + std::map ::iterator itCqi; + itCqi = m_a30CqiRxed.find ((*itMap).first); + std::map ::iterator itTxMode; + itTxMode = m_uesTxMode.find ((*itMap).first); + if (itTxMode == m_uesTxMode.end ()) + { + NS_FATAL_ERROR ("No Transmission Mode info on user " << (*itMap).first); + } + int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); + std::vector worstCqi (2, 15); + if (itCqi != m_a30CqiRxed.end ()) + { + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + if ((*itCqi).second.m_higherLayerSelected.size () > (*itMap).second.at (k)) + { + for (uint8_t j = 0; j < nLayer; j++) + { + if ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.size () > j) + { + if (((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)) < worstCqi.at (j)) + { + worstCqi.at (j) = ((*itCqi).second.m_higherLayerSelected.at ((*itMap).second.at (k)).m_sbCqi.at (j)); + } + } + else + { + // no CQI for this layer of this suband -> worst one + worstCqi.at (j) = 1; + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + } + } + else + { + for (uint8_t j = 0; j < nLayer; j++) + { + worstCqi.at (j) = 1; // try with lowest MCS in RBG with no info on channel + } + } + for (uint8_t j = 0; j < nLayer; j++) + { + newDci.m_mcs.push_back (m_amc->GetMcsFromCqi (worstCqi.at (j))); + int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (j), rbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) + newDci.m_tbsSize.push_back (tbSize); + } + + newDci.m_resAlloc = 0; // only allocation type 0 at this stage + newDci.m_rbBitmap = 0; // TBD (32 bit bitmap see 7.1.6 of 36.213) + uint32_t rbgMask = 0; + for (uint16_t k = 0; k < (*itMap).second.size (); k++) + { + rbgMask = rbgMask + (0x1 << (*itMap).second.at (k)); + } + newDci.m_rbBitmap = rbgMask; // (32 bit bitmap see 7.1.6 of 36.213) + + // create the rlc PDUs -> equally divide resources among actives LCs + std::map ::iterator itBufReq; + for (itBufReq = m_rlcBufferReq.begin (); itBufReq != m_rlcBufferReq.end (); itBufReq++) + { + if (((*itBufReq).first.m_rnti == (*itMap).first) && + (((*itBufReq).second.m_rlcTransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) + || ((*itBufReq).second.m_rlcStatusPduSize > 0) )) + { + for (uint8_t j = 0; j < nLayer; j++) + { + RlcPduListElement_s newRlcEl; + newRlcEl.m_logicalChannelIdentity = (*itBufReq).first.m_lcId; + newRlcEl.m_size = newDci.m_tbsSize.at (j) / lcActives; + newRlcPduLe.push_back (newRlcEl); + UpdateDlRlcBufferInfo (newDci.m_rnti, newRlcEl.m_logicalChannelIdentity, newRlcEl.m_size); + } + } + if ((*itBufReq).first.m_rnti > (*itMap).first) + { + break; + } + } + newDci.m_ndi.push_back (1); // TBD (new data indicator) + newDci.m_rv.push_back (0); // TBD (redundancy version) + + newEl.m_dci = newDci; + // ...more parameters -> ingored in this version + + newEl.m_rlcPduList.push_back (newRlcPduLe); + ret.m_buildDataList.push_back (newEl); + + + itMap++; + } // end while allocation + ret.m_nrOfPdcchOfdmSymbols = 1; // TODO: check correct value according the DCIs txed + + m_schedSapUser->SchedDlConfigInd (ret); + + return; +} + +void +TtaFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TtaFfMacScheduler::DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + for (unsigned int i = 0; i < params.m_cqiList.size (); i++) + { + if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::P10 ) + { + // wideband CQI reporting + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_p10CqiRxed.find (rnti); + if (it == m_p10CqiRxed.end ()) + { + // create the new entry + m_p10CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_wbCqi.at (0)) ); // only codeword 0 at this stage (SISO) + // generate correspondent timer + m_p10CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_wbCqi.at (0); + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_p10CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else if ( params.m_cqiList.at (i).m_cqiType == CqiListElement_s::A30 ) + { + // subband CQI reporting high layer configured + std::map ::iterator it; + uint16_t rnti = params.m_cqiList.at (i).m_rnti; + it = m_a30CqiRxed.find (rnti); + if (it == m_a30CqiRxed.end ()) + { + // create the new entry + m_a30CqiRxed.insert ( std::pair (rnti, params.m_cqiList.at (i).m_sbMeasResult) ); + m_a30CqiTimers.insert ( std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the CQI value and refresh correspondent timer + (*it).second = params.m_cqiList.at (i).m_sbMeasResult; + std::map ::iterator itTimers; + itTimers = m_a30CqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + } + } + else + { + NS_LOG_ERROR (this << " CQI type unknown"); + } + } + + return; +} + + +double +TtaFfMacScheduler::EstimateUlSinr (uint16_t rnti, uint16_t rb) +{ + std::map >::iterator itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + return (NO_SINR); + + } + else + { + // take the average SINR value among the available + double sinrSum = 0; + int sinrNum = 0; + for (uint32_t i = 0; i < m_cschedCellConfig.m_ulBandwidth; i++) + { + double sinr = (*itCqi).second.at (i); + if (sinr != NO_SINR) + { + sinrSum += sinr; + sinrNum++; + } + } + double estimatedSinr = sinrSum / (double)sinrNum; + // store the value + (*itCqi).second.at (rb) = estimatedSinr; + return (estimatedSinr); + } +} + +void +TtaFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params) +{ + NS_LOG_FUNCTION (this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. " << (0xF & params.m_sfnSf)); + + RefreshUlCqiMaps (); + + std::map ::iterator it; + int nflows = 0; + + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + // remove old entries of this UE-LC + if ((*it).second > 0) + { + nflows++; + } + } + + if (nflows == 0) + { + return ; // no flows to be scheduled + } + + + // Divide the resource equally among the active users + int rbPerFlow = m_cschedCellConfig.m_ulBandwidth / nflows; + if (rbPerFlow == 0) + { + rbPerFlow = 1; // at least 1 rbg per flow (till available resource) + } + int rbAllocated = 0; + + FfMacSchedSapUser::SchedUlConfigIndParameters ret; + std::vector rbgAllocationMap; + if (m_nextRntiUl != 0) + { + for (it = m_ceBsrRxed.begin (); it != m_ceBsrRxed.end (); it++) + { + if ((*it).first == m_nextRntiUl) + { + break; + } + } + if (it == m_ceBsrRxed.end ()) + { + NS_LOG_ERROR (this << " no user found"); + } + } + else + { + it = m_ceBsrRxed.begin (); + m_nextRntiUl = (*it).first; + } + do + { + if (rbAllocated + rbPerFlow > m_cschedCellConfig.m_ulBandwidth) + { + // limit to physical resources last resource assignment + rbPerFlow = m_cschedCellConfig.m_ulBandwidth - rbAllocated; + } + + UlDciListElement_s uldci; + uldci.m_rnti = (*it).first; + uldci.m_rbStart = rbAllocated; + uldci.m_rbLen = rbPerFlow; + std::map >::iterator itCqi = m_ueCqi.find ((*it).first); + int cqi = 0; + if (itCqi == m_ueCqi.end ()) + { + // no cqi info about this UE + uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD +// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); + } + else + { + // take the lowest CQI value (worst RB) + double minSinr = (*itCqi).second.at (uldci.m_rbStart); + if (minSinr == NO_SINR) + { + minSinr = EstimateUlSinr ((*it).first, uldci.m_rbStart); + } + for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) + { +// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); + double sinr = (*itCqi).second.at (i); + if (sinr == NO_SINR) + { + sinr = EstimateUlSinr ((*it).first, i); + } + if ((*itCqi).second.at (i) < minSinr) + { + minSinr = (*itCqi).second.at (i); + } + } + + // translate SINR -> cqi: WILD ACK: same as DL + double s = log2 ( 1 + ( + std::pow (10, minSinr / 10 ) / + ( (-std::log (5.0 * 0.00005 )) / 1.5) )); + cqi = m_amc->GetCqiFromSpectralEfficiency (s); + if (cqi == 0) + { + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) + } + uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); +// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); + + } + + rbAllocated += rbPerFlow; + // store info on allocation for managing ul-cqi interpretation + for (int i = 0; i < rbPerFlow; i++) + { + rbgAllocationMap.push_back ((*it).first); + } + uldci.m_tbSize = (m_amc->GetTbSizeFromMcs (uldci.m_mcs, rbPerFlow) / 8); + NS_LOG_DEBUG (this << " UE " << (*it).first << " startPRB " << (uint32_t)uldci.m_rbStart << " nPRB " << (uint32_t)uldci.m_rbLen << " CQI " << cqi << " MCS " << (uint32_t)uldci.m_mcs << " TBsize " << uldci.m_tbSize << " RbAlloc " << rbAllocated); + UpdateUlRlcBufferInfo (uldci.m_rnti, uldci.m_tbSize); + uldci.m_ndi = 1; + uldci.m_cceIndex = 0; + uldci.m_aggrLevel = 1; + uldci.m_ueTxAntennaSelection = 3; // antenna selection OFF + uldci.m_hopping = false; + uldci.m_n2Dmrs = 0; + uldci.m_tpc = 0; // no power control + uldci.m_cqiRequest = false; // only period CQI at this stage + uldci.m_ulIndex = 0; // TDD parameter + uldci.m_dai = 1; // TDD parameter + uldci.m_freqHopping = 0; + uldci.m_pdcchPowerOffset = 0; // not used + ret.m_dciList.push_back (uldci); + + + it++; + if (it == m_ceBsrRxed.end ()) + { + // restart from the first + it = m_ceBsrRxed.begin (); + } + if (rbAllocated == m_cschedCellConfig.m_ulBandwidth) + { + // Stop allocation: no more PRBs + m_nextRntiUl = (*it).first; + break; + } + } + while ((*it).first != m_nextRntiUl); + + + m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); + m_schedSapUser->SchedUlConfigInd (ret); + return; +} + +void +TtaFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TtaFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + // TODO: Implementation of the API + return; +} + +void +TtaFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); + + std::map ::iterator it; + + for (unsigned int i = 0; i < params.m_macCeList.size (); i++) + { + if ( params.m_macCeList.at (i).m_macCeType == MacCeListElement_s::BSR ) + { + // buffer status report + // note that we only consider LCG 0, the other three LCGs are neglected + // this is consistent with the assumption in LteUeMac that the first LCG gathers all LCs + uint16_t rnti = params.m_macCeList.at (i).m_rnti; + it = m_ceBsrRxed.find (rnti); + if (it == m_ceBsrRxed.end ()) + { + // create the new entry + uint8_t bsrId = params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0); + int buffer = BufferSizeLevelBsr::BsrId2BufferSize (bsrId); + m_ceBsrRxed.insert ( std::pair (rnti, buffer)); + } + else + { + // update the buffer size value + (*it).second = BufferSizeLevelBsr::BsrId2BufferSize (params.m_macCeList.at (i).m_macCeValue.m_bufferStatus.at (0)); + } + } + } + + return; +} + +void +TtaFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) +{ + NS_LOG_FUNCTION (this); +// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); + // retrieve the allocation for this subframe + switch (m_ulCqiFilter) + { + case FfMacScheduler::SRS_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::SRS) + { + return; + } + } + break; + case FfMacScheduler::PUSCH_UL_CQI: + { + // filter all the CQIs that are not SRS based + if (params.m_ulCqi.m_type!=UlCqi_s::PUSCH) + { + return; + } + } + case FfMacScheduler::ALL_UL_CQI: + break; + + default: + NS_FATAL_ERROR ("Unknown UL CQI type"); + } + + switch (params.m_ulCqi.m_type) + { + case UlCqi_s::PUSCH: + { + std::map >::iterator itMap; + std::map >::iterator itCqi; + itMap = m_allocationMaps.find (params.m_sfnSf); + if (itMap == m_allocationMaps.end ()) + { + NS_LOG_DEBUG (this << " Does not find info on allocation, size : " << m_allocationMaps.size ()); + return; + } + for (uint32_t i = 0; i < (*itMap).second.size (); i++) + { + // convert from fixed point notation Sxxxxxxxxxxx.xxx to double + // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); + //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); + itCqi = m_ueCqi.find ((*itMap).second.at (i)); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + if (i == j) + { + newCqi.push_back (sinr); + } + else + { + // initialize with NO_SINR value. + newCqi.push_back (NO_SINR); + } + + } + m_ueCqi.insert (std::pair > ((*itMap).second.at (i), newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair ((*itMap).second.at (i), m_cqiTimersThreshold)); + } + else + { + // update the value + (*itCqi).second.at (i) = sinr; + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find ((*itMap).second.at (i)); + (*itTimers).second = m_cqiTimersThreshold; + + } + + } + // remove obsolete info on allocation + m_allocationMaps.erase (itMap); + } + break; + case UlCqi_s::SRS: + { + // get the RNTI from vendor specific parameters + uint16_t rnti = 0; + NS_ASSERT (params.m_vendorSpecificList.size () > 0); + for (uint16_t i = 0; i < params.m_vendorSpecificList.size (); i++) + { + if (params.m_vendorSpecificList.at (i).m_type == SRS_CQI_RNTI_VSP) + { + Ptr vsp = DynamicCast (params.m_vendorSpecificList.at (i).m_value); + rnti = vsp->GetRnti (); + } + } + std::map >::iterator itCqi; + itCqi = m_ueCqi.find (rnti); + if (itCqi == m_ueCqi.end ()) + { + // create a new entry + std::vector newCqi; + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + newCqi.push_back (sinr); + NS_LOG_DEBUG (this << " RNTI " << rnti << " new SRS-CQI for RB " << j << " value " << sinr); + + } + m_ueCqi.insert (std::pair > (rnti, newCqi)); + // generate correspondent timer + m_ueCqiTimers.insert (std::pair (rnti, m_cqiTimersThreshold)); + } + else + { + // update the values + for (uint32_t j = 0; j < m_cschedCellConfig.m_ulBandwidth; j++) + { + double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (j)); + (*itCqi).second.at (j) = sinr; + NS_LOG_DEBUG (this << " RNTI " << rnti << " update SRS-CQI for RB " << j << " value " << sinr); + } + // update correspondent timer + std::map ::iterator itTimers; + itTimers = m_ueCqiTimers.find (rnti); + (*itTimers).second = m_cqiTimersThreshold; + + } + + + } + break; + case UlCqi_s::PUCCH_1: + case UlCqi_s::PUCCH_2: + case UlCqi_s::PRACH: + { + NS_FATAL_ERROR ("TtaFfMacScheduler supports only PUSCH and SRS UL-CQIs"); + } + break; + default: + NS_FATAL_ERROR ("Unknown type of UL-CQI"); + } + return; +} + +void +TtaFfMacScheduler::RefreshDlCqiMaps(void) +{ + // refresh DL CQI P01 Map + std::map ::iterator itP10 = m_p10CqiTimers.begin (); + while (itP10!=m_p10CqiTimers.end ()) + { +// NS_LOG_INFO (this << " P10-CQI for user " << (*itP10).first << " is " << (uint32_t)(*itP10).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itP10).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_p10CqiRxed.find ((*itP10).first); + NS_ASSERT_MSG (itMap != m_p10CqiRxed.end (), " Does not find CQI report for user " << (*itP10).first); + NS_LOG_INFO (this << " P10-CQI exired for user " << (*itP10).first); + m_p10CqiRxed.erase (itMap); + std::map ::iterator temp = itP10; + itP10++; + m_p10CqiTimers.erase (temp); + } + else + { + (*itP10).second--; + itP10++; + } + } + + // refresh DL CQI A30 Map + std::map ::iterator itA30 = m_a30CqiTimers.begin (); + while (itA30!=m_a30CqiTimers.end ()) + { +// NS_LOG_INFO (this << " A30-CQI for user " << (*itA30).first << " is " << (uint32_t)(*itA30).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itA30).second == 0) + { + // delete correspondent entries + std::map ::iterator itMap = m_a30CqiRxed.find ((*itA30).first); + NS_ASSERT_MSG (itMap != m_a30CqiRxed.end (), " Does not find CQI report for user " << (*itA30).first); + NS_LOG_INFO (this << " A30-CQI exired for user " << (*itA30).first); + m_a30CqiRxed.erase (itMap); + std::map ::iterator temp = itA30; + itA30++; + m_a30CqiTimers.erase (temp); + } + else + { + (*itA30).second--; + itA30++; + } + } + + return; +} + + +void +TtaFfMacScheduler::RefreshUlCqiMaps(void) +{ + // refresh UL CQI Map + std::map ::iterator itUl = m_ueCqiTimers.begin (); + while (itUl!=m_ueCqiTimers.end ()) + { +// NS_LOG_INFO (this << " UL-CQI for user " << (*itUl).first << " is " << (uint32_t)(*itUl).second << " thr " << (uint32_t)m_cqiTimersThreshold); + if ((*itUl).second == 0) + { + // delete correspondent entries + std::map >::iterator itMap = m_ueCqi.find ((*itUl).first); + NS_ASSERT_MSG (itMap != m_ueCqi.end (), " Does not find CQI report for user " << (*itUl).first); + NS_LOG_INFO (this << " UL-CQI exired for user " << (*itUl).first); + (*itMap).second.clear (); + m_ueCqi.erase (itMap); + std::map ::iterator temp = itUl; + itUl++; + m_ueCqiTimers.erase (temp); + } + else + { + (*itUl).second--; + itUl++; + } + } + + return; +} + +void +TtaFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size) +{ + size = size - 2; // remove the minimum RLC overhead + std::map::iterator it; + LteFlowId_t flow (rnti, lcid); + it = m_rlcBufferReq.find (flow); + if (it!=m_rlcBufferReq.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); + // Update queues: RLC tx order Status, ReTx, Tx + // Update status queue + if ((*it).second.m_rlcStatusPduSize <= size) + { + size -= (*it).second.m_rlcStatusPduSize; + (*it).second.m_rlcStatusPduSize = 0; + } + else + { + (*it).second.m_rlcStatusPduSize -= size; + return; + } + // update retransmission queue + if ((*it).second.m_rlcRetransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcRetransmissionQueueSize; + (*it).second.m_rlcRetransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcRetransmissionQueueSize -= size; + return; + } + // update transmission queue + if ((*it).second.m_rlcTransmissionQueueSize <= size) + { + size -= (*it).second.m_rlcTransmissionQueueSize; + (*it).second.m_rlcTransmissionQueueSize = 0; + } + else + { + (*it).second.m_rlcTransmissionQueueSize -= size; + return; + } + } + else + { + NS_LOG_ERROR (this << " Does not find DL RLC Buffer Report of UE " << rnti); + } +} + +void +TtaFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) +{ + + size = size - 2; // remove the minimum RLC overhead + std::map ::iterator it = m_ceBsrRxed.find (rnti); + if (it!=m_ceBsrRxed.end ()) + { +// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); + if ((*it).second >= size) + { + (*it).second -= size; + } + else + { + (*it).second = 0; + } + } + else + { + NS_LOG_ERROR (this << " Does not find BSR report info of UE " << rnti); + } + +} + +void +TtaFfMacScheduler::TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode) +{ + NS_LOG_FUNCTION (this << " RNTI " << rnti << " txMode " << (uint16_t)txMode); + FfMacCschedSapUser::CschedUeConfigUpdateIndParameters params; + params.m_rnti = rnti; + params.m_transmissionMode = txMode; + m_cschedSapUser->CschedUeConfigUpdateInd (params); +} + + +} diff --git a/src/lte/model/tta-ff-mac-scheduler.h b/src/lte/model/tta-ff-mac-scheduler.h new file mode 100644 index 000000000..8417b0abf --- /dev/null +++ b/src/lte/model/tta-ff-mac-scheduler.h @@ -0,0 +1,205 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo // original version + * Modification: Dizhi Zhou // modify codes related to downlink scheduler + */ + +#ifndef TTA_FF_MAC_SCHEDULER_H +#define TTA_FF_MAC_SCHEDULER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace ns3 { + +/** + * \ingroup lte + * + * \brief Implements the SCHED SAP and CSCHED SAP for a Throughput to Average scheduler + * + * This class implements the interface defined by the FfMacScheduler abstract class + */ +class TtaFfMacScheduler : public FfMacScheduler +{ +public: + /** + * \brief Constructor + * + * Creates the MAC Scheduler interface implementation + */ + TtaFfMacScheduler (); + + /** + * Destructor + */ + virtual ~TtaFfMacScheduler (); + + // inherited from Object + virtual void DoDispose (void); + static TypeId GetTypeId (void); + + // inherited from FfMacScheduler + virtual void SetFfMacCschedSapUser (FfMacCschedSapUser* s); + virtual void SetFfMacSchedSapUser (FfMacSchedSapUser* s); + virtual FfMacCschedSapProvider* GetFfMacCschedSapProvider (); + virtual FfMacSchedSapProvider* GetFfMacSchedSapProvider (); + + friend class TtaSchedulerMemberCschedSapProvider; + friend class TtaSchedulerMemberSchedSapProvider; + + void TransmissionModeConfigurationUpdate (uint16_t rnti, uint8_t txMode); + +private: + // + // Implementation of the CSCHED API primitives + // (See 4.1 for description of the primitives) + // + + void DoCschedCellConfigReq (const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params); + + void DoCschedUeConfigReq (const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params); + + void DoCschedLcConfigReq (const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params); + + void DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params); + + void DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params); + + // + // Implementation of the SCHED API primitives + // (See 4.2 for description of the primitives) + // + + void DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params); + + void DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params); + + void DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params); + + void DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params); + + void DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params); + + void DoSchedDlCqiInfoReq (const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params); + + void DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params); + + void DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params); + + void DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params); + + void DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params); + + void DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params); + + + int GetRbgSize (int dlbandwidth); + + int LcActivePerFlow (uint16_t rnti); + + double EstimateUlSinr (uint16_t rnti, uint16_t rb); + + void RefreshDlCqiMaps (void); + void RefreshUlCqiMaps (void); + + void UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t size); + void UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size); + Ptr m_amc; + + /* + * Vectors of UE's LC info + */ + std::map m_rlcBufferReq; + + + /* + * Set of UE's RNTI in downlink + */ + std::set m_flowStatsDl; + + /* + * Set of UE's RNTI in uplink + */ + std::set m_flowStatsUl; + + /* + * Map of UE's DL CQI P01 received + */ + std::map m_p10CqiRxed; + /* + * Map of UE's timers on DL CQI P01 received + */ + std::map m_p10CqiTimers; + + /* + * Map of UE's DL CQI A30 received + */ + std::map m_a30CqiRxed; + /* + * Map of UE's timers on DL CQI A30 received + */ + std::map m_a30CqiTimers; + + /* + * Map of previous allocated UE per RBG + * (used to retrieve info from UL-CQI) + */ + std::map > m_allocationMaps; + + /* + * Map of UEs' UL-CQI per RBG + */ + std::map > m_ueCqi; + /* + * Map of UEs' timers on UL-CQI per RBG + */ + std::map m_ueCqiTimers; + + /* + * Map of UE's buffer status reports received + */ + std::map m_ceBsrRxed; + + // MAC SAPs + FfMacCschedSapUser* m_cschedSapUser; + FfMacSchedSapUser* m_schedSapUser; + FfMacCschedSapProvider* m_cschedSapProvider; + FfMacSchedSapProvider* m_schedSapProvider; + + + // Internal parameters + FfMacCschedSapProvider::CschedCellConfigReqParameters m_cschedCellConfig; + + uint16_t m_nextRntiUl; // RNTI of the next user to be served next scheduling in UL + + uint32_t m_cqiTimersThreshold; // # of TTIs for which a CQI canbe considered valid + + std::map m_uesTxMode; // txMode of the UEs +}; + +} // namespace ns3 + +#endif /* TTA_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc b/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc new file mode 100644 index 000000000..c88189073 --- /dev/null +++ b/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc @@ -0,0 +1,530 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + +#include "lte-test-fdbet-ff-mac-scheduler.h" + +NS_LOG_COMPONENT_DEFINE ("LenaTestFdBetFfMacCheduler"); + +namespace ns3 { + +LenaTestFdBetFfMacSchedulerSuite::LenaTestFdBetFfMacSchedulerSuite () + : TestSuite ("lte-fdbet-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestFdBetFfMacSchedulerSuite"); + + //Test Case 1: AMC works in FDBET + + // DOWNLINK- DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 26 -> 2196 -> 2196000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 bytes/sec + // UPLINK- DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 bytes/sec + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (1,0,0,2196000,2292000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (3,0,0,749000,749000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (6,0,0,373000,373000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (12,0,0,185000,185000)); + + // DOWNLINK - DISTANCE 3000 -> MCS 22 -> Itbs 20 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 20 -> 1383 -> 1383000 bytes/sec + // 3 users -> 8 PRB at Itbs 20 -> 469 -> 469000 bytes/sec + // 6 users -> 4 PRB at Itbs 20 -> 233 -> 233000 bytes/sec + // 12 users -> 2 PRB at Itbs 20 -> 113 -> 113000 bytes/sec + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 bytes/sec + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (1,0,3000,1383000,1239000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (3,0,3000,469000,389000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (6,0,3000,233500,193000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (12,0,3000,113000,97000)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 15 -> 903 -> 903000 bytes/sec + // 3 users -> 8 PRB at Itbs 15 -> 309 -> 309000 bytes/sec + // 6 users -> 4 PRB at Itbs 15 -> 153 -> 153000 bytes/sec + // 12 users -> 2 PRB at Itbs 15 -> 75 -> 75000 bytes/sec + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (1,0,6000,903000,621000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (3,0,6000,309000,201000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (6,0,6000,153000,97000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (12,0,6000,75000,47000)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 11 -> 597 -> 597000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 bytes/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 bytes/sec + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (1,0,9000,597000,437000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (3,0,9000,201000,137000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (6,0,9000,97000,67000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (12,0,9000,47000,32000)); + + // DOWNLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 6 -> 309 -> 309000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 101 -> 101000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 49 -> 49000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 22 -> 22000 bytes/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (1,0,15000,309000,233000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (3,0,15000,101000,69000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (6,0,15000,49000,32000)); + AddTestCase (new LenaFdBetFfMacSchedulerTestCase1 (12,0,15000,22000,15000)); + + // Test Case 2: fairness check + + std::vector dist; + dist.push_back (0); // User 0 distance --> MCS 28 + dist.push_back (3000); // User 1 distance --> MCS 24 + dist.push_back (6000); // User 2 distance --> MCS 16 + dist.push_back (9000); // User 3 distance --> MCS 12 + std::vector estAchievableRateDl; + estAchievableRateDl.push_back (2196000); + estAchievableRateDl.push_back (1383000); + estAchievableRateDl.push_back (903000); + estAchievableRateDl.push_back (597000); + std::vector estThrFdBetUl; + estThrFdBetUl.push_back (549000); // User 0 estimated TTI throughput from FDBET + estThrFdBetUl.push_back (293000); // User 1 estimated TTI throughput from FDBET + estThrFdBetUl.push_back (149000); // User 2 estimated TTI throughput from FDBET + estThrFdBetUl.push_back (101000); // User 3 estimated TTI throughput from FDBET + AddTestCase (new LenaFdBetFfMacSchedulerTestCase2 (dist, estAchievableRateDl, estThrFdBetUl)); + + +} + +static LenaTestFdBetFfMacSchedulerSuite lenaTestFdBetFfMacSchedulerSuite; + + +// --------------- T E S T - C A S E # 1 ------------------------------ + + +std::string +LenaFdBetFfMacSchedulerTestCase1::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + +LenaFdBetFfMacSchedulerTestCase1::LenaFdBetFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaFdBetFfMacSchedulerTestCase1::~LenaFdBetFfMacSchedulerTestCase1 () +{ +} + +void +LenaFdBetFfMacSchedulerTestCase1::DoRun (void) +{ + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbFdBetc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeFdBetc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("FdBetFfMacScheduler", LOG_LEVEL_ALL); + LogComponentEnable ("LenaTestFdBetFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + /** + * Initialize Simulation Scenario: 1 eNB and m_nUser UEs + */ + + Ptr lteHelper = CreateObject (); + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::FdBetFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + // Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + lteHelper->EnableRlcTraces (); + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "FD blind equal throughput" manner + */ + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + /** + * Check that the assignation is done in a "FD blind equal throughput" manner among users + * with equal SINRs: the bandwidth should be distributed according to the + * ratio of the estimated throughput per TTI of each user; therefore equally + * partitioning the whole bandwidth achievable from a single users in a TTI + */ + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Unfair Throughput!"); + } + + /** + * Check that the uplink assignation is done in a "FD blind equal throughput" manner + */ + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + /** + * Check that the assignation is done in a "FD blind equal throughput" manner among users + * with equal SINRs: the bandwidht should be distributed according to the + * ratio of the estimated throughput per TTI of each user; therefore equally + * partitioning the whole bandwidth achievable from a single users in a TTI + */ + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + + + +// --------------- T E S T - C A S E # 2 ------------------------------ + + +std::string +LenaFdBetFfMacSchedulerTestCase2::BuildNameString (uint16_t nUser, std::vector dist) +{ + std::ostringstream oss; + oss << "distances (m) = [ " ; + for (std::vector::iterator it = dist.begin (); it != dist.end (); ++it) + { + oss << *it << " "; + } + oss << "]"; + return oss.str (); +} + + +LenaFdBetFfMacSchedulerTestCase2::LenaFdBetFfMacSchedulerTestCase2 (std::vector dist, std::vector estAchievableRateDl, std::vector estThrFdBetUl) + : TestCase (BuildNameString (dist.size (), dist)), + m_nUser (dist.size ()), + m_dist (dist), + m_achievableRateDl (estAchievableRateDl), + m_estThrFdBetUl (estThrFdBetUl) +{ +} + +LenaFdBetFfMacSchedulerTestCase2::~LenaFdBetFfMacSchedulerTestCase2 () +{ +} + +void +LenaFdBetFfMacSchedulerTestCase2::DoRun (void) +{ + // LogComponentEnable ("LteEnbFdBetc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeFdBetc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("FdBetFfMacScheduler", LOG_LEVEL_ALL); + LogComponentEnable ("LenaTestFdBetFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); + // LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + /** + * Initialize Simulation Scenario: 1 eNB and m_nUser UEs + */ + + Ptr lteHelper = CreateObject (); + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::FdBetFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + // Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + lteHelper->EnableRlcTraces (); + + double simulationTime = 1; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s)"); + std::vector dlDataRxed; + double totalData = 0; + double estTotalThr = 0; + double estUeThr = 0; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + totalData += (double)dlDataRxed.at (i); + estTotalThr += 1 / m_achievableRateDl.at (i); + //NS_LOG_INFO ("\tUser " << i << " dist " << m_dist.at (i) << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_nUser); + } + + estTotalThr = m_nUser * (1 / estTotalThr); + estUeThr = estTotalThr / m_nUser; + /** + * Check that the assignation is done in a "FD blind equal throughput" manner among users + * with different SINRs: the bandwidth should be distributed equally in long term + */ + for (int i = 0; i < m_nUser; i++) + { + double thrRatio = (double) 1 / m_nUser; + double estThrRatio = (double)dlDataRxed.at (i) / totalData; + NS_LOG_INFO ("\tUser " << i << " thrRatio " << thrRatio << " estThrRatio " << estThrRatio); + NS_TEST_ASSERT_MSG_EQ_TOL (estThrRatio, thrRatio, tolerance, " Unfair Throughput!"); + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, estUeThr, estUeThr * tolerance, " Unfair Throughput!"); + + } + + /** + * Check that the assignation in uplink is done in a round robin manner. + */ + + NS_LOG_INFO ("UL - Test with " << m_nUser); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " dist " << m_dist.at (i) << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << (double)m_estThrFdBetUl.at (i)); + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, (double)m_estThrFdBetUl.at (i), (double)m_estThrFdBetUl.at (i) * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + + +} // namespace ns3 + + + + diff --git a/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h b/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h new file mode 100644 index 000000000..083dc48df --- /dev/null +++ b/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_TDBET_FF_MAC_SCHEDULER_H +#define LENA_TEST_TDBET_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +namespace ns3 { + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is equal among users is consistent with the definition of blind equal throughput +* scheduling +*/ +class LenaFdBetFfMacSchedulerTestCase1 : public TestCase +{ +public: + LenaFdBetFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl); + virtual ~LenaFdBetFfMacSchedulerTestCase1 (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + double m_thrRefDl; + double m_thrRefUl; +}; + + +class LenaFdBetFfMacSchedulerTestCase2 : public TestCase +{ +public: + LenaFdBetFfMacSchedulerTestCase2 (std::vector dist, std::vector m_achievableRateDl, std::vector estThrFdBetUl); + virtual ~LenaFdBetFfMacSchedulerTestCase2 (); + +private: + static std::string BuildNameString (uint16_t nUser, std::vector dist); + virtual void DoRun (void); + uint16_t m_nUser; + std::vector m_dist; + std::vector m_achievableRateDl; + std::vector m_estThrFdBetUl; +}; + + + + +class LenaTestFdBetFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestFdBetFfMacSchedulerSuite (); +}; + + + + +} // namespace ns3 + + +#endif /* LENA_TEST_TDBET_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc b/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc new file mode 100644 index 000000000..b4140f714 --- /dev/null +++ b/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc @@ -0,0 +1,347 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include "lte-test-fdmt-ff-mac-scheduler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + + +NS_LOG_COMPONENT_DEFINE ("LenaTestFdMtFfMacCheduler"); + +using namespace ns3; + +LenaTestFdMtFfMacSchedulerSuite::LenaTestFdMtFfMacSchedulerSuite () + : TestSuite ("lte-fdmt-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestFdMtFfMacSchedulerSuite"); + + //Test Case : AMC works in FDMT + + // DOWNLINK - DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 26 -> 2196 -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 2196000 among 3 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 2196000 among 6 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 2196000 among 12 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 2196000 among 15 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK- DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 bytes/sec + // 15 users -> 1 PRB at Itbs 26 -> 89 -> 89000 bytes/sec + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (1,0,0,2196000,2292000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (3,0,0,2196000,749000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (6,0,0,2196000,373000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (12,0,0,2196000,185000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (15,0,0,2196000,89000)); + + // DOWNLINK - DISTANCE 3000 -> MCS 24 -> Itbs 30 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 20 -> 1383 -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 1383000 among 3 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 1383000 among 6 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 1383000 among 12 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 1383000 among 15 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 bytes/sec + // 15 users -> 1 PRB at Itbs 18 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (1,0,3000,1383000,1239000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (3,0,3000,1383000,389000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (6,0,3000,1383000,193000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (12,0,3000,1383000,97000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (15,0,3000,1383000,47000)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 15 -> 903 -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 903000 among 3 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 903000 among 6 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 903000 among 12 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 903000 among 15 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 bytes/sec + // 15 users -> 1 PRB at Itbs 11 -> 22 -> 22000 bytes/sec + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (1,0,6000,903000,621000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (3,0,6000,903000,201000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (6,0,6000,903000,97000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (12,0,6000,903000,47000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (15,0,6000,903000,22000)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 11 -> 597 -> 597000 bytes/sec + // 3 users -> 597000 among 3 users -> 199000 bytes/sec + // 6 users -> 597000 among 6 users -> 99500 bytes/sec + // 12 users -> 597000 among 12 users -> 49750 bytes/sec + // 15 users -> 597000 among 15 users -> 39800 bytes/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 bytes/sec + // 15 users -> 1 PRB at Itbs 8 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (1,0,9000,597000,437000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (3,0,9000,597000,137000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (6,0,9000,597000,67000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (12,0,9000,597000,32000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (15,0,9000,597000,15000)); + + // DONWLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 6 -> 309 -> 309000 bytes/sec + // 3 users -> 309000 among 3 users -> 103000 bytes/sec + // 6 users -> 309000 among 6 users -> 51500 bytes/sec + // 12 users -> 309000 among 12 users -> 25750 bytes/sec + // 15 users -> 309000 among 15 users -> 20600 bytes/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 bytes/sec + // 15 users -> 1 PRB at Itbs 6 -> 7 -> 7000 bytes/sec + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (1,0,15000,309000,233000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (3,0,15000,309000,69000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (6,0,15000,309000,32000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (12,0,15000,309000,15000)); + AddTestCase (new LenaFdMtFfMacSchedulerTestCase (15,0,15000,309000,7000)); + + +} + +static LenaTestFdMtFfMacSchedulerSuite lenaTestFdMtFfMacSchedulerSuite; + + +// --------------- T E S T - C A S E ------------------------------ + + +std::string +LenaFdMtFfMacSchedulerTestCase::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + +LenaFdMtFfMacSchedulerTestCase::LenaFdMtFfMacSchedulerTestCase (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaFdMtFfMacSchedulerTestCase::~LenaFdMtFfMacSchedulerTestCase () +{ +} + +void +LenaFdMtFfMacSchedulerTestCase::DoRun (void) +{ + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("FdMtFfMacScheduler", LOG_LEVEL_ALL); + LogComponentEnable ("LenaTestFdMtFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + /** + * Initialize Simulation Scenario: 1 eNB and m_nUser UEs + */ + + Ptr lteHelper = CreateObject (); + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::FdMtFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + // Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + lteHelper->EnableRlcTraces (); + lteHelper->EnableMacTraces (); + + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "maximum throughput" manner + */ + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + /** + * Check that the assignation is done in a "maximum throughput" manner among users + * with equal SINRs: all bandwidth should be allocated to the first UE in script + */ + for (int i = 0; i < 1; i++) + { + if (i == 0) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Invalid Throughput!"); + } + else + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, 0, tolerance, " Invalid Throughput!"); + } + } + + /** + * Check that the uplink assignation is done in a "maximum throughput" manner + */ + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + /** + * Check that the assignation is done in a "maximum throughput" manner among users + * with equal SINRs: the bandwidht should be distributed according to the + * ratio of the estimated throughput per TTI of each user; therefore equally + * partitioning the whole bandwidth achievable from a single users in a TTI + * + */ + for (int i = 0; i < 1; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + diff --git a/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h b/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h new file mode 100644 index 000000000..ad3ad48b8 --- /dev/null +++ b/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_FDMT_FF_MAC_SCHEDULER_H +#define LENA_TEST_FDMT_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +using namespace ns3; + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is consistent with the definition of maximum throughput +* scheduling +*/ +class LenaFdMtFfMacSchedulerTestCase : public TestCase +{ +public: + LenaFdMtFfMacSchedulerTestCase (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl); + virtual ~LenaFdMtFfMacSchedulerTestCase (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + double m_thrRefDl; + double m_thrRefUl; +}; + +class LenaTestFdMtFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestFdMtFfMacSchedulerSuite (); +}; + + + + +#endif /* LENA_TEST_FDMT_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc b/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc new file mode 100644 index 000000000..290c7c51f --- /dev/null +++ b/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc @@ -0,0 +1,764 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + +#include "ns3/epc-helper.h" +#include "ns3/network-module.h" +#include "ns3/ipv4-global-routing-helper.h" +#include "ns3/internet-module.h" +#include "ns3/applications-module.h" +#include "ns3/point-to-point-helper.h" + +#include "lte-test-fdtbfq-ff-mac-scheduler.h" + +NS_LOG_COMPONENT_DEFINE ("LenaTestFdTbfqFfMacCheduler"); + +namespace ns3 { + +LenaTestFdTbfqFfMacSchedulerSuite::LenaTestFdTbfqFfMacSchedulerSuite () + : TestSuite ("lte-fdtbfq-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestFdTbfqFfMacSchedulerSuite"); + + // General config + // Traffic: UDP traffic with fixed rate + // Token generation rate = traffic rate + // RLC header length = 2 bytes, PDCP header = 2 bytes + // Simulation time = 1.0 sec + // Throughput in this file is calculated in RLC layer + + //Test Case 1: homogeneous flow test in FDTBFQ (same distance) + + // DOWNLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 2196000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 2196000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 < 2196000 -> throughput = 232000 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 2196000 -> throughput = 2196000 / 12 = 183000 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 2196000 -> throughput = 2196000 / 15 = 146400 byte/sec + // UPLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 > 232000 -> throughput = 232000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 > 232000 -> throughput = 232000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 < 232000 -> throughput = 185000 bytes/sec + // 15 users -> 1 PRB at Itbs 26 -> 89 -> 89000 bytes/sec + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (1,0,0,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (3,0,0,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (6,0,0,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (12,0,0,183000,185000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (15,0,0,146400,89000,200,1)); + + // DOWNLINK - DISTANCE 3000 -> MCS 24 -> Itbs 20 (from table 7.1.7.2.1-1 of 36.213) + // DOWNLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 1383000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 1383000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 > 1383000 -> throughput = 1383000 / 6 = 230500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 1383000 -> throughput = 1383000 / 12 = 115250 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 1383000 -> throughput = 1383000 / 15 = 92200 byte/sec + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 > 232000 -> throughput = 232000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 < 232000 -> throughput = 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 < 232000 -> throughput = 97000 bytes/sec + // 15 users -> 1 PRB at Itbs 18 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (1,0,3000,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (3,0,3000,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (6,0,3000,230500,193000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (12,0,3000,115250,97000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (15,0,3000,92200,47000,200,1)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 15 -> 903 -> 903000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 903000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 903000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 > 903000 -> throughput = 903000 / 6 = 150500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 903000 -> throughput = 903000 / 12 = 75250 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 903000 -> throughput = 903000 / 15 = 60200 byte/sec + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 < 232000 -> throughput = 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 < 232000 -> throughput = 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 < 232000 -> throughput = 47000 bytes/sec + // 15 users -> 1 PRB at Itbs 11 -> 22 -> 22000 bytes/sec + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (1,0,6000,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (3,0,6000,232000,201000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (6,0,6000,150500,97000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (12,0,6000,75250,47000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (15,0,6000,60200,22000,200,1)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 11 -> 597 -> 597000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 597000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 > 597000 -> througphut = 597000 / 3 = 199000byte/sec + // 6 user -> 232000 * 6 = 139200 > 597000 -> throughput = 597000 / 6 = 99500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 597000 -> throughput = 597000 / 12 = 49750 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 597000 -> throughput = 597000 / 15 = 39800 byte/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 < 232000 -> throughput = 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 < 232000 -> throughput = 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 < 232000 -> throughput = 32000 bytes/sec + // 15 users -> 1 PRB at Itbs 8 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (1,0,9000,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (3,0,9000,199000,137000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (6,0,9000,99500,67000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (12,0,9000,49750,32000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (15,0,9000,39800,15000,200,1)); + + // DONWLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 6 -> 309 -> 309000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 309000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 > 309000 -> througphut = 309000 / 3 = 103000byte/sec + // 6 user -> 232000 * 6 = 139200 > 309000 -> throughput = 309000 / 6 = 51500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 309000 -> throughput = 309000 / 12 = 25750 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 309000 -> throughput = 309000 / 15 = 20600 byte/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 < 232000 -> throughput = 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 < 232000 -> throughput = 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 < 232000 -> throughput = 15000 bytes/sec + // 15 users -> 1 PRB at Itbs 6 -> 7 -> 7000 bytes/sec + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (1,0,15000,232000,232000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (3,0,15000,103000,69000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (6,0,15000,51500,32000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (12,0,15000,25750,15000,200,1)); + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase1 (15,0,15000,20600,7000,200,1)); + + // Test Case 2: homogeneous flow test in FDTBFQ (different distance) + // Traffic1 info + // UDP traffic: payload size = 100 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 132000 byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 + 1/597000 + 1/309000) = 694720 byte/s + // 132000 * 5 = 660000 < 694720 -> estimated throughput in downlink = 132000 byte/sec + std::vector dist1; + dist1.push_back (0); // User 0 distance --> MCS 28 + dist1.push_back (3000); // User 1 distance --> MCS 24 + dist1.push_back (6000); // User 2 distance --> MCS 16 + dist1.push_back (9000); // User 3 distance --> MCS 12 + dist1.push_back (15000); // User 4 distance --> MCS 6 + std::vector packetSize1; + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + std::vector estThrFdTbfqDl1; + estThrFdTbfqDl1.push_back (132000); // User 0 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl1.push_back (132000); // User 1 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl1.push_back (132000); // User 2 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl1.push_back (132000); // User 3 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl1.push_back (132000); // User 4 estimated TTI throughput from FDTBFQ + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase2 (dist1,estThrFdTbfqDl1,packetSize1,1)); + + // Traffic2 info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 + 1/597000 + 1/309000) = 694720 byte/s + // 232000 * 5 = 1160000 > 694720 -> estimated throughput in downlink = 694720 / 5 = 138944 byte/sec + std::vector dist2; + dist2.push_back (0); // User 0 distance --> MCS 28 + dist2.push_back (3000); // User 1 distance --> MCS 24 + dist2.push_back (6000); // User 2 distance --> MCS 16 + dist2.push_back (9000); // User 3 distance --> MCS 12 + dist2.push_back (15000); // User 4 distance --> MCS 6 + std::vector packetSize2; + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + std::vector estThrFdTbfqDl2; + estThrFdTbfqDl2.push_back (138944); // User 0 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl2.push_back (138944); // User 1 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl2.push_back (138944); // User 2 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl2.push_back (138944); // User 3 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl2.push_back (138944); // User 4 estimated TTI throughput from FDTBFQ + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase2 (dist2,estThrFdTbfqDl2,packetSize2,1)); + + // Test Case 3: heterogeneous flow test in FDTBFQ + // UDP traffic: payload size = [100,200,300] bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> [132000, 232000, 332000] byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 ) = 1312417 byte/s + // 132000 + 232000 + 332000 = 696000 < 1312417 -> estimated throughput in downlink = [132000, 232000, 332000] byte/sec + std::vector dist3; + dist3.push_back (0); // User 0 distance --> MCS 28 + dist3.push_back (3000); // User 1 distance --> MCS 24 + dist3.push_back (6000); // User 2 distance --> MCS 16 + std::vector packetSize3; + packetSize3.push_back (100); + packetSize3.push_back (200); + packetSize3.push_back (300); + std::vector estThrFdTbfqDl3; + estThrFdTbfqDl3.push_back (132000); // User 0 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl3.push_back (232000); // User 1 estimated TTI throughput from FDTBFQ + estThrFdTbfqDl3.push_back (332000); // User 2 estimated TTI throughput from FDTBFQ + AddTestCase (new LenaFdTbfqFfMacSchedulerTestCase2 (dist3,estThrFdTbfqDl3,packetSize3,1)); + +} + +static LenaTestFdTbfqFfMacSchedulerSuite lenaTestFdTbfqFfMacSchedulerSuite; + +// --------------- T E S T - C A S E # 1 ------------------------------ + + +std::string +LenaFdTbfqFfMacSchedulerTestCase1::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + + +LenaFdTbfqFfMacSchedulerTestCase1::LenaFdTbfqFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl, uint16_t packetSize, uint16_t interval) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_packetSize (packetSize), + m_interval (interval), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaFdTbfqFfMacSchedulerTestCase1::~LenaFdTbfqFfMacSchedulerTestCase1 () +{ +} + +void +LenaFdTbfqFfMacSchedulerTestCase1::DoRun (void) +{ + Ptr lteHelper = CreateObject (); + Ptr epcHelper = CreateObject (); + lteHelper->SetEpcHelper (epcHelper); + + Ptr pgw = epcHelper->GetPgwNode (); + + // Create a single RemoteHost + NodeContainer remoteHostContainer; + remoteHostContainer.Create (1); + Ptr remoteHost = remoteHostContainer.Get (0); + InternetStackHelper internet; + internet.Install (remoteHostContainer); + + // Create the Internet + PointToPointHelper p2ph; + p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s"))); + p2ph.SetDeviceAttribute ("Mtu", UintegerValue (1500)); + p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.001))); + NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost); + Ipv4AddressHelper ipv4h; + ipv4h.SetBase ("1.0.0.0", "255.0.0.0"); + Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices); + // interface 0 is localhost, 1 is the p2p device + Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress (1); + + Ipv4StaticRoutingHelper ipv4RoutingHelper; + Ptr remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject ()); + remoteHostStaticRouting->AddNetworkRouteTo (Ipv4Address ("7.0.0.0"), Ipv4Mask ("255.0.0.0"), 1); + + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + + lteHelper->SetAttribute ("EpsBearerToRlcMapping", EnumValue (LteHelper::RLC_UM_ALWAYS)); + + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("FdTbfqFfMacScheduler", LOG_LEVEL_AlL); + LogComponentEnable ("LenaTestFdTbfqFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::FdTbfqFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + // Install the IP stack on the UEs + internet.Install (ueNodes); + Ipv4InterfaceContainer ueIpIface; + ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueDevs)); + // Assign IP address to UEs, and install applications + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + Ptr ueNode = ueNodes.Get (u); + // Set the default gateway for the UE + Ptr ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject ()); + ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1); + } + +// Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + GbrQosInformation qos; + qos.gbrDl = (m_packetSize + 32) * (1000 / m_interval) * 8; // bit/s, considering IP, UDP, RLC, PDCP header size + qos.gbrUl = 0; + qos.mbrDl = qos.gbrDl; + qos.mbrUl = 0; + + EpsBearer bearer (q, qos); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + lteHelper->EnableMacTraces (); + lteHelper->EnableRlcTraces (); + lteHelper->EnablePdcpTraces (); + + // Install downlind and uplink applications + uint16_t dlPort = 1234; + uint16_t ulPort = 2000; + PacketSinkHelper dlPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort)); + PacketSinkHelper ulPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), ulPort)); + ApplicationContainer clientApps; + ApplicationContainer serverApps; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + ++ulPort; + serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get (u))); // receive packets from remotehost + serverApps.Add (ulPacketSinkHelper.Install (remoteHost)); // receive packets from UEs + + UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort); // uplink packets generator + dlClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + dlClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + dlClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + UdpClientHelper ulClient (remoteHostAddr, ulPort); // downlink packets generator + ulClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + ulClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + ulClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + clientApps.Add (dlClient.Install (remoteHost)); + clientApps.Add (ulClient.Install (ueNodes.Get (u))); + } + + serverApps.Start (Seconds (0.001)); + clientApps.Start (Seconds (0.001)); + + double simulationTime = 2.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "token bank fair queue" manner + */ + + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + uint64_t data = rlcStats->GetDlRxData (imsi, lcId); + dlDataRxed.push_back (data); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Unfair Throughput!"); + } + + /** + * Check that the uplink assignation is done in a "round robin" manner + */ + + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + + + +// --------------- T E S T - C A S E # 2 ------------------------------ + + +std::string +LenaFdTbfqFfMacSchedulerTestCase2::BuildNameString (uint16_t nUser, std::vector dist) +{ + std::ostringstream oss; + oss << "distances (m) = [ " ; + for (std::vector::iterator it = dist.begin (); it != dist.end (); ++it) + { + oss << *it << " "; + } + oss << "]"; + return oss.str (); +} + + +LenaFdTbfqFfMacSchedulerTestCase2::LenaFdTbfqFfMacSchedulerTestCase2 (std::vector dist, std::vector estThrFdTbfqDl, std::vector packetSize, uint16_t interval) + : TestCase (BuildNameString (dist.size (), dist)), + m_nUser (dist.size ()), + m_dist (dist), + m_packetSize (packetSize), + m_interval (interval), + m_estThrFdTbfqDl (estThrFdTbfqDl) +{ +} + +LenaFdTbfqFfMacSchedulerTestCase2::~LenaFdTbfqFfMacSchedulerTestCase2 () +{ +} + +void +LenaFdTbfqFfMacSchedulerTestCase2::DoRun (void) +{ + Ptr lteHelper = CreateObject (); + Ptr epcHelper = CreateObject (); + lteHelper->SetEpcHelper (epcHelper); + + Ptr pgw = epcHelper->GetPgwNode (); + + // Create a single RemoteHost + NodeContainer remoteHostContainer; + remoteHostContainer.Create (1); + Ptr remoteHost = remoteHostContainer.Get (0); + InternetStackHelper internet; + internet.Install (remoteHostContainer); + + // Create the Internet + PointToPointHelper p2ph; + p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s"))); + p2ph.SetDeviceAttribute ("Mtu", UintegerValue (1500)); + p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.001))); + NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost); + Ipv4AddressHelper ipv4h; + ipv4h.SetBase ("1.0.0.0", "255.0.0.0"); + Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices); + // interface 0 is localhost, 1 is the p2p device + Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress (1); + + Ipv4StaticRoutingHelper ipv4RoutingHelper; + Ptr remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject ()); + remoteHostStaticRouting->AddNetworkRouteTo (Ipv4Address ("7.0.0.0"), Ipv4Mask ("255.0.0.0"), 1); + + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + + lteHelper->SetAttribute ("EpsBearerToRlcMapping", EnumValue (LteHelper::RLC_UM_ALWAYS)); + + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("FdTbfqFfMacScheduler", LOG_LEVEL_AlL); + LogComponentEnable ("LenaTestFdTbfqFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::FdTbfqFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + // Install the IP stack on the UEs + internet.Install (ueNodes); + Ipv4InterfaceContainer ueIpIface; + ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueDevs)); + // Assign IP address to UEs, and install applications + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + Ptr ueNode = ueNodes.Get (u); + // Set the default gateway for the UE + Ptr ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject ()); + ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1); + } + +// Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + GbrQosInformation qos; + uint16_t mbrDl = 0; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + mbrDl = mbrDl + m_packetSize.at (u); + } + mbrDl = mbrDl / ueNodes.GetN (); + qos.gbrDl = (mbrDl + 32) * (1000 / m_interval) * 8; // bit/s, considering IP, UDP, RLC, PDCP header size + qos.gbrUl = 0; + qos.mbrDl = qos.gbrDl; + qos.mbrUl = 0; + EpsBearer bearer (q, qos); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + lteHelper->EnableMacTraces (); + lteHelper->EnableRlcTraces (); + lteHelper->EnablePdcpTraces (); + + // Install downlind and uplink applications + uint16_t dlPort = 1234; + uint16_t ulPort = 2000; + PacketSinkHelper dlPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort)); + PacketSinkHelper ulPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), ulPort)); + ApplicationContainer clientApps; + ApplicationContainer serverApps; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + ++ulPort; + serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get (u))); // receive packets from remotehost + serverApps.Add (ulPacketSinkHelper.Install (remoteHost)); // receive packets from UEs + + UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort); // uplink packets generator + dlClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + dlClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + dlClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize.at (u))); + + UdpClientHelper ulClient (remoteHostAddr, ulPort); // downlink packets generator + ulClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + ulClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + ulClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize.at (u))); + + clientApps.Add (dlClient.Install (remoteHost)); + clientApps.Add (ulClient.Install (ueNodes.Get (u))); + } + + serverApps.Start (Seconds (0.001)); + clientApps.Start (Seconds (0.001)); + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "token bank fair queue" manner + */ + + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s)"); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " dist " << m_dist.at (i) << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_nUser); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_estThrFdTbfqDl.at (i), m_estThrFdTbfqDl.at (i) * tolerance, " Unfair Throughput!"); + } + + Simulator::Destroy (); + +} + + +} // namespace ns3 + + + + diff --git a/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h b/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h new file mode 100644 index 000000000..d89fcbc8b --- /dev/null +++ b/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_FDTBFQ_FF_MAC_SCHEDULER_H +#define LENA_TEST_FDTBFQ_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +namespace ns3 { + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is equal among users is consistent with the definition of token bank fair +* queue scheduling +*/ +class LenaFdTbfqFfMacSchedulerTestCase1 : public TestCase +{ +public: + LenaFdTbfqFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl, uint16_t packetSize, uint16_t interval); + virtual ~LenaFdTbfqFfMacSchedulerTestCase1 (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + uint16_t m_packetSize; // byte + uint16_t m_interval; // ms + double m_thrRefDl; + double m_thrRefUl; +}; + + +class LenaFdTbfqFfMacSchedulerTestCase2 : public TestCase +{ +public: + LenaFdTbfqFfMacSchedulerTestCase2 (std::vector dist, std::vector estThrFdTbfqDl, std::vector packetSize, uint16_t interval); + virtual ~LenaFdTbfqFfMacSchedulerTestCase2 (); + +private: + static std::string BuildNameString (uint16_t nUser, std::vector dist); + virtual void DoRun (void); + uint16_t m_nUser; + std::vector m_dist; + std::vector m_packetSize; // byte + uint16_t m_interval; // ms + std::vector m_estThrFdTbfqDl; +}; + + +class LenaTestFdTbfqFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestFdTbfqFfMacSchedulerSuite (); +}; + + + + +} // namespace ns3 + + +#endif /* LENA_TEST_FDTBFQ_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-pss-ff-mac-scheduler.cc b/src/lte/test/lte-test-pss-ff-mac-scheduler.cc new file mode 100644 index 000000000..ed771ffd4 --- /dev/null +++ b/src/lte/test/lte-test-pss-ff-mac-scheduler.cc @@ -0,0 +1,791 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + +#include "ns3/epc-helper.h" +#include "ns3/network-module.h" +#include "ns3/ipv4-global-routing-helper.h" +#include "ns3/internet-module.h" +#include "ns3/applications-module.h" +#include "ns3/point-to-point-helper.h" + +#include "lte-test-pss-ff-mac-scheduler.h" + +NS_LOG_COMPONENT_DEFINE ("LenaTestPssFfMacCheduler"); + +namespace ns3 { + +LenaTestPssFfMacSchedulerSuite::LenaTestPssFfMacSchedulerSuite () + : TestSuite ("lte-pss-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestPssFfMacSchedulerSuite"); + + // General config + // Traffic: UDP traffic with fixed rate + // Token generation rate = traffic rate + // RLC header length = 2 bytes, PDCP header = 2 bytes + // Simulation time = 1.0 sec + // Throughput in this file is calculated in RLC layer + + //Test Case 1: homogeneous flow test in PSS (same distance) + + // DOWNLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 2196000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 2196000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 < 2196000 -> throughput = 232000 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 2196000 -> throughput = 2196000 / 12 = 183000 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 2196000 -> throughput = 2196000 / 15 = 146400 byte/sec + // UPLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 > 232000 -> throughput = 232000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 > 232000 -> throughput = 232000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 < 232000 -> throughput = 185000 bytes/sec + // 15 users -> 1 PRB at Itbs 26 -> 89 -> 89000 bytes/sec + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (1,0,0,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (3,0,0,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (6,0,0,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (12,0,0,183000,185000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (15,0,0,146400,89000,200,1)); + + // DOWNLINK - DISTANCE 3000 -> MCS 24 -> Itbs 20 (from table 7.1.7.2.1-1 of 36.213) + // DOWNLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 1383000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 1383000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 > 1383000 -> throughput = 1383000 / 6 = 230500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 1383000 -> throughput = 1383000 / 12 = 115250 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 1383000 -> throughput = 1383000 / 15 = 92200 byte/sec + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 > 232000 -> throughput = 232000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 < 232000 -> throughput = 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 < 232000 -> throughput = 97000 bytes/sec + // 15 users -> 1 PRB at Itbs 18 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (1,0,3000,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (3,0,3000,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (6,0,3000,230500,193000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (12,0,3000,115250,97000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (15,0,3000,92200,47000,200,1)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 15 -> 903 -> 903000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 903000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 903000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 > 903000 -> throughput = 903000 / 6 = 150500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 903000 -> throughput = 903000 / 12 = 75250 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 903000 -> throughput = 903000 / 15 = 60200 byte/sec + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 < 232000 -> throughput = 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 < 232000 -> throughput = 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 < 232000 -> throughput = 47000 bytes/sec + // 15 users -> 1 PRB at Itbs 11 -> 22 -> 22000 bytes/sec + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (1,0,6000,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (3,0,6000,232000,201000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (6,0,6000,150500,97000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (12,0,6000,75250,47000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (15,0,6000,60200,22000,200,1)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 11 -> 597 -> 597000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 597000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 > 597000 -> througphut = 597000 / 3 = 199000byte/sec + // 6 user -> 232000 * 6 = 139200 > 597000 -> throughput = 597000 / 6 = 99500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 597000 -> throughput = 597000 / 12 = 49750 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 597000 -> throughput = 597000 / 15 = 39800 byte/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 < 232000 -> throughput = 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 < 232000 -> throughput = 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 < 232000 -> throughput = 32000 bytes/sec + // 15 users -> 1 PRB at Itbs 8 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (1,0,9000,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (3,0,9000,199000,137000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (6,0,9000,99500,67000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (12,0,9000,49750,32000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (15,0,9000,39800,15000,200,1)); + + // DONWLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 6 -> 309 -> 309000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 309000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 > 309000 -> througphut = 309000 / 3 = 103000byte/sec + // 6 user -> 232000 * 6 = 139200 > 309000 -> throughput = 309000 / 6 = 51500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 309000 -> throughput = 309000 / 12 = 25750 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 309000 -> throughput = 309000 / 15 = 20600 byte/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 < 232000 -> throughput = 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 < 232000 -> throughput = 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 < 232000 -> throughput = 15000 bytes/sec + // 15 users -> 1 PRB at Itbs 6 -> 7 -> 7000 bytes/sec + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (1,0,15000,232000,232000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (3,0,15000,103000,69000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (6,0,15000,51500,32000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (12,0,15000,25750,15000,200,1)); + AddTestCase (new LenaPssFfMacSchedulerTestCase1 (15,0,15000,20600,7000,200,1)); + + // Test Case 2: homogeneous flow test in PSS (different distance) + // Traffic1 info + // UDP traffic: payload size = 100 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 132000 byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 + 1/597000 + 1/309000) = 694720 byte/s + // 132000 * 5 = 660000 < 694720 -> estimated throughput in downlink = 694720/5 = 132000 bytes/sec + std::vector dist1; + dist1.push_back (0); // User 0 distance --> MCS 28 + dist1.push_back (3000); // User 1 distance --> MCS 24 + dist1.push_back (6000); // User 2 distance --> MCS 16 + dist1.push_back (9000); // User 3 distance --> MCS 12 + dist1.push_back (15000); // User 4 distance --> MCS 6 + std::vector packetSize1; + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + std::vector estThrPssDl1; + estThrPssDl1.push_back (132000); + estThrPssDl1.push_back (132000); + estThrPssDl1.push_back (132000); + estThrPssDl1.push_back (132000); + estThrPssDl1.push_back (132000); + AddTestCase (new LenaPssFfMacSchedulerTestCase2 (dist1,estThrPssDl1,packetSize1,1)); + + // Traffic2 info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 132000 byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 + 1/597000 + 1/309000) = 694720 byte/s + // 232000 * 5 = 1160000 > 694720 -> estimated throughput in downlink = 694720/5 = 138944 bytes/sec + std::vector dist2; + dist2.push_back (0); // User 0 distance --> MCS 28 + dist2.push_back (3000); // User 1 distance --> MCS 24 + dist2.push_back (6000); // User 2 distance --> MCS 16 + dist2.push_back (9000); // User 3 distance --> MCS 12 + dist2.push_back (15000); // User 4 distance --> MCS 6 + std::vector packetSize2; + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + std::vector estThrPssDl2; + estThrPssDl2.push_back (138944); + estThrPssDl2.push_back (138944); + estThrPssDl2.push_back (138944); + estThrPssDl2.push_back (138944); + estThrPssDl2.push_back (138944); + AddTestCase (new LenaPssFfMacSchedulerTestCase2 (dist2,estThrPssDl2,packetSize2,1)); + + // Test Case 3: : heterogeneous flow test in PSS (same distance) + // Traffic3 info: + // UDP traffic: payload size = [100, 200,300,400,500] bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Maximum throughput = 2196000 byte/sec + // 132000 + 232000 + 332000 + 432000 + 532000 = 1660000 < 2196000 -> estimated throughput in downlink = [132000, 232000,332000, 432000, 53200] bytes/sec + std::vector dist3; + dist3.push_back (0); // User 0 distance --> MCS 28 + dist3.push_back (0); // User 1 distance --> MCS 24 + dist3.push_back (0); // User 2 distance --> MCS 16 + dist3.push_back (0); // User 3 distance --> MCS 12 + dist3.push_back (0); // User 4 distance --> MCS 6 + std::vector packetSize3; + packetSize3.push_back (100); + packetSize3.push_back (200); + packetSize3.push_back (300); + packetSize3.push_back (400); + packetSize3.push_back (500); + std::vector estThrPssDl3; + estThrPssDl3.push_back (132000); + estThrPssDl3.push_back (232000); + estThrPssDl3.push_back (332000); + estThrPssDl3.push_back (432000); + estThrPssDl3.push_back (532000); + AddTestCase (new LenaPssFfMacSchedulerTestCase2 (dist3,estThrPssDl3,packetSize3,1)); + + + // Test Case 4: heterogeneous flow test in PSS (different distance) + // Traffic4 info + // UDP traffic: payload size = [100,200,300] bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> [132000, 232000, 332000] byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 ) = 1312417 byte/s + // 132000 + 232000 + 332000 = 696000 < 1312417 -> estimated throughput in downlink = [132000, 232000, 332000] byte/sec + std::vector dist4; + dist4.push_back (0); // User 0 distance --> MCS 28 + dist4.push_back (3000); // User 1 distance --> MCS 24 + dist4.push_back (6000); // User 2 distance --> MCS 16 + std::vector packetSize4; + packetSize4.push_back (100); + packetSize4.push_back (200); + packetSize4.push_back (300); + std::vector estThrPssDl4; + estThrPssDl4.push_back (132000); // User 0 estimated TTI throughput from PSS + estThrPssDl4.push_back (232000); // User 1 estimated TTI throughput from PSS + estThrPssDl4.push_back (332000); // User 2 estimated TTI throughput from PSS + AddTestCase (new LenaPssFfMacSchedulerTestCase2 (dist4,estThrPssDl4,packetSize4,1)); +} + +static LenaTestPssFfMacSchedulerSuite lenaTestPssFfMacSchedulerSuite; + +// --------------- T E S T - C A S E # 1 ------------------------------ + + +std::string +LenaPssFfMacSchedulerTestCase1::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + + +LenaPssFfMacSchedulerTestCase1::LenaPssFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl, uint16_t packetSize, uint16_t interval) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_packetSize (packetSize), + m_interval (interval), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaPssFfMacSchedulerTestCase1::~LenaPssFfMacSchedulerTestCase1 () +{ +} + +void +LenaPssFfMacSchedulerTestCase1::DoRun (void) +{ + Ptr lteHelper = CreateObject (); + Ptr epcHelper = CreateObject (); + lteHelper->SetEpcHelper (epcHelper); + + Ptr pgw = epcHelper->GetPgwNode (); + + // Create a single RemoteHost + NodeContainer remoteHostContainer; + remoteHostContainer.Create (1); + Ptr remoteHost = remoteHostContainer.Get (0); + InternetStackHelper internet; + internet.Install (remoteHostContainer); + + // Create the Internet + PointToPointHelper p2ph; + p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s"))); + p2ph.SetDeviceAttribute ("Mtu", UintegerValue (1500)); + p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.001))); + NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost); + Ipv4AddressHelper ipv4h; + ipv4h.SetBase ("1.0.0.0", "255.0.0.0"); + Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices); + // interface 0 is localhost, 1 is the p2p device + Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress (1); + + Ipv4StaticRoutingHelper ipv4RoutingHelper; + Ptr remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject ()); + remoteHostStaticRouting->AddNetworkRouteTo (Ipv4Address ("7.0.0.0"), Ipv4Mask ("255.0.0.0"), 1); + + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + + lteHelper->SetAttribute ("EpsBearerToRlcMapping", EnumValue (LteHelper::RLC_UM_ALWAYS)); + + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("PssFfMacScheduler", LOG_LEVEL_AlL); + LogComponentEnable ("LenaTestPssFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::PssFfMacScheduler"); + lteHelper->SetSchedulerAttribute("PssFdSchedulerType", StringValue("PFsch")); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + // Install the IP stack on the UEs + internet.Install (ueNodes); + Ipv4InterfaceContainer ueIpIface; + ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueDevs)); + // Assign IP address to UEs, and install applications + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + Ptr ueNode = ueNodes.Get (u); + // Set the default gateway for the UE + Ptr ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject ()); + ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1); + + // Activate an EPS bearer + Ptr ueDevice = ueDevs.Get (u); + GbrQosInformation qos; + qos.gbrDl = (m_packetSize + 32) * (1000 / m_interval) * 8 * 1; // bit/ + qos.gbrUl = (m_packetSize + 32) * (1000 / m_interval) * 8 * 1; + qos.mbrDl = 0; + qos.mbrUl = 0; + + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q, qos); + lteHelper->ActivateEpsBearer (ueDevice, bearer, EpcTft::Default ()); + } + + lteHelper->EnableMacTraces (); + lteHelper->EnableRlcTraces (); + lteHelper->EnablePdcpTraces (); + + // Install downlind and uplink applications + uint16_t dlPort = 1234; + uint16_t ulPort = 2000; + PacketSinkHelper dlPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort)); + PacketSinkHelper ulPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), ulPort)); + ApplicationContainer clientApps; + ApplicationContainer serverApps; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + ++ulPort; + serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get (u))); // receive packets from remotehost + serverApps.Add (ulPacketSinkHelper.Install (remoteHost)); // receive packets from UEs + + UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort); // uplink packets generator + dlClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + dlClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + dlClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + UdpClientHelper ulClient (remoteHostAddr, ulPort); // downlink packets generator + ulClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + ulClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + ulClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + clientApps.Add (dlClient.Install (remoteHost)); + clientApps.Add (ulClient.Install (ueNodes.Get (u))); + } + + serverApps.Start (Seconds (0.001)); + clientApps.Start (Seconds (0.001)); + + double simulationTime = 2.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "priority set scheduler" manner + */ + + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + uint64_t data = rlcStats->GetDlRxData (imsi, lcId); + dlDataRxed.push_back (data); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Unfair Throughput!"); + } + + /** + * Check that the uplink assignation is done in a "round robin" manner + */ + + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + + + +// --------------- T E S T - C A S E # 2 ------------------------------ + + +std::string +LenaPssFfMacSchedulerTestCase2::BuildNameString (uint16_t nUser, std::vector dist) +{ + std::ostringstream oss; + oss << "distances (m) = [ " ; + for (std::vector::iterator it = dist.begin (); it != dist.end (); ++it) + { + oss << *it << " "; + } + oss << "]"; + return oss.str (); +} + + +LenaPssFfMacSchedulerTestCase2::LenaPssFfMacSchedulerTestCase2 (std::vector dist, std::vector estThrPssDl, std::vector packetSize, uint16_t interval) + : TestCase (BuildNameString (dist.size (), dist)), + m_nUser (dist.size ()), + m_dist (dist), + m_packetSize (packetSize), + m_interval (interval), + m_estThrPssDl (estThrPssDl) +{ +} + +LenaPssFfMacSchedulerTestCase2::~LenaPssFfMacSchedulerTestCase2 () +{ +} + +void +LenaPssFfMacSchedulerTestCase2::DoRun (void) +{ + Ptr lteHelper = CreateObject (); + Ptr epcHelper = CreateObject (); + lteHelper->SetEpcHelper (epcHelper); + + Ptr pgw = epcHelper->GetPgwNode (); + + // Create a single RemoteHost + NodeContainer remoteHostContainer; + remoteHostContainer.Create (1); + Ptr remoteHost = remoteHostContainer.Get (0); + InternetStackHelper internet; + internet.Install (remoteHostContainer); + + // Create the Internet + PointToPointHelper p2ph; + p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s"))); + p2ph.SetDeviceAttribute ("Mtu", UintegerValue (1500)); + p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.001))); + NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost); + Ipv4AddressHelper ipv4h; + ipv4h.SetBase ("1.0.0.0", "255.0.0.0"); + Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices); + // interface 0 is localhost, 1 is the p2p device + Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress (1); + + Ipv4StaticRoutingHelper ipv4RoutingHelper; + Ptr remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject ()); + remoteHostStaticRouting->AddNetworkRouteTo (Ipv4Address ("7.0.0.0"), Ipv4Mask ("255.0.0.0"), 1); + + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + + lteHelper->SetAttribute ("EpsBearerToRlcMapping", EnumValue (LteHelper::RLC_UM_ALWAYS)); + + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("PssFfMacScheduler", LOG_LEVEL_AlL); + LogComponentEnable ("LenaTestPssFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::PssFfMacScheduler"); + lteHelper->SetSchedulerAttribute ("PssFdSchedulerType", StringValue ("PFsch")); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + // Install the IP stack on the UEs + internet.Install (ueNodes); + Ipv4InterfaceContainer ueIpIface; + ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueDevs)); + // Assign IP address to UEs, and install applications + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + Ptr ueNode = ueNodes.Get (u); + // Set the default gateway for the UE + Ptr ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject ()); + ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1); + + // Activate an EPS bearer + Ptr ueDevice = ueDevs.Get (u); + GbrQosInformation qos; + qos.gbrDl = (m_packetSize.at (u) + 32) * (1000 / m_interval) * 8 * 1; // bit/s, = Target Bit Rate(TBR) + qos.gbrUl = (m_packetSize.at (u) + 32) * (1000 / m_interval) * 8 * 1; + qos.mbrDl = qos.gbrDl; + qos.mbrUl = qos.gbrUl; + + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q, qos); + lteHelper->ActivateEpsBearer (ueDevice, bearer, EpcTft::Default ()); + + } + + lteHelper->EnableMacTraces (); + lteHelper->EnableRlcTraces (); + lteHelper->EnablePdcpTraces (); + + // Install downlind and uplink applications + uint16_t dlPort = 1234; + uint16_t ulPort = 2000; + PacketSinkHelper dlPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort)); + PacketSinkHelper ulPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), ulPort)); + ApplicationContainer clientApps; + ApplicationContainer serverApps; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + ++ulPort; + serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get (u))); // receive packets from remotehost + serverApps.Add (ulPacketSinkHelper.Install (remoteHost)); // receive packets from UEs + + UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort); // uplink packets generator + dlClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + dlClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + dlClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize.at (u))); + + UdpClientHelper ulClient (remoteHostAddr, ulPort); // downlink packets generator + ulClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + ulClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + ulClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize.at (u))); + + clientApps.Add (dlClient.Install (remoteHost)); + clientApps.Add (ulClient.Install (ueNodes.Get (u))); + } + + serverApps.Start (Seconds (0.001)); + clientApps.Start (Seconds (0.001)); + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "priority set scheduler" manner + */ + + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s)"); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " dist " << m_dist.at (i) << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_nUser); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_estThrPssDl.at (i), m_estThrPssDl.at (i) * tolerance, " Unfair Throughput!"); + } + + Simulator::Destroy (); + +} + + +} // namespace ns3 + + + + diff --git a/src/lte/test/lte-test-pss-ff-mac-scheduler.h b/src/lte/test/lte-test-pss-ff-mac-scheduler.h new file mode 100644 index 000000000..133698caf --- /dev/null +++ b/src/lte/test/lte-test-pss-ff-mac-scheduler.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_PSS_FF_MAC_SCHEDULER_H +#define LENA_TEST_PSS_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +namespace ns3 { + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is equal among users is consistent with the definition of priority set scheduling +*/ +class LenaPssFfMacSchedulerTestCase1 : public TestCase +{ +public: + LenaPssFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl, uint16_t packetSize, uint16_t interval); + virtual ~LenaPssFfMacSchedulerTestCase1 (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + uint16_t m_packetSize; // byte + uint16_t m_interval; // ms + double m_thrRefDl; + double m_thrRefUl; +}; + + +class LenaPssFfMacSchedulerTestCase2 : public TestCase +{ +public: + LenaPssFfMacSchedulerTestCase2 (std::vector dist, std::vector estThrPssDl, std::vector packetSize, uint16_t interval); + virtual ~LenaPssFfMacSchedulerTestCase2 (); + +private: + static std::string BuildNameString (uint16_t nUser, std::vector dist); + virtual void DoRun (void); + uint16_t m_nUser; + std::vector m_dist; + std::vector m_packetSize; // byte + uint16_t m_interval; // ms + std::vector m_estThrPssDl; +}; + + +class LenaTestPssFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestPssFfMacSchedulerSuite (); +}; + + + + +} // namespace ns3 + + +#endif /* LENA_TEST_PSS_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc b/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc new file mode 100644 index 000000000..f7320a4b8 --- /dev/null +++ b/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc @@ -0,0 +1,548 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + +#include "lte-test-tdbet-ff-mac-scheduler.h" + +NS_LOG_COMPONENT_DEFINE ("LenaTestTdBetFfMacCheduler"); + +namespace ns3 { + +LenaTestTdBetFfMacSchedulerSuite::LenaTestTdBetFfMacSchedulerSuite () + : TestSuite ("lte-tdbet-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestTdBetFfMacSchedulerSuite"); + + //Test Case 1: AMC works in TDBET + + // DOWNLINK - DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 26 -> 2196 -> 2196000 bytes/sec + // 3 users -> 2196000 among 3 users -> 732000 bytes/sec + // 6 users -> 2196000 among 6 users -> 366000 bytes/sec + // 12 users -> 2196000 among 12 users -> 183000 bytes/sec + // 15 users -> 2196000 among 15 users -> 146400 bytes/sec + // UPLINK- DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 bytes/sec + // 15 users -> 1 PRB at Itbs 26 -> 89 -> 89000 bytes/sec + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (1,0,0,2196000,2292000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (3,0,0,732000,749000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (6,0,0,366000,373000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (12,0,0,183000,185000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (15,0,0,146400,89000)); + + // DOWNLINK - DISTANCE 3000 -> MCS 24 -> Itbs 30 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 20 -> 1383 -> 1383000 bytes/sec + // 3 users -> 1383000 among 3 users -> 461000 bytes/sec + // 6 users -> 1383000 among 6 users -> 230500 bytes/sec + // 12 users -> 1383000 among 12 users -> 115250 bytes/sec + // 15 users -> 1383000 among 15 users -> 92200 bytes/sec + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 bytes/sec + // 15 users -> 1 PRB at Itbs 18 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (1,0,3000,1383000,1239000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (3,0,3000,461000,389000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (6,0,3000,230500,193000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (12,0,3000,115250,97000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (15,0,3000,92200,47000)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 15 -> 903 -> 903000 bytes/sec + // 3 users -> 903000 among 3 users -> 301000 bytes/sec + // 6 users -> 903000 among 6 users -> 150500 bytes/sec + // 12 users -> 903000 among 12 users -> 75250 bytes/sec + // 15 users -> 903000 among 15 users -> 60200 bytes/sec + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 bytes/sec + // 15 users -> 1 PRB at Itbs 11 -> 22 -> 22000 bytes/sec + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (1,0,6000,903000,621000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (3,0,6000,301000,201000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (6,0,6000,150500,97000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (12,0,6000,75250,47000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (15,0,6000,60200,22000)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 11 -> 597 -> 597000 bytes/sec + // 3 users -> 597000 among 3 users -> 199000 bytes/sec + // 6 users -> 597000 among 6 users -> 99500 bytes/sec + // 12 users -> 597000 among 12 users -> 49750 bytes/sec + // 15 users -> 597000 among 15 users -> 39800 bytes/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 bytes/sec + // 15 users -> 1 PRB at Itbs 8 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (1,0,9000,597000,437000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (3,0,9000,199000,137000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (6,0,9000,99500,67000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (12,0,9000,49750,32000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (15,0,9000,39800,15000)); + + // DONWLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 6 -> 309 -> 309000 bytes/sec + // 3 users -> 309000 among 3 users -> 103000 bytes/sec + // 6 users -> 309000 among 6 users -> 51500 bytes/sec + // 12 users -> 309000 among 12 users -> 25750 bytes/sec + // 15 users -> 309000 among 15 users -> 20600 bytes/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 bytes/sec + // 15 users -> 1 PRB at Itbs 6 -> 7 -> 7000 bytes/sec + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (1,0,15000,309000,233000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (3,0,15000,103000,69000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (6,0,15000,51500,32000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (12,0,15000,25750,15000)); + AddTestCase (new LenaTdBetFfMacSchedulerTestCase1 (15,0,15000,20600,7000)); + + // Test Case 2: fairness check + + std::vector dist; + dist.push_back (0); // User 0 distance --> MCS 28 + dist.push_back (3000); // User 1 distance --> MCS 24 + dist.push_back (6000); // User 2 distance --> MCS 16 + dist.push_back (9000); // User 3 distance --> MCS 12 + dist.push_back (15000); // User 4 distance --> MCS 6 + std::vector estAchievableRateDl; + estAchievableRateDl.push_back (2196000); + estAchievableRateDl.push_back (1383000); + estAchievableRateDl.push_back (903000); + estAchievableRateDl.push_back (597000); + estAchievableRateDl.push_back (309000); + std::vector estThrTdBetUl; + estThrTdBetUl.push_back (469000); // User 0 estimated TTI throughput from TDBET + estThrTdBetUl.push_back (249000); // User 1 estimated TTI throughput from TDBET + estThrTdBetUl.push_back (125000); // User 2 estimated TTI throughput from TDBET + estThrTdBetUl.push_back (85000); // User 3 estimated TTI throughput from TDBET + estThrTdBetUl.push_back (41000); // User 4 estimated TTI throughput from TDBET + AddTestCase (new LenaTdBetFfMacSchedulerTestCase2 (dist, estAchievableRateDl, estThrTdBetUl)); + + +} + +static LenaTestTdBetFfMacSchedulerSuite lenaTestTdBetFfMacSchedulerSuite; + + +// --------------- T E S T - C A S E # 1 ------------------------------ + + +std::string +LenaTdBetFfMacSchedulerTestCase1::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + +LenaTdBetFfMacSchedulerTestCase1::LenaTdBetFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaTdBetFfMacSchedulerTestCase1::~LenaTdBetFfMacSchedulerTestCase1 () +{ +} + +void +LenaTdBetFfMacSchedulerTestCase1::DoRun (void) +{ + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("TdBetFfMacScheduler", LOG_LEVEL_ALL); + LogComponentEnable ("LenaTestTdBetFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + /** + * Initialize Simulation Scenario: 1 eNB and m_nUser UEs + */ + + Ptr lteHelper = CreateObject (); + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::TdBetFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + // Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + lteHelper->EnableRlcTraces (); + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "TD blind equal throughput" manner + */ + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + /** + * Check that the assignation is done in a "TD blind equal throughput" manner among users + * with equal SINRs: the bandwidth should be distributed according to the + * ratio of the estimated throughput per TTI of each user; therefore equally + * partitioning the whole bandwidth achievable from a single users in a TTI + */ + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Unfair Throughput!"); + } + + /** + * Check that the uplink assignation is done in a "TD blind equal throughput" manner + */ + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + /** + * Check that the assignation is done in a "TD blind equal throughput" manner among users + * with equal SINRs: the bandwidht should be distributed according to the + * ratio of the estimated throughput per TTI of each user; therefore equally + * partitioning the whole bandwidth achievable from a single users in a TTI + */ + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + + + +// --------------- T E S T - C A S E # 2 ------------------------------ + + +std::string +LenaTdBetFfMacSchedulerTestCase2::BuildNameString (uint16_t nUser, std::vector dist) +{ + std::ostringstream oss; + oss << "distances (m) = [ " ; + for (std::vector::iterator it = dist.begin (); it != dist.end (); ++it) + { + oss << *it << " "; + } + oss << "]"; + return oss.str (); +} + + +LenaTdBetFfMacSchedulerTestCase2::LenaTdBetFfMacSchedulerTestCase2 (std::vector dist, std::vector estAchievableRateDl, std::vector estThrTdBetUl) + : TestCase (BuildNameString (dist.size (), dist)), + m_nUser (dist.size ()), + m_dist (dist), + m_achievableRateDl (estAchievableRateDl), + m_estThrTdBetUl (estThrTdBetUl) +{ +} + +LenaTdBetFfMacSchedulerTestCase2::~LenaTdBetFfMacSchedulerTestCase2 () +{ +} + +void +LenaTdBetFfMacSchedulerTestCase2::DoRun (void) +{ + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("TdBetFfMacScheduler", LOG_LEVEL_ALL); + LogComponentEnable ("LenaTestTdBetFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); + // LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + /** + * Initialize Simulation Scenario: 1 eNB and m_nUser UEs + */ + + Ptr lteHelper = CreateObject (); + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::TdBetFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + // Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + lteHelper->EnableRlcTraces (); + + double simulationTime = 1; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s)"); + std::vector dlDataRxed; + double totalData = 0; + double estTotalThr = 0; + double estUeThr = 0; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + totalData += (double)dlDataRxed.at (i); + estTotalThr += 1 / m_achievableRateDl.at (i); + //NS_LOG_INFO ("\tUser " << i << " dist " << m_dist.at (i) << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_nUser); + } + + estTotalThr = m_nUser * (1 / estTotalThr); + estUeThr = estTotalThr / m_nUser; + + /** + * Check that the assignation is done in a "TD blind equal throughput" manner among users + * with different SINRs: the bandwidth should be distributed equally in long term + */ + for (int i = 0; i < m_nUser; i++) + { + double thrRatio = (double) 1 / m_nUser; + double estThrRatio = (double)dlDataRxed.at (i) / totalData; + NS_LOG_INFO ("\tUser " << i << " thrRatio " << thrRatio << " estThrRatio " << estThrRatio); + NS_TEST_ASSERT_MSG_EQ_TOL (estThrRatio, thrRatio, tolerance, " Unfair Throughput!"); + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, estUeThr, estUeThr * tolerance, " Unfair Throughput!"); + } + + /** + * Check that the assignation in uplink is done in a round robin manner. + */ + + NS_LOG_INFO ("UL - Test with " << m_nUser); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " dist " << m_dist.at (i) << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << (double)m_estThrTdBetUl.at (i)); + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, (double)m_estThrTdBetUl.at (i), (double)m_estThrTdBetUl.at (i) * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + + +} // namespace ns3 + + + + diff --git a/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h b/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h new file mode 100644 index 000000000..38bffda63 --- /dev/null +++ b/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_TDBET_FF_MAC_SCHEDULER_H +#define LENA_TEST_TDBET_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +namespace ns3 { + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is equal among users is consistent with the definition of blind equal throughput +* scheduling +*/ +class LenaTdBetFfMacSchedulerTestCase1 : public TestCase +{ +public: + LenaTdBetFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl); + virtual ~LenaTdBetFfMacSchedulerTestCase1 (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + double m_thrRefDl; + double m_thrRefUl; +}; + + +class LenaTdBetFfMacSchedulerTestCase2 : public TestCase +{ +public: + LenaTdBetFfMacSchedulerTestCase2 (std::vector dist, std::vector m_achievableRateDl, std::vector estThrTdBetUl); + virtual ~LenaTdBetFfMacSchedulerTestCase2 (); + +private: + static std::string BuildNameString (uint16_t nUser, std::vector dist); + virtual void DoRun (void); + uint16_t m_nUser; + std::vector m_dist; + std::vector m_achievableRateDl; + std::vector m_estThrTdBetUl; +}; + + + + +class LenaTestTdBetFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestTdBetFfMacSchedulerSuite (); +}; + + + + +} // namespace ns3 + + +#endif /* LENA_TEST_TDBET_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc b/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc new file mode 100644 index 000000000..948964120 --- /dev/null +++ b/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc @@ -0,0 +1,347 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include "lte-test-tdmt-ff-mac-scheduler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + + +NS_LOG_COMPONENT_DEFINE ("LenaTestTdMtFfMacCheduler"); + +using namespace ns3; + +LenaTestTdMtFfMacSchedulerSuite::LenaTestTdMtFfMacSchedulerSuite () + : TestSuite ("lte-tdmt-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestTdMtFfMacSchedulerSuite"); + + //Test Case : AMC works in TDMT + + // DOWNLINK - DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 26 -> 2196 -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 2196000 among 3 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 2196000 among 6 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 2196000 among 12 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 2196000 among 15 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK- DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 bytes/sec + // 15 users -> 1 PRB at Itbs 26 -> 89 -> 89000 bytes/sec + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (1,0,0,2196000,2292000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (3,0,0,2196000,749000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (6,0,0,2196000,373000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (12,0,0,2196000,185000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (15,0,0,2196000,89000)); + + // DOWNLINK - DISTANCE 3000 -> MCS 24 -> Itbs 30 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 20 -> 1383 -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 1383000 among 3 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 1383000 among 6 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 1383000 among 12 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 1383000 among 15 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 bytes/sec + // 15 users -> 1 PRB at Itbs 18 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (1,0,3000,1383000,1239000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (3,0,3000,1383000,389000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (6,0,3000,1383000,193000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (12,0,3000,1383000,97000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (15,0,3000,1383000,47000)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 15 -> 903 -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 903000 among 3 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 903000 among 6 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 903000 among 12 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 903000 among 15 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 bytes/sec + // 15 users -> 1 PRB at Itbs 11 -> 22 -> 22000 bytes/sec + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (1,0,6000,903000,621000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (3,0,6000,903000,201000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (6,0,6000,903000,97000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (12,0,6000,903000,47000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (15,0,6000,903000,22000)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 11 -> 597 -> 597000 bytes/sec + // 3 users -> 597000 among 3 users -> 199000 bytes/sec + // 6 users -> 597000 among 6 users -> 99500 bytes/sec + // 12 users -> 597000 among 12 users -> 49750 bytes/sec + // 15 users -> 597000 among 15 users -> 39800 bytes/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 bytes/sec + // 15 users -> 1 PRB at Itbs 8 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (1,0,9000,597000,437000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (3,0,9000,597000,137000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (6,0,9000,597000,67000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (12,0,9000,597000,32000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (15,0,9000,597000,15000)); + + // DONWLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 6 -> 309 -> 309000 bytes/sec + // 3 users -> 309000 among 3 users -> 103000 bytes/sec + // 6 users -> 309000 among 6 users -> 51500 bytes/sec + // 12 users -> 309000 among 12 users -> 25750 bytes/sec + // 15 users -> 309000 among 15 users -> 20600 bytes/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 bytes/sec + // 15 users -> 1 PRB at Itbs 6 -> 7 -> 7000 bytes/sec + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (1,0,15000,309000,233000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (3,0,15000,309000,69000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (6,0,15000,309000,32000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (12,0,15000,309000,15000)); + AddTestCase (new LenaTdMtFfMacSchedulerTestCase (15,0,15000,309000,7000)); + + +} + +static LenaTestTdMtFfMacSchedulerSuite lenaTestTdMtFfMacSchedulerSuite; + + +// --------------- T E S T - C A S E ------------------------------ + + +std::string +LenaTdMtFfMacSchedulerTestCase::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + +LenaTdMtFfMacSchedulerTestCase::LenaTdMtFfMacSchedulerTestCase (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaTdMtFfMacSchedulerTestCase::~LenaTdMtFfMacSchedulerTestCase () +{ +} + +void +LenaTdMtFfMacSchedulerTestCase::DoRun (void) +{ + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("TdMtFfMacScheduler", LOG_LEVEL_ALL); + LogComponentEnable ("LenaTestTdMtFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + /** + * Initialize Simulation Scenario: 1 eNB and m_nUser UEs + */ + + Ptr lteHelper = CreateObject (); + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::TdMtFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + // Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + lteHelper->EnableRlcTraces (); + lteHelper->EnableMacTraces (); + + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "maximum throughput" manner + */ + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + /** + * Check that the assignation is done in a "maximum throughput" manner among users + * with equal SINRs: all bandwidth should be allocated to the first UE in script + */ + for (int i = 0; i < 1; i++) + { + if (i == 0) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Invalid Throughput!"); + } + else + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, 0, tolerance, " Invalid Throughput!"); + } + } + + /** + * Check that the uplink assignation is done in a "maximum throughput" manner + */ + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + /** + * Check that the assignation is done in a "maximum throughput" manner among users + * with equal SINRs: the bandwidht should be distributed according to the + * ratio of the estimated throughput per TTI of each user; therefore equally + * partitioning the whole bandwidth achievable from a single users in a TTI + * + */ + for (int i = 0; i < 1; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + diff --git a/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h b/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h new file mode 100644 index 000000000..284f1afbb --- /dev/null +++ b/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_TDMT_FF_MAC_SCHEDULER_H +#define LENA_TEST_TDMT_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +using namespace ns3; + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is consistent with the definition of maximum throughput +* scheduling +*/ +class LenaTdMtFfMacSchedulerTestCase : public TestCase +{ +public: + LenaTdMtFfMacSchedulerTestCase (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl); + virtual ~LenaTdMtFfMacSchedulerTestCase (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + double m_thrRefDl; + double m_thrRefUl; +}; + +class LenaTestTdMtFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestTdMtFfMacSchedulerSuite (); +}; + + + + +#endif /* LENA_TEST_TDMT_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc b/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc new file mode 100644 index 000000000..82c58daf3 --- /dev/null +++ b/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc @@ -0,0 +1,760 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo , + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + +#include "ns3/epc-helper.h" +#include "ns3/network-module.h" +#include "ns3/ipv4-global-routing-helper.h" +#include "ns3/internet-module.h" +#include "ns3/applications-module.h" +#include "ns3/point-to-point-helper.h" + +#include "lte-test-tdtbfq-ff-mac-scheduler.h" + +NS_LOG_COMPONENT_DEFINE ("LenaTestTdTbfqFfMacCheduler"); + +namespace ns3 { + +LenaTestTdTbfqFfMacSchedulerSuite::LenaTestTdTbfqFfMacSchedulerSuite () + : TestSuite ("lte-tdtbfq-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestTdTbfqFfMacSchedulerSuite"); + + // General config + // Traffic: UDP traffic with fixed rate + // Token generation rate = traffic rate + // RLC header length = 2 bytes, PDCP header = 2 bytes + // Simulation time = 1.0 sec + // Throughput in this file is calculated in RLC layer + + //Test Case 1: homogeneous flow test in TDTBFQ (same distance) + + // DOWNLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 26 -> 2196 -> 2196000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 2196000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 2196000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 < 2196000 -> throughput = 232000 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 2196000 -> throughput = 2196000 / 12 = 183000 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 2196000 -> throughput = 2196000 / 15 = 146400 byte/sec + // UPLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 > 232000 -> throughput = 232000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 > 232000 -> throughput = 232000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 < 232000 -> throughput = 185000 bytes/sec + // 15 users -> 1 PRB at Itbs 26 -> 89 -> 89000 bytes/sec + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (1,0,0,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (3,0,0,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (6,0,0,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (12,0,0,183000,185000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (15,0,0,146400,89000,200,1)); + + // DOWNLINK - DISTANCE 3000 -> MCS 24 -> Itbs 20 (from table 7.1.7.2.1-1 of 36.213) + // DOWNLINK -> DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.2 13) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 20 -> 1383 -> 1383000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 1383000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 1383000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 > 1383000 -> throughput = 1383000 / 6 = 230500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 1383000 -> throughput = 1383000 / 12 = 115250 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 1383000 -> throughput = 1383000 / 15 = 92200 byte/sec + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 > 232000 -> throughput = 232000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 < 232000 -> throughput = 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 < 232000 -> throughput = 97000 bytes/sec + // 15 users -> 1 PRB at Itbs 18 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (1,0,3000,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (3,0,3000,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (6,0,3000,230500,193000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (12,0,3000,115250,97000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (15,0,3000,92200,47000,200,1)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 15 -> 903 -> 903000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 903000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 < 903000 -> througphut = 232000 byte/sec + // 6 user -> 232000 * 6 = 139200 > 903000 -> throughput = 903000 / 6 = 150500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 903000 -> throughput = 903000 / 12 = 75250 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 903000 -> throughput = 903000 / 15 = 60200 byte/sec + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 < 232000 -> throughput = 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 < 232000 -> throughput = 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 < 232000 -> throughput = 47000 bytes/sec + // 15 users -> 1 PRB at Itbs 11 -> 22 -> 22000 bytes/sec + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (1,0,6000,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (3,0,6000,232000,201000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (6,0,6000,150500,97000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (12,0,6000,75250,47000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (15,0,6000,60200,22000,200,1)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 11 -> 597 -> 597000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 597000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 > 597000 -> througphut = 597000 / 3 = 199000byte/sec + // 6 user -> 232000 * 6 = 139200 > 597000 -> throughput = 597000 / 6 = 99500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 597000 -> throughput = 597000 / 12 = 49750 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 597000 -> throughput = 597000 / 15 = 39800 byte/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 < 232000 -> throughput = 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 < 232000 -> throughput = 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 < 232000 -> throughput = 32000 bytes/sec + // 15 users -> 1 PRB at Itbs 8 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (1,0,9000,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (3,0,9000,199000,137000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (6,0,9000,99500,67000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (12,0,9000,49750,32000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (15,0,9000,39800,15000,200,1)); + + // DONWLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // Traffic info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Totol bandwidth: 24 PRB at Itbs 6 -> 309 -> 309000 byte/sec + // 1 user -> 232000 * 1 = 232000 < 309000 -> throughput = 232000 byte/sec + // 3 user -> 232000 * 3 = 696000 > 309000 -> througphut = 309000 / 3 = 103000byte/sec + // 6 user -> 232000 * 6 = 139200 > 309000 -> throughput = 309000 / 6 = 51500 byte/sec + // 12 user -> 232000 * 12 = 2784000 > 309000 -> throughput = 309000 / 12 = 25750 byte/sec + // 15 user -> 232000 * 15 = 3480000 > 309000 -> throughput = 309000 / 15 = 20600 byte/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 > 232000 -> throughput = 232000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 < 232000 -> throughput = 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 < 232000 -> throughput = 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 < 232000 -> throughput = 15000 bytes/sec + // 15 users -> 1 PRB at Itbs 6 -> 7 -> 7000 bytes/sec + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (1,0,15000,232000,232000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (3,0,15000,103000,69000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (6,0,15000,51500,32000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (12,0,15000,25750,15000,200,1)); + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase1 (15,0,15000,20600,7000,200,1)); + + // Test Case 2: homogeneous flow test in TDTBFQ (different distance) + // Traffic1 info + // UDP traffic: payload size = 100 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 132000 byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 + 1/597000 + 1/309000) = 694720 byte/s + // 132000 * 5 = 660000 < 694720 -> estimated throughput in downlink = 132000 byte/sec + std::vector dist1; + dist1.push_back (0); // User 0 distance --> MCS 28 + dist1.push_back (3000); // User 1 distance --> MCS 24 + dist1.push_back (6000); // User 2 distance --> MCS 16 + dist1.push_back (9000); // User 3 distance --> MCS 12 + dist1.push_back (15000); // User 4 distance --> MCS 6 + std::vector packetSize1; + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + packetSize1.push_back (100); + std::vector estThrTdTbfqDl1; + estThrTdTbfqDl1.push_back (132000); // User 0 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl1.push_back (132000); // User 1 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl1.push_back (132000); // User 2 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl1.push_back (132000); // User 3 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl1.push_back (132000); // User 4 estimated TTI throughput from TDTBFQ + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase2 (dist1,estThrTdTbfqDl1,packetSize1,1)); + + // Traffic2 info + // UDP traffic: payload size = 200 bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> 232000 byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 + 1/597000 + 1/309000) = 694720 byte/s + // 232000 * 5 = 1160000 > 694720 -> estimated throughput in downlink = 694720 / 5 = 138944 byte/sec + std::vector dist2; + dist2.push_back (0); // User 0 distance --> MCS 28 + dist2.push_back (3000); // User 1 distance --> MCS 24 + dist2.push_back (6000); // User 2 distance --> MCS 16 + dist2.push_back (9000); // User 3 distance --> MCS 12 + dist2.push_back (15000); // User 4 distance --> MCS 6 + std::vector packetSize2; + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + packetSize2.push_back (200); + std::vector estThrTdTbfqDl2; + estThrTdTbfqDl2.push_back (138944); // User 0 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl2.push_back (138944); // User 1 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl2.push_back (138944); // User 2 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl2.push_back (138944); // User 3 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl2.push_back (138944); // User 4 estimated TTI throughput from TDTBFQ + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase2 (dist2,estThrTdTbfqDl2,packetSize2,1)); + + // Test Case 3: heterogeneous flow test in TDTBFQ + // UDP traffic: payload size = [100,200,300] bytes, interval = 1 ms + // UDP rate in scheduler: (payload + RLC header + PDCP header + IP header + UDP header) * 1000 byte/sec -> [132000, 232000, 332000] byte/rate + // Maximum throughput = 5 / ( 1/2196000 + 1/1383000 + 1/903000 ) = 1312417 byte/s + // 132000 + 232000 + 332000 = 696000 < 1312417 -> estimated throughput in downlink = [132000, 232000, 332000] byte/sec + std::vector dist3; + dist3.push_back (0); // User 0 distance --> MCS 28 + dist3.push_back (3000); // User 1 distance --> MCS 24 + dist3.push_back (6000); // User 2 distance --> MCS 16 + std::vector packetSize3; + packetSize3.push_back (100); + packetSize3.push_back (200); + packetSize3.push_back (300); + std::vector estThrTdTbfqDl3; + estThrTdTbfqDl3.push_back (132000); // User 0 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl3.push_back (232000); // User 1 estimated TTI throughput from TDTBFQ + estThrTdTbfqDl3.push_back (332000); // User 2 estimated TTI throughput from TDTBFQ + AddTestCase (new LenaTdTbfqFfMacSchedulerTestCase2 (dist3,estThrTdTbfqDl3,packetSize3,1)); + +} + +static LenaTestTdTbfqFfMacSchedulerSuite lenaTestTdTbfqFfMacSchedulerSuite; + +// --------------- T E S T - C A S E # 1 ------------------------------ + + +std::string +LenaTdTbfqFfMacSchedulerTestCase1::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + + +LenaTdTbfqFfMacSchedulerTestCase1::LenaTdTbfqFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl, uint16_t packetSize, uint16_t interval) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_packetSize (packetSize), + m_interval (interval), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaTdTbfqFfMacSchedulerTestCase1::~LenaTdTbfqFfMacSchedulerTestCase1 () +{ +} + +void +LenaTdTbfqFfMacSchedulerTestCase1::DoRun (void) +{ + Ptr lteHelper = CreateObject (); + Ptr epcHelper = CreateObject (); + lteHelper->SetEpcHelper (epcHelper); + + Ptr pgw = epcHelper->GetPgwNode (); + + // Create a single RemoteHost + NodeContainer remoteHostContainer; + remoteHostContainer.Create (1); + Ptr remoteHost = remoteHostContainer.Get (0); + InternetStackHelper internet; + internet.Install (remoteHostContainer); + + // Create the Internet + PointToPointHelper p2ph; + p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s"))); + p2ph.SetDeviceAttribute ("Mtu", UintegerValue (1500)); + p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.001))); + NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost); + Ipv4AddressHelper ipv4h; + ipv4h.SetBase ("1.0.0.0", "255.0.0.0"); + Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices); + // interface 0 is localhost, 1 is the p2p device + Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress (1); + + Ipv4StaticRoutingHelper ipv4RoutingHelper; + Ptr remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject ()); + remoteHostStaticRouting->AddNetworkRouteTo (Ipv4Address ("7.0.0.0"), Ipv4Mask ("255.0.0.0"), 1); + + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + + lteHelper->SetAttribute ("EpsBearerToRlcMapping", EnumValue (LteHelper::RLC_UM_ALWAYS)); + + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("TdTbfqFfMacScheduler", LOG_LEVEL_AlL); + LogComponentEnable ("LenaTestTdTbfqFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::TdTbfqFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + // Install the IP stack on the UEs + internet.Install (ueNodes); + Ipv4InterfaceContainer ueIpIface; + ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueDevs)); + // Assign IP address to UEs, and install applications + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + Ptr ueNode = ueNodes.Get (u); + // Set the default gateway for the UE + Ptr ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject ()); + ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1); + } + +// Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + GbrQosInformation qos; + qos.gbrDl = (m_packetSize + 32) * (1000 / m_interval) * 8; // bit/s, considering IP, UDP, RLC, PDCP header size + qos.gbrUl = 0; + qos.mbrDl = qos.gbrDl; + qos.mbrUl = 0; + EpsBearer bearer (q, qos); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + lteHelper->EnableMacTraces (); + lteHelper->EnableRlcTraces (); + lteHelper->EnablePdcpTraces (); + + // Install downlind and uplink applications + uint16_t dlPort = 1234; + uint16_t ulPort = 2000; + PacketSinkHelper dlPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort)); + PacketSinkHelper ulPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), ulPort)); + ApplicationContainer clientApps; + ApplicationContainer serverApps; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + ++ulPort; + serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get (u))); // receive packets from remotehost + serverApps.Add (ulPacketSinkHelper.Install (remoteHost)); // receive packets from UEs + + UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort); // uplink packets generator + dlClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + dlClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + dlClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + UdpClientHelper ulClient (remoteHostAddr, ulPort); // downlink packets generator + ulClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + ulClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + ulClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + clientApps.Add (dlClient.Install (remoteHost)); + clientApps.Add (ulClient.Install (ueNodes.Get (u))); + } + + serverApps.Start (Seconds (0.001)); + clientApps.Start (Seconds (0.001)); + + double simulationTime = 2.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "token bank fair queue" manner + */ + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + uint64_t data = rlcStats->GetDlRxData (imsi, lcId); + dlDataRxed.push_back (data); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Unfair Throughput!"); + } + + /** + * Check that the uplink assignation is done in a "round robin" manner + */ + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + + + +// --------------- T E S T - C A S E # 2 ------------------------------ + + +std::string +LenaTdTbfqFfMacSchedulerTestCase2::BuildNameString (uint16_t nUser, std::vector dist) +{ + std::ostringstream oss; + oss << "distances (m) = [ " ; + for (std::vector::iterator it = dist.begin (); it != dist.end (); ++it) + { + oss << *it << " "; + } + oss << "]"; + return oss.str (); +} + + +LenaTdTbfqFfMacSchedulerTestCase2::LenaTdTbfqFfMacSchedulerTestCase2 (std::vector dist, std::vector estThrTdTbfqDl, std::vector packetSize, uint16_t interval) + : TestCase (BuildNameString (dist.size (), dist)), + m_nUser (dist.size ()), + m_dist (dist), + m_packetSize (packetSize), + m_interval (interval), + m_estThrTdTbfqDl (estThrTdTbfqDl) +{ +} + +LenaTdTbfqFfMacSchedulerTestCase2::~LenaTdTbfqFfMacSchedulerTestCase2 () +{ +} + +void +LenaTdTbfqFfMacSchedulerTestCase2::DoRun (void) +{ + Ptr lteHelper = CreateObject (); + Ptr epcHelper = CreateObject (); + lteHelper->SetEpcHelper (epcHelper); + + Ptr pgw = epcHelper->GetPgwNode (); + + // Create a single RemoteHost + NodeContainer remoteHostContainer; + remoteHostContainer.Create (1); + Ptr remoteHost = remoteHostContainer.Get (0); + InternetStackHelper internet; + internet.Install (remoteHostContainer); + + // Create the Internet + PointToPointHelper p2ph; + p2ph.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("100Gb/s"))); + p2ph.SetDeviceAttribute ("Mtu", UintegerValue (1500)); + p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.001))); + NetDeviceContainer internetDevices = p2ph.Install (pgw, remoteHost); + Ipv4AddressHelper ipv4h; + ipv4h.SetBase ("1.0.0.0", "255.0.0.0"); + Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign (internetDevices); + // interface 0 is localhost, 1 is the p2p device + Ipv4Address remoteHostAddr = internetIpIfaces.GetAddress (1); + + Ipv4StaticRoutingHelper ipv4RoutingHelper; + Ptr remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject ()); + remoteHostStaticRouting->AddNetworkRouteTo (Ipv4Address ("7.0.0.0"), Ipv4Mask ("255.0.0.0"), 1); + + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + + lteHelper->SetAttribute ("EpsBearerToRlcMapping", EnumValue (LteHelper::RLC_UM_ALWAYS)); + + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("TdTbfqFfMacScheduler", LOG_LEVEL_AlL); + LogComponentEnable ("LenaTestTdTbfqFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::TdTbfqFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + // Install the IP stack on the UEs + internet.Install (ueNodes); + Ipv4InterfaceContainer ueIpIface; + ueIpIface = epcHelper->AssignUeIpv4Address (NetDeviceContainer (ueDevs)); + // Assign IP address to UEs, and install applications + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + Ptr ueNode = ueNodes.Get (u); + // Set the default gateway for the UE + Ptr ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject ()); + ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1); + } + +// Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + GbrQosInformation qos; + uint16_t mbrDl = 0; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + mbrDl = mbrDl + m_packetSize.at (u); + } + mbrDl = mbrDl / ueNodes.GetN (); + qos.gbrDl = (mbrDl + 32) * (1000 / m_interval) * 8; // bit/s, considering IP, UDP, RLC, PDCP header size + qos.gbrUl = 0; + qos.mbrDl = qos.gbrDl; + qos.mbrUl = 0; + EpsBearer bearer (q, qos); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + lteHelper->EnableMacTraces (); + lteHelper->EnableRlcTraces (); + lteHelper->EnablePdcpTraces (); + + // Install downlind and uplink applications + uint16_t dlPort = 1234; + uint16_t ulPort = 2000; + PacketSinkHelper dlPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort)); + PacketSinkHelper ulPacketSinkHelper ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), ulPort)); + ApplicationContainer clientApps; + ApplicationContainer serverApps; + for (uint32_t u = 0; u < ueNodes.GetN (); ++u) + { + ++ulPort; + serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get (u))); // receive packets from remotehost + serverApps.Add (ulPacketSinkHelper.Install (remoteHost)); // receive packets from UEs + + UdpClientHelper dlClient (ueIpIface.GetAddress (u), dlPort); // uplink packets generator + dlClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + dlClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + dlClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize.at (u))); + + UdpClientHelper ulClient (remoteHostAddr, ulPort); // downlink packets generator + ulClient.SetAttribute ("Interval", TimeValue (MilliSeconds (m_interval))); + ulClient.SetAttribute ("MaxPackets", UintegerValue (1000000)); + ulClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize.at (u))); + + clientApps.Add (dlClient.Install (remoteHost)); + clientApps.Add (ulClient.Install (ueNodes.Get (u))); + } + + serverApps.Start (Seconds (0.001)); + clientApps.Start (Seconds (0.001)); + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the assignation is done in a "token bank fair queue" manner + */ + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s)"); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " dist " << m_dist.at (i) << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_nUser); + } + + for (int i = 0; i < m_nUser; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_estThrTdTbfqDl.at (i), m_estThrTdTbfqDl.at (i) * tolerance, " Unfair Throughput!"); + } + + Simulator::Destroy (); + +} + + +} // namespace ns3 + + + + diff --git a/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h b/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h new file mode 100644 index 000000000..0e54a9f61 --- /dev/null +++ b/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_TDTBFQ_FF_MAC_SCHEDULER_H +#define LENA_TEST_TDTBFQ_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +namespace ns3 { + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is equal among users is consistent with the definition of token bank fair +* queue scheduling +*/ +class LenaTdTbfqFfMacSchedulerTestCase1 : public TestCase +{ +public: + LenaTdTbfqFfMacSchedulerTestCase1 (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl, uint16_t packetSize, uint16_t interval); + virtual ~LenaTdTbfqFfMacSchedulerTestCase1 (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + uint16_t m_packetSize; // byte + uint16_t m_interval; // ms + double m_thrRefDl; + double m_thrRefUl; +}; + + +class LenaTdTbfqFfMacSchedulerTestCase2 : public TestCase +{ +public: + LenaTdTbfqFfMacSchedulerTestCase2 (std::vector dist, std::vector estThrTdTbfqDl, std::vector packetSize, uint16_t interval); + virtual ~LenaTdTbfqFfMacSchedulerTestCase2 (); + +private: + static std::string BuildNameString (uint16_t nUser, std::vector dist); + virtual void DoRun (void); + uint16_t m_nUser; + std::vector m_dist; + std::vector m_packetSize; // byte + uint16_t m_interval; // ms + std::vector m_estThrTdTbfqDl; +}; + + +class LenaTestTdTbfqFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestTdTbfqFfMacSchedulerSuite (); +}; + + + + +} // namespace ns3 + + +#endif /* LENA_TEST_TDTBFQ_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/test/lte-test-tta-ff-mac-scheduler.cc b/src/lte/test/lte-test-tta-ff-mac-scheduler.cc new file mode 100644 index 000000000..dec2715a0 --- /dev/null +++ b/src/lte/test/lte-test-tta-ff-mac-scheduler.cc @@ -0,0 +1,347 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/radio-bearer-stats-calculator.h" +#include +#include "lte-test-tta-ff-mac-scheduler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "ns3/string.h" +#include "ns3/double.h" +#include +#include +#include +#include + + +NS_LOG_COMPONENT_DEFINE ("LenaTestTtaFfMacCheduler"); + +using namespace ns3; + +LenaTestTtaFfMacSchedulerSuite::LenaTestTtaFfMacSchedulerSuite () + : TestSuite ("lte-tta-ff-mac-scheduler", SYSTEM) +{ + NS_LOG_INFO ("creating LenaTestTtaFfMacSchedulerSuite"); + + //Test Case : AMC works in TTA + + // DOWNLINK - DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 26 -> 2196 -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 2196000 among 3 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 2196000 among 6 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 2196000 among 12 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 2196000 among 15 users -> 2196000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK- DISTANCE 0 -> MCS 28 -> Itbs 26 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 26 -> 2292 -> 2292000 bytes/sec + // 3 users -> 8 PRB at Itbs 26 -> 749 -> 749000 bytes/sec + // 6 users -> 4 PRB at Itbs 26 -> 373 -> 373000 bytes/sec + // 12 users -> 2 PRB at Itbs 26 -> 185 -> 185000 bytes/sec + // 15 users -> 1 PRB at Itbs 26 -> 89 -> 89000 bytes/sec + AddTestCase (new LenaTtaFfMacSchedulerTestCase (1,0,0,2196000,2292000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (3,0,0,2196000,749000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (6,0,0,2196000,373000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (12,0,0,2196000,185000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (15,0,0,2196000,89000)); + + // DOWNLINK - DISTANCE 3000 -> MCS 24 -> Itbs 30 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 20 -> 1383 -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 1383000 among 3 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 1383000 among 6 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 1383000 among 12 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 1383000 among 15 users -> 1383000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK - DISTANCE 3000 -> MCS 20 -> Itbs 18 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 18 -> 1239 -> 1239000 bytes/sec + // 3 users -> 8 PRB at Itbs 18 -> 389 -> 389000 bytes/sec + // 6 users -> 4 PRB at Itbs 18 -> 193 -> 193000 bytes/sec + // 12 users -> 2 PRB at Itbs 18 -> 97 -> 97000 bytes/sec + // 15 users -> 1 PRB at Itbs 18 -> 47 -> 47000 bytes/sec + AddTestCase (new LenaTtaFfMacSchedulerTestCase (1,0,3000,1383000,1239000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (3,0,3000,1383000,389000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (6,0,3000,1383000,193000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (12,0,3000,1383000,97000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (15,0,3000,1383000,47000)); + + // DOWNLINK - DISTANCE 6000 -> MCS 16 -> Itbs 15 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 15 -> 903 -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 3 users -> 903000 among 3 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 6 users -> 903000 among 6 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 12 users -> 903000 among 12 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // 15 users -> 903000 among 15 users -> 903000 bytes/sec for first UE; 0 bytes/sec for other UEs + // UPLINK - DISTANCE 6000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 11 -> 621 -> 621000 bytes/sec + // 3 users -> 8 PRB at Itbs 11 -> 201 -> 201000 bytes/sec + // 6 users -> 4 PRB at Itbs 11 -> 97 -> 97000 bytes/sec + // 12 users -> 2 PRB at Itbs 11 -> 47 -> 47000 bytes/sec + // 15 users -> 1 PRB at Itbs 11 -> 22 -> 22000 bytes/sec + AddTestCase (new LenaTtaFfMacSchedulerTestCase (1,0,6000,903000,621000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (3,0,6000,903000,201000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (6,0,6000,903000,97000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (12,0,6000,903000,47000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (15,0,6000,903000,22000)); + + // DOWNLINK - DISTANCE 9000 -> MCS 12 -> Itbs 11 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 11 -> 597 -> 597000 bytes/sec + // 3 users -> 597000 among 3 users -> 199000 bytes/sec + // 6 users -> 597000 among 6 users -> 99500 bytes/sec + // 12 users -> 597000 among 12 users -> 49750 bytes/sec + // 15 users -> 597000 among 15 users -> 39800 bytes/sec + // UPLINK - DISTANCE 9000 -> MCS 8 -> Itbs 8 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 8 -> 437 -> 437000 bytes/sec + // 3 users -> 8 PRB at Itbs 8 -> 137 -> 137000 bytes/sec + // 6 users -> 4 PRB at Itbs 8 -> 67 -> 67000 bytes/sec + // 12 users -> 2 PRB at Itbs 8 -> 32 -> 32000 bytes/sec + // 15 users -> 1 PRB at Itbs 8 -> 15 -> 15000 bytes/sec + AddTestCase (new LenaTtaFfMacSchedulerTestCase (1,0,9000,597000,437000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (3,0,9000,597000,137000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (6,0,9000,597000,67000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (12,0,9000,597000,32000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (15,0,9000,597000,15000)); + + // DONWLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 24 PRB at Itbs 6 -> 309 -> 309000 bytes/sec + // 3 users -> 309000 among 3 users -> 103000 bytes/sec + // 6 users -> 309000 among 6 users -> 51500 bytes/sec + // 12 users -> 309000 among 12 users -> 25750 bytes/sec + // 15 users -> 309000 among 15 users -> 20600 bytes/sec + // UPLINK - DISTANCE 15000 -> MCS 6 -> Itbs 6 (from table 7.1.7.2.1-1 of 36.213) + // 1 user -> 25 PRB at Itbs 6 -> 233 -> 233000 bytes/sec + // 3 users -> 8 PRB at Itbs 6 -> 69 -> 69000 bytes/sec + // 6 users -> 4 PRB at Itbs 6 -> 32 -> 32000 bytes/sec + // 12 users -> 2 PRB at Itbs 6 -> 15 -> 15000 bytes/sec + // 15 users -> 1 PRB at Itbs 6 -> 7 -> 7000 bytes/sec + AddTestCase (new LenaTtaFfMacSchedulerTestCase (1,0,15000,309000,233000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (3,0,15000,309000,69000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (6,0,15000,309000,32000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (12,0,15000,309000,15000)); + AddTestCase (new LenaTtaFfMacSchedulerTestCase (15,0,15000,309000,7000)); + + +} + +static LenaTestTtaFfMacSchedulerSuite lenaTestTtaFfMacSchedulerSuite; + + +// --------------- T E S T - C A S E ------------------------------ + + +std::string +LenaTtaFfMacSchedulerTestCase::BuildNameString (uint16_t nUser, uint16_t dist) +{ + std::ostringstream oss; + oss << nUser << " UEs, distance " << dist << " m"; + return oss.str (); +} + +LenaTtaFfMacSchedulerTestCase::LenaTtaFfMacSchedulerTestCase (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl) + : TestCase (BuildNameString (nUser, dist)), + m_nUser (nUser), + m_nLc (nLc), + m_dist (dist), + m_thrRefDl (thrRefDl), + m_thrRefUl (thrRefUl) +{ +} + +LenaTtaFfMacSchedulerTestCase::~LenaTtaFfMacSchedulerTestCase () +{ +} + +void +LenaTtaFfMacSchedulerTestCase::DoRun (void) +{ + Config::SetDefault ("ns3::LteAmc::AmcModel", EnumValue (LteAmc::PiroEW2010)); + Config::SetDefault ("ns3::LteAmc::Ber", DoubleValue (0.00005)); + Config::SetDefault ("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", BooleanValue (false)); + Config::SetDefault ("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue (false)); + LogComponentDisableAll (LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeRrc", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbMac", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeMac", LOG_LEVEL_ALL); +// LogComponentEnable ("LteRlc", LOG_LEVEL_ALL); +// +// LogComponentEnable ("LtePhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteEnbPhy", LOG_LEVEL_ALL); +// LogComponentEnable ("LteUePhy", LOG_LEVEL_ALL); + + // LogComponentEnable ("LteSpectrumPhy", LOG_LEVEL_ALL); + // LogComponentEnable ("LteInterference", LOG_LEVEL_ALL); + // LogComponentEnable ("LteSinrChunkProcessor", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LtePropagationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("LossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("ShadowingLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PenetrationLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("MultipathLossModel", LOG_LEVEL_ALL); + // LogComponentEnable ("PathLossModel", LOG_LEVEL_ALL); + // + // LogComponentEnable ("LteNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteUeNetDevice", LOG_LEVEL_ALL); + // LogComponentEnable ("LteEnbNetDevice", LOG_LEVEL_ALL); + +// LogComponentEnable ("TtaFfMacScheduler", LOG_LEVEL_ALL); + LogComponentEnable ("LenaTestTtaFfMacCheduler", LOG_LEVEL_ALL); + // LogComponentEnable ("LteAmc", LOG_LEVEL_ALL); +// LogComponentEnable ("RadioBearerStatsCalculator", LOG_LEVEL_ALL); + + /** + * Initialize Simulation Scenario: 1 eNB and m_nUser UEs + */ + + Ptr lteHelper = CreateObject (); + + lteHelper->SetAttribute ("PathlossModel", StringValue ("ns3::FriisSpectrumPropagationLossModel")); + + // Create Nodes: eNodeB and UE + NodeContainer enbNodes; + NodeContainer ueNodes; + enbNodes.Create (1); + ueNodes.Create (m_nUser); + + // Install Mobility Model + MobilityHelper mobility; + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (enbNodes); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (ueNodes); + + // Create Devices and install them in the Nodes (eNB and UE) + NetDeviceContainer enbDevs; + NetDeviceContainer ueDevs; + lteHelper->SetSchedulerType ("ns3::TtaFfMacScheduler"); + enbDevs = lteHelper->InstallEnbDevice (enbNodes); + ueDevs = lteHelper->InstallUeDevice (ueNodes); + + // Attach a UE to a eNB + lteHelper->Attach (ueDevs, enbDevs.Get (0)); + + // Activate an EPS bearer + enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE; + EpsBearer bearer (q); + lteHelper->ActivateEpsBearer (ueDevs, bearer, EpcTft::Default ()); + + Ptr lteEnbDev = enbDevs.Get (0)->GetObject (); + Ptr enbPhy = lteEnbDev->GetPhy (); + enbPhy->SetAttribute ("TxPower", DoubleValue (30.0)); + enbPhy->SetAttribute ("NoiseFigure", DoubleValue (5.0)); + + // Set UEs' position and power + for (int i = 0; i < m_nUser; i++) + { + Ptr mm = ueNodes.Get (i)->GetObject (); + mm->SetPosition (Vector (m_dist, 0.0, 0.0)); + Ptr lteUeDev = ueDevs.Get (i)->GetObject (); + Ptr uePhy = lteUeDev->GetPhy (); + uePhy->SetAttribute ("TxPower", DoubleValue (23.0)); + uePhy->SetAttribute ("NoiseFigure", DoubleValue (9.0)); + } + + lteHelper->EnableRlcTraces (); + lteHelper->EnableMacTraces (); + + + double simulationTime = 1.0; + double tolerance = 0.1; + Simulator::Stop (Seconds (simulationTime)); + + Ptr rlcStats = lteHelper->GetRlcStats (); + rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime))); + + Simulator::Run (); + + /** + * Check that the downlink assignation is done in a "throughput to average" manner + */ + NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector dlDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + dlDataRxed.push_back (rlcStats->GetDlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)dlDataRxed.at (i) << " thr " << (double)dlDataRxed.at (i) / simulationTime << " ref " << m_thrRefDl); + } + /** + * Check that the assignation is done in a "throughput to average" manner among users + * with equal SINRs: all bandwidth should be allocated to the first UE in script + */ + for (int i = 0; i < 1; i++) + { + if (i == 0) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, m_thrRefDl, m_thrRefDl * tolerance, " Invalid Throughput!"); + } + else + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)dlDataRxed.at (i) / simulationTime, 0, tolerance, " Invalid Throughput!"); + } + } + + /** + * Check that the uplink assignation is done in a "throughput to average" manner + */ + NS_LOG_INFO ("UL - Test with " << m_nUser << " user(s) at distance " << m_dist); + std::vector ulDataRxed; + for (int i = 0; i < m_nUser; i++) + { + // get the imsi + uint64_t imsi = ueDevs.Get (i)->GetObject ()->GetImsi (); + // get the lcId + uint8_t lcId = ueDevs.Get (i)->GetObject ()->GetRrc ()->GetLcIdVector ().at (0); + ulDataRxed.push_back (rlcStats->GetUlRxData (imsi, lcId)); + NS_LOG_INFO ("\tUser " << i << " imsi " << imsi << " bytes rxed " << (double)ulDataRxed.at (i) << " thr " << (double)ulDataRxed.at (i) / simulationTime << " ref " << m_thrRefUl); + } + /** + * Check that the assignation is done in a "throughput to average" manner among users + * with equal SINRs: the bandwidht should be distributed according to the + * ratio of the estimated throughput per TTI of each user; therefore equally + * partitioning the whole bandwidth achievable from a single users in a TTI + * + */ + for (int i = 0; i < 1; i++) + { + NS_TEST_ASSERT_MSG_EQ_TOL ((double)ulDataRxed.at (i) / simulationTime, m_thrRefUl, m_thrRefUl * tolerance, " Unfair Throughput!"); + } + Simulator::Destroy (); + +} + diff --git a/src/lte/test/lte-test-tta-ff-mac-scheduler.h b/src/lte/test/lte-test-tta-ff-mac-scheduler.h new file mode 100644 index 000000000..f2253e843 --- /dev/null +++ b/src/lte/test/lte-test-tta-ff-mac-scheduler.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Marco Miozzo , + * Nicola Baldo + * Dizhi Zhou + */ + +#ifndef LENA_TEST_TTA_FF_MAC_SCHEDULER_H +#define LENA_TEST_TTA_FF_MAC_SCHEDULER_H + +#include "ns3/simulator.h" +#include "ns3/test.h" + + +using namespace ns3; + + +/** +* This system test program creates different test cases with a single eNB and +* several UEs, all having the same Radio Bearer specification. In each test +* case, the UEs see the same SINR from the eNB; different test cases are +* implemented obtained by using different SINR values and different numbers of +* UEs. The test consists on checking that the obtained throughput performance +* is consistent with the definition of throughput to average scheduling +*/ +class LenaTtaFfMacSchedulerTestCase : public TestCase +{ +public: + LenaTtaFfMacSchedulerTestCase (uint16_t nUser, uint16_t nLc, uint16_t dist, double thrRefDl, double thrRefUl); + virtual ~LenaTtaFfMacSchedulerTestCase (); + +private: + static std::string BuildNameString (uint16_t nUser, uint16_t dist); + virtual void DoRun (void); + uint16_t m_nUser; + uint16_t m_nLc; + uint16_t m_dist; + double m_thrRefDl; + double m_thrRefUl; +}; + +class LenaTestTtaFfMacSchedulerSuite : public TestSuite +{ +public: + LenaTestTtaFfMacSchedulerSuite (); +}; + + + + +#endif /* LENA_TEST_TTA_FF_MAC_SCHEDULER_H */ diff --git a/src/lte/wscript b/src/lte/wscript index 03f025737..67836d056 100644 --- a/src/lte/wscript +++ b/src/lte/wscript @@ -58,6 +58,14 @@ def build(bld): 'model/lte-interference.cc', 'model/lte-sinr-chunk-processor.cc', 'model/pf-ff-mac-scheduler.cc', + 'model/fdmt-ff-mac-scheduler.cc', + 'model/tdmt-ff-mac-scheduler.cc', + 'model/tta-ff-mac-scheduler.cc', + 'model/fdbet-ff-mac-scheduler.cc', + 'model/tdbet-ff-mac-scheduler.cc', + 'model/fdtbfq-ff-mac-scheduler.cc', + 'model/tdtbfq-ff-mac-scheduler.cc', + 'model/pss-ff-mac-scheduler.cc', 'model/epc-gtpu-header.cc', 'model/trace-fading-loss-model.cc', 'model/epc-enb-application.cc', @@ -78,6 +86,14 @@ def build(bld): 'test/lte-test-ue-phy.cc', 'test/lte-test-rr-ff-mac-scheduler.cc', 'test/lte-test-pf-ff-mac-scheduler.cc', + 'test/lte-test-fdmt-ff-mac-scheduler.cc', + 'test/lte-test-tdmt-ff-mac-scheduler.cc', + 'test/lte-test-tta-ff-mac-scheduler.cc', + 'test/lte-test-fdbet-ff-mac-scheduler.cc', + 'test/lte-test-tdbet-ff-mac-scheduler.cc', + 'test/lte-test-fdtbfq-ff-mac-scheduler.cc', + 'test/lte-test-tdtbfq-ff-mac-scheduler.cc', + 'test/lte-test-pss-ff-mac-scheduler.cc', 'test/lte-test-earfcn.cc', 'test/lte-test-spectrum-value-helper.cc', 'test/lte-test-pathloss-model.cc', @@ -155,6 +171,14 @@ def build(bld): 'model/lte-interference.h', 'model/lte-sinr-chunk-processor.h', 'model/pf-ff-mac-scheduler.h', + 'model/fdmt-ff-mac-scheduler.h', + 'model/tdmt-ff-mac-scheduler.h', + 'model/tta-ff-mac-scheduler.h', + 'model/fdbet-ff-mac-scheduler.h', + 'model/tdbet-ff-mac-scheduler.h', + 'model/fdtbfq-ff-mac-scheduler.h', + 'model/tdtbfq-ff-mac-scheduler.h', + 'model/pss-ff-mac-scheduler.h', 'model/trace-fading-loss-model.h', 'model/epc-gtpu-header.h', 'model/epc-enb-application.h', @@ -163,6 +187,14 @@ def build(bld): 'model/epc-tft.h', 'model/epc-tft-classifier.h', 'model/lte-mi-error-model.h', + 'test/lte-test-fdmt-ff-mac-scheduler.h', + 'test/lte-test-tdmt-ff-mac-scheduler.h', + 'test/lte-test-tta-ff-mac-scheduler.h', + 'test/lte-test-fdbet-ff-mac-scheduler.h', + 'test/lte-test-tdbet-ff-mac-scheduler.h', + 'test/lte-test-fdtbfq-ff-mac-scheduler.h', + 'test/lte-test-tdtbfq-ff-mac-scheduler.h', + 'test/lte-test-pss-ff-mac-scheduler.h', ] if (bld.env['ENABLE_EXAMPLES']): From a3839c733dff19ce35f0015ecc7e4ec0c9b8b125 Mon Sep 17 00:00:00 2001 From: Dizhi Zhou Date: Sun, 21 Oct 2012 21:48:38 -0400 Subject: [PATCH 2/4] correct code style errors in GSoC 2012 LTE NS-3 MAC schedulers --- src/lte/model/fdbet-ff-mac-scheduler.cc | 2 +- src/lte/model/fdtbfq-ff-mac-scheduler.cc | 144 +++++++++++------------ src/lte/model/pss-ff-mac-scheduler.cc | 33 +++--- src/lte/model/tdmt-ff-mac-scheduler.cc | 36 +++--- src/lte/model/tdtbfq-ff-mac-scheduler.cc | 24 ++-- src/lte/model/tta-ff-mac-scheduler.cc | 18 +-- 6 files changed, 129 insertions(+), 128 deletions(-) diff --git a/src/lte/model/fdbet-ff-mac-scheduler.cc b/src/lte/model/fdbet-ff-mac-scheduler.cc index c477b0a15..d184834bb 100644 --- a/src/lte/model/fdbet-ff-mac-scheduler.cc +++ b/src/lte/model/fdbet-ff-mac-scheduler.cc @@ -246,7 +246,7 @@ FdBetFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&FdBetFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) - ; + ; return tid; } diff --git a/src/lte/model/fdtbfq-ff-mac-scheduler.cc b/src/lte/model/fdtbfq-ff-mac-scheduler.cc index 4e5470304..36220367e 100644 --- a/src/lte/model/fdtbfq-ff-mac-scheduler.cc +++ b/src/lte/model/fdtbfq-ff-mac-scheduler.cc @@ -515,27 +515,27 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S continue; } - std::set ::iterator rnti; + std::set ::iterator rnti; rnti = allocatedRnti.find((*it).first); - if (rnti != allocatedRnti.end ()) // already allocated RBGs to this UE - { - continue; - } + if (rnti != allocatedRnti.end ()) // already allocated RBGs to this UE + { + continue; + } - double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); + double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); - if (firstRnti == true) - { - metricMax = metric; - itMax = it; - firstRnti = false; - continue; - } - if (metric > metricMax) - { - metricMax = metric; - itMax = it; - } + if (firstRnti == true) + { + metricMax = metric; + itMax = it; + firstRnti = false; + continue; + } + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } } // end for m_flowStatsDl if (itMax == m_flowStatsDl.end()) @@ -551,37 +551,37 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S uint32_t budget = 0; if ( bankSize > 0 ) { - budget = (*itMax).second.counter - (*itMax).second.debtLimit; - if ( budget > (*itMax).second.burstCredit ) - budget = (*itMax).second.burstCredit; - if ( budget > bankSize ) - budget = bankSize; - } + budget = (*itMax).second.counter - (*itMax).second.debtLimit; + if ( budget > (*itMax).second.burstCredit ) + budget = (*itMax).second.burstCredit; + if ( budget > bankSize ) + budget = bankSize; + } budget = budget + (*itMax).second.tokenPoolSize; // calcualte how much bytes this UE actally need if (budget == 0) { // there are no tokens for this UE - continue; + continue; } else { - // calculate rlc buffer size - uint32_t rlcBufSize; + // calculate rlc buffer size + uint32_t rlcBufSize; uint8_t lcid; std::map::iterator itRlcBuf; for (itRlcBuf = m_rlcBufferReq.begin (); itRlcBuf != m_rlcBufferReq.end (); itRlcBuf++) - { + { if ( (*itRlcBuf).first.m_rnti == (*itMax).first ) lcid = (*itRlcBuf).first.m_lcId; - } + } LteFlowId_t flow ((*itMax).first, lcid); itRlcBuf = m_rlcBufferReq.find (flow); if (itRlcBuf!=m_rlcBufferReq.end ()) - rlcBufSize = (*itRlcBuf).second.m_rlcTransmissionQueueSize + (*itRlcBuf).second.m_rlcRetransmissionQueueSize + (*itRlcBuf).second.m_rlcStatusPduSize; - if ( budget > rlcBufSize ) - budget = rlcBufSize; + rlcBufSize = (*itRlcBuf).second.m_rlcTransmissionQueueSize + (*itRlcBuf).second.m_rlcRetransmissionQueueSize + (*itRlcBuf).second.m_rlcStatusPduSize; + if ( budget > rlcBufSize ) + budget = rlcBufSize; } // assign RBGs to this UE @@ -605,12 +605,12 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S // find RBG with largest achievableRate double achievableRateMax = 0.0; rbgIndex = rbgNum; - for (int k = 0; k < rbgNum; k++) - { + for (int k = 0; k < rbgNum; k++) + { std::set ::iterator rbg; rbg = allocatedRbg.find (k); - if (rbg != allocatedRbg.end ()) // RBGs are already allocated to this UE - continue; + if (rbg != allocatedRbg.end ()) // RBGs are already allocated to this UE + continue; std::vector sbCqi; if (itCqi == m_a30CqiRxed.end ()) @@ -636,7 +636,7 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S if (LcActivePerFlow ((*itMax).first) > 0) { // this UE has data to transmit - double achievableRate = 0.0; + double achievableRate = 0.0; for (uint8_t j = 0; j < nLayer; j++) { uint8_t mcs = 0; @@ -652,26 +652,26 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI } - if ( achievableRate > achievableRateMax ) - { - achievableRateMax = achievableRate; - rbgIndex = k; - } - } // end of LcActivePerFlow - } // end of cqi + if ( achievableRate > achievableRateMax ) + { + achievableRateMax = achievableRate; + rbgIndex = k; + } + } // end of LcActivePerFlow + } // end of cqi } // end of for rbgNum - if ( rbgIndex == rbgNum) // impossible - { - // all RBGs are already assigned - totalRbg = rbgNum; - break; - } - else - { - // mark this UE as "allocated" + if ( rbgIndex == rbgNum) // impossible + { + // all RBGs are already assigned + totalRbg = rbgNum; + break; + } + else + { + // mark this UE as "allocated" allocatedRbg.insert (rbgIndex); - } + } // assign this RBG to UE std::map >::iterator itMap; @@ -733,41 +733,41 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S } } - bytesTxedTmp = bytesTxed; + bytesTxedTmp = bytesTxed; bytesTxed = 0; - for (uint8_t j = 0; j < nLayer; j++) + for (uint8_t j = 0; j < nLayer; j++) { int tbSize = (m_amc->GetTbSizeFromMcs (m_amc->GetMcsFromCqi (worstCqi.at (j)), RbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) bytesTxed += tbSize; } - } // end of while() + } // end of while() - // remove and unmark last RBG assigned to UE - if ( bytesTxed > budget ) - { + // remove and unmark last RBG assigned to UE + if ( bytesTxed > budget ) + { std::map >::iterator itMap; itMap = allocationMap.find ((*itMax).first); (*itMap).second.pop_back (); allocatedRbg.erase (rbgIndex); - bytesTxed = bytesTxedTmp; // recovery bytesTxed + bytesTxed = bytesTxedTmp; // recovery bytesTxed totalRbg--; - } + } - // update UE stats + // update UE stats if ( bytesTxed <= (*itMax).second.tokenPoolSize ) { - (*itMax).second.tokenPoolSize -= bytesTxed; + (*itMax).second.tokenPoolSize -= bytesTxed; } else - { - (*itMax).second.counter = (*itMax).second.counter - ( bytesTxed - (*itMax).second.tokenPoolSize ); - (*itMax).second.tokenPoolSize = 0; - if (bankSize <= ( bytesTxed - (*itMax).second.tokenPoolSize )) - bankSize = 0; - else - bankSize = bankSize - ( bytesTxed - (*itMax).second.tokenPoolSize ); - } + { + (*itMax).second.counter = (*itMax).second.counter - ( bytesTxed - (*itMax).second.tokenPoolSize ); + (*itMax).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*itMax).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*itMax).second.tokenPoolSize ); + } } // end of RBGs // generate the transmission opportunities by grouping the RBGs of the same RNTI and diff --git a/src/lte/model/pss-ff-mac-scheduler.cc b/src/lte/model/pss-ff-mac-scheduler.cc index 0a6283146..23ad9820a 100644 --- a/src/lte/model/pss-ff-mac-scheduler.cc +++ b/src/lte/model/pss-ff-mac-scheduler.cc @@ -555,6 +555,7 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche else nMux = (int)((ueSet1.size() + ueSet2.size()) / 2) ; // TD scheduler only transfers half selected UE per RTT to TD scheduler } + for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it--) { std::vector >::iterator itSet; @@ -701,38 +702,38 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche sbCqi = 0; } colMetric += (double)sbCqi / (double)(*itSbCqiSum).second; - } + } } // end if cqi double metric; - if (colMetric != 0) - metric= weight * colMetric; - else - metric = 1; + if (colMetric != 0) + metric= weight * colMetric; + else + metric = 1; - if (metric > metricMax ) + if (metric > metricMax ) { metricMax = metric; itMax = it; } - } // end of tdUeSet + } // end of tdUeSet if (itMax == m_flowStatsDl.end ()) { // no UE available for downlink - return; + return; } else { // assign all RBGs to this UE std::vector tempMap; - for (int i = 0; i < rbgNum; i++) - { + for (int i = 0; i < rbgNum; i++) + { tempMap.push_back (i); - } + } allocationMap.insert (std::pair > ((*itMax).first, tempMap)); } - }// end of rbgNum + }// end of rbgNum }// end of CoIta @@ -747,8 +748,8 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche { // calculate PF weigth double weight = (*it).second.targetThroughput / (*it).second.lastAveragedThroughput; - if (weight < 1.0) - weight = 1.0; + if (weight < 1.0) + weight = 1.0; std::map ::iterator itCqi; itCqi = m_a30CqiRxed.find ((*it).first); @@ -794,9 +795,9 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche { // no info on this subband -> worst MCS mcs = 0; - } + } achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI - } + } schMetric = achievableRate / (*it).second.secondLastAveragedThroughput; } // end if cqi diff --git a/src/lte/model/tdmt-ff-mac-scheduler.cc b/src/lte/model/tdmt-ff-mac-scheduler.cc index db67cd9c1..d5267e2a6 100644 --- a/src/lte/model/tdmt-ff-mac-scheduler.cc +++ b/src/lte/model/tdmt-ff-mac-scheduler.cc @@ -248,7 +248,7 @@ TdMtFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&TdMtFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) - ; + ; return tid; } @@ -458,18 +458,18 @@ TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it)); } int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); - uint8_t wbCqi = 0; - if (itCqi == m_p10CqiRxed.end()) - { - wbCqi = 1; // start with lowest value - } + uint8_t wbCqi = 0; + if (itCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } else { - wbCqi = (*itCqi).second; + wbCqi = (*itCqi).second; } - if (wbCqi > 0) - { + if (wbCqi > 0) + { if (LcActivePerFlow (*it) > 0) { // this UE has data to transmit @@ -481,30 +481,30 @@ TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI } - double metric = achievableRate; + double metric = achievableRate; if (metric > metricMax) { metricMax = metric; itMax = it; } - } + } } // end of wbCqi } // end for m_flowStatsDl if (itMax == m_flowStatsDl.end ()) { - // no UE available for downlink - return; + // no UE available for downlink + return; } else { // assign all RBGs to this UE std::vector tempMap; - for (int i = 0; i < rbgNum; i++) - { + for (int i = 0; i < rbgNum; i++) + { tempMap.push_back (i); - } + } allocationMap.insert (std::pair > ((*itMax), tempMap)); } @@ -523,8 +523,8 @@ TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch newDci.m_rnti = (*itMap).first; uint16_t lcActives = LcActivePerFlow ((*itMap).first); - std::map ::iterator itCqi; - itCqi = m_p10CqiRxed.find((*itMap).first); + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find((*itMap).first); std::map ::iterator itTxMode; itTxMode = m_uesTxMode.find ((*itMap).first); if (itTxMode == m_uesTxMode.end ()) diff --git a/src/lte/model/tdtbfq-ff-mac-scheduler.cc b/src/lte/model/tdtbfq-ff-mac-scheduler.cc index c14d93beb..f652e583b 100644 --- a/src/lte/model/tdtbfq-ff-mac-scheduler.cc +++ b/src/lte/model/tdtbfq-ff-mac-scheduler.cc @@ -661,18 +661,18 @@ TdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S if (it != m_flowStatsDl.end ()) { if ( bytesTxed <= (*it).second.tokenPoolSize ) - { - (*it).second.tokenPoolSize -= bytesTxed; - } - else - { - (*it).second.counter = (*it).second.counter - ( bytesTxed - (*it).second.tokenPoolSize ); - (*it).second.tokenPoolSize = 0; - if (bankSize <= ( bytesTxed - (*it).second.tokenPoolSize )) - bankSize = 0; - else - bankSize = bankSize - ( bytesTxed - (*it).second.tokenPoolSize ); - } + { + (*it).second.tokenPoolSize -= bytesTxed; + } + else + { + (*it).second.counter = (*it).second.counter - ( bytesTxed - (*it).second.tokenPoolSize ); + (*it).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*it).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*it).second.tokenPoolSize ); + } } else { diff --git a/src/lte/model/tta-ff-mac-scheduler.cc b/src/lte/model/tta-ff-mac-scheduler.cc index 590bcb093..3a5dacbac 100644 --- a/src/lte/model/tta-ff-mac-scheduler.cc +++ b/src/lte/model/tta-ff-mac-scheduler.cc @@ -248,7 +248,7 @@ TtaFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&TtaFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) - ; + ; return tid; } @@ -475,14 +475,14 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche sbCqi = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; } - uint8_t wbCqi = 0; - if (itWbCqi == m_p10CqiRxed.end()) - { - wbCqi = 1; // start with lowest value - } + uint8_t wbCqi = 0; + if (itWbCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } else { - wbCqi = (*itWbCqi).second; + wbCqi = (*itWbCqi).second; } uint8_t cqi1 = sbCqi.at(0); @@ -497,7 +497,7 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche if (LcActivePerFlow (*it) > 0) { // this UE has data to transmit - uint8_t sbMcs = 0; + uint8_t sbMcs = 0; uint8_t wbMcs = 0; double achievableSbRate = 1.0; double achievableWbRate = 1.0; @@ -519,7 +519,7 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche double metric = achievableSbRate / achievableWbRate; - if (metric > metricMax) + if (metric > metricMax) { metricMax = metric; itMax = it; From 3408e5bb1e3447624e3bc748c0f038d9c2d8815c Mon Sep 17 00:00:00 2001 From: Dizhi Zhou Date: Fri, 30 Nov 2012 18:40:56 -0400 Subject: [PATCH 3/4] GSoC 2012 NS-3 LTE schedulers: updated version based on Tom's comment --- src/lte/doc/source/lte-design.rst | 4 +- src/lte/doc/source/lte-references.rst | 2 +- src/lte/model/fdbet-ff-mac-scheduler.cc | 36 ++--- src/lte/model/fdmt-ff-mac-scheduler.cc | 29 +--- src/lte/model/fdtbfq-ff-mac-scheduler.cc | 172 +++++++++++------------ src/lte/model/pss-ff-mac-scheduler.cc | 62 +++----- src/lte/model/tdbet-ff-mac-scheduler.cc | 33 +---- src/lte/model/tdmt-ff-mac-scheduler.cc | 64 ++++----- src/lte/model/tdtbfq-ff-mac-scheduler.cc | 52 +++---- src/lte/model/tta-ff-mac-scheduler.cc | 46 +++--- 10 files changed, 187 insertions(+), 313 deletions(-) diff --git a/src/lte/doc/source/lte-design.rst b/src/lte/doc/source/lte-design.rst index da3adc3e2..1d3d24d0e 100644 --- a/src/lte/doc/source/lte-design.rst +++ b/src/lte/doc/source/lte-design.rst @@ -657,7 +657,7 @@ Maximum Throughput (MT) Scheduler The Maximum Throughput (MT) scheduler [FCapo2012]_ aims to maximize the overall throughput of eNB. It allocates each RB to the user that can achieve the maximum achievable rate in the current TTI. -Currently, MT scheduler in LENA has two versions: frequency domain (FDMT) and time domain (TDMT). +Currently, MT scheduler in NS-3 has two versions: frequency domain (FDMT) and time domain (TDMT). In FDMT, every TTI, MAC scheduler allocates RBGs to the UE who has highest achievable rate calculated by subband CQI. In TDMT, every TTI, MAC scheduler selects one UE which has highest achievable rate calculated by wideband CQI. Then MAC scheduler allocates all RBGs to this UE in current TTI. @@ -733,7 +733,7 @@ that, in every TTI, the scheduler tries the best to achieve the equal throughput Token Bank Fair Queue Scheduler ------------------------------- -Token Band Fair Queue (TBFQ) is a QoS aware scheduler which derives from the leaky-bucket mechanism. In TBFQ, +Token Bank Fair Queue (TBFQ) is a QoS aware scheduler which derives from the leaky-bucket mechanism. In TBFQ, a traffic flow of user :math:`i` is characterized by following parameters: * :math:`t_{i}`: packet arrival rate (byte/sec ) diff --git a/src/lte/doc/source/lte-references.rst b/src/lte/doc/source/lte-references.rst index a7d46c8b7..dd4a07680 100644 --- a/src/lte/doc/source/lte-references.rst +++ b/src/lte/doc/source/lte-references.rst @@ -86,7 +86,7 @@ References .. [R4-081920] 3GPP R4-081920 `LTE PDCCH/PCFICH Demodulation Performance Results with Implementation Margin `_ -.. [FCapo2012] F.Capozzi, G.Piro L.A.Grieco, G.Boggia, P.Camarda, "Downlink Packet Scheduling in LTE Cellular Networks: Key Design Issues and a Survey", IEEE Comm. Surveys and Tutorials, to appear +.. [FCapo2012] F.Capozzi, G.Piro L.A.Grieco, G.Boggia, P.Camarda, "Downlink Packet Scheduling in LTE Cellular Networks: Key Design Issues and a Survey", IEEE Comm. Surveys and Tutorials, vol.2012, no.99, pp.1-23, Jun. 2012 .. [FABokhari2009] F.A. Bokhari, H. Yanikomeroglu, W.K. Wong, M. Rahman, "Cross-Layer Resource Scheduling for Video Traffic in the Downlink of OFDMA-Based Wireless 4G Networks",EURASIP J. Wirel. Commun. Netw., vol.2009, no.3, pp. 1-10, Jan. 2009. diff --git a/src/lte/model/fdbet-ff-mac-scheduler.cc b/src/lte/model/fdbet-ff-mac-scheduler.cc index d184834bb..982818da3 100644 --- a/src/lte/model/fdbet-ff-mac-scheduler.cc +++ b/src/lte/model/fdbet-ff-mac-scheduler.cc @@ -246,7 +246,7 @@ FdBetFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&FdBetFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) - ; + ; return tid; } @@ -341,16 +341,14 @@ FdBetFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::C void FdBetFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdBetFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -382,16 +380,14 @@ FdBetFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider:: void FdBetFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdBetFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -677,8 +673,7 @@ FdBetFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sc void FdBetFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -847,7 +842,6 @@ FdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -859,7 +853,6 @@ FdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -887,8 +880,6 @@ FdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); - } rbAllocated += rbPerFlow; @@ -919,9 +910,6 @@ FdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc if (itStats != m_flowStatsUl.end ()) { (*itStats).second.lastTtiBytesTransmitted = uldci.m_tbSize; -// NS_LOG_DEBUG (this << " UE bytes txed " << (*it).second.lastTtiBytesTransmitted); - - } else { @@ -952,8 +940,6 @@ FdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted; // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term Evolution, Ed Wiley) (*itStats).second.lastAveragedThroughput = ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); - // NS_LOG_DEBUG (this << " UE tot bytes " << (*itStats).second.totalBytesTransmitted); - // NS_LOG_DEBUG (this << " UE avg thr " << (*itStats).second.lastAveragedThroughput); (*itStats).second.lastTtiBytesTransmitted = 0; } m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); @@ -964,16 +950,14 @@ FdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc void FdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdBetFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -1015,7 +999,6 @@ void FdBetFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -1060,7 +1043,6 @@ FdBetFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::Sc // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1253,7 +1235,6 @@ FdBetFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_ it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1303,7 +1284,6 @@ FdBetFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; diff --git a/src/lte/model/fdmt-ff-mac-scheduler.cc b/src/lte/model/fdmt-ff-mac-scheduler.cc index 6477dfc9a..8b5759e92 100644 --- a/src/lte/model/fdmt-ff-mac-scheduler.cc +++ b/src/lte/model/fdmt-ff-mac-scheduler.cc @@ -336,16 +336,14 @@ FdMtFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::Cs void FdMtFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdMtFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -377,16 +375,14 @@ FdMtFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::S void FdMtFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdMtFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -661,8 +657,7 @@ FdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch void FdMtFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -830,7 +825,6 @@ FdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -842,7 +836,6 @@ FdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -870,8 +863,6 @@ FdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); - } rbAllocated += rbPerFlow; @@ -922,16 +913,14 @@ FdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch void FdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdMtFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -973,7 +962,6 @@ void FdMtFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -1018,7 +1006,6 @@ FdMtFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::Sch // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1211,7 +1198,6 @@ FdMtFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1261,7 +1247,6 @@ FdMtFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; diff --git a/src/lte/model/fdtbfq-ff-mac-scheduler.cc b/src/lte/model/fdtbfq-ff-mac-scheduler.cc index 36220367e..231f30597 100644 --- a/src/lte/model/fdtbfq-ff-mac-scheduler.cc +++ b/src/lte/model/fdtbfq-ff-mac-scheduler.cc @@ -375,16 +375,14 @@ FdTbfqFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider:: void FdTbfqFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdTbfqFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -416,16 +414,14 @@ FdTbfqFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider: void FdTbfqFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdTbfqFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -515,27 +511,27 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S continue; } - std::set ::iterator rnti; + std::set ::iterator rnti; rnti = allocatedRnti.find((*it).first); - if (rnti != allocatedRnti.end ()) // already allocated RBGs to this UE - { - continue; - } + if (rnti != allocatedRnti.end ()) // already allocated RBGs to this UE + { + continue; + } - double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); + double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); - if (firstRnti == true) - { - metricMax = metric; - itMax = it; - firstRnti = false; - continue; - } - if (metric > metricMax) - { - metricMax = metric; - itMax = it; - } + if (firstRnti == true) + { + metricMax = metric; + itMax = it; + firstRnti = false; + continue; + } + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } } // end for m_flowStatsDl if (itMax == m_flowStatsDl.end()) @@ -551,37 +547,37 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S uint32_t budget = 0; if ( bankSize > 0 ) { - budget = (*itMax).second.counter - (*itMax).second.debtLimit; - if ( budget > (*itMax).second.burstCredit ) - budget = (*itMax).second.burstCredit; - if ( budget > bankSize ) - budget = bankSize; - } + budget = (*itMax).second.counter - (*itMax).second.debtLimit; + if ( budget > (*itMax).second.burstCredit ) + budget = (*itMax).second.burstCredit; + if ( budget > bankSize ) + budget = bankSize; + } budget = budget + (*itMax).second.tokenPoolSize; // calcualte how much bytes this UE actally need if (budget == 0) { // there are no tokens for this UE - continue; + continue; } else { - // calculate rlc buffer size - uint32_t rlcBufSize; + // calculate rlc buffer size + uint32_t rlcBufSize; uint8_t lcid; std::map::iterator itRlcBuf; for (itRlcBuf = m_rlcBufferReq.begin (); itRlcBuf != m_rlcBufferReq.end (); itRlcBuf++) - { + { if ( (*itRlcBuf).first.m_rnti == (*itMax).first ) lcid = (*itRlcBuf).first.m_lcId; - } + } LteFlowId_t flow ((*itMax).first, lcid); itRlcBuf = m_rlcBufferReq.find (flow); if (itRlcBuf!=m_rlcBufferReq.end ()) - rlcBufSize = (*itRlcBuf).second.m_rlcTransmissionQueueSize + (*itRlcBuf).second.m_rlcRetransmissionQueueSize + (*itRlcBuf).second.m_rlcStatusPduSize; - if ( budget > rlcBufSize ) - budget = rlcBufSize; + rlcBufSize = (*itRlcBuf).second.m_rlcTransmissionQueueSize + (*itRlcBuf).second.m_rlcRetransmissionQueueSize + (*itRlcBuf).second.m_rlcStatusPduSize; + if ( budget > rlcBufSize ) + budget = rlcBufSize; } // assign RBGs to this UE @@ -605,12 +601,12 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S // find RBG with largest achievableRate double achievableRateMax = 0.0; rbgIndex = rbgNum; - for (int k = 0; k < rbgNum; k++) - { + for (int k = 0; k < rbgNum; k++) + { std::set ::iterator rbg; rbg = allocatedRbg.find (k); - if (rbg != allocatedRbg.end ()) // RBGs are already allocated to this UE - continue; + if (rbg != allocatedRbg.end ()) // RBGs are already allocated to this UE + continue; std::vector sbCqi; if (itCqi == m_a30CqiRxed.end ()) @@ -636,7 +632,7 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S if (LcActivePerFlow ((*itMax).first) > 0) { // this UE has data to transmit - double achievableRate = 0.0; + double achievableRate = 0.0; for (uint8_t j = 0; j < nLayer; j++) { uint8_t mcs = 0; @@ -652,26 +648,26 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI } - if ( achievableRate > achievableRateMax ) - { - achievableRateMax = achievableRate; - rbgIndex = k; - } - } // end of LcActivePerFlow - } // end of cqi + if ( achievableRate > achievableRateMax ) + { + achievableRateMax = achievableRate; + rbgIndex = k; + } + } // end of LcActivePerFlow + } // end of cqi } // end of for rbgNum - if ( rbgIndex == rbgNum) // impossible - { - // all RBGs are already assigned - totalRbg = rbgNum; - break; - } - else - { - // mark this UE as "allocated" + if ( rbgIndex == rbgNum) // impossible + { + // all RBGs are already assigned + totalRbg = rbgNum; + break; + } + else + { + // mark this UE as "allocated" allocatedRbg.insert (rbgIndex); - } + } // assign this RBG to UE std::map >::iterator itMap; @@ -733,41 +729,41 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S } } - bytesTxedTmp = bytesTxed; + bytesTxedTmp = bytesTxed; bytesTxed = 0; - for (uint8_t j = 0; j < nLayer; j++) + for (uint8_t j = 0; j < nLayer; j++) { int tbSize = (m_amc->GetTbSizeFromMcs (m_amc->GetMcsFromCqi (worstCqi.at (j)), RbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) bytesTxed += tbSize; } - } // end of while() + } // end of while() - // remove and unmark last RBG assigned to UE - if ( bytesTxed > budget ) - { + // remove and unmark last RBG assigned to UE + if ( bytesTxed > budget ) + { std::map >::iterator itMap; itMap = allocationMap.find ((*itMax).first); (*itMap).second.pop_back (); allocatedRbg.erase (rbgIndex); - bytesTxed = bytesTxedTmp; // recovery bytesTxed + bytesTxed = bytesTxedTmp; // recovery bytesTxed totalRbg--; - } + } - // update UE stats + // update UE stats if ( bytesTxed <= (*itMax).second.tokenPoolSize ) { - (*itMax).second.tokenPoolSize -= bytesTxed; + (*itMax).second.tokenPoolSize -= bytesTxed; } else - { - (*itMax).second.counter = (*itMax).second.counter - ( bytesTxed - (*itMax).second.tokenPoolSize ); - (*itMax).second.tokenPoolSize = 0; - if (bankSize <= ( bytesTxed - (*itMax).second.tokenPoolSize )) - bankSize = 0; - else - bankSize = bankSize - ( bytesTxed - (*itMax).second.tokenPoolSize ); - } + { + (*itMax).second.counter = (*itMax).second.counter - ( bytesTxed - (*itMax).second.tokenPoolSize ); + (*itMax).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*itMax).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*itMax).second.tokenPoolSize ); + } } // end of RBGs // generate the transmission opportunities by grouping the RBGs of the same RNTI and @@ -896,8 +892,7 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S void FdTbfqFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -1065,7 +1060,6 @@ FdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -1077,7 +1071,6 @@ FdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -1105,7 +1098,6 @@ FdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); } @@ -1156,16 +1148,14 @@ FdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S void FdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void FdTbfqFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -1207,7 +1197,6 @@ void FdTbfqFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -1252,7 +1241,6 @@ FdTbfqFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::S // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1445,7 +1433,6 @@ FdTbfqFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16 it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1495,7 +1482,6 @@ FdTbfqFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; diff --git a/src/lte/model/pss-ff-mac-scheduler.cc b/src/lte/model/pss-ff-mac-scheduler.cc index 23ad9820a..288be1e3d 100644 --- a/src/lte/model/pss-ff-mac-scheduler.cc +++ b/src/lte/model/pss-ff-mac-scheduler.cc @@ -360,16 +360,14 @@ PssFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::Csc void PssFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void PssFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -401,16 +399,14 @@ PssFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::Sc void PssFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void PssFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -555,7 +551,6 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche else nMux = (int)((ueSet1.size() + ueSet2.size()) / 2) ; // TD scheduler only transfers half selected UE per RTT to TD scheduler } - for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it--) { std::vector >::iterator itSet; @@ -702,38 +697,38 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche sbCqi = 0; } colMetric += (double)sbCqi / (double)(*itSbCqiSum).second; - } + } } // end if cqi double metric; - if (colMetric != 0) - metric= weight * colMetric; - else - metric = 1; + if (colMetric != 0) + metric= weight * colMetric; + else + metric = 1; - if (metric > metricMax ) + if (metric > metricMax ) { metricMax = metric; itMax = it; } - } // end of tdUeSet + } // end of tdUeSet if (itMax == m_flowStatsDl.end ()) { // no UE available for downlink - return; + return; } else { // assign all RBGs to this UE std::vector tempMap; - for (int i = 0; i < rbgNum; i++) - { + for (int i = 0; i < rbgNum; i++) + { tempMap.push_back (i); - } + } allocationMap.insert (std::pair > ((*itMax).first, tempMap)); } - }// end of rbgNum + }// end of rbgNum }// end of CoIta @@ -748,8 +743,8 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche { // calculate PF weigth double weight = (*it).second.targetThroughput / (*it).second.lastAveragedThroughput; - if (weight < 1.0) - weight = 1.0; + if (weight < 1.0) + weight = 1.0; std::map ::iterator itCqi; itCqi = m_a30CqiRxed.find ((*it).first); @@ -795,9 +790,9 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche { // no info on this subband -> worst MCS mcs = 0; - } + } achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI - } + } schMetric = achievableRate / (*it).second.secondLastAveragedThroughput; } // end if cqi @@ -995,8 +990,7 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche void PssFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -1164,7 +1158,6 @@ PssFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -1176,7 +1169,6 @@ PssFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -1204,8 +1196,6 @@ PssFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); - } rbAllocated += rbPerFlow; @@ -1255,16 +1245,14 @@ PssFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche void PssFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void PssFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -1306,7 +1294,6 @@ void PssFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -1351,7 +1338,6 @@ PssFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::Sche // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1544,7 +1530,6 @@ PssFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1594,7 +1579,6 @@ PssFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; diff --git a/src/lte/model/tdbet-ff-mac-scheduler.cc b/src/lte/model/tdbet-ff-mac-scheduler.cc index 8cf5e3d2d..e84cfeb85 100644 --- a/src/lte/model/tdbet-ff-mac-scheduler.cc +++ b/src/lte/model/tdbet-ff-mac-scheduler.cc @@ -341,16 +341,14 @@ TdBetFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::C void TdBetFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdBetFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -382,16 +380,14 @@ TdBetFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider:: void TdBetFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdBetFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -604,8 +600,7 @@ TdBetFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sc void TdBetFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -774,7 +769,6 @@ TdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -786,7 +780,6 @@ TdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -814,7 +807,6 @@ TdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); } @@ -846,9 +838,6 @@ TdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc if (itStats != m_flowStatsUl.end ()) { (*itStats).second.lastTtiBytesTransmitted = uldci.m_tbSize; -// NS_LOG_DEBUG (this << " UE bytes txed " << (*it).second.lastTtiBytesTransmitted); - - } else { @@ -879,8 +868,6 @@ TdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted; // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term Evolution, Ed Wiley) (*itStats).second.lastAveragedThroughput = ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) + ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001)); - // NS_LOG_DEBUG (this << " UE tot bytes " << (*itStats).second.totalBytesTransmitted); - // NS_LOG_DEBUG (this << " UE avg thr " << (*itStats).second.lastAveragedThroughput); (*itStats).second.lastTtiBytesTransmitted = 0; } m_allocationMaps.insert (std::pair > (params.m_sfnSf, rbgAllocationMap)); @@ -891,16 +878,14 @@ TdBetFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sc void TdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdBetFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -942,7 +927,6 @@ void TdBetFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -987,7 +971,6 @@ TdBetFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::Sc // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1180,7 +1163,6 @@ TdBetFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_ it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1230,7 +1212,6 @@ TdBetFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; diff --git a/src/lte/model/tdmt-ff-mac-scheduler.cc b/src/lte/model/tdmt-ff-mac-scheduler.cc index d5267e2a6..09f0d3985 100644 --- a/src/lte/model/tdmt-ff-mac-scheduler.cc +++ b/src/lte/model/tdmt-ff-mac-scheduler.cc @@ -248,7 +248,7 @@ TdMtFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&TdMtFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) - ; + ; return tid; } @@ -336,16 +336,14 @@ TdMtFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::Cs void TdMtFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdMtFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -377,16 +375,14 @@ TdMtFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::S void TdMtFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdMtFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -458,18 +454,18 @@ TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch NS_FATAL_ERROR ("No Transmission Mode info on user " << (*it)); } int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); - uint8_t wbCqi = 0; - if (itCqi == m_p10CqiRxed.end()) - { - wbCqi = 1; // start with lowest value - } + uint8_t wbCqi = 0; + if (itCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } else { - wbCqi = (*itCqi).second; + wbCqi = (*itCqi).second; } - if (wbCqi > 0) - { + if (wbCqi > 0) + { if (LcActivePerFlow (*it) > 0) { // this UE has data to transmit @@ -481,30 +477,30 @@ TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI } - double metric = achievableRate; + double metric = achievableRate; if (metric > metricMax) { metricMax = metric; itMax = it; } - } + } } // end of wbCqi } // end for m_flowStatsDl if (itMax == m_flowStatsDl.end ()) { - // no UE available for downlink - return; + // no UE available for downlink + return; } else { // assign all RBGs to this UE std::vector tempMap; - for (int i = 0; i < rbgNum; i++) - { + for (int i = 0; i < rbgNum; i++) + { tempMap.push_back (i); - } + } allocationMap.insert (std::pair > ((*itMax), tempMap)); } @@ -523,8 +519,8 @@ TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch newDci.m_rnti = (*itMap).first; uint16_t lcActives = LcActivePerFlow ((*itMap).first); - std::map ::iterator itCqi; - itCqi = m_p10CqiRxed.find((*itMap).first); + std::map ::iterator itCqi; + itCqi = m_p10CqiRxed.find((*itMap).first); std::map ::iterator itTxMode; itTxMode = m_uesTxMode.find ((*itMap).first); if (itTxMode == m_uesTxMode.end ()) @@ -601,8 +597,7 @@ TdMtFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sch void TdMtFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -770,7 +765,6 @@ TdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -782,7 +776,6 @@ TdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -810,7 +803,6 @@ TdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); } @@ -862,16 +854,14 @@ TdMtFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sch void TdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdMtFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -913,7 +903,6 @@ void TdMtFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -958,7 +947,6 @@ TdMtFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::Sch // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1151,7 +1139,6 @@ TdMtFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1201,7 +1188,6 @@ TdMtFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; diff --git a/src/lte/model/tdtbfq-ff-mac-scheduler.cc b/src/lte/model/tdtbfq-ff-mac-scheduler.cc index f652e583b..f78fcb514 100644 --- a/src/lte/model/tdtbfq-ff-mac-scheduler.cc +++ b/src/lte/model/tdtbfq-ff-mac-scheduler.cc @@ -375,16 +375,14 @@ TdTbfqFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider:: void TdTbfqFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdTbfqFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -416,16 +414,14 @@ TdTbfqFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider: void TdTbfqFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdTbfqFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -661,18 +657,18 @@ TdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S if (it != m_flowStatsDl.end ()) { if ( bytesTxed <= (*it).second.tokenPoolSize ) - { - (*it).second.tokenPoolSize -= bytesTxed; - } - else - { - (*it).second.counter = (*it).second.counter - ( bytesTxed - (*it).second.tokenPoolSize ); - (*it).second.tokenPoolSize = 0; - if (bankSize <= ( bytesTxed - (*it).second.tokenPoolSize )) - bankSize = 0; - else - bankSize = bankSize - ( bytesTxed - (*it).second.tokenPoolSize ); - } + { + (*it).second.tokenPoolSize -= bytesTxed; + } + else + { + (*it).second.counter = (*it).second.counter - ( bytesTxed - (*it).second.tokenPoolSize ); + (*it).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*it).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*it).second.tokenPoolSize ); + } } else { @@ -692,8 +688,7 @@ TdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S void TdTbfqFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -861,7 +856,6 @@ TdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -873,7 +867,6 @@ TdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -901,7 +894,6 @@ TdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); } @@ -952,16 +944,14 @@ TdTbfqFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::S void TdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TdTbfqFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -1003,7 +993,6 @@ void TdTbfqFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -1048,7 +1037,6 @@ TdTbfqFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::S // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1241,7 +1229,6 @@ TdTbfqFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16 it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1291,7 +1278,6 @@ TdTbfqFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; diff --git a/src/lte/model/tta-ff-mac-scheduler.cc b/src/lte/model/tta-ff-mac-scheduler.cc index 3a5dacbac..1d974558f 100644 --- a/src/lte/model/tta-ff-mac-scheduler.cc +++ b/src/lte/model/tta-ff-mac-scheduler.cc @@ -248,7 +248,7 @@ TtaFfMacScheduler::GetTypeId (void) UintegerValue (1000), MakeUintegerAccessor (&TtaFfMacScheduler::m_cqiTimersThreshold), MakeUintegerChecker ()) - ; + ; return tid; } @@ -336,16 +336,14 @@ TtaFfMacScheduler::DoCschedLcConfigReq (const struct FfMacCschedSapProvider::Csc void TtaFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TtaFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -377,16 +375,14 @@ TtaFfMacScheduler::DoSchedDlRlcBufferReq (const struct FfMacSchedSapProvider::Sc void TtaFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TtaFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -475,14 +471,14 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche sbCqi = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; } - uint8_t wbCqi = 0; - if (itWbCqi == m_p10CqiRxed.end()) - { - wbCqi = 1; // start with lowest value - } + uint8_t wbCqi = 0; + if (itWbCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } else { - wbCqi = (*itWbCqi).second; + wbCqi = (*itWbCqi).second; } uint8_t cqi1 = sbCqi.at(0); @@ -497,7 +493,7 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche if (LcActivePerFlow (*it) > 0) { // this UE has data to transmit - uint8_t sbMcs = 0; + uint8_t sbMcs = 0; uint8_t wbMcs = 0; double achievableSbRate = 1.0; double achievableWbRate = 1.0; @@ -519,7 +515,7 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche double metric = achievableSbRate / achievableWbRate; - if (metric > metricMax) + if (metric > metricMax) { metricMax = metric; itMax = it; @@ -677,8 +673,7 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche void TtaFfMacScheduler::DoSchedDlRachInfoReq (const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -846,7 +841,6 @@ TtaFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche { // no cqi info about this UE uldci.m_mcs = 0; // MCS 0 -> UL-AMC TBD -// NS_LOG_DEBUG (this << " UE does not have ULCQI " << (*it).first ); } else { @@ -858,7 +852,6 @@ TtaFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche } for (uint16_t i = uldci.m_rbStart; i < uldci.m_rbStart + uldci.m_rbLen; i++) { -// NS_LOG_DEBUG (this << " UE " << (*it).first << " has SINR " << (*itCqi).second.at(i)); double sinr = (*itCqi).second.at (i); if (sinr == NO_SINR) { @@ -886,7 +879,6 @@ TtaFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche continue; // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213) } uldci.m_mcs = m_amc->GetMcsFromCqi (cqi); -// NS_LOG_DEBUG (this << " UE " << (*it).first << " minsinr " << minSinr << " -> mcs " << (uint16_t)uldci.m_mcs); } @@ -938,16 +930,14 @@ TtaFfMacScheduler::DoSchedUlTriggerReq (const struct FfMacSchedSapProvider::Sche void TtaFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } void TtaFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params) { - NS_LOG_FUNCTION (this); - // TODO: Implementation of the API + NS_FATAL_ERROR ("unimplemented"); return; } @@ -989,7 +979,6 @@ void TtaFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) { NS_LOG_FUNCTION (this); -// NS_LOG_DEBUG (this << " RX SFNID " << params.m_sfnSf); // retrieve the allocation for this subframe switch (m_ulCqiFilter) { @@ -1034,7 +1023,6 @@ TtaFfMacScheduler::DoSchedUlCqiInfoReq (const struct FfMacSchedSapProvider::Sche // convert from fixed point notation Sxxxxxxxxxxx.xxx to double // NS_LOG_INFO (this << " i " << i << " size " << params.m_ulCqi.m_sinr.size () << " mapSIze " << (*itMap).second.size ()); double sinr = LteFfConverter::fpS11dot3toDouble (params.m_ulCqi.m_sinr.at (i)); - //NS_LOG_DEBUG (this << " UE " << (*itMap).second.at (i) << " SINRfp " << params.m_ulCqi.m_sinr.at (i) << " sinrdb " << sinr); itCqi = m_ueCqi.find ((*itMap).second.at (i)); if (itCqi == m_ueCqi.end ()) { @@ -1227,7 +1215,6 @@ TtaFfMacScheduler::UpdateDlRlcBufferInfo (uint16_t rnti, uint8_t lcid, uint16_t it = m_rlcBufferReq.find (flow); if (it!=m_rlcBufferReq.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " LC " << (uint16_t)lcid << " txqueue " << (*it).second.m_rlcTransmissionQueueSize << " retxqueue " << (*it).second.m_rlcRetransmissionQueueSize << " status " << (*it).second.m_rlcStatusPduSize << " decrease " << size); // Update queues: RLC tx order Status, ReTx, Tx // Update status queue if ((*it).second.m_rlcStatusPduSize <= size) @@ -1277,7 +1264,6 @@ TtaFfMacScheduler::UpdateUlRlcBufferInfo (uint16_t rnti, uint16_t size) std::map ::iterator it = m_ceBsrRxed.find (rnti); if (it!=m_ceBsrRxed.end ()) { -// NS_LOG_DEBUG (this << " UE " << rnti << " size " << size << " BSR " << (*it).second); if ((*it).second >= size) { (*it).second -= size; From 2a844fe0231b42f39064f949517c9482e24ca976 Mon Sep 17 00:00:00 2001 From: Dizhi Zhou Date: Fri, 30 Nov 2012 18:05:02 -0500 Subject: [PATCH 4/4] correct code style errors in GSoC 2012 LTE NS-3 MAC schedulers --- src/lte/model/fdtbfq-ff-mac-scheduler.cc | 138 +++++++++++------------ src/lte/model/pss-ff-mac-scheduler.cc | 36 +++--- src/lte/model/tdbet-ff-mac-scheduler.cc | 2 +- src/lte/model/tdtbfq-ff-mac-scheduler.cc | 24 ++-- src/lte/model/tta-ff-mac-scheduler.cc | 16 +-- 5 files changed, 108 insertions(+), 108 deletions(-) diff --git a/src/lte/model/fdtbfq-ff-mac-scheduler.cc b/src/lte/model/fdtbfq-ff-mac-scheduler.cc index 231f30597..0f26d46a7 100644 --- a/src/lte/model/fdtbfq-ff-mac-scheduler.cc +++ b/src/lte/model/fdtbfq-ff-mac-scheduler.cc @@ -511,28 +511,28 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S continue; } - std::set ::iterator rnti; + std::set ::iterator rnti; rnti = allocatedRnti.find((*it).first); - if (rnti != allocatedRnti.end ()) // already allocated RBGs to this UE - { - continue; - } + if (rnti != allocatedRnti.end ()) // already allocated RBGs to this UE + { + continue; + } - double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); + double metric = ( ( (double)(*it).second.counter ) / ( (double)(*it).second.tokenGenerationRate ) ); - if (firstRnti == true) - { - metricMax = metric; - itMax = it; - firstRnti = false; - continue; - } - if (metric > metricMax) - { - metricMax = metric; - itMax = it; - } - } // end for m_flowStatsDl + if (firstRnti == true) + { + metricMax = metric; + itMax = it; + firstRnti = false; + continue; + } + if (metric > metricMax) + { + metricMax = metric; + itMax = it; + } + } // end for m_flowStatsDl if (itMax == m_flowStatsDl.end()) { @@ -547,37 +547,37 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S uint32_t budget = 0; if ( bankSize > 0 ) { - budget = (*itMax).second.counter - (*itMax).second.debtLimit; - if ( budget > (*itMax).second.burstCredit ) - budget = (*itMax).second.burstCredit; - if ( budget > bankSize ) - budget = bankSize; - } + budget = (*itMax).second.counter - (*itMax).second.debtLimit; + if ( budget > (*itMax).second.burstCredit ) + budget = (*itMax).second.burstCredit; + if ( budget > bankSize ) + budget = bankSize; + } budget = budget + (*itMax).second.tokenPoolSize; // calcualte how much bytes this UE actally need if (budget == 0) { // there are no tokens for this UE - continue; + continue; } else { // calculate rlc buffer size - uint32_t rlcBufSize; + uint32_t rlcBufSize; uint8_t lcid; std::map::iterator itRlcBuf; for (itRlcBuf = m_rlcBufferReq.begin (); itRlcBuf != m_rlcBufferReq.end (); itRlcBuf++) - { + { if ( (*itRlcBuf).first.m_rnti == (*itMax).first ) lcid = (*itRlcBuf).first.m_lcId; - } + } LteFlowId_t flow ((*itMax).first, lcid); itRlcBuf = m_rlcBufferReq.find (flow); if (itRlcBuf!=m_rlcBufferReq.end ()) - rlcBufSize = (*itRlcBuf).second.m_rlcTransmissionQueueSize + (*itRlcBuf).second.m_rlcRetransmissionQueueSize + (*itRlcBuf).second.m_rlcStatusPduSize; - if ( budget > rlcBufSize ) - budget = rlcBufSize; + rlcBufSize = (*itRlcBuf).second.m_rlcTransmissionQueueSize + (*itRlcBuf).second.m_rlcRetransmissionQueueSize + (*itRlcBuf).second.m_rlcStatusPduSize; + if ( budget > rlcBufSize ) + budget = rlcBufSize; } // assign RBGs to this UE @@ -601,12 +601,12 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S // find RBG with largest achievableRate double achievableRateMax = 0.0; rbgIndex = rbgNum; - for (int k = 0; k < rbgNum; k++) - { + for (int k = 0; k < rbgNum; k++) + { std::set ::iterator rbg; rbg = allocatedRbg.find (k); - if (rbg != allocatedRbg.end ()) // RBGs are already allocated to this UE - continue; + if (rbg != allocatedRbg.end ()) // RBGs are already allocated to this UE + continue; std::vector sbCqi; if (itCqi == m_a30CqiRxed.end ()) @@ -632,7 +632,7 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S if (LcActivePerFlow ((*itMax).first) > 0) { // this UE has data to transmit - double achievableRate = 0.0; + double achievableRate = 0.0; for (uint8_t j = 0; j < nLayer; j++) { uint8_t mcs = 0; @@ -648,26 +648,26 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI } - if ( achievableRate > achievableRateMax ) - { - achievableRateMax = achievableRate; - rbgIndex = k; - } - } // end of LcActivePerFlow - } // end of cqi + if ( achievableRate > achievableRateMax ) + { + achievableRateMax = achievableRate; + rbgIndex = k; + } + } // end of LcActivePerFlow + } // end of cqi } // end of for rbgNum - if ( rbgIndex == rbgNum) // impossible - { - // all RBGs are already assigned - totalRbg = rbgNum; - break; - } - else - { - // mark this UE as "allocated" + if ( rbgIndex == rbgNum) // impossible + { + // all RBGs are already assigned + totalRbg = rbgNum; + break; + } + else + { + // mark this UE as "allocated" allocatedRbg.insert (rbgIndex); - } + } // assign this RBG to UE std::map >::iterator itMap; @@ -731,39 +731,39 @@ FdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S bytesTxedTmp = bytesTxed; bytesTxed = 0; - for (uint8_t j = 0; j < nLayer; j++) + for (uint8_t j = 0; j < nLayer; j++) { int tbSize = (m_amc->GetTbSizeFromMcs (m_amc->GetMcsFromCqi (worstCqi.at (j)), RbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) bytesTxed += tbSize; } - } // end of while() + } // end of while() // remove and unmark last RBG assigned to UE - if ( bytesTxed > budget ) - { + if ( bytesTxed > budget ) + { std::map >::iterator itMap; itMap = allocationMap.find ((*itMax).first); (*itMap).second.pop_back (); allocatedRbg.erase (rbgIndex); - bytesTxed = bytesTxedTmp; // recovery bytesTxed + bytesTxed = bytesTxedTmp; // recovery bytesTxed totalRbg--; - } + } // update UE stats if ( bytesTxed <= (*itMax).second.tokenPoolSize ) { - (*itMax).second.tokenPoolSize -= bytesTxed; + (*itMax).second.tokenPoolSize -= bytesTxed; } else - { - (*itMax).second.counter = (*itMax).second.counter - ( bytesTxed - (*itMax).second.tokenPoolSize ); - (*itMax).second.tokenPoolSize = 0; - if (bankSize <= ( bytesTxed - (*itMax).second.tokenPoolSize )) - bankSize = 0; - else - bankSize = bankSize - ( bytesTxed - (*itMax).second.tokenPoolSize ); - } + { + (*itMax).second.counter = (*itMax).second.counter - ( bytesTxed - (*itMax).second.tokenPoolSize ); + (*itMax).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*itMax).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*itMax).second.tokenPoolSize ); + } } // end of RBGs // generate the transmission opportunities by grouping the RBGs of the same RNTI and diff --git a/src/lte/model/pss-ff-mac-scheduler.cc b/src/lte/model/pss-ff-mac-scheduler.cc index 288be1e3d..e1078b480 100644 --- a/src/lte/model/pss-ff-mac-scheduler.cc +++ b/src/lte/model/pss-ff-mac-scheduler.cc @@ -505,7 +505,7 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche } int nLayer = TransmissionModesLayers::TxMode2LayerNum ((*itTxMode).second); uint8_t wbCqi = 0; - if (itCqi == m_p10CqiRxed.end()) + if (itCqi == m_p10CqiRxed.end()) { wbCqi = 1; // start with lowest value } @@ -701,34 +701,34 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche } // end if cqi double metric; - if (colMetric != 0) - metric= weight * colMetric; - else - metric = 1; + if (colMetric != 0) + metric= weight * colMetric; + else + metric = 1; - if (metric > metricMax ) + if (metric > metricMax ) { metricMax = metric; itMax = it; } - } // end of tdUeSet + } // end of tdUeSet if (itMax == m_flowStatsDl.end ()) { // no UE available for downlink - return; + return; } else { // assign all RBGs to this UE std::vector tempMap; - for (int i = 0; i < rbgNum; i++) - { + for (int i = 0; i < rbgNum; i++) + { tempMap.push_back (i); - } + } allocationMap.insert (std::pair > ((*itMax).first, tempMap)); } - }// end of rbgNum + }// end of rbgNum }// end of CoIta @@ -743,8 +743,8 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche { // calculate PF weigth double weight = (*it).second.targetThroughput / (*it).second.lastAveragedThroughput; - if (weight < 1.0) - weight = 1.0; + if (weight < 1.0) + weight = 1.0; std::map ::iterator itCqi; itCqi = m_a30CqiRxed.find ((*it).first); @@ -784,15 +784,15 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche uint8_t mcs = 0; if (sbCqis.size () > k) { - mcs = m_amc->GetMcsFromCqi (sbCqis.at (k)); + mcs = m_amc->GetMcsFromCqi (sbCqis.at (k)); } else { // no info on this subband -> worst MCS mcs = 0; - } + } achievableRate += ((m_amc->GetTbSizeFromMcs (mcs, rbgSize) / 8) / 0.001); // = TB size / TTI - } + } schMetric = achievableRate / (*it).second.secondLastAveragedThroughput; } // end if cqi @@ -899,7 +899,7 @@ PssFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche } } uint32_t bytesTxed = 0; - for (uint8_t j = 0; j < nLayer; j++) + for (uint8_t j = 0; j < nLayer; j++) { newDci.m_mcs.push_back (m_amc->GetMcsFromCqi (worstCqi.at (j))); int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (j), rbgPerRnti * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) diff --git a/src/lte/model/tdbet-ff-mac-scheduler.cc b/src/lte/model/tdbet-ff-mac-scheduler.cc index e84cfeb85..b1f2b9f0b 100644 --- a/src/lte/model/tdbet-ff-mac-scheduler.cc +++ b/src/lte/model/tdbet-ff-mac-scheduler.cc @@ -518,7 +518,7 @@ TdBetFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sc } for (uint8_t i = 0; i < nLayer; i++) - { + { int tbSize = (m_amc->GetTbSizeFromMcs (newDci.m_mcs.at (0), rbgNum * rbgSize) / 8); // (size of TB in bytes according to table 7.1.7.2.1-1 of 36.213) newDci.m_tbsSize.push_back (tbSize); bytesTxed += tbSize; diff --git a/src/lte/model/tdtbfq-ff-mac-scheduler.cc b/src/lte/model/tdtbfq-ff-mac-scheduler.cc index f78fcb514..8e2f2f57b 100644 --- a/src/lte/model/tdtbfq-ff-mac-scheduler.cc +++ b/src/lte/model/tdtbfq-ff-mac-scheduler.cc @@ -657,18 +657,18 @@ TdTbfqFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::S if (it != m_flowStatsDl.end ()) { if ( bytesTxed <= (*it).second.tokenPoolSize ) - { - (*it).second.tokenPoolSize -= bytesTxed; - } - else - { - (*it).second.counter = (*it).second.counter - ( bytesTxed - (*it).second.tokenPoolSize ); - (*it).second.tokenPoolSize = 0; - if (bankSize <= ( bytesTxed - (*it).second.tokenPoolSize )) - bankSize = 0; - else - bankSize = bankSize - ( bytesTxed - (*it).second.tokenPoolSize ); - } + { + (*it).second.tokenPoolSize -= bytesTxed; + } + else + { + (*it).second.counter = (*it).second.counter - ( bytesTxed - (*it).second.tokenPoolSize ); + (*it).second.tokenPoolSize = 0; + if (bankSize <= ( bytesTxed - (*it).second.tokenPoolSize )) + bankSize = 0; + else + bankSize = bankSize - ( bytesTxed - (*it).second.tokenPoolSize ); + } } else { diff --git a/src/lte/model/tta-ff-mac-scheduler.cc b/src/lte/model/tta-ff-mac-scheduler.cc index 1d974558f..2c58ea95f 100644 --- a/src/lte/model/tta-ff-mac-scheduler.cc +++ b/src/lte/model/tta-ff-mac-scheduler.cc @@ -471,14 +471,14 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche sbCqi = (*itCqi).second.m_higherLayerSelected.at (i).m_sbCqi; } - uint8_t wbCqi = 0; - if (itWbCqi == m_p10CqiRxed.end()) - { - wbCqi = 1; // start with lowest value - } + uint8_t wbCqi = 0; + if (itWbCqi == m_p10CqiRxed.end()) + { + wbCqi = 1; // start with lowest value + } else { - wbCqi = (*itWbCqi).second; + wbCqi = (*itWbCqi).second; } uint8_t cqi1 = sbCqi.at(0); @@ -493,7 +493,7 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche if (LcActivePerFlow (*it) > 0) { // this UE has data to transmit - uint8_t sbMcs = 0; + uint8_t sbMcs = 0; uint8_t wbMcs = 0; double achievableSbRate = 1.0; double achievableWbRate = 1.0; @@ -515,7 +515,7 @@ TtaFfMacScheduler::DoSchedDlTriggerReq (const struct FfMacSchedSapProvider::Sche double metric = achievableSbRate / achievableWbRate; - if (metric > metricMax) + if (metric > metricMax) { metricMax = metric; itMax = it;