pyobs-qhyccd 2.0.0.dev11__tar.gz → 2.0.0.dev13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/.github/dependabot.yml +5 -0
  2. pyobs_qhyccd-2.0.0.dev13/.github/workflows/dependabot-automerge.yml +24 -0
  3. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/.github/workflows/pypi.yml +1 -1
  4. pyobs_qhyccd-2.0.0.dev13/.github/workflows/pyrefly.yml +26 -0
  5. pyobs_qhyccd-2.0.0.dev13/.github/workflows/pytest.yml +28 -0
  6. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/PKG-INFO +1 -1
  7. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/pyobs_qhyccd/gui.py +12 -4
  8. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/pyobs_qhyccd/qhyccdcamera.py +37 -17
  9. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/pyobs_qhyccd/qhyccddriver.pyx +10 -1
  10. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/pyproject.toml +7 -1
  11. pyobs_qhyccd-2.0.0.dev13/tests/test_import.py +23 -0
  12. pyobs_qhyccd-2.0.0.dev13/tests/test_qhyccdcamera.py +157 -0
  13. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/uv.lock +61 -1
  14. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/.github/workflows/ruff.yml +0 -0
  15. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/.pre-commit-config.yaml +0 -0
  16. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/.readthedocs.yml +0 -0
  17. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/CMakeLists.txt +0 -0
  18. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/README.md +0 -0
  19. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/docs/Makefile +0 -0
  20. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/docs/make.bat +0 -0
  21. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/docs/requirements.txt +0 -0
  22. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/docs/source/_static/pyobs.gif +0 -0
  23. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/docs/source/conf.py +0 -0
  24. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/docs/source/index.rst +0 -0
  25. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/distclean.sh +0 -0
  26. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/etc/udev/rules.d/85-qhyccd.rules +0 -0
  27. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/install.sh +0 -0
  28. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IC16200A.HEX +0 -0
  29. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IC16803.HEX +0 -0
  30. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IC8300.HEX +0 -0
  31. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IC90A.HEX +0 -0
  32. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IMG0H.HEX +0 -0
  33. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IMG2P.HEX +0 -0
  34. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IMG2S.HEX +0 -0
  35. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/IMG50.HEX +0 -0
  36. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/Origin678C.img +0 -0
  37. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/POLEMASTER.HEX +0 -0
  38. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY0204.img +0 -0
  39. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY09000A.HEX +0 -0
  40. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY10.HEX +0 -0
  41. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY10768.img +0 -0
  42. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY11.HEX +0 -0
  43. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY12.HEX +0 -0
  44. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY1253.img +0 -0
  45. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY128.img +0 -0
  46. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY128PRO.img +0 -0
  47. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY15.HEX +0 -0
  48. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY16.HEX +0 -0
  49. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY16000.HEX +0 -0
  50. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY160002AD.HEX +0 -0
  51. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY16200A.HEX +0 -0
  52. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY163.img +0 -0
  53. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY165.img +0 -0
  54. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY168.img +0 -0
  55. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY16803A.HEX +0 -0
  56. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY174.img +0 -0
  57. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY178.img +0 -0
  58. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY183.img +0 -0
  59. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY183A.img +0 -0
  60. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY1920.img +0 -0
  61. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY2.HEX +0 -0
  62. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY20.HEX +0 -0
  63. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY2020.img +0 -0
  64. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY21.HEX +0 -0
  65. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY22.HEX +0 -0
  66. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY224.img +0 -0
  67. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY23.HEX +0 -0
  68. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY247.img +0 -0
  69. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY268.img +0 -0
  70. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY27.HEX +0 -0
  71. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY28.HEX +0 -0
  72. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY29.HEX +0 -0
  73. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY290.img +0 -0
  74. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY294.img +0 -0
  75. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY294PRO.img +0 -0
  76. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY2E.HEX +0 -0
  77. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY2PRO.HEX +0 -0
  78. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY342.img +0 -0
  79. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY342PRO.img +0 -0
  80. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY367.img +0 -0
  81. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY367PRO.img +0 -0
  82. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY4040.img +0 -0
  83. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY4040PRO.img +0 -0
  84. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY410.img +0 -0
  85. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY411.img +0 -0
  86. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY411ERIS.img +0 -0
  87. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY411_1.3.3.img +0 -0
  88. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY42.img +0 -0
  89. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY42PRO.img +0 -0
  90. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY432.img +0 -0
  91. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY461.img +0 -0
  92. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY487.img +0 -0
  93. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5.HEX +0 -0
  94. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY530.img +0 -0
  95. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY533.img +0 -0
  96. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY550.img +0 -0
  97. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY585.img +0 -0
  98. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5II.HEX +0 -0
  99. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III174.img +0 -0
  100. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III178.img +0 -0
  101. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III185.img +0 -0
  102. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III200.img +0 -0
  103. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III224.img +0 -0
  104. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III290.img +0 -0
  105. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III334.img +0 -0
  106. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III415.img +0 -0
  107. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III462.img +0 -0
  108. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III482.img +0 -0
  109. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III485.img +0 -0
  110. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III568.img +0 -0
  111. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III585.img +0 -0
  112. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III678.img +0 -0
  113. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5III715.img +0 -0
  114. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY5LOADER.HEX +0 -0
  115. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY6.HEX +0 -0
  116. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY600-9267-1203.img +0 -0
  117. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY600.img +0 -0
  118. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY600ERIS-12-qxx.img +0 -0
  119. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY6060.img +0 -0
  120. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY6060PRO.img +0 -0
  121. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY695A.HEX +0 -0
  122. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY7.HEX +0 -0
  123. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY8.HEX +0 -0
  124. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY811.img +0 -0
  125. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY814A.HEX +0 -0
  126. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY8L.HEX +0 -0
  127. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY8M.HEX +0 -0
  128. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY8PRO.HEX +0 -0
  129. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY90A.HEX +0 -0
  130. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY9701.img +0 -0
  131. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY990.img +0 -0
  132. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY991.img +0 -0
  133. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY992.img +0 -0
  134. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/QHY9S.HEX +0 -0
  135. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/miniCam5.HEX +0 -0
  136. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/firmware/qhy/miniCam8.img +0 -0
  137. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/lib/udev/rules.d/85-qhyccd.rules +0 -0
  138. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/sbin/fxload +0 -0
  139. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/uninstall.sh +0 -0
  140. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/cmake_modules/FindOpenCV.cmake +0 -0
  141. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/cmake_modules/FindUSB-1.cmake +0 -0
  142. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/cmake_modules/FindUSB-ARM.cmake +0 -0
  143. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/doc/HowTo_QHYCCD_SDK_CrossPlatform.pdf +0 -0
  144. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/COPYING +0 -0
  145. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/Makefile +0 -0
  146. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/README.txt +0 -0
  147. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/a3load.hex +0 -0
  148. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/ezusb.c +0 -0
  149. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/ezusb.h +0 -0
  150. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/fxload +0 -0
  151. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/fxload.8 +0 -0
  152. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/fxload.spec +0 -0
  153. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/fx3load/main.c +0 -0
  154. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/include/config.h +0 -0
  155. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/include/qhyccd.h +0 -0
  156. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/include/qhyccdcamdef.h +0 -0
  157. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/include/qhyccderr.h +0 -0
  158. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/include/qhyccdstruct.h +0 -0
  159. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/lib/libqhyccd.a +0 -0
  160. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/lib/libqhyccd.so +0 -0
  161. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/lib/libqhyccd.so.20 +0 -0
  162. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/lib/libqhyccd.so.25.9.29.10 +0 -0
  163. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/riffa_linux_driver/Makefile +0 -0
  164. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/riffa_linux_driver/README.txt +0 -0
  165. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/riffa_linux_driver/circ_queue.c +0 -0
  166. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/riffa_linux_driver/circ_queue.h +0 -0
  167. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/riffa_linux_driver/riffa_driver.c +0 -0
  168. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/riffa_linux_driver/riffa_driver.h +0 -0
  169. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/DownloadFirmware/.svn/entries +0 -0
  170. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/DownloadFirmware/.svn/format +0 -0
  171. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/DownloadFirmware/.svn/text-base/Makefile.svn-base +0 -0
  172. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/DownloadFirmware/.svn/text-base/downfirmware.cpp.svn-base +0 -0
  173. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/DownloadFirmware/Makefile +0 -0
  174. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/DownloadFirmware/downfirmware.cpp +0 -0
  175. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/.svn/entries +0 -0
  176. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/.svn/format +0 -0
  177. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/.svn/text-base/LiveFrameSample.cpp.svn-base +0 -0
  178. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/.svn/text-base/Makefile.svn-base +0 -0
  179. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/LiveFrameSample +0 -0
  180. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/LiveFrameSample.cpp +0 -0
  181. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/LiveFrameSample.o +0 -0
  182. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/Makefile +0 -0
  183. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode/qhyccd.ini +0 -0
  184. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode_PCIE_Raw_Riffa/LiveFrameSample.cpp +0 -0
  185. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode_PCIE_Raw_Riffa/Makefile +0 -0
  186. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/LiveFrameMode_PCIE_Raw_Riffa/qhyccd.ini +0 -0
  187. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/.svn/entries +0 -0
  188. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/.svn/format +0 -0
  189. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/.svn/text-base/Makefile.svn-base +0 -0
  190. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/.svn/text-base/SingleFrameMode.cpp.svn-base +0 -0
  191. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/Makefile +0 -0
  192. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/SingleFrameMode +0 -0
  193. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/SingleFrameMode.cpp +0 -0
  194. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/SingleFrameMode.o +0 -0
  195. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/SingleFrameMode/qhyccd.ini +0 -0
  196. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/cmake/FindLibQHYCCD.cmake +0 -0
  197. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_base/CMakeLists.txt +0 -0
  198. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_base/main.cpp +0 -0
  199. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_cfw/CMakeLists.txt +0 -0
  200. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_cfw/build/qhyccd.ini +0 -0
  201. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_cfw/main.cpp +0 -0
  202. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/CMakeLists.txt +0 -0
  203. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/CMakeCommon.cmake +0 -0
  204. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/CMakeParseArguments.cmake +0 -0
  205. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindCFITSIO.cmake +0 -0
  206. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindDC1394.cmake +0 -0
  207. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindFFTW3.cmake +0 -0
  208. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindGMock.cmake +0 -0
  209. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindGSL.cmake +0 -0
  210. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindINDI.cmake +0 -0
  211. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindJPEG.cmake +0 -0
  212. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindLibXISF.cmake +0 -0
  213. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindLibev.cmake +0 -0
  214. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindNova.cmake +0 -0
  215. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindOggTheora.cmake +0 -0
  216. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindOpenAL.cmake +0 -0
  217. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindPackageHandleStandardArgs.cmake +0 -0
  218. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindPackageMessage.cmake +0 -0
  219. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindRTLSDR.cmake +0 -0
  220. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindUSB1.cmake +0 -0
  221. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/FindVorbis.cmake +0 -0
  222. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/cmake_modules/UnityBuild.cmake +0 -0
  223. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/main.cpp +0 -0
  224. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_client/main.h +0 -0
  225. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/CMakeLists.txt +0 -0
  226. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/CMakeCommon.cmake +0 -0
  227. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/CMakeParseArguments.cmake +0 -0
  228. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindCFITSIO.cmake +0 -0
  229. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindDC1394.cmake +0 -0
  230. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindFFTW3.cmake +0 -0
  231. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindGMock.cmake +0 -0
  232. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindGSL.cmake +0 -0
  233. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindINDI.cmake +0 -0
  234. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindJPEG.cmake +0 -0
  235. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindLibXISF.cmake +0 -0
  236. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindLibev.cmake +0 -0
  237. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindNova.cmake +0 -0
  238. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindOggTheora.cmake +0 -0
  239. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindOpenAL.cmake +0 -0
  240. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindPackageHandleStandardArgs.cmake +0 -0
  241. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindPackageMessage.cmake +0 -0
  242. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindRTLSDR.cmake +0 -0
  243. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindUSB1.cmake +0 -0
  244. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/FindVorbis.cmake +0 -0
  245. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/cmake_modules/UnityBuild.cmake +0 -0
  246. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/main.cpp +0 -0
  247. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_indi_focus/main.h +0 -0
  248. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/CMakeLists.txt +0 -0
  249. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/FindLibQHYCCD.cmake +0 -0
  250. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/README.md +0 -0
  251. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/CyAPI.h +0 -0
  252. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/CyUSB30_def.h +0 -0
  253. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/config.h +0 -0
  254. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/dllqhyccd.cpp +0 -0
  255. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/dllqhyccd.h +0 -0
  256. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/qhyccd.h +0 -0
  257. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/qhyccdcamdef.h +0 -0
  258. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/qhyccderr.h +0 -0
  259. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/qhyccdinternal.h +0 -0
  260. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/include/qhyccdstruct.h +0 -0
  261. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/lib/qhyccd.lib +0 -0
  262. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_multicam_opencv/main.cpp +0 -0
  263. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_opencv/CMakeLists.txt +0 -0
  264. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_live_opencv/main.cpp +0 -0
  265. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_single_opencv/CMakeLists.txt +0 -0
  266. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/cmake_demo/test_single_opencv/main.cpp +0 -0
  267. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/CMakeLists.txt +0 -0
  268. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/Capture.cpp +0 -0
  269. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/ControlCFW.cpp +0 -0
  270. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/ControlCool.cpp +0 -0
  271. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/ForceStop.cpp +0 -0
  272. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/GetCameraStatus.cpp +0 -0
  273. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/GetHistJPG.cpp +0 -0
  274. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/GetLastImageFIT.cpp +0 -0
  275. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/GetLastImageJPG.cpp +0 -0
  276. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/GetTemperature.cpp +0 -0
  277. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/LiveFrameSample.cpp +0 -0
  278. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/OSXDownloadFirmware2Camera.cpp +0 -0
  279. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/README +0 -0
  280. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/SetTemperature.cpp +0 -0
  281. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/testapp/common/SingleFrameSample.cpp +0 -0
  282. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/local/udev/85-qhyccd.rules +0 -0
  283. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/lib/usr/share/usb/a3load.hex +0 -0
  284. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/pyobs_qhyccd/__init__.py +0 -0
  285. {pyobs_qhyccd-2.0.0.dev11 → pyobs_qhyccd-2.0.0.dev13}/pyobs_qhyccd/libqhyccd.pxd +0 -0
