Measurements from small DIY NMR spectrometer

11 Jan 2026 - tsp
Last update 11 Jan 2026
Reading time 17 mins

Following up on the theory articles on generic two level quantum systems and nuclear magnetic resonance, this short article quickly shows a few NMR measurements on a very simple NMR spectrometer based on a permanent magnet. A simple spin active sample has been used. This system measures - after a protective circuit and variable gain amplifier as well as variable low pass filter and some blanking the envelope via a diode detector and also supplies the downmix (I & Q channels) with a fixed phase. A synchronization pulse is provided while the pulse is sent to the sample. The actual measurement is done using a stock oscilloscope - it’s a very simple setup that you can also build at home on your kitchen table actually without much hassle.

Due to the very limited nature and missing automation of the setup (i.e. manual readout) the data may be somewhat noisy and we will not look into detail - but rather at the basic shapes to expect when building an NMR experiment.

The theory is already covered in a previous article, I would highly recommend crunching through the math and simulations first. This blog post looks into:

Fitting functions

Before diving into experimental data let’s define fit functions that we are going to use on our experimental data:

Those will later be fitted using lmfit (least squares)

import numpy as np
import matplotlib.pyplot as plt
import csv

from lmfit import Parameters, fit_report, minimize
def model_exp(pars, x, data = None):
    vals = pars.valuesdict()

    amp = vals["amp"]
    lamb = vals["lamb"]
    phase = vals["phase"]
    offset = vals["offset"]

    modelValue = amp * np.exp(lamb * (x + phase)) + offset
    if data is not None:
        return modelValue - data
    else:
        return modelValue

def model_sin(pars, x, data = None):
    vals = pars.valuesdict()

    amp = vals["amp"]
    omega = vals["omega"]
    phase = vals["phase"]
    offset = vals["offset"]

    modelValue = amp * np.sin(omega * (x + phase)) + offset
    if data is not None:
        return modelValue - data
    else:
        return modelValue

Initial frequency tuning

The initial frequency tuning has been done manually with an experimental repetition period of 800ms to allow thermalization of the system in between the runs and a long RF pulse duration ($15 \mu s$) at $-10 dBm$. The sequences have been recorded around $f=21.52550 MHz$. The shown frequency sweep has been done to demonstrate how the beatings behave around the resonance. We will have a closer look in the next step after showing initial data. The sequence covers the following frequencies:

Frequency Data file
21.52578 002.CSV
21.52565 003.CSV
21.52557 004.CSV
21.52550 005.CSV
21.52545 006.CSV
21.52540 007.CSV
21.52535 008.CSV
21.52530 009.CSV

First we look into a typical off resonant scan and apply an exponential fit:

    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 63
        # data points      = 16369
        # variables        = 3
        chi-square         = 299.453115
        reduced chi-square = 0.01829727
        Akaike info crit   = -65489.4254
        Bayesian info crit = -65466.3160
    [[Variables]]
        amp:     12.0552112 +/- 0.00801156 (0.07%) (init = 0.008689544)
        phase:   0 (fixed)
        lamb:   -582.919227 +/- 0.64330752 (0.11%) (init = -1)
        offset:  0.29124029 +/- 0.00215269 (0.74%) (init = 0)
    [[Correlations]] (unreported correlations are < 0.100)
        C(lamb, offset) = -0.7693
        C(amp, lamb)    = -0.7256
        C(amp, offset)  = +0.2770

    Decay half life time: 1.1890964454245143ms

Typical slightly off resonant pulse response

It is important to emphasize that this decay constant is neither $T_2$ nor $T_1$. It corresponds to the free induction decay time $T_2^*$, which is dominated by reversible dephasing mechanisms such as static field inhomogeneities and frequency dispersion across the spin ensemble. These effects are largely refocusable by echo techniques and therefore do not represent irreversible coherence loss.

Showing beatings when (close) off resonant

