An annotated example¶
The examples
directory in the SpectrumWars source tree contains a number
of player code examples (see it on GitHub). You are
encouraged to explore the example code and study how it works. To help you,
better_cognitive.py
, one of the examples, is explained step-by-step in
this section.
Refer to the Class reference for details on SpectrumWars-specific method calls used. The examples also use numpy to simplify some parts of the code. See NumPy reference for details.
# Import the Transceiver base class.
from spectrumwars import Transceiver
# Also import NumPy to get some convenient array functions.
import numpy as np
# Two modules from the Python standard library we'll use.
import random
import time
# First, let's write the code that controls our source.
class Source(Transceiver):
# We use the procedural style in this example, so we only override the
# start() method. This way our code gets called right at the start of
# the game. Our source will not leave this method until the game
# ends.
def start(self):
# We simply make an infinite loop. We don't need to care what happens
# when the game ends - game controller takes care of cleaning up
# after us.
while True:
# Ignore this delay for now. Real testbeds have various delays
# when sending packets or when sensing the spectrum. Sometimes it
# helps to artificially slow down your algorithm.
time.sleep(.2)
# self.get_status().spectrum returns a list of received signal
# strength indicators (RSSI) for all channels in the testbed.
# Index into this array directly translates to the radio channel
# number.
#
# We convert the result to a NumPy array for convenience.
spectrum = np.array( self.get_status().spectrum )
# These two lines take the RSSI list and select one channel at
# random from 20 channels that have the lowest signal strength.
chl = np.argsort(spectrum)
ch = chl[random.randint(0, 20)]
# Now we tune the radio to the selected channel. We also select
# the slowest (and most reliable) bitrate setting and the
# strongest transmission power.
self.set_configuration(ch, 0, 0)
# Next, we transmit 20 packets on the selected channel. We don't
# add any control data to the packets, so the complete packet is
# filled with payload by the game controller.
for n in xrange(20):
self.send()
# We delay a little bit the transmission of packets.
time.sleep(.05)
# After 20 packets have been transmitted, our loop rolls around
# for another iteration. We again check the spectrum occupancy,
# select one of the channels that appear to be least occupied and
# transmit another 20 packets.
#
# The spectrum sensor has some averaging. If we would loop
# immediately from self.send() to self.get_status(), the spectral
# scan would still contain the trace of our own packets. Hence the
# 200 ms delay at the start of the loop to let our packets fall
# out of the averaging window and make it possible to select the
# same channel again.
# Now for the destination side of the code.
class Destination(Transceiver):
# Again, destination spends the duration of the game in the start() method.
def start(self):
# Since the first thing we do in the source is a 200 ms delay,
# there is no point in trying to receive anything earlier than that.
time.sleep(.2)
# Another infinite loop.
while True:
# Wait a bit more to be sure that the source is transmitting
# at this point and that its packets have been picket up by the
# spectrum sensor.
time.sleep(.1)
# Use the same method as in the source to get a NumPy array
# containing RSSI values for all channels.
spectrum = np.array( self.get_status().spectrum )
# This line uses a similar argsort trick as in the source.
# We want an array of channel numbers, sorted with the channel with
# the highest signal strength on top.
chl = np.argsort(spectrum)[::-1]
# For each channel of the top five by signal strength...
for ch in chl[:5]:
# ... tune the radio to that channel. Set bitrate to the same
# one as used by the source.
#
# We also set the transmit power to the higher setting. However
# we don't transmit anything from the destination side in this
# example.
self.set_configuration(ch, 0, 0)
# On the selected channel, wait 200 ms for a packet.
for packet in self.recv_loop(timeout=.2):
# We don't do anything with the received packet - the
# source did not include any control data that would be
# interesting to us.
#
# Game controller takes care of the payload data automatically.
#
# If a packet has been received within 200 ms, the inner for
# loop rolls around and waits 200 ms for another packet.
pass
# If a packet has not been received for 200 ms, the outer for
# loop tries with the next most occupied channel.
# If reception has been unsuccessful, the while loop rolls around
# and performs another spectral scan, repeating the process.
At this point, you should try running this example in the simulation a few times and check the resulting time-frequency diagrams. Try to run it in a game competing with some other example players. Find its flaws and see how it can be improved.