def beatsfilter(self, data): self.all_data.append(data) scaled_csi = self.all_data no_frames = len(scaled_csi) if no_frames < 256: return None Fs = 10 no_subcarriers = scaled_csi[0]["csi"].shape[0] finalEntries = self.getCSI(scaled_csi) sigs = [] for x in range(no_subcarriers): finalEntry = finalEntries[x].flatten() filtData = bandpass(7, 1, 1.5, Fs, finalEntry) for i in range(0, 70): filtData[i] = 0 sigs.append(filtData) pxxs = [] for data in sigs: f, Pxx_den = signal.welch(data, Fs) pxxs.append(Pxx_den) meanPsd = np.mean(pxxs, axis=0) print("Beats: %.2f" % float(f[np.argmax(meanPsd)] * 60)) self.all_data = []
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()
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()
def shorttime(reader, subcarrier): scaled_csi = reader.csi_trace #Replace complex CSI with amplitude. no_frames, no_subcarriers, finalEntry = getCSI(scaled_csi) # hampelData = hampel(finalEntry, 30) # smoothedData = running_mean(hampelData, 20) y = finalEntry[subcarrier] n = no_frames Fs = 20 fmin = 1 fmax = 1.7 y = bandpass(9, fmin, fmax, Fs, y) # f, t, Zxx = signal.stft(y, Fs, nperseg=2000, noverlap=1750) f, t, Zxx = signal.stft(y, Fs) #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()
def updateHeat2(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 = scaled_csi[0]["csi"].shape[0] ylimit = scaled_csi[no_frames - 1]["timestamp"] if no_frames < 80: return None # limits = [1, no_subcarriers, 0, ylimit] limits = [0, ylimit, 1, no_subcarriers] finalEntry = np.zeros((no_subcarriers, no_frames)) #Replace complex CSI with amplitude. for y in range(no_subcarriers): for x in range(no_frames): scaled_entry = scaled_csi[x]["csi"] finalEntry[y][x] = db(abs(scaled_entry[y][0][0])) for j in range(no_subcarriers): sig = finalEntry[j] #hampelData = hampel(sig, 10) #smoothedData = running_mean(sig, 30) y = sig.flatten() y = bandpass(5, 1.0, 1.3, 20, y) for x in range(70): y[x] = 0 finalEntry[j] = y #x = subcarrier index #y = time (s) #z = amplitude (cBm) if not hasattr(self, "im"): self.im = self.ax.imshow(finalEntry, cmap="jet", extent=limits, aspect="auto") cbar = self.ax.figure.colorbar(self.im, ax=self.ax) cbar.ax.set_ylabel("Amplitude (dBm)", rotation=-91, va="bottom") else: self.im.set_array(finalEntry) self.im.set_extent(limits) self.ax.relim() self.ax.autoscale_view() self.fig.canvas.draw() self.fig.canvas.flush_events()
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()
def fft(reader): scaled_csi = reader.csi_trace Fs = 20 no_frames, no_subcarriers, finalEntry = getCSI(scaled_csi) y = bandpass(7, 1, 1.5, Fs, finalEntry[15]) #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), 0.05) freqX = [((i * Fs) / no_frames) * 60 for i in range(len(freq))] plt.subplot(211) plt.xlabel("Time (s)") plt.ylabel("Amplitude (Hz)") plt.plot(y) plt.subplot(212) plt.xlabel("Breaths Per Minute (BPM)") plt.ylabel("Amplitude (dB/Hz)") axes = plt.gca() axes.set_xlim([50, 100]) plt.plot(freqX, np.abs(ffty)) plt.show()
def beatsfilter(reader, Fs): scaled_csi = reader.csi_trace no_frames, no_subcarriers, finalEntries = getCSI(scaled_csi, metric="phasediff") sigs = [] for x in range(no_subcarriers): finalEntry = finalEntries[x] # hampelData = hampel(finalEntry, 10, 5) # # detrended = dynamic_detrend(hampelData, 5, 3, 1.2, Fs) # # rehampeledData = hampel(detrended, 10, 0.1) # # hampelData = hampel(finalEntry, 10) # # runningMeanData = running_mean(hampelData, 20) # filtData = bandpass(7, 1, 1.5, Fs, hampelData) # hampelData = hampel(finalEntries[x], 5, 3) # runningMeanData = running_mean(hampelData, 20) # smoothedData = dynamic_detrend(runningMeanData, 5, 3, 1.2, 10) # doubleHampel = hampel(smoothedData, 10, 3) filtData = bandpass(7, 1.1, 1.4, Fs, finalEntry) for i in range(0, 70): filtData[i] = 0 sigs.append(filtData) pxxs = [] for data in sigs: f, Pxx_den = signal.welch(data, Fs) pxxs.append(Pxx_den) meanPsd = np.mean(pxxs, axis=0) # plt.plot(f*60, meanPsd) # plt.xlim([40, 100]) # plt.show() return f[np.argmax(meanPsd)] * 60
def filter_seismo(sismo, freqs=[0.1, 10], ftype='bandpass', dt=0.01): """ filter seismograms. Inputs: -freqs[tuple][(0.1,10)] : corner frequencies of filter response -ftype[str][default=bandpass] : filter type Return: -Updates self.velocity array. """ filtered_s = np.zeros(sismo.shape) if ftype == 'bandpass': for i in range(sismo.shape[-1]): filtered_s[:, i] = bandpass(sismo[:, i], freqs[0], freqs[1], dt=dt, corners=4, zerophase=True) return filtered_s
def filter_signal(self, signal): filtered_signal = filters.bandpass(signal, self.sample_rate, MORSE_FREQUENCY-self.options['bandwidth'], MORSE_FREQUENCY+self.options['bandwidth']) return filtered_signal
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