Skip to content
This repository was archived by the owner on Mar 28, 2023. It is now read-only.

Cape Fox Simulator

Marcin Szczodrak edited this page Feb 24, 2014 · 53 revisions

Cape Fox is a simulator build as an extension to TinyOS's TOSSIM simulator.

Besides TOSSIM functionality, Cape Fox also provides:

  • support for simulation of the MAC protocol source code implementation
  • integration with TOSSIM Live extension
  • interface for inserting real-time sensor data or sensor data traces
  • interface for reading actuation signals
  • support for execution of the Fennec Fox protocol stack

The Cape Fox source code comes with Fennec Fox. It is located in src/support/cape. Examples of the simulator control scripts can be found in sim directory.

Testing and debugging embedded firmware running on a network of motes can be time consuming. Many software design decisions and implementation issues can be evaluated and corrected while running the code in a simulator. The following steps show how to test a Swift Fox program collecting sensor measurements over a multihop mesh network at a single mote (called sink) that forwards all the sensor measurements to a PC. The figure below shows a sketch of the target system that we want to implement.

Target System Architecture

The system presented of the figure above can be programmed in Swift Fox with the following code:

configuration sense { telosSens(5, 1024)
                        ctp(5)
                        csmaca(5, 0, 10, 10, 1, 1, 1)
                        # always ON, delay_after_receive, backoff, min_backoff
                        # ack, cca, crc
                        cc2420(26, 31, 1, 1)
                        # channel, power, ack, crc
                        }

state test { sense }

start test

In this program we assume that the Fennec Fox application module collects measurements from the sensors that are on the TelosB motes. At the picture above, the sink mote is the mote attached through the serial cable to a PC, and it has address ID 5. Sensors' measurements are collected every 1024ms, and they are forwarded to the sink mote (ID 5) over the CTP data collection network protocol. Because TelosB motes use CC2420 radio chip for the wireless communication, the Swift Fox sense network process uses the cc2420 radio driver, sending packets on the channel 26, with the maximum (31) signal strength (0dBm).

Before we program and test the code that is running on the motes, we may want to test it in a simulator. In Cape Fox simulator we will be able to test the top three layers of the stack, i.e. the application, network and MAC layers. This means that the same code that will run on these layers in the final hardware implementation is the same code that we can test in the simulator. In the sensor measurements collection application there are two differences between the actual hardware code implementation and the code that we can test in the simulator:

  1. we simulate the wireless radio communication
  2. we simulate sensor measurements

That is, every part of the Wireless Sensor Network or a Cyber-Physical System that is interacting with the surrounding environment must be simulated. Thus, we simulated wireless communication and simulate sensor measurements.

First, let's look at the example of the Swift Fox code above and its modification to enable Cape Fox simulation:

configuration sense { telosSens(5, 1024)
                        ctp(5)
                        csmaca(5, 0, 10, 10, 1, 1, 1)
                        # always ON, delay_after_receive, backoff, min_backoff
                        # ack, cca, crc
                        cape()
                        }

state test { sense }

start test

Noticed the difference on the radio layer of the sense network process: instead of using the cc2420() radio driver, the process runs on the cape() radio driver, a code that simulated wireless communication. Switching the radio driver from the one specific to the target platform architecture into the cape() is all it is needed to let the Swift Fox program to be successfully tested in the Cape Fox simulator. Thus, now we can imagine that instead of running actual wireless network, we simulate a network as the one shown on the figure below.

Simulated System Architecture

Another difference between the actual system deployment and the simulated network scenario is the sensor data generation. In the real deployment, the embedded firmware accesses the hardware sensors to get the measurements, while in the simulated system, the sensor measurements are fed into the simulator.

First, let's look into the implementation of the telosSens application. The source code of this application is stored in src/Application/tests/TelosbSensors. The top configuration file of this application is TelosbSensorsC.nc. Looking into this file we see the following lines of code:

#ifndef TOSSIM 

components new SensirionSht11C();
TelosbSensorsP.ReadHumidity -> SensirionSht11C.Humidity;
TelosbSensorsP.ReadTemperature -> SensirionSht11C.Temperature;

components new HamamatsuS10871TsrC();
TelosbSensorsP.ReadLight -> HamamatsuS10871TsrC;

#else

components new CapeInputC() as CapeHumidityC;
TelosbSensorsP.ReadHumidity -> CapeHumidityC.Read16;

components new CapeInputC() as CapeTemperatureC;
TelosbSensorsP.ReadTemperature -> CapeTemperatureC.Read16;

components new CapeInputC() as CapeLightC;
TelosbSensorsP.ReadLight -> CapeLightC.Read16;

#endif

