= 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}}} ||<#CCCCFF> ||<#CCCCFF>'''acA640-120gm''' ||<#CCCCFF>'''a2A5328-4gmPRO''' || || Maximum resolution || {{{659×494}}} || {{{5328×4608}}} || || Maximum frame rate || {{{120}}} || {{{~4.8}}} || || SDK/GenlCam version || {{{1.x}}} || {{{2.x}}} || ||<|6> 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 setExposure; std::function 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() }}}