3. UnetStack basics

The material in this chapter is also available as a short video .

UnetStack is an agent-based network stack . Each agent is similar to a layer in a traditional network stack, but has more flexibility to use the scarce resources (bandwidth, energy, etc) in the Unet more efficiently. In order to develop Unet applications, we need to understand some basic concepts in UnetStack.

3.1. The command shell

The simplest way to interact with UnetStack is via the command shell (or simply shell ). The shell may be accessed on the console, a TCP/IP port or via the web interface. In Chapter 2 , we have already seen how to set up a 2-node network and access the command shell for each of the nodes using a web browser. For the rest of this section, we assume that you have shells open on nodes A and B.

On node A, we can ask for a list of agents running:

> ps
statemanager: org.arl.unet.state.StateManager - IDLE
remote: org.arl.unet.remote.RemoteControl - IDLE
rdp: org.arl.unet.net.RouteDiscoveryProtocol - IDLE
ranging: org.arl.unet.localization.Ranging - IDLE
uwlink: org.arl.unet.link.ReliableLink - IDLE
node: org.arl.unet.nodeinfo.NodeInfo - IDLE
websh: org.arl.fjage.shell.ShellAgent - RUNNING
simulator: org.arl.unet.sim.SimulationAgent - IDLE
phy: org.arl.unet.sim.HalfDuplexModem - IDLE
bbmon: org.arl.unet.bb.BasebandSignalMonitor - IDLE
arp: org.arl.unet.addr.AddressResolution - IDLE
transport: org.arl.unet.transport.SWTransport - IDLE
router: org.arl.unet.net.Router - IDLE
mac: org.arl.unet.mac.CSMA - IDLE

We can further ask for more details of a specific agent:

> phy
« Half-duplex modem »

Generic half duplex modem simulator.

[org.arl.unet.DatagramParam]
  MTU ⤇ 56
  RTU ⤇ 56

[org.arl.unet.bb.BasebandParam]
  basebandRate = 12000.0
  carrierFrequency = 12000.0
  maxPreambleID ⤇ 4
  maxSignalLength = 65536
  signalPowerLevel = -42.0

[org.arl.unet.phy.PhysicalParam]
  busy ⤇ false
  maxPowerLevel = 0.0
  minPowerLevel = -96.0
  propagationSpeed ⤇ 1534.4574
  refPowerLevel = 185.0
  rxEnable = true
  rxSensitivity = -200.0
  time ⤇ 3074996380
  timestampedTxDelay = 1.0

[org.arl.unet.sim.HalfDuplexModemParam]
  basebandRxDuration = 1.0
  clockOffset = 2932.3245

We asked for details of the agent phy , and we got a list of parameters supported by the agent. We can get or set individual parameters of the agent:

> phy.MTU
56
> phy.rxEnable
true
> phy.rxEnable = false
false
> phy.rxEnable
false
> phy.rxEnable = true
true

To find out more about a specific parameter, we can ask for help on the parameter:

> help phy.MTU
phy.MTU - maximum transmission unit (MTU) in bytes
> help phy.rxEnable
phy.rxEnable - true if reception enabled

We can also ask for help on an agent:

> help phy
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

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
- phy[].fecList - list of available forward error correction codes

From this help, we see that phy agent also supports channel parameters (also known as indexed parameters). It supports two logical channels, CONTROL (1) and DATA (2). The CONTROL channel is meant for low-rate robust data transmission, whereas the DATA channel is typically configured for higher rate data transmission. Channel parameters work in the same way as normal parameters, but with an index:

> phy[CONTROL]
« PHY »

[org.arl.unet.DatagramParam]
  MTU ⤇ 16
  RTU ⤇ 16

[org.arl.unet.phy.PhysicalChannelParam]
  dataRate = 202.10527
  errorDetection ⤇ 1
  fec ⤇ 0
  fecList ⤇ null
  frameDuration ⤇ 0.95
  frameLength = 24
  janus = false
  llr ⤇ false
  maxFrameLength = 128
  powerLevel = -42.0

> phy[DATA]
« PHY »

[org.arl.unet.DatagramParam]
  MTU ⤇ 56
  RTU ⤇ 56

[org.arl.unet.phy.PhysicalChannelParam]
  dataRate = 731.4286
  errorDetection ⤇ 1
  fec ⤇ 0
  fecList ⤇ null
  frameDuration ⤇ 0.7
  frameLength = 64
  janus = false
  llr ⤇ false
  maxFrameLength = 512
  powerLevel = -42.0

> phy[CONTROL].MTU
16
> phy[CONTROL].frameLength = 32
32
> phy[CONTROL].frameLength
32
> phy[CONTROL].MTU
24
> phy[CONTROL].frameLength = 24
24
The actual parameters you see may differ if you are working with a modem, depending on the specific capabilities of the modem. Use help to find out more about any listed parameter on your modem, or refer to the modem’s documentation for further information.

