{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Load py_ballisticcalc and set units"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:12.060896Z",
     "start_time": "2025-06-13T12:33:11.962351Z"
    }
   },
   "outputs": [],
   "source": [
    "import logging\n",
    "from matplotlib import pyplot as plt\n",
    "from py_ballisticcalc import (DragModel, TableG1, Ammo, Weapon, Shot, Atmo, Vacuum, Wind,\n",
    "                              Calculator, HitResult, TrajFlag, logger, BaseEngineConfigDict)\n",
    "from py_ballisticcalc.unit import *\n",
    "PreferredUnits.distance = Unit.Meter\n",
    "PreferredUnits.velocity = Unit.MPS\n",
    "PreferredUnits.drop = Unit.Meter\n",
    "logger.setLevel(logging.WARNING)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ex.1: Shot hits minimum altitude before end of range\n",
    "\n",
    "Using a projectile with a very low BC (meaning not very aerodynamic).  We simulate a shot starting at altitude of 1000m above ground level and ask for trajectory going 1000m downrange.  Simulation reports it hitting ground level before it can travel 1000m horizontally."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:13.508450Z",
     "start_time": "2025-06-13T12:33:12.284349Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Max range not reached: (Minimum altitude reached), last distance: 2150.64ft\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 51828 characters]",
      "text/plain": [
       "<Figure size 640x480 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "drag_model = DragModel(bc=0.03, drag_table=TableG1)  # Projectile has G1 BC of 0.03\n",
    "zero = Shot(ammo=Ammo(drag_model, Velocity.MPS(500)),  # Muzzle velocity is 500m/s\n",
    "            atmo=Atmo(altitude=Distance.Meter(1000)),  # Shot is fired at an altitude of 1000m ASL\n",
    "            relative_angle=Angular.Degree(2.0)  # Point barrel 2° above horizon\n",
    ")\n",
    "calc = Calculator(config={\n",
    "        \"cMinimumVelocity\": 0,  # Run simulation as long as velocity is non-zero\n",
    "        \"cMinimumAltitude\": 0,  # Stop simulation at sea level\n",
    "})\n",
    "# Compute trajectory out to horizontal range of 1000m\n",
    "hit_result = calc.fire(zero, Distance.Meter(1000), time_step=0.1, flags=TrajFlag.ALL, raise_range_error=False)\n",
    "if hit_result.error:\n",
    "    print(hit_result.error)\n",
    "hit_result.plot()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inspect data\n",
    "We can see that shot begins pointing 2° above horizon, but after 28 seconds and 655m travel downrange it has almost no forward velocity.  Instead it has reached its terminal velocity of 42m/s and is falling almost vertically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:13.556496Z",
     "start_time": "2025-06-13T12:33:13.526494Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>angle</th>\n",
       "      <th>density_ratio</th>\n",
       "      <th>drag</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>distance</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0.0 m</th>\n",
       "      <td>0.000 s</td>\n",
       "      <td>500 m/s</td>\n",
       "      <td>1.49 mach</td>\n",
       "      <td>-0.0 m</td>\n",
       "      <td>2.0000 °</td>\n",
       "      <td>9.07483e-01</td>\n",
       "      <td>4.577e-03</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>30.9 m</th>\n",
       "      <td>0.077 s</td>\n",
       "      <td>336 m/s</td>\n",
       "      <td>1.00 mach</td>\n",
       "      <td>1.1 m</td>\n",
       "      <td>1.8920 °</td>\n",
       "      <td>9.07483e-01</td>\n",
       "      <td>3.340e-03</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>38.5 m</th>\n",
       "      <td>0.100 s</td>\n",
       "      <td>314 m/s</td>\n",
       "      <td>0.93 mach</td>\n",
       "      <td>1.3 m</td>\n",
       "      <td>1.8516 °</td>\n",
       "      <td>9.07483e-01</td>\n",
       "      <td>2.680e-03</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             time velocity       mach  height     angle density_ratio  \\\n",
       "distance                                                                \n",
       "0.0 m     0.000 s  500 m/s  1.49 mach  -0.0 m  2.0000 °   9.07483e-01   \n",
       "30.9 m    0.077 s  336 m/s  1.00 mach   1.1 m  1.8920 °   9.07483e-01   \n",
       "38.5 m    0.100 s  314 m/s  0.93 mach   1.3 m  1.8516 °   9.07483e-01   \n",
       "\n",
       "               drag  \n",
       "distance             \n",
       "0.0 m     4.577e-03  \n",
       "30.9 m    3.340e-03  \n",
       "38.5 m    2.680e-03  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>angle</th>\n",
       "      <th>density_ratio</th>\n",
       "      <th>drag</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>distance</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>655.5 m</th>\n",
       "      <td>28.400 s</td>\n",
       "      <td>42 m/s</td>\n",
       "      <td>0.12 mach</td>\n",
       "      <td>-995.2 m</td>\n",
       "      <td>-89.7414 °</td>\n",
       "      <td>9.99564e-01</td>\n",
       "      <td>1.705e-03</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>655.5 m</th>\n",
       "      <td>28.500 s</td>\n",
       "      <td>42 m/s</td>\n",
       "      <td>0.12 mach</td>\n",
       "      <td>-999.4 m</td>\n",
       "      <td>-89.7473 °</td>\n",
       "      <td>9.99968e-01</td>\n",
       "      <td>1.705e-03</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>655.5 m</th>\n",
       "      <td>28.515 s</td>\n",
       "      <td>42 m/s</td>\n",
       "      <td>0.12 mach</td>\n",
       "      <td>-1000.1 m</td>\n",
       "      <td>-89.7482 °</td>\n",
       "      <td>1.00003e+00</td>\n",
       "      <td>1.705e-03</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              time velocity       mach     height       angle density_ratio  \\\n",
       "distance                                                                      \n",
       "655.5 m   28.400 s   42 m/s  0.12 mach   -995.2 m  -89.7414 °   9.99564e-01   \n",
       "655.5 m   28.500 s   42 m/s  0.12 mach   -999.4 m  -89.7473 °   9.99968e-01   \n",
       "655.5 m   28.515 s   42 m/s  0.12 mach  -1000.1 m  -89.7482 °   1.00003e+00   \n",
       "\n",
       "               drag  \n",
       "distance             \n",
       "655.5 m   1.705e-03  \n",
       "655.5 m   1.705e-03  \n",
       "655.5 m   1.705e-03  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "df = hit_result.dataframe(True).drop(['slant_height', 'drop_angle', 'windage', 'windage_angle', 'slant_distance', 'energy', 'ogw', 'flag'], axis=1).set_index('distance')\n",
    "#df.to_clipboard()\n",
    "display(df.head(3))\n",
    "display(df.tail(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ex.2: Dropped Projectile\n",
    "Same projectile, dropped from an altitude of 1000m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:13.886787Z",
     "start_time": "2025-06-13T12:33:13.595581Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 23960 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "zero = Shot(ammo=Ammo(drag_model, Velocity.MPS(0)),  # Projectile starts with no velocity\n",
    "            atmo=Atmo(altitude=Distance.Meter(1000)),  # Projectile dropped from an altitude of 1000m ASL\n",
    ")\n",
    "hit_result = calc.fire(zero, Distance.Meter(1000), time_step=0.1, raise_range_error=False)\n",
    "drop_df = hit_result.dataframe()\n",
    "ax = drop_df.plot(x='velocity', y='height', xlabel='Velocity (m/s)', ylabel='Altitude (m)', legend=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inspect data\n",
    "Maximum velocity of 43m/s is achieved after falling 463.9m.  Thereafter the terminal velocity decreases to 42m/s as the projectile encounters increasingly dense atmosphere."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:13.944190Z",
     "start_time": "2025-06-13T12:33:13.905923Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>angle</th>\n",
       "      <th>density_ratio</th>\n",
       "      <th>drag</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>138</th>\n",
       "      <td>13.800 s</td>\n",
       "      <td>43 m/s</td>\n",
       "      <td>0.13 mach</td>\n",
       "      <td>-465.9 m</td>\n",
       "      <td>-90.0000 °</td>\n",
       "      <td>9.49744e-01</td>\n",
       "      <td>1.700e-03</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         time velocity       mach    height       angle density_ratio  \\\n",
       "138  13.800 s   43 m/s  0.13 mach  -465.9 m  -90.0000 °   9.49744e-01   \n",
       "\n",
       "          drag  \n",
       "138  1.700e-03  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>angle</th>\n",
       "      <th>density_ratio</th>\n",
       "      <th>drag</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>263</th>\n",
       "      <td>26.300 s</td>\n",
       "      <td>42 m/s</td>\n",
       "      <td>0.12 mach</td>\n",
       "      <td>-998.6 m</td>\n",
       "      <td>-90.0000 °</td>\n",
       "      <td>9.99883e-01</td>\n",
       "      <td>1.705e-03</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         time velocity       mach    height       angle density_ratio  \\\n",
       "263  26.300 s   42 m/s  0.12 mach  -998.6 m  -90.0000 °   9.99883e-01   \n",
       "\n",
       "          drag  \n",
       "263  1.705e-03  "
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = hit_result.dataframe(True).drop(['distance', 'slant_height', 'drop_angle', 'windage', 'windage_angle', 'slant_distance', 'energy', 'ogw', 'flag'], axis=1)\n",
    "max_velocity_row = drop_df[drop_df.velocity == drop_df.velocity.max()].index[0]\n",
    "display(df[df.index == max_velocity_row])\n",
    "df.tail(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ex.3: Plunging Fire\n",
    "Same blunt projectile is fired at an elevation of 87° – nearly straight upwards.  So it will reach an apex and then fall back to ground."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:14.510920Z",
     "start_time": "2025-06-13T12:33:14.028895Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 48596 characters]",
      "text/plain": [
       "<Figure size 640x480 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "zero = Shot(ammo=Ammo(drag_model, Velocity.MPS(100)), relative_angle=Angular.Degree(87.0))\n",
    "hit_result = calc.fire(zero, Distance.Meter(1000), time_step=0.1, flags=TrajFlag.APEX, raise_range_error=False)\n",
    "hit_result.plot()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Apex data\n",
    "Looking for the maximum observed height, we find the apex reached 5 seconds after launch at a horizontal distance of 14.1m."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>distance</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>angle</th>\n",
       "      <th>drag</th>\n",
       "      <th>flag</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>50</th>\n",
       "      <td>5.000 s</td>\n",
       "      <td>14.0 m</td>\n",
       "      <td>2 m/s</td>\n",
       "      <td>0.01 mach</td>\n",
       "      <td>173.9 m</td>\n",
       "      <td>15.6293 °</td>\n",
       "      <td>1.821e-03</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>51</th>\n",
       "      <td>5.058 s</td>\n",
       "      <td>14.1 m</td>\n",
       "      <td>2 m/s</td>\n",
       "      <td>0.01 mach</td>\n",
       "      <td>173.9 m</td>\n",
       "      <td>-0.0000 °</td>\n",
       "      <td>1.822e-03</td>\n",
       "      <td>APEX</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>52</th>\n",
       "      <td>5.100 s</td>\n",
       "      <td>14.2 m</td>\n",
       "      <td>2 m/s</td>\n",
       "      <td>0.01 mach</td>\n",
       "      <td>173.9 m</td>\n",
       "      <td>-11.6079 °</td>\n",
       "      <td>1.822e-03</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       time distance velocity       mach   height       angle       drag  \\\n",
       "50  5.000 s   14.0 m    2 m/s  0.01 mach  173.9 m   15.6293 °  1.821e-03   \n",
       "51  5.058 s   14.1 m    2 m/s  0.01 mach  173.9 m   -0.0000 °  1.822e-03   \n",
       "52  5.100 s   14.2 m    2 m/s  0.01 mach  173.9 m  -11.6079 °  1.822e-03   \n",
       "\n",
       "     flag  \n",
       "50  RANGE  \n",
       "51   APEX  \n",
       "52  RANGE  "
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "traj_df = hit_result.dataframe()\n",
    "apex_row = traj_df[traj_df.flag == TrajFlag.APEX].index[0]\n",
    "\n",
    "df = hit_result.dataframe(True).drop(['slant_height', 'drop_angle', 'windage', 'windage_angle', 'slant_distance', 'density_ratio', 'energy', 'ogw'], axis=1)\n",
    "df.loc[apex_row-1:apex_row+1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ex.4: Strong-wind Reversing Trajectory\n",
    "Here we loft the blunt projectile at 100m/s at a 70° elevation into a 20m/s headwind.  This windspeed blows the projectile backwards so that it impacts the ground behind the firing point."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:15.032375Z",
     "start_time": "2025-06-13T12:33:14.715429Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Code\\py-ballisticcalc\\py_ballisticcalc\\visualize\\plot.py:232: UserWarning: Trajectory bends backward; suppressing time axis.\n",
      "  warnings.warn(\"Trajectory bends backward; suppressing time axis.\")\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>distance</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>slant_height</th>\n",
       "      <th>angle</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>time</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>10.500 s</th>\n",
       "      <td>-5.9 m</td>\n",
       "      <td>38 m/s</td>\n",
       "      <td>0.11 mach</td>\n",
       "      <td>18.1 m</td>\n",
       "      <td>18.1 m</td>\n",
       "      <td>-109.3714 °</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10.600 s</th>\n",
       "      <td>-7.2 m</td>\n",
       "      <td>39 m/s</td>\n",
       "      <td>0.11 mach</td>\n",
       "      <td>14.4 m</td>\n",
       "      <td>14.4 m</td>\n",
       "      <td>-109.4703 °</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10.700 s</th>\n",
       "      <td>-8.5 m</td>\n",
       "      <td>39 m/s</td>\n",
       "      <td>0.11 mach</td>\n",
       "      <td>10.8 m</td>\n",
       "      <td>10.8 m</td>\n",
       "      <td>-109.5689 °</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10.800 s</th>\n",
       "      <td>-9.8 m</td>\n",
       "      <td>39 m/s</td>\n",
       "      <td>0.11 mach</td>\n",
       "      <td>7.1 m</td>\n",
       "      <td>7.1 m</td>\n",
       "      <td>-109.6672 °</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10.900 s</th>\n",
       "      <td>-11.1 m</td>\n",
       "      <td>39 m/s</td>\n",
       "      <td>0.12 mach</td>\n",
       "      <td>3.5 m</td>\n",
       "      <td>3.5 m</td>\n",
       "      <td>-109.7651 °</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         distance velocity       mach  height slant_height        angle\n",
       "time                                                                   \n",
       "10.500 s   -5.9 m   38 m/s  0.11 mach  18.1 m       18.1 m  -109.3714 °\n",
       "10.600 s   -7.2 m   39 m/s  0.11 mach  14.4 m       14.4 m  -109.4703 °\n",
       "10.700 s   -8.5 m   39 m/s  0.11 mach  10.8 m       10.8 m  -109.5689 °\n",
       "10.800 s   -9.8 m   39 m/s  0.11 mach   7.1 m        7.1 m  -109.6672 °\n",
       "10.900 s  -11.1 m   39 m/s  0.12 mach   3.5 m        3.5 m  -109.7651 °"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 56876 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "zero = Shot(ammo=Ammo(drag_model, Velocity.MPS(100)), relative_angle=Angular.Degree(70.0))\n",
    "zero.winds = [Wind(Velocity.MPS(20), Angular.OClock(6))]\n",
    "\n",
    "hit_result = calc.fire(zero, Distance.Meter(1000), time_step=0.1, raise_range_error=False)\n",
    "hit_result.plot()\n",
    "\n",
    "df = hit_result.dataframe(True).drop(['drop_angle', 'windage', 'windage_angle', 'slant_distance', 'density_ratio', 'drag', 'energy', 'ogw', 'flag'], axis=1).set_index('time')\n",
    "#df.to_clipboard()\n",
    "df.tail()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ex.5: Vacuum Trajectory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-13T12:33:15.342553Z",
     "start_time": "2025-06-13T12:33:15.079935Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "In a vacuum, projectile returns to ground after 10.000s at a distance of 2786.3544642630713 ft\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>distance</th>\n",
       "      <th>velocity</th>\n",
       "      <th>mach</th>\n",
       "      <th>height</th>\n",
       "      <th>angle</th>\n",
       "      <th>flag</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>time</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0.000 s</th>\n",
       "      <td>0.00 ft</td>\n",
       "      <td>321.7 ft/s</td>\n",
       "      <td>0.29 mach</td>\n",
       "      <td>-0.00 ft</td>\n",
       "      <td>30.0000 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1.000 s</th>\n",
       "      <td>278.64 ft</td>\n",
       "      <td>306.9 ft/s</td>\n",
       "      <td>0.28 mach</td>\n",
       "      <td>144.78 ft</td>\n",
       "      <td>24.7913 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2.000 s</th>\n",
       "      <td>557.27 ft</td>\n",
       "      <td>294.9 ft/s</td>\n",
       "      <td>0.26 mach</td>\n",
       "      <td>257.39 ft</td>\n",
       "      <td>19.1066 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3.000 s</th>\n",
       "      <td>835.91 ft</td>\n",
       "      <td>286.0 ft/s</td>\n",
       "      <td>0.26 mach</td>\n",
       "      <td>337.83 ft</td>\n",
       "      <td>13.0039 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4.000 s</th>\n",
       "      <td>1114.54 ft</td>\n",
       "      <td>280.5 ft/s</td>\n",
       "      <td>0.25 mach</td>\n",
       "      <td>386.09 ft</td>\n",
       "      <td>6.5868 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5.000 s</th>\n",
       "      <td>1393.18 ft</td>\n",
       "      <td>278.6 ft/s</td>\n",
       "      <td>0.25 mach</td>\n",
       "      <td>402.18 ft</td>\n",
       "      <td>0.0000 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5.000 s</th>\n",
       "      <td>1393.18 ft</td>\n",
       "      <td>278.6 ft/s</td>\n",
       "      <td>0.25 mach</td>\n",
       "      <td>402.18 ft</td>\n",
       "      <td>0.0000 °</td>\n",
       "      <td>APEX</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6.000 s</th>\n",
       "      <td>1671.81 ft</td>\n",
       "      <td>280.5 ft/s</td>\n",
       "      <td>0.25 mach</td>\n",
       "      <td>386.09 ft</td>\n",
       "      <td>-6.5868 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7.000 s</th>\n",
       "      <td>1950.45 ft</td>\n",
       "      <td>286.0 ft/s</td>\n",
       "      <td>0.26 mach</td>\n",
       "      <td>337.83 ft</td>\n",
       "      <td>-13.0039 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8.000 s</th>\n",
       "      <td>2229.08 ft</td>\n",
       "      <td>294.9 ft/s</td>\n",
       "      <td>0.26 mach</td>\n",
       "      <td>257.39 ft</td>\n",
       "      <td>-19.1066 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9.000 s</th>\n",
       "      <td>2507.72 ft</td>\n",
       "      <td>306.9 ft/s</td>\n",
       "      <td>0.28 mach</td>\n",
       "      <td>144.78 ft</td>\n",
       "      <td>-24.7913 °</td>\n",
       "      <td>RANGE</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10.000 s</th>\n",
       "      <td>2786.35 ft</td>\n",
       "      <td>321.7 ft/s</td>\n",
       "      <td>0.29 mach</td>\n",
       "      <td>0.00 ft</td>\n",
       "      <td>-30.0000 °</td>\n",
       "      <td>ZERO_DOWN|RANGE</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            distance    velocity       mach     height       angle  \\\n",
       "time                                                                 \n",
       "0.000 s      0.00 ft  321.7 ft/s  0.29 mach   -0.00 ft   30.0000 °   \n",
       "1.000 s    278.64 ft  306.9 ft/s  0.28 mach  144.78 ft   24.7913 °   \n",
       "2.000 s    557.27 ft  294.9 ft/s  0.26 mach  257.39 ft   19.1066 °   \n",
       "3.000 s    835.91 ft  286.0 ft/s  0.26 mach  337.83 ft   13.0039 °   \n",
       "4.000 s   1114.54 ft  280.5 ft/s  0.25 mach  386.09 ft    6.5868 °   \n",
       "5.000 s   1393.18 ft  278.6 ft/s  0.25 mach  402.18 ft    0.0000 °   \n",
       "5.000 s   1393.18 ft  278.6 ft/s  0.25 mach  402.18 ft    0.0000 °   \n",
       "6.000 s   1671.81 ft  280.5 ft/s  0.25 mach  386.09 ft   -6.5868 °   \n",
       "7.000 s   1950.45 ft  286.0 ft/s  0.26 mach  337.83 ft  -13.0039 °   \n",
       "8.000 s   2229.08 ft  294.9 ft/s  0.26 mach  257.39 ft  -19.1066 °   \n",
       "9.000 s   2507.72 ft  306.9 ft/s  0.28 mach  144.78 ft  -24.7913 °   \n",
       "10.000 s  2786.35 ft  321.7 ft/s  0.29 mach    0.00 ft  -30.0000 °   \n",
       "\n",
       "                     flag  \n",
       "time                       \n",
       "0.000 s             RANGE  \n",
       "1.000 s             RANGE  \n",
       "2.000 s             RANGE  \n",
       "3.000 s             RANGE  \n",
       "4.000 s             RANGE  \n",
       "5.000 s             RANGE  \n",
       "5.000 s              APEX  \n",
       "6.000 s             RANGE  \n",
       "7.000 s             RANGE  \n",
       "8.000 s             RANGE  \n",
       "9.000 s             RANGE  \n",
       "10.000 s  ZERO_DOWN|RANGE  "
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# In vacuum we have analytic solutions to trajectory:\n",
    "# Time to return to ground level t = 2 * v_0 * sin(theta) / gravity\n",
    "# Horizontal distance x = t * v_0 * cos(theta)\n",
    "import math\n",
    "PreferredUnits.distance = Unit.Foot\n",
    "PreferredUnits.velocity = Unit.FPS\n",
    "PreferredUnits.drop = Unit.Foot\n",
    "\n",
    "launch_angle = Angular.Degree(30)\n",
    "v_0 = -10 * calc.gravity_vector.y\n",
    "weapon = Weapon(zero_elevation=launch_angle)\n",
    "shot_info = Shot(weapon=weapon, ammo=Ammo(DragModel(1, TableG1), Velocity.FPS(v_0)), atmo=Vacuum())\n",
    "t = -2 * v_0 * math.sin(launch_angle >> Angular.Radian) / calc.gravity_vector.y\n",
    "x = v_0 * t * math.cos(launch_angle >> Angular.Radian)\n",
    "calc = Calculator(config=BaseEngineConfigDict(cMinimumVelocity=0))\n",
    "data = calc.fire(shot_info, Distance.Foot(x), Distance.Foot(x/10.0), flags=TrajFlag.ALL)\n",
    "print(f\"In a vacuum, projectile returns to ground after {t:.3f}s at a distance of {x} ft\")\n",
    "data.dataframe(True).drop(['slant_height', 'slant_distance', 'energy', 'ogw', 'windage', 'windage_angle', 'drop_angle', 'density_ratio', 'drag'], axis=1).set_index('time')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ex.6 Max Range and Lofted Zero"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Max range is 7136.26m with elevation=38.8236°\n"
     ]
    }
   ],
   "source": [
    "from py_ballisticcalc import SciPyEngineConfigDict, loadMetricUnits, UnitPropsDict, UnitProps\n",
    "UnitPropsDict[Unit.Meter] = UnitProps(\"meter\", 2, \"m\")\n",
    "loadMetricUnits()\n",
    "drag_model = DragModel(bc=0.759, drag_table=TableG1)\n",
    "ammo = Ammo(drag_model, Velocity.MPS(930))\n",
    "shot = Shot(ammo=ammo)\n",
    "config = SciPyEngineConfigDict(cMinimumVelocity=0)\n",
    "calc = Calculator(config=config, engine=\"scipy_engine\")\n",
    "max_range, max_angle = calc.find_max_range(shot)\n",
    "print(f\"Max range is {max_range<<Distance.Meter} with elevation={max_angle<<Angular.Degree}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Low angle for 5000.0m is elevation=11.7053°\n",
      "High angle for 5000.0m is elevation=66.4193°\n"
     ]
    }
   ],
   "source": [
    "distance = Distance.Meter(5000)\n",
    "low_angle = calc.find_zero_angle(shot, distance)\n",
    "high_angle = calc.find_zero_angle(shot, distance, lofted=True)\n",
    "print(f\"Low angle for {distance << Distance.Meter} is elevation={low_angle << Angular.Degree}\")\n",
    "print(f\"High angle for {distance << Distance.Meter} is elevation={high_angle << Angular.Degree}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot flat trajectory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 50648 characters]",
      "text/plain": [
       "<Figure size 640x480 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "PreferredUnits.drop = Unit.Meter\n",
    "shot.relative_angle = low_angle\n",
    "low = calc.fire(shot, trajectory_range=Distance.Meter(5200), trajectory_step=Distance.Meter(200), flags=TrajFlag.ALL)\n",
    "low.plot()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot lofted trajectory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 53340 characters]",
      "text/plain": [
       "<Figure size 640x480 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "shot.relative_angle = high_angle\n",
    "high = calc.fire(shot, trajectory_range=Distance.Meter(5200), trajectory_step=Distance.Meter(200),\n",
    "                 time_step=0.1, flags=TrajFlag.ALL, raise_range_error=False)\n",
    "high.plot()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot together"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 119272 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "df_high = high.dataframe()[['distance', 'height', 'velocity', 'time']]\n",
    "df_low = low.dataframe()[['distance', 'height', 'velocity', 'time']]\n",
    "fig, ax1 = plt.subplots(figsize=(10, 6))\n",
    "N = 5  # label every N seconds\n",
    "for df, color, label in [\n",
    "    (df_high, (0., 0.5, 0.3, 1.0), 'High Trajectory'),\n",
    "    (df_low, (0.3, 0.5, 0., 1.0), 'Low Trajectory')\n",
    "]:\n",
    "    ax1.plot(df['distance'], df['height'], label=label, color=color, linewidth=2)\n",
    "    last_label_time = -N\n",
    "    for i in range(len(df)):\n",
    "        t = df['time'].iloc[i]\n",
    "        # Only label if time is >= 1 and is a new N-second mark\n",
    "        if t >= 1 and (int(t) % N == 0) and (int(t) != last_label_time):\n",
    "            ax1.annotate(f\"{t:.0f}s\", (df['distance'].iloc[i], df['height'].iloc[i]),\n",
    "                textcoords=\"offset points\", xytext=(0,5), ha='center', fontsize=10, color='black')\n",
    "            last_label_time = int(t)\n",
    "ax1.set_xlabel('Distance (m)')\n",
    "ax1.set_ylabel('Height (m)')\n",
    "ax1.axhline(0, color='black', linewidth=1, linestyle='-')  # Horizontal line\n",
    "ax2 = ax1.twinx()\n",
    "ax2.plot(df_high['distance'], df_high['velocity'], label='High Trajectory Velocity', color=(0.0, 0.2, 0.8, 1.0), linestyle=':')\n",
    "ax2.plot(df_low['distance'], df_low['velocity'], label='Low Trajectory Velocity', color=(0.5, 0., 0.8, 1.0), linestyle=':')\n",
    "ax2.set_ylabel('Velocity (m/s)')\n",
    "lines_1, labels_1 = ax1.get_legend_handles_labels()\n",
    "lines_2, labels_2 = ax2.get_legend_handles_labels()\n",
    "ax2.legend(lines_1 + lines_2, labels_1 + labels_2, loc='upper right')\n",
    "plt.title('Height and Velocity vs Distance for High and Low Trajectories\\n(Time labels along height curves)')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-ballisticcalc",
   "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.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
