pyrpl-fpga / pyrpl

pyrpl turns your RedPitaya into a powerful DSP device, especially suitable as a lockbox in quantum optics experiments.
http://lneuhaus.github.io/pyrpl/
MIT License
139 stars 110 forks source link

locking to arbitrary point of MZI #342

Open tymomboe opened 6 years ago

tymomboe commented 6 years ago

Sorry for the potentially long winded question: Has anyone found a good way to lock to an arbitrary point of a MZI using pyRPL? I have a dither lock set up which works great for locking to the peak, and in principle could also use the bare (not demodulated) signal to lock to the point where the transmission is 50% (though, in principle using the bare signal means the lock would be sensitive to power fluctuations), but I have no way of smoothly going between these two extremes. Also, using either the dither or locking using the bare signal becomes quite unstable, as expected, when moving too far from the optimal locking point.

There is a paper (https://www.osapublishing.org/ol/abstract.cfm?uri=ol-20-6-635) which achieves this, but it uses demodulation at both omega and 2omega followed by some (fast) algebra of the resulting two signals, which I think* is outside my comfort zone implementing.

Just hoping I'm missing something and that it is quite simple. Does anyone have any thoughts?

-T

lneuhaus commented 6 years ago

Hi @tymomboe ,

there is a number of things you can try. The possibiliities listed here are in order of increasing effort to implement, with increasing payoff in the performance, simplicifty or beauty:

  1. I have successfully locked Mach-Zehnder-Interferometers at phases +-60 degrees using the "bare" DC photodetector signal, and similarly at phases 90+-60 degrees using a dither/PDH-like signal. From your post it is not clear whether you lock your MZI using directly the IQ and PID modules or whether you are already using the Lockbox module. This lockbox module of Pyrpl (with lockbox class set to "Interferometer" or "PdhInterferometer") is written exactly to accomplish the goal of locking an interferometer at different phases. To do so, the lockbox sets a PID gain proportional to the inverse slope of the error signal at the setpoint, such that - when you lock not at 50% transmission - you still get the optimum gain. There are two hazards here: a) the lock acquisition may fail if you are too far away from the optimum working point, because smaller perturbations of the lock can bring you to a region where the error signal swaps sign, and you will quickly reach the actuator voltage range and loose lock in that case. To avoid this, it usually helps to establish a lock with small enough gain and possibly at the 50% transmission setpoint, and only then switch the setpoint and gain to the desired values.
    b) if in the locked state, the deviations are too high, you may also pass the error signal sign inversion point and loose lock. But for this to be the case, your error signal in the locked state must fluctuate by over 10% of its full range, which is already a very bad lock. In this case, you should first optimize the gain and transfer function of the controller, and possibly try to remove sources of noise and perturbations in the setup. The primary candidate for this is probably your HV amplifier driving a piezo, whose noise you can attenuate by inserting an analog lowpass filter before the piezo.
  2. You can automatize the switching between direct and dither error signal as a function of the desired interferometer phase difference (e.g. at +-45 degrees). I have done this, and it worked more or less. The critical thing to achieve a smooth transition between these two error signals is to do a careful calibration to be able to define the correct setpoints as a function of the phase difference. Especially with a dither error signal, you often see small ripples on top of the error signal which come from insufficient filtering out of the modulation, and which can trick you into miscalibrating your error signal shape and then lead to wrong setpoint voltage values. The solution here is to apply a strong enough zero-phase lowpass filter to the calibration data. See the calibration methods of the PdhInterferometer class for more details.
  3. So far, both the dither signal and the direct signal are proportional to the laser power, and thus your locking point stability suffers from power fluctuations when it is non-zero. The paper you mention essentially shows one trick to get rid of this: it combines two signals (each one proportional to laser power) in such a way that at the desired locking point, the combined error signal is zero and therefore power-independent. While the paper dates back to analog times, there are more efficient ways in digital signal processing to get the same result as the paper: simply add the two signals using different weights. Wherever the summed signal crosses zero is your locking point. For example, using the identity sin(a)*cos(b)=0.5*(sin(a-b)+sin(a+b)), and with the definition s1(phi) = A*cos(phi) your direct signal, s2(phi)=B*sin(phi) your low-pass-filtered dither signal, when you want to lock the interferometer phase phi at a value phi0, you compute the combined error signal by applying weights B*(-1)*sin(phi0) and A*cos(phi0) to get s3(phi) = s1(phi)*B*(-1)*sin(phi0) + s2(phi)*A*cos(phi0), which evaluates to s3(phi) = A*B*(-sin(phi0)*cos(phi)+sin(phi)*cos(phi0)) = A*B*0.5(-sin(phi0-phi)-sin(phi0+phi)+sin(phi-phi0)+sin(phi+phi0)) = A*B*sin(phi-phi0) which is simply a phase-shifted version of the original signal that crosses zero at the angle phi0. So, when you want to change the setpoint, you simply change the factors -sin(phi0) and cos(phi0). To implement this, the best would be to wait for the differential PID module (or help implement it) that will allow to combine two inputs with different weights into one PID controller (see issue #332 ). If you need an immediate solution, you can immediately try to pass the two signals through two different pid controllers, and make sure that the gains ratio is the same as the ratio between the two factors to apply according to above calculation. The problem with that solution is that each PID may have a large offset between input and setpoint and therefore might saturate individually. So it would be really better to have that differential PID, which is however much simpler to implement than the algorithm of the paper you mentioned.
  4. Instead of the direct photodiode signal, the paper uses its second derivative (the signal demodulated at twice the modulation frequency, or f-2f-signal), which has however again the shape of a cosine. The only reason to do so would be to avoid possible offsets (there is certainly an analog offset) in the direct signal, at the cost that you will have to dither rather strongly to get a good 2f-signal. Since you are already using the standard "dither" lock-in signal, where modulation and demodulation occur at the same frequency f, you can quite easily get the f-2f-signal from pyrpl by using an additional IQ module with the frequency set to EXACTLY twice the frequency of the first IQ module used for the dither signal and zero drive amplitude. When setting this up, make sure that you do not simply enter e.g. 1e4 for the f-f-IQ-frequency and 2e4 for the second one, since small rounding errors might cause that the 2f-frequency is not exactly twice the f-frequency. Instead, after entering the f-IQ-frequency, take the exact value that is shown (i.e. after rounding), double it and enter it as the frequency of the 2f-IQ signal. The next problem you will face is to get the demodulation phase right. At the moment, there is no way to synchronize two different IQ modules, so you will simply have to find the optimum demodulation phase experimentally after having set up the frequencies of the two IQ modules. We can think of implementing a synchronization mechanism (i.e. a register that, once it is set, makes all IQ sine generators start from their initial phase), but this will take a little bit of time. In my opinion, with a proper calibration of the offsets of the direct photodiode signal, it may not be worth it to implement the f-2f-signal.

Let me know if anything is unclear. I expect to have the differential PID module working in about 2 months from now, unless somebody jumps in to accelerate the development.

tymomboe commented 6 years ago

Thanks very much for you detailed response! That is all very useful.

I am currently using the lockbox module, though only in GUI mode since I had trouble working out how to implement it in python.

  1. I can indeed lock to 90 +- 60 (ie. anywhere from 150-30 degrees is fine). For the experiment I am doing, being able to lock smoothly anywhere from -90 to 90 would be very helpful.
  2. This is something I had though of, but hadn't devoted time to implementing yet (as it would require me learning how to implement the lockbox class in python, rather than using the GUI as I have been doing)
  3. I had definitely not thought of this, and this is indeed a very clever method.
  4. In general, is doing math (like a weighted sum of two signals) in python (as I assume you would have to do to implement this) - rather than on the microprocessor or the FPGA - fast enough to achieve a high locking bandwidth?

Lastly, to get me started on using pyRPL and the lockbox class from the command line(or python notebook), is it best to get a lockbox module setup with the GUI, then import the config file from the command line? This was the quickest way I could think of for using the lockbox module and avoiding having to fish around in the class definition to find the names of all the variables that need setting.

Sorry for all the questions and thank you so much for all of your help, T.

lneuhaus commented 6 years ago

Sorry for the long absence. It is best to setup in the GUI as much as possible (check the video on it), and then customize by editing the config file (.yml) direclty in a text editor. I would only use the API for starting locks and reading out the status of the lockbox.

lneuhaus commented 5 years ago

this is now possible with the differential PID functionality. What is now missing is an upgraded interferometer class that acts on the differential pid.

The way to go here is to define a new input signal class DifferentialInput with:

The locking sequence will then have adjust_error_signal and the desired setpoint in the first or final stage, and should work out of the box.

For backwards compatibility, i.e. if a lock to a direct signal is desired, it should not be forgotten to upgrade the normal lockbox functions such that they disable the differential pid mode.

tymomboe commented 5 years ago

Taking a stab at this, but ran into trouble when defining two "input_signal" (InputSelectProperty). The trouble is that all of the useful functions defined in InputSignal and its children are defined for a single input derived from a dsp module (and linked to it using input_signal).

Does this mean I will have to implement new versions of _input_signal_dsp_module, signal, sweep_acquire, and calibrate so that we have two-input-versions? Or is there some slick way of having two input_signal's and reusing all the old functions?

lneuhaus commented 5 years ago

I think that is the way to go, by re-using as much existing code as possible. But I realized that there are corner cases where the ratio of gains should be very large, i.e. close to 0 and 90 degrees, such that we would better have two inpur gains in the differential pid module for this to work properly. So I would first implement that before the lockbox module. But feel free to give it a try since I will not be able to do this before christmas.

tymomboe commented 5 years ago

Can the two gains not be implemented in the InputSignal class? We are already planning on adjusting the relative gains to make the two error signals roughly equal in magnitude. Why not have some function that adjusts them to make the error signal 0 at the point you want to lock? Then pass the gained signals to the differential pid module rather than the bare inputs.

lneuhaus commented 5 years ago

This is possible if you have a way to adjust the two input signal gains. For the dither signal the gain can be tuned with quadrature_factor, but if the other signal comes directly from an analog input, there is currently no gain implemented in the fpga. One could of course pass that input through another pid, and this is the only implementation of the MZI lock that would work without fpga changes. If you want to go this way, you should define something like ScaledInpuSignal class to group the input with a PID for scaling, and then define a DifferentialInput class whos constructor takes other InputSignals with a settable gain.

lneuhaus commented 5 years ago

Have you done this already or should I give it a try?

tymomboe commented 5 years ago

I'm afraid I wasn't able to, no. I'm having a little trouble understanding how the input signal classes are implemented. I'm sure I will learn a lot from your code, so go for it!