USB-only driver for Santec SLMs - #193
Conversation
Implements the 16-byte FTDI framing protocol via PyD3XX (no vendor DLLs or Windows-only dependencies). Tested on SLM-200 firmware 2018021001.
Drop-in replacement for Santec. Operates in Memory mode; phase patterns are double-buffered across slots 1 and 2. Wavelength calibration retries ReadWL for up to 300 s after WriteWL+WriteAW to handle FPGA settle time.
The warning fires on every import, including from SantecUSB users who never touch the DLL path. The RuntimeError in Santec.__init__ already covers missing DLLs with an identical message.
|
Hi @spomjaksilp , We have been excited about your fork! You did great work in reconstructing the Santec API. Some comments:
Let me know what you think! I can also help reorganize the files into a unified class and start putting in the updated memory mode API. Best, |
|
Hi Ian, First of all: great work on slmsuite. It's clear the design has been carefully thought through, and the algorithmic foundation is what makes us want to migrate our own SLM/WGS codebase to it. SantecThanks for the suggestion. I agree that reducing duplication is important here. However, I think inheritance is cleaner than if/else backend switching. Managing all the if/else cases throughout the class gets messy fast. We'd have 4 distinct combinations to support: DLL + DVI mode With if/else routing, almost every method becomes a branching point. You'd have to touch dozens of places to add a backend or fix a mode-specific issue. It's easy to forget a switch statement and introduce bugs. Imho I would still go with an inheritance approach but with a common base class encapsulating higher level logic (think of it as "Santec specific routines for the class SantecBase(SLM):
# Shared user facing logic: wavelength calibration logic, vendor phase correction, setting phase, necessary abstraction for
# has private hooks as abstract methods to be implemented by the children
class SantecDLL(SantecBase):
def __init__(self, mode='dvi', slm_number=1, display_number=2, ...):
class SantecFTDI(SantecBase):
def __init__(self, mode='dvi', serial_number, ...):Each backend subclass only implements what's unique to its communication layer. The mode parameter handles DVI vs Memory differences cleanly without branching. Users instantiate directly: slm = SantecDLL(mode='memory', ...)
slm = SantecFTDI(mode='dvi', ...)We can alias Santec = SantecDLL for backward compatibility if needed. Memory mode API in
|
|
Hi Suthep @spomjaksilp , AlgorithmsHa, the algorithms module is getting a revamp. It's good if you just want to use WGS and get spots, but it's constraining if you want to do something more custom (we intend to wrap the new algorithm structure with backwards-compatible functions that respect the old API, but the number of lines of code inside these wrappers should be minimal). SantecOur
A middle ground approach would be to have hidden driver classes for FTDI and DLL which both implement the same function traces such that these can be interchanged inside a unified Santec class. Also, regarding FTDI:DVI mode, there would need to be a Memory ModeIt depends on your coding philosophy. I prefer to try to minimize the number of functions and number of arguments, overloading and reusing functions in ways that behave "intuitively". If I pass an RemoteI'm a little bit concerned about remote holograms. Remote cameras/slms currently kinda work because there's a minimal number of functions that need to be passed, but it's different and more complicated for cameraslms or algorithms. This is why the current slmsuite expectation is for the host computer to be the one with the GPU and handle these more complicated classes. (But I look forward to see how you architect things.) Best, |
|
Thanks for the detailed feedback Ian. Here's what I'll do for the next iteration of this PR: Class structureI'll adopt the middle ground you proposed: one public
Memory APII'll defer the public multi-slot API to the superclass work you outlined:
def _set_phase_hw(self, display, index=None):
# ndarray, index=None : write to back buffer and swap (default path)
# ndarray, index=i : write to slot i
# int : switch the displayed slotThis is backward compatible with the current base class (which calls
Best, |
|
Sounds great! Looking forward to it. |
|
Hi Ian, Implemented the refactor as discussed:
Cheers, edit: I tested it on our linux box (smoke test via |
|
Hi @spomjaksilp Sorry for my very late reply. Looks good! I made some small commits:
I'm good with approving the pull request pending testing! Best, |
ichristen
left a comment
There was a problem hiding this comment.
Approved, pending testing.
|
We should also consider combining with #122 |

Description
This MR adds an alternative driver
SantecUSBfor Santec SLMs purely based on usb communication following the "Memory Mode" workflow in the Santec manual (p. 9). Instead of using the .dll libs from Santec, the code leverages direct communication to the FTDI chip inside the controller box via PyD3XX.hardware.slms.santec.SantecUSBis designed to be a separate access path for the SLM, the dll-based implementation is accessible viahardware.slms.santec.Santec. To this end, both classes are now wrapped into thehardware.slms.santecsubmodule.Rationale
Technical
The underlying
SantecFTDI(living in_santec_ftdi.py) is the low-level USB protocol layer that handles the hardware communication via PyD3XX.The inner workings of
SantecFTDIwhere gathered by sniffing and reverse engineering the original dll calls viaFT_WritePipe/FT_ReadPipeinD3XX.dll.Known Limitations
As stated in the manual, setting the phase pattern via the internal memory is slower compared to the update rate via DVI. Yet, this opens the path to using multiple slots and quickly change between them via an external trigger within realtime timing constraints.
The methods for that still need to be implemented and tested, but the groundwork is there.
Code
black -l 120