@@ -6,5 +6,10 @@ updates:
6
6
  interval: "weekly"
7
7
  day: "tuesday"
8
8
  target-branch: "develop"
9
+ groups:
10
+ patch-and-minor:
11
+ update-types:
12
+ - "minor"
13
+ - "patch"
9
14
  labels:
10
15
  - "dependencies"
@@ -0,0 +1,24 @@
1
+ name: Dependabot auto-merge
2
+
3
+ on: pull_request
4
+
5
+ permissions:
6
+ contents: write
7
+ pull-requests: write
8
+
9
+ jobs:
10
+ dependabot:
11
+ runs-on: ubuntu-latest
12
+ if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
13
+ steps:
14
+ - name: Fetch Dependabot metadata
15
+ id: metadata
16
+ uses: dependabot/fetch-metadata@v2
17
+ with:
18
+ compat-lookup: true
19
+ - name: Enable auto-merge for patch and minor updates
20
+ if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' }}
21
+ run: gh pr merge --auto --merge "$PR_URL"
22
+ env:
23
+ PR_URL: ${{ github.event.pull_request.html_url }}
24
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -56,7 +56,7 @@ jobs:
56
56
  - name: Set up Python
57
57
  uses: astral-sh/setup-uv@v5
58
58
  with:
59
- enable-cache: true
59
+ enable-cache: false
60
60
  python-version: "3.13"
