Skip to main content

A Python toolkit for Deep Frequency Modulation Interferometry.

Project description

loopkit

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.


🔧 What is loopkit?

loopkit provides an object-oriented framework for building and analyzing control loops as directed graphs of signal-processing components. It supports:

  • Modular design of control systems using Python classes and NumPy/Scipy tools
  • Construction of linear time-invariant (LTI) models via symbolic transfer functions or tabulated data
  • Frequency-domain and time-domain simulation of system behavior
  • Visualization and analysis of loop stability, gain, noise propagation, and performance
  • Extensibility through subclassing and dynamic graph generation

The library is designed with scientific control systems in mind—especially applications in optics, interferometry, and laser metrology.


🧩 Key Features

  • Component-based modeling: Each system block is a Component with defined transfer functions, inputs, and outputs.
  • Flexible interconnection: Loops are defined by connecting components into directed graphs, not just serial chains.
  • Simulation support: Built-in methods to simulate step responses, noise propagation, and open/closed-loop behavior.
  • Plotting utilities: Visualize Bode plots, noise budgets, signal flow graphs, and more.
  • Extensible: Easily define custom blocks, auto-generate loop structures, and integrate with hardware configurations.

📦 Installation

pip install loopkit

For block diagram generation (loop.block_diagram()), install the optional dependencies:

pip install loopkit[diagram]

Note: pytikz is required for block diagrams but is not on PyPI. Install it from the allefeld fork:

pip install git+https://github.com/allefeld/pytikz.git

🚀 Quick Start

Example

import numpy as np
import matplotlib.pyplot as plt
from loopkit.component import Component
from loopkit.components import PIControllerComponent
from loopkit.loop import LOOP
import loopkit.loopmath as lm

# Define loop parameters
sps = 80e6  # Loop update frequency in Hz
frfr = np.logspace(np.log10(1e0), np.log10(40e6), int(1e5))[:-1]  # Frequency array (Hz)

# Define Plant using Laplace-domain string (auto-discretized)
w_n = 2 * np.pi * 1e6 # 10 kHz resonance
zeta = 2.0 # damping ratio
plant = Component("Plant", sps=sps, tf=f"{w_n**2} / (s**2 + {2*zeta*w_n}*s + {w_n**2})", domain='s')

# Define Sensor using z-domain string (explicit difference equation)
sensor = Component("Sensor", sps=sps, tf="(0.391 + 0.391*z**-1) / (1 - 0.218*z**-1)", domain='z')

# Compute the P-servo log2 gain from a dB value
p_log2_gain = lm.db_to_log2_gain(15)

# Compute the integrator log2 gains for a certain cross-over frequency with the P-servo
i_log2_gain = lm.gain_for_crossover_frequency(p_log2_gain, sps, 1e5, kind='I')

# Define PI controller component with those gains
controller = PIControllerComponent("Controller", sps=sps, Kp=p_log2_gain, Ki=i_log2_gain)

# Build the loop
loop = LOOP(sps, [plant, sensor, controller], name="My Loop")

ugf, margin = lm.get_margin(loop.Gf(f=frfr), frfr, deg=True, unwrap_phase=True, interpolate=True) # compute UGF and phase margin

print(f"Unity gain frequency = {ugf:.4e} Hz; Phase margin = {margin:.4f} degrees")

# Visualize block diagram
loop.block_diagram(dpi=150)

# Bode plot of open-loop gain
ax = loop.bode_plot(frfr)
ax[0].axvline(x=ugf, ls='--', c='gray')
ax[1].axvline(x=ugf, ls='--', c='gray')
plt.show()

# Nyquist plot of open-loop gain
ax = loop.nyquist_plot(frfr, which='G', logy=True, logx=True, critical_point=True)
plt.show()

🧪 Specialized Loop Implementations

The loopkit.loops subpackage provides pre-built loop models:

  • PLL: Digital phase-locked loop model.
  • MokuLaserLock: Laser frequency lock model for Moku hardware (heterodyne phase-locking).
  • NPROLaserLock: Composite model for NPRO laser frequency stabilization using PZT and temperature control loops with digital PLL (phasemeter) readout.
  • LaserLockPZT, LaserLockTemp: Building blocks for laser lock subsystems.
from loopkit.loops import PLL, MokuLaserLock, NPROLaserLock

📚 Documentation

In-depth API documentation, tutorials, and example notebooks coming soon.


💡 Design Philosophy

  • Favor explicit, graph-based design over implicit signal paths
  • Maintain clarity between loop structure and numerical simulation
  • Support rapid prototyping of complex control systems
  • Blend symbolic (transfer function) and numeric (data-driven) components

👥 Contributing

Contributions are welcome! We are especially interested in:

  • New component definitions (hardware models, filters, sensors)
  • Visualization tools for loop inspection
  • Test coverage and validation scripts
  • Real-world loop configurations from physics labs

📜 License

BSD 3-Clause License.


🛰 Authors & Acknowledgments

Developed by Miguel Dovale, based on an initial implementation by Kohei Yamamoto.


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loopkit-1.0.0.tar.gz (123.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

loopkit-1.0.0-py3-none-any.whl (102.5 kB view details)

Uploaded Python 3

File details

Details for the file loopkit-1.0.0.tar.gz.

File metadata

  • Download URL: loopkit-1.0.0.tar.gz
  • Upload date:
  • Size: 123.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for loopkit-1.0.0.tar.gz
Algorithm Hash digest
SHA256 9119b1653ef318ec3677ad7938807263fce88ba3ec4d6a18eb2a812b16b37aa2
MD5 8b5e9d2ad6064ed6d3027a22214c7c7d
BLAKE2b-256 cbb673e8ada0dc3ea0e51ff21b2d578d281bf7efa3194b2bba05afd7f98219aa

See more details on using hashes here.

File details

Details for the file loopkit-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: loopkit-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 102.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for loopkit-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 efa3ea925cf3055e516c6d56470c0000321bf70c9774cef867f0e68c20f68d6f
MD5 42c9b5dbb8160e9766c94879cbec4339
BLAKE2b-256 7424db5ac45c4cc35ff35680973e85e9eca1f3edefb33964325bff137847e241

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page