Consideration to use Basler a2A series
KAGRA uses acA640-120gm cameras as the GigE system. Cameras are controlled by pylon-camera-server developed in LIGO. When considering the use of a Basler cameras with pylon-camera-server also for TCam, a2A5328-4gmPRO was listed up as a candidate that satisfies the specification requirements. We then verified the compatibility between a2A5328-4gmPRO and pylon-camera-server.
Conclusion
There was no compatibility between a2A5328-4gmPRO as SDK/GenlCam 2.x and pylon-camera-server as SDK/GenlCam 1.x.
- The best solution from the view point of human resources is to find another camera as SDK/GenlCam 1.x which satisfies the specification requirements.
It's possible to modify pylon-camera-server, but many changes and great efforts are required.
Modifying pylon-camera-server to make compatibility with both 1.x and 2.x is not so fruitful because 3.x and 4.x already exist.
So, the 2nd best solution may be to fork pylon-camera-server as pylon-camera-server2 which is compatible only with 2.x if a2A5328-4gmPRO is really used at the very limited places.
First trial
Config file was prepared as /kagra/camera/pylon-camera-config/K1-CAM-TEST0.ini. Different settings are only resolution (5328x4608 instead of 640x480) and frame rate (4fps instead of 25fps) as follows.
width=5328 height=4608 framerate_numerator=4
- Camera server can be launched by following command.
> /kagra/camera/pylon-camera-service start TEST0
- EPICS IOC was running but camera images cannot be received by camera_client because pylon-camera-server showed a following error.
Feb 18 15:42:55 k1cam2 systemd[750]: Started pylon-camera-server@K1-CAM-TEST0.service - Basler 2D GigE camera RTP H264 UDP server. Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: Obtained request pad src_0 for appsink branch. Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: Obtained request pad src_1 for udpsink branch. Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: Starting main loop. Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: cas warning: Configured TCP port was unavailable. Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: cas warning: Using dynamically assigned TCP port 38533, Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: cas warning: but now two or more servers share the same UDP port. Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: cas warning: Depending on your IP kernel this server may not be Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST) Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: The camera is connected. Feb 18 15:42:55 k1cam2 pylon-camera-server[2132807]: Attaching camera. Feb 18 15:42:56 k1cam2 pylon-camera-server[2132807]: Opening camera. Feb 18 15:42:56 k1cam2 pylon-camera-server[2132807]: Device Model Name: a2A5328-4gmPRO Feb 18 15:42:57 k1cam2 pylon-camera-server[2132807]: Parameter not found in CStringParameter::GetValue. (No node attached.)
- Accessing camera device was succeeded, but camera property cannot be obtained at the beginning of the server launching.
Details
Differences that affect the use of pylon-camera-server
acA640-120gm
a2A5328-4gmPRO
Maximum resolution
659×494
5328×4608
Maximum frame rate
120
~4.8
SDK/GenlCam version
1.x
2.x
Class members
camera.GevCurrentIPAddress
device (*1)
camera.DeviceID
camera.DeviceSerialNumber
camera.AcquisitionFrameRateAbs
camera.AcquisitionFrameRate
camera.ResultingFrameRateAbs
camera.ResultingFrameRate
camera.ExposureTimeRaw
camera.ExposureTime
camera.GainRaw
camera.Gain
- Some of different class members are used in the main infinite-loop as follows, so it's difficult to insert version check for each call of member class.
while (1) { ~ ~ if (epics_exposure_time_raw_set > 0) { g_print("Setting exposure time: %i\n", epics_exposure_time_raw_req); camera.ExposureTimeRaw.TrySetValue((int64_t) epics_exposure_time_raw_req, Pylon::IntegerValueCorrection_Nearest); exposure_time_raw_set = 0; } exposure_time_raw = camera.ExposureTimeRaw.GetValue(); ~ ~ } A wrapper class defined at the out of main loop as follows is required for all methods in all different classes because the class type is also different.
std::function<void(double)> setExposure; std::function<double(void)> getExposure; GenApi::INodeMap& nodemap = camera.GetNodeMap(); if (GenApi::CIntegerPtr(nodemap.GetNode("ExposureTimeRaw")).IsValid()) { GenApi::CIntegerPtr node = nodemap.GetNode("ExposureTimeRaw"); setExposure = [node](double v) { node->SetValue((int64_t)v); }; getExposure = [node]() -> double { return (double)node->GetValue(); }; } else if (GenApi::CFloatPtr(nodemap.GetNode("ExposureTime")).IsValid()) { GenApi::CFloatPtr node = nodemap.GetNode("ExposureTime"); setExposure = [node](double v) { node->SetValue(v); }; getExposure = [node]() -> double { return node->GetValue(); }; }- Then, we can keep a cycle speed of the main loop as follows.
while (1) { ~ ~ if (epics_exposure_time_raw_set > 0) { g_print("Setting exposure time: %f\n", epics_exposure_time_raw_req); setExposure(epics_exposure_time_raw_req); exposure_time_raw_set = 0; } exposure_time_raw = getExposure(); ~ ~ }
For (*1), replacing method directly seems to work fine like as follows.
if (version=1) x = camera.GevCurrentIPAddress.GetValue()); elif (version=2) x = device.GevIpAddress()
