{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "<center><img width=\"400em\" src=\"./logo.svg\"/></center>\n",
    "\n",
    "# Demo\n",
    "\n",
    "In this demo we will\n",
    "\n",
    "1. setup the spectrometer, \n",
    "2. record a free induction decay (FID) and \n",
    "3. do some simple analysis.\n",
    "\n",
    "To prevent any confusion we'll look at all the code, but glance over some parts more quickly.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Imports\n",
    "\n",
    "* First, some standard `imports` you might already be familiar with\n",
    "* These make the imported `modules` / `classes` / `functions` available later\n",
    "* The `%matplotlib widget` makes the graphs interactive\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Python imports\n",
    "from pathlib import Path\n",
    "\n",
    "# Standard science imports\n",
    "import numpy as np\n",
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "import nmrglue as ng\n",
    "\n",
    "# Spectrometer imports\n",
    "from spectrometer import (\n",
    "    FID1D,\n",
    "    ConnectionSettings,\n",
    "    Delay,\n",
    "    NMRSequence,\n",
    "    Pulse,\n",
    "    Record,\n",
    "    Server,\n",
    "    Spectrometer,\n",
    "    plot,\n",
    "    process,\n",
    ")\n",
    "\n",
    "%matplotlib widget"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Setup \n",
    "\n",
    "### Prepare the hardware\n",
    "\n",
    "The basic steps are:\n",
    "\n",
    "1. Compiling and flashing the FPGA firmware\n",
    "2. Setting up the Spectrometer server\n",
    "3. Connecting to the server\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Connect the server platform (i.e. the RedPitaya)\n",
    "server = Server(\"192.168.1.100\")\n",
    "\n",
    "# Flash the FPGA bitstream (or \"low level server\")\n",
    "server.flash_fpga()\n",
    "\n",
    "# Compile the server on the spectrometer (or \"high level server\")\n",
    "server.setup()\n",
    "\n",
    "# Start the server on the spectrometer\n",
    "server.start()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### Connect to the spectrometer server\n",
    "\n",
    "Now that the server is running we can connect to it.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Setup the spectrometer connection\n",
    "connection_settings = ConnectionSettings(ip_address=\"192.168.1.100\")\n",
    "\n",
    "# Create the spectrometer object\n",
    "spectrometer = Spectrometer(\n",
    "    tx_freq=25_091_000,  # Center transmission frequency\n",
    "    rx_freq=None,  # Receive frequency\n",
    "    sample_rate=320_000,  # samples/second\n",
    "    server_config=connection_settings,\n",
    ")\n",
    "\n",
    "# Connect to the spectrometer server\n",
    "spectrometer.connect()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Perform an experiment\n",
    "\n",
    "Now we want to perform a simple experiment.\n",
    "\n",
    "Here we'll send a single pulse and look at the received signal\n",
    "\n",
    " 1. Prepare the pulse sequence\n",
    " 2. Send the sequence and save the received signal\n",
    " 3. Processing\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### Preparing a pulse sequence\n",
    "\n",
    "We have multiple options for defining sequences:\n",
    "\n",
    "1. Use the simple built-in ones (`simple`, `spin_echo`)\n",
    "2. Build our own from a list of instructions\n",
    "3. Define the control arrays manually   \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "We'll define a pulse with\n",
    "\n",
    "* 9us length\n",
    "* 25us delay\n",
    "* 20ms record\n",
    "\n",
    "using all three methods.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "seq_simple = NMRSequence.simple(pulse_length_us=9, delay_us=25, record_length_us=20_000)\n",
    "# same as\n",
    "seq_built = NMRSequence.build(\n",
    "    [Pulse(duration_us=9, power=1), Delay(duration_us=25), Record(duration_us=20_000)]\n",
    ")\n",
    "# or\n",
    "seq_manual = NMRSequence(\n",
    "    tx_sequence=(np.array([0, 9]), np.array([1, 0])), rx_sequence=np.array([34, 20034])\n",
    ")\n",
    "\n",
    "assert seq_simple == seq_built == seq_manual"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### Sending the pulse sequence\n",
    "\n",
    "Since all the sequences are the same we can sent any of them\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Send the sequence\n",
    "data = spectrometer.send_sequence(seq_simple, debug=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "`data` contains the complex QI-demodulated data as a one dimensional `numpy` array.\n",
    "\n",
    "Now we can put the data into a simple container with the relevant metadata and save it to a file for later processing.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Create FID object\n",
    "fid = FID1D(\n",
    "    data=data,\n",
    "    spectral_width=spectrometer.sample_rate,\n",
    "    carrier_freq=0.0,\n",
    "    observation_freq=spectrometer.rx_freq,\n",
    "    label=\"1H\",\n",
    "    sample=\"Water\",\n",
    "    pulse=\"single_90_degree_pulse,length=9us,delay=30us\",\n",
    "    spectrometer=\"magnETHical v0.1\",\n",
    ")\n",
    "\n",
    "# Save to file\n",
    "time = fid.timestamp.strftime(\"%Y%m%d-%H%M%S\")\n",
    "file = Path(f\"../../data/{time}-{fid.sample}-{fid.label}-{fid.pulse}.fid\")\n",
    "fid.to_file(file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "The data is stored in standard `nmrpipe` format, thus further analysis can be conducted in any other tooling capable of reading that format.\n",
    "\n",
    "For more features and further information, see the various `record_*` and `process_*` scripts in the `scripts` folder and the library reference.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Stop the server\n",
    "server.stop()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Analyzing the received signal\n",
    "\n",
    "Having conducted all of our experiments, we can load the data from file to analyze it. \n",
    "\n",
    "* outside of Python, e.g. in `NMRPipe`, \n",
    "* in Python using e.g. `nmrglue`,                    `numpy`,                    `scipy`,                    `matplotlib`, ... or\n",
    "* any tooling that can read `NMRPipe` `.fid` files\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### Loading the data back into Python\n",
    "\n",
    "We'll now quickly look at the data with some small helper functions built around `nmrglue`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Load data back from file\n",
    "fid = FID1D.from_file(file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "### Plot the data\n",
    "\n",
    "Now we can plot the time domain data\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Take a quick look at the time domain data (i.e. the FID)\n",
    "fig = fid.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Try to plot simple least squares fit for a decaying sinusoid\n",
    "decaying_sinus = process.decaying_sinus.fit(fid.ms_scale, fid.data.real)\n",
    "ms_scale_fine = np.linspace(fid.ms_scale[0], fid.ms_scale[-1], len(fid.ms_scale) * 10)\n",
    "\n",
    "# Plot raw values again\n",
    "fig = fid.plot()\n",
    "\n",
    "# Plot fit\n",
    "_ = fig.axes[0].plot(\n",
    "    ms_scale_fine,\n",
    "    decaying_sinus(ms_scale_fine),\n",
    "    linestyle=\"--\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Take a look at the fit parameters\n",
    "print(decaying_sinus)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "There's also a function to quickly get a simple spectrum.\n",
    "\n",
    "This function does by default\n",
    "* zero-fill\n",
    "* complex fft\n",
    "* simple automatic phase correction\n",
    "    - by minimizing the difference of minima around the highest maximum\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Take a quick look at the spectrum\n",
    "# (includes an auto zero-fill, complex fft and simple auto phase correction)\n",
    "spectrum, p0 = fid.spectrum()\n",
    "print(\"Zero-order phase correction:\", p0)\n",
    "\n",
    "# Plot the real part\n",
    "fig = spectrum.hz.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "We could - for example - crop the data a bit \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# FFT again cause we can\n",
    "# We can also manually specify the zero-order phase shift by passing `phase_shift_kwargs={\"p0\": 68}`\n",
    "spectrum, _ = fid.spectrum(phase_shift_kwargs={\"p0\": p0 + 0})\n",
    "\n",
    "# Crop\n",
    "cropped_spectrum = spectrum.hz.crop_around(spectrum.hz.max_peak, 2000)\n",
    "fig = cropped_spectrum.hz.plot(linestyle=\"-\", marker=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### Do a fit\n",
    "\n",
    "Now we can take the peak and try to do a Lorentz fit to get some interesting paramters:\n",
    "\n",
    "* amplitude\n",
    "* position\n",
    "* peak width (gamma)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Perform least squared fit of a lorentzian mixture on detected peaks above 3*noise threshold\n",
    "fit_spectrum, fitpeaks = cropped_spectrum.hz.fit()\n",
    "peaks = cropped_spectrum.hz.peaks()\n",
    "\n",
    "# Plot\n",
    "fig = cropped_spectrum.hz.plot(linestyle=\"-\", marker=\"\", label=\"raw\")\n",
    "fig.axes[0].plot(cropped_spectrum.hz.scale, fit_spectrum, linestyle=\"--\", label=\"fit\")\n",
    "fig.axes[0].plot(\n",
    "    [peak.location for peak in peaks],\n",
    "    [peak.amplitude.real for peak in peaks],\n",
    "    linestyle=\"\",\n",
    "    marker=\"o\",\n",
    "    color=\"red\",\n",
    "    label=\"peaks\",\n",
    ")\n",
    "fig.legend()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Look at the first peak\n",
    "print(peaks[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## That's it!\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
