merge
authorNicola Baldo <nicola@baldo.biz>
Sat, 01 Dec 2012 11:40:51 +0100
changeset 9154 e2048877ac28
parent 9148 9c37ec6053bd (current diff)
parent 9153 f04612961e79 (diff)
child 9155 3a2efbb0e7d9
merge
RELEASE_NOTES
--- a/AUTHORS	Wed Nov 28 21:32:28 2012 +0100
+++ b/AUTHORS	Sat Dec 01 11:40:51 2012 +0100
@@ -104,3 +104,4 @@
 Florian Westphal (fw@strlen.de)
 He Wu (mdzz@u.washington.edu)
 Yoshihiko Yazawa (yoshiyaz@gmail.com)
+Dizhi Zhou (dizhi.zhou@gmail.com)
--- a/RELEASE_NOTES	Wed Nov 28 21:32:28 2012 +0100
+++ b/RELEASE_NOTES	Sat Dec 01 11:40:51 2012 +0100
@@ -21,6 +21,8 @@
 
 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
 ----------
--- a/src/lte/AUTHORS	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/AUTHORS	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <dizhi.zhou@gmail.com>
+develop several LTE MAC schedulers
+ 
+Nicola Baldo, CTTC <nbaldo@cttc.es>
+Marco Miozzo, CTTC <mmiozzo@cttc.es>
+supervision and GSoC project mentorship
+
+
 ====================================================
        Google Summer of Code (GSoC) (2010)
 ====================================================
--- a/src/lte/doc/source/lte-design.rst	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/doc/source/lte-design.rst	Sat Dec 01 11:40:51 2012 +0100
@@ -652,6 +652,176 @@
    \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 NS-3 has two versions: frequency domain (FDMT) and time domain (TDMT).
+In FDMT, every TTI, MAC scheduler allocates RBGs to the UE who has highest achievable rate calculated 
+by subband CQI. In TDMT, every TTI, MAC scheduler selects one UE which has highest achievable rate 
+calculated by wideband CQI. Then MAC scheduler allocates all RBGs to this UE in current TTI.
+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 Bank Fair Queue (TBFQ) is a QoS aware scheduler which derives from the leaky-bucket mechanism. In TBFQ, 
+a traffic flow of user :math:`i` is characterized by following parameters: 
+
+ * :math:`t_{i}`: packet arrival rate (byte/sec )
+ * :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
 ----------------
--- a/src/lte/doc/source/lte-references.rst	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/doc/source/lte-references.rst	Sat Dec 01 11:40:51 2012 +0100
@@ -86,5 +86,11 @@
 .. [R4-081920] 3GPP R4-081920 `LTE PDCCH/PCFICH Demodulation Performance Results with Implementation Margin
  <http://www.3gpp.org/ftp/tsg_ran/wg4_radio/TSGR4_48/Documents/R4-081920.zip>`_
 
+.. [FCapo2012] F.Capozzi, G.Piro L.A.Grieco, G.Boggia, P.Camarda, "Downlink Packet Scheduling in LTE Cellular Networks: Key Design Issues and a Survey", IEEE Comm. Surveys and Tutorials, vol.2012, no.99, pp.1-23, Jun. 2012
 
+.. [FABokhari2009] F.A. Bokhari, H. Yanikomeroglu, W.K. Wong, M. Rahman, "Cross-Layer Resource Scheduling for Video Traffic in the Downlink of OFDMA-Based Wireless 4G Networks",EURASIP J. Wirel. Commun. Netw., vol.2009, no.3, pp. 1-10, Jan. 2009. 
 
