Пример #1
0
def prepostfilter(reader):

    scaled_csi = reader.csi_trace

    no_frames = len(scaled_csi)
    no_subcarriers = scaled_csi[0]["csi"].shape[0]
    finalEntry = getCSI(scaled_csi)[15]

    stdev = running_stdev(finalEntry, 2)
    hampelData = hampel(finalEntry, 15)
    smoothedData = running_mean(hampelData.copy(), 15)

    y = finalEntry
    y2 = hampelData
    y3 = smoothedData

    x = list([x["timestamp"] for x in scaled_csi])

    plt.plot(x, y, label="Raw")
    plt.plot(x, y2, label="Hampel")
    # plt.plot(x, stdev, label="Standard Deviation")
    plt.plot(x, y3, "r", label="Hampel + Running Mean")

    plt.xlabel("Time (s)")
    plt.ylabel("Amplitude (dBm)")
    plt.legend(loc="upper right")

    plt.show()
Пример #2
0
    def updateContents(self, data):
        self.all_data.append(data)
        if not self.updateTimestamps():
            self.all_data = self.all_data[:-1]
            return None

        scaled_csi = self.all_data

        no_frames = len(scaled_csi)
        no_subcarriers = 30

        finalEntry = [
            db(abs(scaled_csi[x]["csi"][14][1][0])) for x in range(no_frames)
        ]

        x = list([x["timestamp"] for x in scaled_csi])

        #self.plotStand.set_xdata(x)
        #self.plotStand.set_ydata(finalEntry)

        hampelData = hampel(finalEntry, 20)
        #smoothedData = running_mean(hampelData.copy(), 15)

        self.plotHampel.set_xdata(x)
        self.plotHampel.set_ydata(hampelData)

        #self.plotAll.set_xdata(x)
        #self.plotAll.set_ydata(smoothedData)

        self.ax.relim()
        self.ax.autoscale_view()
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()
Пример #3
0
def heatmap(scaled_csi):

    xax = getTimestamps(scaled_csi)
    csi, no_frames, no_subcarriers = getCSI(scaled_csi)

    Fs = 1 / np.mean(np.diff(xax))
    print("Sampling Rate: " + str(Fs))

    limits = [0, xax[-1], 1, no_subcarriers]

    #x = subcarrier index
    #y = time (s)
    #z = amplitude (dBm)

    for x in range(no_subcarriers):
        hampelData = hampel(csi[x].flatten(), 5)
        smoothedData = running_mean(hampelData, 5)

        # b, a = signal.butter(9, [1/int(Fs/2), 2/int(Fs/2)], "bandpass")
        # csi[x] = signal.lfilter(b, a, csi[x].flatten())
        csi[x] = smoothedData

    fig, ax = plt.subplots()
    im = ax.imshow(csi, cmap="jet", extent=limits, aspect="auto")

    cbar = ax.figure.colorbar(im, ax=ax)
    cbar.ax.set_ylabel("Amplitude (dBm)")

    plt.xlabel("Time (s)")
    plt.ylabel("Subcarrier Index")

    plt.show()
Пример #4
0
def beatsfilter(scaled_csi):
    xax = getTimestamps(scaled_csi)
    csi, no_frames, no_subcarriers = getCSI(scaled_csi)

    Fs = 1 / np.mean(np.diff(xax))

    stabilities = []

    for x in range(no_subcarriers):
        finalEntry = csi[x].flatten()
        variation = stats.variation(finalEntry)

        if not np.isnan(variation) and variation < 0.5:
            hampelData = hampel(finalEntry, 5)
            smoothedData = running_mean(hampelData, 5)
            filtData = bandpass(5, 1, 1.3, Fs, smoothedData)

            wLength = 1 * Fs
            N = np.mod(len(filtData), wLength)
            windows = np.array_split(filtData, N)

            psds = []
            for window in windows:
                f, Pxx_den = signal.welch(window, Fs)
                fMax = f[np.argmax(Pxx_den)]
                psds.append(fMax)

            specStab = 1 / np.var(psds)
            print("Sub: {} has spectral stability: {}".format(x, specStab))

            # plt.plot(xax, filtData, alpha=0.5)
            stabilities.append({
                "sub": x,
                "stability": specStab,
                "data": filtData
            })

    stabilities.sort(key=lambda x: x["stability"], reverse=True)

    bestStab = stabilities[0]["stability"]
    news = []
    for stab in stabilities:
        if stab["stability"] >= (bestStab / 100) * 20:
            news.append(stab)

    print("Using {} subcarriers.".format(len(news)))
    pxxs = []
    for sub in news:
        filtData = sub["data"]
        f, Pxx_den = signal.welch(filtData, Fs)
        pxxs.append(Pxx_den)

    meanPsd = np.mean(pxxs, axis=0)

    plt.plot(f * 60, meanPsd, alpha=0.5)

    plt.xlabel("Estimated Heart Rate [bpm]")
    plt.ylabel("PSD [V**2/Hz]")
    plt.legend(loc="upper right")
    plt.show()
