diff --git a/CMakeLists.txt b/CMakeLists.txt index dd622766..67595f64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,6 +200,8 @@ set(cam_SRC ${phd_src_dir}/cam_openssag.h ${phd_src_dir}/cam_OSPL130.cpp ${phd_src_dir}/cam_OSPL130.h + ${phd_src_dir}/cam_poa.cpp + ${phd_src_dir}/cam_poa.h ${phd_src_dir}/cam_qguide.cpp ${phd_src_dir}/cam_qguide.h ${phd_src_dir}/cam_qhy.cpp diff --git a/WinLibs/PlayerOneCamera.dll b/WinLibs/PlayerOneCamera.dll new file mode 100644 index 00000000..30155e96 Binary files /dev/null and b/WinLibs/PlayerOneCamera.dll differ diff --git a/cameras/PlayerOneCamera.h b/cameras/PlayerOneCamera.h new file mode 100644 index 00000000..12474a6a --- /dev/null +++ b/cameras/PlayerOneCamera.h @@ -0,0 +1,784 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Player One Astronomy Co., Ltd. +** This software is the secondary software development kit (SDK) for +** the astronomy cameras made by Player One Astronomy Co., Ltd. +** Player One Astronomy Co., Ltd (hereinafter referred to as "the company") owns its copyright. +** This SDK is only used for the secondary development of our company's cameras or other equipment. +** You can use our company's products and this SDK to develop any products without any restrictions. +** This software may use third-party software or technology (including open source code and public domain code that this software may use), +** and such use complies with their open source license or has obtained legal authorization. +** If you have any questions, please contact us: support@player-one-astronomy.com +** Copyright (C) Player One Astronomy Co., Ltd. All rights reserved. +** +****************************************************************************/ + +#ifndef PLAYERONECAMERA_H +#define PLAYERONECAMERA_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +# define POACAMERA_API __declspec(dllexport) +#else +# define POACAMERA_API __attribute__((visibility("default"))) +#endif + +//-----For more information, please read our development manual-----// + +typedef enum _POABool ///< BOOL Value Definition +{ + POA_FALSE = 0, ///< false + POA_TRUE ///< true +} POABool; + +typedef enum _POABayerPattern ///< Bayer Pattern Definition +{ + POA_BAYER_RG = 0, ///< RGGB + POA_BAYER_BG, ///< BGGR + POA_BAYER_GR, ///< GRBG + POA_BAYER_GB, ///< GBRG + POA_BAYER_MONO = -1 ///< Monochrome, the mono camera with this +} POABayerPattern; + +typedef enum _POAImgFormat ///< Image Data Format Definition +{ + POA_RAW8 = 0, ///< 8bit raw data, 1 pixel 1 byte, value range[0, 255] + POA_RAW16, ///< 16bit raw data, 1 pixel 2 bytes, value range[0, 65535] + POA_RGB24, ///< RGB888 color data, 1 pixel 3 bytes, value range[0, 255] (only color camera) + POA_MONO8, ///< 8bit monochrome data, convert the Bayer Filter Array to monochrome data. 1 pixel 1 byte, value range[0, 255] (only color camera) + POA_END = -1 +} POAImgFormat; + +typedef enum _POAErrors ///< Return Error Code Definition +{ + POA_OK = 0, ///< operation successful + POA_ERROR_INVALID_INDEX, ///< invalid index, means the index is < 0 or >= the count( camera or config) + POA_ERROR_INVALID_ID, ///< invalid camera ID + POA_ERROR_INVALID_CONFIG, ///< invalid POAConfig + POA_ERROR_INVALID_ARGU, ///< invalid argument(parameter) + POA_ERROR_NOT_OPENED, ///< camera not opened + POA_ERROR_DEVICE_NOT_FOUND, ///< camera not found, may be removed + POA_ERROR_OUT_OF_LIMIT, ///< the value out of limit + POA_ERROR_EXPOSURE_FAILED, ///< camera exposure failed + POA_ERROR_TIMEOUT, ///< timeout + POA_ERROR_SIZE_LESS, ///< the data buffer size is not enough + POA_ERROR_EXPOSING, ///< camera is exposing. some operation, must stop exposure first + POA_ERROR_POINTER, ///< invalid pointer, when get some value, do not pass the NULL pointer to the function + POA_ERROR_CONF_CANNOT_WRITE, ///< the POAConfig is not writable + POA_ERROR_CONF_CANNOT_READ, ///< the POAConfig is not readable + POA_ERROR_ACCESS_DENIED, ///< access denied + POA_ERROR_OPERATION_FAILED, ///< operation failed, maybe the camera is disconnected suddenly + POA_ERROR_MEMORY_FAILED ///< memory allocation failed +} POAErrors; + +typedef enum _POACameraState ///< Camera State Definition +{ + STATE_CLOSED = 0, ///< camera was closed + STATE_OPENED, ///< camera was opened, but not exposing + STATE_EXPOSING ///< camera is exposing +} POACameraState; + +typedef enum _POAValueType ///< Config Value Type Definition +{ + VAL_INT = 0, ///< integer(long) + VAL_FLOAT, ///< float(double) + VAL_BOOL ///< bool(POABool) +} POAValueType; + +typedef enum _POAConfig ///< Camera Config Definition +{ + POA_EXPOSURE = 0, ///< exposure time(unit: us), read-write, valueType == VAL_INT + POA_GAIN, ///< gain, read-write, valueType == VAL_INT + POA_HARDWARE_BIN, ///< hardware bin, read-write, valueType == VAL_BOOL + POA_TEMPERATURE, ///< camera temperature(uint: C), read-only, valueType == VAL_FLOAT + POA_WB_R, ///< red pixels coefficient of white balance, read-write, valueType == VAL_INT + POA_WB_G, ///< green pixels coefficient of white balance, read-write, valueType == VAL_INT + POA_WB_B, ///< blue pixels coefficient of white balance, read-write, valueType == VAL_INT + POA_OFFSET, ///< camera offset, read-write, valueType == VAL_INT + POA_AUTOEXPO_MAX_GAIN, ///< maximum gain when auto-adjust, read-write, valueType == VAL_INT + POA_AUTOEXPO_MAX_EXPOSURE, ///< maximum exposure when auto-adjust(uint: ms), read-write, valueType == VAL_INT + POA_AUTOEXPO_BRIGHTNESS, ///< target brightness when auto-adjust, read-write, valueType == VAL_INT + POA_GUIDE_NORTH, ///< ST4 guide north, generally,it's DEC+ on the mount, read-write, valueType == VAL_BOOL + POA_GUIDE_SOUTH, ///< ST4 guide south, generally,it's DEC- on the mount, read-write, valueType == VAL_BOOL + POA_GUIDE_EAST, ///< ST4 guide east, generally,it's RA+ on the mount, read-write, valueType == VAL_BOOL + POA_GUIDE_WEST, ///< ST4 guide west, generally,it's RA- on the mount, read-write, valueType == VAL_BOOL + POA_EGAIN, ///< e/ADU, This value will change with gain, read-only, valueType == VAL_FLOAT + POA_COOLER_POWER, ///< cooler power percentage[0-100%](only cool camera), read-only, valueType == VAL_INT + POA_TARGET_TEMP, ///< camera target temperature(uint: C), read-write, valueType == VAL_INT + POA_COOLER, ///< turn cooler(and fan) on or off, read-write, valueType == VAL_BOOL + POA_HEATER, ///< (deprecated)get state of lens heater(on or off), read-only, valueType == VAL_BOOL + POA_HEATER_POWER, ///< lens heater power percentage[0-100%], read-write, valueType == VAL_INT + POA_FAN_POWER, ///< radiator fan power percentage[0-100%], read-write, valueType == VAL_INT + POA_FLIP_NONE, ///< no flip, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write, valueType == VAL_BOOL + POA_FLIP_HORI, ///< flip the image horizontally, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write, valueType == VAL_BOOL + POA_FLIP_VERT, ///< flip the image vertically, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write, valueType == VAL_BOOL + POA_FLIP_BOTH, ///< flip the image horizontally and vertically, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write, valueType == VAL_BOOL + POA_FRAME_LIMIT, ///< Frame rate limit, the range:[0, 2000], 0 means no limit, read-write, valueType == VAL_INT + POA_HQI, ///< High Quality Image, for those without DDR camera(guide camera), if set POA_TRUE, this will reduce the waviness and stripe of the image, + ///< but frame rate may go down, note: this config has no effect on those cameras that with DDR. read-write, valueType == VAL_BOOL + POA_USB_BANDWIDTH_LIMIT, ///< USB bandwidth limit, read-write, valueType == VAL_INT + POA_PIXEL_BIN_SUM, ///< take the sum of pixels after binning, POA_TRUE is sum and POA_FLASE is average, default is POA_FLASE, read-write, valueType == VAL_BOOL + POA_MONO_BIN ///< only for color camera, when set to POA_TRUE, pixel binning will use neighbour pixels and image after binning will lose the bayer pattern, read-write, valueType == VAL_BOOL + +} POAConfig; + +typedef struct _POACameraProperties ///< Camera Properties Definition +{ + char cameraModelName[256]; ///< the camera name + char userCustomID[16]; ///< user custom name, it will be will be added after the camera name, max len 16 bytes,like:Mars-C [Juno], default is empty + int cameraID; ///< it's unique,camera can be controlled and set by the cameraID + int maxWidth; ///< max width of the camera + int maxHeight; ///< max height of the camera + int bitDepth; ///< ADC depth of CMOS sensor + POABool isColorCamera; ///< is a color camera or not + POABool isHasST4Port; ///< does the camera have ST4 port, if not, camera don't support ST4 guide + POABool isHasCooler; ///< does the camera have cooler assembly, generally, the cooled camera with cooler, window heater and fan + POABool isUSB3Speed; ///< is usb3.0 speed connection + POABayerPattern bayerPattern; ///< the bayer filter pattern of camera + double pixelSize; ///< camera pixel size(unit: um) + char SN[64]; ///< the serial number of camera,it's unique + char sensorModelName[32]; ///< the sersor model(name) of camera, eg: IMX462 + char localPath[256]; ///< the path of the camera in the computer host + int bins[8]; ///< bins supported by the camera, 1 == bin1, 2 == bin2,..., end with 0, eg:[1,2,3,4,0,0,0,0] + POAImgFormat imgFormats[8]; ///< image data format supported by the camera, end with POA_END, eg:[POA_RAW8, POA_RAW16, POA_END,...] + POABool isSupportHardBin; ///< does the camera sensor support hardware bin (since V3.3.0) + int pID; ///< camera's Product ID, note: the vID of PlayerOne is 0xA0A0 (since V3.3.0) + + char reserved[248]; ///< reserved, the size of reserved has changed from 256 to 248 since V3.3.0 +} POACameraProperties; + +typedef union _POAConfigValue ///< Config Value Definition +{ + long intValue; ///< long + double floatValue; ///< double + POABool boolValue; ///< POABool +} POAConfigValue; + +typedef struct _POAConfigAttributes ///< Camera Config Attributes Definition(every POAConfig has a POAConfigAttributes) +{ + POABool isSupportAuto; ///< is support auto? + POABool isWritable; ///< is writable? + POABool isReadable; ///< is readable? + POAConfig configID; ///< config ID, eg: POA_EXPOSURE + POAValueType valueType; ///< value type, eg: VAL_INT + POAConfigValue maxValue; ///< maximum value + POAConfigValue minValue; ///< minimum value + POAConfigValue defaultValue; ///< default value + char szConfName[64]; ///< POAConfig name, eg: POA_EXPOSURE: "Exposure", POA_TARGET_TEMP: "TargetTemp" + char szDescription[128]; ///< a brief introduction about this one POAConfig + + char reserved[64]; ///< reserved +} POAConfigAttributes; + + +/*********************************************************************************************************** + * Here is a simple process, please refer to our development manual and examples for detailed usage: + * ----POAGetCameraCount + * ----POAGetCameraProperties + * ----POAOpenCamera + * ----POAInitCamera + * ----POAGetConfigsCount + * ----POAGetConfigAttributes + * ----POASetImageSize + * POASetImageStartPos + * POASetImageFormat + * ----POAStartExposure + * while(1) //this is recommended to do in another thread + * { + * POABool pIsReady = POA_FALSE; + while(pIsReady == POA_FALSE) + { + //sleep(exposure_us /1000 / 10); //ms + POAImageReady(cameraID, &pIsReady); + } + + POAGetImageData + * } + * + * ----POAStopExposure + * ----POACloseCamera +***********************************************************************************************************/ + +/** + * @brief POAGetCameraCount: get camera count + * + * @return the counts of POA cameras connected to the computer host + */ +POACAMERA_API int POAGetCameraCount(); + + +/** + * @brief POAGetCameraProperties: get the property of the connected cameras, NO need to open the camera for this operation + * + * @param nIndex (input), the range: [0, camera count), note: index is not cameraID + * + * @param pProp (output), pointer to POACameraProperties structure, POACameraProperties structure needs to malloc memory first + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pProp is NULL pointer + * POA_ERROR_INVALID_INDEX: no camera connected or nIndex value out of boundary + */ +POACAMERA_API POAErrors POAGetCameraProperties(int nIndex, POACameraProperties *pProp); + + +/** + * @brief POAGetCameraPropertiesByID: get the property of the connected cameras by ID, it's a convenience function to get the property of the known camera ID + * + * @param nCameraID (input), get from in the POACameraProperties structure + * + * @param pProp (output), pointer to POACameraProperties structure, POACameraProperties structure needs to malloc memory first + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pProp is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + */ +POACAMERA_API POAErrors POAGetCameraPropertiesByID(int nCameraID, POACameraProperties *pProp); + + +/** + * @brief POAOpenCamera: open the camera, note: the following API functions need to open the camera first + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_DEVICE_NOT_FOUND: camera may be removed + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POAOpenCamera(int nCameraID); + + +/** + * @brief POAInitCamera: initialize the camera's hardware, parameters, and malloc memory + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_MEMORY_FAILED: malloc memory failed, may be the available memory is insufficient. + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POAInitCamera(int nCameraID); + + +/** + * @brief POACloseCamera: close the camera and free allocated memory + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + */ +POACAMERA_API POAErrors POACloseCamera(int nCameraID); + + +/** + * @brief POAGetConfigsCount: get the count of POAConfig available for this camera + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pConfCount (output), pointer to an int to save the count + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pConfCount is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAGetConfigsCount(int nCameraID, int *pConfCount); + + +/** + * @brief POAGetConfigAttributes: get POAConfig attribute by index + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param nConfIndex (intput), the range: [0, configs count) + * + * @param pConfAttr (output), pointer to POAConfigAttributes structure, POAConfigAttributes structure needs to malloc memory first + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pConfAttr is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_INDEX: no camera connected or nConfIndex value out of boundary + */ +POACAMERA_API POAErrors POAGetConfigAttributes(int nCameraID, int nConfIndex, POAConfigAttributes *pConfAttr); + + +/** + * @brief POAGetConfigAttributesByConfigID: get POAConfig attribute by POAConfig ID, it's a convenience function to get the attribute of the known POAConfig ID + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param confID (input), POAConfig ID, eg: POA_EXPOSURE, POA_USB_BANDWIDTH + * + * @param pConfAttr (output), pointer to POAConfigAttributes structure, POAConfigAttributes structure needs to malloc memory first + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pConfAttr is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_CONFIG: the confID is not a POAConfig or this camera don't support this POAConfig + */ +POACAMERA_API POAErrors POAGetConfigAttributesByConfigID(int nCameraID, POAConfig confID, POAConfigAttributes *pConfAttr); + + +/** + * @brief POASetConfig: set POAConfig value and auto value + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param confID (input), POAConfig ID, eg: POA_EXPOSURE, POA_USB_BANDWIDTH + * + * @param confValue (input), the confValue set to the POAConfig, eg: POAConfigValue confValue; confValue.intValue = 1000 + * + * @param isAuto (input), set the POAConfig auto + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_CONFIG: the confID is not a POAConfig or this camera don't support this POAConfig + * POA_ERROR_CONF_CANNOT_WRITE: this POAConfig does not support writing + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POASetConfig(int nCameraID, POAConfig confID, POAConfigValue confValue, POABool isAuto); + + +/** + * @brief POAGetConfig: get the POAConfig value and auto value + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param confID (input), POAConfig ID, eg: POA_EXPOSURE, POA_USB_BANDWIDTH + * + * @param pConfValue (output), pointer to a POAConfigValue for saving the value get from POAConfig + * + * @param pIsAuto (output), pointer to a POABool for saving the auto value get from POAConfig + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pConfValue or pIsAuto is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_CONFIG: the confID is not a POAConfig or this camera don't support this POAConfig + * POA_ERROR_CONF_CANNOT_READ: this POAConfig does not support reading + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POAGetConfig(int nCameraID, POAConfig confID, POAConfigValue *pConfValue, POABool *pIsAuto); + + +/** + * @brief POAGetConfigValueType: get POAConfig value type + * + * @param confID (input), POAConfig ID, eg: POA_EXPOSURE, POA_USB_BANDWIDTH + * + * @param pConfValueType (output), pointer to a POAValueType value, like: VAL_INT + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pConfValueType is NULL pointer + * POA_ERROR_INVALID_CONFIG: the confID is not a POAConfig or this camera don't support this POAConfig + */ +POACAMERA_API POAErrors POAGetConfigValueType(POAConfig confID, POAValueType *pConfValueType); + + +/** + * @brief POAGetImageStartPos: get the start position of the ROI area. + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pStartX (output), pointer to a int value for saving startX + * + * @param pStartY (output), pointer to a int value for saving startY + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pStartX or pStartY is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAGetImageStartPos(int nCameraID, int *pStartX, int *pStartY); + + +/** + * @brief POASetImageStartPos: set the start position of the ROI area. + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param startX (input), the starting point X of the ROI + * + * @param startY (input), the starting point Y of the ROI + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_ARGU: startX or startY < 0 + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POASetImageStartPos(int nCameraID, int startX, int startY); + + +/** + * @brief POAGetImageSize: get the image size of the ROI area + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pWidth (output), pointer to a int value for saving width + * + * @param pHeight (output), pointer to a int value for saving height + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pWidth or pHeight is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAGetImageSize(int nCameraID, int *pWidth, int *pHeight); + + +/** + * @brief POASetImageSize: set the image size of the ROI area, note: should stop exposure first if exposing + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param width (input), the width of ROI area, the width must divide 4 and no remainder, means: width % 4 == 0 + * note: If you set the width % 4 != 0, this function will automatically adjust the width, + * please call POAGetImageSize to get the width after adjusting + * + * @param height (input), the height of ROI area, the height must divide 2 and no remainder, means: height % 2 == 0 + * note: If you set the height % 2 != 0, this function will automatically adjust the height, + * please call POAGetImageSize to get the height after adjusting + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_ARGU: width or height < 0 + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POASetImageSize(int nCameraID, int width, int height); + + +/** + * @brief POAGetImageBin: get the pixel bin method + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pBin (output), pointer to a int value for saving bin + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pBin is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAGetImageBin(int nCameraID, int *pBin); + + +/** + * @brief POASetImageBin: set the pixel bin method, note: should stop exposure first if exposing, + * If return successful, the image size (width & height) and start position will be changed, + * Please call POAGetImageStartPos and POAGetImageSize to get the new image size and start position after binning + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param bin (input), binning method, eg: 1, 2, .... + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POASetImageBin(int nCameraID, int bin); + + +/** + * @brief POAGetImageFormat: get image format + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pImgFormat (output), pointer to a POAImgFormat value for saving image foramt, eg: POA_RAW16 + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pImgFormat is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAGetImageFormat(int nCameraID, POAImgFormat *pImgFormat); + + +/** + * @brief POASetImageFormat: set image format, note: should stop exposure first if exposing + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param imgFormat (input), image (pixels) format, eg: POA_RAW8, POA_RGB24 + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_ARGU: the imgFormat is not a POAImgFormat + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POASetImageFormat(int nCameraID, POAImgFormat imgFormat); + + +/** + * @brief POAStartExposure: start camera exposure + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param bSingleFrame (input), POA_TRUE: SnapMode, after the exposure, will not continue(Single Shot), POA_FALSE: VideoMode, continuous exposure + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_OPERATION_FAILED: operation failed + * POA_ERROR_EXPOSING: camera is exposing + */ +POACAMERA_API POAErrors POAStartExposure(int nCameraID, POABool bSingleFrame); + + +/** + * @brief POAStopExposure: stop camera exposure + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POAStopExposure(int nCameraID); + + +/** + * @brief POAGetCameraState get the camera current state + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pCameraState (output), pointer to a POACameraState value for saving camera state + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pCameraState is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + */ +POACAMERA_API POAErrors POAGetCameraState(int nCameraID, POACameraState *pCameraState); + + +/** + * @brief POAImageReady: the image data is available? if pIsReady is true, you can call POAGetImageData to get image data + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pIsReady (output), pointer to a POABool value for saving the image data is ready + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pIsReady is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAImageReady(int nCameraID, POABool *pIsReady); + + +/** + * @brief POAGetImageData: get image data after exposure, this function will block and waiting for timeout + * Note: recommended to use POAImageReady function for waiting, if image data 'Is Ready', calling this function will return immediately + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pBuf (output), pointer to data buffer, note: the buffer need to be mallloced first, and make sure it's big enough + * + * @param nBufSize (input), the buffer size, POA_RAW8: width*height, POA_RAW16: width*height*2, POA_RGB24: width*height*3 + * + * @param nTimeoutms (input), wait time (ms), recommend set it to exposure+500ms, -1 means infinite waiting + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pBuf is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_INVALID_ARGU: may be lBufSize < 0 + * POA_ERROR_SIZE_LESS: the nBufSize is not enough to hold the data + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POAGetImageData(int nCameraID, unsigned char *pBuf, long lBufSize, int nTimeoutms); + + +/** + * @brief POAGetDroppedImagesCount: get the dropped image count, reset it to 0 after stop capture + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pDroppedCount (output), pointer to a int value for saving the dropped image count + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pDroppedCount is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAGetDroppedImagesCount(int nCameraID, int *pDroppedCount); + + +/** + * @brief POAGetSensorModeCount: get the number of sensor mode + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pModeCount (output), pointer to a int value for saving the sensor mode count, NOTE: 0 means camera don't supported mode selection + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pModeCount is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + */ +POACAMERA_API POAErrors POAGetSensorModeCount(int nCameraID, int *pModeCount); + + +typedef struct _POASensorModeInfo ///< Sensor mode information +{ + char name[64]; ///< sensor mode name, can be used to display on the UI (eg: Combobox) + char desc[128]; ///< sensor mode description, may be useful for tooltip +}POASensorModeInfo; + + +/** + * @brief POAGetSensorModeInfo: get the camera sensor mode information according to the index + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param modeIndex (input), the range: [0, mode count) + * + * @param pSenModeInfo (output), pointer to a POASensorModeInfo value for saving the sensor mode information + * + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pSenModeInfo is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_ACCESS_DENIED: camera don't supported mode selection + * POA_ERROR_INVALID_ARGU: modeIndex is out of range + */ +POACAMERA_API POAErrors POAGetSensorModeInfo(int nCameraID, int modeIndex, POASensorModeInfo *pSenModeInfo); + + +/** + * @brief POASetSensorMode: set the camera sensor mode by the index, Note: should stop exposure first if exposing + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param modeIndex (input), the range: [0, mode count) + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_ACCESS_DENIED: camera don't supported mode selection + * POA_ERROR_INVALID_ARGU: modeIndex is out of range + * POA_ERROR_OPERATION_FAILED: operation failed, maybe the camera is disconnected suddenly + */ +POACAMERA_API POAErrors POASetSensorMode(int nCameraID, int modeIndex); + + +/** + * @brief POAGetSensorMode: get camera the current sensor mode + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * @param pModeIndex (output), pointer to a int value for saving the current sensor mode index + * @return POA_OK: operation successful + * POA_ERROR_POINTER: pModeIndex is NULL pointer + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_ACCESS_DENIED: camera don't supported mode selection + * POA_ERROR_OPERATION_FAILED: operation failed, the current mode is not matched + */ +POACAMERA_API POAErrors POAGetSensorMode(int nCameraID, int *pModeIndex); + + +/** + * @brief POASetUserCustomID: set user custom ID into camera flash, if set successfully, reacquire the information of this camera to get the custom ID + * Note: this operation will interrupt the exposure, if start a Signal Frame exposure , the exposure progress will be terminated. + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pCustomID (input), pointer to a string,like: const char* pCustomID = "MyCamera",if pCustomID is NULL, the previous Settings will be cleared + * + * @param len (input), max len is 16, if len > 16, the extra part will be cut off, if len is 0, the previous Settings will be cleared + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + * POA_ERROR_NOT_OPENED: camera not opened + * POA_ERROR_EXPOSING:this operation is not allowed while the camera is exposing + * POA_ERROR_OPERATION_FAILED: operation failed + */ +POACAMERA_API POAErrors POASetUserCustomID(int nCameraID, const char* pCustomID, int len); + + +/** + * @brief POAGetGainOffset: get some preset values, Note: deprecated, please use the following function + * + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * + * @param pOffsetHighestDR (output), offset at highest dynamic range + * + * @param pOffsetUnityGain (output), offset at unity gain + * + * @param pGainLowestRN (output), gain at lowest read noise + * + * @param pOffsetLowestRN (output), offset at lowest read noise + * + * @param pHCGain (output), gain at HCG Mode(High Conversion Gain) + * + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + */ +POACAMERA_API POAErrors POAGetGainOffset(int nCameraID, int *pOffsetHighestDR, int *pOffsetUnityGain, int *pGainLowestRN, int *pOffsetLowestRN, int *pHCGain); + +/** + * @brief POAGetGainsAndOffsets: get some preset values of gain and offset + * @param nCameraID (input), get from in the POACameraProperties structure, use POAGetCameraProperties function + * @param pGainHighestDR (output), gain at highest dynamic range, in most cases, this gain is 0 + * @param pHCGain (output), gain at HCG Mode(High Conversion Gain) + * @param pUnityGain (output), unity gain(or standard gain), with this gain, eGain(e/ADU) will be 1 + * @param pGainLowestRN (output), aka Maximum Analog Gain, gain at lowest read noise + * @param pOffsetHighestDR (output), offset at highest dynamic range + * @param pOffsetHCGain (output), offset at HCG Mode + * @param pOffsetUnityGain (output), offset at unity gain + * @param pOffsetLowestRN (output), offset at lowest read noise + * @return POA_OK: operation successful + * POA_ERROR_INVALID_ID: no camera with this ID was found or the ID is out of boundary + */ +POACAMERA_API POAErrors POAGetGainsAndOffsets(int nCameraID, int *pGainHighestDR, int *pHCGain, int *pUnityGain, int *pGainLowestRN, + int *pOffsetHighestDR, int *pOffsetHCGain, int *pOffsetUnityGain, int *pOffsetLowestRN); + + +/** + * @brief POAGetErrorString: convert POAErrors enum to char *, it is convenient to print or display errors + * + * @param err (intput), POAErrors, the value returned by the API function + * + * @return point to const char* error + */ +POACAMERA_API const char* POAGetErrorString(POAErrors err); + + +/** + * @brief POAGetAPIVersion: get the API version + * + * @return: it's a integer value, easy to do version comparison, eg: 20200202 + */ +POACAMERA_API int POAGetAPIVersion(); + + +/** + * @brief POAGetSDKVersion: get the sdk version + * + * @return point to const char* version(major.minor.patch), eg: 1.0.1 + */ +POACAMERA_API const char* POAGetSDKVersion(); + + +/**this function for matlab**/ +POACAMERA_API POAErrors POASetConfig_M(int nCameraID, POAConfig confID, double cfgVal, POABool isAuto); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cameras/PlayerOneCamera.lib b/cameras/PlayerOneCamera.lib new file mode 100644 index 00000000..44609d49 Binary files /dev/null and b/cameras/PlayerOneCamera.lib differ diff --git a/cameras/poalibs/linux/arm32/libPlayerOneCamera.so b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3 b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3.7 b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3.7 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3.7 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3.7.1 b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3.7.1 new file mode 100644 index 00000000..067d30ee Binary files /dev/null and b/cameras/poalibs/linux/arm32/libPlayerOneCamera.so.3.7.1 differ diff --git a/cameras/poalibs/linux/arm64/libPlayerOneCamera.so b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3 b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3.7 b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3.7 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3.7 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3.7.1 b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3.7.1 new file mode 100644 index 00000000..d6c30b19 Binary files /dev/null and b/cameras/poalibs/linux/arm64/libPlayerOneCamera.so.3.7.1 differ diff --git a/cameras/poalibs/linux/x64/libPlayerOneCamera.so b/cameras/poalibs/linux/x64/libPlayerOneCamera.so new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/x64/libPlayerOneCamera.so @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3 b/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3.7 b/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3.7 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3.7 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3.7.1 b/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3.7.1 new file mode 100644 index 00000000..0e53eb9f Binary files /dev/null and b/cameras/poalibs/linux/x64/libPlayerOneCamera.so.3.7.1 differ diff --git a/cameras/poalibs/linux/x86/libPlayerOneCamera.so b/cameras/poalibs/linux/x86/libPlayerOneCamera.so new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/x86/libPlayerOneCamera.so @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3 b/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3.7 b/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3.7 new file mode 120000 index 00000000..1b380d6d --- /dev/null +++ b/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3.7 @@ -0,0 +1 @@ +libPlayerOneCamera.so.3.7.1 \ No newline at end of file diff --git a/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3.7.1 b/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3.7.1 new file mode 100644 index 00000000..0d13eb9a Binary files /dev/null and b/cameras/poalibs/linux/x86/libPlayerOneCamera.so.3.7.1 differ diff --git a/cameras/poalibs/mac/libPlayerOneCamera.3.7.1.dylib b/cameras/poalibs/mac/libPlayerOneCamera.3.7.1.dylib new file mode 100644 index 00000000..9bb6812c Binary files /dev/null and b/cameras/poalibs/mac/libPlayerOneCamera.3.7.1.dylib differ diff --git a/cameras/poalibs/mac/libPlayerOneCamera.3.7.dylib b/cameras/poalibs/mac/libPlayerOneCamera.3.7.dylib new file mode 120000 index 00000000..c9376696 --- /dev/null +++ b/cameras/poalibs/mac/libPlayerOneCamera.3.7.dylib @@ -0,0 +1 @@ +libPlayerOneCamera.3.7.1.dylib \ No newline at end of file diff --git a/cameras/poalibs/mac/libPlayerOneCamera.3.dylib b/cameras/poalibs/mac/libPlayerOneCamera.3.dylib new file mode 120000 index 00000000..c9376696 --- /dev/null +++ b/cameras/poalibs/mac/libPlayerOneCamera.3.dylib @@ -0,0 +1 @@ +libPlayerOneCamera.3.7.1.dylib \ No newline at end of file diff --git a/cameras/poalibs/mac/libPlayerOneCamera.dylib b/cameras/poalibs/mac/libPlayerOneCamera.dylib new file mode 120000 index 00000000..c9376696 --- /dev/null +++ b/cameras/poalibs/mac/libPlayerOneCamera.dylib @@ -0,0 +1 @@ +libPlayerOneCamera.3.7.1.dylib \ No newline at end of file diff --git a/src/cam_poa.cpp b/src/cam_poa.cpp new file mode 100644 index 00000000..85e34bcd --- /dev/null +++ b/src/cam_poa.cpp @@ -0,0 +1,1160 @@ +/* +* cam_poa.cpp +* PHD Guiding +* +* Created by Ethan Chappel based on cam_zwo.cpp +* Copyright (c) 2024 PHD2 Developers +* All rights reserved. +* +* This source code is distributed under the following "BSD" license +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* Neither the name of openphdguiding.org nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include "phd.h" + +#ifdef POA_CAMERA + +#include "cam_poa.h" +#include "cameras/PlayerOneCamera.h" + +#ifdef __WINDOWS__ + +#ifdef OS_WINDOWS +// troubleshooting with the libusb definitions +# undef OS_WINDOWS +#endif + +# include +# include +#endif + +enum CaptureMode +{ + CM_SNAP, + CM_VIDEO, +}; + +class CameraPOA : public GuideCamera +{ + wxRect m_maxSize; + wxRect m_frame; + unsigned short m_prevBinning; + void *m_buffer; + size_t m_buffer_size; + wxByte m_bpp; // bits per pixel: 8 or 16 + CaptureMode m_mode; + bool m_capturing; + int m_cameraId; + int m_minGain; + int m_maxGain; + int m_defaultGainPct; + bool m_isColor; + double m_devicePixelSize; + +public: + CameraPOA(); + ~CameraPOA(); + + bool CanSelectCamera() const override { return true; } + bool EnumCameras(wxArrayString& names, wxArrayString& ids) override; + bool Capture(int duration, usImage& img, int options, const wxRect& subframe) override; + bool Connect(const wxString& camId) override; + bool Disconnect() override; + + bool ST4PulseGuideScope(int direction, int duration) override; + void ClearGuidePort(); + + void ShowPropertyDialog() override; + bool HasNonGuiCapture() override { return true; } + bool ST4HasNonGuiMove() override { return true; } + wxByte BitsPerPixel() override; + bool GetDevicePixelSize(double *devPixelSize) override; + int GetDefaultCameraGain() override; + bool SetCoolerOn(bool on) override; + bool SetCoolerSetpoint(double temperature) override; + bool GetCoolerStatus(bool *on, double *setpoint, double *power, double *temperature) override; + bool GetSensorTemperature(double *temperature) override; + +private: + void StopCapture(); + bool StopExposure(); + + wxSize BinnedFrameSize(unsigned int binning); + + POAErrors GetConfig(int nCameraID, POAConfig confID, long *pValue, POABool *pIsAuto); + POAErrors GetConfig(int nCameraID, POAConfig confID, double *pValue, POABool *pIsAuto); + POAErrors GetConfig(int nCameraID, POAConfig confID, POABool *pIsEnable); + POAErrors SetConfig(int nCameraID, POAConfig confID, long nValue, POABool isAuto); + POAErrors SetConfig(int nCameraID, POAConfig confID, double fValue, POABool isAuto); + POAErrors SetConfig(int nCameraID, POAConfig confID, POABool isEnable); +}; + +CameraPOA::CameraPOA() : m_buffer(nullptr) +{ + Name = _T("Player One Camera"); + PropertyDialogType = PROPDLG_WHEN_DISCONNECTED; + Connected = false; + m_hasGuideOutput = true; + HasSubframes = true; + HasGainControl = true; // workaround: ok to set to false later, but brain dialog will crash if we start false then change to true later when the camera is connected + m_defaultGainPct = GuideCamera::GetDefaultCameraGain(); + int value = pConfig->Profile.GetInt("/camera/POA/bpp", 8); + m_bpp = value == 8 ? 8 : 16; +} + +CameraPOA::~CameraPOA() +{ + ::free(m_buffer); +} + +wxByte CameraPOA::BitsPerPixel() +{ + return m_bpp; +} + +inline wxSize CameraPOA::BinnedFrameSize(unsigned int binning) +{ + // Player One cameras require width % 4 == 0 and height % 2 == 0 + return wxSize( + (m_maxSize.x / binning) & ~(4U - 1), + (m_maxSize.y / binning) & ~(2U - 1)); +} + +struct POACameraDlg : public wxDialog +{ + wxRadioButton *m_bpp8; + wxRadioButton *m_bpp16; + POACameraDlg(); +}; + +POACameraDlg::POACameraDlg() + : wxDialog(wxGetApp().GetTopWindow(), wxID_ANY, _("Player One Camera Properties")) +{ + SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer *bSizer12 = new wxBoxSizer(wxVERTICAL); + wxStaticBoxSizer *sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Camera Mode")), wxHORIZONTAL); + + m_bpp8 = new wxRadioButton(this, wxID_ANY, _("8-bit")); + m_bpp16 = new wxRadioButton(this, wxID_ANY, _("16-bit")); + sbSizer3->Add(m_bpp8, 0, wxALL, 5); + sbSizer3->Add(m_bpp16, 0, wxALL, 5); + bSizer12->Add(sbSizer3, 1, wxEXPAND, 5); + + wxStdDialogButtonSizer *sdbSizer2 = new wxStdDialogButtonSizer(); + wxButton *sdbSizer2OK = new wxButton(this, wxID_OK); + wxButton* sdbSizer2Cancel = new wxButton(this, wxID_CANCEL); + sdbSizer2->AddButton(sdbSizer2OK); + sdbSizer2->AddButton(sdbSizer2Cancel); + sdbSizer2->Realize(); + bSizer12->Add(sdbSizer2, 0, wxALL | wxEXPAND, 5); + + SetSizer(bSizer12); + Layout(); + Fit(); + + Centre(wxBOTH); +} + +void CameraPOA::ShowPropertyDialog() +{ + POACameraDlg dlg; + int value = pConfig->Profile.GetInt("/camera/POA/bpp", m_bpp); + if (value == 8) + dlg.m_bpp8->SetValue(true); + else + dlg.m_bpp16->SetValue(true); + if (dlg.ShowModal() == wxID_OK) + { + m_bpp = dlg.m_bpp8->GetValue() ? 8 : 16; + pConfig->Profile.SetInt("/camera/POA/bpp", m_bpp); + } +} + +inline static int cam_gain(int minval, int maxval, int pct) +{ + return minval + pct * (maxval - minval) / 100; +} + +inline static int gain_pct(int minval, int maxval, int val) +{ + return (val - minval) * 100 / (maxval - minval); +} + +#ifdef __WINDOWS__ + +#if !defined(FACILITY_VISUALCPP) +# define FACILITY_VISUALCPP ((LONG)0x6d) +#endif +#ifndef VcppException +# define VcppException(sev,err) ((sev) | ((FACILITY_VISUALCPP)<<16) | (err)) +#endif + +static LONG WINAPI DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS pExcPointers, wxString *err) +{ + LONG lDisposition = EXCEPTION_EXECUTE_HANDLER; + PDelayLoadInfo pdli = PDelayLoadInfo(pExcPointers->ExceptionRecord->ExceptionInformation[0]); + + switch (pExcPointers->ExceptionRecord->ExceptionCode) + { + case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND): { + *err = wxString::Format(_("Could not load DLL %s"), pdli->szDll); + break; + } + + case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND): + if (pdli->dlp.fImportByName) + *err = wxString::Format("Function %s was not found in %s", pdli->dlp.szProcName, pdli->szDll); + else + *err = wxString::Format("Function ordinal %d was not found in %s", pdli->dlp.dwOrdinal, pdli->szDll); + break; + + default: + // Exception is not related to delay loading + lDisposition = EXCEPTION_CONTINUE_SEARCH; + break; + } + + return lDisposition; +} + +static bool DoTryLoadDll(wxString *err) +{ + __try { + POAGetCameraCount(); + return true; + } + __except (DelayLoadDllExceptionFilter(GetExceptionInformation(), err)) { + return false; + } +} + +#else // __WINDOWS__ + +static bool DoTryLoadDll(wxString *err) +{ + return true; +} + +#endif // __WINDOWS__ + +static bool TryLoadDll(wxString *err) +{ + if (!DoTryLoadDll(err)) + return false; + + static bool s_logged; + if (!s_logged) + { + const char *ver = POAGetSDKVersion(); + Debug.Write(wxString::Format("Player One: SDK Version = [%s]\n", ver)); + s_logged = true; + } + + return true; +} + +bool CameraPOA::EnumCameras(wxArrayString& names, wxArrayString& ids) +{ + wxString err; + if (!TryLoadDll(&err)) + { + wxMessageBox(err, _("Error"), wxOK | wxICON_ERROR); + return true; + } + + // Find available cameras + int numCameras = POAGetCameraCount(); + + for (int i = 0; i < numCameras; i++) + { + POACameraProperties info; + if (POAGetCameraProperties(i, &info) == POA_OK) + { + if (numCameras > 1) + names.Add(wxString::Format("%d: %s", i + 1, info.cameraModelName)); + else + names.Add(info.cameraModelName); + ids.Add(wxString::Format("%d,%s", info.cameraID, info.cameraModelName)); + } + } + + return false; +} + +static int FindCamera(const wxString& camId, wxString *err) +{ + int numCameras = POAGetCameraCount(); + + Debug.Write(wxString::Format("POA: find camera id: [%s], ncams = %d\n", camId, numCameras)); + + if (numCameras <= 0) + { + *err = _("No Player One cameras detected."); + return -1; + } + + if (camId == GuideCamera::DEFAULT_CAMERA_ID) + { + // no model or index specified, connect to the first camera + return 0; + } + + long idx = -1; + wxString model; + + // camId is of the form + // (older PHD2 versions) + // or + // , + int pos = 0; + while (pos < camId.length() && camId[pos] >= '0' && camId[pos] <= '9') + ++pos; + camId.substr(0, pos).ToLong(&idx); + if (pos < camId.length() && camId[pos] == ',') + ++pos; + model = camId.substr(pos); + + if (model.empty()) + { + // we have an index, but no model specified + if (idx < 0 || idx >= numCameras) + { + Debug.Write(wxString::Format("Player One: invalid camera id: '%s', ncams = %d\n", camId, numCameras)); + *err = wxString::Format(_("Player One camera #%d not found"), idx + 1); + return -1; + } + return idx; + } + + // we have a model and an index. Does the camera at that index match the model name? + if (idx >= 0 && idx < numCameras) + { + POACameraProperties info; + if (POAGetCameraProperties(idx, &info) == POA_OK) + { + wxString name(info.cameraModelName); + if (name == model) + { + Debug.Write(wxString::Format("Player One: found matching camera at idx %d\n", info.cameraID)); + return info.cameraID; + } + } + } + Debug.Write(wxString::Format("Player One: no matching camera at idx %d, try to match model name ...\n", idx)); + + // find the first camera matching the model name + + for (int i = 0; i < numCameras; i++) + { + POACameraProperties info; + if (POAGetCameraProperties(i, &info) == POA_OK) + { + wxString name(info.cameraModelName); + Debug.Write(wxString::Format("Player One: cam [%d] %s\n", info.cameraID, name)); + if (name == model) + { + Debug.Write(wxString::Format("Player One: found first matching camera at idx %d\n", info.cameraID)); + return info.cameraID; + } + } + } + + Debug.Write("Player One: no matching cameras\n"); + *err = wxString::Format(_("Camera %s not found"), model); + return -1; +} + +bool CameraPOA::Connect(const wxString& camId) +{ + wxString err; + if (!TryLoadDll(&err)) + { + return CamConnectFailed(err); + } + + int selected = FindCamera(camId, &err); + if (selected == -1) + { + return CamConnectFailed(err); + } + + POAErrors r; + POACameraProperties info; + if ((r = POAGetCameraProperties(selected, &info)) != POA_OK) + { + Debug.Write(wxString::Format("POAGetCameraProperties ret %d\n", r)); + return CamConnectFailed(_("Failed to get camera properties for Player One Camera.")); + } + + if ((r = POAOpenCamera(selected)) != POA_OK) + { + Debug.Write(wxString::Format("POAOpenCamera ret %d\n", r)); + return CamConnectFailed(_("Failed to open Player One Camera.")); + } + + if ((r = POAInitCamera(selected)) != POA_OK) + { + Debug.Write(wxString::Format("POAInitCamera ret %d\n", r)); + POACloseCamera(selected); + return CamConnectFailed(_("Failed to initizlize Player One Camera.")); + } + + Debug.Write(wxString::Format("Player One: using mode BPP = %u\n", (unsigned int) m_bpp)); + + bool is_mini = wxString(info.cameraModelName).Lower().Find(_T("mini")) != wxNOT_FOUND; + bool is_usb3 = info.isUSB3Speed == POA_TRUE; + + Debug.Write(wxString::Format("Player One: usb3 = %d, is_mini = %d, name = [%s]\n", is_usb3, is_mini, info.cameraModelName)); + + if (is_usb3 || is_mini) + { + Debug.Write("Player One: selecting snap mode\n"); + m_mode = CM_SNAP; + } + else + { + Debug.Write("Player One: selecting video mode\n"); + m_mode = CM_VIDEO; + } + + m_cameraId = selected; + Connected = true; + Name = info.cameraModelName; + m_isColor = info.isColorCamera != POA_FALSE; + Debug.Write(wxString::Format("Player One: isColorCam = %d\n", m_isColor)); + + int maxBin = 1; + for (int i = 0; i <= WXSIZEOF(info.bins); i++) + { + if (!info.bins[i]) + break; + Debug.Write(wxString::Format("Player One: supported bin %d = %d\n", i, info.bins[i])); + if (info.bins[i] > maxBin) + maxBin = info.bins[i]; + } + MaxBinning = maxBin; + + if (Binning > MaxBinning) + Binning = MaxBinning; + + m_maxSize.x = info.maxWidth; + m_maxSize.y = info.maxHeight; + + FullSize = BinnedFrameSize(Binning); + m_prevBinning = Binning; + + ::free(m_buffer); + m_buffer_size = info.maxWidth * info.maxHeight * (m_bpp == 8 ? 1 : 2); + m_buffer = ::malloc(m_buffer_size); + + m_devicePixelSize = info.pixelSize; + + wxYield(); + + int numControls; + if ((r = POAGetConfigsCount(m_cameraId, &numControls)) != POA_OK) + { + Debug.Write(wxString::Format("POAGetConfigsCount ret %d\n", r)); + Disconnect(); + return CamConnectFailed(_("Failed to get camera properties for Player One Camera.")); + } + + HasGainControl = false; + HasCooler = false; + bool canSetWB_R = false; + bool canSetWB_G = false; + bool canSetWB_B = false; + + for (int i = 0; i < numControls; i++) + { + POAConfigAttributes caps; + if (POAGetConfigAttributes(m_cameraId, i, &caps) == POA_OK) + { + switch (caps.configID) + { + case POA_GAIN: + if (caps.isWritable) + { + HasGainControl = true; + m_minGain = caps.minValue.intValue; + m_maxGain = caps.maxValue.intValue; + } + break; + case POA_EXPOSURE: + break; + case POA_USB_BANDWIDTH_LIMIT: + POASetConfig(m_cameraId, POA_USB_BANDWIDTH_LIMIT, caps.minValue, POA_FALSE); + break; + case POA_HARDWARE_BIN: + // this control is not present + break; + case POA_COOLER: + if (caps.isWritable) + { + Debug.Write("Player One: camera has cooler\n"); + HasCooler = true; + } + break; + case POA_WB_B: + canSetWB_B = caps.isWritable != POA_FALSE; + break; + case POA_WB_G: + canSetWB_G = caps.isWritable != POA_FALSE; + break; + case POA_WB_R: + canSetWB_R = caps.isWritable != POA_FALSE; + break; + default: + break; + } + } + + } + + if (HasGainControl) + { + Debug.Write(wxString::Format("Player One: gain range = %d .. %d\n", m_minGain, m_maxGain)); + int OffsetHighestDR, OffsetUnityGain, GainLowestRN, OffsetLowestRN, HCGain; + POAGetGainOffset(m_cameraId, &OffsetHighestDR, &OffsetUnityGain, &GainLowestRN, &OffsetLowestRN, &HCGain); + m_defaultGainPct = gain_pct(m_minGain, m_maxGain, GainLowestRN); + Debug.Write(wxString::Format("Player One: lowest RN gain = %d (%d%%)\n", GainLowestRN, m_defaultGainPct)); + } + + const long UNIT_BALANCE = 50; + if (canSetWB_B) + { + SetConfig(m_cameraId, POA_WB_B, UNIT_BALANCE, POA_FALSE); + Debug.Write(wxString::Format("Player One: set color balance WB_B = %d\n", UNIT_BALANCE)); + } + + if (canSetWB_R) + { + SetConfig(m_cameraId, POA_WB_R, UNIT_BALANCE, POA_FALSE); + Debug.Write(wxString::Format("Player One: set color balance WB_R = %d\n", UNIT_BALANCE)); + } + + m_frame = wxRect(FullSize); + Debug.Write(wxString::Format("Player One: frame (%d,%d)+(%d,%d)\n", m_frame.x, m_frame.y, m_frame.width, m_frame.height)); + + POASetImageBin(m_cameraId, Binning); + POASetImageStartPos(m_cameraId, m_frame.GetLeft(), m_frame.GetTop()); + POASetImageSize(m_cameraId, m_frame.GetWidth(), m_frame.GetHeight()); + POASetImageFormat(m_cameraId, m_bpp == 8 ? POA_RAW8 : POA_RAW16); + + POAStopExposure(m_cameraId); + m_capturing = false; + + return false; +} + +void CameraPOA::StopCapture() +{ + if (m_capturing) + { + Debug.Write("Player One: stopcapture\n"); + POAStopExposure(m_cameraId); + m_capturing = false; + } +} + +bool CameraPOA::StopExposure() +{ + Debug.Write("Player One: stopexposure\n"); + POAStopExposure(m_cameraId); + return true; +} + +bool CameraPOA::Disconnect() +{ + StopCapture(); + POACloseCamera(m_cameraId); + + Connected = false; + + ::free(m_buffer); + m_buffer = nullptr; + + return false; +} + +bool CameraPOA::GetDevicePixelSize(double *devPixelSize) +{ + if (!Connected) + return true; + + *devPixelSize = m_devicePixelSize; + return false; +} + +int CameraPOA::GetDefaultCameraGain() +{ + return m_defaultGainPct; +} + +bool CameraPOA::SetCoolerOn(bool on) +{ + return (SetConfig(m_cameraId, POA_COOLER, on ? (long)1 : (long)0, POA_FALSE) != POA_OK); +} + +bool CameraPOA::SetCoolerSetpoint(double temperature) +{ + return (SetConfig(m_cameraId, POA_TARGET_TEMP, temperature, POA_FALSE) != POA_OK); +} + +bool CameraPOA::GetCoolerStatus(bool *on, double *setpoint, double *power, double *temperature) +{ + POAErrors r; + long value; + POABool isAuto; + + if ((r = GetConfig(m_cameraId, POA_COOLER, &value, &isAuto)) != POA_OK) + { + Debug.Write(wxString::Format("Player One: error (%d) getting POA_COOLER\n", r)); + return true; + } + *on = value != 0; + + if ((r = GetConfig(m_cameraId, POA_TARGET_TEMP, &value, &isAuto)) != POA_OK) + { + Debug.Write(wxString::Format("Player One: error (%d) getting POA_TARGET_TEMP\n", r)); + return true; + } + *setpoint = value; + + if ((r = GetConfig(m_cameraId, POA_TEMPERATURE, &value, &isAuto)) != POA_OK) + { + Debug.Write(wxString::Format("Player One: error (%d) getting POA_TEMPERATURE\n", r)); + return true; + } + *temperature = value / 10.0; + + if ((r = GetConfig(m_cameraId, POA_COOLER_POWER, &value, &isAuto)) != POA_OK) + { + Debug.Write(wxString::Format("Player One: error (%d) getting POA_COOLER_POWER\n", r)); + return true; + } + *power = value; + + return false; +} + +bool CameraPOA::GetSensorTemperature(double *temperature) +{ + POAErrors r; + long value; + POABool isAuto; + + if ((r = GetConfig(m_cameraId, POA_TEMPERATURE, &value, &isAuto)) != POA_OK) + { + Debug.Write(wxString::Format("Player One: error (%d) getting POA_TEMPERATURE\n", r)); + return true; + } + *temperature = value / 10.0; + + return false; +} + +inline static int round_down(int v, int m) +{ + return v & ~(m - 1); +} + +inline static int round_up(int v, int m) +{ + return round_down(v + m - 1, m); +} + +static void flush_buffered_image(int cameraId, void *buf, size_t size) +{ + enum { NUM_IMAGE_BUFFERS = 2 }; // camera has 2 internal frame buffers + + // clear buffered frames if any + + for (unsigned int num_cleared = 0; num_cleared < NUM_IMAGE_BUFFERS; num_cleared++) + { + POAErrors status = POAGetImageData(cameraId, (unsigned char *) buf, size, 0); + if (status != POA_OK) + break; // no more buffered frames + + Debug.Write(wxString::Format("Player One: getimagedata clearbuf %u ret %d\n", num_cleared + 1, status)); + } +} + +bool CameraPOA::Capture(int duration, usImage& img, int options, const wxRect& subframe) +{ + bool binning_change = false; + if (Binning != m_prevBinning) + { + FullSize = BinnedFrameSize(Binning); + m_prevBinning = Binning; + binning_change = true; + } + + if (img.Init(FullSize)) + { + DisconnectWithAlert(CAPT_FAIL_MEMORY); + return true; + } + + wxRect frame; + wxPoint subframePos; // position of subframe within frame + + bool useSubframe = UseSubframes; + + if (useSubframe && (subframe.width <= 0 || subframe.height <= 0 || binning_change)) + useSubframe = false; + + if (useSubframe) + { + // ensure transfer size is a multiple of 1024 + // moving the sub-frame or resizing it is somewhat costly (stopCapture / startCapture) + + frame.SetLeft(round_down(subframe.GetLeft(), 32)); + frame.SetRight(round_up(subframe.GetRight() + 1, 32) - 1); + frame.SetTop(round_down(subframe.GetTop(), 32)); + frame.SetBottom(round_up(subframe.GetBottom() + 1, 32) - 1); + + subframePos = subframe.GetLeftTop() - frame.GetLeftTop(); + } + else + { + frame = wxRect(FullSize); + } + + long exposureUS = duration * 1000; + POABool tmp; + long cur_exp; + if (GetConfig(m_cameraId, POA_EXPOSURE, &cur_exp, &tmp) == POA_OK && + cur_exp != exposureUS) + { + Debug.Write(wxString::Format("Player One: set CONTROL_EXPOSURE %d\n", exposureUS)); + SetConfig(m_cameraId, POA_EXPOSURE, exposureUS, POA_FALSE); + } + + long new_gain = cam_gain(m_minGain, m_maxGain, GuideCameraGain); + long cur_gain; + if (GetConfig(m_cameraId, POA_GAIN, &cur_gain, &tmp) == POA_OK && + new_gain != cur_gain) + { + Debug.Write(wxString::Format("Player One: set CONTROL_GAIN %d%% %d\n", GuideCameraGain, new_gain)); + SetConfig(m_cameraId, POA_GAIN, new_gain, POA_FALSE); + } + + bool size_change = frame.GetSize() != m_frame.GetSize(); + bool pos_change = frame.GetLeftTop() != m_frame.GetLeftTop(); + + if (size_change || pos_change) + { + m_frame = frame; + Debug.Write(wxString::Format("Player One: frame (%d,%d)+(%d,%d)\n", m_frame.x, m_frame.y, m_frame.width, m_frame.height)); + } + + if (size_change || binning_change) + { + StopCapture(); + + POAErrors status = POASetImageBin(m_cameraId, Binning); + if (status != POA_OK) + Debug.Write(wxString::Format("Player One: setImageBin(%hu) => %d\n", Binning, status)); + + status = POASetImageSize(m_cameraId, frame.GetWidth(), frame.GetHeight()); + if (status != POA_OK) + Debug.Write(wxString::Format("Player One: setImageSize(%d,%d) => %d\n", frame.GetWidth(), frame.GetHeight(), status)); + } + + if (pos_change) + { + POAErrors status = POASetImageStartPos(m_cameraId, frame.GetLeft(), frame.GetTop()); + if (status != POA_OK) + Debug.Write(wxString::Format("Player One: setStartPos(%d,%d) => %d\n", frame.GetLeft(), frame.GetTop(), status)); + } + + int poll = wxMin(duration, 100); + + unsigned char *const buffer = + m_bpp == 16 && !useSubframe ? (unsigned char *) img.ImageData : (unsigned char *) m_buffer; + + if (m_mode == CM_VIDEO) + { + // the camera and/or driver will buffer frames and return the oldest frame, + // which could be quite stale. read out all buffered frames so the frame we + // get is current + + flush_buffered_image(m_cameraId, m_buffer, m_buffer_size); + + if (!m_capturing) + { + Debug.Write("Player One: startcapture\n"); + POAStartExposure(m_cameraId, POA_FALSE); + m_capturing = true; + } + + CameraWatchdog watchdog(duration, duration + GetTimeoutMs() + 10000); // total timeout is 2 * duration + 15s (typically) + + while (true) + { + POAErrors status = POAGetImageData(m_cameraId, buffer, m_buffer_size, poll); + if (status == POA_OK) + break; + if (WorkerThread::InterruptRequested()) + { + StopCapture(); + return true; + } + if (watchdog.Expired()) + { + Debug.Write(wxString::Format("Player One: getimagedata ret %d\n", status)); + StopCapture(); + DisconnectWithAlert(CAPT_FAIL_TIMEOUT); + return true; + } + } + } + else + { + // CM_SNAP + + bool frame_ready = false; + + for (int tries = 1; tries <= 3 && !frame_ready; tries++) + { + if (tries > 1) + Debug.Write("Player One: getexpstatus EXP_FAILED, retry exposure\n"); + + POAStartExposure(m_cameraId, POA_TRUE); + + CameraWatchdog watchdog(duration, duration + GetTimeoutMs() + 10000); // total timeout is 2 * duration + 15s (typically) + + if (duration > 100) + { + // wait until near end of exposure + if (WorkerThread::MilliSleep(duration - 100, WorkerThread::INT_ANY) && + (WorkerThread::TerminateRequested() || StopExposure())) + { + StopExposure(); + return true; + } + } + + while (true) + { + POABool isready = POA_FALSE; + POAErrors status; + POACameraState expstatus; + if ((status = POAGetCameraState(m_cameraId, &expstatus)) != POA_OK) + { + Debug.Write(wxString::Format("Player One: getexpstatus ret %d\n", status)); + DisconnectWithAlert(_("Lost connection to camera"), RECONNECT); + return true; + } + POAImageReady(m_cameraId, &isready); + if (isready) + { + frame_ready = true; + break; + } + else if (expstatus != STATE_EXPOSING) + { + break; // failed, retry exposure + } + // STATE_EXPOSING + wxMilliSleep(poll); + if (WorkerThread::InterruptRequested()) + { + StopExposure(); + return true; + } + if (watchdog.Expired()) + { + StopExposure(); + DisconnectWithAlert(CAPT_FAIL_TIMEOUT); + return true; + } + } + } + + if (!frame_ready) + { + Debug.Write("Player One: getexpstatus EXP_FAILED, giving up\n"); + DisconnectWithAlert(_("Lost connection to camera"), RECONNECT); + return true; + } + + POAErrors status = POAGetImageData(m_cameraId, buffer, m_buffer_size, -1); + if (status != POA_OK) + { + Debug.Write(wxString::Format("Player One: getdataafterexp ret %d\n", status)); + DisconnectWithAlert(_("Lost connection to camera"), RECONNECT); + return true; + } + } + + if (useSubframe) + { + img.Subframe = subframe; + + // Clear out the image + img.Clear(); + + if (m_bpp == 8) + { + for (int y = 0; y < subframe.height; y++) + { + const unsigned char *src = buffer + (y + subframePos.y) * frame.width + subframePos.x; + unsigned short *dst = img.ImageData + (y + subframe.y) * FullSize.GetWidth() + subframe.x; + for (int x = 0; x < subframe.width; x++) + *dst++ = *src++; + } + } + else + { + for (int y = 0; y < subframe.height; y++) + { + const unsigned short *src = (unsigned short *) buffer + (y + subframePos.y) * frame.width + subframePos.x; + unsigned short *dst = img.ImageData + (y + subframe.y) * FullSize.GetWidth() + subframe.x; + for (int x = 0; x < subframe.width; x++) + *dst++ = *src++; + } + } + } + else + { + if (m_bpp == 8) + { + for (unsigned int i = 0; i < img.NPixels; i++) + img.ImageData[i] = buffer[i]; + } + else + { + // 16-bit mode and no subframe: data is already in img.ImageData + } + } + + if (options & CAPTURE_SUBTRACT_DARK) + SubtractDark(img); + if (m_isColor && Binning == 1 && (options & CAPTURE_RECON)) + QuickLRecon(img); + + return false; +} + +inline static POAConfig GetPOADirection(int direction) +{ + switch (direction) + { + default: + case NORTH: + return POA_GUIDE_NORTH; + case EAST: + return POA_GUIDE_EAST; + case WEST: + return POA_GUIDE_WEST; + case SOUTH: + return POA_GUIDE_SOUTH; + } +} + +bool CameraPOA::ST4PulseGuideScope(int direction, int duration) +{ + POAConfig d = GetPOADirection(direction); + SetConfig(m_cameraId, d, POA_TRUE); + WorkerThread::MilliSleep(duration, WorkerThread::INT_ANY); + SetConfig(m_cameraId, d, POA_FALSE); + + return false; +} + +void CameraPOA::ClearGuidePort() +{ + SetConfig(m_cameraId, POA_GUIDE_NORTH, POA_FALSE); + SetConfig(m_cameraId, POA_GUIDE_SOUTH, POA_FALSE); + SetConfig(m_cameraId, POA_GUIDE_EAST, POA_FALSE); + SetConfig(m_cameraId, POA_GUIDE_WEST, POA_FALSE); +} + + +// Functions from Player One ConvFuncs.h + +// Get the current value of POAConfig with POAValueType is VAL_INT, eg: POA_EXPOSURE, POA_GAIN +POAErrors CameraPOA::GetConfig(int nCameraID, POAConfig confID, long *pValue, POABool *pIsAuto) +{ + POAValueType pConfValueType; + POAErrors error = POAGetConfigValueType(confID, &pConfValueType); + if (error == POA_OK) + { + if (pConfValueType != VAL_INT) + { + return POA_ERROR_INVALID_CONFIG; + } + } + else + { + return error; + } + + POAConfigValue confValue; + error = POAGetConfig(nCameraID, confID, &confValue, pIsAuto); + + if (error == POA_OK) + { + *pValue = confValue.intValue; + } + + return error; +} + +// Get the current value of POAConfig with POAValueType is VAL_FLOAT, eg: POA_TEMPERATURE, POA_EGAIN +POAErrors CameraPOA::GetConfig(int nCameraID, POAConfig confID, double *pValue, POABool *pIsAuto) +{ + POAValueType pConfValueType; + POAErrors error = POAGetConfigValueType(confID, &pConfValueType); + if (error == POA_OK) + { + if (pConfValueType != VAL_FLOAT) + { + return POA_ERROR_INVALID_CONFIG; + } + } + else + { + return error; + } + + POAConfigValue confValue; + error = POAGetConfig(nCameraID, confID, &confValue, pIsAuto); + + if (error == POA_OK) + { + *pValue = confValue.floatValue; + } + + return error; +} + +// Get the current value of POAConfig with POAValueType is VAL_BOOL, eg: POA_COOLER, POA_PIXEL_BIN_SUM +POAErrors CameraPOA::GetConfig(int nCameraID, POAConfig confID, POABool *pIsEnable) +{ + POAValueType pConfValueType; + POAErrors error = POAGetConfigValueType(confID, &pConfValueType); + if (error == POA_OK) + { + if (pConfValueType != VAL_BOOL) + { + return POA_ERROR_INVALID_CONFIG; + } + } + else + { + return error; + } + + POAConfigValue confValue; + POABool boolValue; + error = POAGetConfig(nCameraID, confID, &confValue, &boolValue); + + if (error == POA_OK) + { + *pIsEnable = confValue.boolValue; + } + + return error; +} + +// Set the POAConfig value, the POAValueType of POAConfig is VAL_INT, eg: POA_TARGET_TEMP, POA_OFFSET +POAErrors CameraPOA::SetConfig(int nCameraID, POAConfig confID, long nValue, POABool isAuto) +{ + POAValueType pConfValueType; + POAErrors error = POAGetConfigValueType(confID, &pConfValueType); + if (error == POA_OK) + { + if (pConfValueType != VAL_INT) + { + return POA_ERROR_INVALID_CONFIG; + } + } + else + { + return error; + } + + POAConfigValue confValue; + confValue.intValue = nValue; + + return POASetConfig(nCameraID, confID, confValue, isAuto); +} + +// Set the POAConfig value, the POAValueType of POAConfig is VAL_FLOAT, Note: currently, there is no POAConfig which +// POAValueType is VAL_FLOAT needs to be set +POAErrors CameraPOA::SetConfig(int nCameraID, POAConfig confID, double fValue, POABool isAuto) +{ + POAValueType pConfValueType; + POAErrors error = POAGetConfigValueType(confID, &pConfValueType); + if (error == POA_OK) + { + if (pConfValueType != VAL_FLOAT) + { + return POA_ERROR_INVALID_CONFIG; + } + } + else + { + return error; + } + + POAConfigValue confValue; + confValue.floatValue = fValue; + + return POASetConfig(nCameraID, confID, confValue, isAuto); +} + +// Set the POAConfig value, the POAValueType of POAConfig is VAL_BOOL, eg: POA_HARDWARE_BIN, POA_GUIDE_NORTH +POAErrors CameraPOA::SetConfig(int nCameraID, POAConfig confID, POABool isEnable) +{ + POAValueType pConfValueType; + POAErrors error = POAGetConfigValueType(confID, &pConfValueType); + if (error == POA_OK) + { + if (pConfValueType != VAL_BOOL) + { + return POA_ERROR_INVALID_CONFIG; + } + } + else + { + return error; + } + + POAConfigValue confValue; + confValue.boolValue = isEnable; + + return POASetConfig(nCameraID, confID, confValue, POA_FALSE); +} + +GuideCamera *POACameraFactory::MakePOACamera() +{ + return new CameraPOA(); +} + +#endif // POA_CAMERA diff --git a/src/cam_poa.h b/src/cam_poa.h new file mode 100644 index 00000000..4a4c4b23 --- /dev/null +++ b/src/cam_poa.h @@ -0,0 +1,45 @@ +/* +* cam_poa.h +* PHD Guiding +* +* Created by Ethan Chappel based on cam_zwo.h +* Copyright (c) 2024 PHD2 Developers +* All rights reserved. +* +* This source code is distributed under the following "BSD" license +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* Neither the name of openphdguiding.org nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#ifndef CAM_POA_H_INCLUDED +#define CAM_POA_H_INCLUDED + +class GuideCamera; + +class POACameraFactory +{ +public: + static GuideCamera *MakePOACamera(); +}; + +#endif diff --git a/src/camera.cpp b/src/camera.cpp index 5948df1d..db90a7da 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -90,6 +90,10 @@ wxSize UNDEFINED_FRAME_SIZE = wxSize(0, 0); # include "cam_zwo.h" #endif +#if defined(POA_CAMERA) +# include "cam_poa.h" +#endif + #if defined(TOUPTEK_CAMERA) # include "cam_touptek.h" #endif @@ -315,6 +319,9 @@ wxArrayString GuideCamera::GuideCameraList() #if defined(ZWO_ASI) CameraList.Add(_T("ZWO ASI Camera")); #endif +#if defined(POA_CAMERA) + CameraList.Add(_T("Player One Camera")); +#endif #if defined(TOUPTEK_CAMERA) CameraList.Add(_T("ToupTek Camera")); CameraList.Add(_T("Omegon Pro Camera")); @@ -461,6 +468,10 @@ GuideCamera *GuideCamera::Factory(const wxString& choice) else if (choice == _T("ZWO ASI Camera")) pReturn = ZWOCameraFactory::MakeZWOCamera(); #endif +#if defined(POA_CAMERA) + else if (choice == _T("Player One Camera")) + pReturn = POACameraFactory::MakePOACamera(); +#endif #if defined(TOUPTEK_CAMERA) else if (choice == _T("ToupTek Camera") || choice == _T("Omegon Pro Camera")) { diff --git a/src/cameras.h b/src/cameras.h index e3ee6ec4..103fcf9e 100644 --- a/src/cameras.h +++ b/src/cameras.h @@ -56,6 +56,7 @@ # define OGMA_CAMERA # define OPENCV_CAMERA # define ORION_DSCI +# define POA_CAMERA # define QGUIDE # define QHY_CAMERA # define SBIG @@ -106,6 +107,9 @@ # ifdef HAVE_ZWO_CAMERA # define ZWO_ASI # endif +# ifdef HAVE_POA_CAMERA +# define POA_CAMERA +# endif # ifdef HAVE_SVB_CAMERA # define SVB_CAMERA # endif @@ -125,6 +129,9 @@ # ifdef HAVE_ZWO_CAMERA # define ZWO_ASI # endif +# ifdef HAVE_POA_CAMERA +# define POA_CAMERA +# endif # ifdef HAVE_TOUPTEK_CAMERA # define TOUPTEK_CAMERA # endif diff --git a/thirdparty/thirdparty.cmake b/thirdparty/thirdparty.cmake index 4a7cf751..130b9a5a 100644 --- a/thirdparty/thirdparty.cmake +++ b/thirdparty/thirdparty.cmake @@ -756,6 +756,11 @@ if(WIN32) set(PHD_LINK_EXTERNAL ${PHD_LINK_EXTERNAL} ${PHD_PROJECT_ROOT_DIR}/cameras/ASICamera2.lib) set(PHD_COPY_EXTERNAL_ALL ${PHD_COPY_EXTERNAL_ALL} ${PHD_PROJECT_ROOT_DIR}/WinLibs/ASICamera2.dll) + # Player One cameras + set(PHD_LINK_EXTERNAL ${PHD_LINK_EXTERNAL} ${PHD_PROJECT_ROOT_DIR}/cameras/PlayerOneCamera.lib) + set(PHD_COPY_EXTERNAL_ALL ${PHD_COPY_EXTERNAL_ALL} ${PHD_PROJECT_ROOT_DIR}/WinLibs/PlayerOneCamera.dll) + + # ToupTek cameras set(PHD_LINK_EXTERNAL ${PHD_LINK_EXTERNAL} ${PHD_PROJECT_ROOT_DIR}/cameras/toupcam.lib) set(PHD_COPY_EXTERNAL_ALL ${PHD_COPY_EXTERNAL_ALL} ${PHD_PROJECT_ROOT_DIR}/WinLibs/toupcam.dll) @@ -890,6 +895,16 @@ if(APPLE) set(PHD_LINK_EXTERNAL ${PHD_LINK_EXTERNAL} ${asiCamera2}) set(phd2_OSX_FRAMEWORKS ${phd2_OSX_FRAMEWORKS} ${asiCamera2}) + find_library( poaCamera + NAMES PlayerOneCamera + PATHS ${PHD_PROJECT_ROOT_DIR}/cameras/poalibs/mac) + if(NOT poaCamera) + message(FATAL_ERROR "Cannot find the poaCamera drivers") + endif() + add_definitions(-DHAVE_POA_CAMERA=1) + set(PHD_LINK_EXTERNAL ${PHD_LINK_EXTERNAL} ${poaCamera}) + set(phd2_OSX_FRAMEWORKS ${phd2_OSX_FRAMEWORKS} ${poaCamera}) + find_library( SVBCameraSDK NAMES SVBCameraSDK PATHS ${PHD_PROJECT_ROOT_DIR}/cameras/svblibs/mac/x64) @@ -981,16 +996,19 @@ if(UNIX AND NOT APPLE) if (CMAKE_SYSTEM_PROCESSOR MATCHES "^armv6(.*)") set(zwoarch "armv6") set(qhyarch "arm32") + set(poaarch "arm32") set(toupcam_arch "armel") set(svbony_arch "armv6") elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^armv7(.*)|arm64|aarch64|^armv8(.*)") if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(zwoarch "armv8") set(qhyarch "arm64") + set(poaarch "arm64") set(toupcam_arch "arm64") set(svbony_arch "armv8") else() set(zwoarch "armv7") + set(poaarch "arm32") set(qhyarch "arm32") set(toupcam_arch "armhf") set(svbony_arch "armv7") @@ -998,11 +1016,13 @@ if(UNIX AND NOT APPLE) elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "x86|X86|amd64|AMD64|i.86") if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(zwoarch "x64") + set(poaarch "x64") set(qhyarch "x86_64") set(toupcam_arch "x64") set(svbony_arch "x64") else() set(zwoarch "x86") + set(poaarch "x86") set(qhyarch "x86_32") # no longer distributed by QHY set(toupcam_arch "x86") set(svbony_arch "x86") @@ -1016,6 +1036,11 @@ if(UNIX AND NOT APPLE) PATHS ${PHD_PROJECT_ROOT_DIR}/cameras ) + find_path(POA_INCLUDE_DIR PlayerOneCamera.h + NO_DEFAULT_PATHS + PATHS ${PHD_PROJECT_ROOT_DIR}/cameras + ) + # The binary libraries below do not support FreeBSD, ignore them # when building for FreeBSD. if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") @@ -1031,6 +1056,18 @@ if(UNIX AND NOT APPLE) add_definitions(-DHAVE_ZWO_CAMERA=1) set(PHD_LINK_EXTERNAL ${PHD_LINK_EXTERNAL} ${asiCamera2}) + find_library(poaCamera + NAMES PlayerOneCamera + NO_DEFAULT_PATHS + PATHS ${PHD_PROJECT_ROOT_DIR}/cameras/poalibs/linux/${poaarch}) + + if(NOT poaCamera) + message(FATAL_ERROR "Cannot find the poaCamera drivers") + endif() + message(STATUS "Found PlayerOneCamera lib ${poaCamera}") + add_definitions(-DHAVE_POA_CAMERA=1) + set(PHD_LINK_EXTERNAL ${PHD_LINK_EXTERNAL} ${poaCamera}) + find_library(toupcam NAMES toupcam NO_DEFAULT_PATHS