{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import numpy as np\n",
    "import scipy.fft as ft\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.gridspec as gridspec\n",
    "import matplotlib.ticker as tck\n",
    "rng = np.random.default_rng()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# copied from 01_fourier 4988cf4f6e81b6b9510bf55a264011c37dc71872\n",
    "def ft_spectrum( signal, sample_rate, fft=None, freq=None, mask_bias=False):\n",
    "    \"\"\"Return a FT of $signal$, with corresponding frequencies\"\"\"\n",
    "    n_samples = len(signal)\n",
    "    real_signal = np.isrealobj(signal)\n",
    "    \n",
    "    if fft is None:\n",
    "        if real_signal:\n",
    "            fft = ft.rfft\n",
    "            freq = ft.rfftfreq\n",
    "        else:\n",
    "            fft = ft.fft\n",
    "            freq = ft.fftfreq\n",
    "\n",
    "    if freq is None:\n",
    "        freq = ft.fftfreq\n",
    "    \n",
    "    spectrum = fft(signal) / sample_rate\n",
    "    freqs = freq(n_samples, 1/sample_rate)\n",
    "    \n",
    "    if not mask_bias:\n",
    "        return spectrum, freqs\n",
    "    else:\n",
    "        return spectrum[1:], freqs[1:]\n",
    "\n",
    "    \n",
    "def plot_spectrum( ax, spectrum, freqs, plot_complex=False, plot_power=False, plot_amplitude=None):\n",
    "    \"\"\" Plot a signal's spectrum on an Axis object\"\"\"\n",
    "    plot_amplitude = plot_amplitude or (not plot_power and not plot_complex)\n",
    "    alpha = 1\n",
    "    \n",
    "    ax.set_title(\"Spectrum\")\n",
    "    ax.set_xlabel(\"f (Hz)\")\n",
    "    ylabel = \"\"\n",
    "    if plot_amplitude or plot_complex:\n",
    "        ylabel = \"Amplitude\"\n",
    "    if plot_power:\n",
    "        if ylabel:\n",
    "            ylabel += \"|\"\n",
    "        ylabel += \"Power\"\n",
    "    ax.set_ylabel(ylabel)\n",
    "\n",
    "    if plot_complex:\n",
    "        alpha = 0.5\n",
    "        ax.plot(freqs, np.real(spectrum), '.-', label='Real', alpha=alpha)\n",
    "        ax.plot(freqs, np.imag(spectrum), '.-', label='Imag', alpha=alpha)\n",
    "\n",
    "    if plot_power:\n",
    "        ax.plot(freqs, np.abs(spectrum)**2, '.-', label='Power', alpha=alpha)\n",
    "    \n",
    "    if plot_amplitude:\n",
    "        ax.plot(freqs, np.abs(spectrum), '.-', label='Abs', alpha=alpha)\n",
    "\n",
    "    ax.legend()\n",
    "\n",
    "    return ax\n",
    "\n",
    "\n",
    "def plot_phase( ax, spectrum, freqs, ylim_epsilon=0.5):\n",
    "    ax.set_ylabel(\"Phase\")\n",
    "    ax.set_xlabel(\"f (Hz)\")\n",
    "\n",
    "    ax.plot(freqs, np.angle(spectrum), '.-')\n",
    "    ax.set_ylim(-1*np.pi - ylim_epsilon, np.pi + ylim_epsilon)\n",
    "    \n",
    "    return ax\n",
    "\n",
    "\n",
    "def plot_combined_spectrum(spectrum, freqs, \n",
    "                           spectrum_kwargs={}, fig=None, gs=None):\n",
    "    \"\"\"Plot both the frequencies and phase in one figure.\"\"\"\n",
    "    \n",
    "    # configure plotting layout\n",
    "    if fig is None:\n",
    "        fig = plt.figure(figsize=(8, 16))\n",
    "\n",
    "    if gs is None:\n",
    "        gs = gridspec.GridSpec(2, 1, figure=fig, height_ratios=[3,1], hspace=0)\n",
    "\n",
    "    ax1 = fig.add_subplot(gs[:-1, -1])\n",
    "    ax2 = fig.add_subplot(gs[-1, -1], sharex=ax1)\n",
    "\n",
    "    axes = np.array([ax1, ax2])\n",
    "    \n",
    "    # plot the spectrum \n",
    "    plot_spectrum(ax1, spectrum, freqs, **spectrum_kwargs)\n",
    "\n",
    "    # plot the phase\n",
    "    plot_phase(ax2, spectrum, freqs)\n",
    "\n",
    "    ax1.xaxis.tick_top()\n",
    "    [label.set_visible(False) for label in ax1.get_xticklabels()]\n",
    "    \n",
    "    return fig, axes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def phase_modulo(phase):\n",
    "    \"\"\"\n",
    "    Modulo phase such that it falls within the interval [\\pi, \\pi)\n",
    "    \"\"\"\n",
    "\n",
    "    return (phase + np.pi) % (2*np.pi) - np.pi"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Phase information in the Fourier Transform\n",
    "\n",
    "$$\n",
    "u(t) = sin(2\\pi f t + \\varphi_t)\n",
    "$$\n",
    "\n",
    "Define $f_\\mathrm{max}$ as the frequency with the highest power in the FT (it should be close to $f$).\n",
    "Then $\\varphi_f$ is its associated phase."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Required signal length is: 0.005s\n",
      "Required number of samples: 50.0\n"
     ]
    }
   ],
   "source": [
    "sample_rate = 1/1e-4 # Hz\n",
    "f = 200 # Hz\n",
    "required_N_samples = sample_rate/f\n",
    "\n",
    "signal_func = lambda phase: np.sin(phase)\n",
    "\n",
    "# set signal_func to exp(i*phi)\n",
    "if False:\n",
    "    signal_func = lambda phase: np.exp(1j*phase)\n",
    "\n",
    "print(\"Required signal length is: {}s\".format(1/f))\n",
    "print(\"Required number of samples: {}\".format(required_N_samples))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### $\\varphi_f$ vs $f_\\mathrm{max}$ for differing $\\Delta f$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 106388 characters]\n",
      "text/plain": [
       "<Figure size 1152x576 with 4 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# \\phi_f vs f_max for differing \\Delta f\n",
    "N_delta_f = 200\n",
    "\n",
    "plot_submax = True\n",
    "\n",
    "\n",
    "Ns_samples = required_N_samples//1 + np.arange(0, N_delta_f)\n",
    "phi_f = np.empty(N_delta_f)\n",
    "f_max = np.empty(N_delta_f)\n",
    "\n",
    "if plot_submax:\n",
    "    phi_f_sub = np.empty(N_delta_f)\n",
    "    f_submax  = np.empty(N_delta_f)\n",
    "\n",
    "for i, N_sample in enumerate(Ns_samples):\n",
    "    time = np.arange(N_sample) / sample_rate\n",
    "    \n",
    "    fft, freqs = ft_spectrum(signal_func(2*np.pi*f*time), sample_rate)\n",
    "    \n",
    "    fft_power = np.abs(fft)**2\n",
    "    id_max = np.argmax(fft_power)\n",
    "    \n",
    "    phi_f[i] = np.angle(fft[id_max])\n",
    "    f_max[i] = freqs[id_max]\n",
    "    \n",
    "    if plot_submax:\n",
    "        fft_power[id_max] = 0\n",
    "        id_submax = np.argmax( fft_power )\n",
    "        \n",
    "        phi_f_sub[i] = np.angle(fft[id_submax])\n",
    "        f_submax[i]  = freqs[id_submax]\n",
    "\n",
    "if plot_submax:\n",
    "    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2, figsize=(16,8), sharex=\"col\")\n",
    "    fig.suptitle(\"Maximum (and sub maximum) Power frequencies and their phase\")\n",
    "else:\n",
    "    fig, (ax1, ax2) = plt.subplots(1,2, figsize=(16,4))\n",
    "    fig.suptitle(\"Maximum Power frequencies and their phase\")\n",
    "        \n",
    "    \n",
    "# Maximum values\n",
    "ax1.set_xlabel('$f_\\\\mathrm{max}$')\n",
    "ax1.set_ylabel('$\\\\varphi_f$')\n",
    "ax1.plot(f_max, phi_f, '--', alpha=0.1)\n",
    "sc = ax1.scatter(f_max, phi_f, c=Ns_samples, cmap='Spectral')\n",
    "ax1.axvline(f, color='r', alpha=0.5, label=\"Signal frequency\")\n",
    "for hline in [0, -np.pi/2]:\n",
    "    ax1.axhline(hline, color='k', alpha=0.5)\n",
    "\n",
    "ax2.set_xlabel('$N_\\\\mathrm{samples}$')\n",
    "ax2.set_ylabel('$f_\\\\mathrm{max}$')\n",
    "ax2.scatter(Ns_samples, f_max, c=Ns_samples, cmap='Spectral')\n",
    "ax2.axhline(f, color='r', alpha=0.5, label=\"Signal frequency\")\n",
    "\n",
    "# SubMaximum values\n",
    "if plot_submax:\n",
    "    \n",
    "    # filter submax frequencies above twice the frequency\n",
    "    if True:\n",
    "        idx_submax = np.argwhere(np.abs(f_submax) < 2*f)\n",
    "        \n",
    "        f_submax = f_submax[idx_submax]\n",
    "        phi_f_sub = phi_f_sub[idx_submax]\n",
    "        \n",
    "        Ns_samples_submax = Ns_samples[idx_submax]\n",
    "    else:\n",
    "        Ns_samples_submax = Ns_samples\n",
    "        \n",
    "    \n",
    "    ax3.set_xlabel('$f_\\\\mathrm{submax}$')\n",
    "    ax3.set_ylabel('$\\\\varphi_{f\\_sub}$')\n",
    "    ax3.plot(f_submax, phi_f_sub, '--', alpha=0.1)\n",
    "    sc = ax3.scatter(f_submax, phi_f_sub, c=Ns_samples_submax, cmap='Spectral')\n",
    "    ax3.axvline(f, color='r', alpha=0.5, label=\"Signal frequency\")\n",
    "    for hline in [0, -np.pi/2]:\n",
    "        ax3.axhline(hline, color='k', alpha=0.5)\n",
    "\n",
    "    ax4.set_xlabel('$N_\\\\mathrm{samples}$')\n",
    "    ax4.set_ylabel('$f_\\\\mathrm{submax}$')\n",
    "    ax4.scatter(Ns_samples_submax, f_submax, c=Ns_samples_submax, cmap='Spectral')\n",
    "    ax4.axhline(f, color='r', alpha=0.5, label=\"Signal frequency\")\n",
    "\n",
    "\n",
    "\n",
    "if False:\n",
    "    res = 50\n",
    "    ax1.set_xlim(f-res, f+res)\n",
    "    ax2.set_ylim(f-res, f+res)\n",
    "    if plot_submax:\n",
    "        ax4.set_ylim(f-res, f+res)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## $\\varphi_f$ vs $\\varphi_t$ and the effect of $f/f_\\mathrm{sample}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 72612 characters]\n",
      "text/plain": [
       "<Figure size 1152x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "sample_rate = 1/1e-4 # Hz\n",
    "\n",
    "phase_offsets = np.linspace(-np.pi, np.pi, 500, endpoint=True)# rad\n",
    "\n",
    "frequencies = sample_rate * np.array([0.5, 0.49, 0.45, 0.3, 0.28, 0.25, 0.05, 0.001])\n",
    "\n",
    "signal_func = lambda phase: np.sin(phase)\n",
    "\n",
    "\n",
    "# Precreate figure and axis\n",
    "fig, (ax1) = plt.subplots(1,1, figsize=(16,4))\n",
    "\n",
    "for f in frequencies[::-1]:\n",
    "    \n",
    "    required_N_samples = sample_rate/f\n",
    "\n",
    "    phi_f = np.empty_like(phase_offsets)\n",
    "\n",
    "    time = np.arange(required_N_samples) / sample_rate\n",
    "\n",
    "    for i, offset in enumerate(phase_offsets):\n",
    "\n",
    "        fft, freqs = ft_spectrum(signal_func(2*np.pi*f*time + offset), sample_rate)       \n",
    "        id_max = np.argmax(np.abs(fft)**2)\n",
    "\n",
    "        phi_f[i] = np.angle(fft[id_max])\n",
    "    \n",
    "    \n",
    "    ax1.plot(phase_offsets, phi_f, '.--', label=\"$f/f_\\\\mathrm{{sample}} = {}$\".format(f/sample_rate))\n",
    "\n",
    "    if True:\n",
    "        id_phi_f_min = np.argmin(phi_f)\n",
    "        ylocation = (np.max(phi_f) + np.min(phi_f)) /2\n",
    "        ax1.text(phase_offsets[id_phi_f_min], ylocation, \"${:.2g}\\\\pi$\".format(phase_offsets[id_phi_f_min]/np.pi), horizontalalignment='center')\n",
    "\n",
    "        \n",
    "ax1.set_title(\"Frequencydomain phase of max-power frequency $\\\\varphi_f$ \\n vs Timedomain phase $\\\\varphi_t$ with varying $f / f_\\\\mathrm{{sample}}$\")\n",
    "ax1.set_xlabel('$\\\\varphi_t$')\n",
    "ax1.set_ylabel('$\\\\varphi_f$')\n",
    "ax1.legend(loc='lower right')\n",
    "\n",
    "# grid lines\n",
    "## vertical lines\n",
    "vlines = [\n",
    "    (-np.pi, r'$-\\pi$'),\n",
    "    (-np.pi/np.sqrt(2), r'$\\frac{-\\pi}{\\sqrt{2}}$'),\n",
    "    (-np.pi/np.sqrt(3), r'$\\frac{-\\pi}{\\sqrt{3}}$'),\n",
    "    (-np.pi/2, r'$\\frac{-\\pi}{2}$'),\n",
    "    (np.pi, r'$\\pi$'),\n",
    "]\n",
    "\n",
    "xtrans = ax1.get_xaxis_transform()\n",
    "ax1.axhline(0, alpha=0.1, color='k')\n",
    "for location, label in vlines:\n",
    "    ax1.axvline(location, alpha=0.1, color='k')\n",
    "    ax1.text(location, -0.06, label, transform=xtrans, horizontalalignment='center')\n",
    "\n",
    "## horizontal lines\n",
    "hlines = [\n",
    "    (1, ''),\n",
    "    (-2, ''),\n",
    "    (-np.pi/2, r'$\\frac{-\\pi}{2}$'),\n",
    "    (np.pi/2, r'$\\frac{\\pi}{2}$'),\n",
    "]\n",
    "\n",
    "ytrans = ax1.get_yaxis_transform()\n",
    "ax1.axvline(0, alpha=0.1, color='k')\n",
    "for location, label in hlines:\n",
    "    ax1.axhline(location, alpha=0.1, color='k')\n",
    "    ax1.text(-0.03, location, label, transform=ytrans, verticalalignment='center')\n",
    "    \n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For $f_\\mathrm{sample} \\geq 3f$ the relationship between $\\varphi_t$ and $\\varphi_f$ is (almost) linear.\n",
    "\n",
    "From $f_\\mathrm{sample} \\geq 4f$ onwards, this relationship is stable with\n",
    "\n",
    "$$\n",
    "\\varphi_f = \\varphi_t - \\frac{\\pi}{2} \\delta_\\mathrm{sin}\n",
    ",\n",
    "$$\n",
    "where $\\delta_\\mathrm{sin}$ is 1 if the signal was a sine, and 0 for a cosine."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# reconstruct phase from off-frequency ft\n",
    "\n",
    "Require atleast $f_\\mathrm{sample} \\geq 4 f$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Required signal length is: 0.01s\n",
      "Required number of samples: 100.0\n",
      "Phase to be retrieved: -1.5707963267948966\n"
     ]
    }
   ],
   "source": [
    "sample_rate = 1/1e-4 # Hz\n",
    "f = 100 # Hz\n",
    "required_N_samples = sample_rate/f\n",
    "\n",
    "phase_to_retrieve = phase_modulo(-np.pi/2)\n",
    "signal_func = lambda phase: np.cos(phase + phase_to_retrieve)\n",
    "\n",
    "print(\"Required signal length is: {}s\".format(1/f))\n",
    "print(\"Required number of samples: {}\".format(required_N_samples))\n",
    "print(\"Phase to be retrieved: {}\".format(phase_to_retrieve))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 58972 characters]\n",
      "text/plain": [
       "<Figure size 1152x288 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "N_deltas = 0*required_N_samples + np.arange(1, 50*required_N_samples+1, 1) # set !={0, required_N_sample} for imperfect FT\n",
    "\n",
    "\n",
    "# flags\n",
    "unfold_phases = True\n",
    "N_sub_max = 2 # how many peaks to get\n",
    "enable_single_max = True\n",
    "diffs_on_single_max = True\n",
    "keep_single_max_phase = True\n",
    "\n",
    "\n",
    "\n",
    "retrieved_phases = np.empty_like(N_deltas)\n",
    "submax_amplitudes = np.empty((len(N_deltas), N_sub_max))\n",
    "\n",
    "fig, axes = plt.subplots(1,2, figsize=(16,4))\n",
    "\n",
    "for i, N_delta in enumerate(N_deltas):\n",
    "    # require N_sub => 2\n",
    "    N_sub_max = max(2, N_sub_max)\n",
    "    idx_max = np.empty(N_sub_max, dtype=np.int)\n",
    "    fft_power_max = np.zeros(N_sub_max)\n",
    "\n",
    "    time = np.arange(required_N_samples+N_delta) / sample_rate\n",
    "    \n",
    "    f_delta = sample_rate/(required_N_samples+N_delta)\n",
    "\n",
    "    fft, ft_freqs = ft_spectrum(signal_func(2*np.pi*f*time), sample_rate)\n",
    "    fft_power = np.abs(fft)**2\n",
    "    \n",
    "\n",
    "    idx_single_max = None\n",
    "    for sub in range(len(idx_max)):\n",
    "        idx = np.argmax(fft_power)\n",
    "        idx_max[sub] = idx\n",
    "        fft_power_max[sub] = fft_power[idx]\n",
    "        fft_power[idx] = 0 # mask current fft power\n",
    "        \n",
    "        if f_delta < np.abs(ft_freqs[idx] - f):\n",
    "            idx_single_max = idx\n",
    "            continue\n",
    "\n",
    "    submax_amplitudes[i] = fft_power_max\n",
    "\n",
    "    # No use to interpolate when the max-amplitude frequency\n",
    "    # is within the frequency resolution of f\n",
    "    if enable_single_max and idx_single_max is not None:\n",
    "        freqs = ft_freqs[idx_single_max]\n",
    "        angles = np.angle(fft[idx_single_max])\n",
    "        \n",
    "        if keep_single_max_phase:\n",
    "            retrieved_phases[i] = angles\n",
    "        \n",
    "        l = axes[0].plot(freqs, angles, '1', label=r'$\\Delta N = {}$'.format(N_delta))\n",
    "        \n",
    "        axes[1].plot(N_delta/required_N_samples, angles, '1', color=l[0].get_color())\n",
    "\n",
    "        if not diffs_on_single_max:\n",
    "            continue\n",
    "    \n",
    "    freqs = ft_freqs[idx_max]\n",
    "    angles = np.angle(fft[idx_max])\n",
    "\n",
    "    # fold angles down for higher submax frequencies\n",
    "    if unfold_phases:\n",
    "        folds = 0\n",
    "        for j in range(len(freqs) - 1):\n",
    "            if freqs[j] < freqs[j+1] and angles[j] < angles[j+1]:\n",
    "                folds += 1\n",
    "                angles[j+1] += - 2*np.pi*folds\n",
    "\n",
    "                if False:\n",
    "                    print(freqs[j], freqs[j+1], \"\\t|\", folds, \"\\t|\", angles[j], angles[j+1])\n",
    "\n",
    "\n",
    "\n",
    "    # plot frequencies and angles\n",
    "    axes[0].plot(freqs, angles, '--', alpha=0.5, label=r'$\\Delta N = {}$'.format(N_delta))\n",
    "    sc = axes[0].scatter(freqs, angles, c=np.arange(len(freqs),0, -1), cmap='Spectral')\n",
    "    \n",
    "    # find interpolation between peaks to get the original phase\n",
    "    dphi_df = (angles[0]-angles[1])/(freqs[0]-freqs[1])\n",
    "    offset = angles[1] - dphi_df * freqs[1]\n",
    "    \n",
    "    angle_at_f = dphi_df * f + offset\n",
    "\n",
    "    # modulo phase\n",
    "    if not unfold_phases:\n",
    "        angle_at_f = phase_modulo(angle_at_f)\n",
    "      \n",
    "    retrieved_phases[i] = angle_at_f\n",
    "    axes[0].plot(f, angle_at_f, 'g^')\n",
    "    \n",
    "    # Try to fix the midpoints of each line\n",
    "    if False:\n",
    "        freq_midpoint = (freqs[0]-freqs[1])/2 + freqs[1]\n",
    "        angle_midpoint = (angles[0]-angles[1])/2 + angles[1]\n",
    "        interp_angle_midpoint = dphi_df*freq_midpoint + offset\n",
    "        \n",
    "        # modulo phase\n",
    "        if not unfold_phases:\n",
    "            angle_midpoint = phase_modulo(angle_midpoint)\n",
    "            interp_angle_midpoint = phase_modulo(interp_angle_midpoint)\n",
    "    \n",
    "        l = axes[0].plot(freq_midpoint, angle_midpoint, '+')\n",
    "        axes[0].plot(freq_midpoint, interp_angle_midpoint, 'x', color=l[0].get_color())\n",
    "    \n",
    "    \n",
    "# plot retrieved phases\n",
    "axes[1].plot(N_deltas/required_N_samples, phase_modulo(retrieved_phases), '.--')\n",
    "\n",
    "cbar = fig.colorbar(sc, ax=axes[0])\n",
    "cbar.ax.set_ylabel(\"Power ordering\")\n",
    "cbar.set_ticks([sc.colorbar.vmin, sc.colorbar.vmax])\n",
    "\n",
    "\n",
    "## horizontal lines\n",
    "hlines = [\n",
    "    (0, None),\n",
    "    (-np.pi/2, r'$\\frac{-\\pi}{2}$'),\n",
    "]\n",
    "\n",
    "ytrans = axes[0].get_yaxis_transform()\n",
    "for location, label in hlines:\n",
    "    axes[0].axhline(location, alpha=0.1, color='k')\n",
    "    axes[0].text(-0.06, location, label, transform=ytrans, verticalalignment='center')\n",
    "    \n",
    "axes[0].plot(f, phase_to_retrieve, 'r*')\n",
    "axes[0].set_xlabel(\"$f$\")\n",
    "axes[0].set_ylabel(r\"$\\varphi_f$\")\n",
    "axes[0].axvline(f, alpha=0.1)\n",
    "\n",
    "\n",
    "\n",
    "axes[1].set_xlabel(r\"$\\Delta N / N_\\mathrm{required}$\")\n",
    "axes[1].set_ylabel(r\"$\\varphi_f$\")\n",
    "axes[1].axhline(phase_to_retrieve, alpha=0.1, color='r')\n",
    "\n",
    "# zooming\n",
    "if True:\n",
    "    x_res = 100\n",
    "    y_min = -2\n",
    "    y_max = 0.5\n",
    "    \n",
    "    if False:\n",
    "        x_res = 0.3\n",
    "        y_min = phase_to_retrieve - 0.1\n",
    "        y_max = phase_to_retrieve + 0.1\n",
    "\n",
    "    axes[0].set_xlim(f-x_res, f+x_res)\n",
    "    axes[0].set_ylim(y_min, y_max)\n",
    "    \n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 116004 characters]\n",
      "text/plain": [
       "<Figure size 1152x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Relative Amplitudes of the maximum and submaximum amplitudes plot\n",
    "fig2, axs2 = plt.subplots(1,1,figsize=(16,4))\n",
    "\n",
    "relative_amplitudes = np.empty_like(submax_amplitudes)\n",
    "for i, amps in enumerate(submax_amplitudes):\n",
    "    relative_amplitudes[i] = amps[1:] / amps[0]\n",
    "    \n",
    "\n",
    "axs2.plot(1+N_deltas/required_N_samples, relative_amplitudes[:,1], 'g')\n",
    "\n",
    "axs2_submax = axs2.twinx()\n",
    "axs2_submax.plot(1+N_deltas/required_N_samples, submax_amplitudes[:,0], alpha=0.3)\n",
    "axs2_submax.plot(1+N_deltas/required_N_samples, submax_amplitudes[:,1], alpha=0.3)\n",
    "axs2_submax.set_yticks([],[])\n",
    "\n",
    "axs2.set_title(\"Relative amplitudes\")\n",
    "axs2.set_xlabel(r\"$N_\\mathrm{samples} / N_\\mathrm{required}$\")\n",
    "axs2.set_ylabel(r\"$A_1/A_2 \\; (\\delta_1)$\")\n",
    "\n",
    "\n",
    "\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
