## Copyright © 2005  Gabriele Oligeri
## Copyright © 2006  Francesco Potortì <pot@gnu.org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software Foundation,
## Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA.
##################################

## -*- texinfo -*-
## @deftypefn {Function File} {} wifitmt
## @deftypefnx {Function File} {} wifitmt (@var{speed}, @var{pktlen}, @var{rts}, @var{shpr}, @var{bcast}, @var{bintv})
##
## Compute theoretical maximum throughput of a Wi-Fi channel
##
## Gives the net rate in Mb/s available on a Wi-Fi channel conformant to
## the IEEE 802.11 specifications.
##
## Called without arguments, asks for @var{speed}, that is, one of the
## nominal Wi-Fi rates in Mb/s.
##
## Optional @var{pktlen} is the packet lenght in bytes; it can be a
## vector. A value of 0 (the default) is special: it gives a strict
## upper bound on the TCP throughput by considering a steady-state
## regime of two 1500-bytes packets per ACK and accounting for TCP/IP
## headers overhead.
##
## RTS/CTS handshake is not used if optional @var{rts} flag is 0 (the default).
## Long preambles are used if optional @var{shpr} flag is 0 (the default).
## Time for ACK is computed if @var{bcast} (broadcast) flag is 0 (the default).
## Beacon interval in time units of 1024us is @var{bintv} (default 100);
## a value of 0 mean do not account for beacon overhead.
##
## Considers 8 bytes of LLC/SNAP headers, no encryption, no
## retransmissions and no management frames apart from the beacon.  The
## result is accurate for a two-nodes network where unicast traffic
## flows in a single direction without errors or collisions and ACKs are
## sent back at the same speed as the data.  To obtain the accurate
## result, you must set @var{pktlen} to a value different from 0.
## Beacon overhead includes a 16-bit SSID, 4 supported rates, no TIM or
## IBSS information.
## @end deftypefn

## Author: Gabriele Oligeri and Francesco Potortì
## $Revision: 1.4 $
## Usage: R = wifitmt(speed, pktlen, rts, shpr, bcast, bintv)
## Description: Compute theoretical maximum throughput of a Wi-Fi channel

function R = wifitmt(speed, pktlen, rts, shpr, bcast, bintv)

  speeds=[1, 2, 5.5, 11];	# Available speeds [Mb/s]
  llcsnap = 8;			# length of LLC/SNAP header [bytes]
  maxpktlen = 2304 - llcsnap;	# max payload length [bytes]

  ## Set default parameters
  if (nargin > 6)
    usage ("wifitmt(speed, pktlen, shpr, rts, bcast, bintv)");
  elseif (nargin < 6)
    bintv = 100;		# default: beacon every 102.4 ms
    if (nargin < 5)
      bcast = 0;		# default: unicast mode
      if (nargin < 4)
	shpr = 0;		# default: long preamble
	if (nargin < 3)
	  rts = 0;		# default: CSMA/CA (no RTS/CTS)
	  if (nargin < 2)
	    pktlen = 0;		# default: special value for TCP upper bound
	    if (nargin < 1)
	      choices = split(deblank(sprintf("%g Mb/s\n", speeds)),"\n");
	      speed = speeds(menu("Nominal speeds:", cellstr(choices){:}));
	    endif
	  endif
	endif
      endif
    endif
  endif

  ## Check input parameters
  if (any(pktlen) < 0 || any(pktlen) > maxpktlen)
    error("wifitmt: pktlen must be between 0 and %d", maxpktlen);
  elseif (isempty(find(speed == speeds)))
    error("wifitmt: speed %g not available", speed);
  elseif (bintv < 0 || bintv > 2^16)
    error("wifitmt: bintv must be in [0;65535]");
  endif

  #### TODO add 802.11g
  ## All figures are durations in microsesconds (us)
  backoff = (0+31)/2 * 20;	# mean backoff for first transmission attempt
  sifs    = 10;			# SIFS
  difs    = sifs+2*20;		# DIFS
  plcpovh = (18+6)*8;		# PLCP long preamble and header at 1 Mb/s
  macdata = (24+4)*8./speeds;	# MAC data header and FCS at PDU speed
  llcsnap = (3+5)*8./speeds;	# LLC+SNAP headers at PDU speed
  mlsdata = ceil(macdata+llcsnap);	# MAC+LLC+SNAP data overhead at PDU speed
  macack  = ceil((10+4)*8./speeds);	# MAC ACK and FCS at PDU speed
  macrts  = ceil((16+4)*8./speeds);	# MAC RTS and FCS at PDU speed
  maccts  = ceil((10+4)*8./speeds);	# MAC CTS and FCS at PDU speed
  beacon  = (24+41+4)*8;		# Beacon

  dataovh = difs+backoff+plcpovh+mlsdata;     # Data with long preamble [us]
  ackovh = sifs+plcpovh+macack;		      # ACK with long preamble [us]
  rtsctsovh = 2*(plcpovh+sifs)+macrts+maccts; # RTS/CTS with long preambles [us]
  shortprovh = [0 -96 -96 -96]; # Short preamble overhead [us] (negative)

  i = find(speed == speeds);	# index of the chosen speed
  d = dataovh(i);		# data overhead
  d += !bcast * ackovh(i);	# ACK overhead if unicast
  d += !!rts * rtsctsovh(i);	# RTS/CTS overhead if used
  d += !!shpr * (1+!bcast+2*!!rts) * shortprovh(i); # short preamble overhead
  s = speeds(i);		# channel rate in Mb/s
  if (isscalar(pktlen) && pktlen == 0) # -- special case for TCP upper bound
    n = 3;			# number of packets
    l = 8*(2*1500+40);		# total length in bits
    p = l-3*8*40;	        # payload without TCP/IP overhead
  else				# -- normal case
    n = 1;			# number of packets
    l = 8*pktlen;		# total length in bits
    p = l;			# payload
  endif
  R = p ./ (n*d + l./s);	# payload divided by total time [Mb/s]
  if (bintv > 0)		# account for beacon overhead
    R *= (1-(difs+backoff+plcpovh+beacon)/(bintv*1024));
  endif
end
