Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple cameras #19

Closed
hinxx opened this issue Nov 16, 2018 · 12 comments
Closed

Multiple cameras #19

hinxx opened this issue Nov 16, 2018 · 12 comments

Comments

@hinxx
Copy link
Contributor

hinxx commented Nov 16, 2018

As far as I can tell current ADAndor supports single camera only.

How big of an effort would it be to support 2 or more cameras?

If I'm not mistaken, one could not run two IOCs and have each instantiate a single ADAndor driver, as-is. The reason being that there is no way of telling a particular ADAndor instance which camera should it talk to.

The Andor SDK seems to support up to 8 cameras. I'm not sure if the way vendor envisioned the access to multiple cameras would work for us.

@MarkRivers
Copy link
Member

I have created a new branch that should support systems with more than 1 camera.

This is most of the git diff:

diff --git a/andorApp/src/andorCCD.cpp b/andorApp/src/andorCCD.cpp
index a27aa05..83e842f 100755
--- a/andorApp/src/andorCCD.cpp
+++ b/andorApp/src/andorCCD.cpp
@@ -101,6 +101,8 @@ static void exitHandler(void *drvPvt);
   * \param[in] installPath The path to the Andor directory containing the detector INI files, etc.
   *            This can be specified as an empty string ("") for new detectors that don't use the INI
   *            files on Windows, but must be a valid path on Linux.
+  * \param[in] cameraID The index number of the desired camera.
+  *            0 is the first camera in the system.
   * \param[in] shamrockID The index number of the Shamrock spectrograph, if installed.
   *            0 is the first Shamrock in the system.  Ignored if there are no Shamrocks.
   * \param[in] maxBuffers The maximum number of NDArray buffers that the NDArrayPool for this driver is
@@ -110,7 +112,7 @@ static void exitHandler(void *drvPvt);
   * \param[in] priority The thread priority for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags.
   * \param[in] stackSize The stack size for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags.
   */
