loopkit 0.0.1a1__tar.gz → 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. loopkit-1.0.0/AUTHORS +1 -0
  2. loopkit-1.0.0/LICENSE +34 -0
  3. loopkit-1.0.0/MANIFEST.in +20 -0
  4. loopkit-1.0.0/PKG-INFO +228 -0
  5. loopkit-1.0.0/README.md +159 -0
  6. loopkit-1.0.0/loopkit/__init__.py +12 -0
  7. loopkit-1.0.0/loopkit/component.py +611 -0
  8. loopkit-1.0.0/loopkit/components/__init__.py +66 -0
  9. loopkit-1.0.0/loopkit/components/_opamp.py +135 -0
  10. loopkit-1.0.0/loopkit/components/_validation.py +115 -0
  11. loopkit-1.0.0/loopkit/components/actuators.py +301 -0
  12. loopkit-1.0.0/loopkit/components/controllers.py +958 -0
  13. loopkit-1.0.0/loopkit/components/delay.py +111 -0
  14. loopkit-1.0.0/loopkit/components/filters.py +301 -0
  15. loopkit-1.0.0/loopkit/components/gains.py +181 -0
  16. loopkit-1.0.0/loopkit/components/pll_components.py +198 -0
  17. loopkit-1.0.0/loopkit/dimension.py +216 -0
  18. loopkit-1.0.0/loopkit/dsp.py +252 -0
  19. loopkit-1.0.0/loopkit/loop.py +1255 -0
  20. loopkit-1.0.0/loopkit/loopmath.py +917 -0
  21. loopkit-1.0.0/loopkit/loops/__init__.py +44 -0
  22. loopkit-1.0.0/loopkit/loops/laserlock.py +221 -0
  23. loopkit-1.0.0/loopkit/loops/mokulaserlock.py +108 -0
  24. loopkit-1.0.0/loopkit/loops/nprolaserlock.py +676 -0
  25. loopkit-1.0.0/loopkit/loops/pll.py +342 -0
  26. loopkit-1.0.0/loopkit/plots.py +318 -0
  27. loopkit-1.0.0/loopkit/simulation.py +569 -0
  28. loopkit-1.0.0/loopkit/utils.py +144 -0
  29. loopkit-1.0.0/loopkit/vectfit3.py +897 -0
  30. loopkit-1.0.0/loopkit.egg-info/PKG-INFO +228 -0
  31. loopkit-1.0.0/loopkit.egg-info/SOURCES.txt +46 -0
  32. loopkit-1.0.0/loopkit.egg-info/requires.txt +17 -0
  33. loopkit-1.0.0/pyproject.toml +49 -0
  34. loopkit-1.0.0/tests/__init__.py +1 -0
  35. loopkit-1.0.0/tests/conftest.py +6 -0
  36. loopkit-1.0.0/tests/generate_pll_golden.py +90 -0
  37. loopkit-1.0.0/tests/golden/README.md +18 -0
  38. loopkit-1.0.0/tests/golden/pll_golden.npz +0 -0
  39. loopkit-1.0.0/tests/golden/pll_golden_params.json +10 -0
  40. loopkit-1.0.0/tests/test_component.py +270 -0
  41. loopkit-1.0.0/tests/test_dimension.py +289 -0
  42. loopkit-1.0.0/tests/test_dsp.py +283 -0
  43. loopkit-1.0.0/tests/test_loop.py +498 -0
  44. loopkit-1.0.0/tests/test_loopmath.py +499 -0
  45. loopkit-1.0.0/tests/test_pll.py +416 -0
  46. loopkit-0.0.1a1/LICENSE +0 -7
  47. loopkit-0.0.1a1/PKG-INFO +0 -44
  48. loopkit-0.0.1a1/README.md +0 -1
  49. loopkit-0.0.1a1/pyproject.toml +0 -68
  50. loopkit-0.0.1a1/src/loopkit/__init__.py +0 -85
  51. loopkit-0.0.1a1/src/loopkit/cli/__init__.py +0 -3
  52. loopkit-0.0.1a1/src/loopkit/cli/compare.py +0 -274
  53. loopkit-0.0.1a1/src/loopkit/cli/main.py +0 -62
  54. loopkit-0.0.1a1/src/loopkit/cli/sweep.py +0 -313
  55. loopkit-0.0.1a1/src/loopkit/cli/visualize.py +0 -285
  56. loopkit-0.0.1a1/src/loopkit/config.py +0 -1236
  57. loopkit-0.0.1a1/src/loopkit/git.py +0 -154
  58. loopkit-0.0.1a1/src/loopkit/logger.py +0 -729
  59. loopkit-0.0.1a1/src/loopkit/monitor.py +0 -259
  60. loopkit-0.0.1a1/src/loopkit/torch/__init__.py +0 -43
  61. loopkit-0.0.1a1/src/loopkit/torch/checkpoint.py +0 -339
  62. loopkit-0.0.1a1/src/loopkit/torch/mp.py +0 -346
  63. loopkit-0.0.1a1/src/loopkit/tracking.py +0 -203
  64. loopkit-0.0.1a1/src/loopkit/utils.py +0 -75
  65. loopkit-0.0.1a1/src/loopkit.egg-info/PKG-INFO +0 -44
  66. loopkit-0.0.1a1/src/loopkit.egg-info/SOURCES.txt +0 -24
  67. loopkit-0.0.1a1/src/loopkit.egg-info/entry_points.txt +0 -2
  68. loopkit-0.0.1a1/src/loopkit.egg-info/requires.txt +0 -23
  69. {loopkit-0.0.1a1/src → loopkit-1.0.0}/loopkit.egg-info/dependency_links.txt +0 -0
  70. {loopkit-0.0.1a1/src → loopkit-1.0.0}/loopkit.egg-info/top_level.txt +0 -0
  71. {loopkit-0.0.1a1 → loopkit-1.0.0}/setup.cfg +0 -0
