## -*- texinfo -*-
## @deftypefn  {Function File} {@var{str} =} dsp_compute_f3db (@var{H}, @var{wn})
## @deftypefnx {Function File} {@var{str} =} dsp_compute_f3db (@var{H}, @var{wn}, @var{Fs})
## @deftypefnx {Function File} {[@var{str}, @var{wn_cross}] =} dsp_compute_f3db (@dots{})
## Return a human-readable string describing the -3 dB crossing
## frequencies of a frequency response @var{H} sampled at normalized
## frequencies @var{wn} (in units of @math{\times\pi} rad/sample, so
## @math{wn \in [0, 1]} where 1 corresponds to the Nyquist frequency).
##
## When a valid sampling frequency @var{Fs} is supplied @emph{and} the
## tool-wide axis mode is Hz (@code{dsp_freq_axis_hz}), the crossings are
## reported in Hz -- or kHz once the Nyquist frequency reaches 1 kHz, the
## same threshold @code{dsp_freq_ticks} uses for its tick labels.  Without
## @var{Fs} the normalized form is always used, so callers that deliberately
## stay normalized (the comparison window) need no change.
##
## The second output @var{wn_cross} returns the crossings as raw normalized
## values, for callers that need to do their own formatting (the HTML
## report).  It is empty when there is no crossing.
##
## @seealso{dsp_freq_axis_hz, dsp_freq_ticks}
## @end deftypefn

function [f3db_str, wn_cross] = dsp_compute_f3db(H, wn, Fs)
    if nargin < 3
        Fs = [];
    end

    mag_db = 20 * log10(abs(H));
    max_db = max(mag_db);
    cross  = find(diff(sign(mag_db - (max_db - 3))));

    if isempty(cross)
        f3db_str = "N/A";
        wn_cross = [];
        return;
    end

    if numel(cross) > 1
        wn_cross = [wn(cross(1)), wn(cross(end))];
    else
        wn_cross = wn(cross(1));
    end
    wn_cross = wn_cross(:).';

    use_hz = false;
    if isscalar(Fs) && isfinite(Fs) && Fs > 0
        use_hz = dsp_freq_axis_hz();
    end

    if use_hz
        nyq = Fs / 2;
        if nyq >= 1000
            parts = arrayfun(@(v) sprintf("%.4g kHz", v * nyq / 1000), ...
                             wn_cross, "UniformOutput", false);
        else
            parts = arrayfun(@(v) sprintf("%.4g Hz", v * nyq), ...
                             wn_cross, "UniformOutput", false);
        end
    else
        parts = arrayfun(@(v) sprintf("%.4f x pi rad/sample", v), ...
                         wn_cross, "UniformOutput", false);
    end

    f3db_str = strjoin(parts, " / ");
end