Пример #5
0
def heatmap(scaled_csi):

    timestamps = getTimestamps(scaled_csi)
    csi, no_frames, no_subcarriers = getCSI(scaled_csi)

    Fs = 1 / np.mean(np.diff(timestamps))
    print("Sampling Rate: " + str(Fs))

    limits = [0, timestamps[-1], 1, no_subcarriers]

    for x in range(no_subcarriers):
        hampelData = hampel(csi[x].flatten(), 5)
        smoothedData = running_mean(hampelData, 5)
        butteredData = bandpass(7, 0.2, 0.3, Fs, smoothedData)
        csi[x] = butteredData

    fig, ax = plt.subplots()
    im = ax.imshow(csi, cmap="jet", extent=limits, aspect="auto")

    cbar = ax.figure.colorbar(im, ax=ax)
    cbar.ax.set_ylabel("Amplitude (dBm)")

    plt.xlabel("Time (s)")
    plt.ylabel("Subcarrier Index")

    plt.show()
Пример #6
0
def fft(reader):

    scaled_csi = reader.csi_trace

    no_frames = len(scaled_csi)
    no_subcarriers = scaled_csi[0]["csi"].shape[0]

    finalEntry = getCSI(scaled_csi)[15].flatten()

    hampelData = hampel(finalEntry, 20)
    smoothedData = running_mean(hampelData, 30)
    y = smoothedData.flatten()
    y -= np.mean(y)

    b, a = signal.butter(5, [1 / 10, 2 / 10], "bandpass")
    y = signal.lfilter(b, a, y)

    x = [i / 20 for i in range(no_frames)]
    tdelta = (x[-1] - x[0]) / len(x)

    Fs = 1 / tdelta
    n = no_frames

    #rfft is a real Fast Fourier Transform, as we aren't interested in the imaginary parts.
    #as half of the points lie in the imaginary/negative domain, we get an n/2 point FFT.

    #rfftfreq produces a list of frequency bins, which requires calculation to produce
    #interpretable frequencies for BPM tracking. by plotting calculated bpm values,
    #the graph can be more easily read.

    #despite rfft producing an n/2 point fft, the calculation still uses n,
    #because the point spacing is still based on n points.

    # ((binNumber*samplingRate)/n)*60 produces a per minute rate.

    ffty = np.fft.rfft(y, len(y))
    freq = np.fft.rfftfreq(len(y), tdelta)
    freqX = [((i * Fs) / n) * 60 for i in range(len(freq))]

    plt.subplot(211)
    plt.xlabel("Time (s)")
    plt.ylabel("Amplitude (Hz)")

    plt.plot(x, y)

    plt.subplot(212)
    plt.xlabel("Breaths Per Minute (BPM)")
    plt.ylabel("Amplitude (dB/Hz)")

    axes = plt.gca()
    axes.set_xlim([0, 30])

    plt.plot(freqX, np.abs(ffty))

    plt.show()
