#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3		// Use modern global access method and strict wave access.
#pragma IgorVersion = 6.35
#pragma ModuleName = PearlScientaLive
#include "pearl-area-display"

// Copyright (c) 2019 Paul Scherrer Institut
//
// Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//   http://www.apache.org/licenses/LICENSE-2.0

/// @file
/// @brief utility functions for operating the Scienta analyser.
/// @ingroup ArpesPackage
///
/// this procedure contains various utility functions for working with the Scienta analyser, e.g.
///
/// - statistical analysis of true count rates
/// - estimate energy and angle resolution
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2019 Paul Scherrer Institut @n
/// Licensed under the Apache License, Version 2.0 (the "License"); @n
///   you may not use this file except in compliance with the License. @n
///   You may obtain a copy of the License at
///   http://www.apache.org/licenses/LICENSE-2.0

/// @namespace PearlScientaLive
/// @brief utility functions for operating the Scienta analyser.
///
/// PearlScientaLive is declared in @ref pearl-scienta-live.ipf.

/// open live display of most recent scienta measurement
///
///
/// @param epicsname 	base name of the detector, e.g. X03DA-SCIENTA:
/// 						image1: and cam1: are appended by the function.
/// 						see @ref ad_connect.
/// @param nickname		nick name under which this detector is referred to in Igor.
///							must be a valid data folder name.
/// 						see @ref ad_connect.
/// @param wbRGB			window background color, e.g. "32768,49152,55296"
///
function ScientaLiveDisplay(epicsname, nickname, wbRGB)
	string epicsname
	string nickname
	string wbRGB
	
	ad_connect(epicsname, nickname)
	string df_name
	sprintf df_name, "ad_display_profiles(root:pearl_epics:%s)", nickname
	dfref df = $df_name
	wave /sdfr=df img = image
	string graphname = ad_display(img)
	wbRGB = replacestring("(", wbRGB, "")
	wbRGB = replacestring(")", wbRGB, "")
	variable rr = str2num(StringFromList(0, wbRGB, ","))
	variable gg = str2num(StringFromList(1, wbRGB, ","))
	variable bb = str2num(StringFromList(2, wbRGB, ","))
	ModifyGraph /w=$graphname wbRGB=(rr,gg,bb)
	add_roi_controls()
	ad_add_overlay(img)
end

#if igorVersion() >= 8
/// check exposure and calculate overexposure indicator mask
///
/// calculate the local count rate density and return a mask
/// to indicate where the maximum count rate is exceeded.
/// the raw image is filtered by FFT with a gaussian kernel.
///
/// the raw image must have been acquired in fixed mode.
/// slicing and dwell time are accounted for.
///
/// all intermediate waves are created ad-hoc as free waves.
/// this function requires igor 8.
///
/// @param[i] image		raw image from scienta in fixed mode.
///
///	 @param[o] outmask	mask wave.
///							must have same dimensions as image.
///							suggested data type /b/u.
///							the mask value in overexposed regions is 0,
///							in regular regions 64.
///
/// @param[i] dwelltime	in seconds.
///
function check_exposure(image, outmask, dwelltime)
	wave image
	wave outmask
	variable dwelltime
	
	variable xbin = 1
	variable ybin = 902 / dimsize(image, 1)
	variable thresh = 1e5 / 900 / 900
	
	duplicate /free image, filt
	setscale /p x -dimsize(image, 0)/2, 1, "", filt // energy
	setscale /p y -dimsize(image, 1)/2, 1, "", filt // angle
	variable wx = sqrt(500) / xbin
	variable wy = sqrt(500) / ybin
	filt = exp(-((x/wx)^2 + (y/wy)^2))
	variable nfilt = sum(filt)
	filt /= nfilt
	fft /free /dest=filt_fft filt

	duplicate /free image, img
	img /= dwelltime
	setscale /p x -dimsize(image, 0)/2, 1, "", img
	setscale /p y -dimsize(image, 1)/2, 1, "", img
	fft /free /dest=img_fft img
	img_fft *= filt_fft
	ifft /free /dest=img_ifft img_fft
	imagetransform swap img_ifft
	
	outmask = (img_ifft < thresh) * 64
end
#endif

