Adding support for a new testbed¶
To add support for a new testbed, the following tasks need to be done:
- Add testbed specific code blocks
Add a module with the name like
spectrumwars.testbed.xxx. This module should define two classes:
Testbed, a subclass of
Radio, a subclass of
- Add testbed specific unit tests
- Add tests to a Python file with the name like
tests/test_xxx.py. Please make the tests automatically skip themselves if the testbed-specific hardware is not connected (e.g. raise the
- Add testbed documentation
- Add testbed documentation for players to
docs/reference.rst. Add any testbed-specific installation instructions to
Testbedobjects represent a physical testbed that is used to run a game. Unless stated otherwise, subclasses should override all of the methods and attributes described below.
The class constructor can take optional string-typed keyword arguments. These can be specified in
Should be set to the subclass of the
RadioBaseclass that is used by the testbed (i.e.
Radioin the testbed’s module)
txradioshould be instances of
This method is called multiple times by the game controller, once for each player to obtain the interfaces to player’s radios. It is called before the game starts, and before the call to
Called once, immediately before the start of the game.
Called after the game concluded. This method should perform any clean-up tasks required by the testbed (e.g. stopping any threads started by
Returns the number of frequency channels available to player’s code. The value returned should not change during the lifetime of the object.
Returns the number of bandwidth settings available to player’s code. The value returned should not change during the lifetime of the object.
Returns the number of transmission power settings available to player’s code. The value returned should not change during the lifetime of the object.
Returns the current state of the radio spectrum as a list of floating point values.
The value returned by this method gets assigned to
Returns the current testbed time in seconds since epoch as a floating point number. Selection of an epoch does not matter. Game controller requires only that time increases monotonically.
By default it returns
time.time(), which should be sufficient for most testbeds.
Radioobjects represent a player’s interface to a single transceiver. Unless stated otherwise, subclasses should override all of the methods described below.
Set to the maximum length of a string that can be passed to the
Approximately corresponds to
Transceiver.get_packet_size(). Game controller adds a header to separate control data from payload which adds an overhead of a few bytes. Because of this, the player visible maximum packet size will be lower.
set_configuration(frequency, bandwidth, power)¶
Set up the transceiver for transmission or reception of packets on the specified central frequency, power and bandwidth.
frequencyis specified as channel number from 0 to N-1, where N is the value returned by the
bandwidthis specified as an integer specifying the radio bitrate and channel bandwidth in the interval from 0 to N-1, where N is the value returned by the
Testbed.get_bandwidth_range()method. Higher values mean higher bitrates and wider channel bandwidths.
poweris specified as an integer specifying the transmission power in the interval from 0 to N-1, where N is the value returned by the
Testbed.get_power_range()method. Higher values mean lower power.
Send a data packet over the air.
bindatais a binary string with the data to be included into the packet. Length of
bindatacan be up to
Return a packet from the receive queue.
timeoutspecifies the receive timeout in seconds. If no packet is received within the timeout interval, the method raises
Upon successfull reception, the method should return a binary string. The returned string should be equal to the
bindataparameter that was passed to the corresponding
There is no way for the
Radioclass to push packets towards the game controller. Instead, the game controller polls the radio for received packets by calling
recv()method, as instructed by player’s code. Hence it is in most cases necessary that the actual packet reception happens in another thread (started typically from
Testbed.start()) and that the received packets are held in a queue until the next
SpectrumSensor(base_hz, step_hz, nchannels, time_window=200e-3, gain=10)¶
usrp_sensing.SpectrumSensoris a simple, reusable spectrum sensor implementation using a USRP device.
The sensing algorithm is inspired by a real-time signal analyzer. The recorded samples are converted into power spectral density using continuous end-to-end FFTs with no blind time (and no overlap of the FFT windows). The spectral power density is then averaged over a time window.
The algorithm is very CPU intensive. Using a 2.7 GHz CPU, it will be able to sense at most 64 channels (even if USRP frontend bandwidth would allow for more).
Sensing in this way is necessary because the radios usually have a very low duty cycle (e.g. a “while True: send()” has only around 10% duty cycle on the VESNA testbed). If we would only take one sample the spectrum when players request it, it would mostly appear empty. Hence the need to take a moving average if sensing is to be useful for detecting player transmissions.
base_hz is the lower bound of the frequency band used in the game in hertz. step_hz is the width of each channel. nchannels is the number of channels used in the game. The values for these parameters should be chosen so that the channel frequencies correspond to the channels used by the testbed’s
-------------------------------> frequency (Hz) +---+---+ +---+ | 0 | 1 | ... | n | (channels used in the game) +---+---+ +---+ |---| <- step_hz |-----------------| <- step_hz * nchannels ^ | base_hz
time_window defines the length of the moving average filter in seconds. The value depends on how often players can look up the current state of the spectrum. In most cases it should be longer than the period of
Transceiver.status_update()events in the event-based model.
Stop the worker thread.