import skrf as rf
from pylab import *

"""
Explanation: Networks
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.
End of explanation
"""


from skrf import Network, Frequency

ring_slot = Network('data/ring slot.s2p')

"""
Explanation: If this produces an import error, please see Installation .
Creating Networks
skrf provides an object for a N-port microwave Network. A Network can be created in a number of ways. One way is from data stored in a touchstone file.
End of explanation
"""


ring_slot

"""
Explanation: A short description of the network will be printed out if entered onto the command line
End of explanation
"""


freq = Frequency(1,10,101,'ghz')
ntwk = Network(frequency=freq, s= [-1, 1j, 0], z0=50, name='slippy') 
ntwk

"""
Explanation: Networks can also be created by directly passing values for the frequency, s-paramters  and  port impedance z0.
End of explanation
"""


shape(ring_slot.s)

"""
Explanation: See  network  for more information on network creation.
Basic Properties
The basic attributes of a microwave Network are provided by the 
following properties :

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

The Network object has numerous other properties and methods. If you are using IPython, then these properties and methods can be 'tabbed' out on the command line. 
In [1]: ring_slot.s&lt;TAB&gt;
ring_slot.line.s              ring_slot.s_arcl         ring_slot.s_im
ring_slot.line.s11            ring_slot.s_arcl_unwrap  ring_slot.s_mag
...

All of the network parameters are represented internally as complex numpy.ndarray. The s-parameters are of shape (nfreq, nport, nport)
End of explanation
"""


ring_slot.s[:11,1,0]  # get first 10 values of S21

"""
Explanation: Slicing
You can  slice the Network.s attribute any way you want.
End of explanation
"""


ring_slot[0:10] #  Network for the first 10 frequency points

"""
Explanation: Slicing by frequency can also be done directly on Network objects like so
End of explanation
"""


ring_slot['80-90ghz']

"""
Explanation: or  with a human friendly string ,
End of explanation
"""


ring_slot.s11['80-90ghz'] 

"""
Explanation: Notice that slicing directly on a Network returns a Network.  So, a nice way to express slicing in both dimensions is
End of explanation
"""


%matplotlib inline 
rf.stylely()

"""
Explanation: Ploting
Amongst other things, the methods of the Network class provide convenient ways to plot components of the network parameters, 

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

If you would like to use skrf's plot styling,
End of explanation
"""


ring_slot.plot_s_smith()

"""
Explanation: To plot all four s-parameters of the ring_slot on the Smith Chart.
End of explanation
"""


from matplotlib import pyplot as plt

plt.title('Ring Slot $S_{21}$')

ring_slot.s11.plot_s_db(label='Full Band Response')
ring_slot.s11['82-90ghz'].plot_s_db(lw=3,label='Band of Interest')

"""
Explanation: Combining this with the slicing features,
End of explanation
"""


from skrf.data import wr2p2_short as short 
from skrf.data import wr2p2_delayshort as delayshort 


short - delayshort
short + delayshort
short * delayshort
short / delayshort


"""
Explanation: For more detailed information about plotting see Plotting.   
Operators
Arithmetic 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.
End of explanation
"""


difference = (short- delayshort)
difference.plot_s_mag(label='Mag of difference')

"""
Explanation: All of these operations return Network types.  For example, to plot the complex difference  between  short and delay_short,
End of explanation
"""


(delayshort/short).plot_s_deg(label='Detrended Phase')

"""
Explanation: Another common application is calculating the phase difference using the division operator,
End of explanation
"""


hopen = (short*-1)
hopen.s[:3,...]

rando =  hopen *rand(len(hopen))
rando.s[:3,...]

"""
Explanation: Linear operators can also be used with scalars or an numpy.ndarray  that ais the same length as the Network.
End of explanation
"""


short = rf.data.wr2p2_short
line = rf.data.wr2p2_line
delayshort = line ** short

