tracers+docs: Correcting compilation and installation of trace helpers

Also in this commit an example on how to use trace helpers, including
how to build graphs using R.
diff --git a/docs/source/_static/root-5sec-counts.png b/docs/source/_static/root-5sec-counts.png
new file mode 100644
index 0000000..5257f1d
--- /dev/null
+++ b/docs/source/_static/root-5sec-counts.png
Binary files differ
diff --git a/docs/source/_static/root-rates.png b/docs/source/_static/root-rates.png
new file mode 100644
index 0000000..bc0df92
--- /dev/null
+++ b/docs/source/_static/root-rates.png
Binary files differ
diff --git a/docs/source/examples.rst b/docs/source/examples.rst
index ddb4b26..ff36020 100644
--- a/docs/source/examples.rst
+++ b/docs/source/examples.rst
@@ -255,3 +255,77 @@
 You can also run using visualizer module to verify that both bottleneck links are utilized::
 
         ./waf --run=ndn-congestion-alt-topo-plugin --visualize
+
+.. _trace example:
+
+Example of using trace helpers
+------------------------------
+
+This example (``ndn-tree-tracers.cc``) demonstrates basic usage of :ref:`trace classes`.   
+
+In this scenario we will use a tree-like topology, where consumers are installed on leaf nodes and producer is in the root of the tree:
+
+.. aafig::
+    :aspect: 60
+    :scale: 120
+                                                 
+     /--------\    /--------\    /--------\    /--------\
+     |"leaf-1"|    |"leaf-2"|    |"leaf-3"|    |"leaf-4"|
+     \--------/    \--------/    \--------/    \--------/
+           ^          ^                ^           ^	
+           |          |                |           |
+      	    \        /                  \         / 
+             \      /  			 \  	 /    10Mbps / 1ms
+              \    /  			  \ 	/
+               |  |  			   |   | 
+      	       v  v                        v   v     
+	    /-------\                    /-------\
+	    |"rtr-1"|                    |"rtr-2"|
+            \-------/                    \-------/
+                  ^                        ^                      
+		  |	 		   |
+		   \			  /  10 Mpbs / 1ms 
+		    +--------\  /--------+ 
+			     |  |      
+                             v  v
+			  /--------\
+			  | "root" |                                   
+                          \--------/
+
+The corresponding topology file (``topo-tree.txt``):
+
+.. literalinclude:: ../../examples/topologies/topo-tree.txt
+    :language: bash
+    :linenos:
+    :lines: 1-2,27-
+
+Example simulation (``ndn-tree-tracers.cc``) scenario that utilizes trace helpers:
+
+.. literalinclude:: ../../examples/ndn-tree-tracers.cc
+    :language: c++
+    :linenos:
+    :lines: 21-34,67-
+    :emphasize-lines: 7-11,63-67
+
+
+To run this scenario, use the following command::
+
+        ./waf --run=ndn-tree-tracers
+
+The successful run will create ``rate-trace.txt`` and ``aggregate-trace.txt`` files in the current directly, which can be analyzed manually or used as input to some graph/stats packages.
+
+For example, the following `R script <http://www.r-project.org/>`_ will build a number of nice graphs:
+
+.. image:: _static/root-rates.png
+   :alt: Interest/Data packet rates at the root node
+   :align: right
+
+.. image:: _static/root-5sec-counts.png
+   :alt: Interest/Data packet counts at the root node in 5-second intervals
+   :align: right
+
+.. literalinclude:: ../../examples/graphs/rate-graph.R
+    :language: r
+    :linenos:
+
+For more information about R and ggplot2, please refer to `R language manual <http://cran.r-project.org/manuals.html>`_, `ggplot2 module manual <http://docs.ggplot2.org/current/>`_.
diff --git a/docs/source/metric.rst b/docs/source/metric.rst
index cce98d9..aeff236 100644
--- a/docs/source/metric.rst
+++ b/docs/source/metric.rst
@@ -4,6 +4,7 @@
 
 To obtain simulation results, you would need to connect to one or more `trace sources <doxygen/group___trace_source_list.html>`_ provided by ndnSIM classes.
 
+.. _trace classes:
 
 Tracer classes
 --------------
@@ -20,12 +21,19 @@
 
     .. code-block:: c++
 
+        // necessary includes
+	#include <ns3/ndnSIM/utils/tracers/ndn-l3-aggregate-tracer.h>
+
+	...        
+
         // the following should be put just before calling Simulator::Run in the scenario
 
-        boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<L3AggregateTracer> > >
-            tracers = L3RateTracer::InstallAll ("aggregate-trace.txt", Seconds (1.0));
+        boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<ndn::L3AggregateTracer> > >
+          aggTracers = ndn::L3AggregateTracer::InstallAll ("aggregate-trace.txt", Seconds (1.0));
         
         Simulator::Run ();
