ReservationStatusNtf
notifications:
void sendReservationStatusNtf(ReservationReq msg, ReservationStatus status) {
send new ReservationStatusNtf(
recipient: msg.sender,
inReplyTo: msg.msgID,
to: msg.to,
from: addr,
status: status)
}
Now the hard work is done. We initialize our agent by registering the MAC service, looking up and subscribing to the PHYSICAL service (to transmit and receive PDUs), looking up our own address using the NODE_INFO service, and starting the
fsm
behavior:
AgentID phy
int addr
void setup() {
register Services.MAC
}
void startup() {
phy = agentForService(Services.PHYSICAL)
subscribe(phy)
subscribe(topic(phy, Physical.SNOOP))
add new OneShotBehavior({
def nodeInfo = agentForService(Services.NODE_INFO)
addr = get(nodeInfo, NodeInfoParam.address)
})
add(fsm)
}
Note that we subscribe to the
topic(phy, Physical.SNOOP)
in addition to
phy
. This allows us to snoop RTS/CTS PDUs destined for other nodes. Also note that the address lookup is performed in a
OneShotBehavior
to avoid having the agent to block while the node information agent is starting up.
Just like in the earlier MAC implementation, we have to respond to various requests defined by the MAC service specifications:
Message processRequest(Message msg) {
switch (msg) {
case ReservationReq:
if (msg.to == Address.BROADCAST || msg.to == addr)
return new RefuseRsp(msg, 'Reservation must have a destination node')
if (msg.duration <= 0 || msg.duration > maxReservationDuration)
return new RefuseRsp(msg, 'Bad reservation duration')
if (queue.size() >= MAX_QUEUE_LEN)
return new RefuseRsp(msg, 'Queue full')
queue.add(msg)
fsm.restart() // tell fsm to check queue, as it may block if empty
return new ReservationRsp(msg)
case ReservationCancelReq:
case ReservationAcceptReq:
case TxAckReq:
return new RefuseRsp(msg, 'Not supported')
}
return null
}
If we get a
ReservationReq
, we validate the attributes, add the request to our queue and return a
ReservationRsp
. For other requests that we do not support, we simply refuse them.
If we receive PDUs from the physical agent, they come as
RxFrameNtf
messages via the
processMessage()
method. For all PDUs with a protocol number that we use, we decode them. We trigger appropriate FSM events in response to RTS and CTS PDUs -– RX_RTS and RX_CTS events for PDUs destined to us, and SNOOP_RTS and SNOOP_CTS events for PDUs that we overhear:
void processMessage(Message msg) {
if (msg instanceof RxFrameNtf && msg.protocol == PROTOCOL) {
def rx = pdu.decode(msg.data)
def info = [from: msg.from, to: msg.to, duration: rx.duration/1000.0]
if (rx.type == RTS_PDU)
fsm.trigger(info.to == addr ? Event.RX_RTS : Event.SNOOP_RTS, info)
else if (rx.type == CTS_PDU)
fsm.trigger(info.to == addr ? Event.RX_CTS : Event.SNOOP_CTS, info)
}
}
Finally, we expose the parameters required by the MAC service specification:
List<Parameter> getParameterList() { // publish list of all exposed parameters
return allOf(MacParam)
}
final int reservationPayloadSize = 0 // read-only
final int ackPayloadSize = 0 // read-only
final float maxReservationDuration = 65.535 // read-only
boolean getChannelBusy() { // considered busy if fsm is not IDLE
return fsm.currentState.name != State.IDLE
}
float getRecommendedReservationDuration() { // recommended duration: one DATA packet
return get(phy, Physical.DATA, PhysicalChannelParam.frameDuration)
}
We are done! You can find the full listing of the
MySimpleHandshakeMac
agent in
Appendix D
(and also in the
samples
folder of your Unet simulator).
29.5. Testing our simple MAC with handshake
Let’s try out this MAC. The steps are similar to
Section 29.2
, but since the handshake requires MAC to be running on all nodes, you will have to fire up the 2-node network and replace the default CSMA MAC with
MySimpleHandshakeMac
on both nodes (node A and node B):
> container.kill mac
true
> container.add 'mac', new MySimpleHandshakeMac();
> mac
« MySimpleHandshakeMac »
[org.arl.unet.mac.MacParam]
ackPayloadSize ⤇ 0
maxReservationDuration ⤇ 65.535
recommendedReservationDuration ⤇ 0.7
reservationPayloadSize ⤇ 0
Since the handshaking involves exchange of PDUs between nodes, it is instructive to see the PDUs being exchanged by subscribing to
phy
. You can make a reservation request on node A:
> subscribe phy
> mac << new ReservationReq(to: 31, duration: 3.seconds)
ReservationRsp:AGREE
phy >> TxFrameStartNtf:INFORM[type:CONTROL txTime:3631928985 txDuration:950]
phy >> RxFrameStartNtf:INFORM[type:CONTROL rxTime:3634151681]
phy >> RxFrameNtf:INFORM[type:CONTROL from:31 to:232 protocol:4 rxTime:3634151681 (3 bytes)]
mac >> ReservationStatusNtf:INFORM[to:31 from:232 status:START]
mac >> ReservationStatusNtf:INFORM[to:31 from:232 status:END]
We see that a CTS is transmitted (
TxFrameStartNtf
), then a RTS is received from node B (
RxFrameStartNtf
and
RxFrameNtf
). The reservation starts as soon as the CTS is received, and it ends 3 seconds later. Exactly as we wanted!
Part VI: Simulating underwater networks
30. Writing simulation scripts
We have been using simulations throughout the handbook, to demonstrate and test commands, scripts and agents without having to set up a real Unet. But how exactly do we tell the simulator what we want to simulate?
30.1. Integrated development environment
We have used the 2-node network simulation (
bin/unet samples/2-node-network.groovy
) umpteen times, but how did the simulator know where the nodes were and what agents were running on each node? That information must have been in the
2-node-network.groovy
simulation script, so let’s take a look at that script next.
While we could open the script in our favorite editor directly, let’s instead use the Unet IDE included with UnetStack, as it provides development tools that we will be needing in our journey. To start the Unet IDE:
$ bin/unet sim
Simulator IDE: http://localhost:8080/
This should open the IDE in your default browser:
The IDE provides you with a fairly common 3-panel layout, with a file browser in the left panel, a simulation shell at the bottom, and a file editor occupying most of the window. At the top, you see several buttons. The key buttons to note are the
button that starts/stops simulations, the
Map
button that shows the current simulated nodes on a map, the 'Logs' button that allows you to view simulation logs, and a
Shells
dropdown that lists all the shells of simulated nodes. You can select any of the nodes from the list to connect to the shell of that node. The
Map
and
Shells
buttons are activated only once a simulation is running.
Load the
2-node-network.groovy
simulation script from the
samples
folder in the file editor. Then press
to run it.
You can either press the
button or type
sim.run 'samples/2-node-network.groovy'
to run the simulation from the simulation shell panel.
|
In the shell panel, you’ll see:
2-node network
--------------
Node A: tcp://localhost:1101, http://localhost:8081/
Node B: tcp://localhost:1102, http://localhost:8082/
To access node A shell, either control-click the URL for node A shell (displayed on the simulation shell) or select
Node A (232)
from the
Shells
dropdown menu. This will open the node A shell in a separate browser tab. Once you have access to the shells for your node, you are on familiar ground, as you have been working with numerous realtime simulations in previous chapters. Now, you can safely close the shell tab for now and go back to the IDE tab. The shell tab can be reopened anytime you want.
Next, try out the
Map
button, and you’ll see the 2 nodes in our simulation on a map:
This map doesn’t look like much, with just 2 nodes 1 km apart on a blue background. The 2-node network simulation isn’t geolocated, so the map doesn’t have much to show. Let’s stop this simulation by pressing the
button, and start the
scripts/mission2013-network.groovy
simulation instead.
You can either press the
button or type
sim.stop
in the simulation shell panel to stop the currently running simulation.
|
Now open the
Map
, and you’ll get a much nicer map of the network deployed in southern Singapore waters:
Clicking on each node shows some information about that node, and provides a link to opening that node’s shell (if it has a shell agent running). In case of mobile nodes ( Section 30.5 ), you’ll see the nodes moving on the map.
30.2. 2-node network
Now that we know how to use the IDE, let’s stop the mission2013 network simulation and reopen the 2-node network simulation in the file browser. Recall that we started off the previous section wanting to study the
2-node-network.groovy
simulation script in detail to see how it works. So let’s get down to it:
samples/2-node-network.groovy
:
import org.arl.fjage.* (1)
///////////////////////////////////////////////////////////////////////////////
// display documentation
println ''' (2)
2-node network
--------------
Node A: tcp://localhost:1101, http://localhost:8081/
Node B: tcp://localhost:1102, http://localhost:8082/
'''
///////////////////////////////////////////////////////////////////////////////
// simulator configuration
platform = RealTimePlatform (3)
// run the simulation forever
simulate { (4)
node 'A', location: [ 0.km, 0.km, -15.m], web: 8081, api: 1101, stack: "$home/etc/setup"
node 'B', location: [ 1.km, 0.km, -15.m], web: 8082, api: 1102, stack: "$home/etc/setup"
}
| 1 | Import classes needed in the simulation script. |
| 2 | Display documentation. |
| 3 | Tell the simulator that we want to run in realtime mode. |
| 4 | Describe the simulation specifying nodes names 'A' and 'B', their locations, web interface port numbers, API port numbers and the default network stack to load on each node. |
The simulation script is very simple. All it does is specify that we want to use the
RealTimePlatform
(since we want to run a realtime simulation), and then define the two nodes in the simulation. Node attributes such as node name, location, ports, and stack (agents to load) are specified when describing each node.
Let’s next take a look at the
setup.groovy
script that describes the stack to load on each node:
etc/setup.groovy
:
import org.arl.fjage.Agent
boolean loadAgentByClass(String name, String clazz) { (1)
try {
container.add name, Class.forName(clazz).newInstance()
return true
} catch (Exception ex) {
return false
}
}
boolean loadAgentByClass(String name, String... clazzes) { (2)
for (String clazz: clazzes) {
if (loadAgentByClass(name, clazz)) return true
}
return false
}
loadAgentByClass 'arp', 'org.arl.unet.addr.AddressResolution'
loadAgentByClass 'ranging', 'org.arl.unet.phy.Ranging'
loadAgentByClass 'mac', 'org.arl.unet.mac.CSMA'
loadAgentByClass 'uwlink', 'org.arl.unet.link.ECLink', 'org.arl.unet.link.ReliableLink' (3)
loadAgentByClass 'transport', 'org.arl.unet.transport.SWTransport'
loadAgentByClass 'router', 'org.arl.unet.net.Router'
loadAgentByClass 'rdp', 'org.arl.unet.net.RouteDiscoveryProtocol'
loadAgentByClass 'state', 'org.arl.unet.state.StateManager'
container.add 'remote', new org.arl.unet.remote.RemoteControl(cwd: new File(home, 'scripts'), enable: false)
container.add 'bbmon', new org.arl.unet.bb.BasebandSignalMonitor(new File(home, 'logs/signals-0.txt').path, 64)
| 1 | Helper function to load an agent given it’s class name. |
| 2 | Helper function to load an agent from a list of class names, picking the first available class. |
| 3 |
We use the second helper function to load
ECLink
if available (only premium stack), or
ReliableLink
as a fallback (available in basic stack).
|
While this script might look complicated, what it does is quite simple. It loads the standard agents in the network stack. The complicated bits in the script are mostly to handle errors, if certain agents are unavailable (e.g. agents from the premium stack). We could use a much simpler script to load the stack, if we wanted to avoid this complexity:
etc/setup.groovy
:
container.add 'arp', new org.arl.unet.addr.AddressResolution()
container.add 'ranging', new org.arl.unet.phy.Ranging()
container.add 'mac', new org.arl.unet.mac.CSMA()
container.add 'uwlink', new org.arl.unet.link.ReliableLink()
container.add 'transport', new org.arl.unet.transport.SWTransport()
container.add 'router', new org.arl.unet.net.Router()
container.add 'rdp', new org.arl.unet.net.RouteDiscoveryProtocol()
container.add 'state', new org.arl.unet.state.StateManager()
container.add 'remote', new org.arl.unet.remote.RemoteControl(cwd: new File(home, 'scripts'), enable: false)
container.add 'bbmon', new org.arl.unet.bb.BasebandSignalMonitor(new File(home, 'logs/signals-0.txt').path, 64)
This script just loads all the standard agents in the basic stack.
If you wanted to customize the stack in the simulation, you could specify a different script to setup the stack, or provide a closure directly when defining the simulation:
simulate {
node 'A', location: [ 0.km, 0.km, -15.m], web: 8081, api: 1101, stack: "$home/scripts/custom.groovy"
node 'B', location: [ 1.km, 0.km, -15.m], web: 8082, api: 1102, stack: {
// only load 3 agents on node B
container.add 'arp', new org.arl.unet.addr.AddressResolution()
container.add 'mac', new org.arl.unet.mac.CSMA()
container.add 'uwlink', new org.arl.unet.link.ReliableLink()
}
}
Recall that in
Section 28.2
, we developed our own
EchoDaemon.groovy
agent. If we wanted to preload it in our 2-node network simulation, we can add
container.add 'echo', new EchoDaemon()
in the
custom.groovy
script or directly in the closure shown above.
|
30.3. Netiquette 3-node network
The
2-node-network.groovy
script defined 2 nodes that were 1 km apart, but were not geolocated. Recall from
Section 5.6
that specifying a node origin allows us to geolocate the nodes on a map. The
netq-network.groovy
simulation script does this:
samples/netq-network.groovy
:
import org.arl.fjage.RealTimePlatform
///////////////////////////////////////////////////////////////////////////////
// display documentation
println '''
Netiquette 3-node network
-------------------------
Node A: tcp://localhost:1101, http://localhost:8081/
Node B: tcp://localhost:1102, http://localhost:8082/
Node C: tcp://localhost:1103, http://localhost:8083/
'''
///////////////////////////////////////////////////////////////////////////////
// simulator configuration
platform = RealTimePlatform // use real-time mode
origin = [1.216, 103.851] (1)
simulate {
node 'A', location: [121.m, 137.m, -10.m], web: 8081, api: 1101, stack: "$home/etc/setup"
node 'B', location: [160.m, -232.m, -15.m], web: 8082, api: 1102, stack: "$home/etc/setup"
node 'C', location: [651.m, 140.m, -5.m], web: 8083, api: 1103, stack: "$home/etc/setup"
}
| 1 |
The specified
origin
(latitude, longitude) applies to all nodes in the simulation.
|
Starting the simulation and opening the map shows the nodes on the map, since the origin allows the IDE to geolocate the nodes:
The
icon on the map marks the origin location.
30.4. Mission 2013 network
The simulation script is written in Groovy, so you can include complex logic in the script , if you wish. From this perspective, the
mission2013-network.groovy
script is instructive to look at:
samples/mission2013-network.groovy
:
import org.arl.fjage.RealTimePlatform
import org.arl.unet.sim.channels.Mission2013a
///////////////////////////////////////////////////////////////////////////////
// display documentation
println '''
MISSION 2013 network
--------------------
'''
Mission2013a.nodes.each { addr ->
println "Node $addr: tcp://localhost:${1100+addr}, http://localhost:${8000+addr}/"
}
///////////////////////////////////////////////////////////////////////////////
// simulator configuration
platform = RealTimePlatform // use real-time mode
channel = [ model: Mission2013a ] (1)
origin = [1.217, 103.743]
simulate {
Mission2013a.nodes.each { addr -> (2)
node "$addr", location: Mission2013a.nodeLocation[addr], web: 8000+addr, api: 1100+addr, stack: "$home/etc/setup"
}
}
| 1 |
The
channel
property of the simulation enables us to define details of the simulated physical channel for the network. We will learn more about simulating channels in
Chapter 32
.
|
| 2 |
Nodes can be created programatically by iterating over the list of nodes defined in the
Mission2013a
class.
|
The
Mission2013a
class contains information about the MISSION 2013 experiment. The
mission2013-network.groovy
simulation script uses this information to create simulated nodes at the correct locations, and to define a channel model based on measurements during that experiment.
30.5. Node mobility
Nodes in a simulation may be mobile (e.g. autonomous underwater vehicles). Such nodes have motion models associated with them, to provide appropriate mobility during the simulation:
// AUV-1 moving in a straight line at constant speed
def n1 = node 'AUV-1', location: [0, 0, 0], mobility: true
n1.motionModel = [speed: 1.mps, heading: 30.deg]
// AUV-2 moving in a circle (constant speed, constant turn rate)
def n2 = node 'AUV-2', location: [0, 0, 0], mobility: true
n2.motionModel = [speed: 1.mps, turnRate: 1.dps]
We can also define more complex motion models:
// AUV-3 moving in a lawnmower pattern
def n3 = node 'AUV-3', location: [-20.m, -150.m, 0], heading: 0.deg, mobility: true
n3.motionModel = MotionModel.lawnmower(speed: 1.mps, leg: 200.m, spacing: 20.m, legs: 10)
// AUV-4 moving as defined below, using time or duration
def n4 = node 'AUV-4', location: [-50.m, -50.m, 0], mobility: true
n4.motionModel = [
[time: 0.minutes, heading: 60.deg, speed: 1.mps],
[time: 3.minutes, turnRate: 2.dps, diveRate: 0.1.mps],
[time: 4.minutes, turnRate: 0.dps, diveRate: 0.mps],
[time: 7.minutes, turnRate: 2.dps ],
[time: 8.minutes, turnRate: 0.dps ],
[duration: 3.minutes, turnRate: 2.dps, diveRate: -0.1.mps],
[duration: 1.minute, turnRate: 0.dps, diveRate: 0.mps]
]
We can even combine motion models:
def n5 = node 'AUV-5', location: [-20.m, -150.m, 0], heading: 0.deg, mobility: true
// dive to 30m before starting survey
n5.motionModel = [
[duration: 5.minutes, speed: 1.mps, diveRate: 0.1.mps],
[diveRate: 0.mps]
]
// then do a lawnmower survey
n5.motionModel += MotionModel.lawnmower(speed: 1.mps, leg: 200.m, spacing: 20.m, legs: 10)
// finally, come back to the surface and stop
n5.motionModel += [
[duration: 5.minutes, speed: 1.mps, diveRate: -0.1.mps],
[diveRate: 0.mps, speed: 0.mps]
]
Let’s put AUVs 1-4 together into a single simulation script:
auv-network.groovy
import org.arl.fjage.RealTimePlatform
import org.arl.unet.sim.MotionModel
platform = RealTimePlatform
simulate {
def n1 = node 'AUV-1', location: [0, 0, 0], mobility: true
n1.motionModel = [speed: 1.mps, heading: 30.deg]
def n2 = node 'AUV-2', location: [0, 0, 0], mobility: true
n2.motionModel = [speed: 1.mps, turnRate: 1.dps]
def n3 = node 'AUV-3', location: [-20.m, -150.m, 0], heading: 0.deg, mobility: true
n3.motionModel = MotionModel.lawnmower(speed: 1.mps, leg: 200.m, spacing: 20.m, legs: 10)
def n4 = node 'AUV-4', location: [-50.m, -50.m, 0], mobility: true
n4.motionModel = [
[time: 0.minutes, heading: 60.deg, speed: 1.mps],
[time: 3.minutes, turnRate: 2.dps, diveRate: 0.1.mps],
[time: 4.minutes, turnRate: 0.dps, diveRate: 0.mps],
[time: 7.minutes, turnRate: 2.dps ],
[time: 8.minutes, turnRate: 0.dps ],
[duration: 3.minutes, turnRate: 2.dps, diveRate: -0.1.mps],
[duration: 1.minute, turnRate: 0.dps, diveRate: 0.mps]
]
}
Save this
auv-network.groovy
in your
scripts
folder and run it. Open the map, and watch your AUV nodes move!
31. Discrete event simulation
Running simulations in realtime gives us an experience which is very similar to working with a real Unet, as we have seen in earlier chapters. This is very useful when you want to interact with the network manually, through a shell. However, as a network designer or protocol developer, you may sometimes need to run simulations to see how the network performs over days or months, and maybe run many such simulations, each with slightly different network settings or configuration. Doing this with a realtime simulator is impractical, as a realtime simulation would take days or months to run. The Unet simulator can be run in a discrete event mode, where the waiting time between events is fast-forwarded to yield results worth hours or days of real time within minutes. In this chapter, we explore how to use the disrete event mode for simulation of protocol performance.
31.1. ALOHA performance analysis
The hello world of the networking world is the ALOHA MAC protocol. The protocol is very simple: transmit a frame as soon as data arrives, without worrying about whether any other node is transmitting. While this behavior is straightforward to describe, simulating it accurately requires some thought.
Let’s say we want to simulate a network with ALOHA MAC. On each node, we expect data to arrive randomly, with a known average arrival rate. The total number of data "chunks" arriving per unit time across the network is termed as offered load . As soon as a data chunk arrives, the node transmits it to a randomly chosen destination node (other than itself). The number of successfully delivered data chunks per unit time, across the entire network, is called the throughput . We are interested to study how the throughput varies as a function of offered load.
ALOHA has been extensively studied in literature, and its theoretical performance is well known. In order to simulate a network that can be compared against theory, we need to ensure that our simulation matches the assumptions made in the theoretical derivations:
-
The random arrival process follows a Poisson distribution.
-
If two frames arrive at a receiver with some overlap in time, they collide and are lost. Neither frame can be successfully decoded.
-
Each node is half-duplex, i.e., it cannot receive a frame while it is transmitting.
-
No frames are lost due to noise or channel effects such as multipath.
-
There is no propagation delay between nodes.
Assumptions 2 and 4 together form a model called the protocol channel model. We tell the simulator to adopt this model:
channel.model = ProtocolChannelModel
Underwater acoustic modems are usually half-duplex, and the default modem model in the simulator is the
HalfDuplexModem
, so we shouldn’t need to do anything special for assumption 3. However, the
HalfDuplexModem
is smart enough to delay a transmission if another frame is being transmitted or received by the node, to avoid losing the other frame. While this is usually a good thing to do, it will violate assumption 3 and give us results that don’t agree with theory. To match the theoretical behavior, we have to stop any ongoing transmission or reception, when new data arrives for transmission:
phy << new ClearReq() // stop ongoing transmission/reception
phy << new TxFrameReq(to: dst, type: DATA) // transmit a data frame to dst
The offered load and throughput are usually normalized by the number of frames that can be supported by the channel per unit time. By setting the frame duration to be one second, we ensure that the normalization factor is 1 (one packet can be transitted per second without collision). To do this, we set up the simulated modem to have no header/preamble overheads, and exactly 1 second worth of data that it can carry in a frame:
modem.dataRate = [2400, 2400].bps // arbitrary data rate
modem.frameLength = [2400/8, 2400/8].bytes // 1 second worth of data per frame
modem.headerLength = 0 // no overhead from header
modem.preambleDuration = 0 // no overhead from preamble
modem.txDelay = 0 // don't simulate hardware delays
You can read more about
modem
models in
Section 32.1
.
|
A Poisson process (assumption 1) is easily simulated using the
PoissonBehavior
available in fjåge. Assumption 5 can also be easily met by placing all nodes at the same location.
Now let’s put a first version of our script together to simulate a 4-node ALOHA network:
import org.arl.fjage.*
import org.arl.unet.*
import org.arl.unet.phy.*
import org.arl.unet.sim.*
import org.arl.unet.sim.channels.*
channel.model = ProtocolChannelModel // use the protocol channel model
modem.dataRate = [2400, 2400].bps // arbitrary data rate
modem.frameLength = [2400/8, 2400/8].bytes // 1 second worth of data per frame
modem.headerLength = 0 // no overhead from header
modem.preambleDuration = 0 // no overhead from preamble
modem.txDelay = 0 // don't simulate hardware delays
def nodes = 1..4 // list with 4 nodes
def load = 0.2 // offered load to simulate
simulate 2.hours, { // simulate 2 hours of elapsed time
nodes.each { myAddr ->
def myNode = node "${myAddr}", address: myAddr, location: [0, 0, 0]
myNode.startup = { // startup script to run on each node
def phy = agentForService(Services.PHYSICAL)
def arrivalRate = load/nodes.size() // arrival rate per node
add new PoissonBehavior((long)(1000/arrivalRate), { // avg time between events in ms
def dst = rnditem(nodes-myAddr) // choose destination randomly (excluding self)
phy << new ClearReq()
phy << new TxFrameReq(to: dst, type: Physical.DATA)
})
}
}
}
// display collected statistics
println([trace.txCount, trace.rxCount, trace.offeredLoad, trace.throughput])
The script is easy to understand. In a 2-hour long simulation, we iterate over the list of nodes, and create each node at the origin. Each node adds a
PoissonBehavior
to generate random traffic at a rate corresponding to the offered
load
setting. The parameter of the Poisson behavior is the average time between events in milliseconds, which we compute based on the arrival rate. The destination for each transmission is randomly chosen from the list of nodes excluding the transmitting node. Once the simulation is completed, statistics are printed. The
trace
object is automatically defined by the simulator to collect typically required statistics.
The
rnditem(list)
function allows a random item to be chosen from a list. Other convenience functions related to random number generation include
rnd(min, max)
which generates a uniformly distributed random number between
min
and
max
, and
rndint(n)
which generates a uniformly distributed random number between
0
and
n-1
.
|
Open Unet IDE (
bin/unet sim
), create a new simulation script in the
scripts
folder, copy this code in, and run it. Within a few seconds, you should see the results:
[1459, 984, 0.2026, 0.1367]
1 simulation completed in 2.817 seconds
Since this is a Monte-Carlo simulation driven by a random number generator, the statistics you see will be similar, but not identical. A total of 1459 frames were transmitted, and 984 of them were successfully received. The measured offered load was 0.2026, and the throughput was 0.1367.
Hang on a minute! The simulation was meant to run for 2 hours, but it finished in less than 3 seconds!!
That’s because we ran the simulation in a discrete event simulation mode (it is the default mode, if we don’t set
platform = RealTimePlatform
). We could have explicitly set it (
platform = DiscreteEventSimulator
), if we wanted. Now that we can run hours worth of simulations in seconds, we can go ahead and measure ALOHA throughput at various load settings:
import org.arl.fjage.*
import org.arl.unet.*
import org.arl.unet.phy.*
import org.arl.unet.sim.*
import org.arl.unet.sim.channels.*
println '''
Pure ALOHA simulation
=====================
TX Count\tRX Count\tOffered Load\tThroughput
--------\t--------\t------------\t----------'''
channel.model = ProtocolChannelModel // use the protocol channel model
modem.dataRate = [2400, 2400].bps // arbitrary data rate
modem.frameLength = [2400/8, 2400/8].bytes // 1 second worth of data per frame
modem.headerLength = 0 // no overhead from header
modem.preambleDuration = 0 // no overhead from preamble
modem.txDelay = 0 // don't simulate hardware delays
def nodes = 1..4 // list with 4 nodes
trace.warmup = 15.minutes // collect statistics after a while
for (def load = 0.1; load <= 1.5; load += 0.1) {
simulate 2.hours, { // simulate 2 hours of elapsed time
nodes.each { myAddr ->
def myNode = node "${myAddr}", address: myAddr, location: [0, 0, 0]
myNode.startup = { // startup script to run on each node
def phy = agentForService(Services.PHYSICAL)
def arrivalRate = load/nodes.size() // arrival rate per node
add new PoissonBehavior((long)(1000/arrivalRate), { // avg time between events in ms
def dst = rnditem(nodes-myAddr) // choose destination randomly (excluding self)
phy << new ClearReq()
phy << new TxFrameReq(to: dst, type: Physical.DATA)
})
}
}
} // simulate
// tabulate collected statistics
println sprintf('%6d\t\t%6d\t\t%7.3f\t\t%7.3f',
[trace.txCount, trace.rxCount, trace.offeredLoad, trace.throughput])
} // for
Other than the pretty printing to tabulate the output, you’ll see that we have added a
trace.warmup
time. This is to ensure that we only collect statistics after the simulation has reached steady state (in this case, after 15 minutes of simulation time).
A slightly beautified copy of the above code is available in the
samples/aloha.groovy
script. You can either run that, or run the above code. You should see something like this output:
Pure ALOHA simulation
=====================
TX Count RX Count Offered Load Throughput
-------- -------- ------------ ----------
614 525 0.068 0.058
1228 962 0.137 0.107
1871 1249 0.209 0.139
2480 1407 0.277 0.156
3093 1535 0.347 0.171
3759 1616 0.421 0.180
4273 1665 0.479 0.183
4971 1599 0.558 0.178
5540 1605 0.622 0.178
6256 1532 0.702 0.170
6940 1375 0.783 0.153
7338 1407 0.826 0.156
7992 1338 0.904 0.149
8598 1282 0.972 0.142
9394 1048 1.062 0.116
15 simulations completed in 102.494 seconds
As expected from the ALOHA protocol, the maximum throughput of about 0.18 is reached at an offered load of about 0.5. We plot this against the theoretical ALOHA performance curve (
y = x exp(-2x)
) in
Figure 16
.
31.2. Logs, traces and statistics
When a simulation is run, usually two files are produced.
31.2.1. Log file
The
logs/log-0.txt
file contains detailed text logs from the Java logging framework. Your agents and simulation scripts may log additional information to this file using
log.info()
or
log.fine()
methods. This provides a flexible and customizable way to log events in your simulation for later analysis.
A typical extract of the log file is shown below:
1569242004546|INFO|org.arl.unet.nodeinfo.NodeInfo@558:setAddress|Node address changed to 1
1569242004548|INFO|Script1@558:invoke|Created static node 1 (1) @ [0, 0, 0]
1569242004552|INFO|org.arl.unet.nodeinfo.NodeInfo@558:setAddress|Node address changed to 2
1569242004553|INFO|Script1@558:invoke|Created static node 2 (2) @ [0, 0, 0]
1569242004553|INFO|org.arl.unet.nodeinfo.NodeInfo@558:setAddress|Node address changed to 3
1569242004554|INFO|Script1@558:invoke|Created static node 3 (3) @ [0, 0, 0]
1569242004554|INFO|org.arl.unet.nodeinfo.NodeInfo@558:setAddress|Node address changed to 4
1569242004554|INFO|Script1@558:invoke|Created static node 4 (4) @ [0, 0, 0]
1569242004555|INFO|Script1@558:invoke| --- BEGIN SIMULATION #1 ---
0|INFO|org.arl.unet.sim.SimulationContainer@558:init|Initializing agents...
0|INFO|org.arl.unet.sim.SimulationAgent/1@561:invoke|Loading simulator : SimulationAgent
0|INFO|org.arl.unet.nodeinfo.NodeInfo/1@560:init|Loading agent node v3.0
0|INFO|org.arl.unet.sim.HalfDuplexModem/1@559:init|Loading agent phy v3.0
:
:
5673|INFO|org.arl.unet.sim.SimulationAgent/4@570:call|TxFrameNtf:INFORM[type:DATA txTime:2066947222]
6511|INFO|org.arl.unet.sim.SimulationAgent/3@567:call|TxFrameNtf:INFORM[type:DATA txTime:1157370743]
10919|INFO|org.arl.unet.sim.SimulationAgent/4@570:call|TxFrameNtf:INFORM[type:DATA txTime:2072193222
Note that the timestamp (first column) changes from the clock time to discrete event time when the simulation starts, and switches back to clock time when the simulation ends.
31.3. Trace files
31.4. JSON trace file
Since UnetStack 3.3.0, the default trace file is stored in a rich JSON format.
When running a simulation, a JSON trace file
logs/trace.json
is automatically generated. This file contains a detailed trace for every event in the network stack, on each node. You can even enable trace file generation on real modems and other Unet nodes (using
EventTracer.enable()
), and later combine the traces from multiple nodes to analyze network protocol operation and performance.
A small extract from a typical trace file is shown below:
{"version": "1.0","group":"EventTrace","events":[
{"group":"SIMULATION 1","events":[
{"time":1617877446718,"component":"arp::org.arl.unet.addr.AddressResolution/B","threadID":"0bfb305d-4920-4df0-af95-5282b048b5ec","stimulus":{"clazz":"org.arl.unet.addr.AddressAllocReq","messageID":"0bfb305d-4920-4df0-af95-5282b048b5ec","performative":"REQUEST","sender":"node","recipient":"arp"},"response":{"clazz":"org.arl.unet.addr.AddressAllocRsp","messageID":"3e421e28-89ca-44ec-bc65-16cc404d3703","performative":"INFORM","recipient":"node"}},
{"time":1617877446718,"component":"arp::org.arl.unet.addr.AddressResolution/A","threadID":"04f5b1b9-9178-4e27-aae7-e2a0c4ffcd89","stimulus":{"clazz":"org.arl.unet.addr.AddressAllocReq","messageID":"04f5b1b9-9178-4e27-aae7-e2a0c4ffcd89","performative":"REQUEST","sender":"node","recipient":"arp"},"response":{"clazz":"org.arl.unet.addr.AddressAllocRsp","messageID":"e0fe806d-625d-4261-b24c-6655b90cc06a","performative":"INFORM","recipient":"node"}},
:
:
]}
]}
The trace is organized into a hierarchy of groups, each describing a simulation run or the execution of specific commands. A group consists of a sequence of events, with each event providing information on time of event, component (agent running on a node), thread ID, stimulus and response. The stimulus is typically a message received from another agent, and response a message sent to another agent. The thread ID ties multiple events, potentially across multiple agents and nodes, but with the same root cause together.
An experimental automated trace analysis tool can be used to produce sequence diagrams from JSON trace files.
Integrating the event tracing framework into your own agents is simple. All you need to do is to wrap messages that you generate in response to a stimulus with a
trace()
call. Some examples:
send trace(stimulus, new DatagramDeliveryNtf(stimulus))
request trace(stimulus, req), timeout
31.5. Legacy trace file
The legacy trace file format is similar to the NS2 NAM trace. Since UnetStack 3.3.0, this format is no longer the default, but can be enabled easily in your simulation script if you need it:
trace.open(new File(home, 'logs/trace.nam'))
The trace file contains information about all packet creation, transmission, reception and drop events. It also contains details of node motion. The tracer also computes basic statistics including queued packet count, transmitted packet count, received packet count, dropped packet count, offered load, actual load, average packet latency and normalized throughput.  Lines starting with
+
denote packet arrival into the transmit queue. Lines starting with
-
log packet removal from the transmit queue, i.e., transmission. Lines starting with
r
denote packet reception (or overhearing). Lines starting with
d
log packet drops, and specify a reason for the drop.
CLEAR
indicates a packet transmission/reception abort due to a
ClearReq
request.
COLLISION
indicates that the packet was dropped because the node was busy receiving or transmitting another packet.
BAD_FRAME
indicates that the packet was corrupted (possibly due to interference from a colliding packet).
For more details on the trace file format, see NS2 NAM trace format .
| While the trace provides a simple file format and collects statistics for you, the events monitored by the legacy trace are currently limited to PHYSICAL service events. If you need to monitor or log events from other agents, you would want to use the JSON trace file. |
32. Modems and channel models
In Chapter 30 and Chapter 31 , we learned how to simulate Unets with many nodes, using a single computer. In fact, we have been using simulations throughout the handbook. In a simulated Unet, most agents are identical to the agents running in nodes on a real Unet. However, since the communication in a simulated Unet does not use a modem in water, we need a model for how a real modem behaves.
32.1. Modem models
A modem usually provides the PHYSICAL service (and optionally, the BASEBAND service). In a simulation, we need a simulated modem to provide these services with behaviors as close to reality as possible. The agent that models the modem behavior is called a
modem model
. The Unet simulator comes with the
HalfDuplexModem
model that can be customized to emulate various underwater acoustic modems.
Since the
HalfDuplexModem
is the default modem model, we don’t need to explicitly specify it. But if we wanted to, we could do it in the simulation script:
modem = [ model: org.arl.unet.sim.HalfDuplexModem ]
| Some modem manufacturers may provide you with a modem model that more accurately matches the behaviors of their modem. This can be useful when simulating networks with modems from specific vendors. |
We can specify properties that control the behavior of the modem. You can either specify them while declaring the modem:
modem = [
model: org.arl.unet.sim.HalfDuplexModem,
dataRate: [800.bps, 2400.bps],
frameLength: [16.bytes, 64.bytes],
powerLevel: [0.dB, -10.dB],
preambleDuration: 5.ms
]
or later, by assigning the properties individually:
modem.dataRate = [800.bps, 2400.bps]
modem.frameLength = [16.bytes, 64.bytes]
modem.powerLevel = [0.dB, -10.dB]
modem.preambleDuration = 5.ms
Indexed properties
dataRate
,
frameLength
,
maxFrameLength
,
janus
and
powerLevel
are specified as 3-tuples, with the first entry corresponding to the CONTROL channel, the second for the DATA channel, and the third for the JANUS frame type. If JANUS support is not required, the properties may be specified as 2-tuples.
If you run a realtime simulation with
modem.dataRate = [800.bps, 2400.bps]
, connect to a node’s shell, and then type in
phy[CONTROL].dataRate
, you may be surpirsed to see a much lower data rate (436 bps in this example). The 800 bps is the signaling rate, and exludes overheads from preamble and headers. The 436 bps is the effective average data rate across the frame, and includes all overheads.
|
Short descriptions and default values of these properties are shown below:
-
modem.dataRate = [256, 1024, 80]
-
Communication link data rate (bps).
-
modem.frameLength = [24, 64, 8]
-
Default frame length (bytes).
-
modem.maxFrameLength = [128, 512, 128]
-
Maximum frame length (bytes)
-
modem.janus = [false, false, true]
-
Support for JANUS frames.
-
modem.powerLevel = [-10, -10, -10]
-
Transmit power level (dB re
refPowerLevel
).
In addition to the above indexed properties, several other properties control the modem behavior:
-
modem.signalPowerLevel = -10
-
Transmit power level (dB re
refPowerLevel
) for baseband signals. -
modem.preambleDuration = 0.2
-
Frame detection preamble duration (s).
-
modem.headerLength = 8
-
Frame header length (bytes).
-
modem.timestampLength = 6
-
Timestamp length (bytes), for timestamped frames.
-
modem.txDelay = 0.05
-
Transmission delay when switching from receive to transmit mode (s).
-
modem.timestampedTxDelay = 1.0
-
Transmission delay when scheduling transmission of a timestamped packet (s).
-
modem.maxPowerLevel = 0
-
Maximum allowable transmit power level (dB re
refPowerLevel
). -
modem.minPowerLevel = -96
-
Minimum allowable transmit power level (dB re
refPowerLevel
). -
modem.refPowerLevel = 185
-
Reference transmit power level (dB re µPa @ 1m).
-
modem.rxSensitivity = -200
-
Reference receive sensitivity (dB re µPa).
-
modem.carrierFrequency = 12000
-
Carrier frequency (Hz).
-
modem.basebandRate = 12000
-
Baseband sampling rate (samples/second).
-
modem.basebandRxDuration = 1.0
-
Baseband reception duration (s).
-
modem.maxSignalLength = 65536
-
Maximum allowable baseband signal length.
A modem model simulates the half-duplex nature of the modem, propagation delay, interference, packet detection and packet loss. In order to do this accurately, it uses a channel model.
32.2. Channel models
Channel models implement the
ChannelModel
interface. The default channel model is the
BasicAcousticChannel
, but can be reconfigured in the simulation script. Again, channel models can use either syntax:
channel = [
model: org.arl.unet.sim.channels.ProtocolChannelModel,
communicationRange: 3000.m,
pDetection: 0.9,
pDecoding: 0.8
]
or
channel.model = org.arl.unet.sim.channels.ProtocolChannelModel
channel.communicationRange = 1000.m
channel.pDetection = 0.9
channel.pDecoding = 0.8
The properties supported by a channel model depend on the specifics of that model. Let us next look at a few channel models that come with the Unet simulator.
32.2.1. Protocol channel model
The protocol channel model (
ProtocolChannelModel
) is the simplest of the channel models available in the Unet simulator. Although simple, it captures important first-order effects such as propagation delay, limited communication range, interference range, and collisions. It also captures the probabilistic nature of the channel. It therefore serves as a good first order approximation that is also amenable to mathematical analysis.
The protocol channel model is parametrized by a sound speed
c
, communication range
Rc
, detection range
Rd
, an interference range
Ri
, probability of detection
pd
, and a probability of decoding
pc
. Successful communication is possible at a range
R ≤ Rc
with a probability
pd × pc
. At a range
Rc < R ≤ Rd
, a frame may be detected with probability
pd
, but not successfully decoded. At any range
R ≤ Ri
, a frame interferes with another frame that is being received at the same time, and causes a collision. Both frames are lost (not successfully decoded) during a collision. At a range
R > Ri
, a frame is not detected and does not interfere with other frames.
To select the protocol model, the simulation script must explicitly set it as the channel.model. The parameters of the model can be configured in the simulation script. The configuration of the channel with default parameter values is shown below:
import org.arl.unet.sim.channels.*
channel.model = org.arl.unet.sim.channels.ProtocolChannelModel
channel.soundSpeed = 1500.mps // c
channel.communicationRange = 2000.m // Rc
channel.detectionRange = 2500.m // Rd
channel.interferenceRange = 3000.m // Ri
channel.pDetection = 1 // pd
channel.pDecoding = 1 // pc
32.2.2. Basic acoustic channel model
The basic acoustic channel model (
BasicAcousticChannel
) is the default channel model in the simulator. It provides a good balance between accuracy, applicability and simulation speed. The model is composed of two parts: an acoustic model (
UrickAcousticModel
) based on average transmission loss, and a communication model (
BPSKFadingModel
) based on high time-bandwidth product detection preamble and binary phase shift keying (BPSK) communication in a Rician or Rayleigh fading channel.
Urick acoustic model
The acoustic model is parametrized by carrier frequency
f
, bandwidth
B
, spreading loss factor
α
, water temperature
T
°C, salinity
S
ppt, noise power spectral density level
N0
dB re µPa/√Hz and water depth
d
. The default values are shown below:
import org.arl.unet.sim.channels.*
channel.model = BasicAcousticChannel
channel.carrierFrequency = 25.kHz // f
channel.bandwidth = 4096.Hz // B
channel.spreading = 2 // α
channel.temperature = 25.C // T
channel.salinity = 35.ppt // S
channel.noiseLevel = 60.dB // N0
channel.waterDepth = 20.m // d
The acoustic model automatically computes the sound speed
c
[Mackenzie, JASA, 1981], transmission loss
TL
[Urick 3rd ed, p105-111] and total noise level
NL
. The total signal-to-noise ratio is then given by
SNR = SL - TL - NL
, where
SL
is the source level of the transmission in dB re μPa @ 1m.
BPSK fading model
The fading communication model uses the above
SNR
to simulate detection and successful decoding. The model is parametrized by the Rician fading parameter
K
, fast/slow fading, acceptable probability
pfa
of false alarm during detection, and a processing gain
G
. The default values are shown below:
channel.ricianK = 10 // K
channel.fastFading = true // fast/slow fading
channel.pfa = 1e-6 // pfa
channel.processingGain = 0.dB // G
For a detection preamble of duration
t
seconds and bandwidth
B
, we have an effective
SNR’ = SNR + 10 log(Bt)
after pulse compression. We assume Rician fading (or Rayleigh fading if
K
= 0) and Gaussian noise such that the average
SNR
is
SNR’
to simulate detection.
For the BPSK communication signal with data rate
D
bits/second, we compute
Eb/N0 = SNR + 10 log(B/D) + G
. We then simulate bit errors assuiming Rician fading (or Rayleigh fading if
K
= 0) and Gaussian noise. If fast fading is enabled, each bit generates an independent realization for the Rician fading variate. If fast fading is disabled, the entire frame uses a single realization of the Rician fading variate. If all bits are successful, the frame is successfully decoded. If any bit is in error, the frame is deemed to have failed at decoding.
32.2.3. MISSION 2012 and 2013 channel models
Although channel modeling can provide useful approximations to an underwater channel, there is no real substitute to experimenting at sea. The MISSION 2012 and MISSION 2013 experiments were conducted over several weeks in October 2012 and November 2013 in Singapore waters. Extensive channel measurements were made between Unet nodes deployed during the experiment. These measurements allow us to estimate packet detection probabilities and packet error probabilities on various network links. Although these probabilities are generally time-varying, we can estimate instantaneous probabilities from measurements over a short interval during which the environmental conditions are relatively stable. These can be used to generate a protocol channel model that accurately models the channel between the nodes during the experiment. Any protocol simulation using this model then shall accurately predict what would have happened if the protocol was tested at sea during the experiment. This may be a good way to benchmark protocols in realistic deployment conditions.
To use the
Mission2012a
model for simulation, set the appropriate channel model and node addresses/locations in the simulation script:
import org.arl.unet.sim.channels.*
channel.model = Mission2012a
simulate {
Mission2012a.nodes.each { addr ->
node "P$addr", address: addr, location: Mission2012a.nodeLocation[addr]
}
}
The
Mission2013a
and
Mission2013b
models are used in a similar way.
We have already been using the
Mission2013a
channel model when using the
samples/mission2013-network.groovy
simulation in
Section 6.1
. You may wish to take a look at the simulation script now, to understand how it works.
|
32.2.4. Developing custom channel models
While the above channel models meet the simulation needs for many applications, custom channel models may be developed to meet special research needs. Although developing and testing a model from scratch can be a daunting task, the
ProtocolChannelModel
and the
AbstractAcousticChannel
classes provide excellent starting points to customize the channel models. In this section, we see how each of the classes can be used to create custom channels.
Extending the ProtocolChannelModel
The
ProtocolChannelModel
can be customized to provide per-link detection and decoding probabilities. The
Mission2012a
and
Mission2013a
models do exactly this. To illustrate how this is done, let us take a look at the following code sample:
import org.arl.unet.sim.*
import org.arl.unet.sim.channels.ProtocolChannelModel
class Mission2012Channel extends ProtocolChannelModel {
static final def nodes = [21, 22, 27, 28, 29]
static final def nodeLocation = [
21: [ 0, 0, -5],
22: [ 398, -105, -18],
27: [-434, -499, -12],
28: [ -32, 279, -20],
29: [-199, -307, -12]
]
static def pNoDetect = [
[ 0, 0.047, 0.095, 0.026, 0.056],
[0.032, 0, 0.228, 0.139, 0.081],
[0.047, 0.174, 0, 0.025, 0.011],
[0.019, 0.060, 0.040, 0, 0.420],
[0.026, 0.018, 0.009, 0.048, 0]
]
static def pNoDetectOrDecode = [
[ 0, 0.157, 0.643, 0.197, 0.239],
[0.184, 0, 0.870, 0.639, 0.435],
[0.326, 0.826, 0, 0.975, 0.023],
[0.038, 0.160, 0.760, 0, 0.900],
[0.070, 0.070, 0.018, 0.871, 0]
]
float getProbabilityDetection(Reception rx) {
int from = nodes.indexOf(rx.from)
int to = nodes.indexOf(rx.address)
if (from < 0 || to < 0) return 0
return 1-pNoDetect[from][to]
}
float getProbabilityDecoding(Reception rx) {
int from = nodes.indexOf(rx.from)
int to = nodes.indexOf(rx.address)
if (from < 0 || to < 0) return 0
return (1-pNoDetectOrDecode[from][to])/(1-pNoDetect[from][to])
}
}
The nodes during the MISSION 2012 experiment have addresses 21, 22, 27, 28 and 29. The node locations and inter-node detection/decoding probabilities are measured and tabulated in the model. The model uses these measurements to simulate packet loss.
Extending the AbstractAcousticChannel
The
AbstractAcousticChannel
class provides a framework for acoustic simulation channels, including functionality for collision detection. The
BasicAcousticChannel
class extends the
AbstractAcousticChannel
class and provides implementation for an acoustic model (
UrickAcousticModel
) and a communication model (
BPSKFadingModel
):
class BasicAcousticChannel extends AbstractAcousticChannel {
@Delegate UrickAcousticModel acoustics = new UrickAcousticModel(this)
@Delegate BPSKFadingModel comms = new BPSKFadingModel(this)
}
To customize an acoustic channel model, one may extend or replace the acoustic or communication models. For example, if we wish to have a deep sea noise model where the noise power was a function of a new parameter
seaState
, we could extend the
UrickAcousticModel
:
import org.arl.unet.sim.channels.UrickAcousticModel
class MyAcousticModel extends UrickAcousticModel {
// map of sea state to noise power (dB re uPa^2/Hz)
private final def noiseLevel = [ 0: 20, 1: 30, 2: 35, 3: 40, 4: 42, 5: 44, 6: 46 ]
// sea state parameter
float seaState = 2
double getNoisePower() {
return Math.pow(10, noiseLevel[seaState]/10) * model.bandwidth
}
}
and then replace the
BasicAcousticChannel
model with out own version:
import org.arl.unet.sim.channels.*
class MyAcousticChannel extends AbstractAcousticChannel {
@Delegate UrickAcousticModel acoustics = new MyAcousticModel(this)
@Delegate BPSKFadingModel comms = new BPSKFadingModel(this)
}
Similarly, the communication model can be extended or replaced too.
Appendices
Appendix A: FAQs and resources
A.1. Frequently asked questions (FAQs)
A.2. Useful resources
-
Unet blog articles — take a look, there are many cool articles here!
-
Unet community contributions — explore what others have been doing, and contribute back!
-
UnetStack discussions & support on StackOverflow
A.3. Quick links
Appendix B: List of services
The following services are currently defined in UnetStack:
| Short name | Fully qualified name | Description | Read… |
|---|---|---|---|
|
DATAGRAM |
|
Send and receive datagrams |
|
|
PHYSICAL |
|
Physical layer |
|
|
BASEBAND |
|
Arbitrary waveform transmission & recording |
|
|
RANGING |
|
Ranging & synchronization |
|
|
NODE_INFO |
|
Node & network information |
|
|
ADDRESS_RESOLUTION |
|
Address allocation & resolution |
|
|
LINK |
|
Datagram transmission over a single hop |
|
|
MAC |
|
Medium access control |
|
|
ROUTING |
|
Routing of datagrams over a multihop network |
|
|
ROUTE_MAINTENANCE |
|
Discovery & maintenance of routes in a multihop network |
|
|
TRANSPORT |
|
Datagram transmission over a multihop network |
|
|
REMOTE |
|
Remote command execution, text messaging & file transfer |
|
|
STATE_MANAGER |
|
State persistence across node reboots |
|
|
SCHEDULER |
|
Sleep-wake scheduling for energy management |
|
|
SHELL |
|
Commmand execution & file management services |
Appendix C: Command reference
C.1. fjåge commands
shell
— basic shell commands
-
help
— provide help on a specified topic
Usage:
help [topic]
Examples:
help // get help index help shell // get help on 'shell' help('shell') // alternative syntax
-
ps
— list all the agents -
services
— list all services provided by agents -
who
— display list of variables in workspace -
shutdown
— shutdown the local platform -
run
— run a Groovy script
Scripts are stored in a folder defined by the 'scripts' variable in the workspace. If no such variable is defined, they are in the current folder.
Examples:
run 'myscript' // run a script called myscript.groovy myscript // alternative syntax for running myscript run 'res://myscript.groovy' // run a script from resources (in jar) run 'cls://myscript' // run a precompiled script from class
-
println
— display message on console
Usage:
println output, [type]
Examples:
println 'hello there!' println 'that failed!', org.arl.fjage.shell.OutputType.ERROR
-
href
— make a clickable URL (on terminals that support URLs)
Usage:
href(url) href(url, text)
Examples:
println href('http://www.google.com') println href('http://www.google.com', 'Search...')
-
delay
— delay execution by the specified number of milliseconds
Example:
delay 1000 // delay for 1000 ms
-
logLevel
— set loglevel (optionally for a named logger)
Usage:
logLevel [name],level
Examples:
logLevel INFO // set loglevel to INFO logLevel 'org.arl', ALL // set loglevel for logger org.arl to ALL
-
subscribe
— subscribe to notifications from a named topic
Examples:
subscribe topic('MyTopic') // subscribe to notifications from MyTopic subscribe agent('abc') // subscribe to notifications from agent abc
-
unsubscribe
— unsubscribe from notifications for a named topic
Examples:
unsubscribe topic('MyTopic') // unsubscribe notifications from MyTopic unsubscribe agent('abc') // unsubscribe notifications from agent abc
-
export
— add specified package/classes to list of default imports
Examples:
export 'org.arl.fjage.*' // import package org.arl.fjage export 'mypackage.MyClass' // import class mypackage.MyClass
At the shell prompt (but not in a script), export can be abbreviated to import. For example:
import org.arl.fjage.* // import package org.arl.fjage
-
agent
— return an agent id for the named agent
Usage:
agent(name)
Example:
a = agent('shell')
-
agentForService
— find an agent id providing the specified service
Examples:
a = agentForService Services.SHELL // find agents providing shell service
-
agentsForService
— get a list of all agent ids providing a service
Examples:
a = agentsForService Services.SHELL // list all agents providing a service
-
send
— send the given message
Examples:
send new Message(agent('shell')) // send a message to agent shell
-
request
— send the given request and wait for a response
Usage:
request req,[timeout]
Examples:
rsp = request req // send req and wait for response for default timeout rsp = request req,100 // send req and wait for response for 100 ms
-
receive
— wait for a message
Usage:
receive [filter], [timeout] receive [msg], [timeout]
Examples:
msg = receive // get any message with default timeout msg = receive 100 // get any message within 100 ms msg = receive req // get a response message for request req msg = receive A // get message that of class A msg = receive { it instanceof A } // get message that of class A msg = receive req // get message response to req
-
input
— get user input
Usage:
input [prompt], [hide]
Examples:
name = input('What is your name?') // prompt user and get input secret = input('Secret?', true) // hide input after entering
C.2. Transport commands
transport
— access to transport service
Examples:
transport // access parameters transport.maxRetries = 5 // set maximum retries for reliable delivery transport << new DatagramReq(to: 2, data: [1,2,3], reliability: true) // send reliable datagram
Parameters:
-
transport.MTU
— maximum data transfer size -
transport.RTU
— recommended data transfer size
Commands:
-
abort
— abort all transport datagram transfers
Example:
abort // abort ongoing transfers
C.3. Baseband commands
bb
— access to baseband service
Examples:
bbrec 1000 // record 1000 baseband signals bbtx sig // transmit baseband signal "sig" bb.carrierFrequency // check carrier frequency bb.maxSignalLength // check max signal length (samples)
Parameters:
-
bb.carrierFrequency
— carrier frequency (Hz) -
bb.basebandRate
— baseband sampling rate (Hz) -
bb.maxPreambleID
— maximum preamble ID -
bb.preambleDuration
— preamble duration in seconds -
bb.maxSignalLength
— baseband signal length (samples)
Commands:
-
bbrec
— make a baseband recording
Examples:
bbrec 1000 // record 1000 baseband signals bbrec 1000, 87897287 // schedule a recording at given PHY time
-
bbtx
— transmit a baseband signal
Examples:
bbtx sig // transmit complex baseband signal bbtx sig, 87897287 // schedule a transmission at given PHY time
-
pbtx
— transmit a passband signal sampled at 8 x basebandRate
Examples:
pbtx sig // transmit real passband signal pbtx sig, 87897287 // schedule a transmission at given PHY time
-
cw
— create a tonal signal with unit amplitude
Examples:
cw(10000, 0.5) // 0.5 second 10 kHz baseband tonal cw(15000, 0.2, 0) // 0.2 second 15 kHz passband tonal
-
sweep
— create a linear sweep with unit amplitude
Examples:
sweep(10000, 12000, 0.5) // 0.5 second baseband sweep from 10-12 kHz sweep(10000, 12000, 0.5, 0) // 0.5 second passband sweep from 10-12 kHz
-
wav
— load a wav file
Examples:
wav 'scripts/test.wav' // loads a wav file
-
save
— save signal to a file in the scripts folder
Example:
save 'data.txt', ntf // save notification signal to file save 'data.txt', ntf.signal, 2 // save signal in 2-column format
-
load
— load floating point signal from a file in the scripts folder
Example:
signal = load('data.txt') // load data from file
C.4. NodeInfo commands
node
— access to node information service
Examples:
node // access node info parameters node.address // check node address node.address = 5 // change node address
Parameters:
-
node.address
— Node address -
node.nodeName
— Node name -
node.origin
— origin [lat,long] -
node.location
— Location of the node in [x,y,z] meters or [lat,long,z] -
node.speed
— speed (meter/second) -
node.heading
— heading (deg, 0 is North, clockwise) -
node.turnRate
— turn rate (deg/s, clockwise) -
node.diveRate
— dive rate (meter/second) -
node.mobility
— true if the node is mobile, false if it is fixed -
node.time
— node time (real-time clock) -
node.canForward
— true if node has forwarding capability, false otherwise
C.5. Scheduler commands
scheduler
— access to scheduling service
Commands:
-
addsleep
— schedule sleep and wakeup of the modem
Examples:
addsleep<PHONE_NUMBER>,<PHONE_NUMBER> // sleep from epoch<PHONE_NUMBER> to<PHONE_NUMBER> addsleep<PHONE_NUMBER> // sleep immediately until<PHONE_NUMBER> addsleep 10.s.later, 20.s.later // sleep 10s later and wake up 20s later addsleep 20.s.later // sleep immediately and wake up 20s later addsleep 20.s.later, forever // sleep 20s later forever addsleep // sleep immediately forever
-
showsleep
— shows sleep/wakeup schedule
Example:
showsleep // show schedule
-
rmsleep
— removes sleep/wakeup schedule
Example:
rmsleep '3bf9e744-b225-48f4' // removes sleep schedule with given id
C.6. Physical commands
phy
— access to physical service
Examples:
phy // access physical parameters phy[CONTROL] // access control channel parameters phy[DATA] // access data channel parameters phy << msg // send request msg to physical agent phy.rxEnable = false // disable reception of frames
Commands:
-
pclr
— clear PHY queues -
plvl
— get/set TX power level for all PHY channel types
Examples:
plvl // get all power levels plvl -10 // set all power to -10 dB plvl(-10) // alternative syntax plvl = -10 // alternative syntax
Parameters:
The following parameters are available on all modems. Additional modem dependent parameters are also available. For information on these parameters type "help modem".
-
phy.MTU
— maximum transmission unit (MTU) in bytes -
phy.RTU
— recommended data transfer size in bytes -
phy.rxEnable
— true if reception enabled -
phy.propagationSpeed
— propagation speed in m/s -
phy.timestampedTxDelay
— delay before TX of timestamped frames -
phy.time
— physical layer time (us) -
phy.busy
— true if modem is TX/RX a frame, false if idle -
phy.refPowerLevel
— reference power level in dB re uPa @ 1m -
phy.maxPowerLevel
— maximum supported power level (relative to reference) -
phy.minPowerLevel
— minimum supported power level (relative to reference)
Channel Parameters:
The following parameters are available on all modems. Additional modem dependent parameters are also available. For information on these parameters type "help modem".
-
phy[].MTU
— maximum transmission unit (MTU) in bytes -
phy[].RTU
— recommended data transfer size in bytes -
phy[].dataRate
— effective frame data rate (bps) -
phy[].frameDuration
— frame duration (seconds) -
phy[].powerLevel
— powel level used for transmission (relative to reference) -
phy[].errorDetection
— number of bytes for error detection -
phy[].frameLength
— frame length (bytes) -
phy[].maxFrameLength
— maximum settable frame length (bytes) -
phy[].fec
— forward error correction code
A value of 0 indicates no error correction. Other values represent various FEC codes from the phy[].fecList. For example, a value of 1 means the first FEC code from the phy[].fecList, and a value of 2 means the second FEC code from that list.
-
phy[].fecList
— list of available forward error correction codes
LDPC1 - 1-rate LDPC FEC code LDPC2 - 1/2-rate LDPC FEC code LDPC3 - 1/3-rate LDPC FEC code LDPC4 - 1/4-rate LDPC FEC code LDPC5 - 1/5-rate LDPC FEC code LDPC6 - 1/6-rate LDPC FEC code ICONV2 - 1/2-rate interleaved convolution code
The 1-rate LDPC code is only useful for OFDM, where unused bits in the last OFDM block are used to provide FEC redundancy without loss in data rate.
C.7. Unet commands
unet
— basic unet commands
Commands:
-
ver
— version information -
time
— current platform time -
ls
— list script files -
free
— show free disk space -
dashboards
— show list of dashboards -
iface
— display/enable interfaces
Usage:
iface [iftype, port[, baud] [, settings]]
Examples:
// show all interfaces iface
// enable AT commands on TCP port 5001 iface ATScriptEngine, 5001
// enable Groovy shell on TCP port 5002 iface GroovyScriptEngine, 5002
// enable AT commands on serial port /dev/ttyS0 at 9600 baud iface ATScriptEngine, '/dev/ttyS0'
// enable Groovy shell on serial port /dev/ttyS0 at 115200 baud iface GroovyScriptEngine, '/dev/ttyS0', 115200
// enable API connector on serial port /dev/ttyS0 at 115200 baud // with settings: no partity, 8 bits, 1 stop bit iface API, '/dev/ttyS0', 115200, 'N81'
// alternate syntax to enable AT commands on TCP port 5001 iface new ATScriptEngine(), 5001
-
distance
— compute distance between two points
Example:
distance([0,0], [100,100]) // distance between origin and (100,100)
-
logs
— list log files
Only log files with the default settings (logs/*.txt) are listed.
Example:
logs // list log files
-
clrlogs
— clear old log files
Old log files (logs/ .txt) are deleted. The currently active log files (logs/ -0.txt) are not deleted.
Example:
clrlogs // clear old log files
-
tail
— show the last few lines of the current log file
Examples:
tail // show last 10 lines of the current log file tail 20 // show last 20 lines of the current log file
-
file
— file in the scripts folder
Example:
file('a.groovy').size() // get size of script file a.groovy file('a.groovy').delete() // delete script file a.groovy file('a.groovy').text // show contents of file a.groovy
C.8. Mac commands
mac
— access to medium access control (MAC) service
Examples:
mac // list MAC parameters mac << new ReservationReq(to: 27, duration: 1) // channel reservation request
Parameters:
-
mac.channelBusy
— indicates if the channel is busy -
mac.reservationPayloadSize
— size of payload (bytes) carried in MAC PDU -
mac.ackPayloadSize
— size of ack payload (bytes) carried in ACK PDU -
mac.maxReservationDuration
— maximum reservation duration -
mac.recommendedReservationDuration
— recommended reservation duration
C.9. Arp commands
arp
— access to address resolution service
Commands:
-
host
— resolve hostname to address
Examples:
host 'redstar' // get address for host named "redstar" rs = host('redstar') // save address of "redstar" in variable "rs"
C.10. Ranging commands
ranging
— access to ranging service
Examples:
ranging // show ranging parameters ranging[2] // show parameters for ranging to node 2 ranging << new RangeReq(to: 27) // get range to node 27 ranging << new BeaconReq() // transmit a beacon out
Parameters:
-
ranging.phy
— physical agent for ranging -
ranging.mac
— MAC agent or null to disable MAC -
ranging.maxRange
— default maximum range to peer node (m) -
ranging.channel
— default channel for interrogation (CONTROL/DATA) -
ranging.ipreamble
— default interrogation preamble (0 = none) -
ranging.rpreamble
— default response preamble (0 = none) -
ranging.rsignal
— default baseband response signal ([] = none) -
ranging.rdelay
— default response delay (seconds) -
ranging.respond
— automatically respond to an interrogation -
ranging.threshold
— threshold for signal detection (0 to 1)
Node-specific parameters:
-
ranging[].address
— peer node address -
ranging[].channel
— channel for interrogation (CONTROL/DATA) -
ranging[].ipreamble
— interrogation preamble (0 = none) -
ranging[].isignal
— baseband interrogation signal ([] = none) -
ranging[].rpreamble
— response preamble (0 = none) -
ranging[].rsignal
— baseband response signal ([] = none) -
ranging[].rdelay
— response delay (seconds) -
ranging[].sync
— PHY clock synchronization valid -
ranging[].lastSync
— time of last synchronization (epoch milliseconds) -
ranging[].offset
— PHY clock offset to peer node (microseconds) -
ranging[].lifetime
— synchronization validity lifetime (seconds) -
ranging[].maxRange
— maximum range to peer node (m) -
ranging[].data
— payload data to piggyback on interrogation/response frame -
ranging[].threshold
— threshold for signal detection (0 to 1)
Commands:
-
range
— get ranging information from a node
Examples:
range 21 // get range to node 21
-
beacon
— send a timestamped beacon
Examples:
beacon // broadcast a timestamped beacon
C.11. Remote commands
remote
— access to remote service
Examples:
tell 2, 'hello' // send text message to node 2 fget 3, 'abc.txt' // get file abc.txt from node 3 fput 2, 'abc.txt' // send file abc.txt to node 2 rsh 3, 'reboot' // ask node 3 to reboot itself
Commands:
-
tell
— send a text message to remote node
Example:
tell 2, 'hello' // send text message to node 2
-
fget
— get file from remote node
fget can only get files from nodes with remote.enable = true
Example:
fget 3, 'abc.txt' // get file abc.txt from node 3
-
fput
— put file on remote node
fput can only put files from nodes with remote.enable = true
Examples:
fput 2, 'a.txt' // send file a.txt to node 2 fput 2, 'a.txt', 'abc' // create file a.txt on node 2 // with content 'abc'
-
rsh
— run shell command on remote node
rsh can only execute commands on nodes with remote.enable = true
Example:
rsh 3, 'reboot' // ask node 3 to reboot itself
-
ack
— enable/disable acknowledments for remote commands
Examples:
ack on // enable acknowldegements for fput, rsh, tell ack true // enable acknowldegements for fput, rsh, tell ack off // disable acknowldegements for fput, rsh, tell ack false // disable acknowldegements for fput, rsh, tell ack // check current setting for acknowldegements
C.12. Router commands
router
— access to routing service
Examples:
routes // display routing table routes 2 // display routes to node 2 addroute 27, 29 // add a route to node 27 via node 29 delroute 'as7623' // delete route with UUID as7623 editroute 'as7623', metric: 5.0 // edit route to change metric delroutesto 27 // delete all routes to node 27 delroutes // delete all routes trace 27 // trace route to node 27 ping 27 // check if node 27 is accessible
Parameters:
-
router.MTU
— maximum data transfer size -
router.RTU
— recommended data transfer size -
router.auto1hop
— automatically assume single hop routes -
router.defaultLink
— default link to use
Commands:
-
routes
— print routing table
Examples:
routes // display routing table routes 2 // display routes to node 2
-
addroute
— add a route to the routing table
Example:
addroute 27, 29 // add a route to node 27 via node 29 addroute 27, 29, link2, false // add route on link2 with no reliability addroute to: 27, metric: 3.2 // add route to 27 with metric 3.2
-
editroute
— edit a route in the routing table
The route UUID can be obtained by displaying the routing table using the 'routes' command.
Example:
editroute 'as731', metric: 2.1 // edit route to change metric editroute 'as731', nextHop: 21 // edit route to change next hop
-
delroute
— delete a route from the routing table
The route UUID can be obtained by displaying the routing table using the 'routes' command.
Example:
delroute 'as7623' // delete route with UUID as7623
-
delroutesto
— delete all routes to specified node from the routing table
Example:
delroutesto 27 // delete all routes to node 27
-
delroutes
— delete all routes from the routing table
Example:
delroutes // delete all routes
rdp
— access to route discovery/maintenance service
Examples:
rreq 27 // start route discovery to node 27 rreq 27, 3, 2, 10 // find <3-hop route to node 27 with 2 RREQs 10s apart trace 27 // trace current route to node 27
Commands:
-
rreq
— initiate route discovery
With a single parameter, rreq finds up to 3-hop routes using 3 probes spaced 20 seconds apart.
Examples:
rreq 27 // start route discovery to node 27 rreq 27, 3, 2, 10 // find <3-hop route to node 27 with 2 RREQs 10s apart
-
trace
— trace route
Example:
trace 27 // trace current route to node 27 trace 27, 10000 // trace current route to node 27 with 10s timeout
-
ping
— ping node
Example:
ping 27 // ping node 27 ping 27, 5 // ping node 27, 5 times ping 27, 5, 30000 // ping node 27, 5 times, with 30s timeout
C.13. SWTransport commands
SWTransport parameters:
-
transport.maxRetries
— maximum retries for reliable delivery -
transport.timeout
— end-to-end ACK timeout -
transport.reportProgress
— enable/disable progress reporting of transfer -
transport.dsp
— datagram service provider used for communication
SWTransport commands:
-
netstat
— show status of ongoing transport agent connections
C.14. BasebandSignalMonitor commands
bbmon
— baseband signal monitor
The baseband signal monitor records any signals received by it to a signal log file (called signal-*.txt). The signals are encoded in base64 in the file. A Python package arlpy.unet is available though PyPi to load these files.
Examples:
bbmon.enable = true // enable recording of baseband signals bbmon.enable = false // disable recording of baseband signals
Parameters:
-
bbmon.enable
— enable/disable monitoring of baseband signals
C.15. StateManager commands
statemanager
— access to state manager service
Commands:
-
savestate
— save state of all or specified agent in Groovy script format
Examples:
savestate 'pandan' // save current state of all agents savestate 'pandan', 'phy' // save current state of specified agent savestate 'pandan', phy // save current state of specified agent savestate // save current state in "saved-state.groovy"
-
clrstate
— set current state as the baseline for savestate
Example:
clrstate // set baseline state phy[1].powerLevel = -10 // change parameters savestate // save changed parameters
C.16. CSMA commands
CSMA MAC parameters:
-
mac.phy
— physical agent used for carrier sensing -
mac.minBackoff
— minimum backoff window (seconds) -
mac.maxBackoff
— maximum backoff window (seconds) -
mac.reservationsPending
— number of reservations in queue (read-only)
C.17. ReliableLink commands
uwlink
— access to underwater data link service
Examples:
uwlink // access parameters uwlink.maxRetries = 5 // set maximum retries for reliable delivery uwlink << new DatagramReq(to: 2, data: [1,2,3], reliability: true) // send reliable datagram
Note that reliability is only supported on unicast datagrams.
Parameters:
-
uwlink.MTU
— maximum data transfer size -
uwlink.RTU
— recommended data transfer size -
uwlink.maxRetries
— maximum retries for reliable delivery -
uwlink.reservationGuardTime
— guard period (s) -
uwlink.maxPropagationDelay
— maximum propagation delay (s) -
uwlink.controlChannel
— channel to use for control frames (CONTROL/DATA) -
uwlink.dataChannel
— channel to use for data frames (CONTROL/DATA) -
uwlink.mac
— medium access control (MAC) agent name to use (or 'none') -
uwlink.phy
— physical layer agent name to use
C.18. RemoteControl commands
Parameters:
-
remote.dsp
— datagram service provider used for communication -
remote.shell
— shell service provider used to run commands -
remote.cwd
— working directory to load/save files -
remote.reliability
— reliability to be requested on datagrams -
remote.enable
— enable/disable remote file/shell operations -
remote.groovy
— enable Groovy extensions for shell commands
When Groovy extensions are enabled, rsh commands can use a "me" variable to denote the requsting node address. Commands starting with a "?" send their output back to the requesting node.
Examples:
rsh 3, 'tell me,"hello!"' // ask node 3 to send a text message to me rsh 3, '?phy.MTU' // ask node 3 to send me the value of phy.MTU rsh 3, '?ls' // ask node 3 to send me the list of files
C.19. ECLink commands
uwlink
— access to underwater data link service
Examples:
uwlink // access parameters uwlink.maxRetries = 5 // set maximum retries for reliable delivery uwlink.status // show status of ongoing TX/RX uwlink << new DatagramReq(to: 2, data: [1,2,3], reliability: true) // send reliable datagram
Note that reliability is only supported on unicast datagrams.
Parameters:
-
uwlink.MTU
— maximum data transfer size -
uwlink.RTU
— recommended data transfer size -
uwlink.mac
— medium access control (MAC) agent name to use (or 'none') -
uwlink.phy
— physical layer agent name to use -
uwlink.controlChannel
— channel to use for control frames (CONTROL/DATA) -
uwlink.dataChannel
— channel to use for data frames (CONTROL/DATA) -
uwlink.guardTime
— guard period (s) -
uwlink.maxPropagationDelay
— maximum propagation delay (s) -
uwlink.maxRetries
— maximum retries for reliable delivery -
uwlink.minBatchSize
— minimum number of frames to send in each batch -
uwlink.maxBatchSize
— maximum number of frames to send in each batch -
uwlink.reliableExtra
— extra frames (fraction) for reliable TX -
uwlink.unreliableExtra
— extra frames (fraction) for unreliable TX -
uwlink.status
— show status of ongoing TX/RX on all links
C.20. Unet audio commands
modem
— modem commands and parameters
Commands:
-
reboot
— restart network stack -
probe
— send a channel probe signal -
staticIP
— set static IP address
Only applicable for modems with configurable IP address
Examples:
staticIP // check current static IP address staticIP '<IP_ADDRESS>' // set static IP address staticIP none // remove static IP address staticIP auto // automatic static IP in <IP_ADDRESS>/256
-
fan
— cooling fan control
Only applicable for modems equipped with a cooling fan
Examples:
fan on // turn on cooling fan fan off // turn off cooling fan fan // check if cooling fan is on
-
wakeup
— send acoustic wakeup signal
Examples:
wakeup // send 10 wakeup tones at increasing power levels from -20 dB wakeup 20, -30 // send 20 wakeup tones at increasing power levels from -30 dB wakeup 5, -10, 1 // send 5 wakeup tones in steps of 1 dB starting from -10 dB wakeup 10, -20, 2, 3000 // send 10 wakeup tones at 3 second interval in steps of 2 dB starting from -20 dB
-
ptail
— show the last few lines of the current phy log file
Examples:
ptail // show last 10 lines of the current phy log file ptail 20 // show last 20 lines of the current phy log file
Parameters:
-
phy.vendor
— modem vendor/make -
phy.model
— modem model number -
phy.serial
— modem serial number -
phy.post
— power-on self-test error code (0 = success) -
phy.loopback
— digital loopback enable -
phy.inhibit
— detector inhibit length (samples) -
phy.fullduplex
— full duplex mode enable -
phy.bpfilter
— bandpass filter enable -
phy.isc
— isotropic sign correlator enable -
phy.thermal
— thermal information -
phy.fan
— cooling fan enable -
phy.fanctl
— cooling fan control threshold (deg C) -
phy.hpc
— high-performance (low-drift) clock enable -
phy.voltage
— power supply / battery voltage -
phy.poweramp
— power-amplifier enable -
phy.standby
— power-amplifier auto-shutdown timeout (s)
Power amplifier is shutdown automatically after the specified idle time after the last transmission.
A value of 0 shutsdown the power amplifier immediately after a transmission, only if it was turned on automatically for that transmission.
A value of -1 disables auto-shutdown.
Examples:
phy.standby = 15 // shutdown poweramp after 15 s idle time phy.standby = -1 // disable auto-shutdown of poweramp
-
phy.mute
— power amplifier mute -
phy.preamp
— preamplifier enable -
phy.gain
— preamplifier gain (dB) -
phy.noise
— ambient noise level (dB) -
phy.wakeupdelay
— delay between wakeup signal and frame (ms) -
phy.pbsblk
— passband streaming block size (samples) -
phy.pbscnt
— number of passband data blocks to stream
Setting this parameter starts streaming of passband data for a specified number of blocks. A value of 0 stops streaming. A value of -1 enable streaming forever.
Example:
phy.pbscnt = 10 // stream 10 blocks of pasband data
-
phy.npulses
— number of pulses to send
Each TxBasebandSignalReq transmission is repeated phy.npulses number of times with a delay of phy.pulsedelay ms between them.
-
phy.pulsedelay
— delay between pulses (ms)
Each TxBasebandSignalReq transmission is repeated phy.npulses number of times with a delay of phy.pulsedelay ms between them.
-
phy.adcrate
— ADC sampling rate (Hz) -
phy.dacrate
— DAC sampling rate (Hz) -
phy.downconvRatio
— downconverter decimation factor -
phy.upconvRatio
— upconverter interpolation factor -
phy.adc2rate
— ADC2 sampling rate (Hz) -
phy.adc2channels
— ADC2 channel count -
phy.adc2gain
— ADC2 pre amplifier gain (dB)
Channel Parameters:
-
phy[].modulation
— modulation type
Examples:
phy[1] = 'none' // modulation disabled phy[1] = 'loopback' // loopback mode phy[1] = 'fhbfsk' // frequency-hopping binary FSK phy[1] = 'ofdm' // coherent OFDM
-
phy[].preamble
— preamble (see Preamble class)
Examples:
// set 240-sample long hyperbolic up sweep phy[1].preamble = Preamble.hfmUpSweep(240)
// use 1023-chip m-sequence with 50% bandwidth phy[3].preamble = Preamble.mseq(1023,2)
-
phy[].threshold
— preamble detection threshold (0-1) -
phy[].basebandRx
— baseband signal reception enable -
phy[].basebandExtra
— extra baseband signal to capture (samples) -
phy[].valid
— channel parameter validity -
phy[].test
— test packet reception enable
When test mode is enables, a TxFrameReq causes a standard test frame to be transmitted. Any reception is assumed to be a test frame, and BER is computed for the frame.
Channel Parameters for FHBFSK Modulation:
-
phy[].fmin
— lowest frequency bin (Hz) -
phy[].fstep
— frequency bin spacing (Hz) -
phy[].hops
— number of hops -
phy[].chiplen
— number of chips per symbol -
phy[].tukey
— tukey window enable
Channel Parameters for OFDM Modulation:
-
phy[].nc
— number of carriers -
phy[].np
— cyclic prefix length -
phy[].ns
— cyclic suffix length -
phy[].nz
— number of zero carriers (read-only) -
phy[].bw
— usable bandwidth fraction (0-1) -
phy[].psk
— PSK modulation order (2/4) -
phy[].blks
— number of OFDM blocks (read-only) -
phy[].sync
— sync signal length (baseband samples)
Appendix D: MySimpleHandshakeMac
MySimpleHandshakeMac.groovy
from
Section 29.4
:
import org.arl.fjage.*
import org.arl.fjage.param.Parameter
import org.arl.unet.*
import org.arl.unet.phy.*
import org.arl.unet.mac.*
import org.arl.unet.nodeinfo.*
class MySimpleHandshakeMac extends UnetAgent {
////// protocol constants
private final static int PROTOCOL = Protocol.MAC
private final static float RTS_BACKOFF = 2.seconds
private final static float CTS_TIMEOUT = 5.seconds
private final static float BACKOFF_RANDOM = 5.seconds
private final static float MAX_PROP_DELAY = 2.seconds
private final static int MAX_RETRY = 3
private final static int MAX_QUEUE_LEN = 16
////// reservation request queue
private Queue<ReservationReq> queue = new ArrayDeque<ReservationReq>(MAX_QUEUE_LEN)
////// PDU encoder/decoder
private final static int RTS_PDU = 0x01
private final static int CTS_PDU = 0x02
private final static PDU pdu = PDU.withFormat {
uint8('type') // RTS_PDU/CTS_PDU
uint16('duration') // ms
}
////// protocol FSM
private enum State {
IDLE, RTS, TX, RX, BACKOFF
}
private enum Event {
RX_RTS, RX_CTS, SNOOP_RTS, SNOOP_CTS
}
private FSMBehavior fsm = FSMBuilder.build {
int retryCount = 0
float backoff = 0
def rxInfo
def rnd = AgentLocalRandom.current()
state(State.IDLE) {
action {
if (!queue.isEmpty()) {
after(rnd.nextDouble(0, BACKOFF_RANDOM)) {
setNextState(State.RTS)
}
}
block()
}
onEvent(Event.RX_RTS) { info ->
rxInfo = info
setNextState(State.RX)
}
onEvent(Event.SNOOP_RTS) {
backoff = RTS_BACKOFF
setNextState(State.BACKOFF)
}
onEvent(Event.SNOOP_CTS) { info ->
backoff = info.duration + 2*MAX_PROP_DELAY
setNextState(State.BACKOFF)
}
}
state(State.RTS) {
onEnter {
Message msg = queue.peek()
def bytes = pdu.encode(
type: RTS_PDU,
duration: Math.ceil(msg.duration*1000))
phy << new TxFrameReq(
to: msg.to,
type: Physical.CONTROL,
protocol: PROTOCOL,
data: bytes)
after(CTS_TIMEOUT) {
if (++retryCount >= MAX_RETRY) {
sendReservationStatusNtf(queue.poll(), ReservationStatus.FAILURE)
retryCount = 0
}
setNextState(State.IDLE)
}
}
onEvent(Event.RX_CTS) {
setNextState(State.TX)
}
}
state(State.TX) {
onEnter {
ReservationReq msg = queue.poll()
retryCount = 0
sendReservationStatusNtf(msg, ReservationStatus.START)
after(msg.duration) {
sendReservationStatusNtf(msg, ReservationStatus.END)
setNextState(State.IDLE)
}
}
}
state(State.RX) {
onEnter {
def bytes = pdu.encode(
type: CTS_PDU,
duration: Math.round(rxInfo.duration*1000))
phy << new TxFrameReq(
to: rxInfo.from,
type: Physical.CONTROL,
protocol: PROTOCOL,
data: bytes)
after(rxInfo.duration + 2*MAX_PROP_DELAY) {
setNextState(State.IDLE)
}
rxInfo = null
}
}
state(State.BACKOFF) {
onEnter {
after(backoff) {
setNextState(State.IDLE)
}
}
onEvent(Event.SNOOP_RTS) {
backoff = RTS_BACKOFF
reenterState()
}
onEvent(Event.SNOOP_CTS) { info ->
backoff = info.duration + 2*MAX_PROP_DELAY
reenterState()
}
}
} // of FSMBuilder
////// agent startup sequence
private AgentID phy
private int addr
@Override
void setup() {
register Services.MAC
}
@Override
void startup() {
phy = agentForService(Services.PHYSICAL)
subscribe(phy)
subscribe(topic(phy, Physical.SNOOP))
add new OneShotBehavior({
def nodeInfo = agentForService(Services.NODE_INFO)
addr = get(nodeInfo, NodeInfoParam.address)
})
add(fsm)
}
////// process MAC service requests
@Override
Message processRequest(Message msg) {
switch (msg) {
case ReservationReq:
if (msg.to == Address.BROADCAST || msg.to == addr)
return new RefuseRsp(msg, 'Reservation must have a destination node')
if (msg.duration <= 0 || msg.duration > maxReservationDuration)
return new RefuseRsp(msg, 'Bad reservation duration')
if (queue.size() >= MAX_QUEUE_LEN)
return new Message(msg, Performative.FAILURE)
queue.add(msg)
fsm.restart() // tell fsm to check queue, as it may block if empty
return new ReservationRsp(msg)
case ReservationCancelReq:
case ReservationAcceptReq:
case TxAckReq:
return new RefuseRsp(msg, 'Not supported')
}
return null
}
////// handle incoming MAC packets
@Override
void processMessage(Message msg) {
if (msg instanceof RxFrameNtf && msg.protocol == PROTOCOL) {
def rx = pdu.decode(msg.data)
def info = [from: msg.from, to: msg.to, duration: rx.duration/1000.0]
if (rx.type == RTS_PDU)
fsm.trigger(info.to == addr ? Event.RX_RTS : Event.SNOOP_RTS, info)
else if (rx.type == CTS_PDU)
fsm.trigger(info.to == addr ? Event.RX_CTS : Event.SNOOP_CTS, info)
}
}
////// expose parameters that are expected of a MAC service
final int reservationPayloadSize = 0 // read-only parameters
final int ackPayloadSize = 0
final float maxReservationDuration = 65.535
@Override
List<Parameter> getParameterList() { // publish list of all exposed parameters
return allOf(MacParam)
}
boolean getChannelBusy() { // considered busy if fsm is not IDLE
return fsm.currentState.name != State.IDLE
}
float getRecommendedReservationDuration() { // recommended duration: one DATA packet
return get(phy, Physical.DATA, PhysicalChannelParam.frameDuration)
}
////// utility methods
private void sendReservationStatusNtf(ReservationReq msg, ReservationStatus status) {
send new ReservationStatusNtf(
recipient: msg.sender,
inReplyTo: msg.msgID,
to: msg.to,
from: addr,
status: status)
}
}


| Home | Trees | Indices | Help |
|
|---|
|
|
object --+ | TTraceOptions
Public options (Singleton) for the traces (Host,Port,...).
|
|||
|
|||
|
Inherited from |
|
|||
|
socketHost The Socket Host address. |
|||
|
socketPort The socket port. |
|||
|
socketUDP socket UDP mode. |
|||
|
sendClassInfo When True (default) , the class information is send by sendObject or addObject |
|||
|
sendInherited When True (default) , class information ancestors is send by by sendObject or addObject |
|||
|
sendFields When True (default) , fields are send by sendObject or addObject |
|||
|
sendFunctions When True , functions are send by sendObject or addObject. |
|||
|
sendPrivate When True (default) , private properties and functions are send by sendObject or addObject |
|||
|
sendDoc When True (default) , doc is send by sendObject or addObject |
|||
|
useThread When True , traces are send using a thread. |
|||
|
sendDate Indicate if the date must be send with the time. |
|||
|
sendThreadName Indicate if the thread name must be send . |
|||
|
objectTreeDepth Max Object tree depth for sendValue and Watches. |
|||
|
nativeClasses Your native business classes type that don't have to be parsed by addValue and addTable. |
|
|||
|
Inherited from |
|
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
|
|
socketHostThe Socket Host address. Default is 127.0.01 |
socketPortThe socket port. Default is 8090 |
socketUDPsocket UDP mode. (UDP mode is not yet implemented). default is false |
sendFunctionsWhen True , functions are send by sendObject or addObject. Default is False |
useThreadWhen True , traces are send using a thread. Default is False. You may need to call TTrace.flush() to ensure all traces are send before closing your application. Dependending of the python host, thread synchronisation can slow down the application. |
sendDateIndicate if the date must be send with the time. Default is false |
sendThreadNameIndicate if the thread name must be send . Default is true |
objectTreeDepthMax Object tree depth for sendValue and Watches. Default is 3 levels |
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Dec 23 15:48:20 2010 | http://epydoc.sourceforge.net |


Mushroom Data Set from
UC Irvine Machine Learning Repository
This data set includes descriptions of hypothetical samples corresponding to 23 species of gilled mushrooms in the Agaricus and Lepiota Family (pp. 500-525).
The Guide clearly states that there is no simple rule for determining the edibility of a mushroom; no rule like ``leaflets three, let it be'' for Poisonous Oak and Ivy.


Home
Keyword Clouds
Wordcloud-All
Wordcloud-2014
Wordcloud-POK
Newspaper Relations
Time-Total Reports
Time-Reports' Ratio
Who-Copies-Who
Mails & Resumes
About POK
Event Drops
Extended Network
Crisis Timeline
Story Line
抛
The Storyline


2 Samuel 16:23
And the counsel of Ahithophel, which he counseled in those days, was as if a man had inquired at the oracle of God: so was all the counsel of Ahithophel both with David and with Absalom.


Meet Medulance
Medulance is a user friendly way to request and track ambulances close to you.
Why We exist ?
India's first, GPS based technology platform for fast and reliable first point medical attention.
With an increasing emphasis on promoting independent living today, having access to the nearest ambulance to you can provide much needed peace of mind in a worst case scenario.


@JvmStatic fun getInstance(context: Context): PaymentConfiguration
Attempts to load a PaymentConfiguration instance. First attempt to use the class's singleton instance. If unavailable, attempt to load from Store.
Return
a PaymentConfiguration instance, or throw an exception


Consider the following JavaScript expression:
function (x) { return function (y) { return function (x) { return function (z) { return x(y)(z); }; }; }; }
Which one of the following expressions is a λ expression that is equivalent to the code given above?
Recall that all functions in the lambda calculus are curried.
Scoping rules are the same in JavaScript and the lambda calculus.
Pay special attention to the use of parentheses in function calls in JavaScript and in function applications in the lambda calculus.


nothing much
just some words
Poems
yesterday
vibes
remember
write
your eyes
weather
the love poem (we have all been waiting for)
up here
be
fear
toque me to the ballgame
met
word
smalltalk
shortbread cookie instructions
the most honest love poem (and also an anthem for the non-committal)
vain
wait
bored
moon
rising
objects in space
thinking
eyes or words?
small world
barely afloat
a beautiful truth
white dress, doll-like pose, orange balloon - my, oh my, they grow up so soon
crockashit
lost
toronto, my love
forgotten
reflection
love
and his heart beats were out of tune
patterns of irregularity
i need an eye scanner
saying it again won't help either
in response to cummings
that, and the postage costs were unbearable
zen and the art of motorcycle maintenance
kindness
engage in other pleasantries instead
wake
stepping stones and lonesome stars
fate
life
mad men
hold on
your smile
focus
just plain folk
on the way home
your eyes
foolishness
without you
blah
in the dirt
your nose
fix
mirror
look up
all our words
follower
paces
search
here
recapture
circular
light
just died
locked
time and tide
memories
then again
voices that rise
into the wind
life is like a box of chocolates
of doers and dreamers
magic is music
sticks and stones
no time
take a haiku
my greatest trick
loose ends
a page
out of service
no poet
a letter
without you
breathe
not your madness
put me in that box
reflection
my every moment gone
who i am
emptiness
hit and run


If you're having
problems I feel bad for you son.
I got 99
but a
ain't one.
How Many You Got?
Rave Mode?
Psychedelic Mode?
Let's Sing


Vihaan is the event organized by IEEE DTU Women in Engineering (WIE) Affinity Group, which is the largest professional organization dedicated to promoting women scientists and engineers and inspiring young girls to find their technical aptitude.
IEEE WIE believes in encouraging talent that is not bound by gender inequalities and with Vihaan, IEEE DTU aims at carrying this thought forward. The event aims to use the power of language to empower women. The issues that women face at their home, workplace or elsewhere shall be deliberated upon in form of various debating competitions. The event is aimed to ideate new solutions to these challenges to create a world where gender inequalities cease to exist. The event is open for all.
Registrations begin/Party Starts. Try to come as early as possible to grab the best spot.
Introduction about the event, revealing the theme of the event.
The game is on. Start brainstorming.
Dinner time.
Time for some fun. Let's play some game and grab a bite.
Submit your progress judges will evaluate your idea based on originality.
Want some food ? Please..
More Food? I don't mind.
Selection of the best teams to pitch in front of the judges.

Pitch your projects and wait for results.
Finally result time. Top team will get their prize.
Vihaan is a venture of IEEE WIE DTU, with an objective of motivating girls to take up STEM Fields. This year, Vihaan is a 24 hour hackathon.
27-28 October, 2017.
Delhi Technological University.
University students who are passionate about programming
A maximum of 4 members can form the team, with at least one female member.
No. A team must have 1 female participant.
None. Just the will to build a hack and make life a tad bit easier!
The software can be a mobile or a web application! However, you may need to incorporate a sponsor's API or platform. This would be made clear in the event briefing and updates would be released on the website as well.
Participants will be shortlisted on the basis of their resumé and github handles.
You can find them here
Yes. And redbulls too!
Yes.
College ID, Confirmation mails and laptops!


js projects planning yourself
Dark mode
this will show you the time
what's your name click here and start planning
To-Do list
Enter what's in your mind .
click


A line chart is a way of plotting data points on a line.
Often, it is used to show trend data, and the comparison of two data sets.
Bar Chart
A bar chart is a way of showing data as bars.
It is sometimes used to show trend data, and the comparison of multiple data sets side by side.
Radar Chart
A radar chart is a way of showing multiple data points and the variation between them.
They are often useful for comparing the points of two or more different data sets.
Polar Area Chart
Polar area charts are similar to pie charts, but each segment has the same angle - the radius of the segment
differs depending on the value.
Pie Chart
Pie and doughnut charts are probably the most commonly used chart there are. They are divided into segments, the arc of each segment shows the proportional value of each piece of data.
Doughnut Chart
Pie and doughnut charts are registered under two aliases in the Chart core. Other than their different default value, and different alias, they are exactly the same.


About Me
My name is Jingjiao Ni. Fourth years honor Computing Science student in University of Alberta. I am really interested in Computing Science, especailly in machine learning field. I have strong learning ability so I achieve 4.0 GPA in the last two years (year 2 and year 3 of university). I also would like to combine what I have learned to real-life. So I did a internship in industory and did some research with professor successively. I planned to continue to graduate study since I think there are much more knowledage I need to learn.
My History
I started to study Bachelor of Science with Honors program, Computing Science in University of Alberta, Canada from September, 2013 to present. I did a research named "Comparing Regression Methods for Predicting Dyadic Ordinal Data" with Farzaneh Mirzazadeh (PhD Candidate) as project leader and Professor Russell Greiner as Project Principal Investigator from May, 2016 to August, 2016. I did internship in Dell, China from May, 2015 to July, 2015. I studied in Nanjing Foreign Language School British Columbia Academy from September 2010 to June, 2013.
What can I do
I am pretty interested in coding and machine learning (both reinforcement leaning and supervised/unsupervised learning) related projects. So I am glad to help with the experiment and analytical part of such projects. Also, I think I have good ability of learning so I may be able to handle some task that I have never done and learned before.
My Skills
Programming Languages: Java, C++, C, C#, Python, SQL, Julia, MATLAB, Lisp, Prolog, JavaScript, MIPS Assembling Language Math relative: Basic Linear Algebra, Basic Calculs, Basic Graph Theory
My planning
I am planning to do my graduate study in Canada or US. And I will contine in Computing Science, more specificlly, machine learning field.
Contact
Email<EMAIL_ADDRESS>GitHub: NJJ-UA


A Resume dynamically generated from JSON and hosted with Python Flask on AWS EBS, all source available on Github.
Email | Linkedin | {{json.basics.location}} | {{json.basics.phone}} | Generated on {{generated}}
You can download {{json.basics.firstname}}s resume in a number of formats below. Each format is generated on the fly loading the data from resume.json. You can see the code that generates everything and deploy your own version here: Github.
If you'd like to get in contact with me or see more examples of my work please use the links below, or alternatively email me any time.


Address:
240 McLeod Street
Ottawa, Ontario K2P 2R1
Canada
Telephone:
613.566.4700
1.800.263.4433 (toll-free in Canada and U.S.A.)
Fax
613.364.4021
TTY:
613.566.4770
1.866.600.8801


Question...
11 January 2015
I've got a question for you...do you know how to ask a good question? We ask questions all the time throughout the day, but this week's question that Dev Bootcamp asked us is "How do you ask a good question"? Throughout DBC, I've asked myself and other students a lot of questions about coding, but I have never really thought about what makes up a good question. Asking a good question gets you the information you need in a timely manner.
 If you ask a lot of questions that you can find on your own, people will see that you aren't putting any work into the problem and might not help you out.
Is it concise?
Everyone is busy and has things to do. If you are looking for help, don't waste time with details that aren't important to solving the question. Adding a small rant that doesn't deal with the problem is distracting and might lead to people clicking the back button.
Include all relevant details
While being concise is important, having all of the relevant details is also important. Don't leave out any information that someone would need. If the question is computer related, make sure to include all error logs, screen shots, computer information, and anything else related to your problem or question.
Make it readable
Do whatever you can to make it easier for your reader- that includes using complete sentences and keeping the caps lock off. While typos and spelling errors happen, run spell check and read the question to yourself out loud. If there is any part of the question that isn't clear to you, it probably won't be clear to the reader (you wrote it after all, you should be able to understand it!).
 Make sure you are clear, concise and keep it relevant, and you should be on your way to getting the information you want quickly.


As a Technology Specialist with four years of IT experience, I know I have what it takes to be an impressive performer. Experienced with both front-end and back-end development, I am passionate about web technologies and always looking for ways to improve my skill set. I am adaptable, driven, and hard-working. Let’s get in touch!
To see some of my work, click on one of the links below or take a look at my portfolio.
Design and develop Node.js applications that automate and enhance business processes, integrate data, and reduce costs.
Worked under a Microsoft MVP and assisted a remote client with a TypeScript/Aurelia web application used for personal accounting.
Maintained a Windows/Active Directory environment of over 200 endpoints, deployed software and security patches, and administered internal SharePoint site.
Managed a team of 10 employees in a high volume helpdesk environment, resolved escalations requiring advanced troubleshooting, developed and maintained technical documentation and training material.
Provided campus-wide IT support for students, faculty, and staff. Administered identity and access management tools for CAS, FSUID, Exchange, Active Directory, LDAP, and Multi-factor Authentication.
Major GPA: 4.00
Aside from technology, my other passions include motorcycles, gaming, and music. When forced indoors, I play guitar and watch my favorite streaming platforms. I also spend some of my free time exploring the latest technology advancements in the world of software development.