Getting Multiple Phase Shifts and Amplitudes from FFT

I am working on a heart rate sensor that can currently capture the user's heart rate, display it on the ESP32 OLED and IoT ThingSpeak. Next I am trying to find how to calculate the Amplitude and Phase Shift of the users heart waveform and upload those calculations to ThingSpeak.

[code]
#include <WiFi.h>
#include <Wire.h>
#include "heltec.h"
#include "arduinoFFT.h"
#include "ThingSpeak.h"
#include <PulseSensorPlayground.h> 
#include <math.h>
// connect the signal to the pin 13 of heltec LoRa ESP32

arduinoFFT fft = arduinoFFT();
WiFiClient client_1;


// ************* Variables and WiFi SSID/PWD setup
const char* SSID_1 = "Enter Wifi SSID"; 
const char* PWD_1 = "PWD"; 
unsigned long myChannelNumber = Have channel number;
const char * myWriteAPIKey = "I have write key";
const char * myReadAPIKey = "I have read key"; //if channel is private then add this in readintfeild(channel, field, Readapikey)

int status = WL_IDLE_STATUS;

const int FFT_size = 256;
const int Sampling_freq = 20; 
const int Heart_Read = 13;
//float Signal;
double vReal[FFT_size];
double vImag[FFT_size];
double vPhase[FFT_size];
double x = 0;
// *******************************************************************
void setupOLED()
{
  pinMode(16, OUTPUT);
  digitalWrite(16, LOW);        // turn D16 low to reset OLED
  delay(50);
  digitalWrite(16, HIGH);       // while OLED is running, must set D16 in high
  Heltec.display->init();
  Heltec.display->flipScreenVertically();           
  Heltec.display->setFont(ArialMT_Plain_10);         
  Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); 
}

//********************************************************************

void setupWIFI()
{
  WiFi.disconnect(true);
  delay(1000);
  
  WiFi.mode(WIFI_STA);  
  WiFi.setAutoConnect(true);      
  WiFi.setAutoReconnect(true);    
  WiFi.begin(SSID_1, PWD_1);
  ThingSpeak.begin(client_1);

  Heltec.display->clear();
  if(WiFi.status() == WL_CONNECTED)
    Heltec.display->drawString(0, 0, "WiFi Connected"); 
  else
    Heltec.display->drawString(0, 0, "WiFi Connection Failed");
  Heltec.display->display();
  
}

//*****************************************************

void setup() 
{
  Serial.begin(115200);
  ThingSpeak.begin(client_1);
    
  Heltec.begin(true, false, true);
  pinMode(25, OUTPUT);
  digitalWrite(25,HIGH);
  pinMode(Heart_Read, INPUT);

  Heltec.display->init();
  Heltec.display->flipScreenVertically();           
  Heltec.display->setFont(ArialMT_Plain_10);         
  Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); 
    

}

