citrascope 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- citrascope/__main__.py +8 -5
- citrascope/api/abstract_api_client.py +7 -0
- citrascope/api/citra_api_client.py +30 -1
- citrascope/citra_scope_daemon.py +214 -61
- citrascope/hardware/abstract_astro_hardware_adapter.py +70 -2
- citrascope/hardware/adapter_registry.py +94 -0
- citrascope/hardware/indi_adapter.py +456 -16
- citrascope/hardware/kstars_dbus_adapter.py +179 -0
- citrascope/hardware/nina_adv_http_adapter.py +593 -0
- citrascope/hardware/nina_adv_http_survey_template.json +328 -0
- citrascope/logging/__init__.py +2 -1
- citrascope/logging/_citrascope_logger.py +80 -1
- citrascope/logging/web_log_handler.py +74 -0
- citrascope/settings/citrascope_settings.py +145 -0
- citrascope/settings/settings_file_manager.py +126 -0
- citrascope/tasks/runner.py +124 -28
- citrascope/tasks/scope/base_telescope_task.py +25 -10
- citrascope/tasks/scope/static_telescope_task.py +11 -3
- citrascope/web/__init__.py +1 -0
- citrascope/web/app.py +470 -0
- citrascope/web/server.py +123 -0
- citrascope/web/static/api.js +82 -0
- citrascope/web/static/app.js +500 -0
- citrascope/web/static/config.js +362 -0
- citrascope/web/static/img/citra.png +0 -0
- citrascope/web/static/img/favicon.png +0 -0
- citrascope/web/static/style.css +120 -0
- citrascope/web/static/websocket.js +127 -0
- citrascope/web/templates/dashboard.html +354 -0
- {citrascope-0.1.0.dist-info → citrascope-0.3.0.dist-info}/METADATA +68 -36
- citrascope-0.3.0.dist-info/RECORD +38 -0
- {citrascope-0.1.0.dist-info → citrascope-0.3.0.dist-info}/WHEEL +1 -1
- citrascope/settings/_citrascope_settings.py +0 -42
- citrascope-0.1.0.dist-info/RECORD +0 -21
- {citrascope-0.1.0.dist-info → citrascope-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from citrascope.hardware.abstract_astro_hardware_adapter import (
|
|
7
|
+
AbstractAstroHardwareAdapter,
|
|
8
|
+
ObservationStrategy,
|
|
9
|
+
SettingSchemaEntry,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class KStarsDBusAdapter(AbstractAstroHardwareAdapter):
|
|
14
|
+
"""Adapter for controlling astronomical equipment through KStars via DBus."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, logger: logging.Logger, images_dir: Path, **kwargs):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the KStars DBus adapter.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
logger: Logger instance for logging messages
|
|
22
|
+
images_dir: Path to the images directory
|
|
23
|
+
**kwargs: Configuration including bus_name
|
|
24
|
+
"""
|
|
25
|
+
super().__init__(images_dir=images_dir)
|
|
26
|
+
self.logger: logging.Logger = logger
|
|
27
|
+
self.bus_name = kwargs.get("bus_name", "org.kde.kstars")
|
|
28
|
+
self.bus = None
|
|
29
|
+
self.kstars = None
|
|
30
|
+
self.ekos = None
|
|
31
|
+
self.mount = None
|
|
32
|
+
self.camera = None
|
|
33
|
+
self.scheduler = None
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def get_settings_schema(cls) -> list[SettingSchemaEntry]:
|
|
37
|
+
"""
|
|
38
|
+
Return a schema describing configurable settings for the KStars DBus adapter.
|
|
39
|
+
"""
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
"name": "bus_name",
|
|
43
|
+
"friendly_name": "D-Bus Service Name",
|
|
44
|
+
"type": "str",
|
|
45
|
+
"default": "org.kde.kstars",
|
|
46
|
+
"description": "D-Bus service name for KStars",
|
|
47
|
+
"required": True,
|
|
48
|
+
"placeholder": "org.kde.kstars",
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
def _do_point_telescope(self, ra: float, dec: float):
|
|
53
|
+
raise NotImplementedError
|
|
54
|
+
|
|
55
|
+
def get_observation_strategy(self) -> ObservationStrategy:
|
|
56
|
+
return ObservationStrategy.SEQUENCE_TO_CONTROLLER
|
|
57
|
+
|
|
58
|
+
def perform_observation_sequence(self, task_id, satellite_data) -> str:
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
def connect(self) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Connect to KStars via DBus and initialize the Ekos session.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
bool: True if connection successful, False otherwise
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
# Import dbus here to make it an optional dependency
|
|
70
|
+
try:
|
|
71
|
+
import dbus
|
|
72
|
+
except ImportError:
|
|
73
|
+
self.logger.error("dbus-python is not installed. Install with: pip install dbus-python")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
# Connect to the session bus
|
|
77
|
+
self.logger.info("Connecting to DBus session bus...")
|
|
78
|
+
self.bus = dbus.SessionBus()
|
|
79
|
+
|
|
80
|
+
# Get the KStars service
|
|
81
|
+
try:
|
|
82
|
+
kstars_obj = self.bus.get_object(self.bus_name, "/KStars")
|
|
83
|
+
self.kstars = dbus.Interface(kstars_obj, dbus_interface="org.kde.kstars")
|
|
84
|
+
self.logger.info("Connected to KStars DBus interface")
|
|
85
|
+
except dbus.DBusException as e:
|
|
86
|
+
self.logger.error(f"Failed to connect to KStars: {e}")
|
|
87
|
+
self.logger.error("Make sure KStars is running and DBus is enabled")
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
# Get the Ekos interface
|
|
91
|
+
try:
|
|
92
|
+
ekos_obj = self.bus.get_object(self.bus_name, "/KStars/Ekos")
|
|
93
|
+
self.ekos = dbus.Interface(ekos_obj, dbus_interface="org.kde.kstars.Ekos")
|
|
94
|
+
self.logger.info("Connected to Ekos interface")
|
|
95
|
+
except dbus.DBusException as e:
|
|
96
|
+
self.logger.warning(f"Failed to connect to Ekos interface: {e}")
|
|
97
|
+
self.logger.warning("Attempting to start Ekos...")
|
|
98
|
+
|
|
99
|
+
# Try to start Ekos if it's not running
|
|
100
|
+
try:
|
|
101
|
+
self.kstars.startEkos()
|
|
102
|
+
time.sleep(2) # Give Ekos time to start
|
|
103
|
+
ekos_obj = self.bus.get_object(self.bus_name, "/KStars/Ekos")
|
|
104
|
+
self.ekos = dbus.Interface(ekos_obj, dbus_interface="org.kde.kstars.Ekos")
|
|
105
|
+
self.logger.info("Started and connected to Ekos interface")
|
|
106
|
+
except Exception as start_error:
|
|
107
|
+
self.logger.error(f"Failed to start Ekos: {start_error}")
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
# Get Mount interface
|
|
111
|
+
try:
|
|
112
|
+
mount_obj = self.bus.get_object(self.bus_name, "/KStars/Ekos/Mount")
|
|
113
|
+
self.mount = dbus.Interface(mount_obj, dbus_interface="org.kde.kstars.Ekos.Mount")
|
|
114
|
+
self.logger.info("Connected to Mount interface")
|
|
115
|
+
except dbus.DBusException as e:
|
|
116
|
+
self.logger.warning(f"Mount interface not available: {e}")
|
|
117
|
+
|
|
118
|
+
# Get Camera interface
|
|
119
|
+
try:
|
|
120
|
+
camera_obj = self.bus.get_object(self.bus_name, "/KStars/Ekos/Camera")
|
|
121
|
+
self.camera = dbus.Interface(camera_obj, dbus_interface="org.kde.kstars.Ekos.Camera")
|
|
122
|
+
self.logger.info("Connected to Camera interface")
|
|
123
|
+
except dbus.DBusException as e:
|
|
124
|
+
self.logger.warning(f"Camera interface not available: {e}")
|
|
125
|
+
|
|
126
|
+
# Get Scheduler/Sequence interface
|
|
127
|
+
try:
|
|
128
|
+
scheduler_obj = self.bus.get_object(self.bus_name, "/KStars/Ekos/Scheduler")
|
|
129
|
+
self.scheduler = dbus.Interface(scheduler_obj, dbus_interface="org.kde.kstars.Ekos.Scheduler")
|
|
130
|
+
self.logger.info("Connected to Scheduler interface")
|
|
131
|
+
except dbus.DBusException as e:
|
|
132
|
+
self.logger.warning(f"Scheduler interface not available: {e}")
|
|
133
|
+
|
|
134
|
+
self.logger.info("Successfully connected to KStars via DBus")
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
except Exception as e:
|
|
138
|
+
self.logger.error(f"Failed to connect to KStars via DBus: {e}")
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
def disconnect(self):
|
|
142
|
+
raise NotImplementedError
|
|
143
|
+
|
|
144
|
+
def is_telescope_connected(self) -> bool:
|
|
145
|
+
"""Check if telescope is connected and responsive."""
|
|
146
|
+
# KStars adapter is incomplete - return False for now
|
|
147
|
+
return self.mount is not None
|
|
148
|
+
|
|
149
|
+
def is_camera_connected(self) -> bool:
|
|
150
|
+
"""Check if camera is connected and responsive."""
|
|
151
|
+
# KStars adapter is incomplete - return False for now
|
|
152
|
+
return self.camera is not None
|
|
153
|
+
|
|
154
|
+
def list_devices(self) -> list[str]:
|
|
155
|
+
raise NotImplementedError
|
|
156
|
+
|
|
157
|
+
def select_telescope(self, device_name: str) -> bool:
|
|
158
|
+
raise NotImplementedError
|
|
159
|
+
|
|
160
|
+
def get_telescope_direction(self) -> tuple[float, float]:
|
|
161
|
+
raise NotImplementedError
|
|
162
|
+
|
|
163
|
+
def telescope_is_moving(self) -> bool:
|
|
164
|
+
raise NotImplementedError
|
|
165
|
+
|
|
166
|
+
def select_camera(self, device_name: str) -> bool:
|
|
167
|
+
raise NotImplementedError
|
|
168
|
+
|
|
169
|
+
def take_image(self, task_id: str, exposure_duration_seconds=1) -> str:
|
|
170
|
+
raise NotImplementedError
|
|
171
|
+
|
|
172
|
+
def set_custom_tracking_rate(self, ra_rate: float, dec_rate: float):
|
|
173
|
+
raise NotImplementedError
|
|
174
|
+
|
|
175
|
+
def get_tracking_rate(self) -> tuple[float, float]:
|
|
176
|
+
raise NotImplementedError
|
|
177
|
+
|
|
178
|
+
def perform_alignment(self, target_ra: float, target_dec: float) -> bool:
|
|
179
|
+
raise NotImplementedError
|