Build a real WiFi cell on COSMOS: one node runs an access point and several nodes join it as clients, all configured and measured from the console with Ansible. You'll see how a single 802.11 cell shares its capacity across clients — and run the same playbook unchanged on the over-the-air grid and on an RF-attenuator-matrix sandbox.
grid.cosmos-lab.org and sb4.cosmos-lab.org (1 AP + 7 clients, baseline.ndz).This builds on WiFi Access Point + Client. The first node is the access point; every other node you select becomes a client. The playbook associates all clients, then runs iperf3 from every client at once so you measure the cell under real shared load: aggregate throughput, per-client fairness, and each client's signal strength.
It also auto-detects the environment. On the grid, radios talk over the air. On a sandbox like sb4 (or bed), the node radios are cabled through a JFW attenuator matrix; the playbook detects that and notes the matrix is at its 0 dB pass-through default. The same downloaded playbook runs on both with no edits.
nodes[0] = AP, nodes[1:] = clients).| Where | any WiFi-equipped domain (grid, sb4, bed, …) |
| Nodes | first = AP, the rest = clients (this example: 1 + 7) |
| Image | baseline.ndz |
| Radio | Atheros ath5k — auto-detected |
| Band/channel | 2.4 GHz, channel 1, 802.11g, WPA2-PSK |
| WiFi subnet | 192.168.100.0/24 (AP .1, clients .20, .21, …) |
| Orchestration | Ansible |
The cell playbook ships in the same shared ~/tutorials bundle as the other WiFi tutorial — download once, reuse everywhere:
cd ~
wget https://www.cosmos-lab.org/files/cosmos-tutorials-wifi.zip
unzip -o cosmos-tutorials-wifi.zip # extracts into ~/tutorials/
cd ~/tutorials
The cell experiment is wifi/cell.yml; it reuses the shared wifi_common, wifi_ap, and wifi_sta roles plus a wifi_cell_measure role and an rf_matrix detection role.
Reserve and image 8 nodes (1 AP + 7 clients — adjust to taste):
omf load -t node1-1,node1-2,node1-3,node1-4,node1-5,node1-6,node1-7,node1-8 -i baseline.ndz
omf tell -a on -t node1-1,node1-2,node1-3,node1-4,node1-5,node1-6,node1-7,node1-8
Select your nodes with OMF_NODES (the first becomes the AP), then run:
cd ~/tutorials
export OMF_NODES="node1-1,node1-2,node1-3,node1-4,node1-5,node1-6,node1-7,node1-8"
ansible-playbook wifi/cell.yml
The playbook: detects the environment → (on matrix sandboxes) notes the 0 dB matrix → sets up the AP with an iperf3 server per client → associates every client with its own address → runs all clients' iperf3 concurrently → prints a per-client table and a cell summary.
Tune the SSID, passphrase, channel, or client count in group_vars/all.yml.
Real output — grid (over-the-air), 1 AP + 7 clients:
testbed=grid rf_matrix=False clients=7
...
node1-8 ip=192.168.100.26 rssi=-39 dBm throughput=2.0 Mbit/s
clients=7 aggregate=11.7 Mbit/s per-client=[1.7, 1.2, 1.6, 1.8, 1.9, 1.5, 2.0] fairness=0.978
The same playbook on sb4 (radios wired through the JFW matrix at 0 dB):
testbed=sb4 rf_matrix=True clients=7
RF-matrix sandbox 'sb4' detected: radios are wired through a JFW attenuator matrix (not over-the-air).
The matrix is at its 0 dB default (all node-to-node paths pass-through), so the cell behaves like the over-the-air grid.
...
clients=7 aggregate=19.4 Mbit/s per-client=[2.3, 3.8, 1.5, 4.0, 3.5, 1.7, 2.6] fairness=0.898
PLAY RECAP shows failed=0 on every node in both cases.
What to notice: a single 802.11g cell delivers on the order of ~10–20 Mbit/s total, split across all clients — adding clients does not add capacity, it divides it (high Jain fairness ≈ everyone gets a roughly equal share). The matrix path (sb4) shows lower RSSI (the matrix adds insertion loss) yet higher aggregate, because the wired paths avoid over-the-air interference and hidden-node contention.
ssh root@node1-1 'pkill -x hostapd; pkill -x iperf3'
for n in 2 3 4 5 6 7 8; do ssh root@node1-$n 'pkill -x wpa_supplicant'; done
omf tell -a offh -t node1-1,node1-2,node1-3,node1-4,node1-5,node1-6,node1-7,node1-8
baseline.ndz, or lacks an Atheros radio, is skipped with a clear message. Check omf stat and ssh root@<node> 'iw dev'.group_vars/all.yml and that all nodes share the channel.omf.