Skip to content

Commit

Permalink
added focus executable
Browse files Browse the repository at this point in the history
Co-authored-by: Vincent Berenz <[email protected]>
  • Loading branch information
vincentberenz and Vincent Berenz authored Dec 12, 2024
1 parent 80a747e commit c6ccf1f
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 19 deletions.
34 changes: 31 additions & 3 deletions nightskycam_focus/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,31 @@ class CommandType(Enum):
APERTURE = "A"


class Aperture(Enum):
MAX = 441
V0 = 441
V1 = 512
V2 = 646
V3 = 706
V4 = 857
V5 = 926
V6 = 1110
V7 = 1159
V8 = 1271
V9 = 1347
V10 = 1468
V11 = 2303
MIN = 2303

@classmethod
def is_valid(cls, aperture: str) -> bool:
return aperture in cls.__members__

@classmethod
def get(cls, aperture: str) -> "Aperture":
return cls.__members__[aperture]


PIN = NewType("PIN", int)
SS_PIN = PIN(5)
RESET_PIN = PIN(6)
Expand Down Expand Up @@ -79,7 +104,9 @@ def _prepare_message(command: int, v1: int, v2: int) -> List[int]:
_ERROR_RESET = (2, 2, 2, 2)


def _spi_send(spi: spidev.SpiDev, command_type: CommandType, value: int) -> None:
def _spi_send(
spi: spidev.SpiDev, command_type: CommandType, value: int
) -> None:
logging.debug(f"command {command_type}: {value}")
v1, v2 = divmod(value, 256)
command = ord(command_type.value)
Expand Down Expand Up @@ -163,5 +190,6 @@ def set_focus(target_value: int) -> None:
_send_command(CommandType.FOCUS, target_value)


def set_aperture(target_value: int) -> None:
_send_command(CommandType.APERTURE, target_value)
def set_aperture(target_value: Aperture) -> None:
value: int = target_value.value
_send_command(CommandType.APERTURE, value)
117 changes: 102 additions & 15 deletions nightskycam_focus/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from pathlib import Path
import argparse
import logging
import sys
Expand All @@ -7,12 +8,15 @@
import numpy as np
from camera_zwo_asi import ImageType
from camera_zwo_asi.camera import Camera
from camera_zwo_asi.image import Image

from .adapter import adapter
from .adapter import adapter, set_focus, set_aperture, Aperture
from .focus import find_focus