From this code we learn the following. First, the application's implementation module TelosbSensorsP.nc is using ReadHumidity, ReadTemperature, and ReadLight interfaces to connect to the sensors. Each of those interfaces is of type Read<uint16_t>. The #ifndef TOSSIM and #else ... #endif are include guards that specify when the application module should wire to actual sensors' drivers and when it should wire into simulated sensor drivers. When the code is not compiled into TOSSIM simulator, the module wires to SensirionSht11C and HamamatsuS10871TsrC sensors' drivers. When the code is compiled for simulation, the TelosbSensorsP.nc wires into generic simulated Cape Fox data input streams CapeInputC. Then, during the simulation, the CapeInputC retrieves simulated measurements from the Cape Fox simulator.

Next we look into the steps necessary to compile and run a Cape Fox simulation. Let's say that the above Swift Fox program (the one with cape() as the radio driver) is saved in a file called telosB_cape.sfp. Then, we can compile this program as follows:

user@linux $ sfc telosB_cape.sfp
user@linux $ fennec micaz cape

The second line follows the TOSSIM compilation, which targets micaz architecture.

Once the program is compiled we can run the simulation. In the src/support/cape/sim directory there is a set of Python scripts examples that can run a simulation. These Python scripts follow the TOSSIM simulation model, i.e. the scripts use the same mechanisms to generate the simulated network topology, create wireless communication noise traces, and to inspect each simulated mote status.

When entering the src/support/cape/sim directory for the first time, run make to create various network topologies and noise traces:

user@linux $ pwd 
.../src/support/cape/sim
user@linux $ make

One can create its own simulated network topologies by following TOSSIM tutorial and using the tools to build network topologies for TOSSIM.

Simulation Examples

Fast Simulation. First we look how to run a fast Cape Fox simulation. In the first example, we use the following Swift Fox program:

uint16_t dest_node = 2
uint16_t src_node = 0xFFFF

configuration count {
                        counter(10, 1024, src_node, dest_node)
                        ctp(dest_node)
                        csmaca(dest_node, 0, 10, 10, 1, 1, 1)
                        # always ON, delay_after_receive, backoff, min_backoff
                        # ack, cca, crc
                        cape()
}

state simple_sim { count }

start simple_sim

This code is a modified version of the code example presented above, with the telosSens application substituted with counter Fennec Fox application module. This application instead of sending sensor measurements to the destination node, it sends a message with a counter that is increased by 1 after each transmission. If we save this program in counter_cape.sfp file, then we can compile it as follows:

user@linux $ sfc counter_cape.sfp
user@linux $ fennec micaz cape

Next, we go to the src/support/cape/sim directory and run

user@linux $ ./fastSim.py

Which will run the Cape Fox simulator, following the fastSim.py script. In this script we see that the simulation is set with the following parameters:

		self.dbg_channels = ["Application", "Network"]
		self.cape.setTopologyFile("topos/4/linkgain.out")
		self.cape.setNoiseFile("noise/casino.txt")
		self.cape.setSimulationTime(200)

These parameters say that the simulator should record all dbg() statements from the tested embedded firmware that are send to "Application" and "Network". For example, in src/Application/tests/Counter/CounterP.nc we see for example the following lines of code:

	dbg("Application", "Counter SplitControl.start()");

	dbg("Application", "Counter starting delay: %d", send_delay);
	dbg("Application", "Counter starting src: %d  dest: %d",
		call CounterParams.get_src(), call CounterParams.get_dest());

The fastSim.py also sets the network topology to a 2-by-2 grid, with total of 4 motes. The radio noise simulation follows the model from the casino.txt file, and the simulation runs for 200 seconds.

After running the simulation:

user@linux $ ./fastSim.py

we will find dbg statements in src/support/cape/sim/results folder. A sample set of dbg logs may look like this:

         0 000 000 NODE (0000): ctpP SplitControl.start()
         0 000 000 NODE (0000): CtpRoutingEngineP StdControl.start()
         0 000 000 NODE (0000): ctpP SplitControl.start() - root: 2
         0 000 000 NODE (0000): Counter SplitControl.start()
         0 000 000 NODE (0000): Counter starting delay: 10240
         0 000 000 NODE (0000): Counter starting src: 65535  dest: 2
         0 020 000 NODE (0001): ctpP SplitControl.start()
         0 020 000 NODE (0001): CtpRoutingEngineP StdControl.start()
         0 020 000 NODE (0001): ctpP SplitControl.start() - root: 2
         0 020 000 NODE (0001): Counter SplitControl.start()
         0 020 000 NODE (0001): Counter starting delay: 10240
         0 020 000 NODE (0001): Counter starting src: 65535  dest: 2
         0 040 000 NODE (0002): ctpP SplitControl.start()
         0 040 000 NODE (0002): CtpRoutingEngineP StdControl.start()
         0 040 000 NODE (0002): ctpP SplitControl.start() - root: 2
         0 040 000 NODE (0002): Counter SplitControl.start()
         0 040 000 NODE (0002): Counter starting delay: 10240
         0 040 000 NODE (0002): Counter starting src: 65535  dest: 2
         0 060 000 NODE (0003): ctpP SplitControl.start()
         0 060 000 NODE (0003): CtpRoutingEngineP StdControl.start()
         0 060 000 NODE (0003): ctpP SplitControl.start() - root: 2
         0 060 000 NODE (0003): Counter SplitControl.start()
         0 060 000 NODE (0003): Counter starting delay: 10240
         0 060 000 NODE (0003): Counter starting src: 65535  dest: 2
         0 110 351 NODE (0000): Network CTP send beacon message

