Compare commits

...

12 Commits

Author SHA1 Message Date
3ca66cd142 ... 2026-04-28 09:58:30 +03:00
8a783405bb focus_seq_FLI.py: rewrite for using ccd_capture 2026-04-27 18:09:52 +03:00
66c10e2f31 fxes. working Zeiss-1000+EMCCD sequence 2025-08-24 19:39:54 +03:00
Timur A. Fatkhullin
cd49a9044f best_focus.py: FIX: print best focus in non-verbose case 2024-10-27 18:37:31 +03:00
Timur A. Fatkhullin
9d4c41dc05 ... 2024-10-27 18:13:13 +03:00
Timur A. Fatkhullin
b61261f11c ... 2024-10-27 18:09:59 +03:00
Timur A. Fatkhullin
75b1a5376d ... 2024-10-27 17:57:40 +03:00
Timur A. Fatkhullin
5c9bce4b3f some fixes 2024-10-27 11:16:10 +03:00
Timur A. Fatkhullin
2a48df15c9 fitFocusCurve: fix 'scale' computation for robust fitting 2024-10-27 10:34:10 +03:00
Timur A. Fatkhullin
47659dc918 add description to some commandline arguments 2024-10-14 20:37:11 +03:00
Timur A. Fatkhullin
65f11e6764 ... 2024-10-12 22:11:56 +03:00
ce817b64a1 fixes: real sky tested 2024-10-11 21:48:45 +03:00
7 changed files with 158 additions and 42 deletions

View File

@@ -52,4 +52,7 @@ if __name__ == "__main__":
if args.verbose: if args.verbose:
print("\nALL DONE") print("\nALL DONE")
if not args.verbose:
print("best focus value is {:g}".format(result["focus_value"]))
sys.exit(0) sys.exit(0)

View File

@@ -125,6 +125,10 @@ def focussing_app(parser, init_seq_func, set_focus_func, get_image_func):
pt.with_stem(pt.stem + "_rough") pt.with_stem(pt.stem + "_rough")
) )
# rough estimation: be sure that only a single
# acquisition per focus value
seq_rough_kwds["acq_per_value"] = 1
result = obsutil.focussingSequence( result = obsutil.focussingSequence(
[focus_start, focus_stop], [focus_start, focus_stop],
init_seq_func, init_seq_func,
@@ -141,13 +145,14 @@ def focussing_app(parser, init_seq_func, set_focus_func, get_image_func):
return result["ret_code"] return result["ret_code"]
# compute precise range start and stop values # compute precise range start and stop values
r = args.num * focus_step / 2.0 r = (args.num - 1) * focus_step / 2.0
focus_start = result["focus_value"] - r focus_start = result["focus_value"] - r
focus_stop = result["focus_value"] + r focus_stop = result["focus_value"] + r
if args.verbose: if args.verbose:
print("\n\t\t rough focus position: {:g}".format(result["focus_value"]))
print( print(
"\testimate guess focus position in range of [{:g},{:g}]".format( "\t\testimate guess focus position in range of [{:g},{:g}]".format(
focus_start, focus_stop focus_start, focus_stop
) )
) )
@@ -173,16 +178,36 @@ def focussing_app(parser, init_seq_func, set_focus_func, get_image_func):
**seq_kwds **seq_kwds
) )
if result["ret_code"]:
if args.verbose:
print(
"\tFocussing sequence returned error code: {:g}".format(
result["ret_code"]
)
)
print("\tCannot set best focus value!")
return result["ret_code"]
if args.verbose: if args.verbose:
print("\tThe best focus value: {:g}".format(result["focus_value"])) print("\n\tThe best focus value: {:g}".format(result["focus_value"]))
ret_code = 0
if not args.do_not_set: if not args.do_not_set:
if args.verbose: if args.verbose:
print("\n\tSet focus to the best value ...", end="") print("\n\tSet focus to the best value ...", end="")
set_focus_func(result["focus_value"])
print("\tOK") ret_code = set_focus_func(result["focus_value"])
if ret_code:
if args.verbose:
print("\tFAILED")
else:
if args.verbose:
print("\tOK")
if args.verbose: if args.verbose:
print("DONE.") print("DONE.")
return 0 return ret_code

View File