"""
Explanation: Cascading and De-embedding
Cascading and de-embeding 2-port Networks can also be done though operators. The 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,
End of explanation
"""


short_2 = line.inv ** delayshort

short_2==short

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


tee = rf.data.tee
tee

"""
Explanation: Comparison operators also work with networks.
Connecting Multi-ports
skrf supports the connection of arbitrary ports of N-port networks. It accomplishes this using an algorithm called sub-network growth[1],  available through the function connect(). Terminating one port of an ideal 3-way splitter can be done like so,
End of explanation
"""


terminated_tee = rf.connect(tee,1,delayshort,0)
terminated_tee

"""
Explanation: To connect port 1 of the tee, to port 0 of the delay short,
End of explanation
"""


from skrf.data import wr2p2_line1 as line1

line1

"""
Explanation: 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 Concatenation
A common need is to change the number of frequency points of a Network. To use the operators and cascading functions the networks involved must have matching frequencies, for instance. If two networks have different frequency information, then an error will be raised,
End of explanation
"""


line1.resample(201)
line1

"""
Explanation: line1+line

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
&lt;ipython-input-49-82040f7eab08&gt; in &lt;module&gt;()
----&gt; 1 line1+line

/home/alex/code/scikit-rf/skrf/network.py in __add__(self, other)
    500 
    501         if isinstance(other, Network):
--&gt; 502             self.__compatable_for_scalar_operation_test(other)
    503             result.s = self.s + other.s
    504         else:

/home/alex/code/scikit-rf/skrf/network.py in __compatable_for_scalar_operation_test(self, other)
    701         '''
    702         if other.frequency  != self.frequency:
--&gt; 703             raise IndexError('Networks must have same frequency. See `Network.interpolate`')
    704 
    705         if other.s.shape != self.s.shape:

IndexError: Networks must have same frequency. See `Network.interpolate`

This problem can be solved by interpolating one of Networks allong the frequency axis using Network.resample.
End of explanation
"""


line1+line

"""
Explanation: And now we can do things
End of explanation
"""


line.interpolate_from_f(line1.frequency)

"""
Explanation: You can also interpolate from a Frequency object. For example,
End of explanation
"""


from skrf.data import wr2p2_line, wr1p5_line
	
big_line = rf.stitch(wr2p2_line, wr1p5_line)
big_line

"""
Explanation: A related application is the need to combine Networks which cover different frequency ranges. Two  Netwoks can be concatenated (aka stitched) together using stitch, which  concatenates networks along their frequency axis. To combine a WR-2.2 Network with a WR-1.5 Network,
End of explanation
"""


rf.write('data/myline.ntwk',line) # write out Network using pickle

ntwk = Network('data/myline.ntwk') # read Network using pickle

"""
Explanation: Reading and Writing
For long term data storage, skrf has support for reading and partial support for writing  touchstone file format. Reading is accomplished with the Network initializer as shown above,  and writing with the method  Network.write_touchstone().
For temporary data storage, skrf object can be pickled with  the functions skrf.io.general.read and skrf.io.general.write.  The reason to use temporary pickles over touchstones is that they store all attributes of a network, while touchstone files only store partial information.
End of explanation
"""


dict_o_ntwks = rf.read_all(rf.data.pwd, contains = 'wr2p2')
dict_o_ntwks

"""
Explanation: Frequently there is an entire directory of files that need to be analyzed. rf.read_all  creates Networks from all files in a directory quickly.  To load all skrf files in the data/ directory which contain the string 'wr2p2'.
End of explanation
"""


ring_slot.z[:3,...]

ring_slot.plot_z_im(m=1,n=0)

"""
Explanation: Other 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 Network.z and Network.y, respectively. Scalar components of complex parameters, such as  Network.z_re, Network.z_im and plotting methods are available as well.
Other parameters are only available for 2-port networks, such as wave cascading parameters (Network.t), and  ABCD-parameters (Network.a)
End of explanation
"""


