Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 855aeaf

Browse files
committedAug 27, 2024
Rework IMAG-RGN handling to match other daemons.
1 parent 1e5624f commit 855aeaf

File tree

5 files changed

+53
-48
lines changed

5 files changed

+53
-48
lines changed
 

‎andor2_camd

+45-40
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,38 @@ class AndorStatus:
5959
DrvP6Invalid = 20077
6060

6161

62+
def window_sensor_region(region, window):
63+
"""Calculate new region coordinates when cropped to a given window"""
64+
x1 = max(0, region[0] - window[0])
65+
x2 = min(region[1] - window[0], window[1] - window[0])
66+
y1 = max(0, region[2] - window[2])
67+
y2 = min(region[3] - window[2], window[3] - window[2])
68+
if x1 > x2 or y1 > y2:
69+
return None
70+
71+
return [x1, x2, y1, y2]
72+
73+
74+
def bin_sensor_region(region, binning):
75+
"""Calculate new region coordinates when binned by a given value"""
76+
if region is None:
77+
return region
78+
79+
return [
80+
(region[0] + binning[0] - 1) // binning[0],
81+
region[1] // binning[0],
82+
(region[2] + binning[1] - 1) // binning[1],
83+
region[3] // binning[1]
84+
]
85+
86+
87+
def format_sensor_region(region):
88+
"""Format a 0-indexed region as a 1-indexed fits region"""
89+
return f'[{region[0] + 1}:{region[1] + 1},{region[2] + 1}:{region[3] + 1}]'
90+
91+
6292
def process_frames(process_queue, stop_signal, camera_id, filter_name, output_path,
63-
header_card_capacity, log_name,
93+
header_card_capacity, log_name, native_image_region,
6494
pipeline_daemon_name, pipeline_handover_timeout):
6595
"""
6696
Helper process to save frames to disk.
@@ -70,14 +100,15 @@ def process_frames(process_queue, stop_signal, camera_id, filter_name, output_pa
70100
while True:
71101
frame = process_queue.get()
72102

73-
if frame['image_region'] is not None:
74-
i = frame['image_region']
75-
image_region_header = ('IMAG-RGN', f'[{i[0]}:{i[1]},{i[2]}:{i[3]}]',
103+
# Imaging region in binned/windowed coords
104+
image_region = window_sensor_region(native_image_region, frame['window_region'])
105+
image_region = bin_sensor_region(image_region, frame['window_bin'])
106+
if image_region is not None:
107+
image_region_header = ('IMAG-RGN', format_sensor_region(image_region),
76108
'[x1:x2,y1:y2] image region (image coords)')
77109
else:
78110
image_region_header = ('COMMENT', ' IMAG-RGN not available', '')
79111

80-
w = frame['window_region']
81112
header = [
82113
(None, None, None),
83114
('COMMENT', ' --- DATE/TIME --- ', ''),
@@ -97,11 +128,12 @@ def process_frames(process_queue, stop_signal, camera_id, filter_name, output_pa
97128
('TEMP-LCK', frame['temperature_locked'], 'CCD temperature is locked to set point'),
98129
('CAM-XBIN', frame['window_bin'][0], '[px] x binning'),
99130
('CAM-YBIN', frame['window_bin'][1], '[px] y binning'),
100-
('CAM-WIND', f'[{w[0]}:{w[1]},{w[2]}:{w[3]}]', '[x1:x2,y1:y2] readout region (detector coords)'),
101131
('CAM-GAIN', frame['gain'], 'preamplifier gain setting'),
102132
('CAM-RSPD', frame['horizontal_shift_speed'], '[MHz] CCD readout speed'),
103-
('SHUTTER', 'AUTO' if frame['shutter_enabled'] else 'CLOSED', 'shutter mode'),
133+
('CAM-WIND', format_sensor_region(frame['window_region']),
134+
'[x1:x2,y1:y2] readout region (detector coords)'),
104135
image_region_header,
136+
('SHUTTER', 'AUTO' if frame['shutter_enabled'] else 'CLOSED', 'shutter mode'),
105137
('EXPCNT', frame['exposure_count'], 'running exposure count since EXPCREF'),
106138
('EXPCREF', frame['exposure_count_reference'], 'date the exposure counter was reset'),
107139
('SHTRCNT', frame['shutter_count'], 'running shutter count since SHTRCREF'),
@@ -174,9 +206,6 @@ class CameraDaemon:
174206
self._window_region = [0, 0, 0, 0]
175207
self._window_bin = [1, 1]
176208

177-
# Image geometry: masking away the overscan on the red camera
178-
self._image_region = [0, 0, 0, 0]
179-
180209
# Shutter enabled (opens during exposures), or remains closed
181210
self._shutter_enabled = False
182211

@@ -243,7 +272,7 @@ class CameraDaemon:
243272

244273
Process(target=process_frames, daemon=True, args=(
245274
self._processing_queue, self._processing_stop_signal, config.camera_id, config.filter, config.output_path,
246-
config.header_card_capacity, config.log_name,
275+
config.header_card_capacity, config.log_name, config.image_region,
247276
config.pipeline_daemon_name, config.pipeline_handover_timeout)
248277
).start()
249278

@@ -364,6 +393,7 @@ class CameraDaemon:
364393
readout_width = (self._window_region[1] - self._window_region[0] + 1) // self._window_bin[0]
365394
readout_height = (self._window_region[3] - self._window_region[2] + 1) // self._window_bin[1]
366395
pixel_count = readout_width * readout_height
396+
367397
while not self._stop_acquisition and not self._processing_stop_signal.value:
368398
framedata = bytearray(pixel_count * 2)
369399

@@ -435,7 +465,6 @@ class CameraDaemon:
435465
'shutter_count_reference': self._shutter_count_reference,
436466
'window_bin': self._window_bin,
437467
'window_region': self._window_region,
438-
'image_region': self._image_region
439468
})
440469

441470
# Continue exposure sequence?
@@ -712,14 +741,7 @@ class CameraDaemon:
712741
self._ccd_width = width.value
713742
self._ccd_height = height.value
714743
self._window_bin = [1, 1]
715-
self._window_region = [1, self._ccd_width, 1, self._ccd_height]
716-
self._image_region = [
717-
self._config.overscan[0] + 1,
718-
self._ccd_width - self._config.overscan[1],
719-
1,
720-
self._ccd_height
721-
]
722-
744+
self._window_region = [0, self._ccd_width - 1, 0, self._ccd_height - 1]
723745
self._gains = gains
724746
self._horizontal_shift_speeds = speeds
725747
self._status = CameraStatus.Idle
@@ -976,26 +998,9 @@ class CameraDaemon:
976998
print(f'failed to set geometry {region}({binning[0]}x{binning[1]})')
977999
return False
9781000

1001+
# Convert from 1-indexed to 0-indexed
1002+
self._window_region = [x - 1 for x in window]
9791003
self._window_bin = binning
980-
self._window_region = window
981-
982-
# Round prescan and postscan up to nearest binned pixel to avoid bleeding
983-
prescan = int((self._config.overscan[0] + binning[0] - 1) / binning[0])
984-
postscan = int((self._config.overscan[1] + binning[0] - 1) / binning[0])
985-
986-
# Work out how much pre/post scan columns are left in the requested window
987-
window_x = int((window[0] - 1) / binning[0])
988-
window_y = int((window[2] - 1) / binning[1])
989-
image_x1 = 1 + max(prescan, int((window[0] - 1) / binning[0])) - window_x
990-
image_x2 = min(int(self._ccd_width / binning[0]) - postscan, int(window[1] / binning[0])) - window_x
991-
image_y1 = 1 + int((window[2] - 1) / binning[1]) - window_y
992-
image_y2 = int(window[3] / binning[1]) - window_y
993-
994-
# Window is completely in the overscan
995-
if image_x2 < image_x1:
996-
self._image_region = None
997-
else:
998-
self._image_region = [image_x1, image_x2, image_y1, image_y2]
9991004

10001005
return True
10011006

@@ -1015,7 +1020,7 @@ class CameraDaemon:
10151020
if self._status != CameraStatus.Idle:
10161021
return CommandStatus.CameraNotIdle
10171022

1018-
if not self.__set_readout_window(self._window_region, [bin_x, bin_y]):
1023+
if not self.__set_readout_window([x + 1 for x in self._window_region], [bin_x, bin_y]):
10191024
return CommandStatus.BinningIncompatibleWithWindow
10201025

10211026
if not quiet:

‎blue.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"temperature_query_delay": 1,
1111
"gain_index": 2,
1212
"horizontal_shift_index": 1,
13-
"overscan": [0, 0],
13+
"image_region": [1, 2048, 1, 2048],
1414
"filter": "BG40",
1515
"header_card_capacity": 144,
1616
"camera_id": "BLUE",

‎red.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"temperature_query_delay": 1,
1111
"gain_index": 2,
1212
"horizontal_shift_index": 1,
13-
"overscan": [20, 20],
13+
"image_region": [20, 2067, 0, 2047],
1414
"filter": "NONE",
1515
"header_card_capacity": 144,
1616
"camera_id": "RED",

‎rockit/camera/andor2/client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def status(config, *_):
119119
print(f' Pre-amp gain is [b]{data["gain_label"]}[/b]')
120120
print(f' Readout speed is [b]{data["horizontal_shift_speed_mhz"]:.2f} MHz[/b]')
121121

122-
wr = data['window_region']
122+
wr = [x + 1 for x in data['window_region']]
123123
wb = data['window_bin']
124124
print(f' Readout window is [b]\[{wr[0]}:{wr[1]},{wr[2]}:{wr[3]}] px[/b]')
125125
print(f' Readout binning is [b]{wb[0]} x {wb[1]} px[/b]')

‎rockit/camera/andor2/config.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
'required': [
2828
'daemon', 'pipeline_daemon', 'pipeline_handover_timeout', 'log_name', 'control_machines',
2929
'client_commands_module', 'camera_serial', 'camera_id', 'temperature_setpoint', 'temperature_query_delay',
30-
'gain_index', 'horizontal_shift_index', 'overscan', 'filter', 'header_card_capacity', 'output_path',
30+
'gain_index', 'horizontal_shift_index', 'image_region', 'filter', 'header_card_capacity', 'output_path',
3131
'output_prefix', 'expcount_path'
3232
],
3333
'properties': {
@@ -78,10 +78,10 @@
7878
'minimum': 0,
7979
'maximum': 3,
8080
},
81-
'overscan': {
81+
'image_region': {
8282
'type': 'array',
83-
'minItems': 2,
84-
'maxItems': 2,
83+
'minItems': 4,
84+
'maxItems': 4,
8585
'items': {
8686
'type': 'integer',
8787
'minimum': 0
@@ -136,7 +136,7 @@ def __init__(self, config_filename):
136136
self.expcount_path = config_json['expcount_path']
137137
self.gain_index = config_json['gain_index']
138138
self.horizontal_shift_index = config_json['horizontal_shift_index']
139-
self.overscan = [int(config_json['overscan'][0]), int(config_json['overscan'][1])]
139+
self.image_region = config_json['image_region']
140140
self.filter = config_json['filter']
141141
self.header_card_capacity = config_json['header_card_capacity']
142142
self.temperature_setpoint = config_json['temperature_setpoint']

0 commit comments

Comments
 (0)
Please sign in to comment.