+        
+        ...
 
 
 - :ndnsim:`ndn::L3RateTracer`
@@ -36,12 +44,19 @@
 
     .. code-block:: c++
 
+        // necessary includes
+	#include <ns3/ndnSIM/utils/tracers/ndn-l3-rate-tracer.h>
+
+	...        
+
         // the following should be put just before calling Simulator::Run in the scenario
 
-        boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<L3RateTracer> > >
-            tracers = L3RateTracer::InstallAll ("rate-trace.txt", Seconds (1.0));
+        boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<ndn::L3RateTracer> > >
+          rateTracers = ndn::L3RateTracer::InstallAll ("rate-trace.txt", Seconds (1.0));
         
         Simulator::Run ();
+        
+        ...
 
 
 .. note::
@@ -50,3 +65,7 @@
     Eventually, we will port most of them to the current code, but it is not our main priority at the moment and would really appreciate help with writing new tracers and porting the old ones. 
 
 
+Example
++++++++
+
+Please refer to the :ref:`this example <trace example>`.
diff --git a/examples/graphs/rate-graph.R b/examples/graphs/rate-graph.R
new file mode 100755
index 0000000..6d63298
--- /dev/null
+++ b/examples/graphs/rate-graph.R
@@ -0,0 +1,88 @@
+# Copyright (c) 2012  Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+
+# install.packages ('ggplot2')  
+library (ggplot2)
+# install.packages ('scales')  
+library (scales)
+
+# install.packages ('doBy')
+library (doBy)
+
+#########################
+# Rate trace processing #
+#########################
+data = read.table ("rate-trace.txt", header=T)
+data$Node = factor (data$Node)
+data$FaceId <- factor(data$FaceId)
+data$Kilobits <- data$Kilobytes * 8
+data$Type = factor (data$Type)
+
+# exlude irrelevant types
+data = subset (data, Type %in% c("InInterests", "OutInterests", "InData", "OutData"))
+
+# combine stats from all faces
+data.combined = summaryBy (. ~ Time + Node + Type, data=data, FUN=sum)
+  
+data.root = subset (data.combined, Node == "root")
+data.leaves = subset (data.combined, Node %in% c("leaf-1", "leaf-2", "leaf-3", "leaf-4"))
+
+# graph rates on all nodes in Kilobits
+g.all <- ggplot (data.combined) +
+  geom_point (aes (x=Time, y=Kilobits.sum, color=Type), size=1) +
+  ylab ("Rate [Kbits/s]") +
+  facet_wrap (~ Node)
+
+print (g.all)
+  
+# graph rates on the root nodes in Packets
+g.root <- ggplot (data.root) +
+  geom_point (aes (x=Time, y=Kilobits.sum, color=Type), size=2) +
+  geom_line (aes (x=Time, y=Kilobits.sum, color=Type), size=0.5) +
+  ylab ("Rate [Kbits/s]")
+
+print (g.root)
+
+png ("root-rates.png", width=500, height=250)
+print (g.root)
+dev.off ()
+
+###############################
+# Aggreagate trace processing #
+###############################
+
+data = read.table ("aggregate-trace.txt", header=T)
+data$Node = factor (data$Node)
+data$FaceId <- factor(data$FaceId)
+data$Type = factor (data$Type)
+
+# exlude irrelevant types
+data = subset (data, Type %in% c("InInterests", "OutInterests", "InData", "OutData"))
+
+# Aggregate packet stats in 5-second intervals
+data$Time5Sec = 5 * ceiling (data$Time / 5)
+data.combined = summaryBy (. ~ Time5Sec + Node + Type, data=data, FUN=sum)
+
+data.root = subset (data.combined, Node == "root")
+data.leaves = subset (data.combined, Node %in% c("leaf-1", "leaf-2", "leaf-3", "leaf-4"))
+
+# graph rates on all nodes in Packets
+g.all <- ggplot (data.combined) +
+  geom_point (aes (x=Time5Sec, y=Packets.sum, color=Type), size=2) +
+  geom_line (aes (x=Time5Sec, y=Packets.sum, color=Type), size=0.5) +
+  ylab ("Number of transitted packets in 5 secon intervals") +
+  facet_wrap (~ Node)
+
+print (g.all)
+  
+# graph rates on the root nodes in Packets
+g.root <- ggplot (data.root) +
+  geom_point (aes (x=Time5Sec, y=Packets.sum, color=Type), size=2) +
+  geom_line (aes (x=Time5Sec, y=Packets.sum, color=Type), size=0.5) +
+  ylab ("Number of transitted packets in 5 secon intervals") +
+  ylim (c(0,2000))
+
+print (g.root)
+
+png ("root-5sec-counts.png", width=500, height=250)
+print (g.root)
+dev.off ()
diff --git a/examples/ndn-tree-tracers.cc b/examples/ndn-tree-tracers.cc
new file mode 100644
index 0000000..b76869a
--- /dev/null
+++ b/examples/ndn-tree-tracers.cc
@@ -0,0 +1,125 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011-2012 University of California, Los Angeles
+ *
+ * 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: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+// ndn-tree-tracers.cc
+
+#include "ns3/core-module.h"
+#include "ns3/network-module.h"
+#include "ns3/ndnSIM-module.h"
+
+// for ndn::L3AggregateTracer
+#include <ns3/ndnSIM/utils/tracers/ndn-l3-aggregate-tracer.h>
+
+// for ndn::L3RateTracer
+#include <ns3/ndnSIM/utils/tracers/ndn-l3-rate-tracer.h>
+
+using namespace ns3;
+
+/**
+ * This scenario simulates a tree topology (using topology reader module)
+ *
+ *    /------\      /------\      /------\      /------\
+ *    |leaf-1|      |leaf-2|      |leaf-3|      |leaf-4|
+ *    \------/      \------/      \------/      \------/
+ *         ^          ^                ^           ^	
+ *         |          |                |           |
+ *    	    \        /                  \         / 
+ *           \      /  			 \  	 /    10Mbps / 1ms
+ *            \    /  			  \ 	/
+ *             |  |  			   |   | 
+ *    	       v  v                        v   v     
+ *          /-------\                    /-------\
+ *          | rtr-1 |                    | rtr-2 |
+ *          \-------/                    \-------/
+ *                ^                        ^                      
+ *      	  |	 		   |
+ *      	   \			  /  10 Mpbs / 1ms 
+ *      	    +--------+  +--------+ 
+ *      		     |  |      
+ *                           v  v
+ *      		  /--------\
+ *      		  |  root  |
+ *                        \--------/
+ *
+ *
+ * To run scenario and see what is happening, use the following command:
+ *
+ *     ./waf --run=ndn-tree-tracers
+ */
+
+int
+main (int argc, char *argv[])
+{
+  CommandLine cmd;
+  cmd.Parse (argc, argv);
+
+  AnnotatedTopologyReader topologyReader ("", 1);
+  topologyReader.SetFileName ("src/ndnSIM/examples/topologies/topo-tree.txt");
+  topologyReader.Read ();
+
+  // Install CCNx stack on all nodes
+  ndn::StackHelper ccnxHelper;
+  ccnxHelper.SetForwardingStrategy ("ns3::ndn::fw::BestRoute");
+  ccnxHelper.InstallAll ();
+
+  // Installing global routing interface on all nodes
+  ndn::GlobalRoutingHelper ccnxGlobalRoutingHelper;
+  ccnxGlobalRoutingHelper.InstallAll ();
+
+  // Getting containers for the consumer/producer
+  Ptr<Node> consumers[4] = { Names::Find<Node> ("leaf-1"), Names::Find<Node> ("leaf-2"),
+                             Names::Find<Node> ("leaf-3"), Names::Find<Node> ("leaf-4") };
+  Ptr<Node> producer = Names::Find<Node> ("root");
+
+  for (int i = 0; i < 4; i++)
+    {
+      ndn::AppHelper consumerHelper ("ns3::ndn::ConsumerCbr");
+      consumerHelper.SetAttribute ("Frequency", StringValue ("100")); // 100 interests a second
+
+      // Each consumer will express unique interests /root/<leaf-name>/<seq-no>
+      consumerHelper.SetPrefix ("/root/" + Names::FindName (consumers[i]));
+      consumerHelper.Install (consumers[i]);
+    }
+    
+  ndn::AppHelper producerHelper ("ns3::ndn::Producer");
+  producerHelper.SetAttribute ("PayloadSize", StringValue("1024"));  
+
+  // Register /root prefix with global routing controller and
+  // install producer that will satisfy Interests in /root namespace
+  ccnxGlobalRoutingHelper.AddOrigins ("/root", producer);
+  producerHelper.SetPrefix ("/root");
+  producerHelper.Install (producer);
+
+  // Calculate and install FIBs
+  ccnxGlobalRoutingHelper.CalculateRoutes ();
+
+  Simulator::Stop (Seconds (20.0));
+
+  boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<ndn::L3AggregateTracer> > >
+    aggTracers = ndn::L3AggregateTracer::InstallAll ("aggregate-trace.txt", Seconds (0.5));
+
+  boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<ndn::L3RateTracer> > >
+    rateTracers = ndn::L3RateTracer::InstallAll ("rate-trace.txt", Seconds (0.5));
+  
+  Simulator::Run ();
+  Simulator::Destroy ();
+
+  return 0;
+}
diff --git a/examples/wscript b/examples/wscript
index 5c478a7..67cf5ca 100644
--- a/examples/wscript
+++ b/examples/wscript
@@ -19,3 +19,6 @@
             'custom-strategies/custom-strategy.cc',
             'ndn-congestion-alt-topo-plugin.cc'
             ]
