├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST ├── README.md ├── control ├── LQR.py ├── StateFeedback.py ├── StateObserver.py ├── SystemIdentification.py ├── __init__.py ├── bode.py ├── kalman.py ├── linearize.py ├── rootlocus.py └── system.py ├── docs ├── Makefile ├── _build │ ├── doctrees │ │ ├── environment.pickle │ │ ├── index.doctree │ │ ├── intro.doctree │ │ ├── ref.doctree │ │ └── rep.doctree │ └── html │ │ ├── .buildinfo │ │ ├── .doctrees │ │ ├── environment.pickle │ │ ├── index.doctree │ │ ├── intro.doctree │ │ └── rep.doctree │ │ ├── _images │ │ ├── ss.png │ │ └── tf.png │ │ ├── _sources │ │ ├── index.rst.txt │ │ ├── intro.rst.txt │ │ ├── ref.rst.txt │ │ └── rep.rst.txt │ │ ├── _static │ │ ├── alabaster.css │ │ ├── basic.css │ │ ├── classic.css │ │ ├── custom.css │ │ ├── default.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── jquery-3.5.1.js │ │ ├── jquery.js │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── sidebar.js │ │ ├── underscore-1.3.1.js │ │ └── underscore.js │ │ ├── debug.log │ │ ├── genindex.html │ │ ├── index.html │ │ ├── intro.html │ │ ├── objects.inv │ │ ├── ref.html │ │ ├── rep.html │ │ ├── search.html │ │ └── searchindex.js ├── conf.py ├── index.rst ├── intro.rst ├── make.bat ├── ref.rst ├── rep.rst └── shared_images │ ├── ss.png │ └── tf.PNG ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── setup.py └── tests └── test_system.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.pytest_cache/* 2 | /control/__pycache__/* 3 | /tests/__pycache__/* 4 | /dist/* 5 | /control_toolbox.egg-info 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | - "3.7" 5 | before_install: 6 | - python --version 7 | - pip install -U pip 8 | - pip install -U pytest 9 | - export PYTHONPATH=$PYTHONPATH:$(pwd) 10 | install: 11 | - pip install -r requirements.txt 12 | - pip install -e . 13 | script: pytest 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rushad Mehta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | setup.cfg 3 | setup.py 4 | control\PID.py 5 | control\__init__.py 6 | control\rootlocus.py 7 | control\system.py 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Control Systems Toolbox 2 | [](https://travis-ci.com/rushad7/control-toolbox) 3 | [](https://control-toolbox.readthedocs.io/en/latest/?badge=latest) 4 | [](https://badge.fury.io/py/control-toolbox) 5 |  6 | 7 | The `control-toolbox` is a Python Library for implementing and simulating various systems and control strategies. 8 | 9 | ## Current Supported Functionality: 10 | 11 | - System modeling with Transfer Functions and State Space Representations. 12 | - Time Domain Response. 13 | - Frequency Response. 14 | - System Representation conversion: State Space model to Transfer Function and vice versa. 15 | - Block diagram algebra: Series and Parallel. 16 | - Stability Analysis. 17 | - Root Locus. 18 | - Bode Plot. 19 | - Parameterization of System. 20 | - Pole-Zero / Eigenvalue plot of systems. 21 | - Feedback analysis. 22 | - PID control. 23 | - Observability and Controllability. 24 | - Full State Feedback. 25 | - Full State Observer. 26 | - Linear Quadratic Regulator(LQR). 27 | - Linear Quadratic Estimator(LQE) / Kalman Filter. 28 | - Linearization. 29 | - System Identification. 30 | 31 | ## Future Updates: 32 | - Linear Quadratic Gaussian Control. 33 | - Extended Kalman Filter. 34 | - Unscented Kalman filter. 35 | - Model Predictive Control. 36 | 37 | ## Project Links 38 | Project Homepage: http://control-toolbox.rtfd.io/ 39 | Documentation: [https://control-toolbox.readthedocs.io/](https://control-toolbox.readthedocs.io/) 40 | 41 | ## Installation 42 | ### Pip 43 | To install using pip, run the following command: 44 | 45 | `pip install control-toolbox` 46 | 47 | ### Development 48 | To get the latest unreleased version: 49 | 50 | `git clone https://github.com/rushad7/control-toolbox.git` 51 | -------------------------------------------------------------------------------- /control/LQR.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 24 18:37:14 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | import numpy as np 9 | from .system import StateSpace 10 | 11 | class LQR(): 12 | 13 | def __init__(self, ss, Q, R, N): 14 | ''' 15 | Parameters 16 | ---------- 17 | ss : StateSpce object 18 | DESCRIPTION. StateSpace object on which LQR is to be done. 19 | Q : numpy array 20 | DESCRIPTION. Q matrix of cost function 21 | R : numpy 22 | DESCRIPTION. R matrix of cost function 23 | N : integer 24 | DESCRIPTION. Time horizon. 25 | 26 | Returns 27 | ------- 28 | None. 29 | 30 | ''' 31 | self._ss = ss 32 | self._Q = Q 33 | self._R = R 34 | self._N = N 35 | 36 | def solve(self): 37 | ''' 38 | Returns 39 | ------- 40 | k : numpy array 41 | DESCRIPTION. numpy array of Feedback gain matix 42 | 43 | ''' 44 | A = self._ss.A 45 | B = self._ss.B 46 | 47 | P_current = self._Q 48 | N_range = list(range(1, self._N))[::-1] 49 | 50 | P = {self._N:self._Q} 51 | K = {} 52 | 53 | for t in N_range: 54 | P_prev = self._Q + A.T@P_current@A - A.T@P_current@B@np.linalg.inv(self._R + B.T@P_current@B)@B.T@P_current@A 55 | P[t] = P_prev 56 | P_current = P_prev[:] 57 | 58 | for t in range(self._N): 59 | Kt = -np.linalg.inv(self._R + B.T@P[t+1]@B)@B.T@P[t+1]@A 60 | K[t] = Kt 61 | 62 | k = K[self._N-1] 63 | 64 | return k 65 | 66 | def model(self): 67 | ''' 68 | Returns 69 | ------- 70 | ss_model : TYPE 71 | DESCRIPTION. StateSpace object of system with StateFeedback 72 | 73 | ''' 74 | k = self.solve() 75 | 76 | A_new = self._ss.A - self._ss.B@k 77 | ss_model = StateSpace(A_new, self._ss.B, self._ss.C, self._ss.D) 78 | 79 | return ss_model 80 | -------------------------------------------------------------------------------- /control/StateFeedback.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 21 19:41:45 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | from .system import StateSpace 9 | import numpy as np 10 | from sympy import symbols, Matrix, eye, det, solveset, poly 11 | 12 | class StateFeedback(): 13 | 14 | ''' 15 | State Feedback control. 16 | Returns State Feeback Gain matrix 17 | ''' 18 | 19 | def __init__(self, ss): 20 | ''' 21 | Parameters 22 | ---------- 23 | ss : StateSpace Object 24 | DESCRIPTION. State Space represenation the system state feedback is applied to. 25 | 26 | Returns 27 | ------- 28 | None. 29 | 30 | ''' 31 | 32 | self._ss = ss 33 | q = self._ss.contr(ret=True) 34 | 35 | if np.linalg.det(q) == 0: 36 | print("Input a controlable system") 37 | 38 | elif np.linalg.det(q) != 0: 39 | print("Hence Full State Feedback is possible") 40 | 41 | self._lambd = symbols('lambd') 42 | 43 | k_str = "" 44 | for i in range(len(self._ss.A)): 45 | k_str = k_str + "k" + str(i+1) + "," 46 | 47 | k_str = k_str[:-1] 48 | k_var = symbols(k_str) 49 | k_matrix = Matrix(k_var).transpose() 50 | 51 | A_og = Matrix(ss.A) 52 | B_matrix = Matrix(ss.B) 53 | A_new = A_og - (B_matrix*k_matrix) 54 | 55 | char_eq = eye(len(ss.A))*self._lambd - A_new 56 | determinant = det(char_eq) 57 | self.charactaristic_eq = determinant 58 | 59 | def solve(self, roots): 60 | ''' 61 | Parameters 62 | ---------- 63 | roots : list 64 | DESCRIPTION. List of desired poles of the system 65 | 66 | Returns 67 | ------- 68 | state_feedback_matrix : numpy array 69 | DESCRIPTION. numpy array of state feedback gains 70 | 71 | ''' 72 | 73 | determinant = poly(self.charactaristic_eq, self._lambd) 74 | determinant_coefs = determinant.all_coeffs() 75 | 76 | char_eq_req = (self._lambd - roots[0])*(self._lambd - roots[1])*(self._lambd - roots[2]) 77 | char_eq_req = char_eq_req.expand() 78 | char_eq_req = poly(char_eq_req) 79 | char_eq_req_coefs = char_eq_req.all_coeffs() 80 | 81 | state_feedback_gain_list = [] 82 | for i in range(len(self._ss.A)): 83 | k_elem = float(list(solveset(determinant_coefs[i+1] - char_eq_req_coefs[i+1]).evalf())[0]) 84 | state_feedback_gain_list.append(k_elem) 85 | 86 | state_feedback_gain_list = state_feedback_gain_list[::-1] 87 | self.state_feedback_gain_matrix = np.array(state_feedback_gain_list).reshape(1, len(self._ss.A)) 88 | 89 | return self.state_feedback_gain_matrix 90 | 91 | def model(self, k_ref=1): 92 | ''' 93 | Parameters 94 | ---------- 95 | k_ref : float/integer, optional 96 | DESCRIPTION. Reference gain. Increase/Decrease this to adjust the steady state error. The default is 1. 97 | 98 | Returns 99 | ------- 100 | model : StateSpace object 101 | DESCRIPTION. Returns SS Model with State Feedback 102 | 103 | ''' 104 | A = self._ss.A 105 | B = self._ss.B 106 | C = self._ss.C 107 | D = self._ss.D 108 | K = self.state_feedback_gain_matrix 109 | 110 | A_new = A - np.matmul(B.reshape((len(self._ss.A),1)), K.reshape((1,len(self._ss.A)))) 111 | B_new = B*k_ref 112 | model = StateSpace(A_new,B_new,C,D) 113 | 114 | return model 115 | -------------------------------------------------------------------------------- /control/StateObserver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 25 16:36:40 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | from .system import StateSpace 9 | import numpy as np 10 | from sympy import symbols, Matrix, eye, det, solveset, poly 11 | 12 | class StateObserver(): 13 | 14 | ''' 15 | State Observer. 16 | Returns State Observer Gain matrix 17 | ''' 18 | 19 | def __init__(self, ss): 20 | ''' 21 | Parameters 22 | ---------- 23 | ss : StateSpace Object 24 | DESCRIPTION. State Space represenation the system. 25 | 26 | Returns 27 | ------- 28 | None. 29 | 30 | ''' 31 | 32 | self._ss = ss 33 | q = self._ss.obs(ret=True) 34 | 35 | if np.linalg.det(q) == 0: 36 | print("Input a observable system") 37 | 38 | elif np.linalg.det(q) != 0: 39 | print("Hence Full State observable is possible") 40 | 41 | self._lambd = symbols('lambd') 42 | 43 | k_str = "" 44 | for i in range(len(self._ss.A)): 45 | k_str = k_str + "k" + str(i+1) + "," 46 | 47 | k_str = k_str[:-1] 48 | k_var = symbols(k_str) 49 | k_matrix = Matrix(k_var).transpose() 50 | 51 | A_og = Matrix(ss.A) 52 | B_matrix = Matrix(ss.B) 53 | A_new = A_og - (B_matrix*k_matrix) 54 | 55 | char_eq = eye(len(ss.A))*self._lambd - A_new 56 | determinant = det(char_eq) 57 | self.charactaristic_eq = determinant 58 | 59 | def solve(self, roots): 60 | ''' 61 | Parameters 62 | ---------- 63 | roots : list 64 | DESCRIPTION. List of desired poles of the system 65 | 66 | Returns 67 | ------- 68 | state_observer_matrix : numpy array 69 | DESCRIPTION. numpy array of state observer gains 70 | 71 | ''' 72 | 73 | determinant = poly(self.charactaristic_eq, self._lambd) 74 | determinant_coefs = determinant.all_coeffs() 75 | 76 | char_eq_req = (self._lambd - roots[0])*(self._lambd - roots[1])*(self._lambd - roots[2]) 77 | char_eq_req = char_eq_req.expand() 78 | char_eq_req = poly(char_eq_req) 79 | char_eq_req_coefs = char_eq_req.all_coeffs() 80 | 81 | state_observer_gain_list = [] 82 | for i in range(len(self._ss.A)): 83 | k_elem = float(list(solveset(determinant_coefs[i+1] - char_eq_req_coefs[i+1]).evalf())[0]) 84 | state_observer_gain_list.append(k_elem) 85 | 86 | state_observer_gain_list = state_observer_gain_list[::-1] 87 | self.state_observer_gain_matrix = np.array(state_observer_gain_list).reshape(1, len(self._ss.A)) 88 | 89 | return self.state_observer_gain_matrix 90 | 91 | def model(self, k_ref=1): 92 | ''' 93 | Parameters 94 | ---------- 95 | k_ref : float/integer, optional 96 | DESCRIPTION. Reference gain. Increase/Decrease this to adjust the steady state error. The default is 1. 97 | 98 | Returns 99 | ------- 100 | model : StateSpace object 101 | DESCRIPTION. Returns SS Model with State Observer added 102 | 103 | ''' 104 | A = self._ss.A 105 | B = self._ss.B 106 | C = self._ss.C 107 | D = self._ss.D 108 | K = self.state_observer_gain_matrix 109 | 110 | A_new = A - np.matmul(B.reshape((len(self._ss.A),1)), K.reshape((1,len(self._ss.A)))) 111 | B_new = B*k_ref 112 | model = StateSpace(A_new,B_new,C,D) 113 | 114 | return model 115 | -------------------------------------------------------------------------------- /control/SystemIdentification.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Aug 2 16:01:30 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | import warnings 9 | import numpy as np 10 | import pandas as pd 11 | from tensorflow.keras import initializers 12 | from tensorflow.keras.layers import Dense 13 | from tensorflow.keras.models import Sequential 14 | 15 | warnings.filterwarnings("ignore") 16 | 17 | class SystemIdentification(): 18 | ''' 19 | System Identification module 20 | ''' 21 | def __init__(self, path_x, path_x_dot, path_y): 22 | ''' 23 | Parameters 24 | ---------- 25 | path_x : file path 26 | DESCRIPTION. path to CSV file consisting the X matrix data. 27 | path_x_dot : file path 28 | DESCRIPTION. path to CSV file consisting X_dot matrix data. 29 | path_y : file path 30 | DESCRIPTION. path to CSV file consisting Y matrix data. 31 | 32 | Returns 33 | ------- 34 | None. 35 | 36 | ''' 37 | data_x = pd.read_csv(path_x) 38 | data_x_dot = pd.read_csv(path_x_dot) 39 | data_y = pd.read_csv(path_y) 40 | 41 | self._x = data_x.to_numpy() 42 | self._x_dot = data_x_dot.to_numpy() 43 | self._y = data_y.to_numpy() 44 | 45 | def fit(self, num_epochs=500): 46 | ''' 47 | Parameters 48 | ---------- 49 | num_epochs : int, optional 50 | DESCRIPTION. Number of epochs. The default is 500. 51 | 52 | Returns 53 | ------- 54 | None. 55 | 56 | ''' 57 | self._stateModel = Sequential() 58 | self._stateModel.add(Dense(self._x.shape[0], input_dim=2, activation='sigmoid', kernel_initializer=initializers.glorot_normal())) 59 | self._stateModel.add(Dense(self._x.shape[0]/2, activation='sigmoid')) 60 | self._stateModel.add(Dense(2, activation='relu')) 61 | self._stateModel.compile(loss='mse', optimizer='adam', metrics=['accuracy']) 62 | self._stateModel.fit(self._x, self._x_dot, epochs=num_epochs, batch_size=10) 63 | 64 | self._outputModel = Sequential() 65 | self._outputModel.add(Dense(self._x.shape[0], input_dim=2, activation='sigmoid', kernel_initializer=initializers.glorot_normal())) 66 | self._outputModel.add(Dense(self._x.shape[0]/2, activation='sigmoid')) 67 | self._outputModel.add(Dense(1, activation='relu')) 68 | self._outputModel.compile(loss='mse', optimizer='adam', metrics=['accuracy']) 69 | self._outputModel.fit(self._x, self._y, epochs=num_epochs, batch_size=10) 70 | 71 | def model(self): 72 | ''' 73 | Returns 74 | ------- 75 | model_dict : dict 76 | DESCRIPTION. dictionary of matrices A and C. 77 | 78 | ''' 79 | hyp_state = self._stateModel.predict(self._x) 80 | hyp_output = self._stateModel.predict(self._x) 81 | 82 | A = np.linalg.pinv(self._x)@hyp_state 83 | C = hyp_output.T@np.linalg.pinv(self._x).T 84 | 85 | model_dict = {"A":A, "C":C} 86 | 87 | return model_dict -------------------------------------------------------------------------------- /control/__init__.py: -------------------------------------------------------------------------------- 1 | from .system import TransferFunction, feedback, PID, reduce, StateSpace 2 | from .rootlocus import rootlocus 3 | from .bode import bode 4 | from .StateFeedback import StateFeedback 5 | from .LQR import LQR 6 | from .StateObserver import StateObserver 7 | from .kalman import KalmanFilter 8 | from .linearize import linearize 9 | from .SystemIdentification import SystemIdentification 10 | __version__ = '0.1.0' 11 | -------------------------------------------------------------------------------- /control/bode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 14 15:25:37 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | from scipy.signal.filter_design import freqs 11 | 12 | class bode(): 13 | ''' 14 | Bode Plot of TF 15 | ''' 16 | def __init__(self): 17 | pass 18 | 19 | def freqresp(self, tf): 20 | ''' 21 | Parameters 22 | ---------- 23 | tf : TransferFuction object 24 | DESCRIPTION. TF of which frequency resonse is needed 25 | 26 | Returns 27 | ------- 28 | w : numpy array 29 | DESCRIPTION. Angular Frequency array [rad/s] 30 | h : numpy array 31 | DESCRIPTION. response of the system 32 | ''' 33 | 34 | w, h = freqs(tf.num_coef.flatten(), tf.den_coef.flatten()) 35 | return w, h 36 | 37 | def bode(self, system, ret=False, show=True): 38 | ''' 39 | Parameters 40 | ---------- 41 | system : TransferFunction object 42 | DESCRIPTION. TF object of which bode plot is needed 43 | ret : bool, optional 44 | DESCRIPTION. Return w, mag, phase. The default is False. 45 | show : bool, optional 46 | DESCRIPTION. Plot the response. The default is True. 47 | 48 | Returns 49 | ------- 50 | w : numpy array 51 | DESCRIPTION. Frequency array 52 | mag : numpy array 53 | DESCRIPTION. magnitude array 54 | phase : numpy array 55 | DESCRIPTION. phase array 56 | ''' 57 | 58 | w, y = self.freqresp(system) 59 | mag = 20.0 * np.log10(abs(y)) 60 | phase = np.unwrap(np.arctan2(y.imag, y.real)) * 180.0 / np.pi 61 | 62 | if show == True: 63 | plt.figure() 64 | plt.semilogx(w, mag) 65 | plt.figure() 66 | plt.semilogx(w, phase) 67 | plt.show() 68 | 69 | if ret == True: 70 | return w, mag, phase -------------------------------------------------------------------------------- /control/kalman.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 25 19:01:57 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | class KalmanFilter(): 12 | 13 | def __init__(self, ss, Q, R, P=None, x0=None): 14 | 15 | self._ss = ss 16 | self._A = self._ss.A 17 | self._B = self._ss.B 18 | self._C = self._ss.C 19 | 20 | self._n = self._A.shape[1] 21 | self._m = self._C.shape[1] 22 | 23 | self.Q = Q 24 | self.R = R 25 | self.P = np.eye(self._n) if P is None else P 26 | self.x = np.zeros((self._n, 1)) if x0 is None else x0 27 | 28 | def predict(self, u = 0): 29 | 30 | self.x = np.dot(self._A, self.x) + np.dot(self._B, u) 31 | self.P = np.dot(np.dot(self._A, self.P), self._A.T) + self.Q 32 | 33 | return self.x 34 | 35 | def update(self, z): 36 | 37 | inv = np.linalg.inv(self.R + np.dot(self._C, np.dot(self.P, self._C.T))) 38 | K = np.dot(np.dot(self.P, self._C.T), inv) 39 | 40 | y = z - np.dot(self._C, self.x) 41 | self.x = self.x + np.dot(K, y) 42 | 43 | I = np.eye(self._n) 44 | self.P = np.dot(np.dot(I - np.dot(K, self._C), self.P), (I - np.dot(K, self._C)).T) + np.dot(np.dot(K, self.R), K.T) 45 | 46 | y = np.dot(self._C,self.x) 47 | if y.shape == (1,1): 48 | y = float(y) 49 | return self.x,y,K 50 | 51 | def solve(self, measurements, ret_k=False, ret_x=False): 52 | 53 | time = np.array(range(len(measurements))) 54 | predictions = [] 55 | K = [] 56 | X = [] 57 | 58 | for z in measurements: 59 | x,y,k = self.update(z) 60 | predictions.append(y) 61 | K.append(k) 62 | X.append(x) 63 | 64 | if ret_k == False and ret_x == False: 65 | return predictions 66 | elif ret_k == False and ret_x == True: 67 | return predictions, X 68 | elif ret_k == True and ret_x == False: 69 | return predictions, K 70 | elif ret_k == True and ret_x == True: 71 | return predictions, K, X -------------------------------------------------------------------------------- /control/linearize.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jul 29 17:10:26 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | import numpy as np 9 | from sympy import symbols, Matrix, exp 10 | 11 | class linearize(): 12 | """ 13 | Linearizes systems. 14 | """ 15 | def __init__(self, ss, x0, x_op, u_op, y_op): 16 | ''' 17 | Parameters 18 | ---------- 19 | ss : StateSpace object 20 | DESCRIPTION. StateSpace of the system to be linearized. 21 | x0 : numpy array 22 | DESCRIPTION. ndarray of initial conditions 23 | x_op : numpy array 24 | DESCRIPTION. ndarray of operating points system is to be linearized about 25 | u_op : numpy array 26 | DESCRIPTION. ndarray of operating points system is to be linearized about 27 | y_op : numpy array 28 | DESCRIPTION. ndarray of operating points system is to be linearized about 29 | 30 | Returns 31 | ------- 32 | None. 33 | 34 | ''' 35 | self._x_op = x_op 36 | self._y_op = y_op 37 | self._u_op = u_op 38 | 39 | t = symbols("t") 40 | 41 | x_str = "" 42 | for i in range(len(ss.A)): 43 | x_str = x_str + "x" + str(i+1) + "," 44 | x_str = x_str[:-1] 45 | x_var = symbols(x_str) 46 | self._x_matrix = Matrix([x_var]) 47 | 48 | u_str = "" 49 | for i in range(len(ss.A)-1): 50 | u_str = u_str + "u" + str(i+1) + "," 51 | u_str = u_str[:-1] 52 | u_var = symbols(u_str) 53 | self._u_matrix = Matrix([u_var]) 54 | 55 | state_trans_matrix = exp(Matrix(ss.A)*t) 56 | self._x0 = np.reshape(x0, (1,np.product(x0.shape))) 57 | self._xt = Matrix(state_trans_matrix.dot(Matrix(x0))) 58 | 59 | self._yt = Matrix(ss.C)*(self._x_matrix).T + Matrix(ss.D)*(self._u_matrix) 60 | 61 | self._ft = Matrix(ss.A)*(self._x_matrix).T + Matrix(ss.B)*(self._u_matrix) 62 | self._gt = Matrix(ss.C)*(self._x_matrix).T + Matrix(ss.D)*(self._u_matrix) 63 | 64 | def linearize(self): 65 | ''' 66 | Returns 67 | ------- 68 | coefs_matrix : dict 69 | DESCRIPTION. List of matrices A, B, C, D for the linearized system. 70 | vars_matrix : dict 71 | DESCRIPTION. List of matrices xt, yt, ut for the linearized system. 72 | 73 | ''' 74 | del_xt = self._xt - self._x_op 75 | del_yt = self._yt - self._y_op 76 | del_ut = self._u_matrix - self._u_op 77 | 78 | A_lin = self._ft.jacobian(self._x_matrix) 79 | B_lin = self._ft.jacobian(self._u_matrix) 80 | C_lin = self._gt.jacobian(self._x_matrix) 81 | D_lin = self._gt.jacobian(self._u_matrix) 82 | 83 | coefs_matrix = {"A":np.array(A_lin), "B":np.array(B_lin), "C":np.array(C_lin), "D":np.array(D_lin)} 84 | vars_matrix = {"del_xt":np.array(del_xt), "del_yt":np.array(del_yt), "del_ut":np.array(del_ut)} 85 | 86 | return coefs_matrix, vars_matrix 87 | 88 | 89 | -------------------------------------------------------------------------------- /control/rootlocus.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 7 16:20:26 2020 4 | 5 | @author: Rushad 6 | """ 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | class rootlocus(): 12 | 13 | def __init__(self, tf, gain_range=10.0): 14 | 15 | ''' 16 | Parameters 17 | ---------- 18 | tf : Transfer Function object 19 | DESCRIPTION. TF object whose root locus we want to find 20 | gain_range : float / int, optional 21 | DESCRIPTION. Range of the gain. The default is 10.0. 22 | 23 | Returns 24 | ------- 25 | Root Locus plot of the system 26 | ''' 27 | 28 | gains = np.linspace(0.0, float(gain_range), num=500) 29 | 30 | num = tf.num_coef 31 | num = list(tf.num_coef.reshape(len(num),)) 32 | den = tf.den_coef 33 | den = list(tf.den_coef.reshape(len(den),)) 34 | size = len(den) - len(num) 35 | temp = np.zeros(size) 36 | num = np.concatenate((temp, num)) 37 | tf = np.vstack((num, den)) 38 | 39 | def compute_roots(tf, gains): 40 | num, den = tf[0], tf[1] 41 | roots = [] 42 | 43 | for gain in gains: 44 | ch_eq = den + gain*num 45 | ch_roots = np.roots(ch_eq) 46 | ch_roots.sort() 47 | roots.append(ch_roots) 48 | 49 | # convert final roots list into array 50 | roots = np.vstack(roots) 51 | 52 | return roots 53 | 54 | 55 | def plot_root_locus(gains, roots): 56 | 57 | real_vals = np.real(roots) 58 | imag_vals = np.imag(roots) 59 | 60 | colors = ['b', 'm', 'c', 'r', 'g'] 61 | 62 | fig, ax = plt.subplots() 63 | ax.set_xlabel('Re') 64 | ax.set_ylabel('Im') 65 | ax.axvline(x=0, color='k', lw=1) 66 | ax.grid(True, which='both') 67 | 68 | # plots a blue "x" for the first roots 69 | ax.scatter(real_vals[0, :], imag_vals[0, :], 70 | marker='x', 71 | color='blue') 72 | 73 | # plots a red "o" for the last roots 74 | ax.scatter(real_vals[-1, :], imag_vals[-1, :], marker='o', color='red') 75 | 76 | temp_real_vals = real_vals[1:-1, :] 77 | temp_imag_vals = imag_vals[1:-1, :] 78 | color_range = range(temp_real_vals.shape[1]) 79 | 80 | # plot the rest of the roots in different colors with respect to the regions 81 | for r, i, j in zip(temp_real_vals.T, temp_imag_vals.T, color_range): 82 | ax.plot(r, i, color=colors[j]) 83 | 84 | return fig, ax 85 | 86 | roots = compute_roots(tf, gains) 87 | fig, ax = plot_root_locus(gains, roots) 88 | plt.show() -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/intro.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/doctrees/intro.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/ref.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/doctrees/ref.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/rep.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/doctrees/rep.doctree -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: b305a246092b59063e7917e06b57d270 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/.doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/.doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/intro.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/.doctrees/intro.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/rep.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/.doctrees/rep.doctree -------------------------------------------------------------------------------- /docs/_build/html/_images/ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/_images/ss.png -------------------------------------------------------------------------------- /docs/_build/html/_images/tf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/_images/tf.png -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | ============================== 2 | Python Control Systems Toolbox 3 | ============================== 4 | 5 | The `control-toolbox` is a Python Library for implementing and simulating various systems and control strategies. 6 | 7 | .. rubric:: Current Supported Functionality: 8 | 9 | - System modeling with Transfer Functions and State Space Representations. 10 | - Time Domain Response. 11 | - Frequency Response. 12 | - System Representation conversion: State Space model to Transfer Function and vice versa. 13 | - Block diagram algebra: Series and Parallel. 14 | - Stability Analysis. 15 | - Root Locus. 16 | - Bode Plot. 17 | - Parameterization of System. 18 | - Pole-Zero / Eigenvalue plot of systems. 19 | - Feedback analysis. 20 | - PID control. 21 | - Observability and Controllability. 22 | - Full State Feedback 23 | - Full State Observer 24 | - Linear Quadratic Regulator(LQR) 25 | - Linear Quadratic Estimator(LQE) / Kalman Filter 26 | - Linearization. 27 | - System Identification. 28 | 29 | .. rubric:: Future Updates: 30 | 31 | - Linear Quadratic Gaussian Control. 32 | - Extended Kalman Filter. 33 | - Unscented Kalman filter. 34 | - Model Predictive Control. 35 | 36 | .. rubric:: Documentation: 37 | 38 | .. toctree:: 39 | :maxdepth: 2 40 | 41 | intro 42 | rep 43 | ref 44 | 45 | * :ref:`genindex` -------------------------------------------------------------------------------- /docs/_build/html/_sources/intro.rst.txt: -------------------------------------------------------------------------------- 1 | ============================== 2 | Introduction 3 | ============================== 4 | 5 | The `control-toolbox` is a Python Library for implementing and simulating various systems and control strategies. All information regarding this library can be found here, including the documentation and usage of the modules. 6 | 7 | Installation 8 | =============== 9 | | The `control-toolbox` library can be installed using pip. 10 | To install it use: 11 | :: 12 | pip install control-toolbox 13 | 14 | Development 15 | ============ 16 | | To get the latest unreleased version 17 | `git clone https://github.com/rushad7/control-toolbox.git` 18 | 19 | Getting Started 20 | =============== 21 | To start using the package, we simply import it as: 22 | :: 23 | >>> import control 24 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/ref.rst.txt: -------------------------------------------------------------------------------- 1 | ==================== 2 | Function Reference 3 | ==================== 4 | 5 | The following table lists the classes and methods that can be used to model and simulate various systems and control strategies. 6 | 7 | System Definition 8 | ***************** 9 | .. csv-table:: 10 | :header: "Class", "Description" 11 | :widths: 40, 40 12 | 13 | "`TransferFunction(num, den)`", "Creates a Transfer Function system object" 14 | "`StateSpace(A,B,C,D)`", "Creates a State Space system object" 15 | 16 | Transfer Function Methods 17 | *************************** 18 | .. csv-table:: 19 | :header: "Method", "Description" 20 | :widths: 40, 40 21 | 22 | "`display()`", "Displays the Transfer Function system" 23 | "`parameters(settling_time_tolerance=0.02)`", "Returns the parameters of the system" 24 | "`response(input_type, time_period=10, sample_time=0.05, ret=False, show=True)`", "Returns the time domain response on the system" 25 | "`pzplot(ret=True)`", "Plots the Pole-Zero plot of the system and return poles and zeros" 26 | "`stability()`", "Returns the stability of the system" 27 | "`rootlocus(tf, gain_range=10.0)`", "Returns Root Locus of the system" 28 | 29 | State Space Methods 30 | ******************** 31 | .. csv-table:: 32 | :header: "Method", "Description" 33 | :widths: 40, 40 34 | 35 | "`display()`", "Displays the State Space system" 36 | "`convert2TF()`", "Converts State Space model to Transfer Function" 37 | "`contr()`", "Controllability of the system" 38 | "`obs()`", "Observability of the system" 39 | "`stability()`", "Stability of the system" 40 | "`eigplot(ret=True)`", "Plots eigenvalues/poles of system" 41 | "`StateResponse(t, initial_cond, u, ret=False, show=True)`", "Returns and plots state response" 42 | "`OutputResponse(t, initial_cond, u, ret=False, show=True)`", "Returns and plots output response" 43 | 44 | System Connections 45 | ******************* 46 | .. csv-table:: 47 | :header: "Method", "Description" 48 | :widths: 40, 40 49 | 50 | "`feedback( G, H=1.0, feedback_type=""negative"")`", "Creates a Feedback Object" 51 | "`reduce.series(tf1, *tfn)`", "Return the series connection (tfn * …" 52 | "`reduce.parallel(tf1, *tfn)`", "Return the parallel connection (tfn + …" 53 | 54 | Frequency Domain Analysis 55 | ************************* 56 | 57 | .. csv-table:: 58 | :header: "Method", "Description" 59 | :widths: 40, 40 60 | 61 | "`bode.freqresp(tf)`", "Returns the Frequency Response of the system" 62 | "`bode.bode(tf)`", "Returns the Bode Plot of the system" 63 | 64 | PID Controller Design 65 | ********************** 66 | .. csv-table:: 67 | :header: "Method", "Description" 68 | :widths: 40, 40 69 | 70 | "`PID(Kp, Ki, Kd, tf)`", "Creates a PID object" 71 | "`response(input_type, time_period=10, sample_time=0.05, ret=False, show=True)`", "Return the response of the system after PID control" 72 | "`tune(input_type=""step"", set_point=1, num_itr=70, rate=0.00000000001, lambd=0.7)`", "Tune the PID coefficients" 73 | "`display()`", "Display the PID block" 74 | "`reduced_tf`", "Displays the reduced Transfer Function (Controller + Plant)" 75 | 76 | State Feedback Design 77 | ********************* 78 | .. csv-table:: 79 | :header: "Method", "Description" 80 | :widths: 40, 40 81 | 82 | "`StateFeedback(ss)`", "Creates a StateFeedback object" 83 | "`solve(roots)`", "Calculates the State Feedback gain matrix" 84 | "`model(k_ref=1)`", "Retruns StateSpace object after applying State Feedback" 85 | 86 | State Observer Design 87 | ********************* 88 | .. csv-table:: 89 | :header: "Method", "Description" 90 | :widths: 40, 40 91 | 92 | "`StateObserver(ss)`", "Creates StateObserver object" 93 | "`solve(roots)`", "Calculates the State Observer gain matrix" 94 | "`model(k_ref=1)`", "Retruns the StateSpace object after applying State Observervation" 95 | 96 | Linear Quadratic Regulator(LQR) 97 | ******************************* 98 | .. csv-table:: 99 | :header: "Method", "Description" 100 | :widths: 40, 40 101 | 102 | "`LQR(ss, Q, R, N)`", "Creates an LQR object" 103 | "`solve()`", "Calculates the optimal gain matrix" 104 | "`model()`", "Returns the StateSpace object after applying State Observation" 105 | 106 | Kalman Filter / Linear Quadratic Estimator(LQE) 107 | *********************************************** 108 | .. csv-table:: 109 | :header: "Method", "Description" 110 | :widths: 40, 40 111 | 112 | "`KalmanFilter(ss, Q, R, P=None, x0=None)`", "Creates a KalmanFilter object" 113 | "`predict(u = 0):`", "Predict next state" 114 | "`update(z):`", "Update Predictions" 115 | "`solve(measurements, ret_k=False, ret_x=False):`", "Returns the Predictions, Kalman Gain, and States of the system" 116 | 117 | Linearization 118 | ************** 119 | .. csv-table:: 120 | :header: "Method", "Description" 121 | :widths: 40, 40 122 | 123 | "`linearize(ss, x0, x_op, u_op, y_op)`", "Creates a `linearize` object" 124 | "`linearize()`", "Linearizes the system and returns the systems state space matrices" 125 | 126 | System Identification 127 | *********************** 128 | .. csv-table:: 129 | :header: "Method", "Description" 130 | :widths: 40, 40 131 | 132 | "`SystemIdentification(path_x, path_x_dot, path_y)`", "Creates a `SystemIdentification` object" 133 | "`fit(num_epochs=500):`", "Models the system" 134 | "`model():`", "Returns the dictioinary of system matrices" 135 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/rep.rst.txt: -------------------------------------------------------------------------------- 1 | ====================== 2 | System Representation 3 | ====================== 4 | 5 | Systems can be defined by Transfer Functions and State Space models. Both system representations provide near identical utility. 6 | 7 | Transfer Functions 8 | =================== 9 | 10 | A Transfer Function is the Laplace Transform of the ratio of output to input, and are mathematically described as: 11 | 12 | .. figure:: /shared_images/tf.png 13 | :align: center 14 | 15 | To define a system in terms of a Transfer Function, use the `TransferFunction` class. 16 | :: 17 | >>> import control 18 | >>> s = control.TransferFunction(num, den) 19 | 20 | Here `num` and `den` can be lists or numpy arrays 21 | 22 | 23 | State Space Models 24 | =================== 25 | 26 | Space State models are mathamatically described as: 27 | 28 | .. figure:: /shared_images/ss.png 29 | :align: center 30 | 31 | To define a system as Space State model, use the `StateSpace` class. 32 | :: 33 | >>> import control 34 | >>> s = control.StateSpce(A,B,C,D) 35 | 36 | Here, A,B,C,D are ndarrays. 37 | 38 | .. note:: A system can be defined by any of the two representations above. If a particular method is needed but is not provided for the given system representation (which is unlikely), you can convert the system model to the desired representation using the `convert2TF()` or `convert2SS()` method. -------------------------------------------------------------------------------- /docs/_build/html/_static/alabaster.css: -------------------------------------------------------------------------------- 1 | @import url("basic.css"); 2 | 3 | /* -- page layout ----------------------------------------------------------- */ 4 | 5 | body { 6 | font-family: Georgia, serif; 7 | font-size: 17px; 8 | background-color: #fff; 9 | color: #000; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | 15 | div.document { 16 | width: 940px; 17 | margin: 30px auto 0 auto; 18 | } 19 | 20 | div.documentwrapper { 21 | float: left; 22 | width: 100%; 23 | } 24 | 25 | div.bodywrapper { 26 | margin: 0 0 0 220px; 27 | } 28 | 29 | div.sphinxsidebar { 30 | width: 220px; 31 | font-size: 14px; 32 | line-height: 1.5; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #fff; 41 | color: #3E4349; 42 | padding: 0 30px 0 30px; 43 | } 44 | 45 | div.body > .section { 46 | text-align: left; 47 | } 48 | 49 | div.footer { 50 | width: 940px; 51 | margin: 20px auto 30px auto; 52 | font-size: 14px; 53 | color: #888; 54 | text-align: right; 55 | } 56 | 57 | div.footer a { 58 | color: #888; 59 | } 60 | 61 | p.caption { 62 | font-family: inherit; 63 | font-size: inherit; 64 | } 65 | 66 | 67 | div.relations { 68 | display: none; 69 | } 70 | 71 | 72 | div.sphinxsidebar a { 73 | color: #444; 74 | text-decoration: none; 75 | border-bottom: 1px dotted #999; 76 | } 77 | 78 | div.sphinxsidebar a:hover { 79 | border-bottom: 1px solid #999; 80 | } 81 | 82 | div.sphinxsidebarwrapper { 83 | padding: 18px 10px; 84 | } 85 | 86 | div.sphinxsidebarwrapper p.logo { 87 | padding: 0; 88 | margin: -10px 0 0 0px; 89 | text-align: center; 90 | } 91 | 92 | div.sphinxsidebarwrapper h1.logo { 93 | margin-top: -10px; 94 | text-align: center; 95 | margin-bottom: 5px; 96 | text-align: left; 97 | } 98 | 99 | div.sphinxsidebarwrapper h1.logo-name { 100 | margin-top: 0px; 101 | } 102 | 103 | div.sphinxsidebarwrapper p.blurb { 104 | margin-top: 0; 105 | font-style: normal; 106 | } 107 | 108 | div.sphinxsidebar h3, 109 | div.sphinxsidebar h4 { 110 | font-family: Georgia, serif; 111 | color: #444; 112 | font-size: 24px; 113 | font-weight: normal; 114 | margin: 0 0 5px 0; 115 | padding: 0; 116 | } 117 | 118 | div.sphinxsidebar h4 { 119 | font-size: 20px; 120 | } 121 | 122 | div.sphinxsidebar h3 a { 123 | color: #444; 124 | } 125 | 126 | div.sphinxsidebar p.logo a, 127 | div.sphinxsidebar h3 a, 128 | div.sphinxsidebar p.logo a:hover, 129 | div.sphinxsidebar h3 a:hover { 130 | border: none; 131 | } 132 | 133 | div.sphinxsidebar p { 134 | color: #555; 135 | margin: 10px 0; 136 | } 137 | 138 | div.sphinxsidebar ul { 139 | margin: 10px 0; 140 | padding: 0; 141 | color: #000; 142 | } 143 | 144 | div.sphinxsidebar ul li.toctree-l1 > a { 145 | font-size: 120%; 146 | } 147 | 148 | div.sphinxsidebar ul li.toctree-l2 > a { 149 | font-size: 110%; 150 | } 151 | 152 | div.sphinxsidebar input { 153 | border: 1px solid #CCC; 154 | font-family: Georgia, serif; 155 | font-size: 1em; 156 | } 157 | 158 | div.sphinxsidebar hr { 159 | border: none; 160 | height: 1px; 161 | color: #AAA; 162 | background: #AAA; 163 | 164 | text-align: left; 165 | margin-left: 0; 166 | width: 50%; 167 | } 168 | 169 | div.sphinxsidebar .badge { 170 | border-bottom: none; 171 | } 172 | 173 | div.sphinxsidebar .badge:hover { 174 | border-bottom: none; 175 | } 176 | 177 | /* To address an issue with donation coming after search */ 178 | div.sphinxsidebar h3.donation { 179 | margin-top: 10px; 180 | } 181 | 182 | /* -- body styles ----------------------------------------------------------- */ 183 | 184 | a { 185 | color: #004B6B; 186 | text-decoration: underline; 187 | } 188 | 189 | a:hover { 190 | color: #6D4100; 191 | text-decoration: underline; 192 | } 193 | 194 | div.body h1, 195 | div.body h2, 196 | div.body h3, 197 | div.body h4, 198 | div.body h5, 199 | div.body h6 { 200 | font-family: Georgia, serif; 201 | font-weight: normal; 202 | margin: 30px 0px 10px 0px; 203 | padding: 0; 204 | } 205 | 206 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 207 | div.body h2 { font-size: 180%; } 208 | div.body h3 { font-size: 150%; } 209 | div.body h4 { font-size: 130%; } 210 | div.body h5 { font-size: 100%; } 211 | div.body h6 { font-size: 100%; } 212 | 213 | a.headerlink { 214 | color: #DDD; 215 | padding: 0 4px; 216 | text-decoration: none; 217 | } 218 | 219 | a.headerlink:hover { 220 | color: #444; 221 | background: #EAEAEA; 222 | } 223 | 224 | div.body p, div.body dd, div.body li { 225 | line-height: 1.4em; 226 | } 227 | 228 | div.admonition { 229 | margin: 20px 0px; 230 | padding: 10px 30px; 231 | background-color: #EEE; 232 | border: 1px solid #CCC; 233 | } 234 | 235 | div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { 236 | background-color: #FBFBFB; 237 | border-bottom: 1px solid #fafafa; 238 | } 239 | 240 | div.admonition p.admonition-title { 241 | font-family: Georgia, serif; 242 | font-weight: normal; 243 | font-size: 24px; 244 | margin: 0 0 10px 0; 245 | padding: 0; 246 | line-height: 1; 247 | } 248 | 249 | div.admonition p.last { 250 | margin-bottom: 0; 251 | } 252 | 253 | div.highlight { 254 | background-color: #fff; 255 | } 256 | 257 | dt:target, .highlight { 258 | background: #FAF3E8; 259 | } 260 | 261 | div.warning { 262 | background-color: #FCC; 263 | border: 1px solid #FAA; 264 | } 265 | 266 | div.danger { 267 | background-color: #FCC; 268 | border: 1px solid #FAA; 269 | -moz-box-shadow: 2px 2px 4px #D52C2C; 270 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 271 | box-shadow: 2px 2px 4px #D52C2C; 272 | } 273 | 274 | div.error { 275 | background-color: #FCC; 276 | border: 1px solid #FAA; 277 | -moz-box-shadow: 2px 2px 4px #D52C2C; 278 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 279 | box-shadow: 2px 2px 4px #D52C2C; 280 | } 281 | 282 | div.caution { 283 | background-color: #FCC; 284 | border: 1px solid #FAA; 285 | } 286 | 287 | div.attention { 288 | background-color: #FCC; 289 | border: 1px solid #FAA; 290 | } 291 | 292 | div.important { 293 | background-color: #EEE; 294 | border: 1px solid #CCC; 295 | } 296 | 297 | div.note { 298 | background-color: #EEE; 299 | border: 1px solid #CCC; 300 | } 301 | 302 | div.tip { 303 | background-color: #EEE; 304 | border: 1px solid #CCC; 305 | } 306 | 307 | div.hint { 308 | background-color: #EEE; 309 | border: 1px solid #CCC; 310 | } 311 | 312 | div.seealso { 313 | background-color: #EEE; 314 | border: 1px solid #CCC; 315 | } 316 | 317 | div.topic { 318 | background-color: #EEE; 319 | } 320 | 321 | p.admonition-title { 322 | display: inline; 323 | } 324 | 325 | p.admonition-title:after { 326 | content: ":"; 327 | } 328 | 329 | pre, tt, code { 330 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 331 | font-size: 0.9em; 332 | } 333 | 334 | .hll { 335 | background-color: #FFC; 336 | margin: 0 -12px; 337 | padding: 0 12px; 338 | display: block; 339 | } 340 | 341 | img.screenshot { 342 | } 343 | 344 | tt.descname, tt.descclassname, code.descname, code.descclassname { 345 | font-size: 0.95em; 346 | } 347 | 348 | tt.descname, code.descname { 349 | padding-right: 0.08em; 350 | } 351 | 352 | img.screenshot { 353 | -moz-box-shadow: 2px 2px 4px #EEE; 354 | -webkit-box-shadow: 2px 2px 4px #EEE; 355 | box-shadow: 2px 2px 4px #EEE; 356 | } 357 | 358 | table.docutils { 359 | border: 1px solid #888; 360 | -moz-box-shadow: 2px 2px 4px #EEE; 361 | -webkit-box-shadow: 2px 2px 4px #EEE; 362 | box-shadow: 2px 2px 4px #EEE; 363 | } 364 | 365 | table.docutils td, table.docutils th { 366 | border: 1px solid #888; 367 | padding: 0.25em 0.7em; 368 | } 369 | 370 | table.field-list, table.footnote { 371 | border: none; 372 | -moz-box-shadow: none; 373 | -webkit-box-shadow: none; 374 | box-shadow: none; 375 | } 376 | 377 | table.footnote { 378 | margin: 15px 0; 379 | width: 100%; 380 | border: 1px solid #EEE; 381 | background: #FDFDFD; 382 | font-size: 0.9em; 383 | } 384 | 385 | table.footnote + table.footnote { 386 | margin-top: -15px; 387 | border-top: none; 388 | } 389 | 390 | table.field-list th { 391 | padding: 0 0.8em 0 0; 392 | } 393 | 394 | table.field-list td { 395 | padding: 0; 396 | } 397 | 398 | table.field-list p { 399 | margin-bottom: 0.8em; 400 | } 401 | 402 | /* Cloned from 403 | * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 404 | */ 405 | .field-name { 406 | -moz-hyphens: manual; 407 | -ms-hyphens: manual; 408 | -webkit-hyphens: manual; 409 | hyphens: manual; 410 | } 411 | 412 | table.footnote td.label { 413 | width: .1px; 414 | padding: 0.3em 0 0.3em 0.5em; 415 | } 416 | 417 | table.footnote td { 418 | padding: 0.3em 0.5em; 419 | } 420 | 421 | dl { 422 | margin: 0; 423 | padding: 0; 424 | } 425 | 426 | dl dd { 427 | margin-left: 30px; 428 | } 429 | 430 | blockquote { 431 | margin: 0 0 0 30px; 432 | padding: 0; 433 | } 434 | 435 | ul, ol { 436 | /* Matches the 30px from the narrow-screen "li > ul" selector below */ 437 | margin: 10px 0 10px 30px; 438 | padding: 0; 439 | } 440 | 441 | pre { 442 | background: #EEE; 443 | padding: 7px 30px; 444 | margin: 15px 0px; 445 | line-height: 1.3em; 446 | } 447 | 448 | div.viewcode-block:target { 449 | background: #ffd; 450 | } 451 | 452 | dl pre, blockquote pre, li pre { 453 | margin-left: 0; 454 | padding-left: 30px; 455 | } 456 | 457 | tt, code { 458 | background-color: #ecf0f3; 459 | color: #222; 460 | /* padding: 1px 2px; */ 461 | } 462 | 463 | tt.xref, code.xref, a tt { 464 | background-color: #FBFBFB; 465 | border-bottom: 1px solid #fff; 466 | } 467 | 468 | a.reference { 469 | text-decoration: none; 470 | border-bottom: 1px dotted #004B6B; 471 | } 472 | 473 | /* Don't put an underline on images */ 474 | a.image-reference, a.image-reference:hover { 475 | border-bottom: none; 476 | } 477 | 478 | a.reference:hover { 479 | border-bottom: 1px solid #6D4100; 480 | } 481 | 482 | a.footnote-reference { 483 | text-decoration: none; 484 | font-size: 0.7em; 485 | vertical-align: top; 486 | border-bottom: 1px dotted #004B6B; 487 | } 488 | 489 | a.footnote-reference:hover { 490 | border-bottom: 1px solid #6D4100; 491 | } 492 | 493 | a:hover tt, a:hover code { 494 | background: #EEE; 495 | } 496 | 497 | 498 | @media screen and (max-width: 870px) { 499 | 500 | div.sphinxsidebar { 501 | display: none; 502 | } 503 | 504 | div.document { 505 | width: 100%; 506 | 507 | } 508 | 509 | div.documentwrapper { 510 | margin-left: 0; 511 | margin-top: 0; 512 | margin-right: 0; 513 | margin-bottom: 0; 514 | } 515 | 516 | div.bodywrapper { 517 | margin-top: 0; 518 | margin-right: 0; 519 | margin-bottom: 0; 520 | margin-left: 0; 521 | } 522 | 523 | ul { 524 | margin-left: 0; 525 | } 526 | 527 | li > ul { 528 | /* Matches the 30px from the "ul, ol" selector above */ 529 | margin-left: 30px; 530 | } 531 | 532 | .document { 533 | width: auto; 534 | } 535 | 536 | .footer { 537 | width: auto; 538 | } 539 | 540 | .bodywrapper { 541 | margin: 0; 542 | } 543 | 544 | .footer { 545 | width: auto; 546 | } 547 | 548 | .github { 549 | display: none; 550 | } 551 | 552 | 553 | 554 | } 555 | 556 | 557 | 558 | @media screen and (max-width: 875px) { 559 | 560 | body { 561 | margin: 0; 562 | padding: 20px 30px; 563 | } 564 | 565 | div.documentwrapper { 566 | float: none; 567 | background: #fff; 568 | } 569 | 570 | div.sphinxsidebar { 571 | display: block; 572 | float: none; 573 | width: 102.5%; 574 | margin: 50px -30px -20px -30px; 575 | padding: 10px 20px; 576 | background: #333; 577 | color: #FFF; 578 | } 579 | 580 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 581 | div.sphinxsidebar h3 a { 582 | color: #fff; 583 | } 584 | 585 | div.sphinxsidebar a { 586 | color: #AAA; 587 | } 588 | 589 | div.sphinxsidebar p.logo { 590 | display: none; 591 | } 592 | 593 | div.document { 594 | width: 100%; 595 | margin: 0; 596 | } 597 | 598 | div.footer { 599 | display: none; 600 | } 601 | 602 | div.bodywrapper { 603 | margin: 0; 604 | } 605 | 606 | div.body { 607 | min-height: 0; 608 | padding: 0; 609 | } 610 | 611 | .rtd_doc_footer { 612 | display: none; 613 | } 614 | 615 | .document { 616 | width: auto; 617 | } 618 | 619 | .footer { 620 | width: auto; 621 | } 622 | 623 | .footer { 624 | width: auto; 625 | } 626 | 627 | .github { 628 | display: none; 629 | } 630 | } 631 | 632 | 633 | /* misc. */ 634 | 635 | .revsys-inline { 636 | display: none!important; 637 | } 638 | 639 | /* Make nested-list/multi-paragraph items look better in Releases changelog 640 | * pages. Without this, docutils' magical list fuckery causes inconsistent 641 | * formatting between different release sub-lists. 642 | */ 643 | div#changelog > div.section > ul > li > p:only-child { 644 | margin-bottom: 0; 645 | } 646 | 647 | /* Hide fugly table cell borders in ..bibliography:: directive output */ 648 | table.docutils.citation, table.docutils.citation td, table.docutils.citation th { 649 | border: none; 650 | /* Below needed in some edge cases; if not applied, bottom shadows appear */ 651 | -moz-box-shadow: none; 652 | -webkit-box-shadow: none; 653 | box-shadow: none; 654 | } 655 | 656 | 657 | /* relbar */ 658 | 659 | .related { 660 | line-height: 30px; 661 | width: 100%; 662 | font-size: 0.9rem; 663 | } 664 | 665 | .related.top { 666 | border-bottom: 1px solid #EEE; 667 | margin-bottom: 20px; 668 | } 669 | 670 | .related.bottom { 671 | border-top: 1px solid #EEE; 672 | } 673 | 674 | .related ul { 675 | padding: 0; 676 | margin: 0; 677 | list-style: none; 678 | } 679 | 680 | .related li { 681 | display: inline; 682 | } 683 | 684 | nav#rellinks { 685 | float: right; 686 | } 687 | 688 | nav#rellinks li+li:before { 689 | content: "|"; 690 | } 691 | 692 | nav#breadcrumbs li+li:before { 693 | content: "\00BB"; 694 | } 695 | 696 | /* Hide certain items when printing */ 697 | @media print { 698 | div.related { 699 | display: none; 700 | } 701 | } -------------------------------------------------------------------------------- /docs/_build/html/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | /* -- relbar ---------------------------------------------------------------- */ 19 | 20 | div.related { 21 | width: 100%; 22 | font-size: 90%; 23 | } 24 | 25 | div.related h3 { 26 | display: none; 27 | } 28 | 29 | div.related ul { 30 | margin: 0; 31 | padding: 0 0 0 10px; 32 | list-style: none; 33 | } 34 | 35 | div.related li { 36 | display: inline; 37 | } 38 | 39 | div.related li.right { 40 | float: right; 41 | margin-right: 5px; 42 | } 43 | 44 | /* -- sidebar --------------------------------------------------------------- */ 45 | 46 | div.sphinxsidebarwrapper { 47 | padding: 10px 5px 0 10px; 48 | } 49 | 50 | div.sphinxsidebar { 51 | float: left; 52 | width: 230px; 53 | margin-left: -100%; 54 | font-size: 90%; 55 | word-wrap: break-word; 56 | overflow-wrap : break-word; 57 | } 58 | 59 | div.sphinxsidebar ul { 60 | list-style: none; 61 | } 62 | 63 | div.sphinxsidebar ul ul, 64 | div.sphinxsidebar ul.want-points { 65 | margin-left: 20px; 66 | list-style: square; 67 | } 68 | 69 | div.sphinxsidebar ul ul { 70 | margin-top: 0; 71 | margin-bottom: 0; 72 | } 73 | 74 | div.sphinxsidebar form { 75 | margin-top: 10px; 76 | } 77 | 78 | div.sphinxsidebar input { 79 | border: 1px solid #98dbcc; 80 | font-family: sans-serif; 81 | font-size: 1em; 82 | } 83 | 84 | div.sphinxsidebar #searchbox form.search { 85 | overflow: hidden; 86 | } 87 | 88 | div.sphinxsidebar #searchbox input[type="text"] { 89 | float: left; 90 | width: 80%; 91 | padding: 0.25em; 92 | box-sizing: border-box; 93 | } 94 | 95 | div.sphinxsidebar #searchbox input[type="submit"] { 96 | float: left; 97 | width: 20%; 98 | border-left: none; 99 | padding: 0.25em; 100 | box-sizing: border-box; 101 | } 102 | 103 | 104 | img { 105 | border: 0; 106 | max-width: 100%; 107 | } 108 | 109 | /* -- search page ----------------------------------------------------------- */ 110 | 111 | ul.search { 112 | margin: 10px 0 0 20px; 113 | padding: 0; 114 | } 115 | 116 | ul.search li { 117 | padding: 5px 0 5px 20px; 118 | background-image: url(file.png); 119 | background-repeat: no-repeat; 120 | background-position: 0 7px; 121 | } 122 | 123 | ul.search li a { 124 | font-weight: bold; 125 | } 126 | 127 | ul.search li div.context { 128 | color: #888; 129 | margin: 2px 0 0 30px; 130 | text-align: left; 131 | } 132 | 133 | ul.keywordmatches li.goodmatch a { 134 | font-weight: bold; 135 | } 136 | 137 | /* -- index page ------------------------------------------------------------ */ 138 | 139 | table.contentstable { 140 | width: 90%; 141 | margin-left: auto; 142 | margin-right: auto; 143 | } 144 | 145 | table.contentstable p.biglink { 146 | line-height: 150%; 147 | } 148 | 149 | a.biglink { 150 | font-size: 1.3em; 151 | } 152 | 153 | span.linkdescr { 154 | font-style: italic; 155 | padding-top: 5px; 156 | font-size: 90%; 157 | } 158 | 159 | /* -- general index --------------------------------------------------------- */ 160 | 161 | table.indextable { 162 | width: 100%; 163 | } 164 | 165 | table.indextable td { 166 | text-align: left; 167 | vertical-align: top; 168 | } 169 | 170 | table.indextable ul { 171 | margin-top: 0; 172 | margin-bottom: 0; 173 | list-style-type: none; 174 | } 175 | 176 | table.indextable > tbody > tr > td > ul { 177 | padding-left: 0em; 178 | } 179 | 180 | table.indextable tr.pcap { 181 | height: 10px; 182 | } 183 | 184 | table.indextable tr.cap { 185 | margin-top: 10px; 186 | background-color: #f2f2f2; 187 | } 188 | 189 | img.toggler { 190 | margin-right: 3px; 191 | margin-top: 3px; 192 | cursor: pointer; 193 | } 194 | 195 | div.modindex-jumpbox { 196 | border-top: 1px solid #ddd; 197 | border-bottom: 1px solid #ddd; 198 | margin: 1em 0 1em 0; 199 | padding: 0.4em; 200 | } 201 | 202 | div.genindex-jumpbox { 203 | border-top: 1px solid #ddd; 204 | border-bottom: 1px solid #ddd; 205 | margin: 1em 0 1em 0; 206 | padding: 0.4em; 207 | } 208 | 209 | /* -- domain module index --------------------------------------------------- */ 210 | 211 | table.modindextable td { 212 | padding: 2px; 213 | border-collapse: collapse; 214 | } 215 | 216 | /* -- general body styles --------------------------------------------------- */ 217 | 218 | div.body { 219 | min-width: 450px; 220 | max-width: 800px; 221 | } 222 | 223 | div.body p, div.body dd, div.body li, div.body blockquote { 224 | -moz-hyphens: auto; 225 | -ms-hyphens: auto; 226 | -webkit-hyphens: auto; 227 | hyphens: auto; 228 | } 229 | 230 | a.headerlink { 231 | visibility: hidden; 232 | } 233 | 234 | a.brackets:before, 235 | span.brackets > a:before{ 236 | content: "["; 237 | } 238 | 239 | a.brackets:after, 240 | span.brackets > a:after { 241 | content: "]"; 242 | } 243 | 244 | h1:hover > a.headerlink, 245 | h2:hover > a.headerlink, 246 | h3:hover > a.headerlink, 247 | h4:hover > a.headerlink, 248 | h5:hover > a.headerlink, 249 | h6:hover > a.headerlink, 250 | dt:hover > a.headerlink, 251 | caption:hover > a.headerlink, 252 | p.caption:hover > a.headerlink, 253 | div.code-block-caption:hover > a.headerlink { 254 | visibility: visible; 255 | } 256 | 257 | div.body p.caption { 258 | text-align: inherit; 259 | } 260 | 261 | div.body td { 262 | text-align: left; 263 | } 264 | 265 | .first { 266 | margin-top: 0 !important; 267 | } 268 | 269 | p.rubric { 270 | margin-top: 30px; 271 | font-weight: bold; 272 | } 273 | 274 | img.align-left, .figure.align-left, object.align-left { 275 | clear: left; 276 | float: left; 277 | margin-right: 1em; 278 | } 279 | 280 | img.align-right, .figure.align-right, object.align-right { 281 | clear: right; 282 | float: right; 283 | margin-left: 1em; 284 | } 285 | 286 | img.align-center, .figure.align-center, object.align-center { 287 | display: block; 288 | margin-left: auto; 289 | margin-right: auto; 290 | } 291 | 292 | img.align-default, .figure.align-default { 293 | display: block; 294 | margin-left: auto; 295 | margin-right: auto; 296 | } 297 | 298 | .align-left { 299 | text-align: left; 300 | } 301 | 302 | .align-center { 303 | text-align: center; 304 | } 305 | 306 | .align-default { 307 | text-align: center; 308 | } 309 | 310 | .align-right { 311 | text-align: right; 312 | } 313 | 314 | /* -- sidebars -------------------------------------------------------------- */ 315 | 316 | div.sidebar { 317 | margin: 0 0 0.5em 1em; 318 | border: 1px solid #ddb; 319 | padding: 7px; 320 | background-color: #ffe; 321 | width: 40%; 322 | float: right; 323 | clear: right; 324 | overflow-x: auto; 325 | } 326 | 327 | p.sidebar-title { 328 | font-weight: bold; 329 | } 330 | 331 | div.admonition, div.topic, pre, div[class|="highlight"] { 332 | clear: both; 333 | } 334 | 335 | /* -- topics ---------------------------------------------------------------- */ 336 | 337 | div.topic { 338 | border: 1px solid #ccc; 339 | padding: 7px; 340 | margin: 10px 0 10px 0; 341 | overflow-x: auto; 342 | } 343 | 344 | p.topic-title { 345 | font-size: 1.1em; 346 | font-weight: bold; 347 | margin-top: 10px; 348 | } 349 | 350 | /* -- admonitions ----------------------------------------------------------- */ 351 | 352 | div.admonition { 353 | margin-top: 10px; 354 | margin-bottom: 10px; 355 | padding: 7px; 356 | overflow-x: auto; 357 | } 358 | 359 | div.admonition dt { 360 | font-weight: bold; 361 | } 362 | 363 | p.admonition-title { 364 | margin: 0px 10px 5px 0px; 365 | font-weight: bold; 366 | } 367 | 368 | div.body p.centered { 369 | text-align: center; 370 | margin-top: 25px; 371 | } 372 | 373 | /* -- content of sidebars/topics/admonitions -------------------------------- */ 374 | 375 | div.sidebar > :last-child, 376 | div.topic > :last-child, 377 | div.admonition > :last-child { 378 | margin-bottom: 0; 379 | } 380 | 381 | /* -- tables ---------------------------------------------------------------- */ 382 | 383 | table.docutils { 384 | margin-top: 10px; 385 | margin-bottom: 10px; 386 | border: 0; 387 | border-collapse: collapse; 388 | } 389 | 390 | table.align-center { 391 | margin-left: auto; 392 | margin-right: auto; 393 | } 394 | 395 | table.align-default { 396 | margin-left: auto; 397 | margin-right: auto; 398 | } 399 | 400 | table caption span.caption-number { 401 | font-style: italic; 402 | } 403 | 404 | table caption span.caption-text { 405 | } 406 | 407 | table.docutils td, table.docutils th { 408 | padding: 1px 8px 1px 5px; 409 | border-top: 0; 410 | border-left: 0; 411 | border-right: 0; 412 | border-bottom: 1px solid #aaa; 413 | } 414 | 415 | table.footnote td, table.footnote th { 416 | border: 0 !important; 417 | } 418 | 419 | th { 420 | text-align: left; 421 | padding-right: 5px; 422 | } 423 | 424 | table.citation { 425 | border-left: solid 1px gray; 426 | margin-left: 1px; 427 | } 428 | 429 | table.citation td { 430 | border-bottom: none; 431 | } 432 | 433 | th > :first-child, 434 | td > :first-child { 435 | margin-top: 0px; 436 | } 437 | 438 | th > :last-child, 439 | td > :last-child { 440 | margin-bottom: 0px; 441 | } 442 | 443 | /* -- figures --------------------------------------------------------------- */ 444 | 445 | div.figure { 446 | margin: 0.5em; 447 | padding: 0.5em; 448 | } 449 | 450 | div.figure p.caption { 451 | padding: 0.3em; 452 | } 453 | 454 | div.figure p.caption span.caption-number { 455 | font-style: italic; 456 | } 457 | 458 | div.figure p.caption span.caption-text { 459 | } 460 | 461 | /* -- field list styles ----------------------------------------------------- */ 462 | 463 | table.field-list td, table.field-list th { 464 | border: 0 !important; 465 | } 466 | 467 | .field-list ul { 468 | margin: 0; 469 | padding-left: 1em; 470 | } 471 | 472 | .field-list p { 473 | margin: 0; 474 | } 475 | 476 | .field-name { 477 | -moz-hyphens: manual; 478 | -ms-hyphens: manual; 479 | -webkit-hyphens: manual; 480 | hyphens: manual; 481 | } 482 | 483 | /* -- hlist styles ---------------------------------------------------------- */ 484 | 485 | table.hlist { 486 | margin: 1em 0; 487 | } 488 | 489 | table.hlist td { 490 | vertical-align: top; 491 | } 492 | 493 | 494 | /* -- other body styles ----------------------------------------------------- */ 495 | 496 | ol.arabic { 497 | list-style: decimal; 498 | } 499 | 500 | ol.loweralpha { 501 | list-style: lower-alpha; 502 | } 503 | 504 | ol.upperalpha { 505 | list-style: upper-alpha; 506 | } 507 | 508 | ol.lowerroman { 509 | list-style: lower-roman; 510 | } 511 | 512 | ol.upperroman { 513 | list-style: upper-roman; 514 | } 515 | 516 | ol > li:first-child > :first-child, 517 | ul > li:first-child > :first-child { 518 | margin-top: 0px; 519 | } 520 | 521 | ol ol > li:first-child > :first-child, 522 | ol ul > li:first-child > :first-child, 523 | ul ol > li:first-child > :first-child, 524 | ul ul > li:first-child > :first-child { 525 | margin-top: revert; 526 | } 527 | 528 | ol > li:last-child > :last-child, 529 | ul > li:last-child > :last-child { 530 | margin-bottom: 0px; 531 | } 532 | 533 | ol ol > li:last-child > :last-child, 534 | ol ul > li:last-child > :last-child, 535 | ul ol > li:last-child > :last-child, 536 | ul ul > li:last-child > :last-child { 537 | margin-bottom: revert; 538 | } 539 | 540 | dl.footnote > dt, 541 | dl.citation > dt { 542 | float: left; 543 | margin-right: 0.5em; 544 | } 545 | 546 | dl.footnote > dd, 547 | dl.citation > dd { 548 | margin-bottom: 0em; 549 | } 550 | 551 | dl.footnote > dd:after, 552 | dl.citation > dd:after { 553 | content: ""; 554 | clear: both; 555 | } 556 | 557 | dl.field-list { 558 | display: grid; 559 | grid-template-columns: fit-content(30%) auto; 560 | } 561 | 562 | dl.field-list > dt { 563 | font-weight: bold; 564 | word-break: break-word; 565 | padding-left: 0.5em; 566 | padding-right: 5px; 567 | } 568 | 569 | dl.field-list > dt:after { 570 | content: ":"; 571 | } 572 | 573 | dl.field-list > dd { 574 | padding-left: 0.5em; 575 | margin-top: 0em; 576 | margin-left: 0em; 577 | margin-bottom: 0em; 578 | } 579 | 580 | dl { 581 | margin-bottom: 15px; 582 | } 583 | 584 | dd > :first-child { 585 | margin-top: 0px; 586 | } 587 | 588 | dd ul, dd table { 589 | margin-bottom: 10px; 590 | } 591 | 592 | dd { 593 | margin-top: 3px; 594 | margin-bottom: 10px; 595 | margin-left: 30px; 596 | } 597 | 598 | dl > dd:last-child, 599 | dl > dd:last-child > :last-child { 600 | margin-bottom: 0; 601 | } 602 | 603 | dt:target, span.highlighted { 604 | background-color: #fbe54e; 605 | } 606 | 607 | rect.highlighted { 608 | fill: #fbe54e; 609 | } 610 | 611 | dl.glossary dt { 612 | font-weight: bold; 613 | font-size: 1.1em; 614 | } 615 | 616 | .optional { 617 | font-size: 1.3em; 618 | } 619 | 620 | .sig-paren { 621 | font-size: larger; 622 | } 623 | 624 | .versionmodified { 625 | font-style: italic; 626 | } 627 | 628 | .system-message { 629 | background-color: #fda; 630 | padding: 5px; 631 | border: 3px solid red; 632 | } 633 | 634 | .footnote:target { 635 | background-color: #ffa; 636 | } 637 | 638 | .line-block { 639 | display: block; 640 | margin-top: 1em; 641 | margin-bottom: 1em; 642 | } 643 | 644 | .line-block .line-block { 645 | margin-top: 0; 646 | margin-bottom: 0; 647 | margin-left: 1.5em; 648 | } 649 | 650 | .guilabel, .menuselection { 651 | font-family: sans-serif; 652 | } 653 | 654 | .accelerator { 655 | text-decoration: underline; 656 | } 657 | 658 | .classifier { 659 | font-style: oblique; 660 | } 661 | 662 | .classifier:before { 663 | font-style: normal; 664 | margin: 0.5em; 665 | content: ":"; 666 | } 667 | 668 | abbr, acronym { 669 | border-bottom: dotted 1px; 670 | cursor: help; 671 | } 672 | 673 | /* -- code displays --------------------------------------------------------- */ 674 | 675 | pre { 676 | overflow: auto; 677 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 678 | } 679 | 680 | span.pre { 681 | -moz-hyphens: none; 682 | -ms-hyphens: none; 683 | -webkit-hyphens: none; 684 | hyphens: none; 685 | } 686 | 687 | div[class^="highlight-"] { 688 | margin: 1em 0; 689 | } 690 | 691 | td.linenos pre { 692 | border: 0; 693 | background-color: transparent; 694 | color: #aaa; 695 | } 696 | 697 | table.highlighttable { 698 | display: block; 699 | } 700 | 701 | table.highlighttable tbody { 702 | display: block; 703 | } 704 | 705 | table.highlighttable tr { 706 | display: flex; 707 | } 708 | 709 | table.highlighttable td { 710 | margin: 0; 711 | padding: 0; 712 | } 713 | 714 | table.highlighttable td.linenos { 715 | padding-right: 0.5em; 716 | } 717 | 718 | table.highlighttable td.code { 719 | flex: 1; 720 | overflow: hidden; 721 | } 722 | 723 | .highlight .hll { 724 | display: block; 725 | } 726 | 727 | div.highlight pre, 728 | table.highlighttable pre { 729 | margin: 0; 730 | } 731 | 732 | div.code-block-caption + div { 733 | margin-top: 0; 734 | } 735 | 736 | div.code-block-caption { 737 | margin-top: 1em; 738 | padding: 2px 5px; 739 | font-size: small; 740 | } 741 | 742 | div.code-block-caption code { 743 | background-color: transparent; 744 | } 745 | 746 | table.highlighttable td.linenos, 747 | div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ 748 | user-select: none; 749 | } 750 | 751 | div.code-block-caption span.caption-number { 752 | padding: 0.1em 0.3em; 753 | font-style: italic; 754 | } 755 | 756 | div.code-block-caption span.caption-text { 757 | } 758 | 759 | div.literal-block-wrapper { 760 | margin: 1em 0; 761 | } 762 | 763 | code.descname { 764 | background-color: transparent; 765 | font-weight: bold; 766 | font-size: 1.2em; 767 | } 768 | 769 | code.descclassname { 770 | background-color: transparent; 771 | } 772 | 773 | code.xref, a code { 774 | background-color: transparent; 775 | font-weight: bold; 776 | } 777 | 778 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 779 | background-color: transparent; 780 | } 781 | 782 | .viewcode-link { 783 | float: right; 784 | } 785 | 786 | .viewcode-back { 787 | float: right; 788 | font-family: sans-serif; 789 | } 790 | 791 | div.viewcode-block:target { 792 | margin: -1px -10px; 793 | padding: 0 10px; 794 | } 795 | 796 | /* -- math display ---------------------------------------------------------- */ 797 | 798 | img.math { 799 | vertical-align: middle; 800 | } 801 | 802 | div.body div.math p { 803 | text-align: center; 804 | } 805 | 806 | span.eqno { 807 | float: right; 808 | } 809 | 810 | span.eqno a.headerlink { 811 | position: absolute; 812 | z-index: 1; 813 | } 814 | 815 | div.math:hover a.headerlink { 816 | visibility: visible; 817 | } 818 | 819 | /* -- printout stylesheet --------------------------------------------------- */ 820 | 821 | @media print { 822 | div.document, 823 | div.documentwrapper, 824 | div.bodywrapper { 825 | margin: 0 !important; 826 | width: 100%; 827 | } 828 | 829 | div.sphinxsidebar, 830 | div.related, 831 | div.footer, 832 | #top-link { 833 | display: none; 834 | } 835 | } -------------------------------------------------------------------------------- /docs/_build/html/_static/classic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * classic.css_t 3 | * ~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- classic theme. 6 | * 7 | * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | html { 17 | /* CSS hack for macOS's scrollbar (see #1125) */ 18 | background-color: #FFFFFF; 19 | } 20 | 21 | body { 22 | font-family: sans-serif; 23 | font-size: 100%; 24 | background-color: #11303d; 25 | color: #000; 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | div.document { 31 | background-color: #1c4e63; 32 | } 33 | 34 | div.documentwrapper { 35 | float: left; 36 | width: 100%; 37 | } 38 | 39 | div.bodywrapper { 40 | margin: 0 0 0 230px; 41 | } 42 | 43 | div.body { 44 | background-color: #ffffff; 45 | color: #000000; 46 | padding: 0 20px 30px 20px; 47 | } 48 | 49 | div.footer { 50 | color: #ffffff; 51 | width: 100%; 52 | padding: 9px 0 9px 0; 53 | text-align: center; 54 | font-size: 75%; 55 | } 56 | 57 | div.footer a { 58 | color: #ffffff; 59 | text-decoration: underline; 60 | } 61 | 62 | div.related { 63 | background-color: #133f52; 64 | line-height: 30px; 65 | color: #ffffff; 66 | } 67 | 68 | div.related a { 69 | color: #ffffff; 70 | } 71 | 72 | div.sphinxsidebar { 73 | } 74 | 75 | div.sphinxsidebar h3 { 76 | font-family: 'Trebuchet MS', sans-serif; 77 | color: #ffffff; 78 | font-size: 1.4em; 79 | font-weight: normal; 80 | margin: 0; 81 | padding: 0; 82 | } 83 | 84 | div.sphinxsidebar h3 a { 85 | color: #ffffff; 86 | } 87 | 88 | div.sphinxsidebar h4 { 89 | font-family: 'Trebuchet MS', sans-serif; 90 | color: #ffffff; 91 | font-size: 1.3em; 92 | font-weight: normal; 93 | margin: 5px 0 0 0; 94 | padding: 0; 95 | } 96 | 97 | div.sphinxsidebar p { 98 | color: #ffffff; 99 | } 100 | 101 | div.sphinxsidebar p.topless { 102 | margin: 5px 10px 10px 10px; 103 | } 104 | 105 | div.sphinxsidebar ul { 106 | margin: 10px; 107 | padding: 0; 108 | color: #ffffff; 109 | } 110 | 111 | div.sphinxsidebar a { 112 | color: #98dbcc; 113 | } 114 | 115 | div.sphinxsidebar input { 116 | border: 1px solid #98dbcc; 117 | font-family: sans-serif; 118 | font-size: 1em; 119 | } 120 | 121 | 122 | 123 | /* -- hyperlink styles ------------------------------------------------------ */ 124 | 125 | a { 126 | color: #355f7c; 127 | text-decoration: none; 128 | } 129 | 130 | a:visited { 131 | color: #355f7c; 132 | text-decoration: none; 133 | } 134 | 135 | a:hover { 136 | text-decoration: underline; 137 | } 138 | 139 | 140 | 141 | /* -- body styles ----------------------------------------------------------- */ 142 | 143 | div.body h1, 144 | div.body h2, 145 | div.body h3, 146 | div.body h4, 147 | div.body h5, 148 | div.body h6 { 149 | font-family: 'Trebuchet MS', sans-serif; 150 | background-color: #f2f2f2; 151 | font-weight: normal; 152 | color: #20435c; 153 | border-bottom: 1px solid #ccc; 154 | margin: 20px -20px 10px -20px; 155 | padding: 3px 0 3px 10px; 156 | } 157 | 158 | div.body h1 { margin-top: 0; font-size: 200%; } 159 | div.body h2 { font-size: 160%; } 160 | div.body h3 { font-size: 140%; } 161 | div.body h4 { font-size: 120%; } 162 | div.body h5 { font-size: 110%; } 163 | div.body h6 { font-size: 100%; } 164 | 165 | a.headerlink { 166 | color: #c60f0f; 167 | font-size: 0.8em; 168 | padding: 0 4px 0 4px; 169 | text-decoration: none; 170 | } 171 | 172 | a.headerlink:hover { 173 | background-color: #c60f0f; 174 | color: white; 175 | } 176 | 177 | div.body p, div.body dd, div.body li, div.body blockquote { 178 | text-align: justify; 179 | line-height: 130%; 180 | } 181 | 182 | div.admonition p.admonition-title + p { 183 | display: inline; 184 | } 185 | 186 | div.admonition p { 187 | margin-bottom: 5px; 188 | } 189 | 190 | div.admonition pre { 191 | margin-bottom: 5px; 192 | } 193 | 194 | div.admonition ul, div.admonition ol { 195 | margin-bottom: 5px; 196 | } 197 | 198 | div.note { 199 | background-color: #eee; 200 | border: 1px solid #ccc; 201 | } 202 | 203 | div.seealso { 204 | background-color: #ffc; 205 | border: 1px solid #ff6; 206 | } 207 | 208 | div.topic { 209 | background-color: #eee; 210 | } 211 | 212 | div.warning { 213 | background-color: #ffe4e4; 214 | border: 1px solid #f66; 215 | } 216 | 217 | p.admonition-title { 218 | display: inline; 219 | } 220 | 221 | p.admonition-title:after { 222 | content: ":"; 223 | } 224 | 225 | pre { 226 | padding: 5px; 227 | background-color: unset; 228 | color: unset; 229 | line-height: 120%; 230 | border: 1px solid #ac9; 231 | border-left: none; 232 | border-right: none; 233 | } 234 | 235 | code { 236 | background-color: #ecf0f3; 237 | padding: 0 1px 0 1px; 238 | font-size: 0.95em; 239 | } 240 | 241 | th, dl.field-list > dt { 242 | background-color: #ede; 243 | } 244 | 245 | .warning code { 246 | background: #efc2c2; 247 | } 248 | 249 | .note code { 250 | background: #d6d6d6; 251 | } 252 | 253 | .viewcode-back { 254 | font-family: sans-serif; 255 | } 256 | 257 | div.viewcode-block:target { 258 | background-color: #f4debf; 259 | border-top: 1px solid #ac9; 260 | border-bottom: 1px solid #ac9; 261 | } 262 | 263 | div.code-block-caption { 264 | color: #efefef; 265 | background-color: #1c4e63; 266 | } -------------------------------------------------------------------------------- /docs/_build/html/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/_build/html/_static/default.css: -------------------------------------------------------------------------------- 1 | @import url("classic.css"); 2 | -------------------------------------------------------------------------------- /docs/_build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s === 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node, addItems) { 70 | if (node.nodeType === 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && 74 | !jQuery(node.parentNode).hasClass(className) && 75 | !jQuery(node.parentNode).hasClass("nohighlight")) { 76 | var span; 77 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 78 | if (isInSVG) { 79 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 80 | } else { 81 | span = document.createElement("span"); 82 | span.className = className; 83 | } 84 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 85 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 86 | document.createTextNode(val.substr(pos + text.length)), 87 | node.nextSibling)); 88 | node.nodeValue = val.substr(0, pos); 89 | if (isInSVG) { 90 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 91 | var bbox = node.parentElement.getBBox(); 92 | rect.x.baseVal.value = bbox.x; 93 | rect.y.baseVal.value = bbox.y; 94 | rect.width.baseVal.value = bbox.width; 95 | rect.height.baseVal.value = bbox.height; 96 | rect.setAttribute('class', className); 97 | addItems.push({ 98 | "parent": node.parentNode, 99 | "target": rect}); 100 | } 101 | } 102 | } 103 | else if (!jQuery(node).is("button, select, textarea")) { 104 | jQuery.each(node.childNodes, function() { 105 | highlight(this, addItems); 106 | }); 107 | } 108 | } 109 | var addItems = []; 110 | var result = this.each(function() { 111 | highlight(this, addItems); 112 | }); 113 | for (var i = 0; i < addItems.length; ++i) { 114 | jQuery(addItems[i].parent).before(addItems[i].target); 115 | } 116 | return result; 117 | }; 118 | 119 | /* 120 | * backward compatibility for jQuery.browser 121 | * This will be supported until firefox bug is fixed. 122 | */ 123 | if (!jQuery.browser) { 124 | jQuery.uaMatch = function(ua) { 125 | ua = ua.toLowerCase(); 126 | 127 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 128 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 129 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 130 | /(msie) ([\w.]+)/.exec(ua) || 131 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 132 | []; 133 | 134 | return { 135 | browser: match[ 1 ] || "", 136 | version: match[ 2 ] || "0" 137 | }; 138 | }; 139 | jQuery.browser = {}; 140 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 141 | } 142 | 143 | /** 144 | * Small JavaScript module for the documentation. 145 | */ 146 | var Documentation = { 147 | 148 | init : function() { 149 | this.fixFirefoxAnchorBug(); 150 | this.highlightSearchWords(); 151 | this.initIndexTable(); 152 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { 153 | this.initOnKeyListeners(); 154 | } 155 | }, 156 | 157 | /** 158 | * i18n support 159 | */ 160 | TRANSLATIONS : {}, 161 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, 162 | LOCALE : 'unknown', 163 | 164 | // gettext and ngettext don't access this so that the functions 165 | // can safely bound to a different name (_ = Documentation.gettext) 166 | gettext : function(string) { 167 | var translated = Documentation.TRANSLATIONS[string]; 168 | if (typeof translated === 'undefined') 169 | return string; 170 | return (typeof translated === 'string') ? translated : translated[0]; 171 | }, 172 | 173 | ngettext : function(singular, plural, n) { 174 | var translated = Documentation.TRANSLATIONS[singular]; 175 | if (typeof translated === 'undefined') 176 | return (n == 1) ? singular : plural; 177 | return translated[Documentation.PLURALEXPR(n)]; 178 | }, 179 | 180 | addTranslations : function(catalog) { 181 | for (var key in catalog.messages) 182 | this.TRANSLATIONS[key] = catalog.messages[key]; 183 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 184 | this.LOCALE = catalog.locale; 185 | }, 186 | 187 | /** 188 | * add context elements like header anchor links 189 | */ 190 | addContextElements : function() { 191 | $('div[id] > :header:first').each(function() { 192 | $('\u00B6'). 193 | attr('href', '#' + this.id). 194 | attr('title', _('Permalink to this headline')). 195 | appendTo(this); 196 | }); 197 | $('dt[id]').each(function() { 198 | $('\u00B6'). 199 | attr('href', '#' + this.id). 200 | attr('title', _('Permalink to this definition')). 201 | appendTo(this); 202 | }); 203 | }, 204 | 205 | /** 206 | * workaround a firefox stupidity 207 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 208 | */ 209 | fixFirefoxAnchorBug : function() { 210 | if (document.location.hash && $.browser.mozilla) 211 | window.setTimeout(function() { 212 | document.location.href += ''; 213 | }, 10); 214 | }, 215 | 216 | /** 217 | * highlight the search words provided in the url in the text 218 | */ 219 | highlightSearchWords : function() { 220 | var params = $.getQueryParameters(); 221 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 222 | if (terms.length) { 223 | var body = $('div.body'); 224 | if (!body.length) { 225 | body = $('body'); 226 | } 227 | window.setTimeout(function() { 228 | $.each(terms, function() { 229 | body.highlightText(this.toLowerCase(), 'highlighted'); 230 | }); 231 | }, 10); 232 | $('
' + _('Hide Search Matches') + '
') 234 | .appendTo($('#searchbox')); 235 | } 236 | }, 237 | 238 | /** 239 | * init the domain index toggle buttons 240 | */ 241 | initIndexTable : function() { 242 | var togglers = $('img.toggler').click(function() { 243 | var src = $(this).attr('src'); 244 | var idnum = $(this).attr('id').substr(7); 245 | $('tr.cg-' + idnum).toggle(); 246 | if (src.substr(-9) === 'minus.png') 247 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 248 | else 249 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 250 | }).css('display', ''); 251 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 252 | togglers.click(); 253 | } 254 | }, 255 | 256 | /** 257 | * helper function to hide the search marks again 258 | */ 259 | hideSearchWords : function() { 260 | $('#searchbox .highlight-link').fadeOut(300); 261 | $('span.highlighted').removeClass('highlighted'); 262 | }, 263 | 264 | /** 265 | * make the url absolute 266 | */ 267 | makeURL : function(relativeURL) { 268 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 269 | }, 270 | 271 | /** 272 | * get the current relative url 273 | */ 274 | getCurrentURL : function() { 275 | var path = document.location.pathname; 276 | var parts = path.split(/\//); 277 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 278 | if (this === '..') 279 | parts.pop(); 280 | }); 281 | var url = parts.join('/'); 282 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 283 | }, 284 | 285 | initOnKeyListeners: function() { 286 | $(document).keydown(function(event) { 287 | var activeElementType = document.activeElement.tagName; 288 | // don't navigate when in search box or textarea 289 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' 290 | && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) { 291 | switch (event.keyCode) { 292 | case 37: // left 293 | var prevHref = $('link[rel="prev"]').prop('href'); 294 | if (prevHref) { 295 | window.location.href = prevHref; 296 | return false; 297 | } 298 | case 39: // right 299 | var nextHref = $('link[rel="next"]').prop('href'); 300 | if (nextHref) { 301 | window.location.href = nextHref; 302 | return false; 303 | } 304 | } 305 | } 306 | }); 307 | } 308 | }; 309 | 310 | // quick alias for translations 311 | _ = Documentation.gettext; 312 | 313 | $(document).ready(function() { 314 | Documentation.init(); 315 | }); 316 | -------------------------------------------------------------------------------- /docs/_build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: 'v0.0.7', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]; 14 | 15 | 16 | /* Non-minified version JS is _stemmer.js if file is provided */ 17 | /** 18 | * Porter Stemmer 19 | */ 20 | var Stemmer = function() { 21 | 22 | var step2list = { 23 | ational: 'ate', 24 | tional: 'tion', 25 | enci: 'ence', 26 | anci: 'ance', 27 | izer: 'ize', 28 | bli: 'ble', 29 | alli: 'al', 30 | entli: 'ent', 31 | eli: 'e', 32 | ousli: 'ous', 33 | ization: 'ize', 34 | ation: 'ate', 35 | ator: 'ate', 36 | alism: 'al', 37 | iveness: 'ive', 38 | fulness: 'ful', 39 | ousness: 'ous', 40 | aliti: 'al', 41 | iviti: 'ive', 42 | biliti: 'ble', 43 | logi: 'log' 44 | }; 45 | 46 | var step3list = { 47 | icate: 'ic', 48 | ative: '', 49 | alize: 'al', 50 | iciti: 'ic', 51 | ical: 'ic', 52 | ful: '', 53 | ness: '' 54 | }; 55 | 56 | var c = "[^aeiou]"; // consonant 57 | var v = "[aeiouy]"; // vowel 58 | var C = c + "[^aeiouy]*"; // consonant sequence 59 | var V = v + "[aeiou]*"; // vowel sequence 60 | 61 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 62 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 63 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 64 | var s_v = "^(" + C + ")?" + v; // vowel in stem 65 | 66 | this.stemWord = function (w) { 67 | var stem; 68 | var suffix; 69 | var firstch; 70 | var origword = w; 71 | 72 | if (w.length < 3) 73 | return w; 74 | 75 | var re; 76 | var re2; 77 | var re3; 78 | var re4; 79 | 80 | firstch = w.substr(0,1); 81 | if (firstch == "y") 82 | w = firstch.toUpperCase() + w.substr(1); 83 | 84 | // Step 1a 85 | re = /^(.+?)(ss|i)es$/; 86 | re2 = /^(.+?)([^s])s$/; 87 | 88 | if (re.test(w)) 89 | w = w.replace(re,"$1$2"); 90 | else if (re2.test(w)) 91 | w = w.replace(re2,"$1$2"); 92 | 93 | // Step 1b 94 | re = /^(.+?)eed$/; 95 | re2 = /^(.+?)(ed|ing)$/; 96 | if (re.test(w)) { 97 | var fp = re.exec(w); 98 | re = new RegExp(mgr0); 99 | if (re.test(fp[1])) { 100 | re = /.$/; 101 | w = w.replace(re,""); 102 | } 103 | } 104 | else if (re2.test(w)) { 105 | var fp = re2.exec(w); 106 | stem = fp[1]; 107 | re2 = new RegExp(s_v); 108 | if (re2.test(stem)) { 109 | w = stem; 110 | re2 = /(at|bl|iz)$/; 111 | re3 = new RegExp("([^aeiouylsz])\\1$"); 112 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 113 | if (re2.test(w)) 114 | w = w + "e"; 115 | else if (re3.test(w)) { 116 | re = /.$/; 117 | w = w.replace(re,""); 118 | } 119 | else if (re4.test(w)) 120 | w = w + "e"; 121 | } 122 | } 123 | 124 | // Step 1c 125 | re = /^(.+?)y$/; 126 | if (re.test(w)) { 127 | var fp = re.exec(w); 128 | stem = fp[1]; 129 | re = new RegExp(s_v); 130 | if (re.test(stem)) 131 | w = stem + "i"; 132 | } 133 | 134 | // Step 2 135 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 136 | if (re.test(w)) { 137 | var fp = re.exec(w); 138 | stem = fp[1]; 139 | suffix = fp[2]; 140 | re = new RegExp(mgr0); 141 | if (re.test(stem)) 142 | w = stem + step2list[suffix]; 143 | } 144 | 145 | // Step 3 146 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 147 | if (re.test(w)) { 148 | var fp = re.exec(w); 149 | stem = fp[1]; 150 | suffix = fp[2]; 151 | re = new RegExp(mgr0); 152 | if (re.test(stem)) 153 | w = stem + step3list[suffix]; 154 | } 155 | 156 | // Step 4 157 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 158 | re2 = /^(.+?)(s|t)(ion)$/; 159 | if (re.test(w)) { 160 | var fp = re.exec(w); 161 | stem = fp[1]; 162 | re = new RegExp(mgr1); 163 | if (re.test(stem)) 164 | w = stem; 165 | } 166 | else if (re2.test(w)) { 167 | var fp = re2.exec(w); 168 | stem = fp[1] + fp[2]; 169 | re2 = new RegExp(mgr1); 170 | if (re2.test(stem)) 171 | w = stem; 172 | } 173 | 174 | // Step 5 175 | re = /^(.+?)e$/; 176 | if (re.test(w)) { 177 | var fp = re.exec(w); 178 | stem = fp[1]; 179 | re = new RegExp(mgr1); 180 | re2 = new RegExp(meq1); 181 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 182 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 183 | w = stem; 184 | } 185 | re = /ll$/; 186 | re2 = new RegExp(mgr1); 187 | if (re.test(w) && re2.test(w)) { 188 | re = /.$/; 189 | w = w.replace(re,""); 190 | } 191 | 192 | // and turn initial Y back to y 193 | if (firstch == "y") 194 | w = firstch.toLowerCase() + w.substr(1); 195 | return w; 196 | } 197 | } 198 | 199 | 200 | 201 | 202 | 203 | var splitChars = (function() { 204 | var result = {}; 205 | var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, 206 | 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, 207 | 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, 208 | 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, 209 | 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, 210 | 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, 211 | 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, 212 | 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, 213 | 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, 214 | 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; 215 | var i, j, start, end; 216 | for (i = 0; i < singles.length; i++) { 217 | result[singles[i]] = true; 218 | } 219 | var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], 220 | [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], 221 | [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], 222 | [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], 223 | [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], 224 | [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], 225 | [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], 226 | [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], 227 | [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], 228 | [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], 229 | [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], 230 | [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], 231 | [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], 232 | [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], 233 | [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], 234 | [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], 235 | [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], 236 | [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], 237 | [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], 238 | [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], 239 | [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], 240 | [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], 241 | [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], 242 | [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], 243 | [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], 244 | [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], 245 | [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], 246 | [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], 247 | [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], 248 | [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], 249 | [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], 250 | [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], 251 | [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], 252 | [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], 253 | [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], 254 | [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], 255 | [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], 256 | [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], 257 | [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], 258 | [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], 259 | [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], 260 | [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], 261 | [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], 262 | [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], 263 | [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], 264 | [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], 265 | [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], 266 | [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], 267 | [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; 268 | for (i = 0; i < ranges.length; i++) { 269 | start = ranges[i][0]; 270 | end = ranges[i][1]; 271 | for (j = start; j <= end; j++) { 272 | result[j] = true; 273 | } 274 | } 275 | return result; 276 | })(); 277 | 278 | function splitQuery(query) { 279 | var result = []; 280 | var start = -1; 281 | for (var i = 0; i < query.length; i++) { 282 | if (splitChars[query.charCodeAt(i)]) { 283 | if (start !== -1) { 284 | result.push(query.slice(start, i)); 285 | start = -1; 286 | } 287 | } else if (start === -1) { 288 | start = i; 289 | } 290 | } 291 | if (start !== -1) { 292 | result.push(query.slice(start)); 293 | } 294 | return result; 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /docs/_build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rushad7/control-toolbox/5b10391bf5b2d7f00d2fb81938052fe8baef95a3/docs/_build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 51 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 53 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 54 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 55 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 56 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 57 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 58 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 59 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 60 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 61 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 62 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 63 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 64 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 65 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 66 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 67 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 68 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 69 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_build/html/_static/searchtools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * searchtools.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for the full-text search. 6 | * 7 | * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | if (!Scorer) { 13 | /** 14 | * Simple result scoring code. 15 | */ 16 | var Scorer = { 17 | // Implement the following function to further tweak the score for each result 18 | // The function takes a result array [filename, title, anchor, descr, score] 19 | // and returns the new score. 20 | /* 21 | score: function(result) { 22 | return result[4]; 23 | }, 24 | */ 25 | 26 | // query matches the full name of an object 27 | objNameMatch: 11, 28 | // or matches in the last dotted part of the object name 29 | objPartialMatch: 6, 30 | // Additive scores depending on the priority of the object 31 | objPrio: {0: 15, // used to be importantResults 32 | 1: 5, // used to be objectResults 33 | 2: -5}, // used to be unimportantResults 34 | // Used when the priority is not in the mapping. 35 | objPrioDefault: 0, 36 | 37 | // query found in title 38 | title: 15, 39 | partialTitle: 7, 40 | // query found in terms 41 | term: 5, 42 | partialTerm: 2 43 | }; 44 | } 45 | 46 | if (!splitQuery) { 47 | function splitQuery(query) { 48 | return query.split(/\s+/); 49 | } 50 | } 51 | 52 | /** 53 | * Search Module 54 | */ 55 | var Search = { 56 | 57 | _index : null, 58 | _queued_query : null, 59 | _pulse_status : -1, 60 | 61 | htmlToText : function(htmlString) { 62 | var htmlElement = document.createElement('span'); 63 | htmlElement.innerHTML = htmlString; 64 | $(htmlElement).find('.headerlink').remove(); 65 | docContent = $(htmlElement).find('[role=main]')[0]; 66 | if(docContent === undefined) { 67 | console.warn("Content block not found. Sphinx search tries to obtain it " + 68 | "via '[role=main]'. Could you check your theme or template."); 69 | return ""; 70 | } 71 | return docContent.textContent || docContent.innerText; 72 | }, 73 | 74 | init : function() { 75 | var params = $.getQueryParameters(); 76 | if (params.q) { 77 | var query = params.q[0]; 78 | $('input[name="q"]')[0].value = query; 79 | this.performSearch(query); 80 | } 81 | }, 82 | 83 | loadIndex : function(url) { 84 | $.ajax({type: "GET", url: url, data: null, 85 | dataType: "script", cache: true, 86 | complete: function(jqxhr, textstatus) { 87 | if (textstatus != "success") { 88 | document.getElementById("searchindexloader").src = url; 89 | } 90 | }}); 91 | }, 92 | 93 | setIndex : function(index) { 94 | var q; 95 | this._index = index; 96 | if ((q = this._queued_query) !== null) { 97 | this._queued_query = null; 98 | Search.query(q); 99 | } 100 | }, 101 | 102 | hasIndex : function() { 103 | return this._index !== null; 104 | }, 105 | 106 | deferQuery : function(query) { 107 | this._queued_query = query; 108 | }, 109 | 110 | stopPulse : function() { 111 | this._pulse_status = 0; 112 | }, 113 | 114 | startPulse : function() { 115 | if (this._pulse_status >= 0) 116 | return; 117 | function pulse() { 118 | var i; 119 | Search._pulse_status = (Search._pulse_status + 1) % 4; 120 | var dotString = ''; 121 | for (i = 0; i < Search._pulse_status; i++) 122 | dotString += '.'; 123 | Search.dots.text(dotString); 124 | if (Search._pulse_status > -1) 125 | window.setTimeout(pulse, 500); 126 | } 127 | pulse(); 128 | }, 129 | 130 | /** 131 | * perform a search for something (or wait until index is loaded) 132 | */ 133 | performSearch : function(query) { 134 | // create the required interface elements 135 | this.out = $('#search-results'); 136 | this.title = $('').appendTo(this.out); 139 | this.output = $('
The control-toolbox is a Python Library for implementing and simulating various systems and control strategies.
44 |Current Supported Functionality:
45 |System modeling with Transfer Functions and State Space Representations.
Time Domain Response.
Frequency Response.
System Representation conversion: State Space model to Transfer Function and vice versa.
Block diagram algebra: Series and Parallel.
Stability Analysis.
Root Locus.
Bode Plot.
Parameterization of System.
Pole-Zero / Eigenvalue plot of systems.
Feedback analysis.
PID control.
Observability and Controllability.
Full State Feedback
Full State Observer
Linear Quadratic Regulator(LQR)
Linear Quadratic Estimator(LQE) / Kalman Filter
Linearization.
System Identification.
Future Updates:
67 |Linear Quadratic Gaussian Control.
Extended Kalman Filter.
Unscented Kalman filter.
Model Predictive Control.
Documentation:
74 |The control-toolbox is a Python Library for implementing and simulating various systems and control strategies. All information regarding this library can be found here, including the documentation and usage of the modules.
48 |To install it use:
54 |pip install control-toolbox
55 |
git clone https://github.com/rushad7/control-toolbox.git
64 |To start using the package, we simply import it as:
68 |>>> import control
69 |
The following table lists the classes and methods that can be used to model and simulate various systems and control strategies.
44 |Class |
53 | Description |
54 |
---|---|
TransferFunction(num, den) |
58 | Creates a Transfer Function system object |
59 |
StateSpace(A,B,C,D) |
61 | Creates a State Space system object |
62 |
Method |
75 | Description |
76 |
---|---|
display() |
80 | Displays the Transfer Function system |
81 |
parameters(settling_time_tolerance=0.02) |
83 | Returns the parameters of the system |
84 |
response(input_type, time_period=10, sample_time=0.05, ret=False, show=True) |
86 | Returns the time domain response on the system |
87 |
pzplot(ret=True) |
89 | Plots the Pole-Zero plot of the system and return poles and zeros |
90 |
stability() |
92 | Returns the stability of the system |
93 |
rootlocus(tf, gain_range=10.0) |
95 | Returns Root Locus of the system |
96 |
Method |
109 | Description |
110 |
---|---|
display() |
114 | Displays the State Space system |
115 |
convert2TF() |
117 | Converts State Space model to Transfer Function |
118 |
contr() |
120 | Controllability of the system |
121 |
obs() |
123 | Observability of the system |
124 |
stability() |
126 | Stability of the system |
127 |
eigplot(ret=True) |
129 | Plots eigenvalues/poles of system |
130 |
StateResponse(t, initial_cond, u, ret=False, show=True) |
132 | Returns and plots state response |
133 |
OutputResponse(t, initial_cond, u, ret=False, show=True) |
135 | Returns and plots output response |
136 |
Method |
149 | Description |
150 |
---|---|
feedback( G, H=1.0, feedback_type=”negative”) |
154 | Creates a Feedback Object |
155 |
reduce.series(tf1, *tfn) |
157 | Return the series connection (tfn * … |
158 |
reduce.parallel(tf1, *tfn) |
160 | Return the parallel connection (tfn + … |
161 |
Method |
174 | Description |
175 |
---|---|
bode.freqresp(tf) |
179 | Returns the Frequency Response of the system |
180 |
bode.bode(tf) |
182 | Returns the Bode Plot of the system |
183 |
Method |
196 | Description |
197 |
---|---|
PID(Kp, Ki, Kd, tf) |
201 | Creates a PID object |
202 |
response(input_type, time_period=10, sample_time=0.05, ret=False, show=True) |
204 | Return the response of the system after PID control |
205 |
tune(input_type=”step”, set_point=1, num_itr=70, rate=0.00000000001, lambd=0.7) |
207 | Tune the PID coefficients |
208 |
display() |
210 | Display the PID block |
211 |
reduced_tf |
213 | Displays the reduced Transfer Function (Controller + Plant) |
214 |
Method |
227 | Description |
228 |
---|---|
StateFeedback(ss) |
232 | Creates a StateFeedback object |
233 |
solve(roots) |
235 | Calculates the State Feedback gain matrix |
236 |
model(k_ref=1) |
238 | Retruns StateSpace object after applying State Feedback |
239 |
Method |
252 | Description |
253 |
---|---|
StateObserver(ss) |
257 | Creates StateObserver object |
258 |
solve(roots) |
260 | Calculates the State Observer gain matrix |
261 |
model(k_ref=1) |
263 | Retruns the StateSpace object after applying State Observervation |
264 |
Method |
277 | Description |
278 |
---|---|
LQR(ss, Q, R, N) |
282 | Creates an LQR object |
283 |
solve() |
285 | Calculates the optimal gain matrix |
286 |
model() |
288 | Returns the StateSpace object after applying State Observation |
289 |
Method |
302 | Description |
303 |
---|---|
KalmanFilter(ss, Q, R, P=None, x0=None) |
307 | Creates a KalmanFilter object |
308 |
predict(u = 0): |
310 | Predict next state |
311 |
update(z): |
313 | Update Predictions |
314 |
solve(measurements, ret_k=False, ret_x=False): |
316 | Returns the Predictions, Kalman Gain, and States of the system |
317 |
Method |
330 | Description |
331 |
---|---|
linearize(ss, x0, x_op, u_op, y_op) |
335 | Creates a linearize object |
336 |
linearize() |
338 | Linearizes the system and returns the systems state space matrices |
339 |
Method |
352 | Description |
353 |
---|---|
SystemIdentification(path_x, path_x_dot, path_y) |
357 | Creates a SystemIdentification object |
358 |
fit(num_epochs=500): |
360 | Models the system |
361 |
model(): |
363 | Returns the dictioinary of system matrices |
364 |
Systems can be defined by Transfer Functions and State Space models. Both system representations provide near identical utility.
48 |A Transfer Function is the Laplace Transform of the ratio of output to input, and are mathematically described as:
51 |To define a system in terms of a Transfer Function, use the TransferFunction class.
55 |>>> import control
56 | >>> s = control.TransferFunction(num, den)
57 |
Here num and den can be lists or numpy arrays
60 |Space State models are mathamatically described as:
64 |To define a system as Space State model, use the StateSpace class.
68 |>>> import control
69 | >>> s = control.StateSpce(A,B,C,D)
70 |
Here, A,B,C,D are ndarrays.
73 |Note
75 |A system can be defined by any of the two representations above. If a particular method is needed but is not provided for the given system representation (which is unlikely), you can convert the system model to the desired representation using the convert2TF() or convert2SS() method.
76 |46 | Please activate JavaScript to enable the search 47 | functionality. 48 |
49 |51 | Searching for multiple words only shows matches that contain 52 | all words. 53 |
54 | 59 | 60 |