Skip to content

Commit

Permalink
Merge pull request #77 from scriptorron/monochrome_and_pixelsize_in_p…
Browse files Browse the repository at this point in the history
…rocessed

Monochrome and pixelsize in processed
  • Loading branch information
scriptorron authored Nov 30, 2024
2 parents 71b841e + 502447a commit 4bff74c
Show file tree
Hide file tree
Showing 15 changed files with 152 additions and 80 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
2.7.0
- implemented Mono frame
- scaled RGB and Mono frames have now binning-factor in FITS header
- minor code cleanup and optimization
- minimized FITS metadata to avoid trouble with plate solver
- moved folders CamerInfos and testpattern out of Python library

2.6.5
- running old driver gets killed when started with `python3` and `python`
- fixed typo in label "Disconnect"
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 0 additions & 3 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@

include src/indi_pylibcamera/CameraInfos/*
include src/indi_pylibcamera/testpattern/*
include src/indi_pylibcamera/indi_pylibcamera.ini
include src/indi_pylibcamera/indi_pylibcamera.xml
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ every frame exposure can solve this issue. Valid values of this switch are:
Default (if not otherwise set in INI file) is `auto`.
- `enable_IERS_autoupdate` (`yes`, `no`): Allows the `astropy` library to update the IERS-A table from internet.
By default this is disabled to avoid errors when the camera is not connected to internet.
- `extended_Metadata` (`yes`, `no`, `on`, `off`, `true`, `false`, `1`, `0`, default: `false`): Adds more metadata to the FITS image. For
instance it stores `SCALE` (angle of sky projected to pixel) and `XPIXSZ`/`YPIXSZ` (binned pixel size). When disabled
the pixel sizes `PIXSIZE1`/`PIXSIZE2` get adjusted to the binning. That makes the images look like from a camera
without binning and avoids many issues with plate solvers.

There are more settings, mostly to support debugging.

Expand Down Expand Up @@ -186,6 +190,21 @@ If you get a line containing `python3` and `indi_pylibcamera` in the output the
kill the driver process manually before you restart the indiserver. Otherwise, you will get a libcamera error
when connecting to the camera.

## Frametypes
The driver can (when supported by the camera hardware) provide these image frame types:
- `Raw` is the raw signal coming from the pixel converted to digital. Most cameras have an analog amplifier
(configurable with `Gain`) between the pixel and the A/D converter. There is no software processing of the data.
Typically, these pixel data have higher resolution but suffer from offset and gain errors. Furthermore, the pixel of
color cameras have own color filter (called Bayer pattern) and do not RGB data directly. Such raw images need
post-processing. Astro-photographs like raw images because they allow much better image optimizations. The frame size
of raw images is determined by the modes implemented in the camera hardware.
- `RGB` are images post-processed by the Image Signal Processor (ISP). The ISP corrects for offset and gain,
calculates the colors and can adjust exposure time and wide balance automatically. Drawback is the lower dynamic
(lower bit width) and additional systematic "noise" due to rounding errors. Frame size can be chosen freely because
the image scaling is done by software in the ISP.
- `Mono` is a special case of the `RGB` images, exposed with saturation=0 and transmitted with only one channel per
pixel.

## Special handling for some cameras
The driver is made as generic as possible by using the camera information provided by libcamera. For instance the raw
modes and frame sizes selectable in the driver are coming from libcamera. Unfortunately some important information
Expand Down
129 changes: 88 additions & 41 deletions src/indi_pylibcamera/CameraControl.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/indi_pylibcamera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
INDI driver for libcamera supported cameras
"""

__version__ = "2.6.5"
__version__ = "2.7.0"
44 changes: 23 additions & 21 deletions src/indi_pylibcamera/indi_pylibcamera.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def __init__(self, parent):
ISwitch(name="DISCONNECT", label="Disconnect", value=ISwitchState.ON),
],
label="Connection", group="Main Control",
rule=ISwitchRule.ONEOFMANY, is_savable=False,
rule=ISwitchRule.ONEOFMANY, is_storable=False,
)

def set_byClient(self, values: dict):
Expand Down Expand Up @@ -146,7 +146,7 @@ def __init__(self, parent, min_exp, max_exp):
INumber(name="CCD_EXPOSURE_VALUE", label="Duration (s)", min=min_exp / 1e6, max=max_exp / 1e6,
step=0.001, value=1.0, format="%.3f"),
],
label="Expose", group="Main Control", is_savable=False,
label="Expose", group="Main Control", is_storable=False,
)

def set_byClient(self, values: dict):
Expand Down Expand Up @@ -233,10 +233,12 @@ def __init__(self, parent, CameraThread):
elements = [
ISwitch(name="INDI_RAW", label="RAW", value=ISwitchState.ON),
ISwitch(name="INDI_RGB", label="RGB", value=ISwitchState.OFF),
ISwitch(name="INDI_MONO", label="Mono", value=ISwitchState.OFF),
]
else:
elements = [
ISwitch(name="INDI_RGB", label="RGB", value=ISwitchState.ON),
ISwitch(name="INDI_MONO", label="Mono", value=ISwitchState.OFF),
]
super().__init__(
device=self.parent.device, timestamp=self.parent.timestamp, name="CCD_CAPTURE_FORMAT",
Expand Down Expand Up @@ -286,7 +288,7 @@ def __init__(self, parent, CameraThread, do_CameraAdjustments):
INumber(name="HOR_BIN", label="X", min=1, max=max_HOR_BIN, step=1, value=1, format="%2.0f"),
INumber(name="VER_BIN", label="Y", min=1, max=max_VER_BIN, step=1, value=1, format="%2.0f"),
],
label="Binning", group="Image Settings",
label="Raw binning", group="Image Settings",
state=IVectorState.IDLE, perm=IPermission.RW,
)

Expand Down Expand Up @@ -374,7 +376,7 @@ def __init__(self, parent):
IText(name="KEYWORD_VALUE", label="Value", value=""),
IText(name="KEYWORD_COMMENT", label="Comment", value=""),
],
label="FITS Header", group="General Info", perm=IPermission.WO, is_savable=False,
label="FITS Header", group="General Info", perm=IPermission.WO, is_storable=False,
)

def set_byClient(self, values: dict):
Expand Down Expand Up @@ -418,7 +420,7 @@ def __init__(self, parent):
ISwitch(name="ABORT", label="Abort", value=ISwitchState.OFF),
],
label="Abort", group="Main Control",
rule=ISwitchRule.ATMOST1, is_savable=False,
rule=ISwitchRule.ATMOST1, is_storable=False,
)

def set_byClient(self, values: dict):
Expand All @@ -441,7 +443,7 @@ def __init__(self, parent):
ISwitch(name="PRINT_SNOOPED", label="Print", value=ISwitchState.OFF),
],
label="Print snooped values", group="Snooping",
rule=ISwitchRule.ATMOST1, is_savable=False,
rule=ISwitchRule.ATMOST1, is_storable=False,
)

def set_byClient(self, values: dict):
Expand Down Expand Up @@ -471,7 +473,7 @@ def __init__(self, parent):
ISwitch(name="CONFIG_PURGE", label="Purge", value=ISwitchState.OFF),
],
label="Configuration", group="Options",
rule=ISwitchRule.ATMOST1, is_savable=False,
rule=ISwitchRule.ATMOST1, is_storable=False,
)

def set_byClient(self, values: dict):
Expand Down Expand Up @@ -594,7 +596,7 @@ def __init__(self, config=None):
) for i in range(len(self.Cameras))
],
label="Camera", group="Main Control",
rule=ISwitchRule.ONEOFMANY, is_savable=False,
rule=ISwitchRule.ONEOFMANY, is_storable=False,
)
)
self.checkin(
Expand All @@ -610,7 +612,7 @@ def __init__(self, config=None):
IText(name="DRIVER_INTERFACE", label="Interface", value="2"), # This is a CCD!
],
label="Driver Info", group="General Info",
perm=IPermission.RO, is_savable=False,
perm=IPermission.RO, is_storable=False,
)
)
self.checkin(
Expand All @@ -637,7 +639,7 @@ def __init__(self, config=None):
INumber(name="ELEV", label="Elevation (m)", min=-200, max=10000, step=0, value=0, format="%g"),
],
label="Scope Location", group="Snooping",
perm=IPermission.RW, is_savable=False,
perm=IPermission.RW, is_storable=False,
),
)
self.checkin(
Expand All @@ -648,7 +650,7 @@ def __init__(self, config=None):
INumber(name="DEC", label="DEC (dd:mm:ss)", min=-90, max=90, step=0, value=0, format="%010.6m"),
],
label="Eq. Coordinates", group="Snooping",
perm=IPermission.RW, is_savable=False,
perm=IPermission.RW, is_storable=False,
),
)
# TODO: "EQUATORIAL_COORD" (J2000 coordinates from mount) are not used!
Expand All @@ -661,7 +663,7 @@ def __init__(self, config=None):
INumber(name="DEC", label="DEC (dd:mm:ss)", min=-90, max=90, step=0, value=0, format="%010.6m"),
],
label="Eq. J2000 Coordinates", group="Snooping",
perm=IPermission.RW, is_savable=False,
perm=IPermission.RW, is_storable=False,
),
)
self.checkin(
Expand All @@ -672,7 +674,7 @@ def __init__(self, config=None):
ISwitch(name="PIER_EAST", value=ISwitchState.OFF, label="East (pointing west)"),
],
label="Pier Side", group="Snooping",
rule=ISwitchRule.ONEOFMANY, is_savable=False,
rule=ISwitchRule.ONEOFMANY, is_storable=False,
)
)
self.checkin(
Expand Down Expand Up @@ -749,7 +751,7 @@ def openCamera(self):
IText(name="CAMERA_UNITCELLSIZE", label="Pixel size", value=str(self.CameraThread.getProp("UnitCellSize"))),
],
label="Camera Info", group="General Info",
state=IVectorState.OK, perm=IPermission.RO, is_savable=False,
state=IVectorState.OK, perm=IPermission.RO, is_storable=False,
),
send_defVector=True,
)
Expand Down Expand Up @@ -780,7 +782,7 @@ def openCamera(self):
INumber(name="HEIGHT", label="Height", min=1, max=self.CameraThread.getProp("PixelArraySize")[1],
step=0, value=self.CameraThread.getProp("PixelArraySize")[1], format="%4.0f"),
],
label="RGB format", group="Image Settings",
label="RGB, Mono", group="Image Settings",
perm=IPermission.RW,
),
send_defVector=True,
Expand Down Expand Up @@ -814,7 +816,7 @@ def openCamera(self):
step=0, value=self.CameraThread.getProp("PixelArraySize")[1], format="%4.0f"),
],
label="Frame", group="Image Info",
perm=IPermission.RO, is_savable=False, # TODO: make it savable after implementing frame cropping
perm=IPermission.RO, is_storable=False, # TODO: make it available after implementing frame cropping
),
send_defVector=True,
)
Expand All @@ -827,7 +829,7 @@ def openCamera(self):
ISwitch(name="RESET", label="Reset", value=ISwitchState.OFF),
],
label="Frame Values", group="Image Settings",
rule=ISwitchRule.ONEOFMANY, perm=IPermission.WO, is_savable=False,
rule=ISwitchRule.ONEOFMANY, perm=IPermission.WO, is_storable=False,
),
send_defVector=True,
)
Expand Down Expand Up @@ -856,7 +858,7 @@ def openCamera(self):
INumber(name="CCD_TEMPERATURE_VALUE", label="Temperature (C)", min=-50, max=50, step=0, value=0, format="%5.2f"),
],
label="Temperature", group="Main Control",
state=IVectorState.IDLE, perm=IPermission.RO, is_savable=False,
state=IVectorState.IDLE, perm=IPermission.RO, is_storable=False,
),
send_defVector=True,
)
Expand All @@ -881,7 +883,7 @@ def openCamera(self):
value=8 if len(self.CameraThread.RawModes) < 1 else self.CameraThread.RawModes[0]["bit_depth"], format="%.f"),
],
label="CCD Information", group="Image Info",
state=IVectorState.IDLE, perm=IPermission.RO, is_savable=False,
state=IVectorState.IDLE, perm=IPermission.RO, is_storable=False,
),
send_defVector=True,
)
Expand Down Expand Up @@ -913,7 +915,7 @@ def openCamera(self):
IBlob(name="CCD1", label="Image"),
],
label="Image Data", group="Image Info",
state=IVectorState.OK, perm=IPermission.RO, is_savable=False,
state=IVectorState.OK, perm=IPermission.RO, is_storable=False,
),
send_defVector=True,
)
Expand Down Expand Up @@ -983,7 +985,7 @@ def openCamera(self):
elements=[
INumber(name="FRAMES", label="Frames", min=0, max=100000, step=1, value=1, format="%.f"),
],
label="Fast Count", group="Main Control", is_savable=False,
label="Fast Count", group="Main Control", is_storable=False,
),
send_defVector=True,
)
Expand Down
2 changes: 1 addition & 1 deletion src/indi_pylibcamera/indi_pylibcamera.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<devGroup group="CCDs">
<device label="INDI pylibcamera">
<driver name="INDI pylibcamera">indi_pylibcamera</driver>
<version>2.6.5</version>
<version>2.7.0</version>
</device>
</devGroup>
</driversList>
26 changes: 13 additions & 13 deletions src/indi_pylibcamera/indidevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def __init__(
label: str = None, group: str = "",
state: str = IVectorState.IDLE, perm: str = IPermission.RW,
timeout: int = 60, timestamp: bool = False, message: str = None,
is_savable: bool = True,
is_storable: bool = True,
):
"""constructor
Expand All @@ -169,7 +169,7 @@ def __init__(
timeout: timeout
timestamp: send messages with (True) or without (False) timestamp
message: message send to client
is_savable: can be saved
is_storable: can be saved
"""
self._vectorType = "NotSet"
self.device = device
Expand All @@ -186,7 +186,7 @@ def __init__(
self.timeout = timeout
self.timestamp = timestamp
self.message = message
self.is_savable = is_savable
self.is_storable = is_storable

def __str__(self) -> str:
return f"<Vector {self._vectorType} name={self.name}, device={self.device}>"
Expand Down Expand Up @@ -341,7 +341,7 @@ def save(self):
dict with Vector state
"""
state = None
if self.is_savable:
if self.is_storable:
state = dict()
state["name"] = self.name
state["values"] = {element.name: element.value for element in self.elements}
Expand All @@ -350,7 +350,7 @@ def save(self):
def restore_DriverDefault(self):
"""restore driver defaults for savable vector
"""
if self.is_savable:
if self.is_storable:
self.set_byClient(self.driver_default)


Expand Down Expand Up @@ -390,11 +390,11 @@ def __init__(
label: str = None, group: str = "",
state: str = IVectorState.IDLE, perm: str = IPermission.RW,
timeout: int = 60, timestamp: bool = False, message: str = None,
is_savable: bool = True,
is_storable: bool = True,
):
super().__init__(
device=device, name=name, elements=elements, label=label, group=group,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_savable=is_savable,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_storable=is_storable,
)
self._vectorType = "TextVector"

Expand Down Expand Up @@ -444,11 +444,11 @@ def __init__(
label: str = None, group: str = "",
state: str = IVectorState.IDLE, perm: str = IPermission.RW,
timeout: int = 60, timestamp: bool = False, message: str = None,
is_savable: bool = True,
is_storable: bool = True,
):
super().__init__(
device=device, name=name, elements=elements, label=label, group=group,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_savable=is_savable,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_storable=is_storable,
)
self._vectorType = "NumberVector"

Expand Down Expand Up @@ -490,11 +490,11 @@ def __init__(
state: str = IVectorState.IDLE, perm: str = IPermission.RW,
rule: str = ISwitchRule.ONEOFMANY,
timeout: int = 60, timestamp: bool = False, message: str = None,
is_savable: bool = True,
is_storable: bool = True,
):
super().__init__(
device=device, name=name, elements=elements, label=label, group=group,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_savable=is_savable,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_storable=is_storable,
)
self._vectorType = "SwitchVector"
self.rule = rule
Expand Down Expand Up @@ -629,11 +629,11 @@ def __init__(
label: str = None, group: str = "",
state: str = IVectorState.IDLE, perm: str = IPermission.RO,
timeout: int = 60, timestamp: bool = False, message: str = None,
is_savable: bool = True,
is_storable: bool = True,
):
super().__init__(
device=device, name=name, elements=elements, label=label, group=group,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_savable=is_savable,
state=state, perm=perm, timeout=timeout, timestamp=timestamp, message=message, is_storable=is_storable,
)
self._vectorType = "BLOBVector"

Expand Down
File renamed without changes

0 comments on commit 4bff74c

Please sign in to comment.