Пример #7
0
    def updateButterworth(self, data):
        self.all_data.append(data)
        self.updateTimestamps()

        if not self.updateTimestamps():
            self.all_data = self.all_data[:-1]
            return None
        scaled_csi = self.all_data

        no_frames = len(scaled_csi)
        no_subcarriers = scaled_csi[0]["csi"].shape[0]

        if no_frames < 50:
            return None

        #Replace complex CSI with amplitude.
        finalEntry = [
            db(abs(scaled_csi[x]["csi"][15][0][0])) for x in range(no_frames)
        ]

        hampelData = hampel(finalEntry, 10)
        smoothedData = running_mean(hampelData, 30)
        y = smoothedData

        x = list([x["timestamp"] for x in scaled_csi])
        tdelta = (x[-1] - x[0]) / len(x)

        Fs = 1 / tdelta
        n = no_frames

        y = bandpass(5, 1.0, 1.3, Fs, y)

        ffty = np.fft.rfft(y, len(y))
        freq = np.fft.rfftfreq(len(y), tdelta)
        freqX = [((i * Fs) / n) * 60 for i in range(len(freq))]

        self.plotButt.set_xdata(freqX)
        self.plotButt.set_ydata(np.abs(ffty))

        self.ax.relim()
        self.ax.autoscale_view()
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()
Пример #8
0
def varianceGraph(reader):

    scaled_csi = reader.csi_trace

    no_frames = len(scaled_csi)
    no_subcarriers = scaled_csi[1]["csi"].shape[0]

    y = []

    finalEntry = getCSI(scaled_csi)

    for x in range(no_subcarriers):
        hampelData = hampel(finalEntry[x], 10)
        smoothedData = running_mean(hampelData, 25)
        y.append(variance(smoothedData))

    plt.xlabel("Subcarrier Index")
    plt.ylabel("Variance")

    plt.plot(y)
    plt.show()
Пример #9
0
def breathingfilter(scaled_csi):
    xax = getTimestamps(scaled_csi)
    csi, no_frames, no_subcarriers = getCSI(scaled_csi)

    # Zeroing out invalid subcarriers.
    # for i in []:
    #     for x in range(no_frames):
    #         csi[i][x] = 0

    # for x in range(no_subcarriers):
    for x in range(80, 100):
        finalEntry = csi[x].flatten()

        hampelData = hampel(finalEntry, 10)
        smoothedData = running_mean(hampelData, 10)
        # csi[x] = hampelData
        csi[x] = smoothedData
        plt.plot(xax, csi[x], alpha=0.5, label=x)

    plt.xlabel("Time (s)")
    plt.ylabel("Amplitude")
    plt.legend(loc="upper right")
    plt.show()
