solareclipseworkbench 1.2.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.
- solareclipseworkbench/__init__.py +8 -0
- solareclipseworkbench/camera.py +567 -0
- solareclipseworkbench/commands.py +23 -0
- solareclipseworkbench/de421.bsp +0 -0
- solareclipseworkbench/gps.py +21 -0
- solareclipseworkbench/gpstest.py +27 -0
- solareclipseworkbench/gui.py +1880 -0
- solareclipseworkbench/location.py +40 -0
- solareclipseworkbench/notifications.py +104 -0
- solareclipseworkbench/observer.py +41 -0
- solareclipseworkbench/reference_moments.py +169 -0
- solareclipseworkbench/scripts.py +233 -0
- solareclipseworkbench/sew.py +104 -0
- solareclipseworkbench/utils.py +209 -0
- solareclipseworkbench-1.2.0.dist-info/LICENSE +674 -0
- solareclipseworkbench-1.2.0.dist-info/METADATA +468 -0
- solareclipseworkbench-1.2.0.dist-info/RECORD +18 -0
- solareclipseworkbench-1.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from solareclipseworkbench.notifications import voice_prompt
|
|
2
|
+
from solareclipseworkbench.camera import take_picture
|
|
3
|
+
from solareclipseworkbench.camera import take_burst
|
|
4
|
+
from solareclipseworkbench.camera import take_bracket
|
|
5
|
+
from solareclipseworkbench.commands import execute_command
|
|
6
|
+
from solareclipseworkbench.gui import sync_cameras
|
|
7
|
+
|
|
8
|
+
__all__ = ["voice_prompt", "take_picture", "sync_cameras", "take_burst", "take_bracket", "execute_command"]
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import locale
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import gphoto2
|
|
6
|
+
import gphoto2 as gp
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
from gphoto2 import Camera
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CameraError(Exception):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CameraSettings:
|
|
17
|
+
|
|
18
|
+
def __init__(self, camera_name: str, shutter_speed: str, aperture: str, iso: int):
|
|
19
|
+
""" Initialise new camera settings.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
- camera_name: Name of the camera
|
|
23
|
+
- shutter_speed: Exposure time [s], e.g. "1/2000".
|
|
24
|
+
- aperture: Aperture (f-number), e.g. 5.6.
|
|
25
|
+
- iso: ISO-value.
|
|
26
|
+
"""
|
|
27
|
+
self.camera_name = camera_name
|
|
28
|
+
self.shutter_speed = shutter_speed
|
|
29
|
+
self.aperture = aperture
|
|
30
|
+
self.iso = iso
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def take_picture(camera: Camera, camera_settings: CameraSettings) -> None:
|
|
34
|
+
""" Take a picture with the selected camera
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
- camera_name: Camera object
|
|
38
|
+
- camera_settings: Settings of the camera (exposure, f, iso)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
context, config = __adapt_camera_settings(camera, camera_settings)
|
|
42
|
+
|
|
43
|
+
# Take picture
|
|
44
|
+
camera.capture(gp.GP_CAPTURE_IMAGE, context)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def __adapt_camera_settings(camera, camera_settings):
|
|
48
|
+
context = gp.gp_context_new()
|
|
49
|
+
config = gp.check_result(gp.gp_camera_get_config(camera, context))
|
|
50
|
+
# Set ISO
|
|
51
|
+
if "Nikon" in camera_settings.camera_name:
|
|
52
|
+
gp.gp_widget_set_value(gp.check_result(gp.gp_widget_get_child_by_name(config, 'autoiso')), str("Off"))
|
|
53
|
+
# set config
|
|
54
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
55
|
+
|
|
56
|
+
gp.gp_widget_set_value(gp.check_result(gp.gp_widget_get_child_by_name(config, 'iso')), str(camera_settings.iso))
|
|
57
|
+
# set config
|
|
58
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
59
|
+
time.sleep(0.1)
|
|
60
|
+
|
|
61
|
+
# Set aperture
|
|
62
|
+
try:
|
|
63
|
+
if "Canon" in camera_settings.camera_name:
|
|
64
|
+
gp.gp_widget_set_value(gp.check_result(gp.gp_widget_get_child_by_name(config, 'aperture')),
|
|
65
|
+
str(camera_settings.aperture))
|
|
66
|
+
elif "Nikon" in camera_settings.camera_name:
|
|
67
|
+
gp.gp_widget_set_value(gp.check_result(gp.gp_widget_get_child_by_name(config, 'f-number')),
|
|
68
|
+
str(camera_settings.aperture))
|
|
69
|
+
# set config
|
|
70
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
71
|
+
time.sleep(0.1)
|
|
72
|
+
except gphoto2.GPhoto2Error:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
# Set shutter speed
|
|
76
|
+
gp.gp_widget_set_value(gp.check_result(gp.gp_widget_get_child_by_name(config, 'shutterspeed')),
|
|
77
|
+
str(camera_settings.shutter_speed))
|
|
78
|
+
# set config
|
|
79
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
80
|
+
time.sleep(0.1)
|
|
81
|
+
|
|
82
|
+
return context, config
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def take_burst(camera: Camera, camera_settings: CameraSettings, duration: float) -> None:
|
|
86
|
+
""" Take a burst with the selected camera. For Canon, the duration is the duration in seconds, for Nikon, the
|
|
87
|
+
duration is the number of pictures to take.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
- camera_name: Camera object
|
|
91
|
+
- camera_settings: Settings of the camera (exposure, f, iso)
|
|
92
|
+
- duration: Duration of the burst in seconds (Canon) or number of pictures (Nikon)
|
|
93
|
+
"""
|
|
94
|
+
context, config = __adapt_camera_settings(camera, camera_settings)
|
|
95
|
+
|
|
96
|
+
# Take picture
|
|
97
|
+
if "Canon" in camera_settings.camera_name:
|
|
98
|
+
# Push the button
|
|
99
|
+
remote_release = gp.check_result(gp.gp_widget_get_child_by_name(config, 'eosremoterelease'))
|
|
100
|
+
gp.gp_widget_set_value(remote_release, "Press Full")
|
|
101
|
+
# set config
|
|
102
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
103
|
+
time.sleep(duration)
|
|
104
|
+
|
|
105
|
+
# Release the button
|
|
106
|
+
remote_release = gp.check_result(gp.gp_widget_get_child_by_name(config, 'eosremoterelease'))
|
|
107
|
+
gp.gp_widget_set_value(remote_release, "Release Full")
|
|
108
|
+
# set config
|
|
109
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
110
|
+
elif "Nikon" in camera_settings.camera_name:
|
|
111
|
+
# Push the button
|
|
112
|
+
capture_mode = gp.check_result(gp.gp_widget_get_child_by_name(config, 'capturemode'))
|
|
113
|
+
gp.gp_widget_set_value(capture_mode, "Burst")
|
|
114
|
+
# set config
|
|
115
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
116
|
+
|
|
117
|
+
burst_number = gp.check_result(gp.gp_widget_get_child_by_name(config, 'burstnumber'))
|
|
118
|
+
gp.gp_widget_set_value(burst_number, round(duration))
|
|
119
|
+
# set config
|
|
120
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
121
|
+
|
|
122
|
+
camera.capture(gp.GP_CAPTURE_IMAGE, context)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def take_bracket(camera: Camera, camera_settings: CameraSettings, steps: str) -> None:
|
|
126
|
+
""" Take a bracketing of images with the selected camera.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
- camera_name: Camera object
|
|
130
|
+
- camera_settings: Settings of the camera (exposure, f, iso)
|
|
131
|
+
- steps: Steps for each bracketing step (e.g. +/- 1 2/3)
|
|
132
|
+
"""
|
|
133
|
+
context, config = __adapt_camera_settings(camera, camera_settings)
|
|
134
|
+
|
|
135
|
+
if "Canon" in camera_settings.camera_name:
|
|
136
|
+
# Set aeb
|
|
137
|
+
aeb = gp.check_result(gp.gp_widget_get_child_by_name(config, 'aeb'))
|
|
138
|
+
gp.gp_widget_set_value(aeb, steps)
|
|
139
|
+
# set config
|
|
140
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
141
|
+
|
|
142
|
+
camera.capture(gp.GP_CAPTURE_IMAGE, context)
|
|
143
|
+
camera.capture(gp.GP_CAPTURE_IMAGE, context)
|
|
144
|
+
camera.capture(gp.GP_CAPTURE_IMAGE, context)
|
|
145
|
+
camera.capture(gp.GP_CAPTURE_IMAGE, context)
|
|
146
|
+
camera.capture(gp.GP_CAPTURE_IMAGE, context)
|
|
147
|
+
|
|
148
|
+
# Set aeb
|
|
149
|
+
aeb = gp.check_result(gp.gp_widget_get_child_by_name(config, 'aeb'))
|
|
150
|
+
gp.gp_widget_set_value(aeb, "off")
|
|
151
|
+
# set config
|
|
152
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def mirror_lock(camera: Camera, camera_settings: CameraSettings) -> None:
|
|
156
|
+
""" Lock the mirror
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
- camera_name: Camera object
|
|
160
|
+
"""
|
|
161
|
+
context = gp.gp_context_new()
|
|
162
|
+
config = gp.check_result(gp.gp_camera_get_config(camera, context))
|
|
163
|
+
|
|
164
|
+
if "Canon" in camera_settings.camera_name:
|
|
165
|
+
lock = gp.check_result(gp.gp_widget_get_child_by_name(config, 'mirrorlock'))
|
|
166
|
+
gp.gp_widget_set_value(lock, "1")
|
|
167
|
+
# set config
|
|
168
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
169
|
+
|
|
170
|
+
context, config = __adapt_camera_settings(camera, camera_settings)
|
|
171
|
+
|
|
172
|
+
# # Push the button
|
|
173
|
+
# remote_release = gp.check_result(gp.gp_widget_get_child_by_name(config, 'eosremoterelease'))
|
|
174
|
+
# gp.gp_widget_set_value(remote_release, "Press 2")
|
|
175
|
+
# # set config
|
|
176
|
+
# gp.gp_camera_set_config(camera, config, context)
|
|
177
|
+
|
|
178
|
+
# Release the button
|
|
179
|
+
# remote_release = gp.check_result(gp.gp_widget_get_child_by_name(config, 'eosremoterelease'))
|
|
180
|
+
# gp.gp_widget_set_value(remote_release, "Release Full")
|
|
181
|
+
# # set config
|
|
182
|
+
# gp.gp_camera_set_config(camera, config, context)
|
|
183
|
+
|
|
184
|
+
# Set mirror lock back to off
|
|
185
|
+
lock = gp.check_result(gp.gp_widget_get_child_by_name(config, 'mirrorlock'))
|
|
186
|
+
gp.gp_widget_set_value(lock, "0")
|
|
187
|
+
# set config
|
|
188
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def get_cameras() -> list:
|
|
192
|
+
""" Returns a list with the cameras.
|
|
193
|
+
|
|
194
|
+
Returns: List with all the attached cameras ([name, USB port]).
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
locale.setlocale(locale.LC_ALL, '')
|
|
198
|
+
|
|
199
|
+
gp.check_result(gp.use_python_logging())
|
|
200
|
+
# make a list of all available cameras
|
|
201
|
+
return list(gp.Camera.autodetect())
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def __get_address(camera_name: str) -> str:
|
|
205
|
+
""" Gets the address of the camera if the name is given
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
- camera_name: Name of the camera
|
|
209
|
+
|
|
210
|
+
Returns: Address of the camera
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
camera_tuple = [camera[1] for camera in get_cameras() if camera[0] == camera_name]
|
|
214
|
+
try:
|
|
215
|
+
return camera_tuple[0]
|
|
216
|
+
except IndexError:
|
|
217
|
+
raise CameraError(f"Camera {camera_name} not found")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def get_camera(camera_name: str):
|
|
221
|
+
""" Returns the initialized camera object of the selected camera
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
- camera_name: Name of the camera
|
|
225
|
+
|
|
226
|
+
Returns: Initialized camera object of the selected camera.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
addr = __get_address(camera_name)
|
|
230
|
+
if addr == '':
|
|
231
|
+
return ''
|
|
232
|
+
|
|
233
|
+
# get port info
|
|
234
|
+
port_info_list = gp.PortInfoList()
|
|
235
|
+
port_info_list.load()
|
|
236
|
+
abilities_list = gp.CameraAbilitiesList()
|
|
237
|
+
abilities_list.load()
|
|
238
|
+
|
|
239
|
+
camera = gp.Camera()
|
|
240
|
+
idx = port_info_list.lookup_path(addr)
|
|
241
|
+
camera.set_port_info(port_info_list[idx])
|
|
242
|
+
idx = abilities_list.lookup_model(camera_name)
|
|
243
|
+
camera.set_abilities(abilities_list[idx])
|
|
244
|
+
|
|
245
|
+
context = gp.gp_context_new()
|
|
246
|
+
|
|
247
|
+
# Initialize the camera
|
|
248
|
+
try:
|
|
249
|
+
camera.init(context)
|
|
250
|
+
|
|
251
|
+
# find the capture target config item (to save to the memory card)
|
|
252
|
+
config = gp.check_result(gp.gp_camera_get_config(camera, context))
|
|
253
|
+
capture_target = gp.check_result(gp.gp_widget_get_child_by_name(config, 'capturetarget'))
|
|
254
|
+
# set value
|
|
255
|
+
value = gp.check_result(gp.gp_widget_get_choice(capture_target, 1))
|
|
256
|
+
gp.gp_widget_set_value(capture_target, value)
|
|
257
|
+
# set config
|
|
258
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
259
|
+
|
|
260
|
+
# find the drivemode and set to Continuous high speed
|
|
261
|
+
drive_mode = gp.check_result(gp.gp_widget_get_child_by_name(config, 'drivemode'))
|
|
262
|
+
gp.gp_widget_set_value(drive_mode, "Continuous high speed")
|
|
263
|
+
# set config
|
|
264
|
+
gp.gp_camera_set_config(camera, config, context)
|
|
265
|
+
except gphoto2.GPhoto2Error:
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
return camera
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def get_free_space(camera: Camera) -> float:
|
|
272
|
+
""" Return the free space on the card of the selected camera
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
- camera: Camera object
|
|
276
|
+
|
|
277
|
+
Returns: Free space on the card of the camera [GB]
|
|
278
|
+
"""
|
|
279
|
+
return round(camera.get_storageinfo()[0].freekbytes / 1024 / 1024, 1)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_space(camera: Camera) -> float:
|
|
283
|
+
""" Return the size of the memory card of the selected camera
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
- camera: Camera object
|
|
287
|
+
|
|
288
|
+
Returns: Size of memory card of the camera [GB]
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
return round(camera.get_storageinfo()[0].capacitykbytes / 1024 / 1024, 1)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def get_shooting_mode(camera_name: str, camera: Camera) -> str:
|
|
295
|
+
""" Return the shooting mode of the selected camera. Should be "Manual".
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
- camera: Camera object
|
|
299
|
+
|
|
300
|
+
Returns: Shooting mode of the camera
|
|
301
|
+
"""
|
|
302
|
+
if "Canon" in camera_name:
|
|
303
|
+
return camera.get_config().get_child_by_name('autoexposuremodedial').get_value()
|
|
304
|
+
elif "Nikon" in camera_name:
|
|
305
|
+
mode = camera.get_config().get_child_by_name('expprogram').get_value()
|
|
306
|
+
if mode == "M":
|
|
307
|
+
return "Manual"
|
|
308
|
+
else:
|
|
309
|
+
return mode
|
|
310
|
+
else:
|
|
311
|
+
return ""
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def get_focus_mode(camera: Camera) -> str:
|
|
315
|
+
""" Return the focus mode of the selected camera. Should be "Manual"
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
- camera: Camera object
|
|
319
|
+
|
|
320
|
+
Returns: Focus mode of the camera
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
return camera.get_config().get_child_by_name('focusmode').get_value()
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def get_battery_level(camera: Camera) -> str:
|
|
327
|
+
""" Return the battery level of the selected camera
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
- camera: Name of the camera
|
|
331
|
+
|
|
332
|
+
Returns: Current battery level of the camera [%]
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
return camera.get_config().get_child_by_name('batterylevel').get_value()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def get_time(camera: Camera) -> str:
|
|
339
|
+
""" Returns the current time of the selected camera
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
- camera: Camera object
|
|
343
|
+
|
|
344
|
+
Returns: Current time of the camera
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
# get configuration tree
|
|
348
|
+
config = camera.get_config()
|
|
349
|
+
# find the date/time setting config item and get it
|
|
350
|
+
# name varies with camera driver
|
|
351
|
+
# Canon EOS - 'datetime'
|
|
352
|
+
# PTP - 'd034'
|
|
353
|
+
for name, fmt in (('datetime', '%Y-%m-%d %H:%M:%S'),
|
|
354
|
+
('d034', None)):
|
|
355
|
+
now = datetime.now()
|
|
356
|
+
ok, datetime_config = gp.gp_widget_get_child_by_name(config, name)
|
|
357
|
+
if ok >= gp.GP_OK:
|
|
358
|
+
widget_type = datetime_config.get_type()
|
|
359
|
+
raw_value = datetime_config.get_value()
|
|
360
|
+
if widget_type == gp.GP_WIDGET_DATE:
|
|
361
|
+
camera_time = datetime.fromtimestamp(raw_value)
|
|
362
|
+
else:
|
|
363
|
+
if fmt:
|
|
364
|
+
camera_time = datetime.strptime(raw_value, fmt)
|
|
365
|
+
else:
|
|
366
|
+
camera_time = datetime.utcfromtimestamp(float(raw_value))
|
|
367
|
+
logging.info('Camera clock: ', camera_time.isoformat(' '))
|
|
368
|
+
logging.info('Computer clock:', now.isoformat(' '))
|
|
369
|
+
err = now - camera_time
|
|
370
|
+
if err.days < 0:
|
|
371
|
+
err = -err
|
|
372
|
+
lead_lag = 'ahead'
|
|
373
|
+
logging.info('Camera clock is ahead by', )
|
|
374
|
+
else:
|
|
375
|
+
lead_lag = 'behind'
|
|
376
|
+
logging.warning('Camera clock is %s by %d days and %d seconds' % (
|
|
377
|
+
lead_lag, err.days, err.seconds))
|
|
378
|
+
break
|
|
379
|
+
else:
|
|
380
|
+
logging.warning('Unknown date/time config item')
|
|
381
|
+
return "Unknown date/time config item"
|
|
382
|
+
|
|
383
|
+
return camera_time.isoformat(' ')
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def set_time(camera: Camera) -> None:
|
|
387
|
+
""" Set the computer time on the selected camera """
|
|
388
|
+
|
|
389
|
+
# get configuration tree
|
|
390
|
+
config = camera.get_config()
|
|
391
|
+
|
|
392
|
+
if __set_datetime(config):
|
|
393
|
+
# apply the changed config
|
|
394
|
+
camera.set_config(config)
|
|
395
|
+
else:
|
|
396
|
+
logging.error('Could not set date & time')
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def __set_datetime(config) -> bool:
|
|
400
|
+
""" Private method to set the date and time of the camera. """
|
|
401
|
+
|
|
402
|
+
ok, date_config = gp.gp_widget_get_child_by_name(config, 'datetimeutc')
|
|
403
|
+
if ok == -2:
|
|
404
|
+
ok, date_config = gp.gp_widget_get_child_by_name(config, 'datetime')
|
|
405
|
+
|
|
406
|
+
if ok >= gp.GP_OK:
|
|
407
|
+
widget_type = date_config.get_type()
|
|
408
|
+
if widget_type == gp.GP_WIDGET_DATE:
|
|
409
|
+
now = int(time.time())
|
|
410
|
+
date_config.set_value(now)
|
|
411
|
+
else:
|
|
412
|
+
now = time.strftime('%Y-%m-%d %H:%M:%S')
|
|
413
|
+
date_config.set_value(now)
|
|
414
|
+
return True
|
|
415
|
+
return False
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def get_camera_dict() -> dict:
|
|
419
|
+
""" Get a dictionary of camera names and their GPhoto2 camera object
|
|
420
|
+
Returns: Dictionary of camera names and their GPhoto2 camera object
|
|
421
|
+
"""
|
|
422
|
+
camera_names = get_cameras()
|
|
423
|
+
cameras = dict()
|
|
424
|
+
for camera_name in camera_names:
|
|
425
|
+
cameras[camera_name[0]] = get_camera(camera_name[0])
|
|
426
|
+
return cameras
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def get_camera_overview() -> dict:
|
|
430
|
+
""" Returns a dictionary with information of the connected cameras.
|
|
431
|
+
|
|
432
|
+
The keys in the dictionary are the camera names and the values (the camera information) contains information about
|
|
433
|
+
the battery level and space on the memory card of the camera.
|
|
434
|
+
|
|
435
|
+
Returns: Dictionary with information of the connected cameras.
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
camera_overview = {}
|
|
439
|
+
|
|
440
|
+
camera_names = get_cameras()
|
|
441
|
+
for camera_name in camera_names:
|
|
442
|
+
camera = get_camera(camera_name[0])
|
|
443
|
+
|
|
444
|
+
try:
|
|
445
|
+
battery_level = get_battery_level(camera)
|
|
446
|
+
free_space = get_free_space(camera)
|
|
447
|
+
total_space = get_space(camera)
|
|
448
|
+
|
|
449
|
+
camera_overview[camera_name[0]] = CameraInfo(camera_name, battery_level, free_space, total_space)
|
|
450
|
+
# camera.exit()
|
|
451
|
+
except gp.GPhoto2Error:
|
|
452
|
+
logging.error("Could not connect to the camera. Did you start Solar Eclipse Workbench in sudo mode?")
|
|
453
|
+
|
|
454
|
+
return camera_overview
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
class CameraInfo:
|
|
458
|
+
|
|
459
|
+
def __init__(self, camera_name: str, battery_level: str, free_space: float, total_space: float) -> None:
|
|
460
|
+
""" Create a new CameraInfo object.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
- camera_name: Name of the camera
|
|
464
|
+
- battery_level: Battery level [%]
|
|
465
|
+
- free_space: Free space on the camera memory card [GB]
|
|
466
|
+
- total_space: Total space on the camera memory card [GB]
|
|
467
|
+
"""
|
|
468
|
+
|
|
469
|
+
self.camera_name = camera_name
|
|
470
|
+
self.battery_level = battery_level
|
|
471
|
+
self.free_space = free_space
|
|
472
|
+
self.total_space = total_space
|
|
473
|
+
|
|
474
|
+
def get_camera_name(self) -> str:
|
|
475
|
+
""" Returns the name of the camera.
|
|
476
|
+
|
|
477
|
+
Returns: Name of the camera.
|
|
478
|
+
"""
|
|
479
|
+
return self.camera_name[0]
|
|
480
|
+
|
|
481
|
+
def get_battery_level(self) -> str:
|
|
482
|
+
""" Returns the battery level of the camera.
|
|
483
|
+
|
|
484
|
+
Returns: Battery level of the camera [%].
|
|
485
|
+
"""
|
|
486
|
+
return self.battery_level
|
|
487
|
+
|
|
488
|
+
def get_absolute_free_space(self) -> float:
|
|
489
|
+
""" Returns the absolute free space on the memory card of the camera.
|
|
490
|
+
|
|
491
|
+
Returns: Free space on the memory card of the camera [GB].
|
|
492
|
+
"""
|
|
493
|
+
|
|
494
|
+
return self.free_space
|
|
495
|
+
|
|
496
|
+
def get_relative_free_space(self) -> float:
|
|
497
|
+
""" Returns the relative free space on the memory card of the camera.
|
|
498
|
+
|
|
499
|
+
Returns: Free space on the memory card of the camera [%].
|
|
500
|
+
"""
|
|
501
|
+
|
|
502
|
+
return self.get_absolute_free_space() / self.get_total_space() * 100
|
|
503
|
+
|
|
504
|
+
def get_total_space(self) -> float:
|
|
505
|
+
""" Returns the total space on the memory card of the camera.
|
|
506
|
+
|
|
507
|
+
Returns: Total space on the memory card of the camera [GB].
|
|
508
|
+
"""
|
|
509
|
+
return self.total_space
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def main():
|
|
513
|
+
# Get cameras
|
|
514
|
+
cameras = get_cameras()
|
|
515
|
+
|
|
516
|
+
# Get all information for the gui
|
|
517
|
+
get_camera_overview()
|
|
518
|
+
|
|
519
|
+
# Get battery and free space
|
|
520
|
+
for camera in cameras:
|
|
521
|
+
try:
|
|
522
|
+
camera_object = get_camera(camera[0])
|
|
523
|
+
|
|
524
|
+
# Get general info
|
|
525
|
+
print(f"{camera[0]}: {get_battery_level(camera_object)} - {get_free_space(camera_object)} GB "
|
|
526
|
+
f"of {get_space(camera_object)} GB free.")
|
|
527
|
+
|
|
528
|
+
# Check if the lens and the camera are set to manual
|
|
529
|
+
if get_shooting_mode(camera[0], camera_object) != "Manual":
|
|
530
|
+
print("Set the camera in Manual mode!")
|
|
531
|
+
exit()
|
|
532
|
+
|
|
533
|
+
if get_focus_mode(camera_object) != "Manual":
|
|
534
|
+
print("Set the lens in Manual mode!")
|
|
535
|
+
exit()
|
|
536
|
+
|
|
537
|
+
# Set the correct time
|
|
538
|
+
print(get_time(camera_object))
|
|
539
|
+
set_time(camera_object)
|
|
540
|
+
|
|
541
|
+
# Take picture
|
|
542
|
+
camera_settings = CameraSettings(camera[0], "1/1000", "8", 100)
|
|
543
|
+
|
|
544
|
+
take_picture(camera_object, camera_settings)
|
|
545
|
+
|
|
546
|
+
time.sleep(1)
|
|
547
|
+
camera_settings = CameraSettings(camera[0], "1/200", "6.3", 400)
|
|
548
|
+
# take_bracket(camera_object, camera_settings, "+/- 1 2/3")
|
|
549
|
+
take_picture(camera_object, camera_settings)
|
|
550
|
+
|
|
551
|
+
# Mirror lock
|
|
552
|
+
# mirror_lock(camera_object, camera_settings)
|
|
553
|
+
|
|
554
|
+
# take_picture(camera_object, camera_settings)
|
|
555
|
+
|
|
556
|
+
time.sleep(1)
|
|
557
|
+
camera_settings = CameraSettings(camera[0], "1/4000", "5.6", 200)
|
|
558
|
+
take_burst(camera_object, camera_settings, 1)
|
|
559
|
+
time.sleep(3)
|
|
560
|
+
camera_object.exit()
|
|
561
|
+
|
|
562
|
+
except gphoto2.GPhoto2Error:
|
|
563
|
+
print("Could not connect to the camera. Did you start Solar Eclipse Workbench in sudo mode?")
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
if __name__ == "__main__":
|
|
567
|
+
main()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
def execute_command(command: str) -> None:
|
|
5
|
+
""" Executes a command.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
- command: Command to execute, as a string. The command should be a valid bash command.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Execute a bash command in the shell
|
|
12
|
+
logging.info(f"Executing command: {command}")
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
subprocess.run(command, shell=True, check=True)
|
|
16
|
+
except subprocess.CalledProcessError as e:
|
|
17
|
+
logging.error(f"An error occurred while executing the command: {e}")
|
|
18
|
+
raise
|
|
19
|
+
except Exception as e:
|
|
20
|
+
logging.error(f"An unexpected error occurred: {e}")
|
|
21
|
+
raise
|
|
22
|
+
|
|
23
|
+
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Add the GPS, check the port,
|
|
2
|
+
# GPSD_SOCKET="/usr/local/var/gpsd.sock" /usr/local/Cellar/gpsd/3.25/sbin/gpsdctl add /dev/ttys010
|
|
3
|
+
from gpsdclient import GPSDClient
|
|
4
|
+
|
|
5
|
+
# get your data as json strings:
|
|
6
|
+
# with GPSDClient(host="127.0.0.1") as client:
|
|
7
|
+
# for result in client.json_stream():
|
|
8
|
+
# print(result)
|
|
9
|
+
|
|
10
|
+
# or as python dicts (optionally convert time information to `datetime` objects)
|
|
11
|
+
with GPSDClient(host="127.0.0.1") as client:
|
|
12
|
+
# for result in client.json_stream():
|
|
13
|
+
# print(result)
|
|
14
|
+
for result in client.dict_stream(convert_datetime=True, filter=["TPV"]):
|
|
15
|
+
print("Latitude: %s" % result.get("lat", "n/a"))
|
|
16
|
+
print("Longitude: %s" % result.get("lon", "n/a"))
|
|
17
|
+
|
|
18
|
+
# you can optionally filter by report class
|
|
19
|
+
with GPSDClient() as client:
|
|
20
|
+
for result in client.dict_stream(filter=["TPV", "SKY"]):
|
|
21
|
+
print(result)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import time, pynmea2
|
|
2
|
+
|
|
3
|
+
import serial
|
|
4
|
+
|
|
5
|
+
port = '/dev/serial0'
|
|
6
|
+
port = '/dev/tty'
|
|
7
|
+
baud = 9600
|
|
8
|
+
|
|
9
|
+
serialPort = serial.Serial(port, timeout=0.5)
|
|
10
|
+
while True:
|
|
11
|
+
|
|
12
|
+
str = ''
|
|
13
|
+
try:
|
|
14
|
+
str = serialPort.readline().decode().strip()
|
|
15
|
+
print (str)
|
|
16
|
+
except Exception as e:
|
|
17
|
+
print(e)
|
|
18
|
+
# print(str)
|
|
19
|
+
|
|
20
|
+
if str.find('GGA') > 0:
|
|
21
|
+
try:
|
|
22
|
+
msg = pynmea2.parse(str)
|
|
23
|
+
print(msg.timestamp, 'Lat:', round(msg.latitude, 6), 'Lon:', round(msg.longitude, 6), 'Alt:', msg.altitude,
|
|
24
|
+
'Sats:', msg.num_sats)
|
|
25
|
+
except Exception as e:
|
|
26
|
+
print(e)
|
|
27
|
+
time.sleep(0.1)
|