.. _networks:

*******************
Networks
*******************

.. currentmodule:: skrf.network


.. ipython::
	:suppress:
	
	
	In [144]: from pylab import *
	
	In [145]: ion();clf()
	
	


Introduction
-------------------------

This tutorial gives an overview of the microwave network analysis 
features of **skrf**. 

For this tutorial, and the rest of the scikit-rf documentation, it is  assumed that **skrf** has been imported as `rf`. Whether or not you follow this convention in your own code is up to you.


.. ipython::

  In [138]: import skrf as rf

If this produces an import error, please see :doc:`installation`.  Loading the  touchstone files referenced in this tutorial assume that you are in the directory `scikit-rf/doc`.




Creating Networks
-------------------------
**skrf** provides an object for a N-port microwave :class:`Network`. A :class:`Network` can be created in a number of ways. One way is from data stored in a touchstone file.

.. ipython::
			
	In [139]: ring_slot = rf.Network('../skrf/data/ring slot.s2p')

 
	
A short description of the network will be printed out if entered onto the command line
	
.. ipython::
	
	In [1]: ring_slot


Networks can also be created by directly passing values for the frequency, s-paramters  and z0. 

.. ipython::
	
	In [1]: custom_ntwk = rf.Network(f = [1,2,3], s= [-1, 1j, 0], z0=50) 
	In [1]: # `f` is interpreted in units of 'ghz'

See  :func:`Network.__init__`  for more informaition on network creation.

Network Basics
-------------------------
	
The basic attributes of a microwave :class:`Network` are provided by the 
following properties :

* :attr:`Network.s` : Scattering Parameter matrix. 
* :attr:`Network.z0`  : Port Characteristic Impedance matrix.
* :attr:`Network.frequency`  : Frequency Object. 

All of the network parameters are represented internally as complex :class:`numpy.ndarray` 's of shape *FxNxN*, where *F* is the number of frequency points and *N* is the number of ports.

.. ipython::
			
	In [139]: shape(ring_slot.s)

Note that the indexing starts at 0, so the first 10 values of :math:`S_{11}` can be accessed with slicing 

.. ipython::
			
	In [139]: ring_slot.s[:10,0,0]


The :class:`Network` object has numerous other properties and methods which can found in the :class:`Network` docstring. If you are using IPython, then these properties and methods can be 'tabbed' out on the command line. 


.. ipython::
	
	@verbatim
	In [1]: short.s<TAB>
	rf.data.line.s              rf.data.line.s_arcl         rf.data.line.s_im
	rf.data.line.s11            rf.data.line.s_arcl_unwrap  rf.data.line.s_mag
	...

.. note:: 

	Although this tutorial focuses on s-parametes, other  network representations such as Impedance (:attr:`Network.z`) and Admittance Parameters (:attr:`Network.y`) are available as well, see `Impedance and Admittance Parameters`_ .
	
Amongst other things, the methods of the :class:`Network` class provide convenient ways to plot components of the network parameters, 

* :func:`Network.plot_s_db` : plot magnitude of s-parameters in log scale
* :func:`Network.plot_s_deg` : plot phase of s-parameters in degrees
* :func:`Network.plot_s_smith` : plot complex s-parameters on Smith Chart
* ...

If you would like to use skrf's plot styling, call stylely.

.. ipython::

  In [138]: rf.stylely({'savefig.dpi':120})
  
To plot all four s-parameters of the `ring_slot` on the Smith Chart.

.. ipython::

   @savefig ring_slot,smith.png 
   In [151]: ring_slot.plot_s_smith();


Or plot a pair of s-parameters individually, in log magnitude 

.. ipython::

   In [153]: figure();
   
   In [153]: ring_slot.plot_s_db(m=1, n=0);	# s21
   
   @savefig ring_slot,db.png 
   In [153]: ring_slot.plot_s_db(m=0, n=0); # s11

For more detailed information about plotting see :doc:`plotting`.   

Network Operators
-------------------

Linear Operations 
=========================
	
Element-wise mathematical operations on the scattering parameter matrices are accessible through overloaded operators. To illustrate their usage, load a couple  Networks stored in the `data` module. 

