def calc_fwhm(self, arr1d, medv=None, gauss_fn=None):
        if not gauss_fn:
            gauss_fn = self.gaussian

        N = len(arr1d)
        X = numpy.array(list(range(N)))
        Y = arr1d
        # Fitting works more reliably if we do the following
        # a. subtract sky background
        if medv is None:
            medv = numpy.median(Y)
        Y = Y - medv
        maxv = Y.max()
        # b. clamp to 0..max (of the sky subtracted field)
        Y = Y.clip(0, maxv)

        # Fit a gaussian
        p0 = [0, N-1, maxv]              # Inital guess
        # Distance to the target function
        errfunc = lambda p, x, y: gauss_fn(x, p) - y
        # Least square fit to the gaussian
        with self.lock:
            # NOTE: without this mutex, optimize.leastsq causes a fatal error
            # sometimes--it appears not to be thread safe.
            # The error is:
            # "SystemError: null argument to internal routine"
            # "Fatal Python error: GC object already tracked"
            p1, success = optimize.leastsq(errfunc, p0[:], args=(X, Y))

        if not success:
            raise IQCalcError("FWHM gaussian fitting failed")

        mu, sdev, maxv = p1
        self.logger.debug("mu=%f sdev=%f maxv=%f" % (mu, sdev, maxv))

        # Now that we have the sdev from fitting, we can calculate FWHM
        # (fwhm = sdev * sqrt(8*log(2)) ?)
        fwhm = 2.0 * numpy.sqrt(2.0 * numpy.log(2.0)) * sdev
        #return (fwhm, mu, sdev, maxv)
        return (float(fwhm), float(mu), float(sdev), maxv)
FWHM calculation on a 1D array by using least square fitting of
dataset/ETHPy150Open ejeschke/ginga/ginga/util/iqcalc.py/IQCalc.calc_fwhm