pyobs-gemini 1.0.1__tar.gz → 2.0.0.dev1__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.
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyobs-gemini
3
+ Version: 2.0.0.dev1
4
+ Summary: pyobs module for Optec Gemini focusser/rotator
5
+ Author: Tim-Oliver Husser, Frederic V. Hessman
6
+ Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>, Frederic V. Hessman <fhessma@uni-goettingen.de>
7
+ License-Expression: MIT
8
+ Requires-Dist: aioserial>=1.3,<2
9
+ Requires-Dist: astropy>=7.0.1,<8
10
+ Requires-Dist: numpy>=2.2.5,<3
11
+ Requires-Dist: pyobs-core>=2.0.0.dev38,<3
12
+ Requires-Python: >=3.11, <3.14
@@ -0,0 +1,3 @@
1
+ from .gemini import GeminiFocuserRotator as GeminiFocuserRotator
2
+
3
+ __all__ = ["GeminiFocuserRotator"]
@@ -21,10 +21,11 @@ The GEMINI responses have the syntax:
21
21
  {END|SET}
22
22
 
23
23
  """
24
- from typing import Dict, Any, Tuple, List
24
+
25
+ from typing import Any
25
26
 
26
27
  # A COMMAND CONSISTS OF name,device,command_string,input_format,output_format,end_string,help
27
- gemini_commands: Dict[str, Dict[str, Any]] = {
28
+ gemini_commands: dict[str, dict[str, Any]] = {
28
29
  "CENTER": {
29
30
  "cmd": "center",
30
31
  "device": "F",
@@ -238,25 +239,25 @@ def gemini_cmd(cmd: str, *args: Any, dev: str = "", trans_id: int = 0) -> str:
238
239
  transaction ID, and whatever input args are needed.
239
240
  """
240
241
  if cmd not in gemini_commands:
241
- raise ValueError("{0} is not a GEMINI command!".format(cmd))
242
+ raise ValueError(f"{cmd} is not a GEMINI command!")
242
243
  info = gemini_commands[cmd]
243
244
  if dev not in info["device"]:
244
- raise ValueError("{0} does not match GEMINI device {1}".format(dev, info["device"]))
245
+ raise ValueError("{} does not match GEMINI device {}".format(dev, info["device"]))
245
246
 
246
- s1 = "<{0}1{1:02d}".format(dev, trans_id)
247
+ s1 = f"<{dev}1{trans_id:02d}"
247
248
  s2 = cmd
248
249
  if info["format"] is None:
249
250
  s3 = ""
250
251
  else:
251
252
  try:
252
253
  s3 = info["format"].format(*args)
253
- except:
254
- raise ValueError("format {0} does not work for {1}".format(info["format"], cmd))
254
+ except Exception:
255
+ raise ValueError(f"format {info['format']} does not work for {cmd}")
255
256
  s4 = ">"
256
257
  return s1 + s2 + s3 + s4
257
258
 
258
259
 