def _get_pixel_from_user(image: np.ndarray, resize: int = 4) -> Tuple[int, int]:
def _get_pixel_from_user(
image: np.ndarray, resize: int = 4
) -> Tuple[int, int]:
down_sized_image = cv2.resize(
image, (image.shape[1] // resize, image.shape[0] // resize)
)
Expand All @@ -39,15 +43,20 @@ def _click_event(event, x, y, flags, param):
return full_size_pixel


def _get_full_image(exposure: int, gain: int, camera_index: int) -> np.ndarray:
def _capture(exposure: int, gain: int, camera_index: int) -> Image:
camera = Camera(camera_index)
camera.set_control("Exposure", exposure)
camera.set_control("Gain", gain)
roi = camera.get_roi()
roi.bins = 1
roi.type = ImageType.rgb24
camera.set_roi(roi)
return camera.capture().get_image()
return camera.capture()


def _get_full_image(exposure: int, gain: int, camera_index: int) -> np.ndarray:
image = _capture(exposure, gain, camera_index)
return image.get_image()


def _add_border(img: np.array, thickness: int = 5, color=(255, 0, 0)):
Expand All @@ -68,9 +77,11 @@ def _add_border(img: np.array, thickness: int = 5, color=(255, 0, 0)):
return bordered_img


def _zwo_asi_focus():
def _zwo_asi_focus_sweep():

parser = argparse.ArgumentParser(description="Focus sweep on zwo-asi camera")
parser = argparse.ArgumentParser(
description="Focus sweep on zwo-asi camera"
)
parser.add_argument(
"--camera_index",
type=int,
Expand All @@ -91,10 +102,16 @@ def _zwo_asi_focus():
help="Camera exposure (default: %(default)s)",
)
parser.add_argument(
"--gain", type=int, default=121, help="Camera gain (default: %(default)s)"
"--gain",
type=int,
default=121,
help="Camera gain (default: %(default)s)",
)
parser.add_argument(
"--step", type=int, default=20, help="Focus step size (default: %(default)s)"
"--step",
type=int,
default=20,
help="Focus step size (default: %(default)s)",
)
parser.add_argument("--show", action="store_true", help="Show the image")
parser.add_argument(
Expand All @@ -115,16 +132,22 @@ def _zwo_asi_focus():

# setting the logs
if args.verbose:
logging.basicConfig(level=logging.DEBUG, format="focus sweep: %(message)s")
logging.basicConfig(
level=logging.DEBUG, format="focus sweep: %(message)s"
)
elif args.silent:
...
else:
logging.basicConfig(level=logging.INFO, format="focus sweep: %(message)s")
logging.basicConfig(
level=logging.INFO, format="focus sweep: %(message)s"
)

logging.info(f"capturing image (exposure: {args.exposure}, gain: {args.gain})")
logging.info(
f"capturing image (exposure: {args.exposure}, gain: {args.gain})"
)
full_image = _get_full_image(args.exposure, args.gain, args.camera_index)

logging.info(f"prompting user for target pixel")
logging.info("prompting user for target pixel")
pixel = _get_pixel_from_user(full_image)

logging.info(
Expand Down Expand Up @@ -159,16 +182,80 @@ def _zwo_asi_focus():
cv2.imwrite("focus.png", concatenated_images)


def zwo_asi_focus():
def zwo_asi_focus_sweep():
try:
_zwo_asi_focus()
_zwo_asi_focus_sweep()
sys.exit(0)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)


def zwo_asi_focus_test():
logging.basicConfig(level=logging.DEBUG, format="focus sweep: %(message)s")
logging.basicConfig(level=logging.DEBUG, format="focus test: %(message)s")
with adapter():
logging.info("adapter running")


def _check_range(value: int, minimum: int = 350, maximum: int = 650) -> int:
"""Check if the input value is an integer within the range 350 to 650."""
try:
ivalue = int(value)
except ValueError:
raise argparse.ArgumentTypeError(f"{value} is not a valid integer")
if ivalue < minimum or ivalue > maximum:
raise argparse.ArgumentTypeError(
f"{value} is an invalid value. Must be between {minimum} and {maximum}."
)
return ivalue


def _valid_aperture(value: str) -> Aperture:
try:
return Aperture.get(value)
except KeyError:
raise argparse.ArgumentTypeError(
f"{value} is not a valid aperture. Valid apertures: MAX (open), V1, ..., V10, MIN (closed)"
)


def zwo_asi_focus():
logging.basicConfig(level=logging.DEBUG, format="focus: %(message)s")
parser = argparse.ArgumentParser(
description="Focus change on zwo-asi camera"
)
parser.add_argument(
"focus",
type=_check_range,
help="desired focus. int between 350 and 650",
)
parser.add_argument(
"--aperture",
type=_valid_aperture,
help="desired aperture. MAX: open, MIN: close, V1 ... V10: intermediate values",
required=False,
)
parser.add_argument(
"--exposure",
type=int,
help="if set (microseconds), a picture will be taken and saved in the current directory",
required=False,
)
args = parser.parse_args()
with adapter():
logging.info(f"setting focus to {args.focus}")
set_focus(args.focus)
if args.aperture is not None:
logging.info(f"setting aperture to {args.aperture}")
set_aperture(args.aperture)
if args.exposure is None:
return
logging.info(f"taking picture with exposure {args.exposure}")
image = _capture(args.exposure, 121, 0)
if args.aperture:
filename = f"img_{args.focus}_{args.aperture}_{args.exposure}.tiff"
else:
filename = f"img_{args.focus}_{args.exposure}.tiff"
filepath = str(Path.cwd() / filename)
logging.info(f"saving image to {filepath}")
image.save(filepath)
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ authors = ["Vincent Berenz <[email protected]>"]
packages = [{ include = "nightskycam_focus" }]

[tool.poetry.scripts]
zwo-asi-focus = 'nightskycam_focus.main:zwo_asi_focus'
zwo-asi-focus-sweep = 'nightskycam_focus.main:zwo_asi_focus_sweep'
zwo-asi-focus-test = 'nightskycam_focus.main:zwo_asi_focus_test'
zwo-asi-focus = 'nightskycam_focus.main:zwo_asi_focus'

[tool.poetry.dependencies]
python = "^3.9"
Expand Down

0 comments on commit c6ccf1f

Please sign in to comment.