+
+        obj = bld.create_ns3_program('ndn-tree-tracers', ['ndnSIM'])
+        obj.source = 'ndn-tree-tracers.cc'
diff --git a/utils/tracers/ndn-l3-aggregate-tracer.cc b/utils/tracers/ndn-l3-aggregate-tracer.cc
index 1280ede..35b85ec 100644
--- a/utils/tracers/ndn-l3-aggregate-tracer.cc
+++ b/utils/tracers/ndn-l3-aggregate-tracer.cc
@@ -28,18 +28,63 @@
 #include "ns3/ndn-face.h"
 #include "ns3/ndn-interest.h"
 #include "ns3/ndn-content-object.h"
+#include "ns3/simulator.h"
+#include "ns3/node-list.h"
+#include "ns3/log.h"
+
+#include <fstream>
+
+NS_LOG_COMPONENT_DEFINE ("ndn.L3AggregateTracer");
 
 namespace ns3 {
 namespace ndn {
 
-L3AggregateTracer::L3AggregateTracer (Ptr<Node> node)
-: L3Tracer (node)
+boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<L3AggregateTracer> > >
+L3AggregateTracer::InstallAll (const std::string &file, Time averagingPeriod/* = Seconds (0.5)*/)
+{
+  using namespace boost;
+  using namespace std;
+  
+  std::list<Ptr<L3AggregateTracer> > tracers;
+  boost::shared_ptr<std::ofstream> outputStream (new std::ofstream ());
+  outputStream->open (file.c_str (), std::ios_base::out | std::ios_base::trunc);
+
+  if (!outputStream->is_open ())
+    return boost::make_tuple (outputStream, tracers);
+
+  for (NodeList::Iterator node = NodeList::Begin ();
+       node != NodeList::End ();
+       node++)
+    {
+      NS_LOG_DEBUG ("Node: " << (*node)->GetId ());
+
+      Ptr<L3AggregateTracer> trace = Create<L3AggregateTracer> (outputStream, *node);
+      trace->SetAveragingPeriod (averagingPeriod);
+      tracers.push_back (trace);
+    }
+
+  if (tracers.size () > 0)
+    {
+      // *m_l3RateTrace << "# "; // not necessary for R's read.table
+      tracers.front ()->PrintHeader (*outputStream);
+      *outputStream << "\n";
+    }
+
+  return boost::make_tuple (outputStream, tracers);
+}
+
+
+
+L3AggregateTracer::L3AggregateTracer (boost::shared_ptr<std::ostream> os, Ptr<Node> node)
+  : L3Tracer (node)
+  , m_os (os)
 {
   Reset ();
 }
 
-L3AggregateTracer::L3AggregateTracer (const std::string &node)
-: L3Tracer (node)
+L3AggregateTracer::L3AggregateTracer (boost::shared_ptr<std::ostream> os, const std::string &node)
+  : L3Tracer (node)
+  , m_os (os)
 {
   Reset ();
 }
@@ -49,144 +94,157 @@
 };
 
 void
-L3AggregateTracer::Reset ()
+L3AggregateTracer::SetAveragingPeriod (const Time &period)
 {
-  m_packets.Reset ();
-  m_bytes.Reset ();
+  m_period = period;
+  m_printEvent.Cancel ();
+  m_printEvent = Simulator::Schedule (m_period, &L3AggregateTracer::PeriodicPrinter, this);
 }
 
+void
+L3AggregateTracer::PeriodicPrinter ()
+{
+  Print (*m_os);
+  Reset ();
+  
+  m_printEvent = Simulator::Schedule (m_period, &L3AggregateTracer::PeriodicPrinter, this);
+}
 
 void
 L3AggregateTracer::PrintHeader (std::ostream &os) const
 {
-  os << "Node" << "\t"
-     << "InInterests" << "\t"
-     << "OutInterests" << "\t"
-     << "DropInterests" << "\t"
-    
-     << "InNacks" << "\t"
-     << "OutNacks" << "\t"
-     << "DropNacks" << "\t"
-    
-     << "InData" << "\t"
-     << "OutData" << "\t"
-     << "DropData" << "\t"
-    
-     << "InInterestsBytes" << "\t"
-     << "OutInterestsBytes" << "\t"
-     << "DropInterestsBytes" << "\t"
-    
-     << "InNacksBytes" << "\t"
-     << "OutNacksBytes" << "\t"
-     << "DropNacksBytes" << "\t"
-    
-     << "InDataBytes" << "\t"
-     << "OutDataBytes" << "\t"
-     << "DropDataBytes";
+  os << "Time" << "\t"
+
+     << "Node" << "\t"
+     << "FaceId" << "\t"
+     << "FaceDescr" << "\t"
+
+     << "Type" << "\t"
+     << "Packets" << "\t"
+     << "Kilobytes";
 }
 
 void
+L3AggregateTracer::Reset ()
+{
+  for (std::map<Ptr<const Face>, boost::tuple<Stats, Stats> >::iterator stats = m_stats.begin ();
+       stats != m_stats.end ();
+       stats++)
+    {
+      stats->second.get<0> ().Reset ();
+      stats->second.get<1> ().Reset ();
+    }
+}
+
+
+#define STATS(INDEX) stats->second.get<INDEX> ()
+
+#define PRINTER(printName, fieldName) \
+  os << time.ToDouble (Time::S) << "\t"                                 \
+  << m_node << "\t"                                                     \
+  << stats->first->GetId () << "\t"                                     \
+  << *stats->first << "\t"                                              \
+  << printName << "\t"                                                  \
+  << STATS(0).fieldName << "\t"                                         \
+  << STATS(1).fieldName / 1024.0 << "\n";
+
+
+void
 L3AggregateTracer::Print (std::ostream &os) const
 {
-  os << m_node << "\t"
-     << m_packets.m_inInterests   << "\t"
-     << m_packets.m_outInterests  << "\t"
-     << m_packets.m_dropInterests << "\t"
+  for (std::map<Ptr<const Face>, boost::tuple<Stats, Stats> >::iterator stats = m_stats.begin ();
+       stats != m_stats.end ();
+       stats++)
+    {
+      Time time = Simulator::Now ();
 
-     << m_packets.m_inNacks   << "\t"
-     << m_packets.m_outNacks  << "\t"
-     << m_packets.m_dropNacks << "\t"
+      PRINTER ("InInterests",   m_inInterests);
+      PRINTER ("OutInterests",  m_outInterests);
+      // PRINTER ("DropInterests", m_dropInterests);
+      
+      PRINTER ("InNacks",   m_inNacks);
+      PRINTER ("OutNacks",  m_outNacks);
+      // PRINTER ("DropNacks", m_dropNacks);
 
-     << m_packets.m_inData   << "\t"
-     << m_packets.m_outData  << "\t"
-     << m_packets.m_dropData << "\t"
-
-     << m_bytes.m_inInterests   << "\t"
-     << m_bytes.m_outInterests  << "\t"
-     << m_bytes.m_dropInterests << "\t"
-
-     << m_bytes.m_inNacks   << "\t"
-     << m_bytes.m_outNacks  << "\t"
-     << m_bytes.m_dropNacks << "\t"
-
-     << m_bytes.m_inData   << "\t"
-     << m_bytes.m_outData  << "\t"
-     << m_bytes.m_dropData;
+      PRINTER ("InData",   m_inData);
+      PRINTER ("OutData",  m_outData);
+      // PRINTER ("DropData", m_dropData);
+    }
 }
 
 void
 L3AggregateTracer::OutInterests  (std::string context,
-                                  Ptr<const InterestHeader> header, Ptr<const Face>)
+                                  Ptr<const InterestHeader> header, Ptr<const Face> face)
 {
-  m_packets.m_outInterests++;
-  m_bytes.m_outInterests += header->GetSerializedSize ();
+  m_stats[face].get<0> ().m_outInterests ++;
+  m_stats[face].get<1> ().m_outInterests += header->GetSerializedSize ();
 }
 
 void
 L3AggregateTracer::InInterests   (std::string context,
-                                  Ptr<const InterestHeader> header, Ptr<const Face>)
+                                  Ptr<const InterestHeader> header, Ptr<const Face> face)
 {
-  m_packets.m_inInterests++;
-  m_bytes.m_inInterests += header->GetSerializedSize ();
+  m_stats[face].get<0> ().m_inInterests ++;
+  m_stats[face].get<1> ().m_inInterests += header->GetSerializedSize ();
 }
 
 void
 L3AggregateTracer::DropInterests (std::string context,
-                                  Ptr<const InterestHeader> header, Ptr<const Face>)
+                                  Ptr<const InterestHeader> header, Ptr<const Face> face)
 {
-  m_packets.m_dropInterests++;
-  m_bytes.m_dropInterests += header->GetSerializedSize ();
+  m_stats[face].get<0> ().m_dropInterests ++;
+  m_stats[face].get<1> ().m_dropInterests += header->GetSerializedSize ();
 }
 
 void
 L3AggregateTracer::OutNacks  (std::string context,
-                              Ptr<const InterestHeader> header, Ptr<const Face>)
+                              Ptr<const InterestHeader> header, Ptr<const Face> face)
 {
-  m_packets.m_outNacks++;
-  m_bytes.m_outNacks += header->GetSerializedSize ();
+  m_stats[face].get<0> ().m_outNacks ++;
+  m_stats[face].get<1> ().m_outNacks += header->GetSerializedSize ();
 }
 
 void
 L3AggregateTracer::InNacks   (std::string context,
-                              Ptr<const InterestHeader> header, Ptr<const Face>)
+                              Ptr<const InterestHeader> header, Ptr<const Face> face)
 {
-  m_packets.m_inNacks++;
-  m_bytes.m_inNacks += header->GetSerializedSize ();
+  m_stats[face].get<0> ().m_inNacks ++;
+  m_stats[face].get<1> ().m_inNacks += header->GetSerializedSize ();
 }
 
 void
 L3AggregateTracer::DropNacks (std::string context,
-                              Ptr<const InterestHeader> header, Ptr<const Face>)
+                              Ptr<const InterestHeader> header, Ptr<const Face> face)
 {
-  m_packets.m_dropNacks++;
-  m_bytes.m_dropNacks += header->GetSerializedSize ();
+  m_stats[face].get<0> ().m_dropNacks ++;
+  m_stats[face].get<1> ().m_dropNacks += header->GetSerializedSize ();
 }
 
 void
 L3AggregateTracer::OutData  (std::string context,
                              Ptr<const ContentObjectHeader> header, Ptr<const Packet> payload,
-                             bool fromCache, Ptr<const Face>)
+                             bool fromCache, Ptr<const Face> face)
 {
-  m_packets.m_outData++;
-  m_bytes.m_outData += header->GetSerializedSize () + payload->GetSize ();
+  m_stats[face].get<0> ().m_outData ++;
+  m_stats[face].get<1> ().m_outData += header->GetSerializedSize () + payload->GetSize ();
 }
 
 void
 L3AggregateTracer::InData   (std::string context,
                              Ptr<const ContentObjectHeader> header, Ptr<const Packet> payload,
-                             Ptr<const Face>)
+                             Ptr<const Face> face)
 {
-  m_packets.m_inData++;
-  m_bytes.m_inData += header->GetSerializedSize () + payload->GetSize ();
+  m_stats[face].get<0> ().m_inData ++;
+  m_stats[face].get<1> ().m_inData += header->GetSerializedSize () + payload->GetSize ();
 }
 
 void
 L3AggregateTracer::DropData (std::string context,
                              Ptr<const ContentObjectHeader> header, Ptr<const Packet> payload,
-                             Ptr<const Face>)
+                             Ptr<const Face> face)
 {
-  m_packets.m_dropData++;
-  m_bytes.m_dropData += header->GetSerializedSize () + payload->GetSize ();
+  m_stats[face].get<0> ().m_dropData ++;
+  m_stats[face].get<1> ().m_dropData += header->GetSerializedSize () + payload->GetSize ();
 }
 
 } // namespace ndn
