|
|
home |
How to Add Support for Multiple Radios in SWANS |
I was recently asked how to extend
JiST/SWANS to support multiple radios. I will share my opinion on how I think
this could be done.
SWANS currently does not include a support for multiple radios. Therefore, one
has to introduce some changes. However, I think that it is not so hard to do in
SWANS. SWANS is written with a very well structured object oriented design and the
modules separation resembles the real networking stack a lot. Therefore, those
changes can be introduced pretty easily.
First, before delving into the code, one must answer two important design
questions:
1) Do you want the radios to interfere with each other or not? Specifically, it
means: do you want one radio to sense the signal (and noise) of the other radio
from the other type.
2) Do you want to make a routing decisions (over which radio to send which
message) at the application level (above NetIP and above Transport) or at the routing level?
A notice: My proposed design tries to minimize the number of changes to the existing
SWANS stack design, without (or with minimal) breaking the current abstractions
of the networking stack. The proposed design below is not complete. It is more
of a high level design suggestions and one still needs to think more thoroughly
of some points. I hope you will find it useful.
Restructuring Radios:
Usually, one would like multiple radios not to interfere with each other. This mimics the behavior of multiple channels. Usually, radios on different channels do to interfere with each other, since the PHY hardware can separate the signals on different channels quite effectively. So lets talk first about non-interfering radios.
There are two problems we need to tackle:
1) delivering the signal from the radio on some channel only to the radios on
this channel;
2) taking care of movements of radios. If a radio that belonging to channel X of
some node moves (the node moves), the other radio should move with it as well.
I suggest to have a separate radio object to represent every radio. Those two
physical radios will be combined into one virtual radio, which will be placed on
the field (kind of a field node object). The virtual radio will represent the node towards the field object.
Movement: when the Field moves the node, it will move the virtual radio.
Signal transmission: there are a number of ways to implement it. I
suggest the following way: every physical radio is also connected directly to
the Field. When the physical radio wants to transmit a signal it does so
directly on the field, as it is done now. The function
void FieldInterface.transmit(RadioInfo srcInfo, Message msg,
long duration) will have an additional
parameter: channel. When the field gets a signal sent on some
channel, it passes the channel parameter to spatial.visitTransmit,
which passes it also to the SpatialTransmitVisitor.visitTransmit().
Inside the function Spatial.SpatialTransmitVisitor.visitTransmit() the
RadioInfo dstInfo is the virtual radio. In this place we simply iterate over
all physical radios inside the virtual radio and take only those for which their
channel equals the srcInfo channel. On those destination radios we
call the original SpatialTransmitVisitor.visitTransmit function
(without the channel parameter).
Some code details:
1) In the driver class, when you assemble the node in function createNode() you should create 3 radios. Two physical radios and one virtual radio:
// you may create 2 different radioInfoShared object, each for every channel.
Lets say on one channel you have a powerful radio transmitting
// at power level 20dBm and the second channel radio transmitting at power level
15dBm.
RadioNoise phyRadio1 = new
RadioNoiseAdditive(i, radioInfoShared1);
RadioNoise phyRadio2 = new
RadioNoiseAdditive(i, radioInfoShared2);
RadioNoise virtRadio = new VirtualRadio(i,
radioInfoShared, phyRadio1, phyRadio2);
// You add to the field only the virtual radio (the field will move only the
virtual radio), while you connect to every physical radio its own mac.
field.addRadio(virtRadio.getRadioInfo(), virtRadio.getProxy(), location);
field.startMobility(virtRadio.getRadioInfo().getUnique().getID());
// Here I connect every physical radio to the field and I will
demultiplex the signals based on the additional channel parameter (passed by the
transmit method).
// Another way would be to connect the virtual radio to the field and then to do
the demultiplexing inside the virtula radio object. This would introduce for my opinion more
abstractions breaking:
// you will need to add a channel parameter to the message and also make the
virtual radio expose a unified RadioInfoShared object to the field
// (with the largest power level for example. I found this to be more
hacky than the first solution).
phyRadio1.setFieldEntity(field.getProxy());
phyRadio2.setFieldEntity(field.getProxy());
// since every MAC is connected only to its own radio,
on one channel, this MAC will sense signals and noise only on this channel
phyRadio1.setMacEntity(mac1.getProxy());
phyRadio2.setMacEntity(mac2.getProxy());
mac1.setRadioEntity(radio1.getProxy());
mac2.setRadioEntity(radio2.getProxy());
byte intId =
net.addInterface(mac1.getProxy());
byte intId =
net.addInterface(mac2.getProxy());
mac1.setNetEntity(net.getProxy(), intId);
mac2.setNetEntity(net.getProxy(), intId);
net.setProtocolHandler(Constants.NET_PROTOCOL_HEARTBEAT,
app.getNetProxy());
app.setNetEntity(net.getProxy());
app.getAppProxy().run(null);
2) The VirtualRadio class will represent the physical radios towards the Field. Therefore, the VirtualRadio must have the same id as its physical radios.
3) In the Field object: as described above.
If you want to have interference between the radios of different channels, you still need one virtual radio for movements, but you do not need to demultiplex the signals with channels.
Routing Decision:
One option is to have two IP objects, with different IP addresses, each one connected to a separate MAC. This way we decide over which channel (interface) to send the message at the application level. This is like having two different interfaces in the real networking stack and deciding to which one to bind a socket to.
Another way is to have one IP layer and to make all decisions inside the
routing object. The IP is connected to two MACs, each one with different
interfaceId. The routing object gets the message and sends it back via function
NetIp.send(NetMessage.Ip msg, int
interfaceId, MacAddress nextHop) on the appropriate interfaceId. There is one
delicate point here thought: broadcast messages. Currently, all broadcast
messages (to NetAddress.ANY) are sent over
the first, default MAC. You will need to change the method
NetIp.sendIp(NetMessage.Ip msg) to direct the broadcast messages over the right
channel as well.
This design mimics the concept of virtual interface in Linux - you can have one
networking interface to the applications and it is connected below to two
physical interfaces. This is also called "bridge" in Lunix.
* I was assisted by Alex Kogan with the above multiple radios design.
Gabriel Kliot
12/31/2008