@@ -7,6 +7,10 @@
# This variant of the script uses Eddy Emelianov's # This variant of the script uses Eddy Emelianov's
# 'fli_control' executable # 'fli_control' executable
# #
# 27.04.2026 (Fatkhullin T.A.)
# changes according to Eddy Emelianov's "ccd_capture" executable
# (a new age FLI hardware control software)
#
from OBSUTILS import focussing_app from OBSUTILS import focussing_app
@@ -16,32 +20,71 @@ import subprocess as sp
import numpy as np import numpy as np
import pathlib as pl import pathlib as pl
# --- FLI-hardware related (ROBOTEL variant) # --- FLI-hardware related (ROBOTEL variant)
# common "ccd_capture" executable commandline options
ccd_capture_cmd_common = [
"ccd_capture",
"-VVV",
"--plugin",
"libdevfli.so",
"-r",
"/tmp/10micron.fitsheader",
"-r",
"/tmp/telescope.fitsheader",
"-r",
"/tmp/dome.fitsheader",
]
def init_seq(seq_kwds): def init_seq(seq_kwds):
# remove extension as Eddy's 'fli_control' add hardcoded '.fit' # # replace extension since Eddy's 'fli_control' adds hardcoded '.fit'
pt = pl.Path(seq_kwds["root_filename"]).with_suffix("") # pt = pl.Path(seq_kwds["root_filename"]).with_suffix(".fit")
seq_kwds["root_filename"] = str(pt) # seq_kwds["root_filename"] = str(pt)
pt = pl.Path(seq_kwds["root_filename"])
if not len(pt.suffix):
pt.with_suffix(".fits")
seq_kwds["root_filename"] = str(pt)
# #
# search for existing files
# and compute start index of focus file
# #
foc_files = list( foc_files = list(
pt.absolute().parent.glob("{}_[0-9][0-9][0-9][0-9].fit".format(pt.stem)) # 27.04.2026
pt.absolute().parent.glob(
"{}_[0-9][0-9][0-9][0-9]{}".format(pt.stem, pt.suffix)
)
# pt.absolute().parent.glob("{}_[0-9][0-9][0-9][0-9].fit".format(pt.stem))
) )
seq_kwds["start_idx"] = 1 seq_kwds["start_idx"] = 1
N = len(foc_files) N = len(foc_files)
if N > 0: if N > 0:
seq_kwds["start_idx"] = N max = 0
for fname in foc_files:
inum = int(fname.stem.split("_")[1])
if inum > max:
max = inum
seq_kwds["start_idx"] = max + 1
return 0 return 0
def set_focus(foc_val): def set_focus(foc_val):
cmd = ["fli_control", "-g", str(int(foc_val))] if foc_val < 0:
print("INVALID FLI-FOCUSER VALUE! IT MUST BE NON-NEGATIVE VALUE!")
return -1
# cmd = ["fli_control", "-g", str(int(foc_val))]
# 27.04.2026
# cmd = ["ccd_capture", "-VVV", "--plugin", "libdevfli.so", "-r", "/tmp/10micron.fitsheader", "-r", "/tmp/telescope.fitsheader", "-r", "/tmp/dome.fitsheader", "-g", str(foc_val)]
cmd = ccd_capture_cmd_common + ["-g", str(foc_val)]
ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
return ret.returncode return ret.returncode
@@ -51,19 +94,22 @@ def get_image(filename, exp_time):
# Eddy's 'fli_control' related stub: # Eddy's 'fli_control' related stub:
# filename is expected in form 'rootname_DDDD.ext' # filename is expected in form 'rootname_DDDD.ext'
# convert it to 'rootname' # convert it to 'rootname'
#
fname = str(filename).split("_") # fname = str(filename).split("_")
fname = fname[0] # fname = fname[0]
cmd = [ # cmd = [
"fli_control", # "fli_control",
"-r", # "-r",
"/tmp/10micron.fitsheader", # "/tmp/10micron.fitsheader",
"-x", # "-x",
"{:d}".format(np.round(exp_time * 1000)), # to microseconds # "{:d}".format(int(np.round(exp_time * 1000))), # to microseconds
fname, # fname,
] # ]
# 27.04.2026
# cmd = ["ccd_capture", "-VVV", "--plugin", "libdevfli.so", "-r", "/tmp/10micron.fitsheader", "-r", "/tmp/telescope.fitsheader", "-r", "/tmp/dome.fitsheader", "-x", str(exp_time), "-o", filename]
cmd = ccd_capture_cmd_common + ["-x", str(exp_time), "-o", filename]
ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
return ret.returncode return ret.returncode
@@ -72,7 +118,9 @@ def get_image(filename, exp_time):
if __name__ == "__main__": if __name__ == "__main__":
parser = ap.ArgumentParser( parser = ap.ArgumentParser(
prog="{}".format(pl.Path(sys.argv[0]).name), prog="{}".format(pl.Path(sys.argv[0]).name),
description="FLI CCD and focuser hardware: focussing sequence implementation. It is assumed that 'fli_control' software is installed in the OS.", # 27.04.2026
description="FLI CCD and focuser hardware: focussing sequence implementation. It is assumed that 'ccd_capture' software is installed in the OS.",
# description="FLI CCD and focuser hardware: focussing sequence implementation. It is assumed that 'fli_control' software is installed in the OS.",
) )
ret = focussing_app(parser, init_seq, set_focus, get_image) ret = focussing_app(parser, init_seq, set_focus, get_image)

