-
-<div class="title">Lesson 11: Simulation with TOSSIM</div>
-<div class="subtitle">Last Modified: 20 April 2007</div>
-
- <p>This lesson introduces the TOSSIM simulator. You will become
- familiar with how to compile TOSSIM and use some of its
- functionality. You will learn how to:</p>
-
- <p>
-
- <ul>
-
- <li>Compile TOSSIM.</li>
-
- <li>Configure a simulation in Python and C++.</li>
-
- <li>Inspect variables.</li>
-
- <li>Inject packets.</li>
- </ul>
- </p>
-
- <p><b>Note:</b> This tutorial is for TOSSIM in TinyOS 2.0.1. If
- you are using TinyOS 2.0.0, it has a slightly different
- A HREF="lesson11-200.html">tutorial.</A> The principal difference
- between the two is how you specify noise in RF simulation.
- </p>
-<h1>Introduction</h1>
-
- TOSSIM simulates entire TinyOS applications. It works by
- replacing components with simulation implementations. The
- level at which components are replaced is very flexible: for
- example, there is a simulation implementation of millisecond
- timers that replaces <code>HilTimerMilliC</code>, while there is also an
- implementation for atmega128 platforms that replaces the HPL
- components of the hardware clocks. The former is general and
- can be used for any platform, but lacks the fidelity of
- capturing an actual chips behavior, as the latter
- does. Similarly, TOSSIM can replace a packet-level
- communication component for packet-level simulation, or
- replace a low-level radio chip component for a more precise
- simulation of the code execution.
-
- TOSSIM is a discrete event simulator. When it runs, it pulls
- events of the event queue (sorted by time) and executes them.
- Depending on the level of simulation, simulation events can
- represent hardware interrupts or high-level system events
- (such as packet reception). Additionally, tasks are simulation
- events, so that posting a task causes it to run a short time
- (e.g., a few microseconds) in the future.
-
- TOSSIM is a library: you must write a program that configures
- a simulation and runs it. TOSSIM supports two programming
- interfaces, Python and C++. Python allows you to interact with
- a running simulation dynamically, like a powerful
- debugger. However, as the interpretation can be a performance
- bottleneck when obtaining results, TOSSIM also has a C++
- interface. Usually, transforming code from one to the other is
- very simple.
-
- TOSSIM currently does not support gathering power
- measurements.
-
- <h1>Compiling TOSSIM</h1>
-
- <p>TOSSIM is a TinyOS library. Its core code lives in <code><a
- href="../../../tos/lib/tossim">tos/lib/tossim</a></code>. Every TinyOS
- source directory has an optional <code>sim</code> subdirectory,
- which contains simulation implementations of that package. For
- example, <code><a
- href="../../../tos/chips/atm128/timersim">tos/chips/atm128/timer/sim</a></code>
- contains TOSSIM implementations of some of the Atmega128 timer
- abstractions.</p>
-
- <p>To compile TOSSIM, you pass the <code>sim</code> option to make:</p>
-
- <pre>
- $ cd apps/Blink
- $ make micaz sim
- </pre>
-
-
- <p>Currently, the only platform TOSSIM supports is the
- micaz. You should see output similar to this:</p>
- <pre>
- mkdir -p build/micaz
- placing object files in build/micaz
- writing XML schema to app.xml
- compiling BlinkAppC to object file sim.o
- ncc -c -fPIC -o build/micaz/sim.o -g -O0 -tossim -fnesc-nido-tosnodes=1000 -fnesc-simulate -fnesc-nido-motenumber=sim_node\(\) -finline-limit=100000 -Wall -Wshadow -DDEF_TOS_AM_GROUP=0x7d -Wnesc-all -target=micaz -fnesc-cfile=build/micaz/app.c -board=micasb -Wno-nesc-data-race BlinkAppC.nc -fnesc-dump=components -fnesc-dump=variables -fnesc-dump=constants -fnesc-dump=typedefs -fnesc-dump=interfacedefs -fnesc-dump=tags -fnesc-dumpfile=app.xml
- compiling Python support into pytossim.o and tossim.o
- g++ -c -shared -fPIC -o build/micaz/pytossim.o -g -O0 /home/pal/src/tinyos-2.x/tos/lib/tossim/tossim_wrap.cxx -I/usr/include/python2.3 -I/home/pal/src/tinyos-2.x/tos/lib/tossim -DHAVE_CONFIG_H
- g++ -c -shared -fPIC -o build/micaz/tossim.o -g -O0 /home/pal/src/tinyos-2.x/tos/lib/tossim/tossim.c -I/usr/include/python2.3 -I/home/pal/src/tinyos-2.x/tos/lib/tossim
- linking into shared object ./_TOSSIMmodule.so
- g++ -shared build/micaz/pytossim.o build/micaz/sim.o build/micaz/tossim.o -lstdc++ -o _TOSSIMmodule.so
- copying Python script interface TOSSIM.py from lib/tossim to local directory
- </pre>
-
- <p>Depending on what OS you are using and what packages are installed, TOSSIM may
- not properly compile on the first try. <A HREF="#appendix">Appendix A</A>
- addresses some of the common causes and gives possible solutions.</p>
-
-
- <A name="#compiling"></A>
-
-
- <p>Compiling TOSSIM has five basic steps. Let's go through
- them one by one.</p>
-
- <h2>Writing an XML schema</h2>
-
- <pre>
- writing XML schema to app.xml
- </pre>
-
- <p>The first thing the TOSSIM build process does is use
- nesc-dump to produce an XML document that describes the
- application. Among other things, this document descibes the
- name and type of every variable.</p>
-
- <h2>Compiling the TinyOS Application</h2>
-
- <p>Besides introducing all of these new compilation steps, the
- <code>sim</code> option changes the include paths of the
- application. If the application has a series of includes</p>
-
- <pre>
- -Ia -Ib -Ic
- </pre>
-
- <p>Then the sim option transforms the list to</p>
-
- <pre>
- -Ia/sim -Ib/sim -Ic/sim -I%T/lib/tossim -Ia -Ib -Ic
- </pre>
-
- <p>This means that any system-specific simulation
- implementations will be used first, followed by generic TOSSIM
- implementations, followed by standard implementations. The
- <code>sim</code> option also passes a bunch of arguments to the
- compiler, so it knows to compile for simulation.</p>
-
- <p>The product of this step is an object file, <code>sim.o</code>,
- which lives in the platform's build directory. This object
- file has a set of C functions which configure the simulation
- and control execution.</p>
-
- <h2>Compiling the Programming Interface</h2>
-
- <pre>
- compiling Python support into pytossim.o and tossim.o
- g++ -c -shared -fPIC -o build/micaz/pytossim.o -g -O0 \
- /home/pal/src/tinyos-2.x/tos/lib/tossim/tossim_wrap.cxx \
- -I/usr/include/python2.3 -I/home/pal/src/tinyos-2.x/tos/lib/tossim \
- -DHAVE_CONFIG_H
- g++ -c -shared -fPIC -o build/micaz/tossim.o -g -O0 \
- /home/pal/src/tinyos-2.x/tos/lib/tossim/tossim.c \
- -I/usr/include/python2.3 -I/home/pal/src/tinyos-2.x/tos/lib/tossim
- </pre>
-
- <p>The next step compiles the support for the C++ and Python
- programming interfaces. The Python interface is actually built
- on top of the C++ interface. Calling a Python object calls a
- C++ object, which then calls TOSSIM through the C
- interface. <code>tossim.o</code> contains the C++ code, while
- <code>pytossim.o</code> contains the Python support. These files
- have to be compiled separately because C++ doesn't understand
- nesC, and nesC doesn't understand C++.</p>
-
- <h2>Building the shared object</h2>
-
- <pre>
- linking into shared object ./_TOSSIMmodule.so
- g++ -shared build/micaz/pytossim.o build/micaz/sim.o build/micaz/tossim.o -lstdc++ -o _TOSSIMmodule.so
- </pre>
-
- <p>The next to last step is to build a shared library that
- contains the TOSSIM code, the C++ support, and the Python
- support.</p>
-
- <h2>Copying Python Support</h2>
-
- <pre>
- copying Python script interface TOSSIM.py from lib/tossim to local directory
- </pre>
-
- <p>Finally, there is the Python code that calls into the
- shared object. This code exists in <code>lib/tossim</code>, and
- the make process copies it into the local directory.</p>
-
-
- <h1>Running TOSSIM with Python</h1>
-
- <p>Go into the <code>RadioCountToLeds</code> application and build
- TOSSIM:</p>
-
- <pre>
-$ cd tinyos-2.x/apps/RadioCountToLeds
-$ make micaz sim
- </pre>
-
- <p>We'll start with running a simulation in Python. You can either
- write a script and just tell Python to run it, or you can
- use Python interactively. We'll start with the latter. Fire
- up your Python interpreter:</p>
-
- <pre>
-$ python
- </pre>
-
- <p>You should see a prompt like this:</p>
-
- <pre>
-Python 2.3.4 (#1, Nov 4 2004, 14:13:38)
-[GCC 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)] on linux2
-Type "help", "copyright", "credits" or "license" for more information.
->>>
- </pre>
-
- <p>The first thing we need to do is import TOSSIM and create a
- TOSSIM object. Type</p>
-
- <pre>
->>> from TOSSIM import *
->>> t = Tossim([])
- </pre>
-
- <p>The square brackets are an optional argument that lets you
- access variables in the simulation. We'll get to how to use
- that later. In this case, we're telling TOSSIM that there are
- no variables that we want to look at. The way you run a TOSSIM
- simulation is with the <code>runNextEvent</code> function. For
- example:</p>
-
- <pre>
->>> t.runNextEvent()
-0
- </pre>
-
- <p>When you tell TOSSIM to run the next event, it returns
- 0. This means that there was no next event to run. The reason
- is simple: we haven't told any nodes to boot. This snippet of
- code will tell mote 32 to boot at time 45654 and run its first
- event (booting):</p>
-
- <pre>
->>> m = t.getNode(32);
->>> m.bootAtTime(45654);
->>> t.runNextEvent()
-1
- </pre>
-
- <p><b>Segmentation faults:</b>If trying to do this causes TOSSIM
- to throw a segmentation violation (segfault), then chances are
- you are using a version of gcc that does not work well with the
- dynamic linking that TOSSIM is doing. In particular, it has been
- verified to work properly with 4.0.2 and 3.6, but some people
- have encountered problems with gcc 4.1.1.</p>
-
- <p>Instead of using raw simulation ticks, you can also use the
- call <code>ticksPerSecond()</code>. However, you want to be careful
- to add some random bits into this number: having every node
- perfectly synchronized and only different in phase in terms of
- seconds can lead to strange results.</p>
-
- <pre>
->>> m = t.getNode(32);
->>> m.bootAtTime(4 * t.ticksPerSecond() + 242119);
->>> t.runNextEVent()
-1
- </pre>
-
- <p>Now, <code>runNextEvent</code> returns 1, because there was an
- event to run. But we have no way of knowing whether the node
- has booted or not. We can find this out in one of two ways.
- The first is that we can just ask it:</p>
-
- <pre>
->>> m.isOn()
-1
->>> m.turnOff()
->>> m.isOn()
-0
->>> m.bootAtTime(560000)
->>> t.runNextEvent()
-0
->>> t.runNextEvent()
-1
- </pre>
-
- <p>Note that the first <code>runNextEvent</code> returned 0. This
- is because when we turned the mote off, there was still an
- event in the queue, for its next timer tick. However, since
- the mote was off when the event was handled in that call,
- <code>runNextEvent</code> returned 0. The second call to
- <code>runNextEvent</code> returned 1 for the second boot event, at
- time 560000.</p>
-
-
- <p>A Tossim object has several useful functions. In Python,
- you can generally see the signature of an object with the
- <code>dir</code> function. E.g.:</p>
-
- <pre>
->>> t = Tossim([])
->>> dir(t)
-['__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__getattr__',
-'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
-'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
-'__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'addChannel',
-'currentNode', 'getNode', 'init', 'mac', 'newPacket', 'radio', 'removeChannel',
-'runNextEvent', 'setCurrentNode', 'setTime', 'this', 'thisown', 'ticksPerSecond', 'time', 'timeStr']
- </pre>
-
- <p>The most common utility functions are:</p>
-
- <ul>
- <li><b><code>currentNode()</code></b>: returns the ID of the current node.</li>
- <li><b><code>getNode(id)</code></b>: returns an object representing a specific mote</li>
- <li><b><code>runNextEvent()</code></b>: run a simulation event</li>
- <li><b><code>time()</code></b>: return the current time in simulation ticks as a large integer </li>
- <li><b><code>timeStr()</code></b>: return a string representation of the current time</li>
- <li><b><code>init()</code></b>: initialize TOSSIM</li>
- <li><b><code>mac()</code></b>: return the object representing the media access layer</li>
- <li><b><code>radio()</code></b>: return the object representing the radio model</li>
- <li><b><code>addChannel(ch, output)</code></b>: add <i>output</i> as an output to channel <i>ch</i> </li>
- <li><b><code>removeChannel(ch, output)</code></b>: remove <i>output</i> as an output to channel <i>ch</i> </li>
- <li><b><code>ticksPerSecond()</code></b>: return how many simulation ticks there are in a simulated second </li>
- </ul>
-
- <p>The next section discusses the last two.</p>
-
- <h1>Debugging Statements</h1>
-
- <p>The second approach to know whether a node is on is to tell
- it to print something out when it boots. TOSSIM has a
- debugging output system, called <code>dbg</code>. There are four
- <code>dbg</code> calls:</p>
-
- <ul>
- <li><code>dbg</code>: print a debugging statement preceded by the node ID.</li>
- <li><code>dbg_clear</code>: print a debugging statement which is not preceded by the node ID. This allows you to easily print out complex data types, such as packets, without interspersing node IDs through the output.</li>
- <li><code>dbgerror</code>: print an error statement preceded by the node ID</li>
- <li><code>dbgerror_clear</code>: print an error statement which is not preceded by the node ID</li>
- </ul>
-
-
- <p>Go into <code>RadioCountToLedsC</code> and modify the <code>Boot.booted</code> event
- to print out a debug message when it boots, such as this:</p>
-
- <pre>
-event void Boot.booted() {
- call Leds.led0On();
- dbg("Boot", "Application booted.\n");
- call AMControl.start();
-}
- </pre>
-
- <p>Calls to the debugging calls take two or more
- parameters. The first parameter ("Boot" in the above example)
- defines the output <i>channel</i>. An output channel is simply
- a string. The second and subsequent parameters are the message
- to output. They are identical to a printf statement. For example
- <code>RadioCountToLedsC</code> has this call:
-
- <pre>
-event message_t* Receive.receive(message_t* bufPtr, void* payload, uint8_t len) {
- dbg("RadioCountToLedsC", "Received packet of length %hhu.\n", len);
- ...
-}
- </pre>
-
- which prints out the length of received packet as an 8-bit unsigned value (%hhu).</p>
-
- <p>Once you have added the debugging statement to the event,
- recompile the application with <code>make micaz sim</code> and
- start up your Python interpreter. Load the TOSSIM module and
- schedule a mote to boot as before:</p>
-
- <pre>
->>> from TOSSIM import *
->>> t = Tossim([])
->>> m = t.getNode(32);
->>> m.bootAtTime(45654);
- </pre>
-
- <p>This time, however, we want to see the debugging message
- that the mote has booted. TOSSIM's debugging output can be
- configured on a per-channel basis. So, for example, you can
- tell TOSSIM to send the "Boot" channel to standard output, but
- another channel, say "AM", to a file.
- By default, a channel has no destination, and so
- messages to it are discarded.</p>
-
- <p>In this case, we want to send the Boot channel to standard
- output. To do this, we need to import the <code>sys</code> Python
- package, which lets us refer to standard out. We can then tell
- TOSSIM to send Boot messages to this destination:
-
- <pre>
->>> import sys
->>> t.addChannel("Boot", sys.stdout);
-1
- </pre>
-
- The return value shows that the channel was added successfully. Run the first
- simulation event, and the mote boots:
-
- <pre>
->>> t.runNextEvent()
-DEBUG (32): Application booted.
-1
- </pre> </p>
-
- <p>The only difference between debug and error functions is
- the string output at the beginning of a message. Debug
- statements print <code>DEBUG (n)</code>, while error statements
- print <code>ERROR (n)</code>.</p>
-
- <p>A debugging statement can have multiple output
- channels. Each channel name is delimited by commas:
-
- <pre>
-event void Boot.booted() {
- call Leds.led0On();
- dbg("Boot,RadioCountToLedsC", "Application booted.\n");
- call AMControl.start();
-}
- </pre>
-
- If a statement has multiple channels and those channels
- share outputs, then TOSSIM only prints the message once. For
- example, if both the Boot channel and RadioCountToLedsC
- channel were connected to standard out, TOSSIM will only
- print one message. For example, this series of debug statements
-
-
- <pre>
-event void Boot.booted() {
- call Leds.led0On();
- dbg("Boot,RadioCountToLedsC", "Application booted.\n");
- dbg("RadioCountToLedsC", "Application booted again.\n");
- dbg("Boot", "Application booted a third time.\n");
- call AMControl.start();
-}
- </pre>
-
- when configured so
-
- <pre>
->>> import sys
->>> t.addChannel("Boot", sys.stdout)
->>> t.addChannel("RadioCountToLedsC", sys.stdout)
- </pre>
-
- will print out this:
-
- <pre>
-DEBUG (32): Application booted.
-DEBUG (32): Application booted again.
-DEBUG (32): Application booted a third time.
- </pre></p>
-
-
- <p>A channel can have multiple outputs. For example, this
- script will tell TOSSIM to write <code>RadioCountToLedsC</code> messages to
- standard output, but to write <code>Boot</code> messages to both standard
- output and a file named <code>log.txt</code>:
-
- <pre>
->>> import sys
->>> f = open("log.txt", "w")
->>> t.addChannel("Boot", f)
->>> t.addChannel("Boot", sys.stdout)
->>> t.addChannel("RadioCountToLedsC", sys.stdout)
- </pre>
- </p>
-
- <h1>Configuring a Network</h1>
-
- <p>When you start TOSSIM, no node can communicate with any
- other. In order to be able to simulate network behavior, you
- have to specify a <i>network topology</i>. Internally, TOSSIM
- is structured so that you can easily change the underlying
- radio simulation, but that's beyond the scope of this
- tutorial. The default TOSSIM radio model is signal-strength
- based. You provide a graph to the simulator that describes the
- propagation strengths. You also specify noise floor, and
- receiver sensitivity. There are some very early results that
- describe current sensor platforms (e.g., the mica2) in these
- terms. Because all of this is through a scripting interface,
- rather than provide a specific radio model, TOSSIM tries to
- provide a few low-level primitives that can express a wide
- range of radios and behavior.</p>
-
- <p>You control the radio simulation through a Python Radio
- object:</p>
-
-
- <pre>
->>> from TOSSIM import *
->>> t = Tossim([])
->>> r = t.radio()
->>> dir(r)
-['__class__', '__del__', '__delattr__', '__dict__', '__doc__',
-'__getattr__', '__getattribute__', '__hash__', '__init__',
-'__module__', '__new__', '__reduce__', '__reduce_ex__',
-'__repr__', '__setattr__', '__str__', '__swig_getmethods__',
-'__swig_setmethods__', '__weakref__', 'add', 'connected',
-'gain', 'remove', 'setNoise', 'this', 'thisown',
-<class 'TOSSIM.RadioPtr'>]
- </pre>
-
- <p>The first set of methods (with the double underscores) are
- ones that you usually don't call. The important ones are at
- the end. They are:</p>
-
- <ul>
- <li><b><code>add(src, dest, gain)</code></b>: Add a link from <i>src</i>
- to <i>dest</i> with <i>gain</i>. When <i>src</i> transmits, <i>dest</i>
- will receive a packet attenuated by the <i>gain</i> value.</li>
-
- <li><b><code>connected(src, dest)</code></b>: Return whether there is a
- link from <i>src</i> to <i>dest</i>.</li>
-
-
- <li><b><code>gain(src, dest)</code></b>: Return the gain value of the
- link from <i>src</i> to <i>dest</i>.</li>
-
- <li><b><code>threshold()</code></b>: Return the CCA threshold.</li>
-
- <li><b><code>setThreshold(val)</code></b>: Set the CCA threshold value in
- dBm. The default is -72dbM.</li>
-
- </ul>
-
- <p>The default values for TOSSIM's radio model are based on the CC2420 radio,
- used in the micaZ, telos family, and imote2. It uses an SNR curve derived
- from experimental data collected using two micaZ nodes, RF shielding, and
- a variable attenuator.</p>
-
- <p>In addition to the radio propagation above, TOSSIM
- also simulates the RF noise and interference a node hears, both from other
- nodes as well as outside sources. It uses the Closest Pattern Matching (CPM)
- algorithm. CPM takes a noise trace as input and generates a statistical model
- from it. This model can capture bursts of interference and other correlated
- phenomena, such that it greatly improves the quality of the RF simulation.
- It is not perfect (there are several things it does not handle, such as
- correlated interference at nodes that are close to one another), but
- it is much better than traditional, independent packet loss models. For
- more details, please refer to the paper "Improving Wireless Simulation through
- Noise Modeling," by Lee et al.</p>
-
-
- <p>To configure CPM, you need to feed it a noise trace. You accomplish this
- by calling <tt>addNoiseTraceReading</tt> on a Mote object. Once you
- have fed the entire noise trace, you must call <tt>createNoiseModel</tt>
- on the node. The directory <tt>tos/lib/tossim/noise</tt> contains
- sample noise traces, which are a series of noise readings, one per line.
- For example, these are the first 10 lines of meyer-heavy.txt,
- which is a noise trace taken from Meyer Library at Stanford University:</p>
-
- <pre>
--39
--98
--98
--98
--99
--98
--94
--98
--98
--98
-</pre>
-
- <p>As you can see, the hardware noise floor is around -98 dBm,
- but there are spikes of interference around -86dBm and -87dBm.</p>
-
- <p>This piece of code will give a node a noise model from a noise trace
- file. It works for nodes 0-7: you can change the range appropriately:</p>
-
- <pre>
-noise = open("meyer-heavy.txt", "r")
-lines = noise.readlines()
-for line in lines:
- str = line.strip()
- if (str != ""):
- val = int(str)
- for i in range(0, 7):
- t.getNode(i).addNoiseTraceReading(val)
-
-for i in range(0, 7):
- t.getNode(i).createNoiseModel()</pre>
-
-
- <p>CPM can use a good deal of RAM: using the entire meyer-heavy
- trace as input has a cost of approximately 10MB per node. You
- can reduce this overhead by using a shorter trace; this will of
- course reduce simulation fidelity. <u>The trace
- must be at least 100 entries long, or CPM will not work as it does
- not have enough data to generate a statistical model.</u> </p>
-
- <p>You can also use
- <p>The Radio object only deals with physical layer
- propagation. The MAC object deals with the data link layer,
- packet lengths, and radio bandwidth. The default TOSSIM MAC
- object is for a CSMA protocol. You get a reference to the MAC
- object by calling <code>mac()</code> on a Tossim object:
- <pre>
->>> mac = t.mac()
- </pre>
-
- The default MAC object has a large number of functions, for
- controlling backoff behavior, packet preamble length, radio
- bandwidth, etc. All time values are specified in terms of
- radio symbols, and you can configure the number of symbols
- per second and bits per symbol. By default, the MAC object
- is configured to act like the standard TinyOS 2.0 CC2420
- stack: it has 4 bits per symbol and 64k symbols per second,
- for 256kbps. This is a subset of the MAC functions that
- could be useful for changing backoff behavior. Every
- accessor function has a corresponding set function that
- takes an integer as a parameter. E.g., there's <code>int
- initHigh()</code> and <code>void setInitHigh(int val)</code>. The
- default value for each parameter is shown italicized in
- parentheses. </p>
-
- <ul>
- <li><b>initHigh</b>: The upper bound of the initial backoff range. <i>(400)</i></li>
- <li><b>initLow</b>: The lower bound of the initial backoff range. <i>(20)</i></li>
- <li><b>high</b>: The upper bound of the backoff range. This is multiplied by the
- exponent base to the nth power, where n is the number of previous backoffs. So if the
- node had its initial backoff, then the upper bound is high * base, while if it
- is after the second backoff then the upper bound is high * base * base. <i>(160)</i></li>
- <li><b>low</b>: The lower bound of the backoff range. This is multiplied by the
- exponent base to the nth power, where n is the number of previous backoffs. So if the
- node had its initial backoff, then the upper bound is low * base, while if it
- is after the second backoff then the upper bound is low * base * base. <i>(20)</i></li>
- <li><b>symbolsPerSec</b>: The number of symbols per second that the radio can
- transmit. <i>(65536)</i></li>
- <li><b>bitsPerSymbol</b>: The number of bits per radio symbol. Multiplying this by
- the symbols per second gives the radio bandwidth. <i>(4)</i></li>
- <li><b>preambleLength</b>: How long a packet preamble is. This is added to the duration
- of transmission for every packet. <i>(12)</i></li>
- <li><b>exponentBase</b>: The base of the exponent used to calculate backoff. Setting it to
- 2 provides binary exponential backoff. <i>(0)</i>.</li>
- <li><b>maxIterations</b>: The maximum number of times the radio will back off before
- signaling failure, zero signifies forever. <i>(0)</i>.</li>
- <li><b>minFreeSamples</b>: The number of times the radio must detect a clear channel
- before it will transmit. This is important for protocols like 802.15.4, whose synchonrous
- acknowledgments requires that this be greater than 1 (you could have sampled in the dead time
- when the radios are changing between RX and TX mode). <i>(2)</i></li>
- <li><b>rxtxDelay</b>: The time it takes to change the radio from RX to TX mode (or vice versa).<i>(32)</i></li>
- <li><b>ackTime</b>: The time it takes to transmit a synchonrous acknowledgment, not including the
- requisite RX/TX transition.<i>(34)</i></li>
- </ul>
-
- <p>Any and all of these configuration constants can be changed
- at compile time with #define directives. Look at
- <code>tos/lib/tossim/sim_csma.h</code>.</p>
-
- <p>Because the radio connectivity graph can be scripted, you
- can easily store topologies in files and then load the
- file. Alternatively, you can store a topology as a script.
- For example, this script will load a file which specifies each
- link in the graph as a line with three values, the source, the
- destination, and the gain, e.g.:
-
- <pre>
-1 2 -54.0
- </pre>
-
- means that when 1 transmits 2 hears it at -54 dBm. Create a file <code>topo.txt</code>
- that looks like this:</p>
-
- <pre>
-1 2 -54.0
-2 1 -55.0
-1 3 -60.0
-3 1 -60.0
-2 3 -64.0
-3 2 -64.0
- </pre>
-
- <p>This script will read such a file:
-
- <pre>
->>> f = open("topo.txt", "r")
->>> lines = f.readlines()
->>> for line in lines:
-... s = line.split()
-... if (len(s) > 0):
-... print " ", s[0], " ", s[1], " ", s[2];
-... r.add(int(s[0]), int(s[1]), float(s[2]))
- </pre></p>
-
-
- <p>Now, when a node transmits a packet, other nodes will hear it.
- This is a complete script for simulating packet transmission with
- RadioCountToLedsC. Save it as a file <code>test.py</code>:</p>
- <pre>
-from TOSSIM import *
-import sys
-
-t = Tossim([])
-r = t.radio()
-f = open("topo.txt", "r")
-
-lines = f.readlines()
-for line in lines:
- s = line.split()
- if (len(s) > 0):
- print " ", s[0], " ", s[1], " ", s[2];
- r.add(int(s[0]), int(s[1]), float(s[2]))
-
-t.addChannel("RadioCountToLedsC", sys.stdout)
-t.addChannel("Boot", sys.stdout)
-
-noise = open("meyer-heavy.txt", "r")
-lines = noise.readlines()
-for line in lines:
- str = line.strip()
- if (str != ""):
- val = int(str)
- for i in range(1, 4):
- t.getNode(i).addNoiseTraceReading(val)
-
-for i in range(1, 4):
- print "Creating noise model for ",i;
- t.getNode(i).createNoiseModel()
-
-t.getNode(1).bootAtTime(100001);
-t.getNode(2).bootAtTime(800008);
-t.getNode(3).bootAtTime(1800009);
-
-for i in range(0, 100):
- t.runNextEvent()
- </pre>
-
- <p>Run it by typing <code>python test.py</code>. You should see
- output that looks like this:</p>
-
- <pre>
-1 2 -54.0
-2 1 -55.0
-1 3 -60.0
-3 1 -60.0
-2 3 -64.0
-3 2 -64.0
-DEBUG (1): Application booted.
-DEBUG (1): Application booted again.
-DEBUG (1): Application booted a third time.
-DEBUG (2): Application booted.
-DEBUG (2): Application booted again.
-DEBUG (2): Application booted a third time.
-DEBUG (3): Application booted.
-DEBUG (3): Application booted again.
-DEBUG (3): Application booted a third time.
-DEBUG (1): RadioCountToLedsC: timer fired, counter is 1.
-DEBUG (1): RadioCountToLedsC: packet sent.
-DEBUG (2): RadioCountToLedsC: timer fired, counter is 1.
-DEBUG (2): RadioCountToLedsC: packet sent.
-DEBUG (3): RadioCountToLedsC: timer fired, counter is 1.
-DEBUG (3): RadioCountToLedsC: packet sent.
-DEBUG (1): Received packet of length 2.
-DEBUG (3): Received packet of length 2.
-DEBUG (2): Received packet of length 2.
-DEBUG (1): RadioCountToLedsC: timer fired, counter is 2.
-DEBUG (1): RadioCountToLedsC: packet sent.
-DEBUG (2): RadioCountToLedsC: timer fired, counter is 2.
-DEBUG (2): RadioCountToLedsC: packet sent.
-DEBUG (3): RadioCountToLedsC: timer fired, counter is 2.
-DEBUG (3): RadioCountToLedsC: packet sent.
-DEBUG (1): Received packet of length 2.
- </pre>
-
- <p>If you set node's clear channel assessment to be at -110dBm,
- then nodes will never transmit, as noise and interference never
- drop this low. You'll see something like this:</p>
- <pre>
-1 2 -54.0
-2 1 -55.0
-1 3 -60.0
-3 1 -60.0
-2 3 -64.0
-3 2 -64.0
-DEBUG (1): Application booted.
-DEBUG (1): Application booted again.
-DEBUG (1): Application booted a third time.
-DEBUG (2): Application booted.
-DEBUG (2): Application booted again.
-DEBUG (2): Application booted a third time.
-DEBUG (3): Application booted.
-DEBUG (3): Application booted again.
-DEBUG (3): Application booted a third time.
-DEBUG (1): RadioCountToLedsC: timer fired, counter is 1.
-DEBUG (1): RadioCountToLedsC: packet sent.
-DEBUG (2): RadioCountToLedsC: timer fired, counter is 1.
-DEBUG (2): RadioCountToLedsC: packet sent.
-DEBUG (3): RadioCountToLedsC: timer fired, counter is 1.
-DEBUG (3): RadioCountToLedsC: packet sent.
-DEBUG (1): RadioCountToLedsC: timer fired, counter is 2.
-DEBUG (2): RadioCountToLedsC: timer fired, counter is 2.
-DEBUG (3): RadioCountToLedsC: timer fired, counter is 2.
- </pre>
-
- <p>Because the nodes backoff perpetually, they never transmit
- the packet and so subsequent attempts to send fail. Although
- it only takes a few simulation events to reach the first timer
- firings, it takes many simulation events (approximately 4000)
- to reach the second timer firings. This is because the nodes
- have MAC backoff events. If you want to simulate in terms of
- time, rather than events, you can always do something like
- this, which simulates 5 seconds from the first node boot:</p>
-
- <pre>
-t.runNextEvent();
-time = t.time()
-while (time + 50000000000 > t.time()):
- t.runNextEvent()
-</pre>
-
- <p>TOSSIM allows you to specify a network topology in terms of
- gain. However, this raises the problem of coming up with a
- topology. There are two approaches you can take. The first is
- to take data from a real world network and input this into
- TOSSIM. The second is to generate it from applying a
- theoretical propagation model to a physical layout. The standard
- file format is
-
- <pre>
-gain src dest g
- </pre>
-
- where each statement is on a separate line.
- The <i>gain</i> statement defines a propagation
- gain <i>g</i> when <i>src</i> transmits to <i>dest</i>. This
- is a snippet of python code that will parse this file
- format:
-
- <pre>
-f = open("15-15-tight-mica2-grid.txt", "r")
-
-lines = f.readlines()
-for line in lines:
- s = line.split()
- if (len(s) > 0):
- if (s[0] == "gain"):
- r.add(int(s[1]), int(s[2]), float(s[3]))
- </pre></p>
-
- <p>TOSSIM has a tool for the second option of generating a
- network topology using a theoretical propagation model. The
- tool is written in Java and is
- <code>net.tinyos.sim.PropagationModel</code>. The tool takes a
- single command line parameter, the name of a configuration
- file, e.g.:</p>
-
- <pre>
-java net.tinyos.sim.PropagationModel config.txt
- </pre>
-
- <p>The format of a configuration file is beyond the scope of
- this document: the tool has its own <A
- HREF="usc-topologies.html">documentation</A>. TOSSIM has a few sample configuration
- files generated from the tool in
- <code>tos/lib/tossim/topologies</code>.
- Note that the tool uses random numbers,
- these configuration files can generate multiple different
- network topologies. Network topology files generated from the
- tool follow the same format as <code>15-15-tight-mica2-grid.txt</code>.
- If you have topologies measured from real networks, we would love
- to include them in the TOSSIM distribution.</p>
-
- <h1>Variables</h1>
-
- <p>TOSSIM allows you to inspect variables in a running TinyOS
- program. Currently, you can only inspect basic types. For
- example, you can't look at fields of structs, but you can look
- at state variables.</p>
-
- <p>When you compile TOSSIM, the make system generates a large
- XML file that contains a lot of information about the TinyOS
- program, including every component variable and its type. If
- you want to examine the state of your program, then you need
- to give TOSSIM this information so it can parse all of the
- variables properly. You do this by instantiating a Python
- object that parses the XML file to extract all of the relevant
- information. You have to import the Python support package for
- TOSSIM to do this. First, set your PYTHONPATH environment variable
- to point to <tt>tinyos-2.x/support/sdk/python</tt>. This tells
- Python where to find the TOSSIM packages. Then, in an interpreter
- type this:</p>
-
- <pre>
-from tinyos.tossim.TossimApp import *
-
-n = NescApp()
- </pre>
-
- <p>Instantiating a <code>NescApp</code> can take quite a while:
- Python has to parse through megabytes of XML. So be patient
- (you only have to do it once). NescApp has two optional
- arguments. The first is the name of the application being
- loaded. The second is the XML file to load. The default for
- the latter is <code>app.xml</code>, which is the name of the file
- that the make system generates. The default for the former is
- "Unknown App." So this code behaves identically to that
- above:</p>
-
- <pre>
-from tinyos.tossim.TossimApp import *
-
-n = NescApp("Unknown App", "app.xml")
- </pre>
-
- <p>You fetch a list of variables from a NescApp object by
- calling the function <code>variables</code> on the field
- <code>variables</code>:</p>
-
- <pre>
-vars = n.variables.variables()
- </pre>
-
- <p>To enable variable inspection, you pass this list to a
- Tossim object when you instantiate it:</p>
-
- <pre>
-t = Tossim(vars)
- </pre>
-
- <p>The TOSSIM object now knows the names, sizes, and types of
- all of the variables in the TinyOS application. This
- information allows the TOSSIM support code to take C variables
- and properly tranform them into Python variables. This
- currently only works for simple types: if a component declares
- a structure, you can't access its fields. But let's say we
- want to read the counter in <code>RadioCountToLedsC</code>. Since each mote
- in the network has its own instance of the variable, we need
- to fetch it from a specific mote:</p>
-
- <pre>
-m = t.getNode(0)
-v = m.getVariable("RadioCountToLedsC.counter")
- </pre>
-
- <p>The name of a variable is usually <i>C.V</i>, where
- <i>C</i> is the component name and <i>V</i> is the variable.
- In the case of generic components, the name is <i>C.N.V</i>,
- where <i>N</i> is an integer that describes which instance.
- Unfortunately, there is currently no easy way to know what
- <i>N</i> is from nesC source, so you have to root through
- <code>app.c</code> in order to know.</p>
-
- <p>Once you have a variable object (<code>v</code> in the above
- code), you can fetch its value with the <code>getData()</code>
- function:</p>
-
- <pre>
-counter = v.getData()
- </pre>
-
- <p>Because <code>getData()</code> transforms the underlying C type
- into a Python type, you can then use its return value in
- Python expressions. For example, this script will start a
- simulation of five nodes and run it until node 0's counter
- reaches 10:</p>
-
- <pre>
-from sys import *
-from random import *
-from TOSSIM import *
-from tinyos.tossim.TossimApp import *
-
-n = NescApp()
-t = Tossim(n.variables.variables())
-r = t.radio()
-
-f = open("topo.txt", "r")
-lines = f.readlines()
-for line in lines:
- s = line.split()
- if (len(s) > 0):
- if (s[0] == "gain"):
- r.add(int(s[1]), int(s[2]), float(s[3]))
-
-noise = open("meyer-heavy.txt", "r")
-lines = noise.readlines()
-for line in lines:
- str = line.strip()
- if (str != ""):
- val = int(str)
- for i in range(0, 4):
- t.getNode(i).addNoiseTraceReading(val)
-
-for i in range (0, 4):
- t.getNode(i).createNoiseModel()
- t.getNode(i).bootAtTime(i * 2351217 + 23542399)
-
-m = t.getNode(0)
-v = m.getVariable("RadioCountToLedsC.counter")
-
-
-
-while (v.getData() < 10):
- t.runNextEvent()
-
-print "Counter variable at node 0 reached 10."
- </pre>
-
- <p>The TOSSIM <A
- HREF="../../../tos/lib/tossim/examples">examples</A>
- subdirectory also has an example script, named
- <code>variables.py</code>.</p>
-
- <h1>Injecting Packets</h1>
-
- <p>TOSSIM allows you to dynamically inject packets into a
- network. Packets can be scheduled to arrive at any time. If a
- packet is scheduled to arrive in the past, then it arrives
- immediately. Injected packets circumvent the radio stack: it is
- possible for a node to receive an injected packet while it is in
- the midst of receiving a packet from another node over its
- radio.</p>
-
- <p>TinyOS 2.0 has support for building Python packet objects.
- Just like the standard Java toolchain, you can build a packet
- class based on a C structure. The packet class gives you a full
- set of packet field mutators and accessors. If an application
- has a packet format, you can generate a packet class for it,
- instantiate packet objects, set their fields, and have nodes
- receive them.</p>
-
- <p>The <code>RadioCountToLeds</code> application Makefile has an
- example of how to do this. First, it adds the Python class as a
- dependency for building the application. Whenever you compile
- the app, if the Python class doesn't exist, make will build it
- for you:</p>
-
- <pre>
-BUILD_EXTRA_DEPS = RadioCountMsg.py RadioCountMsg.class
- </pre>
-
- <p>The Makefile also tells make how to generate RadioCountMsg.py:</p>
-
- <pre>
-RadioCountMsg.py: RadioCountToLeds.h
- mig python -target=$(PLATFORM) $(CFLAGS) -python-classname=RadioCountMsg RadioCountToLeds.h RadioCountMsg -o $@
- </pre>
-
- <p>The rule says to generate RadioCountMsg.py by calling mig
- with the python parameter. The Makefile also has rules on how
- to build Java class, but that's not important for TOSSIM. Since
- we've been using RadioCountToLeds so far, the Python class
- should be there already.</p>
-
- <p>RadioCountMsg.py defines a packet format, but this packet is
- contained in the data payload of another format. If a node is
- sending a <code>RadioCountMsg</code> over AM, then the <code>RadioCountMsg</code>
- structure is put into the AM payload, and might look something
- like this:</p>
-
- <center>
- <table BORDER=1>
- <TR>
- <TD WIDTH=100 BGCOLOR="#ffa0a0">AM Header</TD>
- <TD WIDTH=100 BGCOLOR="white">RadioCountMsg</TD>
- <TD WIDTH=100 BGCOLOR="#ffa0a0">AM Footer</TD>
- </TR>
- </table>
- </center>
-
- <p>If it is sending it over a routing protocol. the packet is
- put in the routing payload, and might look something like this:</p>
-
- <center>
- <table BORDER=1>
- <TR>
- <TD WIDTH=100 BGCOLOR="#ffa0a0">AM Header</TD>
- <TD WIDTH=100 BGCOLOR="#00a0ff">Routing Header</TD>
- <TD WIDTH=100 BGCOLOR="white">RadioCountMsg</TD>
- <TD WIDTH=100 BGCOLOR="#ffa0a0">AM Footer</TD>
- </TR>
- </table>
- </center>
-
- <p>If you want to send a <code>RadioCountMsg</code> to a node, then you need
- to decide how to deliver it. In the simple AM case, you place
- the <code>RadioCountMsg</code> structure in a basic AM packet. In the routing
- case, you put it in a routing packet, which you then put inside
- an AM packet. We'll only deal with the simple AM case here.</p>
-
- <p>To get an AM packet which you can inject into TOSSIM, you
- call the <code>newPacket</code> function on a Tossim object. The
- returned object has the standard expected AM fields:
- <i>destination</i>, <i>length</i>, <i>type</i>, and <i>data</i>,
- as well as <i>strength</i>.</p>
-
- <p>To include support for a packet format, you must import
- it. For example, to include <code>RadioCountMsg</code>, you have to import
- it:</p>
-
- <pre>
-from RadioCountMsg import *
- </pre>
-
- <p>This snippet of code, for example, creates a <code>RadioCountMsg</code>,
- sets its counter to 7, creates an AM packet, stores the
- <code>RadioCountMsg</code> in the AM packet, and configures the AM packet so
- it will be received properly (destination and type):</p>
-
- <pre>
-from RadioCountMsg import *
-
-msg = RadioCountMsg()
-msg.set_counter(7);
-pkt = t.newPacket();
-pkt.setData(msg.data)
-pkt.setType(msg.get_amType())
-pkt.setDestination(0)
- </pre>
-
-
- <p>The variable <code>pkt</code> is now an Active Message of the AM
- type of <code>RadioCountMsg</code> with a destination of 0 that contains a
- <code>RadioCountMsg</code> with a counter of 7. You can deliver this packet
- to a node with the <code>deliver</code> function. The
- <code>deliver</code> function takes two parameters, the destination
- node and the time to deliver:</p>
-
- <pre>
-pkt.deliver(0, t.time() + 3)
- </pre>
-
- <p>This call delivers pkt to node 0 at the current simulation
- time plus 3 ticks (e.g., 3ns). There is also a
- <code>deliverNow</code>, which has no time parameter. Note that if
- the destination of <code>pkt</code> had been set to 1, then the
- TinyOS application would not receive the packet, as it was
- delivered to node 0.</p>
-
- <p>Taken all together, the following script starts a simulation,
- configures the topology based on topo.txt, and delivers a packet
- to node 0. It can also be found as <code>packets.py</code> in the TOSSIM <A
- HREF="../../../tos/lib/tossim/examples/">examples</A>
- subdirectory.
-
- <pre>
-import sys
-from TOSSIM import *
-from RadioCountMsg import *
-
-t = Tossim([])
-m = t.mac();
-r = t.radio();
-
-t.addChannel("RadioCountToLedsC", sys.stdout);
-t.addChannel("LedsC", sys.stdout);
-
-for i in range(0, 2):
- m = t.getNode(i);
- m.bootAtTime((31 + t.ticksPerSecond() / 10) * i + 1);
-
-f = open("topo.txt", "r")
-lines = f.readlines()
-for line in lines:
- s = line.split()
- if (len(s) > 0):
- if (s[0] == "gain"):
- r.add(int(s[1]), int(s[2]), float(s[3]))
-
-noise = open("meyer-heavy.txt", "r")
-lines = noise.readlines()
-for line in lines:
- str = line.strip()
- if (str != ""):
- val = int(str)
- for i in range(0, 4):
- t.getNode(i).addNoiseTraceReading(val)
-
-for i in range (0, 4):
- t.getNode(i).createNoiseModel()
-
-for i in range(0, 60):
- t.runNextEvent();
-
-msg = RadioCountMsg()
-msg.set_counter(7);
-pkt = t.newPacket();
-pkt.setData(msg.data)
-pkt.setType(msg.get_amType())
-pkt.setDestination(0)
-
-print "Delivering " + str(msg) + " to 0 at " + str(t.time() + 3);
-pkt.deliver(0, t.time() + 3)
-
-
-for i in range(0, 20):
- t.runNextEvent();
- </pre>
-
-
- <h1>C++</h1>
-
- <p>Python is very useful because it is succinct, easy to write,
- and can be used interactively. Interpretation, however, has a
- significant cost: a Python/C transition on every event is a
- significant cost (around 100%, so it runs at half the
- speed). Additionally, it's often useful to step through code
- with a standard debugger. TOSSIM also has support for C++, so
- that it can be useful in these circumstances. Because many of
- the Python interfaces are merely wrappers around C++ objects,
- much of the scripting stays the same. The two major exceptions
- are inspecting variables and injecting packets.</p>
-
- <p>In a C++ TOSSIM, there is no variable inspection. While it is
- possible to request memory regions and cast them to the expected
- structures, currently there is no good and simple way to do
- so. The Python support goes through several steps in order to
- convert variables into Python types, and this gets in the way of
- C++. However, as the purpose of C++ is usually to run high
- performance simulations (in which inspecting variables is a big
- cost) or debugging (when you have a debugger), this generally
- isn't a big problem.</p>
-
- <p>There is a C++ <code>Packet</code> class, which the Python
- version is a simple wrapper around. In order to inject packets
- in C++, however, you must build C support for a packet type and
- manually build the packet. There currently is no support in mig
- with which to generate C/C++ packet structures, and since most
- packets are nx_struct types, they cannot be parsed by
- C/C++. Furthermore, as many of the fields are nx types, they are
- big endian, while x86 processors are little endian. Still, if you
- want to deliver a packet through C++, you can do so.</p>
-
- <p>Usually, the C++ and Python versions of a program look pretty
- similar. For example (note that this program will use a lot of RAM
- and take a long time to start due to its noise models):</p>
-
- <table WIDTH=800>
-
- <TR><TD WIDTH=400><TD WIDTH=400></TR>
- <TR><TD ALIGN=CENTER><b>Python</b></TD><TD ALIGN=CENTER><b>C++</b></TD></TR>
- <TR>
- <TD VALIGN=TOP>
- <PRE>
-import TOSSIM
-import sys
-import random
-
-from RadioCountMsg import *
-
-t = TOSSIM.Tossim([])
-r = t.radio();
-
-for i in range(0, 999):
- m = t.getNode(i);
- m.bootAtTime(5000003 * i + 1);
-
- for j in range (0, 2):
- if (j != i):
- r.add(i, j, -50.0);
-
- # Create random noise stream
- for j in range (0, 500):
- m.addNoiseTraceReading(int(random.random() * 20) - 70);
- m.createNoiseModel()
-
-for i in range(0, 1000000):
- t.runNextEvent();
- </PRE>
- </TD>
- <TD VALIGN=TOP>
- <PRE>
-#include <tossim.h>
-#include <stdlib.h>
-
-
-int main() {
- Tossim* t = new Tossim(NULL);
- Radio* r = t->radio();
-
- for (int i = 0; i < 999; i++) {
- Mote* m = t->getNode(i);
- m->bootAtTime(5000003 * i + 1);
- for (int j = 0; j < 2; j++) {
- if (i != j) {
- r->add(i, j, -50.0);
- }
- }
- for (int j = 0; j < 500; j++) {
- m->addNoiseTraceReading((char)(drand48() * 20) - 70);
- }
- m->createNoiseModel();
- }
-
-
- for (int i = 0; i < 1000000; i++) {
- t->runNextEvent();
- }
-}
- </PRE>
- </TD>
- </TR>
- </TABLE>
-
- <p>To compile a C++ TOSSIM, you have to compile the top-level
- driver program (e.g, the one shown above) and link it against
- TOSSIM. Usually the easiest way to do this is to link it against
- the TOSSIM objects rather than the shared library. Often, it's
- useful to have a separate Makefile to do this with. E.g.,
- <code>Makefile.Driver</code>:</p>
-
- <pre>
-all:
- make micaz sim
- g++ -g -c -o Driver.o Driver.c -I../../tos/lib/tossim/
- g++ -o Driver Driver.o build/micaz/tossim.o build/micaz/sim.o build/micaz/c-support.o
- </pre>
-
-
- <h2>Using gdb</h2>
-
- <p>Since Driver is a C++ program, you can use gdb on it to
- step through your TinyOS code, inspect variables, set
- breakpoints, and do everything else you can normally do.
- Unfortunately, as gdb is designed for C and not nesC, the
- component model of nesC means that a single command can have multiple
-providers; referring to a specific command requires specifying the component,
-interface, and command. For example, to break on entry to the <tt>redOff</tt>
-command of the <tt>Leds</tt> interface of <tt>LedsC</tt>, one must type:
-
-<pre>
-$ gdb Driver
-GNU gdb Red Hat Linux (6.0post-0.20040223.19rh)
-Copyright 2004 Free Software Foundation, Inc.
-GDB is free software, covered by the GNU General Public License, and you are
-welcome to change it and/or distribute copies of it under certain conditions.
-Type "show copying" to see the conditions.
-There is absolutely no warranty for GDB. Type "show warranty" for details.
-This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1".
-
-(gdb) break *LedsP$Leds$led0Toggle
-Breakpoint 1 at 0x804f184: file LedsP.nc, line 73.
-</pre>
-
-<p>nesC translates component names to C names using
-$. $ is a legal but almost-never-used character in some versions
-of C, so nesC prohibits it and uses it internally. The leading
-* is necessary so dbg can parse the $s. With the above
-breakpoint set, gdb will break whenever a mote toggles led0.</p>
-
-<p>Variables have similar names. For example, to inspect the packet
-of RadioCountToLedsC in the RadioCountToLeds application,</p>
-
-<pre>
-(gdb) print RadioCountToLedsC$packet
-$1 = {{header = {{data = ""}, {data = ""}, {data = ""}, {data = ""}, {
- data = ""}}, data = {{data = ""} <repeats 28 times>}, footer = {{
- data = ""}, {data = ""}}, metadata = {{data = ""}, {data = ""}, {
- data = ""}, {data = ""}, {data = ""}}} <repeats 1000 times>}
-</pre>
-
-<p>For those who know gdb very well, you'll recognize this as a print
-of an array, rather than a single variable: there are more than 1000
-instances of the message_t struct. This is because TOSSIM simulates
-many motes; rather than there being a single RadioCountToLedsC$packet,
-there is one for every node. To print the packet of a specific node, you
-have to index into the array. This, for example, will print the variable
-for node 6:</p>
-
-<pre>
-(gdb) print RadioCountToLedsC$packet[6]
-$2 = {header = {{data = ""}, {data = ""}, {data = ""}, {data = ""}, {
- data = ""}}, data = {{data = ""} <repeats 28 times>}, footer = {{
- data = ""}, {data = ""}}, metadata = {{data = ""}, {data = ""}, {
- data = ""}, {data = ""}, {data = ""}}}
-</pre>
-
-<p>If you want to print out the variable for the node TOSSIM is currently
-simulating, you can do this:</p>
-
-<pre>
-(gdb) print RadioCountToLedsC$counter[sim_node()]
-$4 = 0
-</pre>
-
-<p>You can also set watchpoints (although, as to be expected, they are
-<i>slow</i>:</p>
-
-<pre>
-(gdb) watch CpmModelC$receiving[23]
-Hardware watchpoint 2: CpmModelC$receiving[23]
-</pre>
-
-<p>This variable happens to be an internal variable in the
-packet-level network simulation, which keeps track of whether
-the radio thinks it is receiving a packet. So setting the
-above watchpoint will cause gdb to break whenever
-node 23 starts receiving a packet or returns to searching
-for packet preambles.</p>
-
-<p>Generic components add another wrinkle. Since they use
-a code-copying approach, each instance of a generic has its
-own separate functions and variables (this is mostly due to the
-fact that you can pass types to them). Take, for example,
-<code>AMQueueImplP</code>, which is used in both the radio
-AM stack and the serial AM stack. If you use gdb on an
-application that uses both serial and radio communication and
-try to break on its Send.send, you'll see an error:</p>
-
-<pre>
-(gdb) break *AMQueueImplP$Send$send
-No symbol "AMQueueImplP$Send$send" in current context.
-</pre>
-
-<p>nesC gives each generic a unique number. So if you
-have an application in which there is a single copy
-of AMQueueImplP, its name will actually be AMQueueImplP$0.
-For example, in RadioCountToLeds, this will work:</p>
-
-<pre>
-(gdb) break *AMQueueImplP$0$Send$send
-Breakpoint 5 at 0x8051b29: file AMQueueImplP.nc, line 79.
-</pre>
-
-<p>If you have multiple instances of a generic in a
-program, there is unfortunately no easy way to figure out each one's
-name besides looking at the source code or stepping into them.
-E.g., if you application uses serial and radio communication,
-knowing which stack has AMQueueImpl$0 and which has AMQueueImplP$1
-requires either stepping through their send operation or looking
-at their <code>app.c</code> files.
-</p>
-
-<h1>Conclusions</h1>
-
-This lesson introduced the basics of the TOSSIM simulator. It showed
-you how to configure a network, how to run a simulation, how to
-inspect variables, how to inject packets, and how to compile with C++.
-
-<center>
-<p>< <b><a href="lesson10.html">Previous Lesson</a></b> | <b><a
- href="index.html">Top</a></b> | <b><a href="lesson12.html">Next
-Lesson </a> ></b>
- </center>
-</p>
-
-
- <A name="appendix"><H1>Appendix A: Troubleshooting TOSSIM compilation</H1></A>
-
- <p>TOSSIM is a C/C++ shared library with an optional
- Python translation layer. Almost all of the problems
- encountered in compiling TOSSIM are due to C linking
- issues. If you don't know what a linker is (or have never
- linked a C program), then chances are the rest of this
- appendix is going to be cryptic and
- incomprehensible. You're best off starting with learning
- about <A
- HREF="http://en.wikipedia.org/wiki/Linker">linkers</A>, <A
- HREF="http://www.iecc.com/linker/linker01.html">why they
- are needed</A>, and how you <A
- HREF="http://www.linuxjournal.com/article/6463">use the
- gcc/g++ compilers</A> to link code.</p>
-
-
- <p>Generally, when compiling TOSSIM using <tt>make micaz sim</tt>,
- one of four things can go wrong:</p>
-
- <ol>
- <li>You are using Cygwin but the <tt>sim</tt> compilation option
- can't figure this out.</li>
-
- <li>You do not have the needed Python support installed.</li>
-
- <li>You have Python support installed, but the make
- system can't find it.</li>
-
- <li>You have Python support installed, but it turns out to
- be incompatible with TOSSIM.</li>
-
- <li>You have a variant of gcc/g++ installed that
- expects slightly different compilation options than the
- normal installation.</li>
- </ol>
-
- <p>We'll visit each in turn.</p>
-
- <h2>You are using Cygwin but the <tt>sim</tt> compilation option
- can't figure this out</h2>
-
- <p>It turns out that the Cygwin and Linux versions of gcc/g++
- have different command-line flags and require different options
- to compile TOSSIM properly. For example, telling the Linux
- compiler to build a library requires <tt>-fPIC</tt> while
- the Cygwin is <tt>-fpic</tt>. If you're using Cygwin and
- you see the output look like this:
-
- <pre>
- ncc -c -shared -fPIC -o build/micaz/sim.o ...
- </pre>
-
- rather than
-
- <pre>
- ncc -c -DUSE_DL_IMPORT -fpic -o build/micaz/sim.o ...
- </pre>
-
- then you have encountered this problem. The problem
- occurs because Cygwin installations do not have a
- consistent naming scheme, and so it's difficult for the
- compilation toolchain to always figure out whether it's
- under Linux or Cygwin.</p>
-
- <p><b>Symptom:</b> You're running cygwin but you see the
- <tt>-fPIC</tt> rather than <tt>-fpic</tt> option being
- passed to the compiler.</b></p>
-
- <p><b>Solution:</b> Explicitly set the OSTYPE environment
- variable to be <tt>cygwin</tt> either in your <tt>.bashrc</tt>
- or when you compile. For example, in bash:</p>
-
- <pre>
-$ OSTYPE=cygwin make micaz sim
- </pre>
-
- or in tcsh
-
- <pre>
-$ setenv OSTYPE cygwin
-$ make micaz sim
- </pre>
-
- <p>Note that often this problem occurs in addition to
- other ones, due to using a nonstandard Cygwin
- installation. So you might have more problems to track
- down.</p>
-
- <h2>You do not have the needed Python support installed</h2>
-
- <p>If when you compile you see lots of errors such as
- "undefined reference to" or "Python.h: No such file or
- directory" then this might be your problem. It is a
- subcase of the more general problem of TOSSIM not being
- able to find needed libraries and files.</p>
-
- <p>Compiling Python scripting support requires that you
- have certain Python development libraries installed. First, check
- that you have Python installed:</p>
-
- <pre>
-$ python -V
-Python 2.4.2
- </pre>
-
- <p>In the above example, the system has Python 2.4.2. If
- you see "command not found" then you do not have Python
- installed. You'll need to track down an RPM and install
- it. TOSSIM has been tested with Python versions 2.3 and
- 2.4. You can install other versions, but there's no
- assurance things will work.</p>
-
- <p>In addition to the Python interpreter itself, you need
- the libraries and files for Python development. This is
- essentially a set of header files and shared libraries. If
- you have the <tt>locate</tt> command, you can type
- <tt>locate libpython</tt>, or if you don't, you can look
- in <tt>/lib</tt>, <tt>/usr/lib</tt> and
- <tt>/usr/local/lib</tt>. You're looking for a file with a
- name such as <tt>libpython2.4.so</tt> and a file named
- <tt>Python.h</tt>. If you can't find these files, then you
- need to install a <tt>python-devel</tt> package.</p>
-
-
- <p><b>Symptom:</b> Compilation can't find critical files
- such as the Python interpreter, <tt>Python.h</tt> or a
- Python shared library, and searching your filesystem shows
- that you don't have them.</p>
-
- <p><b>Solution:</b> Installed the needed files from Python
- and/or Python development RPMS.</p>
-
- <p>If you have all of the needed files, but are still
- getting errors such as "undefined reference" or "Python.h:
- No such file or directory", then you have the next
- problem: they're on your filesystem, but TOSSIM can't find
- them.</p>
-
- <h2>You have Python support installed, but the make
- system can't find it</h2>
-
- <p>You've found libpython and Python.h, but when TOSSIM compiles
- it says that it can't find one or both of them. If it can't
- find Python.h then compilation will fail pretty early, as g++ won't
- be able to compile the Python glue code. If it can't find the python
- library, then compilation will fail at linking, and you'll see
- errors along the lines of "undefined reference to __Py...". You
- need to point the make system at the right place.</p>
-
- <p>Open up <tt>support/make/sim.extra</tt>. If the make
- system can't find Python.h, then chances are it isn't in
- one of the standard places (e.g., /usr/include). You need to tell
- the make system to look in the directory where Python.h is with
- a <tt>-I</tt> option. At the top of sim.extra, under the PFLAGS entry,
- add
-
- <pre>
-CFLAGS += -I/path
- </pre>
-
- where <tt>/path</tt> is the path of the directory where Python.h
- lives. For example, if it is in <tt>/opt/python/include</tt>,
- then add <tt>CFLAGS += -I/opt/python/include</tt>.</p>
-
- <p>If the make system can't find the python library for
- linking (causing "undefined reference") error messages,
- then you need to make sure the make system can find
- it. The sim.extra file uses two variables to find the
- library: <tt>PYDIR</tt> and <tt>PYTHON_VERSION</tt>. It
- looks for a file named libpython$(PYTHON_VERSION).so. So
- if you have Python 2.4 installed, make sure that
- PYTHON_VERSION is 2.4 (be sure to use no spaces!) and if
- 2.3, make sure it is 2.3.</p>
-
- <p>Usually the Python library is found in
- <tt>/usr/lib</tt>. If it isn't there, then you will need
- to modify the <tt>PLATFORM_LIB_FLAGS</tt> variable. The
- -L flag tells gcc in what directories to look for
- libraries. So if libpython2.4.so is in
- <tt>/opt/python/lib</tt>, then add
- <tt>-L/opt/python/lib</tt> to the
- <tt>PLATFORM_LIB_FLAGS</tt>. Note that there are three
- different versions of this variable, depending on what OS
- you're using. Be sure to modify the correct one (or be
- paranoid and modify all three).
-
-
- <p><b>Symptom:</b> You've verified that you have the
- needed Python files and libraries, but compilation is
- still saying that it can't link to them ("undefined
- reference") or can't find them ("cannot find -lpython2.4").</p>
-
- <p><b>Solution:</b> Change the sim.extra file to point to
- the correct directories using -L and -I flags.</p>
-
- <h2>You have Python support installed, but it turns out to
- be incompatible with TOSSIM.</h2>
-
- <p><b>Symptom:</b> You get a "This python version requires
- to use swig with the -classic option" error message.</p>
-
- <p><b>Solution:</b> Install SWIG and regenerate Python
- support with the sing-generate script in
- <tt>tos/lib/tossim</tt>, or install a different version of
- Python.</p>
-
- <h2>You have a variant of gcc/g++ installed that
- expects slightly different compilation options than the
- normal installation.</h2>
-
- <p><b>Symptom:</b> g++ complains that it cannot find
- main() when you are compiling the shared library
- ("undefined reference to `_WinMain@16'").</p>
-
- <p><b>Solution:</b> There are two possible solutions.
- The first is to include a dummy main(), as described
- in this <A HREF="http://mail.millennium.berkeley.edu/pipermail/tinyos-help/2006-December/021719.html">tinyos-help posting.</A> The
- second is to add the -shared option, as described in
- this <A HREF="http://curl.haxx.se/mail/archive-2003-01/0056.html">web page</A>.
-
- <p>Hopefully, these solutions worked and you can get back
- to <A HREF="#compiling">compiling</A>, If not, then you
- should email tinyos-help.</p>
-</center>
-