docs: def send_scp_burst(self, buffer_size, window_size,
                         parameters_and_callbacks):
          """Send a burst of SCP packets and call a callback for each returned
          packet.
  
          Parameters
          ----------
          buffer_size : int
              Number of bytes held in an SCP buffer by SARK, determines how many
              bytes will be expected in a socket.
          window_size : int
              Number of packets which can be awaiting replies from the SpiNNaker
              board.
          parameters_and_callbacks: iterable of :py:class:`.scpcall`
              Iterable of :py:class:`.scpcall` elements.  These elements can
              specify a callback which will be called with the returned packet.
          """
          parameters_and_callbacks = iter(parameters_and_callbacks)
  
          self.sock.setblocking(False)
  
          # Calculate the receive length, this should be the smallest power of
          # two greater than the required size
          max_length = buffer_size + consts.SDP_HEADER_LENGTH
          receive_length = int(2**math.ceil(math.log(max_length, 2)))
  
          class TransmittedPacket(object):
              """A packet which has been transmitted and still awaits a response.
              """
              __slots__ = ["callback", "packet", "bytestring", "n_tries",
                           "timeout", "timeout_time"]
  
              def __init__(self, callback, packet, timeout):
                  self.callback = callback
                  self.packet = packet
                  self.bytestring = packet.bytestring
                  self.n_tries = 1
                  self.timeout = timeout
                  self.timeout_time = time.time() + self.timeout
  
          queued_packets = True
          outstanding_packets = {}
          outstanding_callbacks = collections.deque()
  
          # While there are packets in the queue or packets for which we are
          # still awaiting returns then continue to loop.
          while queued_packets or outstanding_packets or outstanding_callbacks:
              # If there are fewer outstanding packets than the window can take
              # and we still might have packets left to send then transmit a
              # packet and add it to the list of outstanding packets.
              while len(outstanding_packets) < window_size and queued_packets:
                  try:
                      args = next(parameters_and_callbacks)
                  except StopIteration:
                      queued_packets = False
  
                  if queued_packets:
                      # If we extracted a new packet to send then create a new
                      # outstanding packet and transmit it.
                      seq = next(self.seq)
                      while seq in outstanding_packets:
                          # The seq should rarely be already taken, it normally
                          # means that one packet is taking such a long time to
                          # send that the sequence has wrapped around.  It's not
                          # a problem provided that we don't reuse the number.
                          seq = next(self.seq)
  
                      # Construct the packet that we'll be sending
                      packet = SCPPacket(
                          reply_expected=True, tag=0xff, dest_port=0,
                          dest_cpu=args.p, src_port=7, src_cpu=31,
                          dest_x=args.x, dest_y=args.y, src_x=0, src_y=0,
                          cmd_rc=args.cmd, seq=seq,
                          arg1=args.arg1, arg2=args.arg2, arg3=args.arg3,
                          data=args.data
                      )
  
                      # Create a reference to this packet so that we know we're
                      # expecting a response for it and can retransmit it if
                      # necessary.
                      outstanding_packets[seq] = TransmittedPacket(
                          args.callback, packet,
                          self.default_timeout + args.timeout
                      )
  
                      # Actually send the packet
                      self.sock.send(outstanding_packets[seq].bytestring)
  
              # Call all outstanding callbacks
              while outstanding_callbacks:
                  callback, packet = outstanding_callbacks.pop()
                  callback(packet)
  
              # Listen on the socket for an acknowledgement packet, there may not
              # be one.
              if outstanding_packets:
                  timeout = min((o.timeout_time for o in
                                 six.itervalues(outstanding_packets)
                                 )) - time.time()
              else:
                  timeout = 0.0
              r, w, x = select.select([self.sock], [], [], max(timeout, 0.0))
              # Process the received packet (if there is one).
              while r:
                  # Note that 'r' is never changed so this while loop will either
                  # get skipped if r is empty or loop until 'break' of r is not.
                  # Since we may receive multiple packets at once, it is better
                  # to try and pull all out of the socket immediately rather than
                  # running around the parent loop again and incuring the
                  # 'select' cost.
                  try:
                      ack = self.sock.recv(receive_length)
                  except IOError:
                      break
  
                  # Extract the sequence number from the bytestring, iff possible
                  rc, seq = struct.unpack_from("<2H", ack,
                                               consts.SDP_HEADER_LENGTH + 2)
  
                  # If the code is an error then we respond immediately
                  if rc != consts.SCPReturnCodes.ok:
                      if rc in consts.RETRYABLE_SCP_RETURN_CODES:
                          # If the error is timeout related then treat the packet
                          # as though it timed out, just discard.  This avoids us
                          # hammering the board when it's most vulnerable.
                          pass
                      else:
                          # For all other errors, we'll just fall over
                          # immediately.
                          packet = outstanding_packets.get(seq)
                          if packet is not None:
                              packet = packet.packet
                          raise FatalReturnCodeError(rc, packet)
                  else:
                      # Look up the sequence index of packet in the list of
                      # outstanding packets.  We may have already processed a
                      # response for this packet (indicating that the response
                      # was delayed and we retransmitted the initial message) in
                      # which case we can silently ignore the returned packet.
                      # XXX: There is a danger that a response was so delayed
                      # that we already reused the seq number... this is probably
                      # sufficiently unlikely that there is no problem.
                      outstanding = outstanding_packets.pop(seq, None)
                      if outstanding is not None:
                          outstanding_callbacks.appendleft((outstanding.callback,
                                                            ack))
  
              # Look through all the remaining outstanding packets, if any of
              # them have timed out then we retransmit them.
              current_time = time.time()
              for outstanding in six.itervalues(outstanding_packets):
                  if outstanding.timeout_time < current_time:
                      # This packet has timed out, if we have sent it more than
                      # the given number of times then raise a timeout error for
                      # it.
                      if outstanding.n_tries >= self.n_tries:
                          raise TimeoutError(
                              "No response after {} attempts.".format(
                                  self.n_tries),
                              outstanding.packet)
  
                      # Otherwise we retransmit it
                      self.sock.send(outstanding.bytestring)
                      outstanding.n_tries += 1
                      outstanding.timeout_time = (current_time +
                                                  outstanding.timeout)