where the first column is the simulated time (< minutes > < seconds > < milliseconds >), followed by the mote address and followed by the dbg statement.

Real-Time Simulation. In this example we will run a real-time simulation. First, we will simulate a network with one mote attached to a PC. Then, we will enhance this example with a simulation that feeds sensor measurements.

We can reuse the Swift Fox code that collects sensors' measurements from TelosB motes. This program looked as follows:

configuration sense { telosSens(1, 1024)
                        ctp(1)
                        csmaca(1, 0, 10, 10, 1, 1, 1)
                        # always ON, delay_after_receive, backoff, min_backoff
                        # ack, cca, crc
                        cape()
                        }

state test { sense }

start test

After compiling this code, we can run a real-time simulation using the realTimeSim.py Python script.

In one window shell, we run the script as follows:

user@linux $ ./realTimeSim.py

This will run the simulation, with the simulate time advancing according to the real clock of the computer that runs the simulation. During the execution, all dbg channels that are enabled will be generating log statements in the results directory.

Because telosSens application sends messages over the serial connection, we can see those messages by running another Python script that connects to the TinyOS serial forwarder set up by the Cape Fox simulator. In the second window shell, we run UARTGateway.py Python script that follows TinyOS's API of connecting to Serial Forwarder:

user@linux $ ./UARTGateway.py

This will connect to the network port at the computer that runs the simulation. On this connection, the script will receive serial messages and parse them according to the message structure definition specified in the TelosbSensors.h TelosbSensors application. The following are examples of the output printed by the UARTGateway.py script:

1393206961.47 1 0 0 0 0
Message <TelosbMsg> 
  [seq=0x0]
  [src=0x2]
  [hum=0x0]
  [temp=0x0]
  [light=0x0]

1393206962.17 2 0 0 0 0
Message <TelosbMsg> 
  [seq=0x0]
  [src=0x3]
  [hum=0x0]
  [temp=0x0]
  [light=0x0]

In this example, Cape Fox simulated create a set of sensors' measurements. However, we can also set the sensors' measurements by ourselves. With realTimeSim.py script running in one window

user@linux $ ./realTimeSim.py

and UARTGateway.py script running in another window

user@linux $ ./UARTGateway.py

we open a third shell window to run a script that will send in real-time sensor' measurements into the running Cape Fox simulator.

The sensorDataInsert.py Python script shows an example of real-time data trace insert. The following code shows the steps when the sensors' measurements are sent to the simulator, and where the new sensor traces are generated:


        def run(self):
                self.__run = True
                while(self.__run):
                        for node_id in range(self.number_of_sensors):
                                try:
                                        msg_si = pack("!HHL", node_id, 0, self.hum)
                                        self.sock.send(msg_si)
                                        msg_si = pack("!HHL", node_id, 1, self.temp)
                                        self.sock.send(msg_si)
                                        msg_si = pack("!HHL", node_id, 2, self.light)
                                        self.sock.send(msg_si)
                                except:
                                        self.__run = False

                        time.sleep(self.sleep_time)
                        self.__updateSensorValue()


        def __updateSensorValue(self):
                self.hum = math.fabs(4 * math.sin(time.time() / 2))
                self.temp = math.fabs(16 * math.sin(time.time() * 2))
                self.light = math.fabs(32 * math.sin(time.time()))
                print "Humidity: %d  Temp: %d  Light: %d" % (self.hum, self.temp, self.light)

When we run this scrip:

user@linux $ ./sensorDataInsert.py

We should notice the change in the messages sent over the simulated serial interface. The UARTGateway.py script should now print messages with the sensors' measurements equal to the ones inserted into the simulator by the sensorDataInsert.py script.

1393207767.66 3 0 2 12 29
Message <TelosbMsg> 
  [seq=0x0]
  [src=0x0]
  [hum=0x0]
  [temp=0xc]
  [light=0x1d]

1393207767.66 0 0 0 12 29
Message <TelosbMsg> 
  [seq=0x0]
  [src=0x1]
  [hum=0x2]
  [temp=0xc]
  [light=0x1d]

The following figure summarized the Cape Fox architecture:

Cape Fox Simulator

Clone this wiki locally