diff --git a/utils/tracers/ndn-l3-aggregate-tracer.h b/utils/tracers/ndn-l3-aggregate-tracer.h
index bd9cbd0..a04df67 100644
--- a/utils/tracers/ndn-l3-aggregate-tracer.h
+++ b/utils/tracers/ndn-l3-aggregate-tracer.h
@@ -26,16 +26,55 @@
 #include <ns3/nstime.h>
 #include <ns3/event-id.h>
 
+#include <boost/tuple/tuple.hpp>
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <list>
+
 namespace ns3 {
 namespace ndn {
 
+/**
+ * @ingroup ndn
+ * @brief CCNx network-layer tracer for aggregate packet counts
+ */
 class L3AggregateTracer : public L3Tracer
 {
 public:
-  L3AggregateTracer (Ptr<Node> node);
-  L3AggregateTracer (const std::string &node);
-  virtual ~L3AggregateTracer ();
+  /**
+   * @brief Trace constructor that attaches to the node using node pointer
+   * @param os    reference to the output stream
+   * @param node  pointer to the node
+   */
+  L3AggregateTracer (boost::shared_ptr<std::ostream> os, Ptr<Node> node);
   
+  /**
+   * @brief Trace constructor that attaches to the node using node name
+   * @param os        reference to the output stream
+   * @param nodeName  name of the node registered using Names::Add
+   */
+  L3AggregateTracer (boost::shared_ptr<std::ostream> os, const std::string &nodeName);
+
+  /**
+   * @brief Destructor
+   */
+  virtual ~L3AggregateTracer ();
+
+  /**
+   * @brief Helper method to install tracers on all simulation nodes
+   *
+   * @param file File to which traces will be written
+   * @param averagingPeriod How often data will be written into the trace file (default, every half second)
+   *
+   * @returns a tuple of reference to output stream and list of tracers. !!! Attention !!! This tuple needs to be preserved
+   *          for the lifetime of simulation, otherwise SEGFAULTs are inevitable
+   * 
+   */
+  static boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<L3AggregateTracer> > >
+  InstallAll (const std::string &file, Time averagingPeriod = Seconds (0.5));
+
+protected: 
+  // from L3Tracer
   virtual void
   PrintHeader (std::ostream &os) const;
 
@@ -80,17 +119,21 @@
 
 protected:
   void
+  SetAveragingPeriod (const Time &period);
+
+  void
   Reset ();
 
   void
   PeriodicPrinter ();
   
 protected:
-  Stats m_packets;
-  Stats m_bytes;
+  boost::shared_ptr<std::ostream> m_os;
 
   Time m_period;
   EventId m_printEvent;
+  
+  mutable std::map<Ptr<const Face>, boost::tuple<Stats, Stats> > m_stats;
 };
 
 } // namespace ndn
diff --git a/utils/tracers/ndn-l3-rate-tracer.cc b/utils/tracers/ndn-l3-rate-tracer.cc
index 6604f0b..f588503 100644
--- a/utils/tracers/ndn-l3-rate-tracer.cc
+++ b/utils/tracers/ndn-l3-rate-tracer.cc
@@ -157,7 +157,7 @@
   << STATS(2).fieldName << "\t"                                         \
   << STATS(3).fieldName << "\t"                                         \
   << STATS(0).fieldName << "\t"                                         \
-  << STATS(1).fieldName << "\n";
+  << STATS(1).fieldName / 1024.0 << "\n";
 
 void
 L3RateTracer::Print (std::ostream &os) const
@@ -236,8 +236,8 @@
                         Ptr<const ContentObjectHeader> header, Ptr<const Packet> payload,
                         bool fromCache, Ptr<const Face> face)
 {
-  m_stats[face].get<0> ().m_inData ++;
-  m_stats[face].get<1> ().m_inData += header->GetSerializedSize () + payload->GetSize ();
+  m_stats[face].get<0> ().m_outData ++;
+  m_stats[face].get<1> ().m_outData += header->GetSerializedSize () + payload->GetSize ();
 }
 
 void