To tune the frequency one can actually utilize beatings between an off resonant signal and the on resonant emission by the nuclear spins. This happens due to mixing of two sine functions ($sin(\omega_1 * t) * sin(\omega_2 * t) = \frac{1}{2} \left( sin(\omega_1 + \omega_2) + sin(\omega_1 - \omega2) \right)$. The effective frequency one can see in the beatings corresponds to the $sin(\omega_1 - \omega_2)$ term. To optimize the signal one wants to see where the beatings vanish (i.e. moving the excitation signal on resonant). On both sides of the resonance beatings should occur:

I/Q samples for different detunings

Determining $\frac{\pi}{2}$ and $\pi$ pulse duration

The pulse length during pulsed NMR and EPR determines how far one rotates the orientation of the effective spin ensemble on the Bloch sphere (see also the theory article that includes an animation). When hitting $\frac{\pi}{2}$ one kicks the spins in the classical picture mainly into the equatorial plane where one would expect the maximum signal amplitude. For a $\pi$ pulse the spins should flip into the exact opposite state so one would expect a minimum again. One tunes the signal so one sees a local maximum at $\frac{\pi}{2}$. In the following example due to time constraints the measurement stopped near the maximum - but since one knows this should be a sine one can just fit a sine and extract the peak to determine the $\frac{\pi}{2}$ duration afterwards.

The measurements have been performed in a range from 1 to 5 microseconds. After taking a look at this data another set will be shown that takes the oscillations at timescales up to 18 microseconds. I will use the first peak to determine where the maximum population is reached. This should be the $\frac{\pi}{2}$ pulse. When exceeding $\frac{\pi}{2}$ the amplitude should decrease again:

    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 254
        # data points      = 41
        # variables        = 4
        chi-square         = 0.14885819
        reduced chi-square = 0.00402319
        Akaike info crit   = -222.351663
        Bayesian info crit = -215.497375
    ##  Warning: uncertainties could not be estimated:
        offset:  at initial value
    [[Variables]]
        amp:    -6.84039540 (init = 1.40625)
        phase:  -1.0319e-05 (init = 0)
        omega:   272690.913 (init = 334212)
        offset:  5.47695000 (init = 5.47695)

    Fitted length of pi/2 pulse: 5.760354493909174e-06

Truncated amplitude of pulse response for different pulse durations

Rabi Oscillations

A second series has been taken at a different day so pulse duration will be different. The goal was to illustrate the occurring Rabi oscillations as predicted by theory for any two level quantum system. To do this I’ve taken a dataset that sweeps pulse durations up to 18 microseconds:

    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 98
        # data points      = 171
        # variables        = 4
        chi-square         = 38.1163978
        reduced chi-square = 0.22824190
        Akaike info crit   = -248.674245
        Bayesian info crit = -236.107591
    [[Variables]]
        amp:     1.69571743 +/- 0.05211053 (3.07%) (init = 2.827147)
        phase:  -3.5773e-06 +/- 7.8267e-08 (2.19%) (init = 0)
        omega:   658072.149 +/- 6487.09542 (0.99%) (init = 334212)
        offset:  4.35774641 +/- 0.03856861 (0.89%) (init = 3.171873)
    [[Correlations]] (unreported correlations are < 0.100)
        C(phase, omega)  = -0.8002
        C(phase, offset) = -0.2952
        C(omega, offset) = +0.2810
        C(amp, offset)   = -0.1210

    Fitted length of pi/2 pulse: 4.773933461457757e-06

Rabi oscillations

Rabi oscillations are fully consistent with the quantum two-level description of nuclear spins. While a classical Bloch-vector model reproduces the same nutation phenomenology at the ensemble level, the oscillations originate from coherent quantum population transfer between spin states. This confirms the quantum nature of the system.

Hahn echo

Another fascinating technique from spin resonance is the so called Hahn echo. A $\frac{\pi}{2}$ pulse is followed by a $\pi$ pulse. In the following experiment I assumed $4.76 \mu s$ for the $\frac{\pi}{2}$ pulse and $9.52 \mu s$ for the $\pi$ pulse which may be slightly off but this does not matter too much for demonstration purposes as we will see. The result will be that we kick the spins back over the equator a second time and know exactly when they will pass the equator and re-phase after dephasing as before again - this will be two times the period between the $\frac{\pi}{2}$ and $\pi$ pulse. This means we can just sample around this time and know exactly where to look for the signal. Of course spin decay will have reduced the number of spins in the excited state during this period so we will start to see the falloff in amplitude. Measuring for multiple delay times between the $\frac{\pi}{2}$ and the $\pi$ pulse allows us to measure the transversal relaxation time $T_2$ - that is caused by loss of phase coherence of the individual spins and not the lifetime of the state - by spotting the decay over time.

The fit model we are using will be using is

[ \begin{aligned} A(\tau) &= A_0 e^{-\frac{2 \tau}{T_2}} + c \end{aligned} ]

The experimental gain of performing a Hahn echo sequence is:

The sequence is simple, robust and exhibits minimal error budget for the pulses. On the downside we take only one data point per acquisition and are sensitive to long-term drifts due to the slow acquisition sequences. It’s very inefficient for weak signals that we would encounter when not working with a benchmark sample.

The delays used had been:

Delay (ms) Measurement
3 051
5 052
7 053
10 054
15 055
20 056
25 057

Hahn echo sequences for different echo delays

Now we are going to perform a fit on the (smoothed) echo peaks of the sequence, which are easy to locate due to the known delay:

Hahn echo sequences for different echo delays, utilized peaks

    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 25
        # data points      = 7
        # variables        = 3
        chi-square         = 0.00311007
        reduced chi-square = 7.7752e-04
        Akaike info crit   = -48.0331317
        Bayesian info crit = -48.1954013
    [[Variables]]
        C:   0.31594783 +/- 0.06568671 (20.79%) (init = 0.7324223)
        A0:  4.30886266 +/- 0.04695737 (1.09%) (init = 2.880859)
        T2:  0.02203301 +/- 0.00100317 (4.55%) (init = 0.011)
    [[Correlations]] (unreported correlations are < 0.100)
        C(C, T2)  = -0.9449
        C(C, A0)  = -0.4693
        C(A0, T2) = +0.2009

Fitted echo signal amplitudes during Hahn echo

    T2 = 0.022033006655823528 s

The fit shows us a $T_2$ time of $\approx 22ms$

Carr Purcell Multipulse Sequence

Of course there is no need to re-iterate the experiment all the time after exciting them via a $\frac{\pi}{2}$ pulse. We can just apply a $\pi$ pulse in form of a pulse train all the time continuously. This is the so called Carr-Purcell multipulse sequence. It will kick the decaying spins back over the equator again and again. This works like a repeated Hahn echo. In the following example we will see a train of $\pi$ pulses every 3 milliseconds in the first scan or 1.5 milliseconds in the second scan. We can then again sample the local maxima at the given intervals and perform a fit to estimate the effective (not the intrinsic) $T_{2,eff}$.

Our fit model assumes

\[\begin{aligned} A_n &\approx A_0 e^{-\frac{t_n}{T_{2,eff}}} \end{aligned}\]

This sequence:

On the downside pulse errors accumulate coherently. If the $\pi$ pulses are imperfect (as was during the measurement shown below) phase errors build up rapidly.

Carr-Purcell echo sequence

    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 25
        # data points      = 7
        # variables        = 3
        chi-square         = 0.00436836
        reduced chi-square = 0.00109209
        Akaike info crit   = -45.6549510
        Bayesian info crit = -45.8172206
    [[Variables]]
        C:  -0.42254271 +/- 0.08323068 (19.70%) (init = 0.2539058)
        A0:  5.68844876 +/- 0.06077839 (1.07%) (init = 3.515622)
        T2:  0.01924366 +/- 8.3956e-04 (4.36%) (init = 0.01803906)
    [[Correlations]] (unreported correlations are < 0.100)
        C(C, T2)  = -0.9564
        C(C, A0)  = -0.4028
        C(A0, T2) = +0.1625

Fitted decay curve in the Carr-Purcell echo sequence

The fitted $T_{2,eff} \approx 19.24$ milliseconds.

A Second Sequence at Double the Speed

A second Carr-Purcell sequence at double the speed

    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 21
        # data points      = 7
        # variables        = 3
        chi-square         = 0.00279784
        reduced chi-square = 6.9946e-04
        Akaike info crit   = -48.7737318
        Bayesian info crit = -48.9360013
    [[Variables]]
        C:  -1.51790757 +/- 0.17378382 (11.45%) (init = 0.4101566)
        A0:  7.00752981 +/- 0.13620676 (1.94%) (init = 3.87695)
        T2:  0.01602923 +/- 7.9455e-04 (4.96%) (init = 0.00894972)
    [[Correlations]] (unreported correlations are < 0.100)
        C(C, T2)  = -0.9860
        C(C, A0)  = -0.9682
        C(A0, T2) = +0.9196

Fit result of the second Carr-Purcell sequence

This yields a $T_{2,eff} \approx 16.03$ ms

Carr-Purcell-Meiboom-Gill (CPMG) Sequence

The idea behind the Carr-Purcell-Meiboom-Gill (CPMG) sequence is to rotate the measurement frame after initially kicking the system out of equilibrium. This is done by shifting the phase of the $\pi$ pulses by 90 degrees with respect to the $\frac{\pi}{2}$ pulse. Thus we perform measurement along the orthogonal axis. This suppresses systematic pulse-angle errors instead of accumulating them. This leads to:

The measurement is still $T_{2,eff}$ but it’s usually much closer to the real result than the Carr-Purcell sequence.

Carr-Purcell-Meiboom-Gill sequence measurement, shifting the phase of the echo pulses

    [[Fit Statistics]]
        # fitting method   = leastsq
        # function evals   = 25
        # data points      = 13
        # variables        = 3
        chi-square         = 0.00643600
        reduced chi-square = 6.4360e-04
        Akaike info crit   = -92.9403611
        Bayesian info crit = -91.2455130
    [[Variables]]
        C:   0.17757653 +/- 0.01983763 (11.17%) (init = 0.2685544)
        A0:  4.64667558 +/- 0.03730376 (0.80%) (init = 3.432619)
        T2:  0.02116024 +/- 4.0632e-04 (1.92%) (init = 0.03602664)
    [[Correlations]] (unreported correlations are < 0.100)
        C(C, T2)  = -0.8595
        C(A0, T2) = -0.5189
        C(C, A0)  = +0.1313

Fit for the Carr-Purcell-Meiboom-Gill sequence

This yields again a $T_{2,eff} \approx 21.16$ milliseconds.

Conclusion

The above examples have shown how nuclear magnetic spin resonance looks like - and has confirmed its quantum nature - via an extremely simple setup that one can easily build at home (just requiring a mixer, a diode detector, an oscilloscope and a (programmable) function generator and a directional coupler for the given frequencies and fast recovery diodes for protecting the input of the low noise amplifier). It utilized a permanent magnet which turned out to be a not so good idea though due to thermal drift of the experiment.

The measurement results for $T_{2,eff}$ were consistent over the different measurements, which shows that application of a very simple measurement setup can already yield surprisingly good results:

Measurement method $T_{2,eff}$
Hahn echo $\approx$ 22ms
Carr-Purcell $\approx$ 16-19ms
Carr-Purcell-Meiboom-Gill $\approx$ 21ms

The deviation of the Carr-Purcell sequence demonstrates the increasing impact of pulse imperfections as the number of refocusing pulses grows.

Note that $T_1$ was not measured and is not extractable from the data. $T_1$ describes the recovery of longitudinal magnetization. None of the shown sequences measure $M_z(\tau)$, I always probed transverse components! Measuring $T_1$ would require saturation or inversion recovery measurement with a variable repetition delay. This has not been done in this short article.

References

Some excellent books:

This article is tagged: Physics, Electrodynamics, Basics, How stuff works, Tutorial, Measurements, Quantum mechanics, Quantum optics, Experiment


Data protection policy

Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)

This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/

Valid HTML 4.01 Strict Powered by FreeBSD IPv6 support