61
61
 
62
62
  - name: Publish
@@ -0,0 +1,26 @@
1
+ name: pyrefly
2
+ on: push
3
+
4
+ jobs:
5
+ pyrefly:
6
+ runs-on: ubuntu-latest
7
+ timeout-minutes: 10
8
+
9
+ steps:
10
+ - name: Check out repository code
11
+ uses: actions/checkout@v4
12
+
13
+ - name: Set up Python
14
+ uses: astral-sh/setup-uv@v5
15
+ with:
16
+ enable-cache: true
17
+ python-version: "3.13"
18
+
19
+ - name: Install system dependencies
20
+ run: sudo apt-get install -y libusb-1.0-0-dev libcfitsio-dev
21
+
22
+ - name: Install packages
23
+ run: uv sync --all-extras --dev
24
+
25
+ - name: Run pyrefly
26
+ run: uv run pyrefly check
@@ -0,0 +1,28 @@
1
+ name: PyTest
2
+ on: push
3
+
4
+ jobs:
5
+ test:
6
+ runs-on: ubuntu-latest
7
+ timeout-minutes: 10
8
+
9
+ steps:
10
+ - name: Check out repository code
11
+ uses: actions/checkout@v4
12
+
13
+ - name: Set up Python
14
+ uses: astral-sh/setup-uv@v5
15
+ with:
16
+ enable-cache: true
17
+ python-version: "3.13"
18
+
19
+ - name: Install system dependencies
20
+ run: sudo apt-get install -y libusb-1.0-0-dev libcfitsio-dev
21
+
22
+ - name: Install packages
23
+ run: uv sync --all-extras --dev
24
+
25
+ - name: Run tests
26
+ run: uv run pytest tests
27
+ env:
28
+ CI: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-qhyccd
3
- Version: 2.0.0.dev11
3
+ Version: 2.0.0.dev13
4
4
  Summary: pyobs module for QHYCCD cameras