259
- def gemini_parse_output(cmd: str, raw_response: List[bytes]) -> Tuple[Dict[str, Any], Dict[str, Any]]:
260
+ def gemini_parse_output(cmd: str, raw_response: list[bytes]) -> tuple[dict[str, Any], dict[str, Any]]:
260
261
  """
261
262
  Parse the raw GEMINI output, a list of bytearray's from serial.readline,
262
263
  into a dictionary.
@@ -269,13 +270,13 @@ def gemini_parse_output(cmd: str, raw_response: List[bytes]) -> Tuple[Dict[str,
269
270
  info = gemini_commands[cmd]
270
271
  if "end" not in info:
271
272
  raise IndexError("ending is not in info!")
272
- results: Dict[str, Any] = {}
273
+ results: dict[str, Any] = {}
273
274
 
274
275
  # decode and remove newlines
275
276
  try:
276
277
  rsp = [r.decode("utf8").replace("\\n", "") for r in raw_response]
277
278
  except UnicodeDecodeError:
278
- raise ValueError("{0!r} cannot be decoded".format(raw_response))
279
+ raise ValueError(f"{raw_response!r} cannot be decoded")
279
280
 
280
281
  # check for syntax
281
282
  if len(rsp) == 0 or not rsp[0].strip().startswith("!"):
@@ -284,7 +285,7 @@ def gemini_parse_output(cmd: str, raw_response: List[bytes]) -> Tuple[Dict[str,
284
285
  # get transaction ID
285
286
  try:
286
287
  trans_id = int(rsp[0].strip()[1:])
287
- except:
288
+ except ValueError:
288
289
  trans_id = None
289
290
  results["trans_id"] = trans_id
290
291
  errors = {}
@@ -1,20 +1,30 @@
1
1
  import asyncio
2
2
  import logging
3
- from typing import Optional, List, Any, Dict, Tuple
3
+ from typing import Any
4
+
5
+ import astropy.units as u
4
6
  import numpy as np
5
7
  from astropy.coordinates import SkyCoord
6
- import astropy.units as u
7
-
8
- from pyobs.events import MotionStatusChangedEvent, Event
8
+ from pyobs.events import Event, MotionStatusChangedEvent
9
+ from pyobs.interfaces import (
10
+ FitsHeaderEntry,
11
+ FocuserState,
12
+ ICalibrate,
13
+ IFitsHeaderBefore,
14
+ IFocuser,
15
+ IPointingRaDec,
16
+ IRotation,
17
+ RaDecState,
18
+ RotationState,
19
+ )
9
20
  from pyobs.mixins import FitsNamespaceMixin, MotionStatusMixin
10
21
  from pyobs.modules import Module, timeout
11
22
  from pyobs.utils.enums import MotionStatus
12
23
  from pyobs.utils.parallel import event_wait
13
24
  from pyobs.utils.threads import LockWithAbort
14
- from pyobs.interfaces import IRotation, IMotion, IFocuser, ICalibrate, IFitsHeaderBefore, IPointingRaDec
15
25
  from pyobs.utils.time import Time
16
26
 
17
- from .geminidriver import GeminiDriver, GeminiCommException, Vocab
27
+ from .geminidriver import GeminiCommException, GeminiDriver, Vocab
18
28
 
19
29
  log = logging.getLogger(__name__)
20
30
 
@@ -33,8 +43,8 @@ class GeminiFocuserRotator(
33
43
 
34
44
  def __init__(
35
45
  self,
36
- serial_config: Optional[Dict[str, Any]] = None,
37
- fits_config: Optional[Dict[str, Any]] = None,
46
+ serial_config: dict[str, Any] | None = None,
47
+ fits_config: dict[str, Any] | None = None,
38
48
  focus_offset: float = 0.0,
39
49
  rotation_offset: float = 0.0,
40
50
  *args: Any,
@@ -60,7 +70,7 @@ class GeminiFocuserRotator(
60
70
  # ROTATOR STUFF
61
71
  self._rotation_lock = asyncio.Lock()
62
72
  self._rotation_abort = asyncio.Event()
63
- self._skycoord: Optional[SkyCoord] = None
73
+ self._skycoord: SkyCoord | None = None
64
74
  self._rotation_accur = 0.0 # DEG
65
75
  self._rotation_offset = rotation_offset
66
76
 
@@ -78,7 +88,7 @@ class GeminiFocuserRotator(
78
88
  self._serial_config = serial_config
79
89
 
80
90
  # driver
81
- self._driver: Optional[GeminiDriver] = None
91
+ self._driver: GeminiDriver | None = None
82
92
 
83
93
  # FITS HEADER CONFIGURATION
84
94
  if fits_config is None:
@@ -111,8 +121,7 @@ class GeminiFocuserRotator(
111
121
  # subscribe to events
112
122
  if self.comm:
113
123
  if self.follow:
114
- # self.comm.register_event(TelescopeMovingEvent, self._telescope_event)
115
- self.comm.register_event(MotionStatusChangedEvent, self._telescope_event)
124
+ await self.comm.register_event(MotionStatusChangedEvent, self._telescope_event)
116
125
 
117
126
  # create driver and open it
118
127
  self._driver = GeminiDriver(**self._serial_config)
@@ -198,11 +207,15 @@ class GeminiFocuserRotator(
198
207
  # TODO: find out
199
208
  # self._T = fdict.response[Vocab.CURRENT_TEMP.value]
200
209
 
210
+ # publish current focus/rotation state
211
+ await self.comm.set_state(IFocuser, FocuserState(focus=self.focus, focus_offset=self._focus_offset))
212
+ await self.comm.set_state(IRotation, RotationState(rotation=self.rotation))
213
+
201
214
  # get motion status
202
215
  await self._change_motion_status(self._motion_status(fdict.response), interface="IFocuser")
203
216
  await self._change_motion_status(self._motion_status(rdict.response), interface="IRotation")
204
217
 
205
- def _motion_status(self, stat: Dict[str, Any]) -> MotionStatus:
218
+ def _motion_status(self, stat: dict[str, Any]) -> MotionStatus:
206
219
  """
