Detect qrs locations in a single channel ecg. Functionally, a direct port
    of the gqrs algorithm from the original wfdb package. Accepts either a
    physical signal, or a digital signal with known adc_gain and adc_zero.

    See the notes below for a summary of the program. This algorithm is not
    being developed/supported.

    Parameters
    ----------
    sig : 1d numpy array, optional
        The input physical signal. The detection algorithm which replicates
        the original, works using digital samples, and this physical option is
        provided as a convenient interface. If this is the specified input
        signal, automatic adc is performed using 24 bit precision, to obtain
        the `d_sig`, `adc_gain`, and `adc_zero` parameters. There may be minor
        differences in detection results (ie. an occasional 1 sample
        difference) between using `sig` and `d_sig`. To replicate the exact
        output of the original gqrs algorithm, use the `d_sig` argument
        instead.
    fs : int, or float
        The sampling frequency of the signal.
    d_sig : 1d numpy array, optional
        The input digital signal. If this is the specified input signal rather
        than `sig`, the `adc_gain` and `adc_zero` parameters must be specified.
    adc_gain : int, or float, optional
        The analogue to digital gain of the signal (the number of adus per
        physical unit).
    adc_zero: int, optional
        The value produced by the ADC given a 0 volt input.
    threshold : int, or float, optional
        The relative amplitude detection threshold. Used to initialize the peak
        and qrs detection threshold.
    hr : int, or float, optional
        Typical heart rate, in beats per minute.
    RRdelta : int or float, optional
        Typical difference between successive RR intervals in seconds.
    RRmin : int or float, optional
        Minimum RR interval ("refractory period"), in seconds.
    RRmax : int or float, optional
        Maximum RR interval, in seconds. Thresholds will be adjusted if no
        peaks are detected within this interval.
    QS : int or float, optional
        Typical QRS duration, in seconds.
    QT : int or float, optional
        Typical QT interval, in seconds.
    RTmin : int or float, optional
        Minimum interval between R and T peaks, in seconds.
    RTmax : int or float, optional
        Maximum interval between R and T peaks, in seconds.
    QRSa : int or float, optional
        Typical QRS peak-to-peak amplitude, in microvolts.
    QRSamin : int or float, optional
        Minimum QRS peak-to-peak amplitude, in microvolts.

    Returns
    -------
    qrs_locs : numpy array
        Detected qrs locations


    Notes
    -----
    This function should not be used for signals with fs <= 50Hz

    The algorithm theoretically works as follows:

    - Load in configuration parameters. They are used to set/initialize the:

      * allowed rr interval limits (fixed)
      * initial recent rr interval (running)
      * qrs width, used for detection filter widths (fixed)
      * allowed rt interval limits (fixed)
      * initial recent rt interval (running)
      * initial peak amplitude detection threshold (running)
      * initial qrs amplitude detection threshold (running)
      * `Note`: this algorithm does not normalize signal amplitudes, and
        hence is highly dependent on configuration amplitude parameters.

    - Apply trapezoid low-pass filtering to the signal
    - Convolve a QRS matched filter with the filtered signal
    - Run the learning phase using a calculated signal length: detect qrs and
      non-qrs peaks as in the main detection phase, without saving the qrs
      locations. During this phase, running parameters of recent intervals
      and peak/qrs thresholds are adjusted.
    - Run the detection::
        if a sample is bigger than its immediate neighbors and larger
        than the peak detection threshold, it is a peak.
            if it is further than RRmin from the previous qrs, and is a
            *primary peak.
                if it is further than 2 standard deviations from the
                previous qrs, do a backsearch for a missed low amplitude
                beat
                    return the primary peak between the current sample
                    and the previous qrs if any.
                if it surpasses the qrs threshold, it is a qrs complex
                    save the qrs location.
                    update running rr and qrs amplitude parameters.
                    look for the qrs complex's t-wave and mark it if
                    found.
        else if it is not a peak
            lower the peak detection threshold if the last peak found
            was more than RRmax ago, and not already at its minimum.

    *A peak is secondary if there is a larger peak within its neighborhood
    (time +- rrmin), or if it has been identified as a T-wave associated with a
    previous primary peak. A peak is primary if it is largest in its neighborhood,
    or if the only larger peaks are secondary.

    The above describes how the algorithm should theoretically work, but there
    are bugs which make the program contradict certain parts of its supposed
    logic. A list of issues from the original c, code and hence this python
    implementation can be found here:

    https://github.com/bemoody/wfdb/issues/17

    gqrs will not be supported/developed in this library.

    Examples
    --------
    >>> import numpy as np
    >>> import wfdb
    >>> from wfdb import processing

    >>> # Detect using a physical input signal
    >>> record = wfdb.rdrecord('sample-data/100', channels=[0])
    >>> qrs_locs = processing.gqrs_detect(record.p_signal[:,0], fs=record.fs)

    >>> # Detect using a digital input signal
    >>> record_2 = wfdb.rdrecord('sample-data/100', channels=[0], physical=False)
    >>> qrs_locs_2 = processing.gqrs_detect(d_sig=record_2.d_signal[:,0],
                                            fs=record_2.fs,
                                            adc_gain=record_2.adc_gain[0],
                                            adc_zero=record_2.adc_zero[0])