5
5
  Author-Email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -68,12 +68,17 @@ class MainWindow(QtWidgets.QMainWindow):
68
68
  self.device.set_bits_mode(16)
69
69
 
70
70
  self.expose.start_exposure(self.exposure_time.value)
71
- abort_event = asyncio.Event()
71
+ self.abort_exposure.clear()
72
72
  self.device.expose_single_frame()
73
- await event_wait(abort_event, self.exposure_time.value - 0.5)
73
+ aborted = await event_wait(self.abort_exposure, self.exposure_time.value - 0.5)
74
+ self.expose.set_exposures_left()
75
+ if aborted:
76
+ # a cancelled exposure must not be read out (see cancel_exposure's docs)
77
+ self.device.cancel_exposure()
78
+ return
79
+
74
80
  loop = asyncio.get_running_loop()
75
81
  image_data = await loop.run_in_executor(None, self.device.get_single_frame)
76
- self.expose.set_exposures_left()
77
82
 
78
83
  image = fits.PrimaryHDU(image_data)
79
84
  self.data_display_widget.set_data(image)
@@ -81,7 +86,10 @@ class MainWindow(QtWidgets.QMainWindow):
81
86
  @qasync.asyncSlot() # type: ignore
82
87
  async def _abort_clicked(self) -> None:
83
88
  self.abort_exposure.set()
84
- self.abort_exposure = asyncio.Event()
89
+
90
+ def closeEvent(self, event) -> None: # type: ignore[override]
91
+ self.device.close()
92
+ super().closeEvent(event)
85
93
 
86
94
 
87
95
  async def async_main(app: QtWidgets.QApplication) -> None:
@@ -10,6 +10,7 @@ from typing import Any, TypeVar, cast
10
10
  import numpy as np
11
11
  from pyobs.images import Image
