{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Emitter/Receiver Simulation with Signals" ] }, { "cell_type": "code", "execution_count": null, "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", "\n", "rng = np.random.default_rng()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Signal" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# copied from 02_discrete_signal_translation.ipynb #ae7aba6\n", "class TravelSignal:\n", " \"\"\"\n", " Model an arbitrary digitised signal that can be translated to another position and time.\n", " \"\"\"\n", "\n", " def __init__(self, signal, sample_rate, t_0 = 0, x_0 = 0, periodic=True, interp1d_kw = None):\n", " \"\"\"\n", " Initialise by saving the raw signal\n", " \n", " Parameters\n", " ----------\n", " signal : arraylike\n", " The raw signal to wrap.\n", " sample_rate : float\n", " Sample rate of the raw signal.\n", " t_0 : float, optional\n", " Time that this signal is sent out.\n", " x_0 : float, optional\n", " Location that this signal is sent out from.\n", " periodic : bool, optional\n", " Translated signal is 0 if it is not periodic\n", " and the time/distance is outside the samples.\n", " interp1d_kw : bool or dict, optional\n", " Use scipy.interpolate's interp1d_kw for interpolation.\n", " Set to True, or a dictionary to enable.\n", " Dictionary will be entered in as **kwargs.\n", " \"\"\"\n", "\n", " self.raw = signal\n", " self.periodic = periodic\n", "\n", " self.sample_rate = sample_rate # Hz\n", " self.sample_length = len(self.raw)\n", " self.time_length = self.sample_length*sample_rate # s\n", " \n", " self.x_0 = x_0\n", " self.t_0 = t_0\n", "\n", " # choose interpolation method\n", " if not interp1d_kw:\n", " self.interp_f = None\n", "\n", " else:\n", " # offload interpolation to scipy.interpolate\n", " import scipy.interpolate as interp\n", "\n", " interp1d_kw_defaults = {\n", " \"copy\": False,\n", " \"kind\": 'linear',\n", " \"assume_sorted\": True,\n", " \"bounds_error\": True\n", " }\n", "\n", " if self.periodic:\n", " interp1d_kw_defaults['bounds_error'] = False\n", " interp1d_kw_defaults['fill_value'] = (self.raw[-1], self.raw[0])\n", " \n", " # merge kwargs\n", " if interp1d_kw is not True:\n", " interp1d_kw = { **interp1d_kw_defaults, **interp1d_kw }\n", "\n", " self.interp_f = interp.interp1d(\n", " np.arange(0, self.sample_length),\n", " self.raw,\n", " **interp1d_kw\n", " )\n", " \n", " def __len__(self):\n", " return self.sample_length\n", " \n", " def __call__(self, t_f = None, x_f = None, **kwargs):\n", " \"\"\"\n", " Allow this class to be used as a function.\n", " \"\"\"\n", " return self._translate(t_f, x_f, **kwargs)[0]\n", " \n", " def _translate(self, t_f = None, x_f = None, t_0 = None, x_0 = None, velocity = None):\n", " \"\"\"\n", " Translate the signal from (t_0, x_0) to (t_f, x_f) with optional velocity.\n", " \n", " Returns the signal at (t_f, x_f)\n", " \"\"\"\n", " \n", " if t_0 is None:\n", " t_0 = self.t_0\n", " \n", " if velocity is None:\n", " velocity = 1\n", "\n", "\n", " ## spatial offset\n", " if x_f is None:\n", " spatial_time_offset = 0\n", " else:\n", " x_f = np.asarray(x_f)\n", " if x_0 is None:\n", " x_0 = self.x_0\n", "\n", " spatial_time_offset = np.sum(np.sqrt( (x_f - x_0)**2 )/velocity)\n", "\n", " ## temporal offset\n", " if t_f is None:\n", " temporal_time_offset = 0\n", " else:\n", " t_f = np.asarray(t_f)\n", " \n", " if t_0 is None:\n", " t_0 = self.t_0\n", " \n", " temporal_time_offset = t_f - t_0\n", "\n", " # total offset\n", " total_time_offset = spatial_time_offset + temporal_time_offset\n", " n_offset = (total_time_offset * sample_rate )\n", "\n", " # periodic signal\n", " if self.periodic:\n", " n_offset = n_offset % self.sample_length\n", "\n", " # non-periodic and outside the bounds\n", " else:\n", " mask_idx = np.nonzero( (0 > n_offset) | (n_offset >= self.sample_length) )\n", " n_offset[mask_idx] = 0\n", "\n", " # offload to scipy interpolation\n", " if self.interp_f:\n", " amplitude = self.interp_f(n_offset)\n", " \n", " # self written linear interpolation\n", " else:\n", " n_offset_eps, n_offset_int = np.modf(n_offset)\n", " n_offset_int = n_offset.astype(int)\n", "\n", " if True:\n", " amplitude = (1-n_offset_eps) * self.raw[n_offset_int] \\\n", " + n_offset_eps * self.raw[(n_offset_int + 1) % self.sample_length]\n", "\n", " # use nearest value instead of interpolation\n", " else:\n", " amplitude = self.raw[n_offset_int]\n", "\n", " if not self.periodic:\n", " amplitude[mask_idx] = 0\n", " \n", " return amplitude, total_time_offset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## New code" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "### Location\n", "class Location:\n", " \"\"\"\n", " A location is a point designated by a spatial coordinate x.\n", " \"\"\"\n", "\n", " def __init__(self, x):\n", " self.x = np.asarray(x) \n", "\n", " def __repr__(self):\n", " return \"Location({})\".format(repr(self.x))\n", "\n", " def __getitem__(self, key):\n", " return self.x[key]\n", "\n", " def __setitem__(self, key, val):\n", " self.x[key] = val\n", "\n", " def __add__(self, other):\n", " if isinstance(other, self.__class__):\n", " other = other.x\n", "\n", " return self.__class__(self.x + other)\n", "\n", " def __sub__(self, other):\n", " if isinstance(other, self.__class__):\n", " other = other.x\n", "\n", " return self.__class__(self.x - other)\n", " \n", " def __eq__(self, other):\n", " if isinstance(other, self.__class__):\n", " other = other.x\n", "\n", " return np.all(self.x == other)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "### Receiver\n", "class Receiver(Location):\n", " \"\"\"\n", " Receive a signal at position x and time t\n", " \"\"\"\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " ### Emitter\n", "class Emitter(TravelSignal):\n", " \"\"\"\n", " Emit a signal from position x_0 (and time t_0)\n", " \"\"\"\n", " pass" ] } ], "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 }