.. ipython::
	
	In [21]: short = rf.data.wr2p2_short

	In [22]: delayshort = rf.data.wr2p2_delayshort

	In [22]: short - delayshort

	In [22]: short + delayshort

	In [22]: short * delayshort

	In [22]: short / delayshort
	
	In [22]: short / delayshort



All of these operations return :class:`Network` types, so the methods and properties of a :class:`Network` are available on the result.  For example, to plot the complex difference  between  `short` and `delay_short`,
	
.. ipython::
	
	In [21]: figure();
	
	In [21]: difference = (short- delayshort)
	
	@savefig operator_illustration,difference.png
	In [21]: difference.plot_s_mag()


Another common application is calculating the phase difference using the division operator,
	
.. ipython::
	
	In [21]: figure();
	
	@savefig operator_illustration,division.png
	In [21]: (delayshort/short).plot_s_deg()
	
Linear operators can also be used with scalars or an :class:`numpy.ndarray`  that ais the same length as the :class:`Network`. 

.. ipython::
	
	In [21]: open = (short*-1)

	In [21]: open.s[:3,...]
	
	In [21]: rando =  open *rand(len(open))
	
	In [21]: rando.s[:3,...]
	
Note that if you multiply a Network by an :class:`numpy.ndarray`  be sure to place the array on right side.

Cascading and De-embedding
==================================================
Cascading and de-embeding 2-port Networks can also be done though operators. The :func:`cascade` function can be called through the power operator,  `**`. To calculate a new network which is the cascaded connection of the two individual Networks `line` and `short`, 

.. ipython::
	
	In [21]: short = rf.data.wr2p2_short

	In [22]: line = rf.data.wr2p2_line
	
	In [22]: delayshort = line ** short

De-embedding  can be accomplished by cascading the *inverse* of a network. The inverse of a network is accessed through the property :attr:`Network.inv`. To de-embed the `short` from `delay_short`,

.. ipython::
	
	In [21]: short = line.inv ** delayshort


