spacetelescope / catkit2

Package for controlling testbed hardware.
https://spacetelescope.github.io/catkit2/
BSD 3-Clause "New" or "Revised" License
5 stars 0 forks source link

Error in the analytical expression for the zwo camera detector staircase #237

Open steigersg opened 3 months ago

steigersg commented 3 months ago

From https://github.com/spacetelescope/hicat-package2/pull/318 (plot copied below for convenience) the detector staircase (before any shifting to ensure it goes through 0) should be flat and then start going up at 50 $\mu$ s.

image

The current analytical expression for this staircase in the exposure_time setter in the ZWO camera service is np.round((exposure_time - self.exposure_time_base_step) / self.exposure_time_step_size) which yields the following

Screenshot 2024-08-20 at 9 29 27 AM

Instead it should be np.ceil((exposure_time - self.exposure_time_base_step) / self.exposure_time_step_size) which causes the jump in counts to occur at the correct exposure time.

Screenshot 2024-08-20 at 9 29 48 AM

It should also be double checked as to whether there needs to be a +/- 1 in the numerator of the np.ceil call to start going up at exactly 50 (and not 49 or 51). This error causes exposure time discrepancies on the order of ~16 $\mu$ s, i.e. one half of the exposure time step size.

An additional point of concern is that the ZWO camera can give a non-zero flux value between 32 $\mu$ s and 50 $\mu$ s. Additional care in general needs to be taken to ensure that the appropriate fluxes and exposure times are being reported for these smallest exposure times (<100 $\mu$ s)

ehpor commented 3 months ago

An image from the staircase experiment on HiCAT shows that it works fine. We'll talk about this on Monday to see if I'm misunderstanding. image

steigersg commented 3 months ago

After talking to @ehpor and looking through the code, I've convinced myself that the np.round is the right way to go.

This is complex because there are three times simultaneously being considered:

  1. The time requested by the user which is sent to the camera service via setting the exposure_time
  2. The time sent by the service to the actual camera (the code within the exposure_time setter where the staircase function is)
  3. The time the camera actually used

Ideologically we want (1) and (3) to be as close as possible and (2) is the subject of this calibration. For the purposes of converting images from counts to rates (counts/s) we only care about (3), but also want to respect the user's wishes for (1).

Using np.round for the staircase will give the camera the closest exposure time to what was requested by the user. For example, if the user wants an exposure time of 70 microseconds then the desired behavior (with the camera discretization step size of 33 microseconds) is to use 83 microseconds and not 50. Vice versa, if the user requests 55 microseconds you do not want it to jump up to 83, but to instead use 50.

This is in addition to the exposure_time_offset which sets the minimum actual exposure time (3) and was calibrated to be 79.2 microseconds. In other words, even if 50 microseconds is requested for, and returned by the camera (2), the actual camera integrating time (3) is 129.2 microseconds and represents the floor seen in the plots posted by Emiel.

In making this issue @RemiSoummer and I were worried that using the np.round could result in a ~16 microsecond discrepancy between what was reported by the exposure_time property in the zwo_camera service and what was actually used by the camera. I see now though that the staircase is just used in the exposure time setter to select which discretized time step should be used for a given requested exposure time which is then sent to the camera. The getter gets this value back directly from the camera and applies the offset and so should always reflect the "true" time used.

If you agree @RemiSoummer then I will close this particular issue.