Find favorable amsat pass between two locations.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
168 KiB

  1. {
  2. "cells": [
  3. {
  4. "cell_type": "code",
  5. "execution_count": 1,
  6. "metadata": {},
  7. "outputs": [],
  8. "source": [
  9. "%matplotlib inline\n",
  10. "\n",
  11. "from collections import namedtuple\n",
  12. "from math import degrees\n",
  13. "\n",
  14. "import cartopy.crs as ccrs\n",
  15. "import cartopy.feature as cfeature\n",
  16. "import ephem\n",
  17. "import matplotlib.pyplot as plt\n",
  18. "\n",
  19. "\n",
  20. "philly = ephem.Observer()\n",
  21. "philly.lon, philly.lat = '-75.16961', '39.92435'\n",
  22. "\n",
  23. "portland = ephem.Observer()\n",
  24. "portland.lon, portland.lat = '-122.68092', '45.50901'\n",
  25. "\n",
  26. "fo_29 = ephem.readtle(\n",
  27. " \"JAS-2 (FO-29)\",\n",
  28. " \"1 24278U 96046B 19132.42991715 .00000011 00000-0 48833-4 0 9994\",\n",
  29. " \"2 24278 98.5319 290.9039 0349934 336.8182 21.7496 13.53090691122769\",\n",
  30. ")\n",
  31. "\n",
  32. "lakes = cfeature.NaturalEarthFeature('physical', 'lakes', '50m', facecolor=\"none\")\n",
  33. "countries = cfeature.NaturalEarthFeature('cultural', 'admin_0_countries', '50m', facecolor=\"none\")\n",
  34. "states = cfeature.NaturalEarthFeature('cultural', 'admin_1_states_provinces_lines', '50m', facecolor=\"none\")"
  35. ]
  36. },
  37. {
  38. "cell_type": "code",
  39. "execution_count": 2,
  40. "metadata": {},
  41. "outputs": [],
  42. "source": [
  43. "philly.date = '2019/5/12'\n",
  44. "portland.date = '2019/5/12'\n",
  45. "\n",
  46. "def next_n_passses(location, sat, n):\n",
  47. " passes = []\n",
  48. " for i in range(n):\n",
  49. " info = location.next_pass(sat)\n",
  50. " passes.append((info[0], info[4], info[3]))\n",
  51. " location.date = info[4] + 0.01\n",
  52. " return passes\n",
  53. "\n",
  54. "philly_passes = next_n_passses(philly, fo_29, 20)\n",
  55. "portland_passes = next_n_passses(portland, fo_29, 20)\n",
  56. "\n",
  57. "best_overlap = {\"score\": 10000}\n",
  58. "for o_rise, o_set, o_max in philly_passes:\n",
  59. " for t_rise, t_set, t_max in portland_passes:\n",
  60. " if t_rise < o_set and t_set > o_rise:\n",
  61. " score = abs(o_max - t_max)\n",
  62. " if best_overlap[\"score\"] > score:\n",
  63. " best_overlap = {\n",
  64. " \"score\": score,\n",
  65. " \"rise\": o_rise\n",
  66. " }"
  67. ]
  68. },
  69. {
  70. "cell_type": "code",
  71. "execution_count": 3,
  72. "metadata": {},
  73. "outputs": [],
  74. "source": [
  75. "Point = namedtuple('Point', 'lats lons')\n",
  76. "ground_track = Point([], [])\n",
  77. "for i in range(300):\n",
  78. " t = best_overlap['rise'] - 0.01 + i/10000.0\n",
  79. " fo_29.compute(t)\n",
  80. " ground_track.lats.append(degrees(fo_29.sublat))\n",
  81. " ground_track.lons.append(degrees(fo_29.sublong))\n",
  82. "\n",
  83. "begin = best_overlap['rise'] - 0.01\n",
  84. "philly.date = begin\n",
  85. "philly_pass = philly.next_pass(fo_29)\n",
  86. "fo_29.compute(philly_pass[0])\n",
  87. "philly_aos = Point(degrees(fo_29.sublat), degrees(fo_29.sublong))\n",
  88. "\n",
  89. "fo_29.compute(philly_pass[2])\n",
  90. "philly_max = Point(degrees(fo_29.sublat), degrees(fo_29.sublong))\n",
  91. "\n",
  92. "fo_29.compute(philly_pass[4])\n",
  93. "philly_los = Point(degrees(fo_29.sublat), degrees(fo_29.sublong))\n",
  94. "\n",
  95. "\n",
  96. "portland.date = begin\n",
  97. "portland_pass = portland.next_pass(fo_29)\n",
  98. "fo_29.compute(portland_pass[0])\n",
  99. "portland_aos = Point(degrees(fo_29.sublat), degrees(fo_29.sublong))\n",
  100. "\n",
  101. "fo_29.compute(portland_pass[2])\n",
  102. "portland_max = Point(degrees(fo_29.sublat), degrees(fo_29.sublong))\n",
  103. "\n",
  104. "fo_29.compute(portland_pass[4])\n",
  105. "portland_los = Point(degrees(fo_29.sublat), degrees(fo_29.sublong))"
  106. ]
  107. },
  108. {
  109. "cell_type": "code",
  110. "execution_count": 4,
  111. "metadata": {},
  112. "outputs": [
  113. {
  114. "data": {
  115. "image/png": "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
  116. "text/plain": [
  117. "<Figure size 792x612 with 1 Axes>"
  118. ]
  119. },
  120. "metadata": {
  121. "needs_background": "light"
  122. },
  123. "output_type": "display_data"
  124. }
  125. ],
  126. "source": [
  127. "proj = ccrs.AzimuthalEquidistant(\n",
  128. " central_longitude=-95,\n",
  129. " central_latitude=35,\n",
  130. ")\n",
  131. "geodetic = ccrs.Geodetic()\n",
  132. "\n",
  133. "fig = plt.figure(figsize=(11, 8.5))\n",
  134. "ax = fig.add_axes([0.1, 0.1, 0.9, 0.9], projection=proj)\n",
  135. "ax.set_extent([-140, -50, 5, 85], ccrs.Geodetic())\n",
  136. "ax.coastlines(resolution='50m', color=\"grey\", linewidth=0.5, alpha=0.5)\n",
  137. "ax.add_feature(lakes, edgecolor=\"grey\", linewidth=0.5, alpha=0.5)\n",
  138. "ax.add_feature(states, edgecolor=\"grey\", linewidth=0.5, alpha=0.3, dashes='--')\n",
  139. "ax.add_feature(countries, edgecolor=\"grey\", linewidth=0.5, alpha=0.4, dashes='--')\n",
  140. "\n",
  141. "\n",
  142. "ax.plot(ground_track.lons, ground_track.lats, 'k', lw=1, alpha=1, transform=geodetic)\n",
  143. "ax.plot([degrees(philly.lon), philly_aos.lons], [degrees(philly.lat), philly_aos.lats], 'k--', lw=0.5, transform=geodetic)\n",
  144. "ax.plot([degrees(philly.lon), philly_max.lons], [degrees(philly.lat), philly_max.lats], transform=geodetic)\n",
  145. "ax.plot([degrees(philly.lon), philly_los.lons], [degrees(philly.lat), philly_los.lats], 'k--', lw=0.5, transform=geodetic)\n",
  146. "\n",
  147. "ax.plot([degrees(portland.lon), portland_aos.lons], [degrees(portland.lat), portland_aos.lats], 'k--', lw=0.5, transform=geodetic)\n",
  148. "ax.plot([degrees(portland.lon), portland_max.lons], [degrees(portland.lat), portland_max.lats], transform=geodetic)\n",
  149. "ax.plot([degrees(portland.lon), portland_los.lons], [degrees(portland.lat), portland_los.lats], 'k--', lw=0.5, transform=geodetic)\n",
  150. "\n",
  151. "\n",
  152. "plt.show()"
  153. ]
  154. },
  155. {
  156. "cell_type": "markdown",
  157. "metadata": {},
  158. "source": []
  159. }
  160. ],
  161. "metadata": {
  162. "kernelspec": {
  163. "display_name": "Python 3",
  164. "language": "python",
  165. "name": "python3"
  166. },
  167. "language_info": {
  168. "codemirror_mode": {
  169. "name": "ipython",
  170. "version": 3
  171. },
  172. "file_extension": ".py",
  173. "mimetype": "text/x-python",
  174. "name": "python",
  175. "nbconvert_exporter": "python",
  176. "pygments_lexer": "ipython3",
  177. "version": "3.6.8"
  178. }
  179. },
  180. "nbformat": 4,
  181. "nbformat_minor": 2
  182. }