diff --git a/jupyter_notebooks/Generate_Nadir_pointing.ipynb b/jupyter_notebooks/Generate_Nadir_pointing.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..388587967ac47ffad15bcda3426d1c7e45fe6685
--- /dev/null
+++ b/jupyter_notebooks/Generate_Nadir_pointing.ipynb
@@ -0,0 +1,148 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "ModuleNotFoundError",
+     "evalue": "No module named 'pyproj'",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
+      "\u001b[0;32m<ipython-input-1-7e71c9f92a96>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_printoptions\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msuppress\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mpyproj\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'pyproj'"
+     ]
+    }
+   ],
+   "source": [
+    "import numpy as np\n",
+    "np.set_printoptions(suppress=True)\n",
+    "import pyproj"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def reproject(record, semi_major, semi_minor, source_proj, dest_proj, **kwargs):\n",
+    "    \"\"\"\n",
+    "    Thin wrapper around PyProj's Transform() function to transform 1 or more three-dimensional\n",
+    "    point from one coordinate system to another. If converting between Cartesian\n",
+    "    body-centered body-fixed (BCBF) coordinates and Longitude/Latitude/Altitude coordinates,\n",
+    "    the values input for semi-major and semi-minor axes determine whether latitudes are\n",
+    "    planetographic or planetocentric and determine the shape of the datum for altitudes.\n",
+    "    If semi_major == semi_minor, then latitudes are interpreted/created as planetocentric\n",
+    "    and altitudes are interpreted/created as referenced to a spherical datum.\n",
+    "    If semi_major != semi_minor, then latitudes are interpreted/created as planetographic\n",
+    "    and altitudes are interpreted/created as referenced to an ellipsoidal datum.\n",
+    "    Parameters\n",
+    "    ----------\n",
+    "    record : object\n",
+    "          Pandas series object\n",
+    "    semi_major : float\n",
+    "              Radius from the center of the body to the equater\n",
+    "    semi_minor : float\n",
+    "              Radius from the pole to the center of mass\n",
+    "    source_proj : str\n",
+    "                      Pyproj string that defines a projection space ie. 'geocent'\n",
+    "    dest_proj : str\n",
+    "                   Pyproj string that defines a project space ie. 'latlon'\n",
+    "    Returns\n",
+    "    -------\n",
+    "    : list\n",
+    "    Transformed coordinates as y, x, z\n",
+    "    \"\"\"\n",
+    "    source_pyproj = pyproj.Proj(proj = source_proj, a = semi_major, b = semi_minor)\n",
+    "    dest_pyproj = pyproj.Proj(proj = dest_proj, a = semi_major, b = semi_minor)\n",
+    "\n",
+    "    y, x, z = pyproj.transform(source_pyproj, dest_pyproj, record[0], record[1], record[2], **kwargs)\n",
+    "\n",
+    "    return y, x, z"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Input Data"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "semi_major = 1100\n",
+    "semi_minor = 1000\n",
+    "altitude = 50\n",
+    "lon = 35\n",
+    "lat = 30\n",
+    "radius = 0"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Compute the quaternion (as x, y, z, w)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[ 0.49673176 -0.70940648  0.         -0.5       ]\n"
+     ]
+    }
+   ],
+   "source": [
+    "ground_pt = np.asarray(reproject([lon, lat, radius], semi_major, semi_minor, 'latlong', 'geocent'))\n",
+    "sensor_position = np.asarray(reproject([lon, lat, radius + altitude], semi_major, semi_minor, 'latlong', 'geocent'))\n",
+    "look_vec = ground_pt - sensor_position\n",
+    "look_vec = look_vec / np.linalg.norm(look_vec)\n",
+    "quat = np.zeros(4)\n",
+    "quat[:3] = np.cross(np.array([0, 0, 1]), look_vec)\n",
+    "quat[3] = np.dot(np.array([0, 0, 1]), look_vec)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "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.1"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/jupyter_notebooks/OrbitalLsGeneration.ipynb b/jupyter_notebooks/OrbitalLsGeneration.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..8e4f02c92f3142625fa40899c050d6dc0a73952b
--- /dev/null
+++ b/jupyter_notebooks/OrbitalLsGeneration.ipynb
@@ -0,0 +1,137 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "np.set_printoptions(suppress=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "radius = 10 # km\n",
+    "altitude = 9990 # km\n",
+    "detector_size = 0.1 # mm\n",
+    "focal_length = 50 # mm\n",
+    "lines = 16\n",
+    "exposure_duration = 0.1 # seconds"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[1050.    0.   -0.]\n",
+      "[1049.99999524    0.           -0.1       ]\n",
+      "[1049.99998095    0.           -0.2       ]\n",
+      "[1049.99995714    0.           -0.3       ]\n",
+      "[1049.99992381    0.           -0.39999999]\n",
+      "[1049.99988095    0.           -0.49999998]\n",
+      "[1049.99982857    0.           -0.59999997]\n",
+      "[1049.99976667    0.           -0.69999995]\n",
+      "[1049.99969524    0.           -0.79999992]\n",
+      "[1049.99961429    0.           -0.89999989]\n",
+      "[1049.99952381    0.           -0.99999985]\n",
+      "[1049.99942381    0.           -1.0999998 ]\n",
+      "[1049.99931429    0.           -1.19999974]\n",
+      "[1049.99919524    0.           -1.29999967]\n",
+      "[1049.99906667    0.           -1.39999959]\n",
+      "[1049.99892857    0.           -1.49999949]\n",
+      "[   -0.     0. -1050.]\n",
+      "[   -0.1            0.         -1049.99999524]\n",
+      "[   -0.2            0.         -1049.99998095]\n",
+      "[   -0.3            0.         -1049.99995714]\n",
+      "[   -0.39999999     0.         -1049.99992381]\n",
+      "[   -0.49999998     0.         -1049.99988095]\n",
+      "[   -0.59999997     0.         -1049.99982857]\n",
+      "[   -0.69999995     0.         -1049.99976667]\n",
+      "[   -0.79999992     0.         -1049.99969524]\n",
+      "[   -0.89999989     0.         -1049.99961429]\n",
+      "[   -0.99999985     0.         -1049.99952381]\n",
+      "[   -1.0999998      0.         -1049.99942381]\n",
+      "[   -1.19999974     0.         -1049.99931429]\n",
+      "[   -1.29999967     0.         -1049.99919524]\n",
+      "[   -1.39999959     0.         -1049.99906667]\n",
+      "[   -1.49999949     0.         -1049.99892857]\n",
+      "[ 0.         -0.70710678  0.          0.70710678]\n",
+      "[ 0.         -0.70707311  0.          0.70714045]\n",
+      "[ 0.         -0.70703943  0.          0.70717412]\n",
+      "[ 0.         -0.70700576  0.          0.70720779]\n",
+      "[ 0.         -0.70697208  0.          0.70724146]\n",
+      "[ 0.         -0.7069384   0.          0.70727512]\n",
+      "[ 0.         -0.70690472  0.          0.70730878]\n",
+      "[ 0.         -0.70687104  0.          0.70734244]\n",
+      "[ 0.         -0.70683736  0.          0.7073761 ]\n",
+      "[ 0.         -0.70680367  0.          0.70740976]\n",
+      "[ 0.         -0.70676998  0.          0.70744342]\n",
+      "[ 0.         -0.70673629  0.          0.70747707]\n",
+      "[ 0.         -0.7067026   0.          0.70751073]\n",
+      "[ 0.         -0.70666891  0.          0.70754438]\n",
+      "[ 0.         -0.70663522  0.          0.70757803]\n",
+      "[ 0.         -0.70660152  0.          0.70761168]\n"
+     ]
+    }
+   ],
+   "source": [
+    "positions = []\n",
+    "velocities = []\n",
+    "quats = []\n",
+    "for i in np.arange(0,16):\n",
+    "    angle = i*detector_size/(radius+altitude)\n",
+    "    position = (radius+altitude) * np.array([np.cos(angle), 0, -np.sin(angle)])\n",
+    "    positions.append(position)\n",
+    "    velocity = (radius+altitude) * np.array([-np.sin(angle), 0, -np.cos(angle)])\n",
+    "    velocities.append(velocity)\n",
+    "    camera_angle = -np.pi/2 + angle\n",
+    "    quat = np.array([0, np.sin(camera_angle/2), 0, np.cos(camera_angle/2)])\n",
+    "    quats.append(quat)\n",
+    "for pos in positions:\n",
+    "    print(pos)\n",
+    "for vel in velocities:\n",
+    "    print(vel)\n",
+    "for quat in quats:\n",
+    "    print(quat)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "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.1"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/jupyter_notebooks/OrbitalSarGeneration.ipynb b/jupyter_notebooks/OrbitalSarGeneration.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..77afe82a7ed1d80ac022455ddc4e78378b0a39e7
--- /dev/null
+++ b/jupyter_notebooks/OrbitalSarGeneration.ipynb
@@ -0,0 +1,259 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "from itertools import product"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "radius   = 1737400\n",
+    "alt      = 2000000\n",
+    "ground   = 7.5\n",
+    "exposure = 0.005\n",
+    "samples  = 1000\n",
+    "lines    = 1000"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "sensor_rad = radius + alt\n",
+    "angle_per_line = ground / radius\n",
+    "angle_per_samp = angle_per_line\n",
+    "angle_per_Second = angle_per_line / exposure"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "line_vec = np.arange(0, lines+0.00000001)\n",
+    "sample_vec = np.arange(0, samples+0.00000001)\n",
+    "# From here on, matrix indexing is [line, sample, (xyz)]\n",
+    "sample_mat, line_mat = np.meshgrid(line_vec, sample_vec)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Positions\n",
+      "[3737400.0, 0.0, -0.0]\n",
+      "[3737399.912943243, 0.0, -806.6795148600813]\n",
+      "[3737399.651772976, 0.0, -1613.3589921395437]\n",
+      "[3737399.216489211, 0.0, -2420.03839425777]\n",
+      "[3737398.607091969, 0.0, -3226.7176836341473]\n",
+      "[3737397.823581278, 0.0, -4033.396822688066]\n",
+      "[3737396.8659571735, 0.0, -4840.075773838925]\n",
+      "[3737395.734219702, 0.0, -5646.754499506133]\n",
+      "[3737394.4283689144, 0.0, -6453.432962109106]\n",
+      "[3737392.9484048723, 0.0, -7260.111124067275]\n",
+      "[3737391.2943276456, 0.0, -8066.788947800085]\n",
+      "[3737389.4661373096, 0.0, -8873.466395726995]\n",
+      "[3737387.4638339505, 0.0, -9680.14343026748]\n",
+      "[3737385.287417662, 0.0, -10486.820013841041]\n",
+      "[3737382.936888544, 0.0, -11293.496108867193]\n",
+      "[3737380.4122467074, 0.0, -12100.17167776548]\n",
+      "[3737377.713492269, 0.0, -12906.846682955462]\n",
+      "[3737374.840625355, 0.0, -13713.521086856732]\n",
+      "[3737371.7936460995, 0.0, -14520.194851888911]\n",
+      "[3737368.5725546437, 0.0, -15326.867940471646]\n",
+      "[3737365.177351138, 0.0, -16133.540315024618]\n",
+      "Velocities\n",
+      "[-0.0, 0.0, -3737400.0]\n",
+      "[-806.6795148600813, 0.0, -3737399.912943243]\n",
+      "[-1613.3589921395437, 0.0, -3737399.651772976]\n",
+      "[-2420.03839425777, 0.0, -3737399.216489211]\n",
+      "[-3226.7176836341473, 0.0, -3737398.607091969]\n",
+      "[-4033.396822688066, 0.0, -3737397.823581278]\n",
+      "[-4840.075773838925, 0.0, -3737396.8659571735]\n",
+      "[-5646.754499506133, 0.0, -3737395.734219702]\n",
+      "[-6453.432962109106, 0.0, -3737394.4283689144]\n",
+      "[-7260.111124067275, 0.0, -3737392.9484048723]\n",
+      "[-8066.788947800085, 0.0, -3737391.2943276456]\n",
+      "[-8873.466395726995, 0.0, -3737389.4661373096]\n",
+      "[-9680.14343026748, 0.0, -3737387.4638339505]\n",
+      "[-10486.820013841041, 0.0, -3737385.287417662]\n",
+      "[-11293.496108867193, 0.0, -3737382.936888544]\n",
+      "[-12100.17167776548, 0.0, -3737380.4122467074]\n",
+      "[-12906.846682955462, 0.0, -3737377.713492269]\n",
+      "[-13713.521086856732, 0.0, -3737374.840625355]\n",
+      "[-14520.194851888911, 0.0, -3737371.7936460995]\n",
+      "[-15326.867940471646, 0.0, -3737368.5725546437]\n",
+      "[-16133.540315024618, 0.0, -3737365.177351138]\n"
+     ]
+    }
+   ],
+   "source": [
+    "positions = sensor_rad * np.vstack([np.cos(-angle_per_line * line_vec), np.zeros(line_vec.shape), np.sin(-angle_per_line * line_vec)]).T\n",
+    "# Note: The chain rule results in an extra negative on the velocity calculations\n",
+    "velocities = sensor_rad * np.vstack([np.sin(-angle_per_line * line_vec), np.zeros(line_vec.shape), -np.cos(-angle_per_line * line_vec)]).T\n",
+    "print('Positions')\n",
+    "for pos in positions[::int(0.25/exposure)]:\n",
+    "    print(list(pos))\n",
+    "print('Velocities')\n",
+    "for vel in velocities[::int(0.25/exposure)]:\n",
+    "    print(list(vel))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Ground point at line: 500, sample: 500\n",
+      "[1737391.90602155   -3749.98835331   -3749.99708833]\n"
+     ]
+    }
+   ],
+   "source": [
+    "lat = -angle_per_line * line_mat\n",
+    "# Image is a right look, so the longitude goes negative\n",
+    "lon = -angle_per_samp * sample_mat\n",
+    "ground_points = radius * np.stack([np.multiply(np.cos(lat), np.cos(lon)), np.multiply(np.cos(lat), np.sin(lon)), np.sin(lat)], axis=-1)\n",
+    "print(\"Ground point at line: 500, sample: 500\")\n",
+    "print(ground_points[500, 500])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "slant_range = np.array([[np.linalg.norm(point) for point in row] for row in ground_points - positions[:, None, :]])\n",
+    "ground_range = radius * np.abs(lon)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Ground range to slant range polynomial coefficients\n",
+      "[2000000.000003915, -1.0488420462327845e-08, 5.377893056639776e-07, -1.3072058387193456e-15]\n"
+     ]
+    }
+   ],
+   "source": [
+    "range_poly = np.polynomial.polynomial.polyfit(ground_range.flatten(), slant_range.flatten(), 3)\n",
+    "print(\"Ground range to slant range polynomial coefficients\")\n",
+    "print(list(range_poly))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Locus point: [1737392.0956685692, -3849.7959147875467, -3749.9887626446034]\n",
+      "Locus direction: [0.0019248861951120758, -0.9999981473962212, -4.154676206387554e-06]\n"
+     ]
+    }
+   ],
+   "source": [
+    "sc_pos = positions[500]\n",
+    "sc_vel = velocities[500]\n",
+    "off_ground_pt = ground_points[500, 500] - np.array([100, 100, 100])\n",
+    "look_vec = off_ground_pt - positions[500]\n",
+    "zero_doppler_look_vec = look_vec - np.dot(look_vec, sc_vel) / np.dot(sc_vel, sc_vel) * sc_vel\n",
+    "locus_point = sc_pos + slant_range[500, 500] / np.linalg.norm(zero_doppler_look_vec) * zero_doppler_look_vec\n",
+    "# Image is a right look, so do look X velocity\n",
+    "locus_direction = np.cross(zero_doppler_look_vec, sc_vel)\n",
+    "locus_direction = 1.0 / np.linalg.norm(locus_direction) * locus_direction\n",
+    "print(\"Locus point:\", list(locus_point))\n",
+    "print(\"Locus direction:\", list(locus_direction))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Remote locus point: [1737388.3904556318, 0.0, -3749.980765309453]\n",
+      "Remote locus direction: [-0.0, -1.0, 0.0]\n"
+     ]
+    }
+   ],
+   "source": [
+    "remote_look_vec = -slant_range[500, 500] / sensor_rad * sc_pos\n",
+    "remote_zero_doppler_look_vec = remote_look_vec - np.dot(remote_look_vec, sc_vel) / np.dot(sc_vel, sc_vel) * sc_vel\n",
+    "remote_locus_point = sc_pos + remote_zero_doppler_look_vec\n",
+    "remote_locus_direction = np.cross(remote_zero_doppler_look_vec, sc_vel)\n",
+    "remote_locus_direction = 1.0 / np.linalg.norm(remote_locus_direction) * remote_locus_direction\n",
+    "print(\"Remote locus point:\", list(remote_locus_point))\n",
+    "print(\"Remote locus direction:\", list(remote_locus_direction))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "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.1"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}