diff --git a/__init__.py b/__init__.py index 3676144..11b7386 100644 --- a/__init__.py +++ b/__init__.py @@ -9,3 +9,5 @@ from .focus_utils import ( parsFocusCalcCmdlinePars, parsFocussingSequenceCmdlinePars, ) + +from .focus_app import focussing_app diff --git a/focus_app.py b/focus_app.py new file mode 100644 index 0000000..1b6fcbf --- /dev/null +++ b/focus_app.py @@ -0,0 +1,169 @@ +# +# Focusing application skeleton +# + + +import OBSUTILS as obsutil +import sys +import numpy as np + + +def focussing_app(parser, set_focus, get_image): + """ + Focussing sequence standard application skeleton + + parser - argparse.ArgumentParser + set_focus_func: a function of signature: + set_focus_func(foc_val) + get_image_func: a function of signature: + get_image_func(image_filename, exp_time) + exp_time in seconds + + Returns: + 0 - OK + non-zero - ERROR + """ + + parser.add_argument("focus_start", help="focus start value", type=float) + parser.add_argument("focus_stop", help="focus stop value", type=float) + parser.add_argument( + "focus_step", help="focus step value", type=float, nargs="?", default=np.nan + ) + + parser.add_argument( + "--guess", + help="Use of two-steps focussing strategy. Input focus start and stop values are interpretated" + + "as a guess range while step value is one for precise measurements.", + action="store_true", + default=False, + ) + + parser.add_argument( + "-N", + "--num", + help="Number of points for precise stage measurement. Default is 7. Ignored if no '--guess'.", + type=np.uint64, + default=7, + ) + + parser.add_argument( + "-v", "--verbose", help="Verbose output", action="store_true", default=False + ) + + parser = obsutil.getFocussingSequenceCmdlinePars(parser) + + args = parser.parse_args() + + if (args.num < 3) and (args.guess): + if args.verbose: + print("Number of focus values must be greater than 2! Abort!") + return -1 + + focus_start = float(args.focus_start) + focus_stop = float(args.focus_stop) + focus_step = float(args.focus_step) + + if np.isfinite(focus_step): + if np.isclose(focus_step, 0.0): + if args.verbose: + print("Invalid (zero) focus step value! Abort!") + return 1 + + if np.isclose(focus_start, focus_stop): + if args.verbose: + print("Invalid focus range parameters (an empty range)! Abort!") + return 1 + + if (focus_stop - focus_start) * focus_step < 0: + if args.verbose: + print("Invalid focus range parameters! Abort!") + return 1 + + # if (focus_start > focus_stop) and (focus_step > 0): + # if args.verbose: + # print("Invalid focus range parameters! Abort!") + # return 1 + + # if (focus_start < focus_stop) and (focus_step < 0): + # if args.verbose: + # print("Invalid focus range parameters! Abort!") + # return 1 + else: + if args.guess: + if args.verbose: + print("If '--guess' is set then step value must be given! Abort!") + return 1 + + log = [] + + seq_kwds = obsutil.parsFocussingSequenceCmdlinePars(args, log) + + if args.verbose: + if len(log): + for log_str in log: + print(log_str) + + if args.verbose: + print("START FOCUSSING SEQUENCE ...") + + if args.guess: # rough focus estimation + if args.verbose: + print("\trough focus estimation ...") + result = obsutil.focussingSequence( + [focus_start, focus_stop], + set_focus, + get_image, + None, + sys.stdout, + **seq_kwds + ) + + if result["ret_code"] != 0: + print("\n\tERROR: Cannot perform rought focus estimation! Abort!") + print("\nFAIL!") + return result["ret_code"] + + # compute precise range start and stop values + r = args.num * focus_step / 2.0 + focus_start = result["focus_value"] - r + focus_stop = result["focus_value"] + r + + if args.verbose: + print( + "\testimate guess focus position in range [{:g},{:g}]".format( + focus_start, focus_stop + ) + ) + + if np.isfinite(args.focus_step): + result = obsutil.focussingSequence( + [focus_start, focus_stop, focus_step], + set_focus, + get_image, + None, + sys.stdout, + **seq_kwds + ) + else: + result = obsutil.focussingSequence( + [focus_start, focus_stop], + set_focus, + get_image, + None, + sys.stdout, + **seq_kwds + ) + + if args.verbose: + print("\tThe best focus value: {:g}".format(result["focus_value"])) + + if not args.do_not_set: + if args.verbose: + print("\n\tSet focus to the best value ...", end="") + set_focus(result["focus_value"]) + print("\tOK") + + if args.verbose: + print("DONE.") + + return 0 diff --git a/focus_seq_FLI.py b/focus_seq_FLI.py new file mode 100755 index 0000000..c8dc513 --- /dev/null +++ b/focus_seq_FLI.py @@ -0,0 +1,60 @@ +#!/usr/bin/python + +# +# Get sequence of focussing images and +# compute the best focus value +# +# This variant of the script uses Eddy Emelianov's +# 'fli_control' executable +# + + +from OBSUTILS import focussing_app +import sys +import argparse as ap +import subprocess as sp +import numpy as np +import pathlib as pl + + +# --- FLI-hardware related (ROBOTEL variant) + + +def set_focus(foc_val): + cmd = ["fli_control", "-g", str(int(foc_val))] + ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) + return ret.returncode + + +def get_image(filename, exp_time): + # + # Eddy's 'fli_control' related stub: + # filename is expected in form 'rootname_DDDD.ext' + # convert it to 'rootname' + # + + fname = str(filename).split("_") + fname = fname[0] + + cmd = [ + "fli_control", + "-r", + "/tmp/10micron.fitsheader", + "-x", + "{:d}".format(np.round(exp_time * 1000)), # to microseconds + fname, + ] + + ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) + return ret.returncode + + +if __name__ == "__main__": + parser = ap.ArgumentParser( + 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.", + ) + + ret = focussing_app(parser, set_focus, get_image) + + sys.exit(ret) diff --git a/focus_seq_Z1000_IXon.py b/focus_seq_Z1000_IXon.py new file mode 100755 index 0000000..da03606 --- /dev/null +++ b/focus_seq_Z1000_IXon.py @@ -0,0 +1,56 @@ +#!/usr/bin/python + +# +# Get sequence of focussing images and +# compute the best focus value +# +# This variant uses Fatkhullin's AndorAPI2 library +# to control Andor IXon Ultra EMCCD camera +# The script uses TCP-socket Python API to control focus +# of the SAO RAS Zeiss-1000 telescope +# + + +from OBSUTILS import focussing_app +import sys +import argparse as ap +import subprocess as sp +import numpy as np +import socket + + +def set_focus(foc_val): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("ztcs.sao.ru", 4444)) + + sock.send("goto={:g}".format(foc_val)) + resp = sock.recv(20) + + if resp != "OK": + sock.close() + return 1 + + sock.close() + + return 0 + + +# +# Assumes Andor IXon EMCCD server is listenning UNIX socket on localhost +# +def get_image(filename, exp_time): + cmd = ["ixonultra_cmdclient", "-A", "CCD", "-T", "{:g}".format(exp_time), filename] + + ret = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE) + return ret.returncode + + +if __name__ == "__main__": + parser = ap.ArgumentParser( + prog="{}".format(pl.Path(sys.argv[0]).name), + description="Zeiss-1000 and Andor Ixon EMCCD hardware: focussing sequence implementation.", + ) + + ret = focussing_app(parser, set_focus, get_image) + + sys.exit(ret) diff --git a/focus_sequence_SKEL.py b/focus_sequence_SKEL.py index e22c822..a8f7ccd 100755 --- a/focus_sequence_SKEL.py +++ b/focus_sequence_SKEL.py @@ -32,7 +32,7 @@ if __name__ == "__main__": parser.add_argument("focus_start", help="focus start value", type=float) parser.add_argument("focus_stop", help="focus stop value", type=float) parser.add_argument( - "focus_step", help="focus step value", type=float, nargs="*", default=np.nan + "focus_step", help="focus step value", type=float, nargs="?", default=np.nan ) parser.add_argument( diff --git a/focus_sequence_Z1000_Ixon.py b/focus_sequence_Z1000_Ixon.py index 8b89a27..cd6ebe1 100755 --- a/focus_sequence_Z1000_Ixon.py +++ b/focus_sequence_Z1000_Ixon.py @@ -48,7 +48,7 @@ if __name__ == "__main__": parser.add_argument("focus_start", help="focus start value", type=float) parser.add_argument("focus_stop", help="focus stop value", type=float) parser.add_argument( - "focus_step", help="focus step value", type=float, nargs="*", default=np.nan + "focus_step", help="focus step value", type=float, nargs="?", default=np.nan ) parser.add_argument(