Ultrasonic TDoA / ToA Measurement?
The FFT-based cross-correlation is done as follows:
TX = np.fft.fft(tx)
RX = np.fft.fft(rx)
CORR = np.multiply(TX, np.conjugate(RX))
corr = np.real(np.fft.ifft(corr))

Here is a NumPy script that tries a solution.
It does the following:

create a TX and delayed RX signal that similar to your waveform (a sine wave modulated by an envelope cosine with zeros at the front and the back and noise superimposed
calculate the cross-correlation with SciPy's signal.correlate
calculate the cross-correlation with FFTs
find and print out the location of maximum correlation
if all went well, the resulting number should be the same as the delay between TX and RX

In the graph below, you see the time domain test signal at the top, and the correlation result at the bottom (blue = signal.correlate, orange = FFT-based).

As you can see, there's a shift between the blue and the orange result. Figuring out why is an exercise for the reader (I'm learning this stuff myself). It has to do with cyclical convolutions, zero padding etc. You should be able to get a better understanding than what I'm able to offer by googling &quot;FFT convolution&quot;. Here is one such Google result that talks more about this.
In my script, instead of doing it fundamentally right, I went the pragmatic road and changed some fixed constant to make the convolution and the FFT convolution come up with the same result.
The full script finds the maximum value of the 2 correlations, like this:
    max_corr = np.argmax(corr)-len(tx)//2
    max_corr_fft = np.argmax(corr_fft) - len(corr_fft)

And prints the result:
max corr: -129
max corr fft: -129

129 matches the delay that was used to generate the RX signal.