From 87d32c566e7c8d78fb5145b35f37ce4c9894e080 Mon Sep 17 00:00:00 2001 From: Jesse Mapel <jmapel@usgs.gov> Date: Thu, 30 Apr 2020 10:28:21 -0700 Subject: [PATCH] Added ISD generation notebooks (#282) * Added notebooks * Updated SAR notebook --- .../Generate_Nadir_pointing.ipynb | 148 ++++++++++ jupyter_notebooks/OrbitalLsGeneration.ipynb | 137 +++++++++ jupyter_notebooks/OrbitalSarGeneration.ipynb | 259 ++++++++++++++++++ 3 files changed, 544 insertions(+) create mode 100644 jupyter_notebooks/Generate_Nadir_pointing.ipynb create mode 100644 jupyter_notebooks/OrbitalLsGeneration.ipynb create mode 100644 jupyter_notebooks/OrbitalSarGeneration.ipynb diff --git a/jupyter_notebooks/Generate_Nadir_pointing.ipynb b/jupyter_notebooks/Generate_Nadir_pointing.ipynb new file mode 100644 index 0000000..3885879 --- /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 0000000..8e4f02c --- /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 0000000..77afe82 --- /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 +} -- GitLab