@@ -245,8 +245,8 @@
                         Ptr<const ContentObjectHeader> header, Ptr<const Packet> payload,
                         Ptr<const Face> face)
 {
-  m_stats[face].get<0> ().m_outData ++;
-  m_stats[face].get<1> ().m_outData += header->GetSerializedSize () + payload->GetSize ();
+  m_stats[face].get<0> ().m_inData ++;
+  m_stats[face].get<1> ().m_inData += header->GetSerializedSize () + payload->GetSize ();
 }
 
 void
diff --git a/utils/tracers/ndn-l3-rate-tracer.h b/utils/tracers/ndn-l3-rate-tracer.h
index ced3113..791d953 100644
--- a/utils/tracers/ndn-l3-rate-tracer.h
+++ b/utils/tracers/ndn-l3-rate-tracer.h
@@ -35,31 +35,54 @@
 namespace ndn {
 
 /**
- * @ingroup ccnx
+ * @ingroup ndn
  * @brief CCNx network-layer rate tracer
  */
 class L3RateTracer : public L3Tracer
 {
 public:
   /**
-   * @brief Network layer tracer constructor
+   * @brief Trace constructor that attaches to the node using node pointer
+   * @param os    reference to the output stream
+   * @param node  pointer to the node
    */
   L3RateTracer (boost::shared_ptr<std::ostream> os, Ptr<Node> node);
+
+  /**
+   * @brief Trace constructor that attaches to the node using node name
+   * @param os        reference to the output stream
+   * @param nodeName  name of the node registered using Names::Add
+   */
   L3RateTracer (boost::shared_ptr<std::ostream> os, const std::string &node);
+
+  /**
+   * @brief Destructor
+   */
   virtual ~L3RateTracer ();
 
+  /**
+   * @brief Helper method to install tracers on all simulation nodes
+   *
+   * @param file File to which traces will be written
+   * @param averagingPeriod Defines averaging period for the rate calculation,
+   *        as well as how often data will be written into the trace file (default, every half second)
+   *
+   * @returns a tuple of reference to output stream and list of tracers. !!! Attention !!! This tuple needs to be preserved
+   *          for the lifetime of simulation, otherwise SEGFAULTs are inevitable
+   * 
+   */
   static boost::tuple< boost::shared_ptr<std::ostream>, std::list<Ptr<L3RateTracer> > >
   InstallAll (const std::string &file, Time averagingPeriod = Seconds (0.5));
-  
-  void
-  SetAveragingPeriod (const Time &period);
-  
+
+  // from L3Tracer
   virtual void
   PrintHeader (std::ostream &os) const;
 
   virtual void
   Print (std::ostream &os) const;
 
+protected:
+  // from L3Tracer
   virtual void
   OutInterests  (std::string context,
                  Ptr<const InterestHeader>, Ptr<const Face>);
@@ -98,6 +121,9 @@
 
 private:
   void
+  SetAveragingPeriod (const Time &period);
+
+  void
   PeriodicPrinter ();
   
   void
diff --git a/utils/tracers/ndn-l3-tracer.h b/utils/tracers/ndn-l3-tracer.h
index 74c0ef9..72c4cac 100644
--- a/utils/tracers/ndn-l3-tracer.h
+++ b/utils/tracers/ndn-l3-tracer.h
@@ -38,19 +38,43 @@
 class L3Tracer : public SimpleRefCount<L3Tracer>
 {
 public:
+  /**
+   * @brief Trace constructor that attaches to the node using node pointer
+   * @param node  pointer to the node
+   */
   L3Tracer (Ptr<Node> node);
+
+  /**
+   * @brief Trace constructor that attaches to the node using node name
+   * @param nodeName  name of the node registered using Names::Add
+   */
   L3Tracer (const std::string &node);
+
+  /**
+   * @brief Destructor
+   */
   virtual ~L3Tracer ();
 
-  void
-  Connect ();
-  
+  /**
+   * @brief Print head of the trace (e.g., for post-processing)
+   *
+   * @param os reference to output stream
+   */
   virtual void
   PrintHeader (std::ostream &os) const = 0;
 
+  /**
+   * @brief Print current trace data
+   *
+   * @param os reference to output stream
+   */
   virtual void
   Print (std::ostream &os) const = 0;
   
+protected:
+  void
+  Connect ();
+  
   virtual void
   OutInterests  (std::string context,
                  Ptr<const InterestHeader>, Ptr<const Face>) = 0;
@@ -119,6 +143,9 @@
   };
 };
 
+/**
+ * @brief Helper to dump the trace to an output stream
+ */
 inline std::ostream&
 operator << (std::ostream &os, const L3Tracer &tracer)
 {
diff --git a/wscript b/wscript
index e1e4bb6..a48ff80 100644
--- a/wscript
+++ b/wscript
@@ -8,6 +8,8 @@
 from waflib.Errors import WafError
 
 import wutils
+import TaskGen
+import Task
 
 def options(opt):
     opt.tool_options('boost', tooldir=["waf-tools"])
@@ -70,6 +72,8 @@
         deps.append ('mobility')
 
     module = bld.create_ns3_module ('ndnSIM', deps)
+    module.module = 'ndnSIM'
+    module.features += ['ns3fullmoduleheaders']
     module.uselib = 'BOOST BOOST_IOSTREAMS'
 
     headers = bld.new_task_gen(features=['ns3header'])
@@ -84,6 +88,12 @@
                                        'utils/**/*.cc',
                                        'helper/**/*.cc',
                                        ])