-AndorCCD::AndorCCD(const char *portName, const char *installPath, int shamrockID,
+AndorCCD::AndorCCD(const char *portName, const char *installPath, int cameraID, int shamrockID,
                    int maxBuffers, size_t maxMemory, int priority, int stackSize)

   : ADDriver(portName, 1, NUM_ANDOR_DET_PARAMS, maxBuffers, maxMemory,
@@ -191,6 +193,17 @@ AndorCCD::AndorCCD(const char *portName, const char *installPath, int shamrockID

   // Initialize camera
   try {
+    epicsInt32 numCameras;
+    checkStatus(GetAvailableCameras(&numCameras));
+    if (cameraID < numCameras) {
+      epicsInt32 cameraHandle;
+      checkStatus(GetCameraHandle(cameraID, &cameraHandle));
+      checkStatus(SetCurrentCamera(cameraHandle));
+    } else {
+      asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
+        "%s::%s error selecting camera %d, only %d cameras in system\n",
+        driverName, functionName, cameraID, numCameras);
+    }
     printf("%s:%s: initializing camera\n",
       driverName, functionName);
     checkStatus(Initialize(mInstallPath));
@@ -1841,11 +1854,11 @@ static void andorDataTaskC(void *drvPvt)
   * \param[in] stackSize The stack size for the asyn port driver thread
   */
 extern "C" {
-int andorCCDConfig(const char *portName, const char *installPath, int shamrockID,
+int andorCCDConfig(const char *portName, const char *installPath, int cameraID, int shamrockID,
                    int maxBuffers, size_t maxMemory, int priority, int stackSize)
 {
   /*Instantiate class.*/
-  new AndorCCD(portName, installPath, shamrockID, maxBuffers, maxMemory, priority, stackSize);
+  new AndorCCD(portName, installPath, cameraID, shamrockID, maxBuffers, maxMemory, priority, stackSize);
   return(asynSuccess);
 }

So the constructor and andorCCDConfig functions take an additional parameter, cameraID. This is the index number of the desired camera in the system. You will need to know which camera each index number corresponds to.

This change breaks backwards compatibility, but the only alternative is to put the parameter at the end of the argument list, which is not very logical. I think it belongs before shamrockID.

@hinxx please test this branch and let me know if it works for you.

@hinxx
Copy link
Contributor Author

hinxx commented Dec 5, 2018

Thank you @MarkRivers !
I'll give it a run ASAP and report.

@hinxx
Copy link
Contributor Author

hinxx commented Dec 5, 2018

After patching the R2-7 ADAndor with the above changes and adjusting the st.cmd to talk to camera ID 0 and 1 (third argument to the andorCCDConfig), I get the following results:

andorCCDConfig("LUCA1", "/opt/bde/R3.15.5/adandor-R2-7/etc/andor", 0, 0, 0, 0, 0, 0)
andorCCD:AndorCCD: initializing camera
CCD initialized OK!
andorCCD:statusTask: Status thread started...
andorCCD:dataTask: Data thread started...
dbLoadRecords("andorCCD.template",   "P=OLL-LAB:,R=LUCA1:,PORT=LUCA1,ADDR=0,TIMEOUT=1")
andorCCDConfig("IDUS1", "/opt/bde/R3.15.5/adandor-R2-7/etc/andor", 1, 0, 0, 0, 0, 0)
andorCCD:AndorCCD: initializing camera
2018/12/05 13:31:05.223 andorCCD:statusTask: ERROR: Driver is not initialized.
2018/12/05 13:31:09.295 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:09.368 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:09.427 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:09.627 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:09.827 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:10.027 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:10.228 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:10.428 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:10.628 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:10.828 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:11.028 andorCCD:statusTask: ERROR: Unable to communicate with card.
2018/12/05 13:31:11.228 andorCCD:statusTask: ERROR: Unable to communicate with card.
andorCCD:statusTask: Status thread started...
CCD initialized OK!

I'm not really sure if the errors above mean something, since at the end there is and OK message.
I can control one of the cameras, while other gives up with

epics> 2018/12/05 13:36:51.092 andorCCD:setupAcquisition: ERROR: Parameter 4 not valid.

I will need to debug a bit more to see what the problem is.

@hinxx
Copy link
Contributor Author

hinxx commented Dec 5, 2018

I can successfully talk to only one camera. The other one, even though the initialize says it is OK, does not respond to any commands.

I'm also seeing this failure quite often now - it kills the IOC, but not every IOC start suffers from this fault:

Program received signal SIGABRT, Aborted.
[Switching to Thread 0x7fffeaebb700 (LWP 16725)]
0x00007fffeed0c277 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56	  return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);
>>> bt
#0  0x00007fffeed0c277 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00007fffeed0d968 in __GI_abort () at abort.c:90
#2  0x00007fffef8377d5 in __gnu_cxx::__verbose_terminate_handler() () at ../../../../libstdc++-v3/libsupc++/vterminate.cc:95
#3  0x00007fffef835746 in __cxxabiv1::__terminate(void (*)()) (handler=<optimized out>) at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:38
#4  0x00007fffef835773 in std::terminate() () at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:48
#5  0x00007fffef835993 in __cxxabiv1::__cxa_throw(void*, std::type_info*, void (*)(void*)) (obj=0x7fffe0001040, tinfo=0x7ffff1c9d410 <typeinfo for std::string>, dest=
    0x7fffef894bc0 <std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()>) at ../../../../libstdc++-v3/libsupc++/eh_throw.cc:87
#6  0x00007ffff1a9211f in AndorCCD::checkStatus(unsigned int) (this=this@entry=0x765bc0, returnStatus=<optimized out>) at ../andorCCD.cpp:969
#7  0x00007ffff1a94c0f in AndorCCD::setupAcquisition() (this=this@entry=0x765bc0) at ../andorCCD.cpp:1134
#8  0x00007ffff1a96068 in AndorCCD::writeInt32(asynUser*, int) (this=0x765bc0, pasynUser=0xb681c8, value=0) at ../andorCCD.cpp:659
#9  0x00007ffff33406f2 in writeInt32(void*, asynUser*, epicsInt32) (drvPvt=0x765bc0, pasynUser=0xb681c8, value=0) at ../../asyn/asynPortDriver/asynPortDriver.cpp:1689
#10 0x00007ffff334d7f5 in processCallbackOutput (pasynUser=0xb681c8) at ../../asyn/devEpics/devAsynInt32.c:505
#11 0x00007ffff3321273 in portThread (pport=0x77f5e0) at ../../asyn/asynDriver/asynManager.c:902
#12 0x00007ffff210549c in start_routine (arg=0x74e280) at ../../../src/libCom/osi/os/posix/osdThread.c:403
#13 0x00007fffef0aae25 in start_thread (arg=0x7fffeaebb700) at pthread_create.c:308
#14 0x00007fffeedd4bad in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113
>>> 

@MarkRivers
Copy link
Member

Hi Hinko,
My changes to the driver were intended to let you run 1 camera in each IOC. Please try run each camera in its own IOC, so use a different iocBoot/iocAndor_2 for the second camera. Use a different PREFIX in that IOC.

@hinxx
Copy link
Contributor Author

hinxx commented Dec 5, 2018

I see @MarkRivers, let me give it a try in the coming days and get back to you.

@MarkRivers
Copy link
Member

I changed st.cmd to add the cameraID argument to andorCCDConfig.

You should copy iocAndor to iocAndor2 and in iocAndor2/st.cmd change PREFIX to 13ANDOR2:.

You can pull these changes from the multiple_cameras branch on Github.

@MarkRivers
Copy link
Member

Note that the reason you cannot easily control multiple cameras from the same IOC is a limitation of the Andor SDK. The SDK has functions like this:

GetHeadModel(model);
GetCameraSerialNumber(&serialNumber);

Note that those functions do not have an argument that controls what camera is being addressed, such as a pointer or handle. This means that each set of calls to the SDK would need to be prefixed by a call to

SetCurrentCamera(cameraHandle);

This is quite inconvenient, and would require lots of changes to the driver.

Putting the second camera in its own IOC should avoid this, because hopefully SetCurrentCamera sets a handle that is local to this application, and not global to the entire computer.

@hinxx
Copy link
Contributor Author

hinxx commented Dec 6, 2018

Thanks for the effort @MarkRivers . I'll give it a run ASAP!

I'm trying to understand how the Andor SDK enumerates the USB devices, i.e. which is 0 which is 1, and so on. It seems one can go only with trial and error and change the startup script cameraID parameter afterwards, but this would likely to be required after system power cycle and/or camera re-plugging..

Would it be more intuitive to use model:serial, or VID:PID:serial type of 'cameraID' parameter instead? In the constructor of the ADAndor one would search for the cameraID index that the GetCameraHandle() and SetCurrentCamera() need.

I can do some work on this if you would find it acceptable. I can also test this with two different Andor cameras in the coming weeks, but let me first try out what you have in the multiple_cameras branch.

@MarkRivers
Copy link
Member

It would definitely be better to be able to specify a serial number or some other identifier instead of an index number, if that is possible.

@hinxx
Copy link
Contributor Author

hinxx commented Dec 18, 2018

You can pull these changes from the multiple_cameras branch on Github.

I can control two Andor cameras, using two IOCs, with the above branch.

@hinxx
Copy link
Contributor Author

hinxx commented Dec 18, 2018

Created pull request #20 where camera serial number is used instead of the camera index to select the camera.

@hinxx hinxx closed this as completed Mar 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants