LabWired
Design circuits, run firmware, compare variants, and inspect the result in Playground. Open source. MCP for Claude or Codex. Free to try.
Automated measurement of a photonic lantern's wavelength-dependent transfer matrix by off-axis digital holography. UCF CREOL.
Translation pending
Developer toolsPython · Jupyter NotebookGitHub @jeebuscrossaintOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
rinky dink program made by silica
Translation pending
Loading the full README.
Read the full README ↗Automated measurement and analysis of the wavelength-dependent transfer matrix of a photonic lantern by off-axis digital holography — for Prof. Stephen Eikenberry's group at UCF CREOL.
A photonic lantern maps several single-mode fibers to the guided modes of a multimode fiber; that mapping is a complex-valued transfer matrix that evolves with wavelength. This software automates measurement of the encoding matrix: it sweeps the input port and wavelength, captures an off-axis hologram of the multimode output at each point, and recovers the complex field (amplitude and phase) for decomposition into LP modes — one row of the transfer matrix per measurement.
The reconstruction follows the method in:
C. Dobias, M. A. Römer, S. Bhargava, et al., "Wavelength-dependent evolution of full-field transfer matrices in photonic lanterns," Optics Express 34(9), 17217 (2026). https://doi.org/10.1364/OE.595908
| Component | Model | Interface |
|---|---|---|
| Tunable laser | HP/Agilent 8168E (1475–1575 nm) | GPIB · Keysight VISA |
| Camera | Xenics Bobcat 320 GigE (InGaAs, 320×256) | GigE Vision |
| Fiber switch | DiCon GP700 | RS-232 |
| Polarization | Thorlabs MPC320 (3 paddles) | USB · Kinesis |
The shipping app is a single native Windows executable — no Python, Node, server, or browser needed on the lab machine:
uv run python tools/build_app.py # -> dist/Digital Holography.exe
Copy dist/Digital Holography.exe to the lab PC and double-click. Connect
All → Start Experiment. Config, logs, and data are written next to the .exe.
Run from source instead (needs uv; no Node):
uv sync
./start.bat # = uv run python main.py
One-time per machine: install Keysight IO Libraries + Xenics Xeneth SDK and run the camera firewall rule — see LAB_SETUP.md.
holo/ is the library — every line of physics lives there. The desktop app and
the holo command are two front ends over it, and neither contains analysis
code of its own, so they can never drift apart.
holo/ THE LIBRARY
pipeline.py combines both engines — change processing here
data_processing.py single-frame reconstruction + LP decomposition
multiport_reconstruction.py cross-port (paper-fidelity) reconstruction + TM
fringe_detection.py fringe metric + polarization auto-optimizer
discovery.py folder -> records; frame loading and staging
config.py config loading
runtime.py frozen-build paths + Xeneth DLL bootstrap
cli.py the `holo` command
lib/ LP modes, FFT math, vendored Xeneth SDK
hardware/ drivers (laser, camera, switch, motors)
gui/ PySide6 (Qt6) app — window, tabs, acquisition loop
main.py GUI launcher (thin entry point)
tools/ build script + hardware probes
tests/ golden-file + unit tests (`uv run pytest`)
experiment_config.yaml instrument addresses + sweep parameters
docs/ setup guides (GigE camera, Xeneth SDK)
holo acquire -o ./run # sweep 1530-1560 nm, 3 exposures per point
holo process ./data # reconstruct a folder (up to 8 processes)
holo tm ./data -o tm.npz # reconstruct + export the transfer matrix
holo doctor # check the install, config and instruments
holo probe switch # talk to one instrument
holo gui # launch the desktop app
uv run holo <command>, or python -m holo without installing. Every command
takes --help. process.py still works as a deprecated shim for
holo process.
holo acquire drives only the laser and the camera — you connect the lantern
leg and set the paddles yourself, which is the usual way to take a single-leg
test. Everything is a flag; no config file to edit.
holo acquire --start 1530 --stop 1560 --step 1 -n 3 --leg 1 -o ./run
holo acquire --start 1530 --stop 1560 -n 3 --dry-run # print the plan, no hardware
-n 3 takes three exposures back to back at each wavelength and writes them to
run/rep01, run/rep02, run/rep03. They are separate folders on purpose:
frame discovery keeps one file per (leg, wavelength), so repeats sharing a
folder would leave all but one silently unprocessed. Each repNN is a complete
sweep — reconstruct one with holo process ./run/rep01.
Every frame gets a .png preview and a .yaml sidecar recording the commanded
and measured wavelength (the HP 8168E queues errors instead of raising, so a
rejected tune otherwise looks like a good frame at the wrong λ). Saturated
frames are flagged and the command exits non-zero — clipping makes intensity
nonlinear in the field, so both amplitude and phase are wrong.
Using it as a library:
from holo import HolographyDataProcessor, pipeline, discovery
records, cache = discovery.stage("./data", default_wl=1550)
rows = pipeline.process_records(HolographyDataProcessor(), records, config=cfg)
See CLAUDE.md for how the two reconstruction engines relate and where the physics lives.
Two engines, picked automatically by how much data the sweep has:
| single-frame | multiport | |
|---|---|---|
| module | data_processing.py | multiport_reconstruction.py |
| needs | one hologram | ≥ 2 legs across the sweep |
| carrier | per-frame sub-pixel centroid, demodulated to baseband | centroid averaged over all ports per λ, then linear-fit vs λ, then Fourier Fine-Binning |
| basis | per-frame optimum | optima consolidated across the sweep (paper Sec. 2) |
Both compute the paper's fidelity η = |⟨E_rec, E_S⟩ / (‖E_rec‖‖E_S‖)|² (Eq. 5). A multi-leg dataset runs both and keeps, per frame, whichever scored higher — so the result can never be worse than single-frame.
All fidelities below use the 8-mode basis unless stated otherwise, and each says how many frames it covers.
| data | frames | fidelity | notes |
|---|---|---|---|
| Jun 9 2026 sweep, port 1, 1525–1575 nm | 51 | 97.2 ± 0.5%, worst 95.8% | background-subtracted |
| Aug 4 2026 7-port sweep (Rumana Akhter), 1525–1569 nm | 215 of 315 | 92.7 ± 1.7%, worst 83.9% | 100 frames clip at 45000 counts and are excluded |
| Caleb's 19-port archive, multiport, 23 modes | 969 | 96.68 ± 1.58%, worst 80.0% | reproduces his own saved run (96.89%) |
| committed test frame | 1 | 0.9776 (8 modes), 0.9788 (15 modes) | GOLDEN_FIDELITY |
pipeline keeps the better engine per frame.tests/test_multiport.py. It skips unless the gitignored
dataset is unpacked to _caleb_ref/.Amarnath Patel (UCF Physics / CREOL, Eikenberry group), building on Caleb Dobias's original measurement and analysis work (see the paper above). The Aug 4 2026 7-port sweep was taken by Rumana Akhter with this software.