def gqrs_detect(sig=None, fs=None, d_sig=None, adc_gain=None, adc_zero=None,
                threshold=1.0, hr=75, RRdelta=0.2, RRmin=0.28, RRmax=2.4,
                QS=0.07, QT=0.35, RTmin=0.25, RTmax=0.33,
                QRSa=750, QRSamin=130):
    """
    Detect qrs locations in a single channel ecg. Functionally, a direct port
    of the gqrs algorithm from the original wfdb package. Accepts either a
    physical signal, or a digital signal with known adc_gain and adc_zero.

    See the notes below for a summary of the program. This algorithm is not
    being developed/supported.

    Parameters
    ----------
    sig : 1d numpy array, optional
        The input physical signal. The detection algorithm which replicates
        the original, works using digital samples, and this physical option is
        provided as a convenient interface. If this is the specified input
        signal, automatic adc is performed using 24 bit precision, to obtain
        the `d_sig`, `adc_gain`, and `adc_zero` parameters. There may be minor
        differences in detection results (ie. an occasional 1 sample
        difference) between using `sig` and `d_sig`. To replicate the exact
        output of the original gqrs algorithm, use the `d_sig` argument
        instead.
    fs : int, or float
        The sampling frequency of the signal.
    d_sig : 1d numpy array, optional
        The input digital signal. If this is the specified input signal rather
        than `sig`, the `adc_gain` and `adc_zero` parameters must be specified.
    adc_gain : int, or float, optional
        The analogue to digital gain of the signal (the number of adus per
        physical unit).
    adc_zero: int, optional
        The value produced by the ADC given a 0 volt input.
    threshold : int, or float, optional
        The relative amplitude detection threshold. Used to initialize the peak
        and qrs detection threshold.
    hr : int, or float, optional
        Typical heart rate, in beats per minute.
    RRdelta : int or float, optional
        Typical difference between successive RR intervals in seconds.
    RRmin : int or float, optional
        Minimum RR interval ("refractory period"), in seconds.
    RRmax : int or float, optional
        Maximum RR interval, in seconds. Thresholds will be adjusted if no
        peaks are detected within this interval.
    QS : int or float, optional
        Typical QRS duration, in seconds.
    QT : int or float, optional
        Typical QT interval, in seconds.
    RTmin : int or float, optional
        Minimum interval between R and T peaks, in seconds.
    RTmax : int or float, optional
        Maximum interval between R and T peaks, in seconds.
    QRSa : int or float, optional
        Typical QRS peak-to-peak amplitude, in microvolts.
    QRSamin : int or float, optional
        Minimum QRS peak-to-peak amplitude, in microvolts.

    Returns
    -------
    qrs_locs : numpy array
        Detected qrs locations


    Notes
    -----
    This function should not be used for signals with fs <= 50Hz

    The algorithm theoretically works as follows:

    - Load in configuration parameters. They are used to set/initialize the:

      * allowed rr interval limits (fixed)
      * initial recent rr interval (running)
      * qrs width, used for detection filter widths (fixed)
      * allowed rt interval limits (fixed)
      * initial recent rt interval (running)
      * initial peak amplitude detection threshold (running)
      * initial qrs amplitude detection threshold (running)
      * `Note`: this algorithm does not normalize signal amplitudes, and
        hence is highly dependent on configuration amplitude parameters.

    - Apply trapezoid low-pass filtering to the signal
    - Convolve a QRS matched filter with the filtered signal
    - Run the learning phase using a calculated signal length: detect qrs and
      non-qrs peaks as in the main detection phase, without saving the qrs
      locations. During this phase, running parameters of recent intervals
      and peak/qrs thresholds are adjusted.
    - Run the detection::
        if a sample is bigger than its immediate neighbors and larger
        than the peak detection threshold, it is a peak.
            if it is further than RRmin from the previous qrs, and is a
            *primary peak.
                if it is further than 2 standard deviations from the
                previous qrs, do a backsearch for a missed low amplitude
                beat
                    return the primary peak between the current sample
                    and the previous qrs if any.
                if it surpasses the qrs threshold, it is a qrs complex
                    save the qrs location.
                    update running rr and qrs amplitude parameters.
                    look for the qrs complex's t-wave and mark it if
                    found.
        else if it is not a peak
            lower the peak detection threshold if the last peak found
            was more than RRmax ago, and not already at its minimum.

    *A peak is secondary if there is a larger peak within its neighborhood
    (time +- rrmin), or if it has been identified as a T-wave associated with a
    previous primary peak. A peak is primary if it is largest in its neighborhood,
    or if the only larger peaks are secondary.

    The above describes how the algorithm should theoretically work, but there
    are bugs which make the program contradict certain parts of its supposed
    logic. A list of issues from the original c, code and hence this python
    implementation can be found here:

    https://github.com/bemoody/wfdb/issues/17

    gqrs will not be supported/developed in this library.

    Examples
    --------
    >>> import numpy as np
    >>> import wfdb
    >>> from wfdb import processing

    >>> # Detect using a physical input signal
    >>> record = wfdb.rdrecord('sample-data/100', channels=[0])
    >>> qrs_locs = processing.gqrs_detect(record.p_signal[:,0], fs=record.fs)

    >>> # Detect using a digital input signal
    >>> record_2 = wfdb.rdrecord('sample-data/100', channels=[0], physical=False)
    >>> qrs_locs_2 = processing.gqrs_detect(d_sig=record_2.d_signal[:,0],
                                            fs=record_2.fs,
                                            adc_gain=record_2.adc_gain[0],
                                            adc_zero=record_2.adc_zero[0])

    """
    # Perform adc if input signal is physical
    if sig is not None:
        record = Record(p_signal=sig.reshape([-1,1]), fmt=['24'])
        record.set_d_features(do_adc=True)
        d_sig = record.d_signal[:,0]
        adc_zero = 0
        adc_gain = record.adc_gain[0]

    conf = GQRS.Conf(fs=fs, adc_gain=adc_gain, hr=hr, RRdelta=RRdelta, RRmin=RRmin,
                RRmax=RRmax, QS=QS, QT=QT, RTmin=RTmin, RTmax=RTmax, QRSa=QRSa,
                QRSamin=QRSamin, thresh=threshold)
    gqrs = GQRS()

    annotations = gqrs.detect(x=d_sig, conf=conf, adc_zero=adc_zero)

    return np.array([a.time for a in annotations])