-----
    Brief
    -----
    Function for extracting HRV parameters from time and frequency domains.

    -----------
    Description
    -----------
    ECG signals require specific processing due to their cyclic nature. For example, it is expected that in similar
    conditions the RR peak interval to be similar, which would mean that the heart rate variability (HRV) would be
    constant.

    In this function, it is calculated the tachogram of the input ECG signal, that allows to understand the variability
    of the heart rate by the calculus of the time difference between consecutive RR peaks. Thus, different features
    may be extracted from the tachogram, such as, maximum, minimum and average RR peak interval.

    This function extracts a wide range of features related to the HRV and returns them as a dictionary.

    ----------
    Parameters
    ----------
    data : list
        ECG signal or R peak list. When the input is a raw signal the input flag signal should be
        True.

    sample_rate : int
        Sampling frequency.

    signal : boolean
        If True, then the data argument contains the set of the ECG acquired samples.

    in_seconds : boolean
        If the R peaks list defined as the input argument "data" contains the sample numbers where
        the R peaks occur, then in_seconds needs to be False.

    Returns
    -------
    out : dict
        Dictionary with HRV parameters values, with keys:
            MaxRR : Maximum RR interval
            MinRR : Minimum RR interval
            AvgRR : Average RR interval
            MaxBPM : Maximum RR interval in BPM
            MinBPM : Minimum RR interval in BPM
            AvgBPM : Average RR interval in BPM
            SDNN : Standard deviation of the tachogram
            SD1 : Square root of half of the sqaured standard deviation of the differentiated tachogram
            SD2 : Square root of double of the squared SD1 minus the SD2 squared
            SD1/SD2 : quotient between SD1 and SD2
            NN20 : Number of consecutive heartbeats with a difference larger than 20 ms
            pNN20 : Relative number of consecutive heartbeats with a difference larger than 20 ms
            NN50 : Number of consecutive heartbeats with a difference larger than 50 ms
            pNN50 : Relative number of consecutive heartbeats with a difference larger than 50 ms
            ULF_Power : Power of the spectrum between 0 and 0.003 Hz
            VLF_Power : Power of the spectrum between 0.003 and 0.04 Hz
            LF_Power : Power of the spectrum between 0.04 and 0.15 Hz
            HF_Power : Power of the spectrum between 0.15 and 0.40 Hz
            LF_HF_Ratio : Quotient between the values of LF_Power and HF_Power
            Total_Power : Power of the whole spectrum