12
12
  from pyobs.interfaces import (
13
+ Binning,
13
14
  BinningCapabilities,
14
15
  BinningState,
15
16
  CoolingState,
@@ -27,6 +28,7 @@ from pyobs.interfaces import (
27
28
  WindowState,
28
29
  )
29
30
  from pyobs.modules.camera.basecamera import BaseCamera
31
+ from pyobs.utils.exceptions import AbortedError
30
32
  from pyobs.utils.parallel import event_wait
31
33
 
32
34
  from .qhyccddriver import Control, QHYCCDDriver, set_log_level # type: ignore
@@ -58,6 +60,7 @@ class QHYCCDCamera(BaseCamera, ICamera, IWindow, IBinning, IAbortable, ICooling,
58
60
  BaseCamera.__init__(self, **kwargs)
59
61
 
60
62
  self._driver: QHYCCDDriver | None = None
63
+ self._driver_lock = threading.Lock()
61
64
  self._setpoint = setpoint
62
65
  self._window = (0, 0, 0, 0)
63
66
  self._binning = (1, 1)
@@ -70,24 +73,33 @@ class QHYCCDCamera(BaseCamera, ICamera, IWindow, IBinning, IAbortable, ICooling,
70
73
 
71
74
  self.add_background_task(self._update_cooling)
72
75
 
73
- @staticmethod
74
- async def _run_blocking(func: Callable[[], None], timeout: float = _SDK_CALL_TIMEOUT) -> bool:
76
+ async def _run_blocking(self, func: Callable[[], None], timeout: float = _SDK_CALL_TIMEOUT) -> bool:
75
77
  """Run a blocking QHYCCD SDK call in a daemon thread, so a hung call can't freeze the module.
76
78
 
77
79
  A plain executor isn't used here, since its worker threads are non-daemon and Python joins
78
80
  them on interpreter shutdown -- a hung call would then just move the freeze to process exit.
79
81
 
82
+ Every SDK call is serialized behind self._driver_lock, so the helper threads and the 1s
83
+ cooling poll never touch the driver handle concurrently. On timeout the worker is left
84
+ running in the background; its result callback is guarded so it can't set_result on an
85
+ already-cancelled future.
86
+
80
87
  Returns:
81
88
  True if func completed within timeout, False if it's still running in the background.
82
89
  """
83
90
  loop = asyncio.get_running_loop()
84
91
  future: asyncio.Future[None] = loop.create_future()
85
92
 
93
+ def _set_result() -> None:
94
+ if not future.done():
95
+ future.set_result(None)
96
+
86
97
  def _wrapper() -> None:
87
98
  try:
88
- func()
99
+ with self._driver_lock:
100
+ func()
89
101
  finally:
90
- loop.call_soon_threadsafe(future.set_result, None)
102
+ loop.call_soon_threadsafe(_set_result)
91
103
 
92
104
  threading.Thread(target=_wrapper, daemon=True).start()
93
105
  try:
@@ -198,21 +210,21 @@ class QHYCCDCamera(BaseCamera, ICamera, IWindow, IBinning, IAbortable, ICooling,
198
210
 
199
211
  await self._run_blocking_or_raise(_set_custom_params)
200
212
 
201
- async def _available_binnings(self) -> list[BinningState]:
213
+ async def _available_binnings(self) -> list[Binning]:
202
214
  if self._driver is None:
203
215
  return []
204
216
  driver = self._driver
205
217
 
206
- def _get() -> list[BinningState]:
218
+ def _get() -> list[Binning]:
207
219
  binnings = []
208
220
  if driver.is_control_available(Control.CAM_BIN1X1MODE):
209
- binnings.append(BinningState(x=1, y=1))
221
+ binnings.append(Binning(x=1, y=1))
210
222
  if driver.is_control_available(Control.CAM_BIN2X2MODE):
211
- binnings.append(BinningState(x=2, y=2))
223
+ binnings.append(Binning(x=2, y=2))
212
224
  if driver.is_control_available(Control.CAM_BIN3X3MODE):
213
- binnings.append(BinningState(x=3, y=3))
225
+ binnings.append(Binning(x=3, y=3))
214
226
  if driver.is_control_available(Control.CAM_BIN4X4MODE):
215
- binnings.append(BinningState(x=4, y=4))
227
+ binnings.append(Binning(x=4, y=4))
216
228
  return binnings
217
229
 
218
230
  return await self._run_blocking_or_raise(_get)
@@ -320,20 +332,28 @@ class QHYCCDCamera(BaseCamera, ICamera, IWindow, IBinning, IAbortable, ICooling,
320
332
  )
321
333
  date_obs = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")
322
334
  await self._run_blocking_or_raise(driver.expose_single_frame)
323
- await self._wait_exposure(abort_event, exposure_time, open_shutter)
324
- loop = asyncio.get_running_loop()
325
- image_data = await loop.run_in_executor(None, driver.get_single_frame)
335
+ aborted = await self._wait_exposure(abort_event, exposure_time, open_shutter)
336
+ if aborted:
337
+ # a cancelled exposure must not be read out (see cancel_exposure/_abort_exposure)
338
+ raise AbortedError()
339
+ image_data = await self._run_blocking_or_raise(driver.get_single_frame)
326
340
  return await self._get_image_with_header(image_data, date_obs, exposure_time)
327
341
 
328
- async def _wait_exposure(self, abort_event: asyncio.Event, exposure_time: float, open_shutter: bool) -> None:
329
- """Wait for exposure to finish."""
330
- await event_wait(abort_event, exposure_time - 0.5)
342
+ async def _wait_exposure(self, abort_event: asyncio.Event, exposure_time: float, open_shutter: bool) -> bool:
343
+ """Wait for exposure to finish.
344
+
345
+ Returns:
346
+ True if the exposure was aborted, False if it ran to completion.
347
+ """
348
+ return await event_wait(abort_event, exposure_time - 0.5)
331
349
 
332
350
  async def _abort_exposure(self) -> None:
333
351
  """Abort the running exposure."""
334
352
  if self._driver is None:
335
353
  raise ValueError("No camera driver.")
336
- # self._driver.cancel_exposure()
354
+ driver = self._driver
355
+ if not await self._run_blocking(driver.cancel_exposure):
356
+ log.error("Timed out cancelling QHYCCD exposure after %.1fs.", _SDK_CALL_TIMEOUT)
337
357
 
338
358
  async def _get_cooling_power(self) -> float:
339
359
  if self._driver is None:
@@ -79,7 +79,7 @@ class Control(Enum):
79
79
  CAM_IS_COLOR = CONTROL_ID.CAM_IS_COLOR,
80
80
  hasHardwareFrameCounter = CONTROL_ID.hasHardwareFrameCounter,
81
81
  CONTROL_MAX_ID = CONTROL_ID.CONTROL_MAX_ID,
82
- CAM_HUMIDITY = CONTROL_ID.CAM_HUMIDITY
82
+ CAM_HUMIDITY = CONTROL_ID.CAM_HUMIDITY,
83
83
 
84
84
 
85
85
  def set_log_level(log_level):
@@ -216,6 +216,15 @@ cdef class QHYCCDDriver:
216
216
  def expose_single_frame(self):
217
217
  return ExpQHYCCDSingleFrame(self._device)
218
218
 
219
+ def cancel_exposure(self):
220
+ """Force-stop a running exposure without reading out its (incomplete) data.
221
+
222
+ Raises:
223
+ ValueError: If cancelling failed.
224
+ """
225
+ if CancelQHYCCDExposingAndReadout(self._device) != 0:
226
+ raise ValueError('Could not cancel exposure.')
227
+
219
228
  def get_mem_length(self):
220
229
  return GetQHYCCDMemLength(self._device)
221
230
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyobs-qhyccd"
3
- version = "2.0.0.dev11"
3
+ version = "2.0.0.dev13"
4
4
  description = "pyobs module for QHYCCD cameras"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
6
  requires-python = ">=3.11"
@@ -24,6 +24,8 @@ dev = [
24
24
  "pyrefly>=1.1.1",
25
25
  "pre-commit>=4.2.0,<5",
26
26
  "cython>=3.0.0,<4",
27
+ "pytest>=8.0.0",
28
+ "pytest-asyncio>=0.24.0",
27
29
  ]
28
30
 
29
31
  [tool.setuptools]
@@ -61,3 +63,7 @@ select = ["E", "F", "W", "I", "UP", "G"]
61
63
 
62
64
  [tool.pyrefly]
63
65
  python-version = "3.11"
66
+ project-excludes = ["pyobs_qhyccd/gui.py"]
67
+
68
+ [tool.pytest.ini_options]
69
+ asyncio_mode = "strict"
@@ -0,0 +1,23 @@
1
+ """Smoke tests: import the driver and instantiate it without hardware, asserting the
2
+ interfaces it claims.
3
+
4
+ The Cython extension (qhyccddriver) links the vendored QHYCCD static library during
5
+ install, but device enumeration/opening only happens inside open(), so instantiation
6
+ is safe with no QHYCCD hardware attached.
7
+ """
8
+
9
+ from pyobs.interfaces import IAbortable, IBinning, ICamera, ICooling, IGain, IWindow
10
+ from pyobs.modules import Module
11
+
12
+ from pyobs_qhyccd import QHYCCDCamera
13
+
14
+
15
+ def test_instantiate_camera() -> None:
16
+ camera = QHYCCDCamera()
17
+ assert isinstance(camera, Module)
18
+ assert isinstance(camera, ICamera)
19
+ assert isinstance(camera, IWindow)
20
+ assert isinstance(camera, IBinning)
21
+ assert isinstance(camera, ICooling)
22
+ assert isinstance(camera, IGain)
23
+ assert isinstance(camera, IAbortable)
@@ -0,0 +1,157 @@
1
+ """Unit tests for the non-hardware logic in QHYCCDCamera: constructor defaults, the
2
+ window/binning setters, the cooling-control guard, and the _run_blocking wrappers.
3
+
4
+ Hardware I/O (opening, exposing, reading out) is out of scope here.
5
+ """
6
+
7
+ import asyncio
8
+ import threading
9
+ import time
10
+ from unittest.mock import AsyncMock
11
+
12
+ import pytest
13
+ from pyobs.interfaces import IBinning, ICooling, IWindow
14
+
15
+ from pyobs_qhyccd import QHYCCDCamera
16
+
17
+
18
+ def test_constructor_defaults() -> None:
19
+ camera = QHYCCDCamera()
20
+ assert camera._setpoint == -10
21
+ assert camera._window == (0, 0, 0, 0)
22
+ assert camera._binning == (1, 1)
23
+ assert camera._effective_area == (0, 0, 0, 0)
24
+ assert camera._cooling_step == 1.0
25
+ assert camera._cooling_wait == 60.0
26
+ assert camera._driver is None
27
+
28
+
29
+ @pytest.mark.asyncio
30
+ async def test_set_window() -> None:
31
+ camera = QHYCCDCamera()
32
+ camera.comm.set_state = AsyncMock() # type: ignore[method-assign]
33
+
34
+ await camera.set_window(10, 20, 100, 200)
35
+
36
+ assert camera._window == (10, 20, 100, 200)
37
+ assert camera.comm.set_state.await_args is not None
38
+ interface, state = camera.comm.set_state.await_args.args
39
+ assert interface is IWindow
40
+ assert (state.x, state.y, state.width, state.height) == (10, 20, 100, 200)
41
+
42
+
43
+ @pytest.mark.asyncio
44
+ async def test_set_binning() -> None:
45
+ camera = QHYCCDCamera()
46
+ camera.comm.set_state = AsyncMock() # type: ignore[method-assign]
47
+
48
+ await camera.set_binning(2, 3)
49
+
50
+ assert camera._binning == (2, 3)
51
+ assert camera.comm.set_state.await_args is not None
52
+ interface, state = camera.comm.set_state.await_args.args
53
+ assert interface is IBinning
54
+ assert (state.x, state.y) == (2, 3)
55
+
56
+
57
+ @pytest.mark.asyncio
58
+ async def test_set_cooling_without_driver_publishes_state() -> None:
59
+ camera = QHYCCDCamera()
60
+ camera.comm.set_state = AsyncMock() # type: ignore[method-assign]
61
+
62
+ await camera.set_cooling(True, -15.0)
63
+
64
+ # no driver attached, so power can't be read back and is published as None
65
+ assert camera._setpoint == -15.0
66
+ assert camera.comm.set_state.await_args is not None
67
+ interface, state = camera.comm.set_state.await_args.args
68
+ assert interface is ICooling
69
+ assert (state.setpoint, state.power, state.enabled) == (-15.0, None, True)
70
+
71
+
72
+ @pytest.mark.asyncio
73
+ async def test_set_cooling_disabled_publishes_no_setpoint() -> None:
74
+ camera = QHYCCDCamera()
75
+ camera.comm.set_state = AsyncMock() # type: ignore[method-assign]
76
+
77
+ await camera.set_cooling(False, -15.0)
78
+
79
+ # disabling cooling publishes setpoint=None even though a setpoint was passed in
80
+ assert camera.comm.set_state.await_args is not None
81
+ interface, state = camera.comm.set_state.await_args.args
82
+ assert interface is ICooling
83
+ assert (state.setpoint, state.enabled) == (None, False)
84
+
85
+
86
+ @pytest.mark.asyncio
87
+ async def test_set_gain_requires_driver() -> None:
88
+ camera = QHYCCDCamera()
89
+ with pytest.raises(ValueError):
90
+ await camera.set_gain(10.0)
91
+
92
+
93
+ @pytest.mark.asyncio
94
+ async def test_run_blocking_runs_func_and_returns_true() -> None:
95
+ ran: list[bool] = []
96
+
97
+ def fast() -> None:
98
+ ran.append(True)
99
+
100
+ camera = QHYCCDCamera()
101
+ assert await camera._run_blocking(fast) is True
102
+ assert ran == [True]
103
+
104
+
105
+ @pytest.mark.asyncio
106
+ async def test_run_blocking_times_out() -> None:
107
+ done = threading.Event()
108
+
109
+ def slow() -> None:
110
+ done.wait()
111
+
112
+ camera = QHYCCDCamera()
113
+ assert await camera._run_blocking(slow, timeout=0.01) is False
114
+ done.set()
115
+ await asyncio.sleep(0.05)
116
+
117
+
118
+ @pytest.mark.asyncio
119
+ async def test_run_blocking_serializes_concurrent_calls() -> None:
120
+ camera = QHYCCDCamera()
121
+ active = 0
122
+ max_active = 0
123
+ track_lock = threading.Lock()
124
+
125
+ def worker() -> None:
126
+ nonlocal active, max_active
127
+ with track_lock:
128
+ active += 1
129
+ max_active = max(max_active, active)
130
+ time.sleep(0.05)
131
+ with track_lock:
132
+ active -= 1
133
+
134
+ await asyncio.gather(
135
+ camera._run_blocking(worker),
136
+ camera._run_blocking(worker),
137
+ camera._run_blocking(worker),
138
+ )
139
+
140
+ assert max_active == 1
141
+
142
+
143
+ @pytest.mark.asyncio
144
+ async def test_run_blocking_or_raise_returns_value() -> None:
145
+ camera = QHYCCDCamera()
146
+ assert await camera._run_blocking_or_raise(lambda: 42) == 42
147
+
148
+
149
+ @pytest.mark.asyncio
150
+ async def test_run_blocking_or_raise_reraises() -> None:
151
+ camera = QHYCCDCamera()
152
+
153
+ def boom() -> int:
154
+ raise ValueError("boom")
155
+
156
+ with pytest.raises(ValueError):
157
+ await camera._run_blocking_or_raise(boom)
@@ -1007,6 +1007,15 @@ wheels = [
1007
1007
  { url = "https://files.pythonhosted.org/packages/7f/ec/6b120b4a86f6fadc7ddb1d7c7cdcb15bcfb332deb022ab60df51bcd4494c/influxdb_client-1.50.0-py3-none-any.whl", hash = "sha256:f172975cf7f0c95bfe74f288b31273393b164d2c58a948de55497d9956ab49be", size = 746289, upload-time = "2026-01-23T09:39:37.377Z" },
1008
1008
  ]
1009
1009
 
1010
+ [[package]]
1011
+ name = "iniconfig"
1012
+ version = "2.3.0"
1013
+ source = { registry = "https://pypi.org/simple" }
1014
+ sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
1015
+ wheels = [
1016
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
1017
+ ]
1018
+
1010
1019
  [[package]]
1011
1020
  name = "invoke"
1012
1021
  version = "3.0.3"
@@ -1772,6 +1781,15 @@ wheels = [
1772
1781
  { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" },
1773
1782
  ]
1774
1783
 
1784
+ [[package]]
1785
+ name = "pluggy"
1786
+ version = "1.6.0"
1787
+ source = { registry = "https://pypi.org/simple" }
1788
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
1789
+ wheels = [
1790
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
1791
+ ]
1792
+
1775
1793
  [[package]]
1776
1794
  name = "pre-commit"
1777
1795
  version = "4.6.1"
@@ -2160,6 +2178,15 @@ wheels = [
2160
2178
  { url = "https://files.pythonhosted.org/packages/b4/11/97233cf23ad5411ac6f13b1d6ee3888f90ace4f974d9bf9db887aa428912/pyerfa-2.0.1.5-cp39-abi3-win_amd64.whl", hash = "sha256:66292d437dcf75925b694977aa06eb697126e7b86553e620371ed3e48b5e0ad0", size = 349410, upload-time = "2024-11-11T15:22:24.817Z" },
2161
2179
  ]
2162
2180
 
2181
+ [[package]]
2182
+ name = "pygments"
2183
+ version = "2.20.0"
2184
+ source = { registry = "https://pypi.org/simple" }
2185
+ sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
2186
+ wheels = [
2187
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
2188
+ ]
2189
+
2163
2190
  [[package]]
2164
2191
  name = "pynacl"
2165
2192
  version = "1.6.2"
@@ -2241,7 +2268,7 @@ gui = [
2241
2268
 
2242
2269
  [[package]]
2243
2270
  name = "pyobs-qhyccd"
2244
- version = "2.0.0.dev11"
2271
+ version = "2.0.0.dev13"
2245
2272
  source = { editable = "." }
2246
2273
  dependencies = [
2247
2274
  { name = "astropy" },
@@ -2261,6 +2288,8 @@ dev = [
2261
2288
  { name = "cython" },
2262
2289
  { name = "pre-commit" },
2263
2290
  { name = "pyrefly" },
2291
+ { name = "pytest" },
2292
+ { name = "pytest-asyncio" },
2264
2293
  { name = "ruff" },
2265
2294
  ]
2266
2295
 
@@ -2280,6 +2309,8 @@ dev = [
2280
2309
  { name = "cython", specifier = ">=3.0.0,<4" },
2281
2310
  { name = "pre-commit", specifier = ">=4.2.0,<5" },
2282
2311
  { name = "pyrefly", specifier = ">=1.1.1" },
2312
+ { name = "pytest", specifier = ">=8.0.0" },
2313
+ { name = "pytest-asyncio", specifier = ">=0.24.0" },
2283
2314
  { name = "ruff", specifier = ">=0.9.0" },
2284
2315
  ]
2285
2316
 
@@ -2359,6 +2390,35 @@ wheels = [
2359
2390
  { url = "https://files.pythonhosted.org/packages/53/27/d17f25e45820e633a70e6109b35991eda09a5e8000c2a306f0ab7538d48c/pyside6_essentials-6.11.0-cp310-abi3-win_arm64.whl", hash = "sha256:81ca603dbf21bc39f89bb42db215c25ebe0c879a1a4c387625c321d2730ec187", size = 56337457, upload-time = "2026-03-23T12:43:43.573Z" },
2360
2391
  ]
2361
2392
 
2393
+ [[package]]
2394
+ name = "pytest"
2395
+ version = "9.1.1"
2396
+ source = { registry = "https://pypi.org/simple" }
2397
+ dependencies = [
2398
+ { name = "colorama", marker = "sys_platform == 'win32'" },
2399
+ { name = "iniconfig" },
2400
+ { name = "packaging" },
2401
+ { name = "pluggy" },
2402
+ { name = "pygments" },
2403
+ ]
2404
+ sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
2405
+ wheels = [
2406
+ { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
2407
+ ]
2408
+
2409
+ [[package]]
2410
+ name = "pytest-asyncio"
2411
+ version = "1.4.0"
2412
+ source = { registry = "https://pypi.org/simple" }
2413
+ dependencies = [
2414
+ { name = "pytest" },
2415
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
2416
+ ]
2417
+ sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" }
2418
+ wheels = [
2419
+ { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" },
2420
+ ]
2421
+
2362
2422
  [[package]]
2363
2423
  name = "python-dateutil"
2364
2424
  version = "2.9.0.post0"