+    module.full_headers = [p.path_from(bld.path) for p in bld.path.ant_glob([
+                           'utils/**/*.h',
+                           'model/**/*.h',
+                           'apps/**/*.h',
+                           'helper/**/*.h',
+                           ])]
 
     headers.source = [
         "helper/ndn-stack-helper.h",
@@ -135,13 +145,7 @@
             ])
         module.source.extend (bld.path.ant_glob(['plugins/mobility/*.cc']))
 
-    ndnSIM_headers = [p.path_from(bld.path) for p in bld.path.ant_glob([
-                'utils/**/*.h',
-                'model/**/*.h',
-                'apps/**/*.h',
-                'helper/**/*.h',
-                ])]
-    bld.install_files('${INCLUDEDIR}/%s%s/ns3/ndnSIM' % (wutils.APPNAME, wutils.VERSION), ndnSIM_headers, relative_trick=True)
+    # bld.install_files('${INCLUDEDIR}/%s%s/ns3/ndnSIM' % (wutils.APPNAME, wutils.VERSION), ndnSIM_headers, relative_trick=True)
     # bld.install_files('$PREFIX/include', ndnSIM_headers)
     
     tests = bld.create_ns3_module_test_library('ndnSIM')
@@ -151,3 +155,32 @@
         bld.add_subdirs('examples')
 
     bld.ns3_python_bindings()
+
+
+@TaskGen.feature('ns3fullmoduleheaders')
+@TaskGen.after_method('process_rule')
+def apply_ns3fullmoduleheaders(self):
+    # ## get all of the ns3 headers
+    ns3_dir_node = self.bld.path.find_dir("ns3")
+
+    mode = getattr(self, "mode", "install")
+
+    for filename in set(self.to_list(self.full_headers)):
+        src_node = self.path.find_resource(filename)
+        if src_node is None:
+            raise WafError("source ns3 header file %s not found" % (filename,))
+        dst_node = ns3_dir_node.find_or_declare(src_node.path_from(self.bld.path.find_dir('src')))
+        assert dst_node is not None
+
+        relpath = src_node.parent.path_from(self.bld.path.find_dir('src'))
+
+        task = self.create_task('ns3header')
+        task.mode = getattr(self, 'mode', 'install')
+        if task.mode == 'install':
+            self.bld.install_files('${INCLUDEDIR}/%s%s/ns3/%s' % (wutils.APPNAME, wutils.VERSION, relpath), 
+                                   [src_node])
+            task.set_inputs([src_node])
+            task.set_outputs([dst_node])
+        else:
+            task.header_to_remove = dst_node
+