Most agents also support some commands. For example, the phy agent supports the plvl command:

> help plvl
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

> plvl
phy[1].powerLevel = -42.0
phy[2].powerLevel = -42.0
phy[3].powerLevel = -42.0
phy.signalPowerLevel = -42.0
> plvl -20
OK
> plvl
phy[1].powerLevel = -20.0
phy[2].powerLevel = -20.0
phy[3].powerLevel = -20.0
phy.signalPowerLevel = -20.0

The plvl command simply displays or sets the powerLevel parameter of all channels. The same can be manually accomplished by setting or getting individual parameters, if desired:

> phy[1].powerLevel
-20
> phy[1].powerLevel = -10
-10
> phy[1].powerLevel
-10
> plvl
phy[1].powerLevel = -10.0
phy[2].powerLevel = -20.0
phy[3].powerLevel = -20.0
phy.signalPowerLevel = -20.0
While plvl seems like a command to just set/get a powerLevel parameter, it does that for several channels in one go. This can save you a lot of time and typing — to achieve the same thing manually, you’d be typing 4 commands!

3.2. Interacting with agents using messages

While you can access a lot of functionality via parameters and commands, to fully harness the power of UnetStack, we require an understanding of the underlying messaging system between the agents. All agents support messages that expose their functionality. In fact, all parameters and commands are implemented by exchanging messages between the shell agent and other agents. In this section, we’ll take a brief look at how messaging between agents works.

All parameters and commands are implemented by exchanging messages between the shell agent and other agents. When you get/set a parameter, all the shell is doing is sending a ParameterReq message to the appropriate agent, and showing you the ParameterRsp message that the agent responds with.

Typically, we would want to send a request to an agent and get a response message back. This can be accomplished with the request call (or the equivalent alias << ) on the agent:

> phy << new TxFrameReq(data: [1,2,3])
AGREE
phy >> TxFrameNtf:INFORM[type:CONTROL txTime:2913909740]

Here we made a request to the phy agent to transmit some data. The agent responded with an AGREE response, shortly followed by a TxFrameNtf notification from phy telling us that the transmission was successful.

A frame is simply a datagram at the physical layer, also sometimes called a "packet". We prefer the term "frame" when working at the physical layer, but the distinction between frames and datagrams is unimportant at this point in time. We will come back to this later, in Chapter 15 .

We can also use the return value in a condition, but we need to remember that the return value from the request is a message:

> x = phy << new TxFrameReq();
phy >> TxFrameNtf:INFORM[type:CONTROL txTime:3381446740]
> x
AGREE
> x.class
class org.arl.fjage.Message
> x.performative
AGREE
> if (x.performative == Performative.AGREE) print 'OK'
OK
The semicolon ";" at the end of the first statement prevents the return value from being printed on the shell.

Unsolicited notification messages can be received by subscribing to the topic of interest. For example, on node B, we can subscribe to physical layer events on node B:

> subscribe phy

Now, if we broadcast a frame from node A using phy << new TxFrameReq() , we will see the relevant reception events on node B:

phy >> RxFrameStartNtf:INFORM[type:CONTROL rxTime:1765508396]
phy >> RxFrameNtf:INFORM[type:CONTROL from:232 rxTime:1765508396]

The first event RxFrameStartNtf is triggered as soon as the frame is detected at node B. The second event RxFrameNtf is triggered when the frame is fully received, demodulated and successfully decoded at the receiver.

If all of this seems somewhat confusing to you, don’t worry about it. Most of the basic functionality of the stack can be accessed without having to deal with messages directly. As we need functionality that requires an understanding of messaging, we’ll gradually introduce them in later chapters.

3.3. Shell scripting

The default UnetStack shell accepts any Groovy code, and so is very flexible:

> 1+2
3
> 5.times { print it }
0
1
2
3
4

You can also define closures (if you’re not familiar with closures, you can think of them as functions for now):

> tx2 = {
-   2.times {
-     phy << new TxFrameReq()
-   }
- };

and call them later:

> tx2
phy >> TxFrameNtf:INFORM[type:CONTROL txTime:3911898740]
phy >> TxFrameNtf:INFORM[type:CONTROL txTime:3912307740]
You can write Groovy scripts and store them in the scripts folder with an extension .groovy . You can then invoke them from the shell by simply typing the name of the script (without the extension).

This only scratches the surface of what the command shell is capable of. However, it should provide you a basic understanding of how the shell works, and illustrate its power. To understand more, we suggest that you explore the online help . As you further understand the UnetStack and fjåge API, you’ll develop expertise on using the shell.

<<< [Getting started] [Unet basics] >>>