diff --git a/python/PiFinder/camera_raw.py b/python/PiFinder/camera_raw.py new file mode 100644 index 00000000..20c9dc2e --- /dev/null +++ b/python/PiFinder/camera_raw.py @@ -0,0 +1,81 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +""" +This module is the camera +* Captures images +* Places preview images in queue +* Places solver images in queue +* Takes full res images on demand + +""" + +from PIL import Image +from PiFinder import config +from PiFinder.camera_interface import CameraInterface +from typing import Tuple + + +class CameraPI(CameraInterface): + """The camera class for PI cameras. Implements the CameraInterface interface.""" + + def __init__(self, exposure_time, gain) -> None: + from picamera2 import Picamera2 + + self.camera = Picamera2() + # Figure out camera type, hq or gs (global shutter) + self.camera_type = "hq" + self.camType = f"PI {self.camera_type}" + self.exposure_time = exposure_time + self.gain = gain + self.initialize() + + def initialize(self) -> None: + """Initializes the camera and set the needed control parameters""" + self.camera.stop() + # using this smaller scale auto-selects binning on the sensor... + cam_config = self.camera.create_still_configuration( + {"size": (512, 512)}, raw={"size": (2028, 1520), "format": "SRGGB10"} + ) + # cam_config = self.camera.create_still_configuration({"size": (2028, 1520)}) + + self.camera.configure(cam_config) + self.camera.set_controls({"AeEnable": False}) + self.camera.set_controls({"AnalogueGain": self.gain}) + self.camera.set_controls({"ExposureTime": self.exposure_time}) + self.camera.start() + + def capture(self) -> Image.Image: + _request = self.camera.capture_request + _image = _request.make_image("main") + _raw = _request.make_array("raw") + return self.camera.capture_image() + + def capture_file(self, filename) -> None: + return self.camera.capture_file(filename) + + def set_camera_config( + self, exposure_time: float, gain: float + ) -> Tuple[float, float]: + self.camera.stop() + self.camera.set_controls({"AnalogueGain": gain}) + self.camera.set_controls({"ExposureTime": exposure_time}) + self.camera.start() + return exposure_time, gain + + def get_cam_type(self) -> str: + return self.camType + + +def get_images(shared_state, camera_image, command_queue, console_queue): + """ + Instantiates the camera hardware + then calls the universal image loop + """ + + cfg = config.Config() + exposure_time = cfg.get_option("camera_exp") + gain = cfg.get_option("camera_gain") + camera_hardware = CameraPI(exposure_time, gain) + camera_hardware.get_image_loop( + shared_state, camera_image, command_queue, console_queue, cfg + ) diff --git a/python/PiFinder/config.py b/python/PiFinder/config.py index a561692b..5ae28eb2 100644 --- a/python/PiFinder/config.py +++ b/python/PiFinder/config.py @@ -57,6 +57,15 @@ def dump_config(self): def set_option(self, option, value): if option.startswith("session."): self._session_config_dict[option] = value + elif option.startswith("equipment."): + option = option.split(".")[1] + if option == "active_telescope": + self.equipment.set_active_telescope(value) + if option == "active_eyepiece": + self.equipment.set_active_eyepiece(value) + + self.save_equipment() + else: self._config_dict[option] = value self.dump_config() @@ -64,6 +73,12 @@ def set_option(self, option, value): def get_option(self, option, default: Any = None): if option.startswith("session."): return self._session_config_dict.get(option, default) + elif option.startswith("equipment."): + option = option.split(".")[1] + if option == "active_telescope": + return self.equipment.active_telescope + if option == "active_eyepiece": + return self.equipment.active_eyepiece else: return self._config_dict.get( option, self._default_config_dict.get(option, default) diff --git a/python/PiFinder/ui/base.py b/python/PiFinder/ui/base.py index eba4d191..592ef83c 100644 --- a/python/PiFinder/ui/base.py +++ b/python/PiFinder/ui/base.py @@ -48,6 +48,7 @@ def __init__( item_definition={}, add_to_stack=None, remove_from_stack=None, + jump_to_label=None, ): assert shared_state is not None self.title = self.__title__ @@ -61,6 +62,7 @@ def __init__( self.command_queues = command_queues self.add_to_stack = add_to_stack self.remove_from_stack = remove_from_stack + self.jump_to_label = jump_to_label # mode stuff self._display_mode_cycle = cycle(self._display_mode_list) diff --git a/python/PiFinder/ui/equipment.py b/python/PiFinder/ui/equipment.py index e08f5f31..9b6c7f85 100644 --- a/python/PiFinder/ui/equipment.py +++ b/python/PiFinder/ui/equipment.py @@ -2,8 +2,6 @@ from PiFinder import utils from PiFinder.state_utils import sleep_for_framerate from PiFinder.ui.marking_menus import MarkingMenuOption, MarkingMenu -from PiFinder.ui.text_menu import UITextMenu -from PiFinder import config sys_utils = utils.get_sys_utils() @@ -26,73 +24,11 @@ def __init__(self, *args, **kwargs): down=MarkingMenuOption(), ) - cfg = config.Config() - - eyepieces = cfg.equipment.eyepieces - # Loop over eyepieces - eyepiece_menu_items = [] - cnt = 0 - for eyepiece in eyepieces: - eyepiece_menu_items.append( - { - "name": eyepiece.name, - "value": cnt, - } - ) - cnt += 1 - - self.eyepiece_menu = { - "name": "Eyepiece", - "class": UITextMenu, - "select": "single", - "config_option": "session.log_eyepiece", - "items": eyepiece_menu_items, - } - - telescopes = cfg.equipment.telescopes - # Loop over telescopes - telescope_menu_items = [] - cnt = 0 - for telescope in telescopes: - telescope_menu_items.append( - { - "name": telescope.name, - "value": cnt, - } - ) - cnt += 1 - - self.telescope_menu = { - "name": "Telescope", - "class": UITextMenu, - "select": "single", - "config_option": "session.log_telescope", - "items": telescope_menu_items, - } - def update(self, force=False): sleep_for_framerate(self.shared_state) - cfg = config.Config() self.clear_screen() - selected_eyepiece = self.config_object.get_option("session.log_eyepiece", "") - selected_telescope = self.config_object.get_option("session.log_telescope", "") - - if selected_eyepiece != "": - cfg.equipment.set_active_eyepiece( - cfg.equipment.eyepieces[selected_eyepiece] - ) - cfg.save_equipment() - self.config_object.set_option("session.log_eyepiece", "") - - if selected_telescope != "": - cfg.equipment.set_active_telescope( - cfg.equipment.telescopes[selected_telescope] - ) - cfg.save_equipment() - self.config_object.set_option("session.log_telescope", "") - - if cfg.equipment.active_telescope is None: + if self.config_object.equipment.active_telescope is None: self.draw.text( (10, 20), "No telescope selected", @@ -102,12 +38,12 @@ def update(self, force=False): else: self.draw.text( (10, 20), - cfg.equipment.active_telescope.name.strip(), + self.config_object.equipment.active_telescope.name.strip(), font=self.fonts.base.font, fill=self.colors.get(128), ) - if cfg.equipment.active_eyepiece is None: + if self.config_object.equipment.active_eyepiece is None: self.draw.text( (10, 35), "No eyepiece selected", @@ -117,16 +53,16 @@ def update(self, force=False): else: self.draw.text( (10, 35), - cfg.equipment.active_eyepiece.name.strip(), + f"{self.config_object.equipment.active_eyepiece.focal_length_mm}mm {self.config_object.equipment.active_eyepiece.name}", font=self.fonts.base.font, fill=self.colors.get(128), ) if ( - cfg.equipment.active_telescope is not None - and cfg.equipment.active_eyepiece is not None + self.config_object.equipment.active_telescope is not None + and self.config_object.equipment.active_eyepiece is not None ): - mag = cfg.equipment.calc_magnification() + mag = self.config_object.equipment.calc_magnification() if mag > 0: self.draw.text( (10, 50), @@ -135,7 +71,7 @@ def update(self, force=False): fill=self.colors.get(128), ) - tfov = cfg.equipment.calc_tfov() + tfov = self.config_object.equipment.calc_tfov() tfov_degrees = int(tfov) tfov_minutes = int((tfov - tfov_degrees) * 60) self.draw.text( @@ -171,8 +107,8 @@ def update(self, force=False): def key_down(self): self.menu_index += 1 - if self.menu_index > 4: - self.menu_index = 4 + if self.menu_index > 1: + self.menu_index = 1 def key_up(self): self.menu_index -= 1 @@ -181,9 +117,9 @@ def key_up(self): def key_right(self): if self.menu_index == 0: - self.add_to_stack(self.telescope_menu) + self.jump_to_label("select_telescope") if self.menu_index == 1: - self.add_to_stack(self.eyepiece_menu) + self.jump_to_label("select_eyepiece") def draw_menu_pointer(self, horiz_position: int): self.draw.text( diff --git a/python/PiFinder/ui/menu_manager.py b/python/PiFinder/ui/menu_manager.py index 69bbabf3..53c5f53f 100644 --- a/python/PiFinder/ui/menu_manager.py +++ b/python/PiFinder/ui/menu_manager.py @@ -7,6 +7,7 @@ from PiFinder.ui import menu_structure from PiFinder.ui.object_details import UIObjectDetails from PiFinder.displays import DisplayBase +from PiFinder.ui.text_menu import UITextMenu from PiFinder.ui.marking_menus import ( MarkingMenu, MarkingMenuOption, @@ -56,6 +57,53 @@ def find_menu_by_label(label: str): return None +def dyn_menu_equipment(cfg): + """ + Add's equipment related menus to the menu tree + these are hidden under the equipment ui menu object + """ + equipment_menu_item = find_menu_by_label("equipment") + + eyepiece_menu_items = [] + for eyepiece in cfg.equipment.eyepieces: + eyepiece_menu_items.append( + { + "name": f"{eyepiece.focal_length_mm}mm {eyepiece.name}", + "value": eyepiece, + } + ) + + eyepiece_menu = { + "name": "Eyepiece", + "class": UITextMenu, + "select": "single", + "label": "select_eyepiece", + "config_option": "equipment.active_eyepiece", + "items": eyepiece_menu_items, + } + + # Loop over telescopes + telescope_menu_items = [] + for telescope in cfg.equipment.telescopes: + telescope_menu_items.append( + { + "name": telescope.name, + "value": telescope, + } + ) + + telescope_menu = { + "name": "Telescope", + "class": UITextMenu, + "select": "single", + "label": "select_telescope", + "config_option": "equipment.actice_telescope", + "items": telescope_menu_items, + } + + equipment_menu_item["items"] = [telescope_menu, eyepiece_menu] + + class MenuManager: def __init__( self, @@ -93,6 +141,7 @@ def __init__( self.ss_path = os.path.join(root_dir, "screenshots") self.ss_count = 0 + dyn_menu_equipment(self.config_object) self.preload_modules() def screengrab(self): @@ -131,6 +180,7 @@ def preload_modules(self) -> None: item_definition=module_def, add_to_stack=self.add_to_stack, remove_from_stack=self.remove_from_stack, + jump_to_label=self.jump_to_label, ) def add_to_stack(self, item: dict) -> None: @@ -154,6 +204,7 @@ def add_to_stack(self, item: dict) -> None: item_definition=item, add_to_stack=self.add_to_stack, remove_from_stack=self.remove_from_stack, + jump_to_label=self.jump_to_label, ) ) if item.get("stateful", False): diff --git a/python/PiFinder/ui/menu_structure.py b/python/PiFinder/ui/menu_structure.py index 021adce4..366f221b 100644 --- a/python/PiFinder/ui/menu_structure.py +++ b/python/PiFinder/ui/menu_structure.py @@ -824,7 +824,7 @@ "select": "single", "items": [ {"name": "Status", "class": UIStatus}, - {"name": "Equipment", "class": UIEquipment}, + {"name": "Equipment", "class": UIEquipment, "label": "equipment"}, {"name": "Console", "class": UIConsole}, {"name": "Software Upd", "class": UISoftware}, {"name": "Test Mode", "callback": callbacks.activate_debug},