// ******************************************************
void loop() 
{ 
  
  delay(500);
  for (int k = 0; k < FFT_size; k++)
     {
      vReal[k] = (double)analogRead(Heart_Read);
      Serial.println(vReal[k]);
      vImag[k] = 0;
      delay(50);    //Delay = 1000/Sampling_freq
     }
     
  fft.Windowing(vReal, FFT_size, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  fft.Compute(vReal, vImag, FFT_size, FFT_FORWARD);
  fft.ComplexToMagnitude(vReal, vImag, FFT_size);
  x = fft.MajorPeak(vReal, FFT_size, Sampling_freq);

 
  int Heart_rate = (int) (x * 60);
Serial.println("Heart Rate Calculated: " + String(Heart_rate));

 setupWIFI();
  delay(1000);
  int retrieveHR = ThingSpeak.readIntField(myChannelNumber, 1, myReadAPIKey);  

  // Check the status of the read operation to see if it was successful
  int statusCode = ThingSpeak.getLastReadStatus();
  if(statusCode == 200){
    Serial.println("Retrieved Heart Rate from Thingspeak " + String(retrieveHR));
  }
  else{
    Serial.println("Problem reading channel. HTTP error code " + String(statusCode)); 
  }
        
      ThingSpeak.setField(1, Heart_rate);
      ThingSpeak.writeFields(Heart_rate, myWriteAPIKey);
      delay(500);
      WiFi.disconnect();
      WiFi.mode( WIFI_MODE_NULL );
      
      delay(100);
      setupOLED();
      Heltec.display->clear();
 Heltec.display->setFont(ArialMT_Plain_16);
       
         if((Heart_rate < 60 || Heart_rate > 100)|| (retrieveHR < 60 || retrieveHR > 100))  
//Writes to OLED
        {  String BPM = String("ABNORMAL HEART RATE ");
 Heltec.display->drawString(0,10,BPM);
        } else {
            String BPM = String("BPM: " +String(Heart_rate));
 Heltec.display->drawString(0,10,BPM);
        }
         
      Heltec.display->display();
      delay(10000);
      Heltec.display->clear();
      Heltec.display->display(); 

      digitalWrite(16, LOW);        // turn D16 low to reset OLED
      delay(50);
      digitalWrite(16, HIGH);       // while OLED is running, must set D16 in high

  
delay(5000);
    
   while(1);
}
[/code]
``` Thank you in advance!

Phase shift relative to what? Also does your sketch work now? Have you tested the basic functionality?

Phase information is lost when you do the above.

The relative phase angle for any given frequency bin i is atan2(vImag[i],vReal[i]).

Yes I have tested the basic functionality. I am able to get an accurate reading of my BPM. I want to be able to get the phase shift relative the heart waveform. That I get from the sensor that I plug into my board.

I should replace the fft.Complex line and replace it with atan2(vImag[k],vReal[k]) so the bin info doesn't get throw away?

You could populate an array with all the relative phase values, one for each frequency bin. Maybe the code offers a function to do that -- study the library to see all the options.

I want to be able to get the phase shift relative the heart waveform.

Phase shift of what? What defines the "heart waveform" and/or its phase origin? Maybe you should make a drawing to explain your thinking.

I do not understand how your sensor(s) process/provide phase-shift of a heartbeat.

Standard understanding:
image

I think you got way off track with the FFT, or more generally, the Fourier transform.

The way you are using it, the FFT gives you a breakdown of the periodic waveform you measure, in terms of the waveform's components (amplitudes and frequencies of individual sine and cosine terms).

The FFT does not provide any new information, just a different representation of the same data.

I've never actually used FFT...

Phase has to be relative to another waveform. If you have two waves at the same frequency one can be 90 degrees ahead of the other (leading) and in that case we could also say the other wave is 270 degrees behind (lagging).

I don't know how this relates to heart monitoring, but maybe does...

Without two or more waves at the same frequency phase doesn't have any meaning.

Or a time delay (or distance) can create a phase shift. If you have a pair of speakers playing the same sound and they are at different distances from you, the soundwaves hitting your ears will be out-of-phase (to some extent) unless the distance creates a (relative) delay for a 360 degree phase-shift, and then they are back in-phase (at your listening position). This is frequency dependent because different frequencies have different wavelengths.

This is actually very-obvious if you're playing high-frequency test-tones with a wavelength of around 1-inch or so. With a slight movement of your head you can hear the waves going in and out of phase (effectively adding or subtracting). It happens with music too but it's not so noticeable, and we are used to it so it just sounds "natural".

The same thing will happen if you have one sound source and two microphones at different distances. this can create a problem with normal audio recordings because the phase shift (angle) depends on frequency so different frequencies end-up with different phase shifts and you get comb filtering when the sounds are combined. (With digital recordings it's not difficult to correct the time-alignment to get everything back in phase.)

I guess I'm trying more to just get the amplitudes and phases of several freq. elements

That is easy enough. How will you select the "several elements" and what will you do with them?

Did you notice that the FFT generates a real and imaginary output? A rectangular to polar conversion yields the phase angle.

1 Like

I have no idea what that means.
In my frame-of-reference, are you anticipating (based on previous beats) the exact moment of a future beat in time? Then if the beat is early or late from forecast?

The FFT generates imaginary output

I want to be able to collect multiple amp. and phase shifts to be able to create a second wavefrom from them. Taking that generated waveform form all the amp. and phase shifts and compare it to the waveform I get from my sensor.

You have yet to answer the question ... "Phase shift relative to What Reference"?

1 Like

At the moment, you seem to be going around in circles.

None of us has a clear idea of what you really want to do, so you need to figure out what that is, and how to explain it to others in plain and simple language.

1 Like

I think if you investigate, you will find that it also generates real values. It does seem like the least of your problems at the moment...

Is this what you are trying to achieve?

Phase portraits of the ECG signal (τ = 20ms, m = 2). (a) ECG signals... | Download Scientific Diagram (researchgate.net)
Real-Time QRS Detection Method.pdf (5.1 MB)

I am able to get a valid reading and display of my BPM. What I want to do next is be able to take the data that is being read by the pulse sensor and map out a waveform of the heart. To do that I need to figure out of to plot the magnitudes and phase shifts from the FFT