def hrv_parameters(data, sample_rate, signal=False, in_seconds=False):
    """
    -----
    Brief
    -----
    Function for extracting HRV parameters from time and frequency domains.

    -----------
    Description
    -----------
    ECG signals require specific processing due to their cyclic nature. For example, it is expected that in similar
    conditions the RR peak interval to be similar, which would mean that the heart rate variability (HRV) would be
    constant.

    In this function, it is calculated the tachogram of the input ECG signal, that allows to understand the variability
    of the heart rate by the calculus of the time difference between consecutive RR peaks. Thus, different features
    may be extracted from the tachogram, such as, maximum, minimum and average RR peak interval.

    This function extracts a wide range of features related to the HRV and returns them as a dictionary.

    ----------
    Parameters
    ----------
    data : list
        ECG signal or R peak list. When the input is a raw signal the input flag signal should be
        True.

    sample_rate : int
        Sampling frequency.

    signal : boolean
        If True, then the data argument contains the set of the ECG acquired samples.

    in_seconds : boolean
        If the R peaks list defined as the input argument "data" contains the sample numbers where
        the R peaks occur, then in_seconds needs to be False.

    Returns
    -------
    out : dict
        Dictionary with HRV parameters values, with keys:
            MaxRR : Maximum RR interval
            MinRR : Minimum RR interval
            AvgRR : Average RR interval
            MaxBPM : Maximum RR interval in BPM
            MinBPM : Minimum RR interval in BPM
            AvgBPM : Average RR interval in BPM
            SDNN : Standard deviation of the tachogram
            SD1 : Square root of half of the sqaured standard deviation of the differentiated tachogram
            SD2 : Square root of double of the squared SD1 minus the SD2 squared
            SD1/SD2 : quotient between SD1 and SD2
            NN20 : Number of consecutive heartbeats with a difference larger than 20 ms
            pNN20 : Relative number of consecutive heartbeats with a difference larger than 20 ms
            NN50 : Number of consecutive heartbeats with a difference larger than 50 ms
            pNN50 : Relative number of consecutive heartbeats with a difference larger than 50 ms
            ULF_Power : Power of the spectrum between 0 and 0.003 Hz
            VLF_Power : Power of the spectrum between 0.003 and 0.04 Hz
            LF_Power : Power of the spectrum between 0.04 and 0.15 Hz
            HF_Power : Power of the spectrum between 0.15 and 0.40 Hz
            LF_HF_Ratio : Quotient between the values of LF_Power and HF_Power
            Total_Power : Power of the whole spectrum
    """

    out_dict = {}

    # Generation of tachogram.
    tachogram_data, tachogram_time = tachogram(data, sample_rate, signal=signal,
                                               in_seconds=in_seconds, out_seconds=True)

    # Ectopy Removal.
    tachogram_data_nn = remove_ectopy(tachogram_data, tachogram_time)[0]

    # Determination of heart rate in BPM.
    # bpm_data = (1 / numpy.array(tachogram_data_nn)) * 60

    # ================================== Time Parameters ==========================================
    # Maximum, Minimum and Average RR Interval.
    out_dict["MaxRR"] = numpy.max(tachogram_data_nn)
    out_dict["MinRR"] = numpy.min(tachogram_data_nn)
    out_dict["AvgRR"] = numpy.average(tachogram_data_nn)

    # Maximum, Minimum and Average Heart Rate.
    max_hr = 1 / out_dict["MinRR"]  # Cycles per second.
    out_dict["MaxBPM"] = max_hr * 60  # BPM

    min_hr = 1 / out_dict["MaxRR"]  # Cycles per second.
    out_dict["MinBPM"] = min_hr * 60  # BPM

    avg_hr = 1 / out_dict["AvgRR"]  # Cyles per second.
    out_dict["AvgBPM"] = avg_hr * 60  # BPM

    # SDNN.
    out_dict["SDNN"] = numpy.std(tachogram_data_nn)

    # ================================ Poincaré Parameters ========================================
    # Auxiliary Structures.
    tachogram_diff = numpy.diff(tachogram_data)
    sdsd = numpy.std(tachogram_diff)

    # Poincaré Parameters.
    out_dict["SD1"] = numpy.sqrt(0.5 * numpy.power(sdsd, 2))
    out_dict["SD2"] = numpy.sqrt(2 * numpy.power(out_dict["SDNN"], 2) -
                                 numpy.power(out_dict["SD1"], 2))
    out_dict["SD1/SD2"] = out_dict["SD1"] / out_dict["SD2"]

    # ============================= Additional Parameters =========================================
    tachogram_diff_abs = numpy.fabs(tachogram_diff)

    # Number of RR intervals that have a difference in duration, from the previous one, of at least
    # 20 ms.
    out_dict["NN20"] = sum(1 for i in tachogram_diff_abs if i > 0.02)
    out_dict["pNN20"] = int(float(out_dict["NN20"]) / len(tachogram_diff_abs) * 100)  # % value.

    # Number of RR intervals that have a difference in duration, from the previous one, of at least
    # 50 ms.
    out_dict["NN50"] = sum(1 for i in tachogram_diff_abs if i > 0.05)
    out_dict["pNN50"] = int(float(out_dict["NN50"]) / len(tachogram_diff_abs) * 100)  # % value.

    # =============================== Frequency Parameters ========================================
    # Auxiliary Structures.
    freqs, power_spect = psd(tachogram_time, tachogram_data)  # Power spectrum.

    # Frequency Parameters.
    freq_bands = {"ulf_band": [0.00, 0.003], "vlf_band": [0.003, 0.04], "lf_band": [0.04, 0.15],
                  "hf_band": [0.15, 0.40]}
    power_band = {}
    total_power = 0

    band_keys = freq_bands.keys()
    for band in band_keys:
        freq_band = freq_bands[band]
        freq_samples_inside_band = [freq for freq in freqs if freq_band[0] <= freq <= freq_band[1]]
        power_samples_inside_band = [power_val for power_val, freq in zip(power_spect, freqs) if
                                     freq_band[0] <= freq <= freq_band[1]]
        power = numpy.round(integr.simps(power_samples_inside_band, freq_samples_inside_band), 5)

        # Storage of power inside band.
        power_band[band] = {}
        power_band[band]["Power Band"] = power
        power_band[band]["Freqs"] = freq_samples_inside_band
        power_band[band]["Power"] = power_samples_inside_band

        # Total power update.
        total_power = total_power + power

    out_dict["ULF_Power"] = power_band["ulf_band"]["Power Band"]
    out_dict["VLF_Power"] = power_band["vlf_band"]["Power Band"]
    out_dict["LF_Power"] = power_band["lf_band"]["Power Band"]
    out_dict["HF_Power"] = power_band["hf_band"]["Power Band"]
    out_dict["LF_HF_Ratio"] = power_band["lf_band"]["Power Band"] / power_band["hf_band"]["Power Band"]
    out_dict["Total_Power"] = total_power

    return out_dict