/// optimized check exposure and calculate overexposure indicator mask
///
/// calculate the local count rate density and return a mask
/// to indicate where the maximum count rate is exceeded.
/// the raw image is filtered by FFT with a gaussian kernel.
///
/// the raw image must have been acquired in fixed mode.
/// slicing and dwell time are accounted for.
///
/// this function does the same as check_exposure()
/// but keeps intermediate waves for time-optimized processing.
/// moreover it is compatible with igor 6.
///
/// @param[i] image		raw image from scienta in fixed mode.
///
///	 @param[o] outmask	mask wave.
///							must have same dimensions as image.
///							suggested data type /b/u.
///							the mask value in overexposed regions is 0, in regular regions 64.
///
/// @param[i] dwelltime	in seconds.
///
/// @param[i] calc_df	(optional) data folder reference where intermediate and cached waves should be stored.
///							by default, the data folder is named "psc_" plus the name of the image wave
///							and inserted in the folder where the image wave is stored.
///
/// the possible gain can be estimated from the following test data:
/// - total execution time 510 ms
/// - gain from re-using (but recalculating) waves: 20 ms
/// - gain from re-using co_filt_fft: 220 ms
///
function check_exposure_opt(image, outmask, dwelltime, [calc_df])
	wave image
	wave outmask
	variable dwelltime
	dfref calc_df
	
	variable xbin = 1
	variable ybin = 902 / dimsize(image, 1)
	variable thresh = 1e5 / 900 / 900
	
	dfref save_df = GetDataFolderDFR()
	if (ParamIsDefault(calc_df))
		dfref source_df = GetWavesDataFolderDFR(image)
		string calc_df_name = PearlCleanupName("psc_" + NameOfWave(image))
		dfref calc_df = source_df:$calc_df_name
	endif
	NewDataFolder /o /s calc_df
	
	wave /z co_filt
	wave /z /c co_filt_fft
	wave /z co_img
	wave /z /c co_img_fft
	wave /z co_img_ifft
	nvar /z co_img_size_x
	nvar /z co_img_size_y
	
	variable cache = 0
	if (waveexists(co_filt))
		cache = (dimsize(image, 0) == co_img_size_x) && (dimsize(image, 1) == co_img_size_y)
		if (!cache)
			redimension /n=(dimsize(image, 0), dimsize(image, 1)) co_filt, co_filt_fft, co_img, co_img_fft, co_img_ifft
		endif
	else
		duplicate /o image, co_filt, co_img, co_img_ifft
		make /n=(dimsize(image, 0), dimsize(image, 1)) /c co_filt_fft, co_img_fft
		variable /g co_img_size_x
		variable /g co_img_size_y
	endif
	
	co_img_size_x = dimsize(image, 0)
	co_img_size_y = dimsize(image, 1)
	setscale /p x -co_img_size_x/2, 1, "", co_filt, co_filt_fft, co_img, co_img_fft, co_img_ifft
	setscale /p y -co_img_size_y/2, 1, "", co_filt, co_filt_fft, co_img, co_img_fft, co_img_ifft
	
	if (!cache)
		variable wx = sqrt(500) / xbin
		variable wy = sqrt(500) / ybin
		co_filt = exp(-((x/wx)^2 + (y/wy)^2))
		variable nfilt = sum(co_filt)
		co_filt /= nfilt
		fft /dest=co_filt_fft co_filt
	endif

	co_img /= dwelltime
	fft /dest=co_img_fft co_img
	co_img_fft *= co_filt_fft
	ifft /dest=co_img_ifft co_img_fft
	imagetransform swap co_img_ifft
	
	redimension /n=(dimsize(co_img_ifft, 0), dimsize(co_img_ifft, 1)) outmask
	outmask = (co_img_ifft < thresh) * 64
	
	SetDataFolder save_df
end

/// calculate the energy resolution of the analyser
///
/// @param	epass	pass energy in eV
/// @param	slit		analyser entrance slit in mm
///
/// @return energy resolution (FWHM)
function analyser_energy_resolution(epass, slit)
	variable epass
	variable slit

	variable respow
	if (epass < 4)
		respow = 1110
	elseif (epass < 8)
		respow = 1400
	else
		respow = 1750
	endif
	
	return epass * max(0.2, slit) / 0.2 / respow
end