207
220
  Extracts the IMotion status from a dictionary returned
208
221
  by the driver's status method.
@@ -243,7 +256,7 @@ class GeminiFocuserRotator(
243
256
  await event_wait(self._focus_abort, 1)
244
257
  await self._update_status()
245
258
 
246
- while not self._focus_abort.is_set() and await self.get_motion_status("IFocuser") == MotionStatus.SLEWING:
259
+ while not self._focus_abort.is_set() and self.motion_status("IFocuser") == MotionStatus.SLEWING:
247
260
  # sleep a little
248
261
  await event_wait(self._focus_abort, 1)
249
262
 
@@ -254,14 +267,6 @@ class GeminiFocuserRotator(
254
267
  # success
255
268
  log.info("Successfully set new focus.")
256
269
 
257
- async def get_focus(self, **kwargs: Any) -> float:
258
- """Return current focus.
259
-
260
- Returns:
261
- Current focus.
262
- """
263
- return self.focus
264
-
265
270
  @timeout(300000)
266
271
  async def set_rotation(self, angle: float, **kwargs: Any) -> None:
267
272
  """Sets the rotation angle to the given value in degrees."""
@@ -281,9 +286,7 @@ class GeminiFocuserRotator(
281
286
  await event_wait(self._rotation_abort, 1)
282
287
  await self._update_status()
283
288
 
284
- while (
285
- not self._rotation_abort.is_set() and await self.get_motion_status("IRotation") == MotionStatus.SLEWING
286
- ):
289
+ while not self._rotation_abort.is_set() and self.motion_status("IRotation") == MotionStatus.SLEWING:
287
290
  # sleep a little
288
291
  await event_wait(self._rotation_abort, 1)
289
292
 
@@ -294,11 +297,7 @@ class GeminiFocuserRotator(
294
297
  # success
295
298
  log.info("Successfully set new rotation.")
296
299
 
297
- async def get_rotation(self, **kwargs: Any) -> float:
298
- """Returns the current rotation angle."""
299
- return await self._driver.get_rotation()
300
-
301
- async def stop_motion(self, device: Optional[str] = None, **kwargs: Any) -> None:
300
+ async def stop_motion(self, device: str | None = None, **kwargs: Any) -> None:
302
301
  """Stop the motion.
303
302
 
304
303
  Args:
@@ -320,7 +319,7 @@ class GeminiFocuserRotator(
320
319
 
321
320
  # valid coordinates?
322
321
  if ra < 0.0 or ra > 360.0 or np.abs(dec) >= 90.0:
323
- raise ValueError("RA, Dec out of limits (%.2f, %.2f)." % (ra, dec))
322
+ raise ValueError(f"RA, Dec out of limits ({ra:.2f}, {dec:.2f}).")
324
323
  skycoord = SkyCoord(ra * u.deg, dec * u.deg, frame="icrs")
325
324
 
326
325
  # get parallactic angle
@@ -331,14 +330,12 @@ class GeminiFocuserRotator(
331
330
 
332
331
  # start tracking and log it
333
332
  self._skycoord = skycoord
333
+ await self.comm.set_state(IPointingRaDec, RaDecState(ra=ra, dec=dec))
334
334
  log.info(
335
335
  "Started target tracking of parallactic angle at %s...",
336
336
  self._skycoord.to_string(),
337
337
  )
338
338
 
339
- async def get_radec(self, **kwargs: Any) -> Tuple[float, float]:
340
- return 0.0, 0.0
341
-
342
339
  async def _rotation_tracker_func(self) -> None:
343
340
  if self._driver is None:
344
341
  return
@@ -352,11 +349,8 @@ class GeminiFocuserRotator(
352
349
  # get parallactic angle
353
350
  pa = self.observer.parallactic_angle(Time.now(), self._skycoord).degree
354
351
 
355
- # get rotation
356
- rot = await self.get_rotation()
357
-
358
- # need to rotate?
359
- if np.abs(pa - rot) > self._rotation_accur:
352
+ # need to rotate? (self.rotation is kept fresh by _gdriver_update_func)
353
+ if np.abs(pa - self.rotation) > self._rotation_accur:
360
354
  # rotate
361
355
  await self._driver.set_rotation(pa + self._rotation_offset)
362
356
 
@@ -364,8 +358,8 @@ class GeminiFocuserRotator(
364
358
  await asyncio.sleep(1)
365
359
 
366
360
  async def get_fits_header_before(
367
- self, namespaces: Optional[List[str]] = None, **kwargs: Any
368
- ) -> Dict[str, Tuple[Any, str]]:
361
+ self, namespaces: list[str] | None = None, **kwargs: Any
362
+ ) -> dict[str, FitsHeaderEntry]:
369
363
  """Returns FITS header for the current status of this module.
370
364
 
371
365
  Args:
@@ -374,34 +368,34 @@ class GeminiFocuserRotator(
374
368
  Returns:
375
369
  Dictionary containing FITS headers.
376
370
  """
377
- hdr = {}
371
+ hdr: dict[str, FitsHeaderEntry] = {}
378
372
 
379
373
  # SET FOCUS HEADERS
380
374
  if "focus" in self._fits_config:
381
375
  key, comment = self._fits_config["focus"]
382
- hdr[key] = self.focus, comment
376
+ hdr[key] = FitsHeaderEntry(value=self.focus, comment=comment)
383
377
  if "focus-motion" in self._fits_config:
384
378
  key, comment = self._fits_config["focus-motion"]
385
- hdr[key] = (await self.get_motion_status("IFocuser")).value, comment
379
+ hdr[key] = FitsHeaderEntry(value=self.motion_status("IFocuser").value, comment=comment)
386
380
  if "focus-offset" in self._fits_config:
387
381
  key, comment = self._fits_config["focus-offset"]
388
- hdr[key] = self._focus_offset, comment
382
+ hdr[key] = FitsHeaderEntry(value=self._focus_offset, comment=comment)
389
383
 
390
384
  # SET ROTATION HEADERS
391
385
  if "rotation" in self._fits_config:
392
386
  key, comment = self._fits_config["rotation"]
393
- hdr[key] = self.rotation, comment
387
+ hdr[key] = FitsHeaderEntry(value=self.rotation, comment=comment)
394
388
  if "rotation-motion" in self._fits_config:
395
389
  key, comment = self._fits_config["rotation-motion"]
396
- hdr[key] = (await self.get_motion_status("IRotation")).value, comment
390
+ hdr[key] = FitsHeaderEntry(value=self.motion_status("IRotation").value, comment=comment)
397
391
  if "rotation-offset" in self._fits_config:
398
392
  key, comment = self._fits_config["rotation-offset"]
399
- hdr[key] = self._rotation_offset, comment
393
+ hdr[key] = FitsHeaderEntry(value=self._rotation_offset, comment=comment)
400
394
 
401
395
  # TEMPERATURE SENSOR
402
396
  if "temperature" in self._fits_config:
403
397
  key, comment = self._fits_config["temperature"]
404
- hdr[key] = self._T, comment
398
+ hdr[key] = FitsHeaderEntry(value=self._T, comment=comment)
405
399
 
406
400
  # return it
407
401
  return self._filter_fits_namespace(hdr, namespaces=namespaces, **kwargs)
@@ -418,14 +412,6 @@ class GeminiFocuserRotator(
418
412
  """
419
413
  pass
420
414
 
421
- async def get_focus_offset(self, **kwargs: Any) -> float:
422
- """Return current focus offset.
423
-
424
- Returns:
425
- Current focus offset.
426
- """
427
- return 0.0
428
-
429
415
  async def init(self, **kwargs: Any) -> None:
430
416
  """Initialize device.
431
417
 
@@ -442,19 +428,11 @@ class GeminiFocuserRotator(
442
428
  """
443
429
  pass
444
430
 
445
- async def is_ready(self, **kwargs: Any) -> bool:
446
- """Returns the device is "ready", whatever that means for the specific device.
447
-
448
- Returns:
449
- Whether device is ready
450
- """
451
- return True
452
-
453
431
  async def _telescope_event(self, ev: Event, sender: str) -> bool:
454
432
  """Moving events from telescope.
455
433
 
456
434
  Args:
457
- event: Either a MotionStatusChangedEvent or a TelescopeMovingEvent.
435
+ event: A MotionStatusChangedEvent.
458
436
  sender: Who sent it.
459
437
  """
460
438
 
@@ -462,23 +440,8 @@ class GeminiFocuserRotator(
462
440
  if self.follow is None or self.follow != sender:
463
441
  return False
464
442
 
465
- # what kind of event is it?
466
- if isinstance(ev, TelescopeMovingEvent):
467
- # do we have RA/Dec?
468
- if ev.ra is not None and ev.dec is not None:
469
- # start tracking!
470
- log.info("Received an event that telescope is about to start tracking. Following it... ")
471
- await self.track(ev.ra, ev.dec)
472
-
473
- else:
474
- # presumably slewing to fixed coordinates
475
- # are we currently tracking?
476
- if self._skycoord is not None:
477
- log.info("Received event that telescope is not tracking anymore, stopping derotator movement...")
478
- await self.stop_motion("IRotation")
479
-
480
- elif isinstance(ev, MotionStatusChangedEvent):
481
- # we want the ITelescope event and anything except TRACKING and SLEWING (might end up in race condition)
443
+ # we want the ITelescope event and anything except TRACKING and SLEWING (might end up in race condition)
444
+ if isinstance(ev, MotionStatusChangedEvent):
482
445
  if "ITelescope" in ev.interfaces and ev.interfaces["ITelescope"] not in [
483
446
  MotionStatus.TRACKING.value,
484
447
  MotionStatus.SLEWING.value,
@@ -489,5 +452,7 @@ class GeminiFocuserRotator(
489
452
  log.info("Received event that telescope is not tracking anymore, stopping derotator movement...")
490
453
  await self.stop_motion("IRotation")
491
454
 
455
+ return True
456
+
492
457
 
493
458
  __all__ = ["GeminiFocuserRotator"]
@@ -1,13 +1,14 @@
1
1
  import asyncio
2
- import enum
3
2
  import datetime
3
+ import enum
4
4
  import logging
5
- from typing import Optional, Any, Dict, List, cast
6
- import numpy as np
7
- import aioserial
8
5
  from itertools import chain
6
+ from typing import Any, cast
7
+
8
+ import aioserial
9
+ import numpy as np
9
10
 
10
- from pyobs_gemini.api import gemini_commands, gemini_cmd, gemini_parse_output
11
+ from pyobs_gemini.api import gemini_cmd, gemini_commands, gemini_parse_output
11
12
 
12
13
  log = logging.getLogger(__name__)
13
14
 
@@ -16,20 +17,20 @@ class GeminiTransaction:
16
17
  def __init__(
17
18
  self,
18
19
  tid: int = 0,
19
- cmd: Optional[str] = None,
20
- raw_cmd: Optional[str] = None,
21
- response: Optional[Dict[str, Any]] = None,
22
- errors: Optional[Dict[str, Any]] = None,
20
+ cmd: str | None = None,
21
+ raw_cmd: str | None = None,
22
+ response: dict[str, Any] | None = None,
23
+ errors: dict[str, Any] | None = None,
23
24
  ):
24
25
  """Constructor for a gemini transaction dictionary"""
25
26
  self.transaction = {"id": tid, "cmd": cmd, "raw_cmd": raw_cmd}
26
27
  self.response = {} if response is None else response
27
28
  self.errors = {} if errors is None else errors
28
29
  self.time = datetime.datetime.now()
29
- self.data: Dict[Vocab, Any] = {}
30
+ self.data: dict[Vocab, Any] = {}
30
31
 
31
32
 
32
- def has_transaction_error(d: Optional[GeminiTransaction], keys: Optional[List[str]] = None) -> bool:
33
+ def has_transaction_error(d: GeminiTransaction | None, keys: list[str] | None = None) -> bool:
33
34
  """Is there a problem with this transaction dictionary?"""
34
35
  if d is None:
35
36
  return True
@@ -42,7 +43,7 @@ def has_transaction_error(d: Optional[GeminiTransaction], keys: Optional[List[st
42
43
  return False
43
44
 
44
45
 
45
- def dict_union(*args: Any) -> Dict[str, Any]:
46
+ def dict_union(*args: Any) -> dict[str, Any]:
46
47
  return dict(chain.from_iterable(d.items() for d in args if d is not None))
47
48
 
48
49
 
@@ -96,11 +97,11 @@ class GeminiDriver:
96
97
  self._lock = asyncio.Lock()
97
98
 
98
99
  # init
99
- self._serial: Optional[aioserial.AioSerial] = None
100
+ self._serial: aioserial.AioSerial | None = None
100
101
  self.transID = 0
101
102
  self.transactions = np.array([None for i in range(99)], dtype=dict) # CIRCULAR BUFFER
102
- self.focus_model: Optional[Dict[str, float]] = None
103
- self.rotation_model: Optional[Dict[str, float]] = None
103
+ self.focus_model: dict[str, float] | None = None
104
+ self.rotation_model: dict[str, float] | None = None
104
105
  self.focus_offset = 0.0
105
106
  self.rotation_offset = 0.0
106
107
 
@@ -233,9 +234,7 @@ class GeminiDriver:
233
234
 
234
235
  # conversion between hardware and position angle: deg = offset + scale * millideg
235
236
  # native is 0-360000 milli-degrees, convert to -180 to +180 degrees
236
- min_rsteps = 0
237
237
  max_rsteps = 360000
238
- rrange = 360.0 # DEG
239
238
  self.rotation_model = {
240
239
  "offset": -180.0,
241
240
  "scale": 0.001,
@@ -384,7 +383,7 @@ class GeminiDriver:
384
383
  await self._send_to_gemini("F", "DOHOME")
385
384
  return True
386
385
  except GeminiCommException as e:
387
- log.error("Could not home focus: " + str(e))
386
+ log.error("Could not home focus: %s", e)
388
387
  return False
389
388
 
390
389
  async def focus_is_homed(self) -> bool:
@@ -407,7 +406,7 @@ class GeminiDriver:
407
406
  await self._send_to_gemini("R", "DOHOME")
408
407
  return True
409
408
  except GeminiCommException as e:
410
- log.error("Could not home rotation: " + str(e))
409
+ log.error("Could not home rotation: %s", e)
411
410
  return False
412
411
 
413
412
  async def rotation_is_homed(self) -> bool:
@@ -0,0 +1,46 @@
1
+ [project]
2
+ name = "pyobs-gemini"
3
+ version = "2.0.0.dev1"
4
+ description = "pyobs module for Optec Gemini focusser/rotator"
5
+ authors = [
6
+ { name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" },
7
+ { name = "Frederic V. Hessman", email = "fhessma@uni-goettingen.de" },
8
+ ]
9
+ requires-python = ">=3.11,<3.14"
10
+ license = "MIT"
11
+ dependencies = [
12
+ "aioserial>=1.3,<2",
13
+ "astropy>=7.0.1,<8",
14
+ "numpy>=2.2.5,<3",
15
+ "pyobs-core>=2.0.0.dev38,<3",
16
+ ]
17
+
18
+ [dependency-groups]
19
+ dev = [
20
+ "black>=25.1.0,<26",
21
+ "pre-commit>=4.2.0,<5",
22
+ "ruff>=0.9.0",
23
+ "pyrefly>=1.1.1",
24
+ ]
25
+
26
+ [tool.uv.build-backend]
27
+ module-root = ""
28
+
29
+ [build-system]
30
+ requires = ["uv_build>=0.9.14,<0.10.0"]
31
+ build-backend = "uv_build"
32
+
33
+ [tool.black]
34
+ line-length = 120
35
+ target-version = ['py311']
36
+
37
+ [tool.ruff]
38
+ target-version = "py311"
39
+ line-length = 120
40
+
41
+ [tool.ruff.lint]
42
+ select = ["E", "F", "W", "I", "UP", "G"]
43
+
44
+ [tool.pyrefly]
45
+ python-version = "3.11"
46
+ ignore-missing-imports = []
@@ -1,22 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 Tim-Oliver Husser, Frederic V. Hessman
4
- thusser@uni-goettingen.de
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
@@ -1,16 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: pyobs-gemini
3
- Version: 1.0.1
4
- Summary: pyobs module for Optec Gemini focusser/rotator
5
- License: MIT
6
- Author: Tim-Oliver Husser
7
- Author-email: thusser@uni-goettingen.de
8
- Requires-Python: >=3.9,<3.12
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Requires-Dist: aioserial (>=1.3,<2.0)
15
- Requires-Dist: astropy (>=5.1,<6.0)
16
- Requires-Dist: pyobs-core (>=1.0,<2.0)
@@ -1 +0,0 @@
1
- from .gemini import GeminiFocuserRotator
@@ -1,24 +0,0 @@
1
- [tool.poetry]
2
- name = "pyobs-gemini"
3
- version = "1.0.1"
4
- description = "pyobs module for Optec Gemini focusser/rotator"
5
- authors = ["Tim-Oliver Husser <thusser@uni-goettingen.de>", "Frederic V. Hessman <fhessma@uni-goettingen.de>"]
6
- license = "MIT"
7
-
8
- [tool.poetry.dependencies]
9
- python = ">=3.9,<3.12"
10
- astropy = "^5.1"
11
- aioserial = "^1.3"
12
- pyobs-core = "^1.0"
13
-
14
- [tool.poetry.dev-dependencies]
15
- black = "^21.12b0"
16
- pre-commit = "^2.16"
17
-
18
- [build-system]
19
- requires = ["poetry-core>=1.0.0"]
20
- build-backend = "poetry.core.masonry.api"
21
-
22
- [tool.black]
23
- line-length = 120
24
- target-version = ['py39']