"""
.. currentmodule:: pylayers.signal.standard
.. autosummary::
:members:
"""
import numpy as np
import json
import sys
if sys.version_info.major==2:
import ConfigParser
else:
import configparser as ConfigParser
import pylayers.util.pyutil as pyu
import pylayers.antprop.antenna as ant
from pylayers.util.project import *
[docs]class Band(PyLayers,dict):
""" A Band is a structured portion of the spectrum
A band is subdivided into channels
"""
def __init__(self,**kwargs):
"""
"""
defaults = {
'zone' : 'Europe',
'name' : 'ISM24',
'fstart' : 2.412,
'fstop' : 2.472,
'fstep' : 5,
'bmhz' : 20 }
for k in defaults:
if k not in kwargs:
kwargs[k]=defaults[k]
self['zone'] = kwargs['zone']
self['name'] = kwargs['name']
self['fstart'] = kwargs['fstart']
self['fstop'] = kwargs['fstop']
self['fstep'] = kwargs['fstep']
self['bmhz'] = kwargs['bmhz']
self.channelize()
[docs] def channelize(self):
"""
"""
self.chan={}
self.fcghz = np.arange(self['fstart'],self['fstop'],self['fstep']/1000.)
for k,fc in enumerate(self.fcghz):
if (fc>=4) & (fc<5):
channum = int(np.round((fc-4)*200))
if (fc>=5) & (fc<6):
channum = int(np.round((fc-5)*200))
if fc<4:
channum = k+1
self.chan[channum] = Channel(fc,self['bmhz'])
[docs] def select(self,lchan):
""" select a dictionnary of channel from a list of channels
"""
dchan = {}
for k,chan in enumerate(lchan):
dchan[k] = self[chan]
return(dchan)
[docs] def load(self,bandname,_fileini='spectrum.ini'):
""" load spectrum
"""
self._fileini = _fileini
self.config = ConfigParser.ConfigParser()
fp = open(pyu.getlong(_fileini,pstruc['DIRSIMUL']))
self.config.readfp(fp)
band = dict(self.config.items(bandname))
self['name'] = bandname
self['zone'] = band['zone']
self['fstart'] = eval(band['fstart'])
self['fstop'] = eval(band['fstop'])
self['fstep'] = eval(band['fstep'])
self['bmhz'] = eval(band['bmhz'])
self.channelize()
[docs]class Channel(PyLayers,dict):
""" a radio channel abstraction
"""
def __init__(self,fcghz,bmhz,gmhz=0):
"""
Parameters
----------
fcghz : float
center frequency
bmhz : float
effective bandwith
gmhz : float
guard frequency (inter channel gap)
"""
self['fcGHz'] = fcghz
self['BMHz'] = bmhz
self['GMHz'] = gmhz
self.fghz = np.array([fcghz-(bmhz+gmhz)/2000.,fcghz+(bmhz+gmhz)/2000.])
def __repr__(self):
""" representation
"""
st = str(self['fcGHz'])+' : ['+str(self.fghz[0])+','+str(self.fghz[1])+']\n'
return(st)
def __add__(self,chan):
""" add two adjascent channels
"""
if (self.fghz[1]==chan.fghz[0]):
self['fcGHz'] = self.fghz[1]
self['BMHz'] = self['BMHZ']+chan['BMHz']
self.fghz[1]=chan.fghz[1]
elif (self.fghz[0]==chan.fghz[1]):
self['fcGHz'] = self.fghz[0]
self['BMHz'] = self['BMHZ']+chan['BMHz']
self.fghz[0]=chan.fghz[0]
else:
pass
return(self)
[docs] def overlap(self,C):
""" tests wether 2 channels overlap
Parameters
----------
C : Channel
Returns
-------
ov : int
1 if overlaping channel
0 otherwise
Notes
-----
To be used later for optimization purpose
"""
if ( ( (self.f[1]>C.f[0]) & (self.f[1] < C.f[1]))
| ( (self.f[0]<C.f[1]) & (self.f[0] > C.f[0]))):
return(1)
else:
return(0)
[docs] def capacity(self,SNRdB):
""" calculates channel capacity
Parameters
----------
SNRdB : SNR in dB
Returns
-------
C : Channel capacity in Mbit/s (M=1e6)
"""
C = self.bmhz*np.log(1+10**(SNRdB/10.))/np.log(2)
return(C)
[docs]class Wstandard(PyLayers,dict):
""" Wireless standard class
The various available standard are described in a wstd.json file
"""
def __init__(self,stdname='',_filejson='wstd.json'):
"""
Parameters
----------
name : string
Examples
---------
>>> from pylayers.signal.standard import *
>>> Wifiag =Wstandard('ieee80211ah')
"""
self.name = stdname
if stdname != '':
self.load(stdname)
def __repr__(self):
try:
st = self.name+'\n'
st = st+'-------------------------\n'
for k in np.sort(self.chan.keys()):
st = st + str(k) +' : '+ self.chan[k].__repr__()
except:
st = 'No standard loaded \n'
st = st+'Check available standards with ls() method \n'
return(st)
[docs] def load(self, stdname, _fileini='wstd.json'):
""" load a standard from file
Parameters
----------
stdname : string
standard name
_fileini : string
file containing the description of available standards
"""
filename = pyu.getlong(_fileini,pstruc['DIRSIMUL'])
fp = open(filename)
stds = json.load(fp)
fp.close()
std = stds[stdname]
for k in std:
if k!= "channels":
try:
self[k] = eval(std[k])
except:
self[k] = std[k]
else:
chan = std[k]
for k in chan:
bandname = k
fstart=chan[k]['fstart']
fstop =chan[k]['fstop']
smhz =chan[k]['smhz']
bmhz = chan[k]['bmhz']
gmhz = chan[k]['gmhz']
if self.name=='generic':
self.bandplan(fstart=fstart,fstop=fstop,smhz=smhz,bmhz=bmhz,gmhz=gmhz,chan=False)
else:
self.bandplan(fstart=fstart,fstop=fstop,smhz=smhz,bmhz=bmhz,gmhz=gmhz)
[docs] def ls(self):
""" list all available standards
Examples
--------
.. plot::
:include-source:
>>> from pylayers.signal.standard import *
>>> W =Wstandard('ieee80211ah')
>>> W.ls()
"""
fp = open(pyu.getlong('wstd.json',pstruc['DIRSIMUL']))
stds = json.load(fp)
fp.close()
for k in stds:
print(k + ' , ',)
[docs] def power(self, band, info ='max', unit='mw'):
""" Returns power information for a given channel
Parameters
----------
band : int /float/string
'bandnb' : band number
'fghz' : frequency
'bandname' : band name
info : string ('max'|'min'|'step')
requested information about power
unit : string ('mw'|db)
miliwatt or db
Returns
-------
Pmaxmw
power informations for given bandnb/fghz/
"""
fp = open(pyu.getlong('wstd.json',pstruc['DIRSIMUL']))
stds = json.load(fp)
fp.close()
std = stds[self.name]
if info == 'max':
ii = 'pmaxmw'
if info == 'min':
ii = 'pminmw'
if info == 'step':
ii = 'pstepmw'
# band is a band number
if isinstance(band,int):
try:
fc = self.chan[band]['fcGHz']
except:
raise bandeError('incorrect channel number')
# band is a frequency
elif isinstance(band,float):
fc = band
# band is a band name
elif isinstance(band,str):
try:
f0 = std['channels'][band]['fstart']
f1 = std['channels'][band]['fstop']
bmhz = std['channels'][band]['bmhz']
gmhz = std['channels'][band]['gmhz']
f0g = f0 - (bmhz+gmhz)/2000.
f1g = f0 + (bmhz+gmhz)/2000.
fc = (f1g+f0g)/2.
except:
raise TypeError('Incorrect band name')
for k in std['channels'].keys():
f0 = std['channels'][k]['fstart']
f1 = std['channels'][k]['fstop']
bmhz = std['channels'][k]['bmhz']
gmhz = std['channels'][k]['gmhz']
f0g = f0 - (bmhz+gmhz)/2000.
f1g = f1 + (bmhz+gmhz)/2000.
bb = fc >= f0g and fc <= f1g
if bb:
power = stds[self.name]['channels'][k][ii]
if unit.lower() == 'db':
power = 10*np.log10(power)
return power
raise NameError('Requested information not in standard')
[docs] def bandplan(self,fstart,fstop,smhz=5,bmhz=20,gmhz=2,chan=True):
""" construct the different channels of the standard
Parameters
----------
fstart : start frequency GHz
fstop : stop frequency GHz
smhz : step between adjacscent channels
bmhz : useful channel bandwidth
gmhz : gap between channels
"""
if not(hasattr(self,'chan')):
self.chan={}
Nchannel = np.round((fstop-fstart)/(smhz/1000.)).astype(int)+1
fcghz = np.linspace(fstart,fstop,Nchannel,endpoint=True)
if chan:
for k,fc in enumerate(fcghz):
if (fc>=4) & (fc<5):
channum = int(np.round((fc-4)*200))
if (fc>=5) & (fc<6):
channum = int(np.round((fc-5)*200))
if fc<4:
channum = k+1
self.chan[channum] = Channel(fc,bmhz,gmhz)
else:
for k,fc in enumerate(fcghz):
self.chan[k]=Channel(fc,fcghz[1]-fcghz[0],0)
try:
self.fcghz=np.hstack((self.fcghz,fcghz))
except:
self.fcghz=fcghz
self.fcghz=np.sort(self.fcghz)
[docs]class AP(dict):
""" Access Point
Attributes
----------
pos : np.array(,3)
AP position in 3D
wstd : Wireless standard
PtdBm : Transmit Power
channels : list of used channels
snsdBm : float
receiver sensitivity
nant : int
number of antennas
"""
def __init__(self,**kwargs):
"""
Examples
--------
>>> import pylayers.signal.standard as std
>>> AP1 = AP()
>>> AP1.load()
"""
self['name'] = kwargs.pop('name','default')
self['p'] = kwargs.pop('p',np.array([0,0,1.2]))
self['PtdBm'] = kwargs.pop('PtdBm',0)
self['chan'] = kwargs.pop('chan',[11])
self['sensdBm'] = kwargs.pop('sensdBm',-94)
self['nant'] = kwargs.pop('nant',1)
self['on'] = kwargs.pop('on',True)
self['ant'] = kwargs.pop('ant','Omni')
self['phideg'] = kwargs.pop('phideg',0)
self['wstd'] = kwargs.pop('wstd','ieee80211b')
self.s = Wstandard(self['wstd'])
self.A = ant.Antenna(self['ant'])
def __repr__(self):
""" specific representation
It respects keys of the dictionnary
"""
st = 'name : '+str(self['name'])+'\n'
st = st + 'p : '+str(self['p'])+'\n'
st = st+ 'PtdBm : '+str(self['PtdBm'])+'\n'
st = st+ 'channels : '+str(self['chan'])+' '
for k in self['chan']:
st = st + self.s.chan[k].__repr__()
st = st+ 'sensdBm : '+str(self['sensdBm'])+'\n'
st = st+ 'ant : '+str(self['ant'])+'\n'
st = st+ 'phi (deg) : '+str(self['phideg'])+'\n'
st = st+ 'nant : '+str(self['nant'])+'\n'
st = st+ 'On : '+str(self['on'])+'\n'
return(st)
[docs] def load(self,name,_fileini='defAP.json'):
""" loading an access point from file
Parameters
----------
_fileini : string
access point description ini file
"""
self._fileini = _fileini
fileini = pyu.getlong(_fileini,pstruc['DIRSIMUL'])
fp = open(fileini,"r")
ap = json.load(fp)
self['name'] = name
dap = ap[name]
self['p'] = eval(dap['pos'])
wstd = dap['wstd']
self['PtdBm'] = dap['ptdbm']
self['channels'] = eval(dap['chan'])
self['sensdBm'] = dap['snsdbm']
self['nant'] = dap['nant']
standard = Wstandard(wstd)
self.s = standard
fp.close()