View File

@@ -18,6 +18,7 @@ import subprocess as sp
import numpy as np import numpy as np
import socket import socket
import pathlib as pl import pathlib as pl
import time
def init_seq(seq_kwds): def init_seq(seq_kwds):
@@ -30,7 +31,13 @@ def init_seq(seq_kwds):
seq_kwds["start_idx"] = 1 seq_kwds["start_idx"] = 1
N = len(foc_files) N = len(foc_files)
if N > 0: if N > 0:
seq_kwds["start_idx"] = N max = 0
for fname in foc_files:
inum = int(fname.stem.split("_")[1])
if inum > max:
max = inum
seq_kwds["start_idx"] = max + 1
return 0 return 0
@@ -39,13 +46,28 @@ def set_focus(foc_val):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("ztcs.sao.ru", 4444)) sock.connect(("ztcs.sao.ru", 4444))
sock.send("goto={:g}".format(foc_val)) bt = bytearray("goto={:g}".format(foc_val), 'utf8')
resp = sock.recv(20)
sock.send(bt)
resp = sock.recv(20).decode('utf8')
if resp != "OK": if resp != "OK":
sock.close() sock.close()
return 1 return 1
tp_start = time.monotonic()
while True:
time.sleep(0.5)
sock.send(b'status')
resp = sock.recv(20).decode('utf8')
if resp == 'OK':
break;
if (time.monotonic()-tp_start) > 30.0:
sock.close() # timeout!
return 2
sock.close() sock.close()
return 0 return 0
@@ -55,7 +77,7 @@ def set_focus(foc_val):
# Assumes Andor IXon EMCCD server is listenning UNIX socket on localhost # Assumes Andor IXon EMCCD server is listenning UNIX socket on localhost
# #
def get_image(filename, exp_time): def get_image(filename, exp_time):
cmd = ["ixonultra_cmdclient", "-A", "CCD", "-T", "{:g}".format(exp_time), filename] cmd = ["ixonultra_cmdclient", "-A", "CCD", "-p", "1", "--hs", "0", "-T", "{:g}".format(exp_time), filename]
ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
return ret.returncode return ret.returncode

View File