Connecting Multi-ports 
------------------------
**skrf** supports the connection of arbitrary ports of N-port networks. It accomplishes this using an algorithm called sub-network growth [#]_,  available through the function :func:`connect`. Terminating one port of an ideal 3-way splitter can be done like so,

.. ipython::
	
	In [21]: tee = rf.Network('../skrf/data/tee.s3p')
	

To connect port `1` of the tee, to port `0` of the delay short,

.. ipython::
	
	In [21]: terminated_tee = rf.connect(tee,1,delayshort,0)

Note that this function takes into account port impedances. If two connected ports have different port impedances,  an appropriate impedance mismatch is inserted.
	
Interpolation and Stitching 
-----------------------------
A common need is to change the number of frequency points of a :class:`Network`. For instance, to use the operators and cascading functions the networks involved must have matching frequencies. If two networks have different frequency information, then an error will be raised, 

.. ipython::
	
	In [21]: line = rf.data.wr2p2_line.copy()
	
	In [21]: line1 = rf.data.wr2p2_line1.copy()
	
	In [21]: line1
	
	In [21]: line
	
	In [21]: line1+line
	
This problem can be solved by interpolating one of Networks, using :func:`Network.resample`. 

.. ipython::
	
	In [21]: line1
	
	In [21]: line1.resample(201)
	
	In [21]: line1
	
	In [21]: line1+line


A related application is the need to combine Networks which cover different frequency ranges. Two  Netwoks can be stitched together using :func:`stitch`, which  concatenates their s-parameter matrices along their frequency axis. To combine a WR-2.2 Network with a WR-1.5 Network, 
 
.. ipython::
	
	In [21]: from skrf.data import wr2p2_line, wr1p5_line
	
	In [21]: line = rf.stitch(wr2p2_line, wr1p5_line)
	
	In [21]: line
	



	
Reading and Writing 
------------------------------
For long term data storage, **skrf** has support for reading and partial support for writing  `touchstone file format <http://en.wikipedia.org/wiki/Touchstone_file>`_ . Reading is accomplished with the Network initializer as shown above and writing with the method  :func:`Network.write_touchstone`.

For temporary data storage, **skrf** object can be `pickled <http://docs.python.org/2/library/pickle.html>`_ with  the functions :func:`~skrf.io.general.read` and :func:`~skrf.io.general.write`. 

.. ipython::
	
	In [21]: line = rf.Network('../skrf/data/line.s2p')
	
	@verbatim
	In [21]: rf.write(line) # write out Network using pickline IO
	line.ntwk
	
	@verbatim
	In [21]: rf.Network('line.ntwk') # read Network using native IO


.. warning:: 
	
	Pickling methods cant support long term data storage because they require the structure of the object being written to remain unchanged. something that cannot be guarnteed in future versions of skrf.  (see http://docs.python.org/2/library/pickle.html) 

Frequently there is an entire directory of files that need to be analyzed. The function :func:`~skrf.io.general.read_all` is used to create objects from all files in a directory quickly. Given a directory of skrf-readable files, :func:`~skrf.io.general.read_all`  returns a :class:`dict`  with keys equal to the filenames, and values equal to objects. To load all **skrf** files in the \`skrf/data/\` directory which contain the string `\'wr2p2\'`.
	
.. ipython::
	
	In [21]: dict_o_ntwks = rf.read_all('../skrf/data/', contains = 'wr2p2')
	
	In [21]: dict_o_ntwks
	
:func:`~skrf.io.general.read_all` has a companion function, :func:`~skrf.io.general.write_all` which takes a dictionary of **skrf** objects, and writes each object to an individual file. 
	
.. ipython::
	:verbatim:

	In [21]: rf.write_all(dict_o_ntwks, dir = '.')
	
	In [21]: ls
	wr2p2,delayshort.ntwk	wr2p2,line.ntwk		wr2p2,short.ntwk

It is also possible to write a dictionary of objects to a single file, by using :func:`~skrf.io.general.write`,

.. ipython::
	:verbatim:

	In [21]: rf.write('dict_o_ntwk.p', dict_o_ntwks)

	In [21]: ls
	dict_o_ntwk.p
	
A similar function :func:`~skrf.io.general.save_sesh`, can be used to 
save all **skrf** objects in the current namespace.


Impedance and Admittance Parameters	
------------------------------------
This tutorial focuses on s-parameters, but other network represenations are available as well. Impedance  and Admittance Parameters can be accessed through the parameters :attr:`Network.z` and :attr:`Network.y`, respectively. Scalar components of complex parameters, such as  :attr:`Network.z_re`, :attr:`Network.z_im` and plotting methods like :func:`Network.plot_z_mag` are available as well.

.. ipython::
	
	In [21]: ring_slot.z[:3,...]
	
	In [21]: figure();
	
	@savefig ring_slot_z_re.png
	In [21]: ring_slot.plot_z_im(m=1,n=0)
	


Creating Networks 'From Scratch'	
----------------------------------
A :class:`Network` can be created `from scratch` by  passing values of relevant properties as keyword arguments to the constructor,

.. ipython::
			
	In [139]: frequency = rf.Frequency(75,110,101,'ghz')
	
	In [139]: s = -1*ones(101)
	
	In [139]: wr10_short = rf.Network(frequency = frequency, s = s, z0 = 50 )

For  information on creating Networks representing transmission line and lumped components, see the :mod:`~skrf.media` module.

.. ipython::
	:suppress:
	
	In [144]: %logstop skrf_introduction.py


Sub-Networks
---------------------
Frequently, the one-port s-parameters of a multiport network's are of interest. These can be accessed by the sub-network properties, which return one-port :class:`Network` objects,

.. ipython::
	
	In [21]: port1_return = line.s11
	
	In [21]: port1_return 

References
----------	
.. [#] Compton, R.C.; , "Perspectives in microwave circuit analysis," Circuits and Systems, 1989., Proceedings of the 32nd Midwest Symposium on , vol., no., pp.716-718 vol.2, 14-16 Aug 1989. URL: http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=101955&isnumber=3167

.. _ipython: http://ipython.scipy.org/moin/	


.. ipython::
	:suppress:
		
	In [144]: close('all')