+.. [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.
+
--- a/src/lte/doc/source/lte-testing.rst	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/doc/source/lte-testing.rst	Sat Dec 01 11:40:51 2012 +0100
@@ -347,6 +347,197 @@
    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
 -------------------------------
--- a/src/lte/doc/source/lte-user.rst	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/doc/source/lte-user.rst	Sat Dec 01 11:40:51 2012 +0100
@@ -189,6 +189,61 @@
 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> lteHelper = CreateObject<LteHelper> ();
+ 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> lteHelper = CreateObject<LteHelper> ();
+  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> lteHelper = CreateObject<LteHelper> ();
+  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> lteHelper = CreateObject<LteHelper> ();
+  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
 -----------------
--- a/src/lte/model/eps-bearer.cc	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/model/eps-bearer.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -38,6 +38,11 @@
 {
 }
 
+EpsBearer::EpsBearer (Qci x, struct GbrQosInformation y)
+  : qci (x), gbrQosInfo (y)
+{
+}
+
 bool
 EpsBearer::IsGbr () const
 {
--- a/src/lte/model/eps-bearer.h	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/model/eps-bearer.h	Sat Dec 01 11:40:51 2012 +0100
@@ -88,6 +88,8 @@
    */
   EpsBearer (Qci x);
 
+  EpsBearer (Qci x, GbrQosInformation y);
+
   /**
    *
    * @return true if the EPS Bearer is a Guaranteed Bit Rate bearer, false otherwise
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/fdbet-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -0,0 +1,1314 @@
+/* -*- 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 <marco.miozzo@cttc.es>        // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/math.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/fdbet-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<FdBetFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&FdBetFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+    ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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 <uint16_t, fdbetsFlowPerf_t>::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<uint16_t, fdbetsFlowPerf_t> (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<uint16_t, fdbetsFlowPerf_t> (params.m_rnti, flowStatsUl));
+        }
+      else
+        {
+          NS_LOG_ERROR ("RNTI already exists");
+        }
+    }
+
+  return;
+}
+
+void
+FdBetFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdBetFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+FdBetFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdBetFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+  std::map <uint16_t, fdbetsFlowPerf_t>::iterator itFlow;
+  std::map <uint16_t, double> estAveThr;                                // store expected average throughput for UE
+  std::map <uint16_t, double>::iterator itMax = estAveThr.end ();
+  std::map <uint16_t, double>::iterator it;
+  std::map <uint16_t, int> 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 <uint16_t, double> ((*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<uint16_t, int> ((*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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+      std::vector <uint16_t> tempMap;
+      itMap = allocationMap.find ((*itMax).first);
+      if (itMap == allocationMap.end ())
+        {
+          tempMap.push_back (i);
+          allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*itMax).first, tempMap));
+        }
+      else
+        {
+          (*itMap).second.push_back (i);
+        }
+  
+      // caculate expected throughput for current UE
+      std::map <uint16_t,uint8_t>::iterator itCqi;
+      itCqi = m_p10CqiRxed.find ((*itMax).first);
+      std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <uint16_t,int>::iterator itRbgPerRntiLog;
+      itRbgPerRntiLog = rbgPerRntiLog.find ((*itMax).first);
+      std::map <uint16_t, fdbetsFlowPerf_t>::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 <uint16_t, fdbetsFlowPerf_t>::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 <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      uint16_t rbgPerRnti = (*itMap).second.size ();
+      std::map <uint16_t,uint8_t>::iterator itCqi;
+      itCqi = m_p10CqiRxed.find ((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, fdbetsFlowPerf_t>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> rbgAllocationMap;
+  std::map <uint16_t, fdbetsFlowPerf_t>::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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+        }
+      
+      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;
+        }
+      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));
+      (*itStats).second.lastTtiBytesTransmitted = 0;
+    }
+  m_allocationMaps.insert (std::pair <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+FdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdBetFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdBetFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+FdBetFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/fdbet-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef FDBET_FF_MAC_SCHEDULER_H
+#define FDBET_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Map of UE statistics (per RNTI basis) in downlink
+  */
+  std::map <uint16_t, fdbetsFlowPerf_t> m_flowStatsDl;
+
+  /*
+  * Map of UE statistics (per RNTI basis)
+  */
+  std::map <uint16_t, fdbetsFlowPerf_t> m_flowStatsUl;
+
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> m_uesTxMode; // txMode of the UEs
+};
+
+} // namespace ns3
+
+#endif /* FDBET_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/fdmt-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -0,0 +1,1277 @@
+/* -*- 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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/math.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/fdmt-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<FdMtFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&FdMtFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+    ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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<uint16_t>::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_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdMtFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+FdMtFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdMtFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+  for (int i = 0; i < rbgNum; i++)
+    {
+      std::set <uint16_t>::iterator it;
+      std::set <uint16_t>::iterator itMax = m_flowStatsDl.end ();
+      double rcqiMax = 0.0;
+      for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++)
+        {
+          std::map <uint16_t,SbMeasResult_s>::iterator itCqi;
+          itCqi = m_a30CqiRxed.find ((*it));
+          std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+          itMap = allocationMap.find ((*itMax));
+          if (itMap == allocationMap.end ())
+            {
+              // insert new element
+              std::vector <uint16_t> tempMap;
+              tempMap.push_back (i);
+              allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*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 <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      uint16_t rbgPerRnti = (*itMap).second.size ();
+      std::map <uint16_t,SbMeasResult_s>::iterator itCqi;
+      itCqi = m_a30CqiRxed.find ((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> 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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+        }
+      
+      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 <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+FdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdMtFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdMtFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+FdMtFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/fdmt-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef FDMT_FF_MAC_SCHEDULER_H
+#define FDMT_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <set>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Set of UE's RNTI in downlink
+  */
+  std::set <uint16_t> m_flowStatsDl;
+
+  /*
+  * Set of UE's RNTI in uplink
+  */
+  std::set <uint16_t> m_flowStatsUl;
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> m_uesTxMode; // txMode of the UEs
+};
+
+} // namespace ns3
+
+#endif /* FDMT_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/fdtbfq-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -0,0 +1,1512 @@
+/* -*- 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 <marco.miozzo@cttc.es>        // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/integer.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/fdtbfq-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<FdTbfqFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+    .AddAttribute ("DebtLimit",
+                   "Flow debt limit (default -625000 bytes)",
+                   IntegerValue (-625000),
+                   MakeIntegerAccessor (&FdTbfqFfMacScheduler::m_debtLimit),
+                   MakeIntegerChecker<int> ())
+    .AddAttribute ("CreditLimit",
+                   "Flow credit limit (default 625000 bytes)",
+                   UintegerValue (625000),
+                   MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_creditLimit),
+                   MakeUintegerChecker<uint32_t> ())
+    .AddAttribute ("TokenPoolSize",
+                   "The maximum value of flow token pool (default 1 bytes)",
+                   UintegerValue (1),
+                   MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_tokenPoolSize),
+                   MakeUintegerChecker<uint32_t> ())
+    .AddAttribute ("CreditableThreshold",
+                   "Threshold of flow credit (default 0 bytes)",
+                   UintegerValue (0),
+                   MakeUintegerAccessor (&FdTbfqFfMacScheduler::m_creditableThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+  ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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 <uint16_t, fdtbfqsFlowPerf_t>::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<uint16_t, fdtbfqsFlowPerf_t> (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<uint16_t, fdtbfqsFlowPerf_t> (params.m_rnti, flowStatsUl));
+        }
+      else
+        {
+          NS_LOG_ERROR ("RNTI already exists");
+        }
+    }
+
+  return;
+}
+
+
+void
+FdTbfqFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdTbfqFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+FdTbfqFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdTbfqFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, fdtbfqsFlowPerf_t>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+  std::set <uint16_t> allocatedRnti;   // store UEs which are already assigned RBGs
+  std::set <uint8_t> allocatedRbg;  // store RBGs which are already allocated to UE
+
+  int totalRbg = 0;
+  while (totalRbg < rbgNum)
+    {
+      // select UE with largest metric
+      std::map <uint16_t, fdtbfqsFlowPerf_t>::iterator it;
+      std::map <uint16_t, fdtbfqsFlowPerf_t>::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 <uint16_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t,SbMeasResult_s>::iterator itCqi;
+          itCqi = m_a30CqiRxed.find ((*itMax).first);
+          std::map <uint16_t,uint8_t>::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 <uint8_t>::iterator rbg;
+              rbg = allocatedRbg.find (k);
+	      if (rbg != allocatedRbg.end ())  // RBGs are already allocated to this UE
+	        continue;
+
+              std::vector <uint8_t> 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+          itMap = allocationMap.find ((*itMax).first);
+          uint16_t RbgPerRnti;
+          if (itMap == allocationMap.end ())
+            {
+              // insert new element
+              std::vector <uint16_t> tempMap;
+              tempMap.push_back (rbgIndex);
+              allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*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 <uint8_t> 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 <uint16_t, std::vector <uint16_t> >::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 <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      uint16_t RgbPerRnti = (*itMap).second.size ();
+      std::map <uint16_t,SbMeasResult_s>::iterator itCqi;
+      itCqi = m_a30CqiRxed.find ((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> 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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+
+        }
+      
+      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 <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+FdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdTbfqFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+FdTbfqFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+FdTbfqFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/fdtbfq-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef FDTBFQ_FF_MAC_SCHEDULER_H
+#define FDTBFQ_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <set>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Map of UE statistics (per RNTI basis) in downlink
+  */
+  std::map <uint16_t, fdtbfqsFlowPerf_t> m_flowStatsDl;
+
+  /*
+  * Map of UE statistics (per RNTI basis)
+  */
+  std::map <uint16_t, fdtbfqsFlowPerf_t> m_flowStatsUl;
+
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/pss-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -0,0 +1,1609 @@
+/* -*- 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 <marco.miozzo@cttc.es>        // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/integer.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/string.h>
+#include <ns3/pss-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<PssFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&PssFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+    .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<uint32_t> ())
+  ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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 <uint16_t, pssFlowPerf_t>::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<uint16_t, pssFlowPerf_t> (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<uint16_t, pssFlowPerf_t> (params.m_rnti, flowStatsUl));
+        }
+      else
+        {
+          NS_LOG_ERROR ("RNTI already exists");
+        }
+    }
+
+  return;
+}
+
+
+void
+PssFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+PssFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+PssFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+PssFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+  std::map <uint16_t, pssFlowPerf_t>::iterator it;
+
+  // schedulability check
+  std::map <uint16_t, pssFlowPerf_t> ueSet;
+  for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++)
+    {
+      if( LcActivePerFlow ((*it).first) > 0 )
+        {
+          ueSet.insert(std::pair <uint16_t, pssFlowPerf_t> ((*it).first, (*it).second));
+        }
+    }
+
+  if (ueSet.size() == 0)
+    {
+      // no data in RLC buffer
+      return;
+    }
+
+  // Time Domain scheduler
+  std::vector <std::pair<double, uint16_t> > ueSet1;
+  std::vector <std::pair<double,uint16_t> > 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<double, uint16_t> (metric, (*it).first));
+        }
+      else
+        {
+          // calculate TD PF metric
+          std::map <uint16_t,uint8_t>::iterator itCqi;
+          itCqi = m_p10CqiRxed.find ((*it).first);
+          std::map <uint16_t,uint8_t>::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<double, uint16_t> (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 <uint16_t, pssFlowPerf_t> 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 <std::pair<double, uint16_t> >::iterator itSet;
+     for (itSet = ueSet1.begin (); itSet != ueSet1.end () && nMux != 0; itSet++)
+       {  
+         std::map <uint16_t, pssFlowPerf_t>::iterator itUe;
+         itUe = m_flowStatsDl.find((*itSet).second);
+         tdUeSet.insert(std::pair<uint16_t, pssFlowPerf_t> ( (*itUe).first, (*itUe).second ) );
+         nMux--;
+       }
+   
+     if (nMux == 0)
+       break;
+
+     for (itSet = ueSet2.begin (); itSet != ueSet2.end () && nMux != 0; itSet++)
+       {  
+         std::map <uint16_t, pssFlowPerf_t>::iterator itUe;
+         itUe = m_flowStatsDl.find((*itSet).second);
+         tdUeSet.insert(std::pair<uint16_t, pssFlowPerf_t> ( (*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 <uint16_t,SbMeasResult_s>::iterator itCqi;
+              itCqi = m_a30CqiRxed.find ((*it).first);
+              std::map <uint16_t,uint8_t>::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 <uint8_t> 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<uint16_t, uint8_t> ((*it).first, sum));
+        }// end tdUeSet
+
+      for (int i = 0; i < rbgNum; i++)
+        {
+          std::map <uint16_t, pssFlowPerf_t>::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 <uint16_t,SbMeasResult_s>::iterator itCqi;
+              itCqi = m_a30CqiRxed.find ((*it).first);
+              std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <uint16_t> tempMap;
+              for (int i = 0; i < rbgNum; i++)
+                {
+                  tempMap.push_back (i);
+                }
+              allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*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 <uint16_t, pssFlowPerf_t>::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,SbMeasResult_s>::iterator itCqi;
+              itCqi = m_a30CqiRxed.find ((*it).first);
+              std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <uint16_t> tempMap;
+              for (int i = 0; i < rbgNum; i++)
+                {
+                  tempMap.push_back (i);
+                }
+              allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*itMax).first, tempMap));
+            }
+ 
+        }// end of rbgNum
+
+    } // end of PFsch
+
+
+  // reset TTI stats of users
+  std::map <uint16_t, pssFlowPerf_t>::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 <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      uint16_t rbgPerRnti = (*itMap).second.size ();
+      std::map <uint16_t,SbMeasResult_s>::iterator itCqi;
+      itCqi = m_a30CqiRxed.find ((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, pssFlowPerf_t>::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 <uint16_t, pssFlowPerf_t>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> 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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+        }
+      
+      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 <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+PssFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+PssFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+PssFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+PssFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/pss-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef PSS_FF_MAC_SCHEDULER_H
+#define PSS_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Map of UE statistics (per RNTI basis) in downlink
+  */
+  std::map <uint16_t, pssFlowPerf_t> m_flowStatsDl;
+
+  /*
+  * Map of UE statistics (per RNTI basis)
+  */
+  std::map <uint16_t, pssFlowPerf_t> m_flowStatsUl;
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tdbet-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -0,0 +1,1242 @@
+/* -*- 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 <marco.miozzo@cttc.es>        // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/math.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/tdbet-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<TdBetFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&TdBetFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+  ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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 <uint16_t, tdbetsFlowPerf_t>::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<uint16_t, tdbetsFlowPerf_t> (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<uint16_t, tdbetsFlowPerf_t> (params.m_rnti, flowStatsUl));
+        }
+      else
+        {
+          NS_LOG_ERROR ("RNTI already exists");
+        }
+    }
+
+  return;
+}
+
+void
+TdBetFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdBetFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+TdBetFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdBetFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+  std::map <uint16_t, tdbetsFlowPerf_t>::iterator it;
+  std::map <uint16_t, tdbetsFlowPerf_t>::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 <uint16_t> tempMap;
+      for (int i = 0; i < rbgNum; i++)
+        {
+          tempMap.push_back (i);
+        }
+      allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*itMax).first, tempMap));
+    }
+
+  // reset TTI stats of users
+  std::map <uint16_t, tdbetsFlowPerf_t>::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 <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      std::map <uint16_t,uint8_t>::iterator itCqi;
+      itCqi = m_p10CqiRxed.find ((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, tdbetsFlowPerf_t>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> rbgAllocationMap;
+  std::map <uint16_t, tdbetsFlowPerf_t>::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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+
+        }
+      
+      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;
+        }
+      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));
+      (*itStats).second.lastTtiBytesTransmitted = 0;
+    }
+  m_allocationMaps.insert (std::pair <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+TdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdBetFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdBetFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+TdBetFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tdbet-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef TDBET_FF_MAC_SCHEDULER_H
+#define TDBET_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Map of UE statistics (per RNTI basis) in downlink
+  */
+  std::map <uint16_t, tdbetsFlowPerf_t> m_flowStatsDl;
+
+  /*
+  * Map of UE statistics (per RNTI basis)
+  */
+  std::map <uint16_t, tdbetsFlowPerf_t> m_flowStatsUl;
+
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> m_uesTxMode; // txMode of the UEs
+};
+
+} // namespace ns3
+
+#endif /* TDBET_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tdmt-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -0,0 +1,1218 @@
+/* -*- 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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/math.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/tdmt-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<TdMtFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&TdMtFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+    ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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<uint16_t>::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_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdMtFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+TdMtFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdMtFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+  std::set <uint16_t>::iterator it;
+  std::set <uint16_t>::iterator itMax = m_flowStatsDl.end ();
+  double metricMax = 0.0;
+  for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++)
+    {
+      std::map <uint16_t,uint8_t>::iterator itCqi;
+      itCqi = m_p10CqiRxed.find ((*it));
+      std::map <uint16_t,uint8_t>::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 <uint16_t> tempMap;
+	    for (int i = 0; i < rbgNum; i++)
+	      {
+          tempMap.push_back (i);
+	      }
+      allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*itMax), tempMap));
+    }
+
+  // generate the transmission opportunities by grouping the RBGs of the same RNTI and
+  // creating the correspondent DCIs
+  FfMacSchedSapUser::SchedDlConfigIndParameters ret;
+  std::map <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      std::map <uint16_t,uint8_t>::iterator itCqi;
+	    itCqi = m_p10CqiRxed.find((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> 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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+
+        }
+      
+      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 <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+TdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdMtFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdMtFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+TdMtFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tdmt-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef TDMT_FF_MAC_SCHEDULER_H
+#define TDMT_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <set>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Set of UE's RNTI in downlink
+  */
+  std::set <uint16_t> m_flowStatsDl;
+
+  /*
+  * Set of UE's RNTI in uplink
+  */
+  std::set <uint16_t> m_flowStatsUl;
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> m_uesTxMode; // txMode of the UEs
+};
+
+} // namespace ns3
+
+#endif /* TDMT_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tdtbfq-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>        // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/integer.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/tdtbfq-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<TdTbfqFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+    .AddAttribute ("DebtLimit",
+                   "Flow debt limit (default -625000 bytes)",
+                   IntegerValue (-625000),
+                   MakeIntegerAccessor (&TdTbfqFfMacScheduler::m_debtLimit),
+                   MakeIntegerChecker<int> ())
+    .AddAttribute ("CreditLimit",
+                   "Flow credit limit (default 625000 bytes)",
+                   UintegerValue (625000),
+                   MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_creditLimit),
+                   MakeUintegerChecker<uint32_t> ())
+    .AddAttribute ("TokenPoolSize",
+                   "The maximum value of flow token pool (default 1 bytes)",
+                   UintegerValue (1),
+                   MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_tokenPoolSize),
+                   MakeUintegerChecker<uint32_t> ())
+    .AddAttribute ("CreditableThreshold",
+                   "Threshold of flow credit (default 0 bytes)",
+                   UintegerValue (0),
+                   MakeUintegerAccessor (&TdTbfqFfMacScheduler::m_creditableThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+  ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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 <uint16_t, tdtbfqsFlowPerf_t>::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<uint16_t, tdtbfqsFlowPerf_t> (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<uint16_t, tdtbfqsFlowPerf_t> (params.m_rnti, flowStatsUl));
+        }
+      else
+        {
+          NS_LOG_ERROR ("RNTI already exists");
+        }
+    }
+
+  return;
+}
+
+
+void
+TdTbfqFfMacScheduler::DoCschedLcReleaseReq (const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdTbfqFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+TdTbfqFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdTbfqFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, tdtbfqsFlowPerf_t>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+
+  // select UE with largest metric
+  std::map <uint16_t, tdtbfqsFlowPerf_t>::iterator it;
+  std::map <uint16_t, tdtbfqsFlowPerf_t>::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 <uint16_t> tempMap;
+      for (int i = 0; i < rbgNum; i++)
+        {
+          tempMap.push_back (i);
+        }
+      allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*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 <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      uint16_t RgbPerRnti = (*itMap).second.size ();
+      std::map <uint16_t,SbMeasResult_s>::iterator itCqi;
+      itCqi = m_a30CqiRxed.find ((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, tdtbfqsFlowPerf_t>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> 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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+
+        }
+      
+      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 <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+TdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdTbfqFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TdTbfqFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+TdTbfqFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tdtbfq-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef TDTBFQ_FF_MAC_SCHEDULER_H
+#define TDTBFQ_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <set>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Map of UE statistics (per RNTI basis) in downlink
+  */
+  std::map <uint16_t, tdtbfqsFlowPerf_t> m_flowStatsDl;
+
+  /*
+  * Map of UE statistics (per RNTI basis)
+  */
+  std::map <uint16_t, tdtbfqsFlowPerf_t> m_flowStatsUl;
+
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tta-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -0,0 +1,1294 @@
+/* -*- 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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#include <ns3/log.h>
+#include <ns3/pointer.h>
+#include <ns3/math.h>
+
+#include <ns3/simulator.h>
+#include <ns3/lte-amc.h>
+#include <ns3/tta-ff-mac-scheduler.h>
+#include <ns3/lte-vendor-specific-parameters.h>
+
+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 <LteAmc> ();
+  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<FfMacScheduler> ()
+    .AddConstructor<TtaFfMacScheduler> ()
+    .AddAttribute ("CqiTimerThreshold",
+                   "The number of TTIs a CQI is valid (default 1000 - 1 sec.)",
+                   UintegerValue (1000),
+                   MakeUintegerAccessor (&TtaFfMacScheduler::m_cqiTimersThreshold),
+                   MakeUintegerChecker<uint32_t> ())
+    ;
+  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 <uint16_t,uint8_t>::iterator it = m_uesTxMode.find (params.m_rnti);
+  if (it == m_uesTxMode.end ())
+    {
+      m_uesTxMode.insert (std::pair <uint16_t, double> (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<uint16_t>::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_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TtaFfMacScheduler::DoCschedUeReleaseReq (const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> (flow, params));
+    }
+  else
+    {
+      (*it).second = params;
+    }
+
+  return;
+}
+
+void
+TtaFfMacScheduler::DoSchedDlPagingBufferReq (const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TtaFfMacScheduler::DoSchedDlMacBufferReq (const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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 <uint16_t, std::vector <uint16_t> > allocationMap;
+  for (int i = 0; i < rbgNum; i++)
+    {
+      std::set <uint16_t>::iterator it;
+      std::set <uint16_t>::iterator itMax = m_flowStatsDl.end ();
+      double metricMax = 0.0;
+      for (it = m_flowStatsDl.begin (); it != m_flowStatsDl.end (); it++)
+        {
+          std::map <uint16_t,SbMeasResult_s>::iterator itCqi;
+          itCqi = m_a30CqiRxed.find ((*it));
+          std::map <uint16_t,uint8_t>::iterator itWbCqi;
+          itWbCqi = m_p10CqiRxed.find ((*it));
+          std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+          itMap = allocationMap.find ((*itMax));
+          if (itMap == allocationMap.end ())
+            {
+              // insert new element
+              std::vector <uint16_t> tempMap;
+              tempMap.push_back (i);
+              allocationMap.insert (std::pair <uint16_t, std::vector <uint16_t> > ((*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 <uint16_t, std::vector <uint16_t> >::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 <struct RlcPduListElement_s> newRlcPduLe;
+      newDci.m_rnti = (*itMap).first;
+
+      uint16_t lcActives = LcActivePerFlow ((*itMap).first);
+      uint16_t rbgPerRnti = (*itMap).second.size ();
+      std::map <uint16_t,SbMeasResult_s>::iterator itCqi;
+      itCqi = m_a30CqiRxed.find ((*itMap).first);
+      std::map <uint16_t,uint8_t>::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 <uint8_t> 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 <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::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_FATAL_ERROR ("unimplemented");
+  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 <uint16_t,uint8_t>::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<uint16_t, uint8_t > (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<uint16_t, uint32_t > (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 <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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<uint16_t, SbMeasResult_s > (rnti, params.m_cqiList.at (i).m_sbMeasResult) );
+              m_a30CqiTimers.insert ( std::pair<uint16_t, uint32_t > (rnti, m_cqiTimersThreshold));
+            }
+          else
+            {
+              // update the CQI value and refresh correspondent timer
+              (*it).second = params.m_cqiList.at (i).m_sbMeasResult;
+              std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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 <uint16_t> 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 <uint16_t, std::vector <double> >::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
+        }
+      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++)
+            {
+              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);
+
+        }
+      
+      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 <uint16_t, std::vector <uint16_t> > (params.m_sfnSf, rbgAllocationMap));
+  m_schedSapUser->SchedUlConfigInd (ret);
+  return;
+}
+
+void
+TtaFfMacScheduler::DoSchedUlNoiseInterferenceReq (const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TtaFfMacScheduler::DoSchedUlSrInfoReq (const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+{
+  NS_FATAL_ERROR ("unimplemented");
+  return;
+}
+
+void
+TtaFfMacScheduler::DoSchedUlMacCtrlInfoReq (const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+{
+  NS_LOG_FUNCTION (this);
+
+  std::map <uint16_t,uint32_t>::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<uint16_t, uint32_t > (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);
+  // 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 <uint16_t, std::vector <uint16_t> >::iterator itMap;
+        std::map <uint16_t, std::vector <double> >::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));
+            itCqi = m_ueCqi.find ((*itMap).second.at (i));
+            if (itCqi == m_ueCqi.end ())
+              {
+                // create a new entry
+                std::vector <double> 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 <uint16_t, std::vector <double> > ((*itMap).second.at (i), newCqi));
+                // generate correspondent timer
+                m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > ((*itMap).second.at (i), m_cqiTimersThreshold));
+              }
+            else
+              {
+                // update the value
+                (*itCqi).second.at (i) = sinr;
+                // update correspondent timer
+                std::map <uint16_t, uint32_t>::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<SrsCqiRntiVsp> vsp = DynamicCast<SrsCqiRntiVsp> (params.m_vendorSpecificList.at (i).m_value);
+                rnti = vsp->GetRnti ();
+              }
+          }
+        std::map <uint16_t, std::vector <double> >::iterator itCqi;
+        itCqi = m_ueCqi.find (rnti);
+        if (itCqi == m_ueCqi.end ())
+          {
+            // create a new entry
+            std::vector <double> 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 <uint16_t, std::vector <double> > (rnti, newCqi));
+            // generate correspondent timer
+            m_ueCqiTimers.insert (std::pair <uint16_t, uint32_t > (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 <uint16_t, uint32_t>::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 <uint16_t,uint32_t>::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 <uint16_t,uint8_t>::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 <uint16_t,uint32_t>::iterator temp = itP10;
+          itP10++;
+          m_p10CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itP10).second--;
+          itP10++;
+        }
+    }
+  
+  // refresh DL CQI A30 Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t,SbMeasResult_s>::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 <uint16_t,uint32_t>::iterator temp = itA30;
+          itA30++;
+          m_a30CqiTimers.erase (temp);
+        }
+      else
+        {
+          (*itA30).second--;
+          itA30++;
+        }
+    }
+    
+    return;
+}
+
+
+void
+TtaFfMacScheduler::RefreshUlCqiMaps(void)
+{
+  // refresh UL CQI  Map
+  std::map <uint16_t,uint32_t>::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 <uint16_t, std::vector <double> >::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 <uint16_t,uint32_t>::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<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>::iterator it;
+  LteFlowId_t flow (rnti, lcid);
+  it = m_rlcBufferReq.find (flow);
+  if (it!=m_rlcBufferReq.end ())
+    {
+      // 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 <uint16_t,uint32_t>::iterator it = m_ceBsrRxed.find (rnti);
+  if (it!=m_ceBsrRxed.end ())
+    {
+      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);
+}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/model/tta-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>  // original version
+ * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
+ */
+
+#ifndef TTA_FF_MAC_SCHEDULER_H
+#define TTA_FF_MAC_SCHEDULER_H
+
+#include <ns3/lte-common.h>
+#include <ns3/ff-mac-csched-sap.h>
+#include <ns3/ff-mac-sched-sap.h>
+#include <ns3/ff-mac-scheduler.h>
+#include <vector>
+#include <map>
+#include <set>
+#include <ns3/nstime.h>
+#include <ns3/lte-amc.h>
+
+
+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<LteAmc> m_amc;
+
+  /*
+   * Vectors of UE's LC info
+  */
+  std::map <LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters> m_rlcBufferReq;
+
+
+  /*
+  * Set of UE's RNTI in downlink
+  */
+  std::set <uint16_t> m_flowStatsDl;
+
+  /*
+  * Set of UE's RNTI in uplink
+  */
+  std::set <uint16_t> m_flowStatsUl;
+
+  /*
+  * Map of UE's DL CQI P01 received
+  */
+  std::map <uint16_t,uint8_t> m_p10CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI P01 received
+  */
+  std::map <uint16_t,uint32_t> m_p10CqiTimers;
+
+  /*
+  * Map of UE's DL CQI A30 received
+  */
+  std::map <uint16_t,SbMeasResult_s> m_a30CqiRxed;
+  /*
+  * Map of UE's timers on DL CQI A30 received
+  */
+  std::map <uint16_t,uint32_t> m_a30CqiTimers;
+
+  /*
+  * Map of previous allocated UE per RBG
+  * (used to retrieve info from UL-CQI)
+  */
+  std::map <uint16_t, std::vector <uint16_t> > m_allocationMaps;
+
+  /*
+  * Map of UEs' UL-CQI per RBG
+  */
+  std::map <uint16_t, std::vector <double> > m_ueCqi;
+  /*
+  * Map of UEs' timers on UL-CQI per RBG
+  */
+  std::map <uint16_t, uint32_t> m_ueCqiTimers;
+
+  /*
+  * Map of UE's buffer status reports received
+  */
+  std::map <uint16_t,uint32_t> 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 <uint16_t,uint8_t> m_uesTxMode; // txMode of the UEs
+};
+
+} // namespace ns3
+
+#endif /* TTA_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+#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<uint16_t> 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<uint32_t> estAchievableRateDl;
+  estAchievableRateDl.push_back (2196000);
+  estAchievableRateDl.push_back (1383000);
+  estAchievableRateDl.push_back (903000);
+  estAchievableRateDl.push_back (597000);
+  std::vector<uint32_t> 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> lteHelper = CreateObject<LteHelper> ();
+  
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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<uint16_t> dist)
+{
+  std::ostringstream oss;
+  oss << "distances (m) = [ " ;
+  for (std::vector<uint16_t>::iterator it = dist.begin (); it != dist.end (); ++it)
+    {
+      oss << *it << " ";
+    }
+  oss << "]";
+  return oss.str ();
+}
+
+
+LenaFdBetFfMacSchedulerTestCase2::LenaFdBetFfMacSchedulerTestCase2 (std::vector<uint16_t> dist, std::vector<uint32_t> estAchievableRateDl, std::vector<uint32_t> 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> lteHelper = CreateObject<LteHelper> ();
+  
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<RadioBearerStatsCalculator> rlcStats = lteHelper->GetRlcStats ();
+  rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime)));
+
+  Simulator::Run ();
+
+  NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s)");
+  std::vector <uint64_t> 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<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-fdbet-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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<uint16_t> dist, std::vector<uint32_t> m_achievableRateDl, std::vector<uint32_t> estThrFdBetUl);
+  virtual ~LenaFdBetFfMacSchedulerTestCase2 ();
+
+private:
+  static std::string BuildNameString (uint16_t nUser, std::vector<uint16_t> dist);
+  virtual void DoRun (void);
+  uint16_t m_nUser;
+  std::vector<uint16_t> m_dist;
+  std::vector<uint32_t> m_achievableRateDl;
+  std::vector<uint32_t> m_estThrFdBetUl;
+};
+
+
+
+
+class LenaTestFdBetFfMacSchedulerSuite : public TestSuite
+{
+public:
+  LenaTestFdBetFfMacSchedulerSuite ();
+};
+
+
+
+
+} // namespace ns3
+
+
+#endif /* LENA_TEST_TDBET_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include "lte-test-fdmt-ff-mac-scheduler.h"
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+
+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> lteHelper = CreateObject<LteHelper> ();
+  
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 ();
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-fdmt-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+#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<uint16_t> 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<uint16_t> packetSize1;
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  std::vector<uint32_t> 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<uint16_t> 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<uint16_t> packetSize2;
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  std::vector<uint32_t> 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<uint16_t> 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<uint16_t> packetSize3;
+  packetSize3.push_back (100);
+  packetSize3.push_back (200);
+  packetSize3.push_back (300);
+  std::vector<uint32_t> 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> lteHelper = CreateObject<LteHelper> ();
+  Ptr<EpcHelper>  epcHelper = CreateObject<EpcHelper> ();
+  lteHelper->SetEpcHelper (epcHelper);
+
+  Ptr<Node> pgw = epcHelper->GetPgwNode ();
+
+  // Create a single RemoteHost
+  NodeContainer remoteHostContainer;
+  remoteHostContainer.Create (1);
+  Ptr<Node> 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<Ipv4StaticRouting> remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<Node> ueNode = ueNodes.Get (u);
+      // Set the default gateway for the UE
+      Ptr<Ipv4StaticRouting> ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject<Ipv4> ());
+      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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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<uint16_t> dist)
+{
+  std::ostringstream oss;
+  oss << "distances (m) = [ " ;
+  for (std::vector<uint16_t>::iterator it = dist.begin (); it != dist.end (); ++it)
+    {
+      oss << *it << " ";
+    }
+  oss << "]";
+  return oss.str ();
+}
+
+
+LenaFdTbfqFfMacSchedulerTestCase2::LenaFdTbfqFfMacSchedulerTestCase2 (std::vector<uint16_t> dist, std::vector<uint32_t> estThrFdTbfqDl, std::vector<uint16_t> 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> lteHelper = CreateObject<LteHelper> ();
+  Ptr<EpcHelper>  epcHelper = CreateObject<EpcHelper> ();
+  lteHelper->SetEpcHelper (epcHelper);
+
+  Ptr<Node> pgw = epcHelper->GetPgwNode ();
+
+  // Create a single RemoteHost
+  NodeContainer remoteHostContainer;
+  remoteHostContainer.Create (1);
+  Ptr<Node> 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<Ipv4StaticRouting> remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<Node> ueNode = ueNodes.Get (u);
+      // Set the default gateway for the UE
+      Ptr<Ipv4StaticRouting> ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject<Ipv4> ());
+      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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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<uint16_t> dist, std::vector<uint32_t> estThrFdTbfqDl, std::vector<uint16_t> packetSize, uint16_t interval);
+  virtual ~LenaFdTbfqFfMacSchedulerTestCase2 ();
+
+private:
+  static std::string BuildNameString (uint16_t nUser, std::vector<uint16_t> dist);
+  virtual void DoRun (void);
+  uint16_t m_nUser;
+  std::vector<uint16_t> m_dist;
+  std::vector<uint16_t> m_packetSize;  // byte
+  uint16_t m_interval;    // ms
+  std::vector<uint32_t> m_estThrFdTbfqDl;
+};
+
+
+class LenaTestFdTbfqFfMacSchedulerSuite : public TestSuite
+{
+public:
+  LenaTestFdTbfqFfMacSchedulerSuite ();
+};
+
+
+
+
+} // namespace ns3
+
+
+#endif /* LENA_TEST_FDTBFQ_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-pss-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+#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<uint16_t> 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<uint16_t> packetSize1;
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  std::vector<uint32_t> 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<uint16_t> 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<uint16_t> packetSize2;
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  std::vector<uint32_t> 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<uint16_t> 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<uint16_t> packetSize3;
+  packetSize3.push_back (100);
+  packetSize3.push_back (200);
+  packetSize3.push_back (300);
+  packetSize3.push_back (400);
+  packetSize3.push_back (500);
+  std::vector<uint32_t> 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<uint16_t> 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<uint16_t> packetSize4;
+  packetSize4.push_back (100);
+  packetSize4.push_back (200);
+  packetSize4.push_back (300);
+  std::vector<uint32_t> 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> lteHelper = CreateObject<LteHelper> ();
+  Ptr<EpcHelper>  epcHelper = CreateObject<EpcHelper> ();
+  lteHelper->SetEpcHelper (epcHelper);
+
+  Ptr<Node> pgw = epcHelper->GetPgwNode ();
+
+  // Create a single RemoteHost
+  NodeContainer remoteHostContainer;
+  remoteHostContainer.Create (1);
+  Ptr<Node> 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<Ipv4StaticRouting> remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<Node> ueNode = ueNodes.Get (u);
+      // Set the default gateway for the UE
+      Ptr<Ipv4StaticRouting> ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject<Ipv4> ());
+      ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1);
+
+      // Activate an EPS bearer
+      Ptr<NetDevice> 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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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<uint16_t> dist)
+{
+  std::ostringstream oss;
+  oss << "distances (m) = [ " ;
+  for (std::vector<uint16_t>::iterator it = dist.begin (); it != dist.end (); ++it)
+    {
+      oss << *it << " ";
+    }
+  oss << "]";
+  return oss.str ();
+}
+
+
+LenaPssFfMacSchedulerTestCase2::LenaPssFfMacSchedulerTestCase2 (std::vector<uint16_t> dist, std::vector<uint32_t> estThrPssDl, std::vector<uint16_t> 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> lteHelper = CreateObject<LteHelper> ();
+  Ptr<EpcHelper>  epcHelper = CreateObject<EpcHelper> ();
+  lteHelper->SetEpcHelper (epcHelper);
+
+  Ptr<Node> pgw = epcHelper->GetPgwNode ();
+
+  // Create a single RemoteHost
+  NodeContainer remoteHostContainer;
+  remoteHostContainer.Create (1);
+  Ptr<Node> 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<Ipv4StaticRouting> remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<Node> ueNode = ueNodes.Get (u);
+      // Set the default gateway for the UE
+      Ptr<Ipv4StaticRouting> ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject<Ipv4> ());
+      ueStaticRouting->SetDefaultRoute (epcHelper->GetUeDefaultGatewayAddress (), 1);
+
+      // Activate an EPS bearer
+      Ptr<NetDevice> 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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-pss-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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<uint16_t> dist, std::vector<uint32_t> estThrPssDl, std::vector<uint16_t> packetSize, uint16_t interval);
+  virtual ~LenaPssFfMacSchedulerTestCase2 ();
+
+private:
+  static std::string BuildNameString (uint16_t nUser, std::vector<uint16_t> dist);
+  virtual void DoRun (void);
+  uint16_t m_nUser;
+  std::vector<uint16_t> m_dist;
+  std::vector<uint16_t> m_packetSize;  // byte
+  uint16_t m_interval;    // ms
+  std::vector<uint32_t> m_estThrPssDl;
+};
+
+
+class LenaTestPssFfMacSchedulerSuite : public TestSuite
+{
+public:
+  LenaTestPssFfMacSchedulerSuite ();
+};
+
+
+
+
+} // namespace ns3
+
+
+#endif /* LENA_TEST_PSS_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+#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<uint16_t> 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<uint32_t> estAchievableRateDl;
+  estAchievableRateDl.push_back (2196000);
+  estAchievableRateDl.push_back (1383000);
+  estAchievableRateDl.push_back (903000);
+  estAchievableRateDl.push_back (597000);
+  estAchievableRateDl.push_back (309000);
+  std::vector<uint32_t> 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> lteHelper = CreateObject<LteHelper> ();
+  
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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<uint16_t> dist)
+{
+  std::ostringstream oss;
+  oss << "distances (m) = [ " ;
+  for (std::vector<uint16_t>::iterator it = dist.begin (); it != dist.end (); ++it)
+    {
+      oss << *it << " ";
+    }
+  oss << "]";
+  return oss.str ();
+}
+
+
+LenaTdBetFfMacSchedulerTestCase2::LenaTdBetFfMacSchedulerTestCase2 (std::vector<uint16_t> dist, std::vector<uint32_t> estAchievableRateDl, std::vector<uint32_t> 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> lteHelper = CreateObject<LteHelper> ();
+  
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<RadioBearerStatsCalculator> rlcStats = lteHelper->GetRlcStats ();
+  rlcStats->SetAttribute ("EpochDuration", TimeValue (Seconds (simulationTime)));
+
+  Simulator::Run ();
+
+  NS_LOG_INFO ("DL - Test with " << m_nUser << " user(s)");
+  std::vector <uint64_t> 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<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tdbet-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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<uint16_t> dist, std::vector<uint32_t> m_achievableRateDl, std::vector<uint32_t> estThrTdBetUl);
+  virtual ~LenaTdBetFfMacSchedulerTestCase2 ();
+
+private:
+  static std::string BuildNameString (uint16_t nUser, std::vector<uint16_t> dist);
+  virtual void DoRun (void);
+  uint16_t m_nUser;
+  std::vector<uint16_t> m_dist;
+  std::vector<uint32_t> m_achievableRateDl;
+  std::vector<uint32_t> m_estThrTdBetUl;
+};
+
+
+
+
+class LenaTestTdBetFfMacSchedulerSuite : public TestSuite
+{
+public:
+  LenaTestTdBetFfMacSchedulerSuite ();
+};
+
+
+
+
+} // namespace ns3
+
+
+#endif /* LENA_TEST_TDBET_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include "lte-test-tdmt-ff-mac-scheduler.h"
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+
+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> lteHelper = CreateObject<LteHelper> ();
+  
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 ();
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tdmt-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>,
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+#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<uint16_t> 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<uint16_t> packetSize1;
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  packetSize1.push_back (100);
+  std::vector<uint32_t> 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<uint16_t> 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<uint16_t> packetSize2;
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  packetSize2.push_back (200);
+  std::vector<uint32_t> 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<uint16_t> 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<uint16_t> packetSize3;
+  packetSize3.push_back (100);
+  packetSize3.push_back (200);
+  packetSize3.push_back (300);
+  std::vector<uint32_t> 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> lteHelper = CreateObject<LteHelper> ();
+  Ptr<EpcHelper>  epcHelper = CreateObject<EpcHelper> ();
+  lteHelper->SetEpcHelper (epcHelper);
+
+  Ptr<Node> pgw = epcHelper->GetPgwNode ();
+
+  // Create a single RemoteHost
+  NodeContainer remoteHostContainer;
+  remoteHostContainer.Create (1);
+  Ptr<Node> 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<Ipv4StaticRouting> remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<Node> ueNode = ueNodes.Get (u);
+      // Set the default gateway for the UE
+      Ptr<Ipv4StaticRouting> ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject<Ipv4> ());
+      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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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<uint16_t> dist)
+{
+  std::ostringstream oss;
+  oss << "distances (m) = [ " ;
+  for (std::vector<uint16_t>::iterator it = dist.begin (); it != dist.end (); ++it)
+    {
+      oss << *it << " ";
+    }
+  oss << "]";
+  return oss.str ();
+}
+
+
+LenaTdTbfqFfMacSchedulerTestCase2::LenaTdTbfqFfMacSchedulerTestCase2 (std::vector<uint16_t> dist, std::vector<uint32_t> estThrTdTbfqDl, std::vector<uint16_t> 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> lteHelper = CreateObject<LteHelper> ();
+  Ptr<EpcHelper>  epcHelper = CreateObject<EpcHelper> ();
+  lteHelper->SetEpcHelper (epcHelper);
+
+  Ptr<Node> pgw = epcHelper->GetPgwNode ();
+
+  // Create a single RemoteHost
+  NodeContainer remoteHostContainer;
+  remoteHostContainer.Create (1);
+  Ptr<Node> 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<Ipv4StaticRouting> remoteHostStaticRouting = ipv4RoutingHelper.GetStaticRouting (remoteHost->GetObject<Ipv4> ());
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist.at (i), 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<Node> ueNode = ueNodes.Get (u);
+      // Set the default gateway for the UE
+      Ptr<Ipv4StaticRouting> ueStaticRouting = ipv4RoutingHelper.GetStaticRouting (ueNode->GetObject<Ipv4> ());
+      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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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<uint16_t> dist, std::vector<uint32_t> estThrTdTbfqDl, std::vector<uint16_t> packetSize, uint16_t interval);
+  virtual ~LenaTdTbfqFfMacSchedulerTestCase2 ();
+
+private:
+  static std::string BuildNameString (uint16_t nUser, std::vector<uint16_t> dist);
+  virtual void DoRun (void);
+  uint16_t m_nUser;
+  std::vector<uint16_t> m_dist;
+  std::vector<uint16_t> m_packetSize;  // byte
+  uint16_t m_interval;    // ms
+  std::vector<uint32_t> m_estThrTdTbfqDl;
+};
+
+
+class LenaTestTdTbfqFfMacSchedulerSuite : public TestSuite
+{
+public:
+  LenaTestTdTbfqFfMacSchedulerSuite ();
+};
+
+
+
+
+} // namespace ns3
+
+
+#endif /* LENA_TEST_TDTBFQ_FF_MAC_SCHEDULER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tta-ff-mac-scheduler.cc	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <ns3/object.h>
+#include <ns3/spectrum-interference.h>
+#include <ns3/spectrum-error-model.h>
+#include <ns3/log.h>
+#include <ns3/test.h>
+#include <ns3/simulator.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+#include "ns3/radio-bearer-stats-calculator.h"
+#include <ns3/constant-position-mobility-model.h>
+#include "lte-test-tta-ff-mac-scheduler.h"
+#include <ns3/eps-bearer.h>
+#include <ns3/node-container.h>
+#include <ns3/mobility-helper.h>
+#include <ns3/net-device-container.h>
+#include <ns3/lte-ue-net-device.h>
+#include <ns3/lte-enb-net-device.h>
+#include <ns3/lte-ue-rrc.h>
+#include <ns3/lte-helper.h>
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include <ns3/lte-enb-phy.h>
+#include <ns3/lte-ue-phy.h>
+#include <ns3/boolean.h>
+#include <ns3/enum.h>
+
+
+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> lteHelper = CreateObject<LteHelper> ();
+  
+  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<LteEnbNetDevice> lteEnbDev = enbDevs.Get (0)->GetObject<LteEnbNetDevice> ();
+  Ptr<LteEnbPhy> 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<ConstantPositionMobilityModel> mm = ueNodes.Get (i)->GetObject<ConstantPositionMobilityModel> ();
+      mm->SetPosition (Vector (m_dist, 0.0, 0.0));
+      Ptr<LteUeNetDevice> lteUeDev = ueDevs.Get (i)->GetObject<LteUeNetDevice> ();
+      Ptr<LteUePhy> 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<RadioBearerStatsCalculator> 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 <uint64_t> dlDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 <uint64_t> ulDataRxed;
+  for (int i = 0; i < m_nUser; i++)
+    {
+      // get the imsi
+      uint64_t imsi = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->GetImsi ();
+      // get the lcId
+      uint8_t lcId = ueDevs.Get (i)->GetObject<LteUeNetDevice> ()->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 ();
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lte/test/lte-test-tta-ff-mac-scheduler.h	Sat Dec 01 11:40:51 2012 +0100
@@ -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 <marco.miozzo@cttc.es>,
+ *         Nicola Baldo <nbaldo@cttc.es>
+ *         Dizhi Zhou <dizhi.zhou@gmail.com>
+ */
+
+#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 */
--- a/src/lte/wscript	Wed Nov 28 21:32:28 2012 +0100
+++ b/src/lte/wscript	Sat Dec 01 11:40:51 2012 +0100
@@ -58,6 +58,14 @@
         '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 @@
         '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 @@
         '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 @@
         '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']):