@@ -20,6 +20,9 @@ import subprocess as sp
def set_focus(foc_val): def set_focus(foc_val):
if foc_val < 0:
print("INVALID FLI-FOCUSER VALUE! IT MUST BE NON-NEGATIVE VALUE!")
return -1
cmd = ["fli_control", "-g", str(int(foc_val))] cmd = ["fli_control", "-g", str(int(foc_val))]
ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
return ret.returncode return ret.returncode
@@ -159,8 +162,11 @@ if __name__ == "__main__":
focus_stop = result["focus_value"] + r focus_stop = result["focus_value"] + r
if args.verbose: if args.verbose:
print("\testimate guess focus position in range [{:g},{:g}]".format(focus_start, focus_stop)) print(
"\testimate guess focus position in range [{:g},{:g}]".format(
focus_start, focus_stop
)
)
if np.isfinite(args.focus_step): if np.isfinite(args.focus_step):
result = obsutil.focussingSequence( result = obsutil.focussingSequence(

View File

@@ -23,7 +23,8 @@ def set_focus(foc_val):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("ztcs.sao.ru", 4444)) sock.connect(("ztcs.sao.ru", 4444))
sock.send("goto={:g}".format(foc_val)) print(foc_val)
sock.send("goto={:f}".format(foc_val))
resp = sock.recv(20) resp = sock.recv(20)
if resp != "OK": if resp != "OK":
@@ -36,7 +37,7 @@ def set_focus(foc_val):
def get_image(filename, exp_time): def get_image(filename, exp_time):
cmd = ["ixonultra_cmdclient", "-A", "CCD", "-T", "{:g}".format(exp_time), filename] cmd = ["ixonultra_cmdclient", "-A", "CCD", "-p", "1", "--hs", "0", "-T", "{:g}".format(exp_time), filename]
ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
return ret.returncode return ret.returncode
@@ -175,7 +176,7 @@ if __name__ == "__main__":
if args.verbose: if args.verbose:
print("\tThe best focus value: {:g}".format(result["focus_value"])) print("\tThe best focus value: {:g}".format(result["focus_value"]))
if not args.do_no_set: if not args.do_not_set:
if args.verbose: if args.verbose:
print("\n\tSet focus to the best value ...", end="") print("\n\tSet focus to the best value ...", end="")
set_focus(result["focus_value"]) set_focus(result["focus_value"])
@@ -185,3 +186,4 @@ if __name__ == "__main__":
print("DONE.") print("DONE.")
sys.exit(0) sys.exit(0)

View File

@@ -121,11 +121,20 @@ def fitFocusCurve(
np.inf, np.inf,
) # parabola with minimum (2nd derivative must be positive) ) # parabola with minimum (2nd derivative must be positive)
if coeffs[2] < 0: if coeffs[2] < 0: # it seems the non-robust fit failed!
coeffs[2] = 0 coeffs[2] = 0 # so, guess curve is just constant!
coeffs[1] = 0
coeffs[0] = np.nanmedian(flux_rad)
scale = np.nanmedian(np.abs(flux_rad - coeffs[0]))
else:
nr_model = nr_res(np.asarray(focus_value))
scale = np.nanmedian(np.abs(flux_rad - nr_model))
# if coeffs[2] < 0:
# coeffs[2] = 0
# nr_model = nr_res(np.asarray(focus_value))
# scale = np.abs(np.nanmedian(flux_rad - nr_model))
nr_model = nr_res(np.asarray(focus_value))
scale = np.abs(np.nanmedian(flux_rad - nr_model))
# robust fitting # robust fitting
res = least_squares( res = least_squares(
polyModelResid, polyModelResid,
@@ -335,7 +344,7 @@ def focussingSequence(
ret_val["ret_code"] = -1 ret_val["ret_code"] = -1
return ret_val return ret_val
N = 7 N = 5
if len(focus_range) > 2: if len(focus_range) > 2:
if np.isclose(focus_range[2], 0): if np.isclose(focus_range[2], 0):
@@ -421,7 +430,7 @@ def focussingSequence(
# filename = "{}_{:04d}{}".format(fname, i_foc, file_ext) # filename = "{}_{:04d}{}".format(fname, i_foc, file_ext)
# full_filename = str(pt.Path.joinpath(dir, pt.Path(filename))) # full_filename = str(pt.Path.joinpath(dir, pt.Path(filename)))
full_filename = str(rname.with_stem("{}_{:04d}", rname.stem, i_foc)) full_filename = str(rname.with_stem("{}_{:04d}".format(rname.stem, i_foc)))
print(log_ident, file=log_output, end="") print(log_ident, file=log_output, end="")
print( print(
@@ -580,7 +589,7 @@ def getFocusCalcCmdlinePars(parser):
parser.add_argument( parser.add_argument(
"-o", "-o",
"--data-curve", "--data-curve",
help="filename of focussing curve data file", help="filename of focussing curve ASCII data file",
type=str, type=str,
default=None, default=None,
) )
@@ -613,7 +622,8 @@ def getFocussingSequenceCmdlinePars(parser):
parser.add_argument( parser.add_argument(
"--foc-file", "--foc-file",
help="Rootname of output focussing images. Default is 'focus.fits'.", help="Rootname of output focussing images. Default is 'focus.fits'. The algorithm generates files of sequence as "
"'rootname_0001.rootname_ext, rootname_0002.rootname_ext ... rootname_NNNN.rootname_ext', where NNNN is an index number of acquisition.",
type=str, type=str,
default="focus.fits", default="focus.fits",
) )