Пример #10
0
def specstabfilter(reader, Fs):

    scaled_csi = reader.csi_trace
    no_frames, no_subcarriers, finalEntries = getCSI(scaled_csi)

    #Using CSI phase difference data.

    stabilities = []

    for x in range(no_subcarriers):
        finalEntry = finalEntries[x]

        #CardioFi paper uses 100Hz uniformly sampled data, which may mean we need to use different
        #filter parameters. For now, we will copy them verbatim.
        #Currently the initial hampel filter is run using T1=0.5s t1=0.4
        #              dynamic_detrending is run with c=5 l=3 alpha=1.2 Fs=Fs
        #          the second hampel filter is run using T1=0.5s t1=0.1
        #          the bandpass filter is run at 5th order with a range of 1-2Hz.

        hampelData = hampel(finalEntry, 20)
        detrended = dynamic_detrend(hampelData, 5, 3, 1.2, Fs)
        rehampeledData = hampel(detrended, 20)

        filtData = bandpass(7, 1, 1.5, Fs, rehampeledData)

        #Spectral Stability section.
        #The spectral stability metric aims to score subcarriers based on the
        #variance of their heart rate estimations.

        #To do this, both the length of the data being used for estimations,
        #and a sliding window length must be selected.

        #While the dataLength should ideally be around 40s to mirror that used
        #in the CardioFi paper, this is difficult to replicate using the data
        #which was produced over the last few months.

        #Shorter data and window lengths are being experimented with but do not
        #appear to be working well. This is evidenced by the number of subcarriers
        #being selected using the metric, which is typically lower than that seen
        #in the CardioFi paper.

        #Notably, it is also difficult to take consistent ground truth heart rate
        #measurements over a 40 second period.

        wLength = 5 * Fs
        dataLength = 10 * Fs
        windows = []
        position = 0

        #This sliding window system splits filtData into wLength-sized windows,
        #except for the final window which contains the remainder of the data.
        #Each sliding window has an overlap of 1 second (or Fs).

        while position < dataLength:
            if (position + wLength) > dataLength:
                window = filtData[position:]
            else:
                window = filtData[position:position + wLength]
            windows.append(window)
            position += wLength - Fs

        #PSD measurements are then taken for each window, and the variance of
        #these measurements are then collected to produce a final score for
        #this subcarrier.

        psds = []
        for window in windows:
            f, Pxx_den = signal.welch(window, Fs)
            fMax = f[np.argmax(Pxx_den)]
            psds.append(fMax)

        specStab = 1 / np.var(psds)

        if specStab == np.inf:
            specStab = 0
        stabilities.append([x, specStab, filtData])

        # print("Sub: {} has spectral stability: {}".format(x, specStab))
        # print("Sub: {} mean pred: {}".format(x, np.mean(psds)*60))

    stabilities.sort(key=lambda x: x[1], reverse=True)
    bestStab = stabilities[0][1]
    news = [x for x in stabilities if x[1] > bestStab * 0.2]

    print("Using {} subcarriers.".format(len(news)))
    pxxs = []
    for sub in news:
        data = sub[2]
        f, Pxx_den = signal.welch(data, Fs)
        pxxs.append(Pxx_den)

    #The mean PSD of the top subcarriers is then selected, with the
    #peak taken as the final heart rate estimation.

    meanPsd = np.mean(pxxs, axis=0)
    return f[np.argmax(meanPsd)] * 60
Пример #11
0
def shorttime(reader, subcarrier):

    scaled_csi = reader.csi_trace

    no_frames = len(scaled_csi)
    no_subcarriers = scaled_csi[0]["csi"].shape[0]

    finalEntry = np.zeros((no_frames, 1))
    hampelData = np.zeros((no_frames, 1))
    smoothedData = np.zeros((no_frames, 1))

    #Replace complex CSI with amplitude.

    finalEntry = [
        db(
            abs(scaled_csi[x]["csi"][subcarrier][reader.x_antenna][
                reader.y_antenna])) for x in range(no_frames)
    ]

    hampelData = hampel(finalEntry, 30)
    smoothedData = running_mean(hampelData, 20)
    y = smoothedData

    tdelta = 0.01
    x = list([x * tdelta for x in range(0, no_frames)])

    Fs = 1 / tdelta
    n = no_frames

    fmin = 0.7
    fmax = 2.3

    b, a = signal.butter(3, [fmin / (Fs / 2), fmax / (Fs / 2)], "bandpass")
    y = signal.lfilter(b, a, y)

    f, t, Zxx = signal.stft(y, Fs, nperseg=2000, noverlap=1750)

    #bin indices for the observed amplitude peak in each segment.
    Zxx = np.abs(Zxx)
    maxAx = np.argmax(Zxx, axis=0)

    freqs = []
    for i in maxAx:
        arc = f[i]
        if arc >= fmin and arc <= fmax:
            arc *= 60
            freqs.append(arc)
        else:
            freqs.append(None)

    bpms = []
    for freq in freqs:
        if freq != None:
            bpms.append(freq)

    print(bpms)
    print("Average HR: " + str(np.average(bpms)))

    #We're only interested in frequencies between 0.5 and 2.
    freq_slice = np.where((f >= fmin) & (f <= fmax))

    f = f[freq_slice]
    Zxx = Zxx[freq_slice, :][0]

    plt.pcolormesh(t, f * 60, np.abs(Zxx))
    plt.plot(t, freqs, color="red", marker="x")

    plt.title('STFT Magnitude')
    # plt.ylabel('Frequency [Hz]')
    plt.ylabel('Heart Rate [BPM]')
    plt.xlabel('Time [sec]')
    plt.show()