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']):