loopkit-1.0.0/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Miguel Dovale, University of Arizona
loopkit-1.0.0/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Miguel Dovale
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ This software may be subject to U.S. export control laws. By accepting this
31
+ software, the user agrees to comply with all applicable U.S. export laws and
32
+ regulations. User has the responsibility to obtain export licenses, or other
33
+ export authority as may be required before exporting such information to
34
+ foreign countries or providing access to foreign persons.
@@ -0,0 +1,20 @@
1
+ # Include top-level metadata
2
+ include LICENSE
3
+ include README.md
4
+ include AUTHORS
5
+ include pyproject.toml
6
+
7
+ # Ship library source
8
+ graft loopkit
9
+
10
+ # Ship tests and examples in sdist
11
+ graft examples
12
+ graft tests
13
+ include tests/*.gz
14
+
15
+ # Exclusions
16
+ prune **/__pycache__
17
+ prune tests/__pycache__
18
+
19
+ # Global ignores
20
+ global-exclude *.py[cod] *.so *.dylib *.dll *.DS_Store
loopkit-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: loopkit
3
+ Version: 1.0.0
4
+ Summary: A Python toolkit for Deep Frequency Modulation Interferometry.
5
+ Author-email: Miguel Dovale <mikedovale@pm.me>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2025, Miguel Dovale
9
+
10
+ Redistribution and use in source and binary forms, with or without
11
+ modification, are permitted provided that the following conditions are met:
12
+
13
+ 1. Redistributions of source code must retain the above copyright notice, this
14
+ list of conditions and the following disclaimer.
15
+
16
+ 2. Redistributions in binary form must reproduce the above copyright notice,
17
+ this list of conditions and the following disclaimer in the documentation
18
+ and/or other materials provided with the distribution.
19
+
20
+ 3. Neither the name of the copyright holder nor the names of its
21
+ contributors may be used to endorse or promote products derived from
22
+ this software without specific prior written permission.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ This software may be subject to U.S. export control laws. By accepting this
36
+ software, the user agrees to comply with all applicable U.S. export laws and
37
+ regulations. User has the responsibility to obtain export licenses, or other
38
+ export authority as may be required before exporting such information to
39
+ foreign countries or providing access to foreign persons.
40
+
41
+ Project-URL: Homepage, https://github.com/mdovale/LoopKit
42
+ Project-URL: Repository, https://github.com/mdovale/LoopKit
43
+ Project-URL: Issues, https://github.com/mdovale/LoopKit/issues
44
+ Classifier: Programming Language :: Python :: 3
45
+ Classifier: License :: OSI Approved :: BSD License
46
+ Classifier: Operating System :: OS Independent
47
+ Classifier: Intended Audience :: Science/Research
48
+ Classifier: Topic :: Scientific/Engineering :: Physics
49
+ Requires-Python: >=3.9
50
+ Description-Content-Type: text/markdown
51
+ License-File: LICENSE
52
+ License-File: AUTHORS
53
+ Requires-Dist: numpy
54
+ Requires-Dist: scipy
55
+ Requires-Dist: matplotlib
56
+ Requires-Dist: tqdm
57
+ Requires-Dist: control
58
+ Requires-Dist: sympy
59
+ Requires-Dist: joblib
60
+ Provides-Extra: dev
61
+ Requires-Dist: pytest; extra == "dev"
62
+ Requires-Dist: build; extra == "dev"
63
+ Requires-Dist: twine; extra == "dev"
64
+ Requires-Dist: ruff; extra == "dev"
65
+ Provides-Extra: diagram
66
+ Requires-Dist: PyMuPDF; extra == "diagram"
67
+ Requires-Dist: IPython; extra == "diagram"
68
+ Dynamic: license-file
69
+
70
+
71
+ # loopkit
72
+
73
+ A Python library for modeling, analyzing, and simulating feedback control loops, with a focus on scientific and engineering applications such as laser locking, electronics, and precision instrumentation.
74
+
75
+ ---
76
+
77
+ ## 🔧 What is `loopkit`?
78
+
79
+ `loopkit` provides an object-oriented framework for building and analyzing control loops as directed graphs of signal-processing components. It supports:
80
+
81
+ - Modular design of control systems using Python classes and NumPy/Scipy tools
82
+ - Construction of linear time-invariant (LTI) models via symbolic transfer functions or tabulated data
83
+ - Frequency-domain and time-domain simulation of system behavior
84
+ - Visualization and analysis of loop stability, gain, noise propagation, and performance
85
+ - Extensibility through subclassing and dynamic graph generation
86
+
87
+ The library is designed with scientific control systems in mind—especially applications in optics, interferometry, and laser metrology.
88
+
89
+ ---
90
+
91
+ ## 🧩 Key Features
92
+
93
+ - **Component-based modeling**: Each system block is a `Component` with defined transfer functions, inputs, and outputs.
94
+ - **Flexible interconnection**: Loops are defined by connecting components into directed graphs, not just serial chains.
95
+ - **Simulation support**: Built-in methods to simulate step responses, noise propagation, and open/closed-loop behavior.
96
+ - **Plotting utilities**: Visualize Bode plots, noise budgets, signal flow graphs, and more.
97
+ - **Extensible**: Easily define custom blocks, auto-generate loop structures, and integrate with hardware configurations.
98
+
99
+ ---
100
+
101
+ ## 📦 Installation
102
+
103
+ ```bash
104
+ pip install loopkit
105
+ ```
106
+
107
+ For block diagram generation (`loop.block_diagram()`), install the optional dependencies:
108
+
109
+ ```bash
110
+ pip install loopkit[diagram]
111
+ ```
112
+
113
+ **Note:** `pytikz` is required for block diagrams but is not on PyPI. Install it from the allefeld fork:
114
+
115
+ ```bash
116
+ pip install git+https://github.com/allefeld/pytikz.git
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 🚀 Quick Start
122
+
123
+ ### Example
124
+
125
+ ```python
126
+ import numpy as np
127
+ import matplotlib.pyplot as plt
128
+ from loopkit.component import Component
129
+ from loopkit.components import PIControllerComponent
130
+ from loopkit.loop import LOOP
131
+ import loopkit.loopmath as lm
132
+
133
+ # Define loop parameters
134
+ sps = 80e6 # Loop update frequency in Hz
135
+ frfr = np.logspace(np.log10(1e0), np.log10(40e6), int(1e5))[:-1] # Frequency array (Hz)
136
+
137
+ # Define Plant using Laplace-domain string (auto-discretized)
138
+ w_n = 2 * np.pi * 1e6 # 10 kHz resonance
139
+ zeta = 2.0 # damping ratio
140
+ plant = Component("Plant", sps=sps, tf=f"{w_n**2} / (s**2 + {2*zeta*w_n}*s + {w_n**2})", domain='s')
141
+
142
+ # Define Sensor using z-domain string (explicit difference equation)
143
+ sensor = Component("Sensor", sps=sps, tf="(0.391 + 0.391*z**-1) / (1 - 0.218*z**-1)", domain='z')
144
+
145
+ # Compute the P-servo log2 gain from a dB value
146
+ p_log2_gain = lm.db_to_log2_gain(15)
147
+
148
+ # Compute the integrator log2 gains for a certain cross-over frequency with the P-servo
149
+ i_log2_gain = lm.gain_for_crossover_frequency(p_log2_gain, sps, 1e5, kind='I')
150
+
151
+ # Define PI controller component with those gains
152
+ controller = PIControllerComponent("Controller", sps=sps, Kp=p_log2_gain, Ki=i_log2_gain)
153
+
154
+ # Build the loop
155
+ loop = LOOP(sps, [plant, sensor, controller], name="My Loop")
156
+
157
+ ugf, margin = lm.get_margin(loop.Gf(f=frfr), frfr, deg=True, unwrap_phase=True, interpolate=True) # compute UGF and phase margin
158
+
159
+ print(f"Unity gain frequency = {ugf:.4e} Hz; Phase margin = {margin:.4f} degrees")
160
+
161
+ # Visualize block diagram
162
+ loop.block_diagram(dpi=150)
163
+
164
+ # Bode plot of open-loop gain
165
+ ax = loop.bode_plot(frfr)
166
+ ax[0].axvline(x=ugf, ls='--', c='gray')
167
+ ax[1].axvline(x=ugf, ls='--', c='gray')
168
+ plt.show()
169
+
170
+ # Nyquist plot of open-loop gain
171
+ ax = loop.nyquist_plot(frfr, which='G', logy=True, logx=True, critical_point=True)
172
+ plt.show()
173
+ ```
174
+
175
+ ---
176
+
177
+ ## 🧪 Specialized Loop Implementations
178
+
179
+ The `loopkit.loops` subpackage provides pre-built loop models:
180
+
181
+ - **`PLL`**: Digital phase-locked loop model.
182
+ - **`MokuLaserLock`**: Laser frequency lock model for Moku hardware (heterodyne phase-locking).
183
+ - **`NPROLaserLock`**: Composite model for NPRO laser frequency stabilization using PZT and temperature control loops with digital PLL (phasemeter) readout.
184
+ - **`LaserLockPZT`**, **`LaserLockTemp`**: Building blocks for laser lock subsystems.
185
+
186
+ ```python
187
+ from loopkit.loops import PLL, MokuLaserLock, NPROLaserLock
188
+ ```
189
+
190
+ ---
191
+
192
+ ## 📚 Documentation
193
+
194
+ In-depth API documentation, tutorials, and example notebooks coming soon.
195
+
196
+ ---
197
+
198
+ ## 💡 Design Philosophy
199
+
200
+ - Favor explicit, graph-based design over implicit signal paths
201
+ - Maintain clarity between loop structure and numerical simulation
202
+ - Support rapid prototyping of complex control systems
203
+ - Blend symbolic (transfer function) and numeric (data-driven) components
204
+
205
+ ---
206
+
207
+ ## 👥 Contributing
208
+
209
+ Contributions are welcome! We are especially interested in:
210
+
211
+ - New component definitions (hardware models, filters, sensors)
212
+ - Visualization tools for loop inspection
213
+ - Test coverage and validation scripts
214
+ - Real-world loop configurations from physics labs
215
+
216
+ ---
217
+
218
+ ## 📜 License
219
+
220
+ BSD 3-Clause License.
221
+
222
+ ---
223
+
224
+ ## 🛰 Authors & Acknowledgments
225
+
226
+ Developed by Miguel Dovale, based on an initial implementation by Kohei Yamamoto.
227
+
228
+ ---
@@ -0,0 +1,159 @@
1
+
2
+ # loopkit
3
+
4
+ A Python library for modeling, analyzing, and simulating feedback control loops, with a focus on scientific and engineering applications such as laser locking, electronics, and precision instrumentation.
5
+
6
+ ---
7
+
8
+ ## 🔧 What is `loopkit`?
9
+
10
+ `loopkit` provides an object-oriented framework for building and analyzing control loops as directed graphs of signal-processing components. It supports:
11
+
12
+ - Modular design of control systems using Python classes and NumPy/Scipy tools
13
+ - Construction of linear time-invariant (LTI) models via symbolic transfer functions or tabulated data
14
+ - Frequency-domain and time-domain simulation of system behavior
15
+ - Visualization and analysis of loop stability, gain, noise propagation, and performance
16
+ - Extensibility through subclassing and dynamic graph generation
17
+
18
+ The library is designed with scientific control systems in mind—especially applications in optics, interferometry, and laser metrology.
19
+
20
+ ---
21
+
22
+ ## 🧩 Key Features
23
+
24
+ - **Component-based modeling**: Each system block is a `Component` with defined transfer functions, inputs, and outputs.
25
+ - **Flexible interconnection**: Loops are defined by connecting components into directed graphs, not just serial chains.
26
+ - **Simulation support**: Built-in methods to simulate step responses, noise propagation, and open/closed-loop behavior.
27
+ - **Plotting utilities**: Visualize Bode plots, noise budgets, signal flow graphs, and more.
28
+ - **Extensible**: Easily define custom blocks, auto-generate loop structures, and integrate with hardware configurations.
29
+
30
+ ---
31
+
32
+ ## 📦 Installation
33
+
34
+ ```bash
35
+ pip install loopkit
36
+ ```
37
+
38
+ For block diagram generation (`loop.block_diagram()`), install the optional dependencies:
39
+
40
+ ```bash
41
+ pip install loopkit[diagram]
42
+ ```
43
+
44
+ **Note:** `pytikz` is required for block diagrams but is not on PyPI. Install it from the allefeld fork:
45
+
46
+ ```bash
47
+ pip install git+https://github.com/allefeld/pytikz.git
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 🚀 Quick Start
53
+
54
+ ### Example
55
+
56
+ ```python
57
+ import numpy as np
58
+ import matplotlib.pyplot as plt
59
+ from loopkit.component import Component
60
+ from loopkit.components import PIControllerComponent
61
+ from loopkit.loop import LOOP
62
+ import loopkit.loopmath as lm
63
+
64
+ # Define loop parameters
65
+ sps = 80e6 # Loop update frequency in Hz
66
+ frfr = np.logspace(np.log10(1e0), np.log10(40e6), int(1e5))[:-1] # Frequency array (Hz)
67
+
68
+ # Define Plant using Laplace-domain string (auto-discretized)
69
+ w_n = 2 * np.pi * 1e6 # 10 kHz resonance
70
+ zeta = 2.0 # damping ratio
71
+ plant = Component("Plant", sps=sps, tf=f"{w_n**2} / (s**2 + {2*zeta*w_n}*s + {w_n**2})", domain='s')
72
+
73
+ # Define Sensor using z-domain string (explicit difference equation)
74
+ sensor = Component("Sensor", sps=sps, tf="(0.391 + 0.391*z**-1) / (1 - 0.218*z**-1)", domain='z')
75
+
76
+ # Compute the P-servo log2 gain from a dB value
77
+ p_log2_gain = lm.db_to_log2_gain(15)
78
+
79
+ # Compute the integrator log2 gains for a certain cross-over frequency with the P-servo
80
+ i_log2_gain = lm.gain_for_crossover_frequency(p_log2_gain, sps, 1e5, kind='I')
81
+
82
+ # Define PI controller component with those gains
83
+ controller = PIControllerComponent("Controller", sps=sps, Kp=p_log2_gain, Ki=i_log2_gain)
84
+
85
+ # Build the loop
86
+ loop = LOOP(sps, [plant, sensor, controller], name="My Loop")
87
+
88
+ ugf, margin = lm.get_margin(loop.Gf(f=frfr), frfr, deg=True, unwrap_phase=True, interpolate=True) # compute UGF and phase margin
89
+
90
+ print(f"Unity gain frequency = {ugf:.4e} Hz; Phase margin = {margin:.4f} degrees")
91
+
92
+ # Visualize block diagram
93
+ loop.block_diagram(dpi=150)
94
+
95
+ # Bode plot of open-loop gain
96
+ ax = loop.bode_plot(frfr)
97
+ ax[0].axvline(x=ugf, ls='--', c='gray')
98
+ ax[1].axvline(x=ugf, ls='--', c='gray')
99
+ plt.show()
100
+
101
+ # Nyquist plot of open-loop gain
102
+ ax = loop.nyquist_plot(frfr, which='G', logy=True, logx=True, critical_point=True)
103
+ plt.show()
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 🧪 Specialized Loop Implementations
109
+
110
+ The `loopkit.loops` subpackage provides pre-built loop models:
111
+
112
+ - **`PLL`**: Digital phase-locked loop model.
113
+ - **`MokuLaserLock`**: Laser frequency lock model for Moku hardware (heterodyne phase-locking).
114
+ - **`NPROLaserLock`**: Composite model for NPRO laser frequency stabilization using PZT and temperature control loops with digital PLL (phasemeter) readout.
115
+ - **`LaserLockPZT`**, **`LaserLockTemp`**: Building blocks for laser lock subsystems.
116
+
117
+ ```python
118
+ from loopkit.loops import PLL, MokuLaserLock, NPROLaserLock
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 📚 Documentation
124
+
125
+ In-depth API documentation, tutorials, and example notebooks coming soon.
126
+
127
+ ---
128
+
129
+ ## 💡 Design Philosophy
130
+
131
+ - Favor explicit, graph-based design over implicit signal paths
132
+ - Maintain clarity between loop structure and numerical simulation
133
+ - Support rapid prototyping of complex control systems
134
+ - Blend symbolic (transfer function) and numeric (data-driven) components
135
+
136
+ ---
137
+
138
+ ## 👥 Contributing
139
+
140
+ Contributions are welcome! We are especially interested in:
141
+
142
+ - New component definitions (hardware models, filters, sensors)
143
+ - Visualization tools for loop inspection
144
+ - Test coverage and validation scripts
145
+ - Real-world loop configurations from physics labs
146
+
147
+ ---
148
+
149
+ ## 📜 License
150
+
151
+ BSD 3-Clause License.
152
+
153
+ ---
154
+
155
+ ## 🛰 Authors & Acknowledgments
156
+
157
+ Developed by Miguel Dovale, based on an initial implementation by Kohei Yamamoto.
158
+
159
+ ---
@@ -0,0 +1,12 @@
1
+ """loopkit: control loop modeling and analysis."""
2
+
3
+ import sys
4
+
5
+ # Backward compatibility: expose loops submodules at old paths
6
+ import loopkit.loops.pll
7
+ import loopkit.loops.mokulaserlock
8
+ import loopkit.loops.nprolaserlock
9
+
10
+ sys.modules["loopkit.pll"] = loopkit.loops.pll
11
+ sys.modules["loopkit.mokulaserlock"] = loopkit.loops.mokulaserlock
12
+ sys.modules["loopkit.nprolaserlock"] = loopkit.loops.nprolaserlock