From bea14609bb9b76c798596764651cb1ef00f91806 Mon Sep 17 00:00:00 2001
From: Katie Zaback Open-source modular toolkit for simulating high-fidelity pulsar X-ray events. OpenXNAV is designed to aid development and testing of Pulsar-based Autonomous Navigation (XNAV) Positioning, Navigation, and Timing (PNT) solutions. You can read our IEEE paper to learn more: IEEE.org. This tool is a flexible, cost-effective testbed to enable the next generation of XNAV solutions. OpenXNAV includes three modular components that allow engineers simulate high-fidelity pulsar X-ray events along a flight trajectory over a user-defined mission timeline. Hardware-in-the-loop functionality can be configured using software-defined radios (SDRs), and users can query for pulsar candidates using the query tool (powered by the ATNF Pulsar Catalogue). For more information, you can read our IEEE paper: IEEE.org. Open-source modular toolkit for simulating high-fidelity pulsar X-ray events. OpenXNAV is designed to aid development and testing of Pulsar-based Autonomous Navigation (XNAV) Positioning, Navigation, and Timing (PNT) solutions. You can read our IEEE paper to learn more: IEEE.org. Each component of OpenXNAV can be used individually, or strung together as an end-to-end pipeline. To get started with OpenXNAV, select the module you are interested in leveraging and access the documentation. Query for all known pulsars catalogued in the ATNF database by providing a coordinate in space, and a search radius. Model the desired flight path, taking into account spacecraft orientation, viewing angle and line of sight. Generate representative photon arrival events at the location of interest along the user-defined trajectory. Before using the OpenXNAV Pulsar Querying Tool, you will need to install the necessary python packages. You can do this using your preferred python package manager, but instructions are provided below for using Inside the The default environment name in Open your Anaconda Prompt (or activate Anaconda in a terminal window). Navigate to Introduction to OpenXNAV
Components
diff --git a/search/search_index.json b/search/search_index.json
index 2c51424..21d5c11 100644
--- a/search/search_index.json
+++ b/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":"
"},{"location":"components/","title":"Components","text":"Anaconda
open-xnav/1__pulsar_querying/
directory, there is an environment.yml
file. This file can be passed to your preferred package manager to create a new environment that can run the tool. environment.yml
is openXNAV-gui
. If you wish to change the environment name, simply edit environment.yml
before creating the environment. open-xnav/`1__pulsar_querying/
and run the following command: conda env create -f environment.yaml\n
To activate your environment, run the following command:
conda activate openXNAV-gui\n
NOTE: If you altered the environment name, you will need to alter the above command to reflect that new name. "},{"location":"components/1__pulsar_querying/pq_overview/#install-kivymd","title":"Install KivyMD","text":"The tool also depends on KivyMD, which is a library built off of the popular Kivy library. This package is not available through conda
, so you must install it using pip
.
First, make sure you have activated the openXNAV-gui
environment in your terminal. Then, run the following command:
pip install kivymd\n
If you are getting a ReadTimeoutError
response during installation, you might be using a VPN or have a slower internet connection. If this happens, try running the following instead:
pip install --default-timeout=1000 kivymd\n
If that still does not work, you can troubleshoot your issues further using the pip
documentation here.
Once you have successfully installed all the required packages, you are ready to run the tool!
First, ensure you have navigated to open-xnav/1__pulsar_querying/
in your terminal and have your openXNAV-gui
environment activated.
Then, simply run the following in your terminal:
python run.py\n
Upon startup, the tool will need to pull the current ATNF Pulsar Catalogue from the web. This process only need to be done the first time you open the application.
However, if you wish to update the database, simply delete the .../pulsar_database/
directory from your file system. Then, relaunch the application. Once the database has been generated, you can continue begin querying.
You should see the OpenXNAV launch window with the blue Launch Application
button active. In the future, when you open the application, the tool will locate the previously-generated database and you can quickly continue to the querying functionality.
After you click Launch Application
, the query tool will display.
There are three fields that you need to provide to execute a query:
These values will create a query, searching for all known pulsars that are within a given search radius of the provided coordinates in space.
The OpenXNAV tool leverages the open-source psrqpy
library. More information about this package, and how queries are executed, can be found here.
The query results will display to the GUI interface, and will also be stored in a sub-directory within .../query_results/
.
For information about how these sub-directories are tagged, see below.
"},{"location":"components/1__pulsar_querying/pq_overview/#tagging-results","title":"Tagging Results","text":"There is an optional Tag
field that OpenXNAV also provides. This allows you to specify the tag for this query you are about to execute. If a tag is provided, that will be used to name the sub-directory within .../query_results/
where all pulsars returned by the query are stored.
Sample Queries
Below are couple sample queries that you can run to get familiar with the OpenXNAV tool. There are additional sample query results contained in the .../query_results/
directory.
Right now, the OpenXNAV Pulsar Querying Tool only outputs pulsar data into the .st
format. This is the format that is required by STK to perform subsequent mission planning.
To display the format of this file, here is J00012_5431.st
as an example:
stk.v.12.0\nWrittenBy OpenXNAV\n\nBEGIN Star\n\n Name J0012_5431\n\n BEGIN PathDescription\n\n Epoch 58912.0\n RefFrame J2000\n RightAscension 3.097083333333333\n Declination +54:31:47\n ProperMotionRAPerYr 0\n ProperMotionDecPerYr 0\n Parallax 0\n RadialVelocity 0.0000000000000000e+00\n\n END PathDescription\n\n BEGIN PhysicalData\n\n Magnitude None\n\n END PhysicalData\n\n BEGIN IdentityData\n\n Id 0\n\n END IdentityData\n\n\n BEGIN Extensions\n\n BEGIN ExternData\n END ExternData\n\n BEGIN ADFFileData\n END ADFFileData\n\n BEGIN AccessConstraints\n LineOfSight IncludeIntervals\n\n UsePreferredMaxStep No\n PreferredMaxStep 360\n END AccessConstraints\n\n BEGIN Desc\n END Desc\n\n BEGIN Crdn\n END Crdn\n\n BEGIN Graphics\n\n BEGIN Attributes\n\n MarkerColor #00ff00\n LabelColor #00ff00\n MarkerStyle 2\n FontStyle 0\n\n END Attributes\n\n BEGIN Graphics\n\n Show On\n Inherit On\n ShowLabel On\n ShowMarker On\n\n END Graphics\n END Graphics\n\n BEGIN VO\n END VO\n\n END Extensions\n\nEND Star\n
You will need to provide these .st
file(s) to STK to successfully execute OpenXNAV's mission planning functionality.
However, if you would like pulsar information to be stored/saved in a different format, you can modify the source code.
Navigate to queryPulsar.py
and locate the following function:
def saveToFile(self, root_directory) ...\n
Here, you can alter/update the code to print the pulsar information into your desired format. Once this function has been updated, query results will automatically store in this new format. All query results will still appear in .../query_results/
."},{"location":"components/2__mission_planning/mp_overview/","title":"Mission Planning","text":"In this Python notebook, the mission_planning
module from the OpenXNAV library is demonstrated.
We start by importing the necessary libraries:
from astropy import units as u\nfrom astropy.coordinates import SkyCoord,get_body\nfrom astropy.time import Time,TimeDelta\nfrom astropy.table import Table,QTable\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\n
Next we define the necessary variables that we will use to instantiate the orbit. We will define an elliptical orbit directly by its orbital elements in this example. Other options, which are currently commented out, are a circular orbit (special case of elliptical orbit where eccentricity = 0) and an elliptical orbit defined by its burnout trajectory.
# User definitions for elliptical orbital elements\n\ne = 0.3 # eccentricity of the orbit\na = 750000*u.m # semi-major axis \ninc = 20*u.deg # inclination of orbit\nw = 90*u.deg # argument of periapsis\nv_0 = 0*u.deg # initial eccentric anomaly\nOmega = 90*u.deg # longitude of ascending node\n\nimport warnings\nwarnings.filterwarnings('ignore',message='leap-second file is expired')\nt = Time.now() + TimeDelta(120*u.day) + TimeDelta(np.linspace(0,800,51)*u.s)\n\n# Alternative: User definitions for elliptical orbital insertion \n# (note that this is just an example and would not generate the \n# same trajectory as the one above defined directly by orbital elements)\n\n#e = 0.1 # eccentricity of the orbit, also user defined\n#a = 750000*u.m # m semi-major axis \n#B = 70*u.deg # azimuth heading measured in degrees clockwise from north\n#dec = 20*u.deg # geocentric latitude in degrees (declination) of burnout\n#v_0 = 0*u.deg # initial eccentric anomaly\n
We can now do the same thing with a low-fidelity elliptical orbit, starting with the EllipticalOrbit
class:
from mission_planning import EllipticalOrbit\n\neo = EllipticalOrbit(t,a,e,v_0=v_0,\n inc=inc,w=w,\n Omega=Omega)\n\nplt.figure()\nplt.plot(eo.x.to(u.km),eo.y.to(u.km),'b.')\n#plt.xlim([-a/u.km,a/u.km])\n#plt.ylim([-a/u.km,a/u.km])\nplt.show()\n
While the above plot is represented in two dimensions as the projection of the orbit onto the XY-plane, this orbit was actually instantiated in 3D. To show the trajectory in the 3D Cartesian reference frame with the center of Earth at the origin, we just have to plot it in the 3D figure:
fig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(eo.x.to(u.km),eo.y.to(u.km),eo.z.to(u.km))\n\nplt.show()\n
"},{"location":"components/2__mission_planning/mp_overview/#creating-vectors-from-pulsar-to-space-objects-to-look-for-access","title":"Creating vectors from pulsar to space objects to look for access","text":"In order to better demonstrate the pulsar access calculation functions in the OpenXNAV mission_planning
module, we will create a SkyCoord object representing the moon, but with a distorted size and orbit to ensure some obfuscation of a chosen pulsar (whose coordinates are also exaggerated for demonstration purposes).
from mission_planning import plot_accesses\n\nmoon = SkyCoord(\n x = -284405*u.m,\n y = 249415*u.m,\n z = 0*u.m,\n frame='gcrs',\n representation_type = 'cartesian'\n)\n\n#moon_rad = 1740*u.km # actual radius of the moon \nmoon_rad = 150*u.km # fake news \n\n\n# assuming we have the location of the pulsars, using one as an example\npulsar = SkyCoord(\n x = -853215*u.m,\n y = 748245*u.m,\n z = 0*u.km,\n frame='gcrs',\n representation_type = 'cartesian'\n)\n\naccess = eo.pulsar_access(pulsar,moon,moon_rad)\n\n# create figure to plot pulsar access\nfig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(pulsar.x.to(u.km),pulsar.y.to(u.km),pulsar.z.to(u.km),label='pulsar')\nax.scatter(eo.x.to(u.km),eo.y.to(u.km),eo.z.to(u.km),label='spacecraft')\nax.scatter(moon.x.to(u.km),moon.y.to(u.km),moon.z.to(u.km),label='moon',s=500)\nplt.legend()\n\nplt.show()\n\nfig = plt.figure()\nax = fig.add_subplot()\nax = plot_accesses(ax,t,{'pulsar':access})\n
We can also instantiate an EllipticalOrbit
object with high fidelity. This version of the class calculates the eccentric anomaly at each time in the time array in order to calculate a more exact position.
There is currently a bug in this version that causes it to only output positive x
and positive z
values, so we'll need to fix that. (Negative values shown below are there because x
and z
are positive before coordinate rotation to account for longitude of ascending node, orbital inclination, and argument of periapsis)
eo_h = EllipticalOrbit(t,a,e,v_0=v_0,\n inc=inc,w=w,\n Omega=Omega,\n hifi=True)\n\nplt.figure()\nplt.plot(eo_h.x,eo_h.y,'b.')\nplt.xlim([-a/u.m,a/u.m])\nplt.ylim([-a/u.m,a/u.m])\nplt.show()\n\nfig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(eo_h.x,eo_h.y,eo_h.z)\n\nplt.show()\n
"},{"location":"components/2__mission_planning/mp_overview/#using-the-actual-position-of-known-celestial-bodies","title":"Using the actual position of known celestial bodies","text":""},{"location":"components/2__mission_planning/mp_overview/#example-1-circular-orbit","title":"Example 1 - Circular Orbit","text":"Next we will take advantage of the get_body
and pulsar_access_export
functionalities of Astropy and OpenXNAV Mission Planning, respectively.
Astropy allows us to create a SkyCoord object using the actual location of the moon at any time or array of times. We will make our moon much bigger than in real life to ensure that we obfuscate some pulsars for demonstration purposes. We will define a new orbit circular orbit using the CircularOrbit
class, which is a special case of the EllipticalOrbit
class in which eccentricity and orbital inclination are zero.
from mission_planning import CircularOrbit\n\n# User definitions for circular orbit\nr = 360000*u.km\nt0 = Time('2023-08-24 12:12:15.932083')\nt = t0 + TimeDelta(np.linspace(0,25,100)*u.day)\n\nmoon = get_body('moon',t)\nmoon.representation_type='cartesian'\nMOON_RAD = 1740*u.km\n\nearth = get_body('earth',t)\nearth.representation_type = 'cartesian'\nEARTH_RAD = 6378.14*u.km\n\nco = CircularOrbit(t,r)\n
Next we will create an astropy.table.Table
object with some pulsars that we want to load in to our pulsar access export function. The table is shown and created here for demonstration purposes; however, tables with this format can be created in the OpenXNAV pulsar query module.
name = [ 'J0002+6216' , 'J0006+1834' , 'J0007+7303' , 'J0011+08' , 'J0012+5431']\nraJ_deg = [ 0.74238 , 1.52 , 1.7571 , 2.9 , 3.0971 ] * u.deg\ndecJ_deg = [ 62.26928 , 18.5831 , 1.7571 , 8.17 , 54.5297 ] * u.deg\ndist_kpc = [ 6.357 , 0.86 , 1.4 , 5.399 , 5.425 ] * u.kpc\n\nt = Table([name,raJ_deg,decJ_deg,dist_kpc],\n names = ['NAME','RAJD','DECJD','DIST'])\n\nt.write('some_pulsars.fits',overwrite=True)\nprint(t)\n
NAME RAJD DECJD DIST\n deg deg kpc \n---------- ------- -------- -----\nJ0002+6216 0.74238 62.26928 6.357\nJ0006+1834 1.52 18.5831 0.86\nJ0007+7303 1.7571 1.7571 1.4\n J0011+08 2.9 8.17 5.399\nJ0012+5431 3.0971 54.5297 5.425\n
"},{"location":"components/2__mission_planning/mp_overview/#using-the-pulsar_access_export-method","title":"Using the pulsar_access_export
method","text":"Now that we have our spacecraft, moon, and pulsar table, we can use the pulsar_access_export
method to create a table and plot of pulsar accesses. Both this method and the pulsar_access
function allow you to account for obfuscation from multiple celestial bodies; just make sure to define them as shown below, entered before the keyword arguments and alternating between the SkyCoord
object representing the celestial body and the radius of the body represented as an astropy.units.Quantity
object.
pulsars_qtbl = QTable.read('some_pulsars.fits')\n\n# 3D plot of satellite orbit and moon\nfig1 = plt.figure()\nax1 = fig1.add_subplot(projection='3d')\nax1.scatter(moon.x.to(u.km),moon.y.to(u.km),moon.z.to(u.km),label='moon',s=100)\nax1.scatter(co.x.to(u.km),co.y.to(u.km),co.z.to(u.km),label='satellite in cirular orbit')\nax1.scatter(earth.x.to(u.km),earth.y.to(u.km),earth.z.to(u.km),label='earth',s=500)\nplt.legend(loc='upper left')\n\n# Plot of pulsar accesses\n_,(fig2,ax2) = co.pulsar_access_export(pulsars_qtbl,\n moon,MOON_RAD,\n earth,EARTH_RAD,\n make_csv=False,\n make_fig=True,save_fig=False)\n
"},{"location":"components/2__mission_planning/mp_overview/#example-2-elliptical-orbit","title":"Example 2 - Elliptical Orbit","text":"Next we will define a new elliptical orbit from the EllipticalOrbit
class in which eccentricity and orbital inclination are non-zero.
# User definitions for elliptical orbital elements\n\ne = 0.3 # eccentricity of the orbit\na = 30000*u.km # semi-major axis \ninc = 20*u.deg # inclination of orbit\nw = 90*u.deg # argument of periapsis\nv_0 = 0*u.deg # initial eccentric anomaly\nOmega = 90*u.deg # longitude of ascending node\n\nt0 = Time('2023-08-24 12:12:15.932')\nt = t0 + TimeDelta(np.linspace(0,25,1000)*u.day)\n\nmoon = get_body('moon',t)\nmoon.representation_type='cartesian'\nMOON_RAD = 1740*u.km\n\nearth = SkyCoord(x=0*u.m,y=0*u.m,z=0*u.m,frame='gcrs',representation_type='cartesian')\nEARTH_RAD = 6378.14*u.km\n\neo2 = EllipticalOrbit(t,a,e,v_0=v_0,\n inc=inc,w=w,\n Omega=Omega)\n\nfig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(eo2.x.to(u.km),eo2.y.to(u.km),eo2.z.to(u.km))\n\nplt.show()\n
Next we will create a CSV with some pulsars that we want to load in to our pulsar access export function. The CSV is shown and created here for demonstration purposes:
name = [ 'J0002+6216' , 'J0006+1834' , 'J0007+7303' , 'J0011+08' , 'J0012+5431']\nraJ_deg = [ 0.74238 , 1.52 , 1.7571 , 2.9 , 3.0971 ] * u.deg\ndecJ_deg = [ 62.26928 , 18.5831 , 1.7571 , 8.17 , 54.5297 ] * u.deg\ndist_kpc = [ 6.357 , 0.86 , 1.4 , 5.399 , 5.425 ] * u.kpc\n\ntbl = Table([name,raJ_deg,decJ_deg,dist_kpc],\n names = ['NAME','RAJD','DECJD','DIST'])\n\ntbl.write('some_pulsars.fits',overwrite=True)\nprint(tbl)\n
NAME RAJD DECJD DIST\n deg deg kpc \n---------- ------- -------- -----\nJ0002+6216 0.74238 62.26928 6.357\nJ0006+1834 1.52 18.5831 0.86\nJ0007+7303 1.7571 1.7571 1.4\n J0011+08 2.9 8.17 5.399\nJ0012+5431 3.0971 54.5297 5.425\n
"},{"location":"components/2__mission_planning/mp_overview/#using-the-pulsar_access_export-method_1","title":"Using the pulsar_access_export
method","text":"Now that we have our spacecraft, moon, and pulsar CSV, we can use the pulsar_access_export
method to create a table and plot of pulsar accesses. Both this method and the pulsar_access
function allow you to account for obfuscation from multiple celestial bodies; just make sure to define them as shown below, entered before the keyword arguments and alternating between the SkyCoord object representing the celestial body and the radius of the body represented as an astropy.units.Quantity object
.
As part of the pulsar_access_export
method call, we can write all pulsar access data to a CSV file. This CSV can then be used in the pulsar photon time-of-arrival simulation module to model the X-ray pulse sequence that the spacecraft would observe at those coordinates.
pulsars_qtbl = QTable.read('some_pulsars.fits')\n\n# 3D plot of satellite orbit\nfig1 = plt.figure()\nax1 = fig1.add_subplot(projection='3d')\nax1.scatter(eo2.x.to(u.km),eo2.y.to(u.km),eo2.z.to(u.km),label='satellite in elliptical orbit')\nax1.scatter(earth.x.to(u.km),earth.y.to(u.km),earth.z.to(u.km),label='earth',s=200)\nplt.legend(loc='upper left')\n\n# 3D plot of satellite orbit and moon\nfig2 = plt.figure()\nax2 = fig2.add_subplot(projection='3d')\nax2.scatter(moon.x.to(u.km),moon.y.to(u.km),moon.z.to(u.km),label='moon',s=100)\nax2.scatter(eo2.x.to(u.km),eo2.y.to(u.km),eo2.z.to(u.km),label='satellite in elliptical orbit')\nax2.scatter(earth.x.to(u.km),earth.y.to(u.km),earth.z.to(u.km),label='earth',s=200)\nplt.legend(loc='upper left')\n\n# Plot of pulsar accesses, and save pulsar access table to CSV\naccesses,(fig3,ax3) = eo2.pulsar_access_export(pulsars_qtbl,\n moon,MOON_RAD,\n earth,EARTH_RAD,\n make_csv=True,save_csv=True,\n make_fig=True,save_fig=False)\n
The breaks in the plot above might be hard to see since they are so small compared to the scale of the timeline displayed. We have therefore represented the breaks in tabular form below.
# Create DataFrame containing all times at which access to at least one pulsar is interrupted\naccess_breaks = accesses[accesses.all(axis=1) == False]\naccess_breaks\n
Time_JDate Spacecraft_pos_X_km Spacecraft_pos_Y_km Spacecraft_pos_Z_km Spacecraft_vel_X_kmps Spacecraft_vel_Y_kmps Spacecraft_vel_Z_kmps J0002+6216 J0006+1834 J0007+7303 J0011+08 J0012+5431 0 2.460181e+06 -19733.545037 2.343791e-12 4.132736e-13 4.667259 0.000000 0.000000 True False False False True 24 2.460182e+06 -19722.356542 -8.518943e+02 -1.662908e+01 4.663209 0.190622 0.065196 True False False False True 48 2.460182e+06 -19688.801001 -1.703092e+03 -3.324457e+01 4.651077 0.380744 0.130222 True False False False True 72 2.460183e+06 -19632.908273 -2.552897e+03 -4.983288e+01 4.630912 0.569870 0.194907 True True False False True 96 2.460183e+06 -19554.728216 -3.400614e+03 -6.638041e+01 4.602798 0.757509 0.259083 True True False False True 120 2.460184e+06 -19454.330828 -4.245547e+03 -8.287361e+01 4.566851 0.943180 0.322587 True True False False True 143 2.460185e+06 -19264.410077 5.493859e+03 1.072408e+02 4.499325 -1.214310 -0.415318 True True False True True 144 2.460185e+06 -19331.806438 -5.087002e+03 -9.929892e+01 4.523216 1.126412 0.385256 True True False False True 167 2.460185e+06 -19397.632920 4.654341e+03 9.085332e+01 4.546627 -1.032427 -0.353111 True True False False True 168 2.460185e+06 -19187.265950 -5.924286e+03 -1.156428e+02 4.472073 1.306749 0.446935 True True False False True 191 2.460186e+06 -19508.779991 3.811009e+03 7.439137e+01 4.586325 -0.847881 -0.289993 True True False False True 192 2.460186e+06 -19020.841125 -6.756708e+03 -1.318918e+02 4.413626 1.483752 0.507473 True True False False True 215 2.460186e+06 -19597.751010 2.964556e+03 5.786850e+01 4.618256 -0.661138 -0.226122 True True False False True 239 2.460187e+06 -19664.466140 2.115677e+03 4.129829e+01 4.642291 -0.472672 -0.161663 True True False False True 263 2.460188e+06 -19708.865772 1.265068e+03 2.469429e+01 4.658329 -0.282972 -0.096782 True True False False True 287 2.460188e+06 -19730.910356 4.134251e+02 8.070109e+00 4.666305 -0.092530 -0.031647 True False False False True 311 2.460189e+06 -19730.580291 -4.385562e+02 -8.560672e+00 4.666185 0.098154 0.033571 True False False False True 333 2.460189e+06 -13839.201092 1.850473e+04 3.612147e+02 2.807521 -3.552673 -1.215086 True True False True True 334 2.460189e+06 -18399.018428 9.194010e+03 1.794682e+02 4.199348 -1.985878 -0.679210 True True False True True 335 2.460189e+06 -19707.875871 -1.290179e+03 -2.518445e+01 4.657971 0.288581 0.098700 True False False False True 359 2.460190e+06 -19662.817284 -2.140747e+03 -4.178765e+01 4.641696 0.478252 0.163572 True True False False True 383 2.460191e+06 -19595.444668 -2.989564e+03 -5.835666e+01 4.617427 0.666673 0.228016 True True False False True 407 2.460191e+06 -19505.818229 -3.835935e+03 -7.487792e+01 4.585264 0.853359 0.291866 True True False False True 431 2.460192e+06 -19394.018402 -4.679164e+03 -9.133787e+01 4.545339 1.037832 0.354960 True True False False True 454 2.460192e+06 -19335.736565 5.062236e+03 9.881549e+01 4.524612 -1.121046 -0.383420 True True False True True 455 2.460192e+06 -19260.146075 -5.518559e+03 -1.077230e+02 4.497816 1.219630 0.417138 True True False False True 478 2.460193e+06 -19457.609710 4.220669e+03 8.238798e+01 4.568022 -0.937736 -0.320724 True True False False True 479 2.460193e+06 -19104.322848 -6.353426e+03 -1.240197e+02 4.442885 1.398305 0.478248 True True False False True 502 2.460194e+06 -19557.352895 3.375643e+03 6.589298e+01 4.603740 -0.752000 -0.257199 True True False False True 526 2.460194e+06 -19634.876395 2.527855e+03 4.934405e+01 4.631621 -0.564310 -0.193005 True True False False True 550 2.460195e+06 -19690.110807 1.677999e+03 3.275475e+01 4.651550 -0.375147 -0.128308 True True False False True 574 2.460195e+06 -19723.006865 8.267705e+02 1.613866e+01 4.663444 -0.185003 -0.063275 True True False False True 598 2.460196e+06 -19733.535299 -2.513369e+01 -4.906128e-01 4.667255 0.005626 0.001924 True False False False True 622 2.460197e+06 -19721.686753 -8.770174e+02 -1.711949e+01 4.662967 0.196240 0.067118 True False False False True 646 2.460197e+06 -19687.471756 -1.728184e+03 -3.373437e+01 4.650597 0.386340 0.132136 True False False False True 670 2.460198e+06 -19630.920755 -2.577938e+03 -5.032167e+01 4.630196 0.575429 0.196808 True True False False True 694 2.460198e+06 -19552.084202 -3.425582e+03 -6.686780e+01 4.601849 0.763017 0.260967 True True False False True 718 2.460199e+06 -19451.032689 -4.270423e+03 -8.335919e+01 4.565673 0.948623 0.324448 True True False False True 741 2.460200e+06 -19268.654973 5.469155e+03 1.067586e+02 4.500827 -1.208987 -0.413498 True True False True True 742 2.460200e+06 -19327.857151 -5.111765e+03 -9.978229e+01 4.521814 1.131776 0.387090 True True False False True 765 2.460200e+06 -19401.228226 4.629514e+03 9.036870e+01 4.547908 -1.027019 -0.351261 True True False False True 766 2.460200e+06 -19182.669102 -5.948915e+03 -1.161236e+02 4.470452 1.312021 0.448737 True True False False True 789 2.460201e+06 -19511.722453 3.786080e+03 7.390476e+01 4.587378 -0.842402 -0.288118 True True False False True 790 2.460201e+06 -19015.600922 -6.781183e+03 -1.323695e+02 4.411793 1.488919 0.509240 True True False False True 813 2.460201e+06 -19600.037983 2.939546e+03 5.738031e+01 4.619079 -0.655600 -0.224229 True True False False True 837 2.460202e+06 -19666.095577 2.090606e+03 4.080890e+01 4.642879 -0.467091 -0.159755 True True False False True 861 2.460203e+06 -19709.836218 1.239957e+03 2.420411e+01 4.658680 -0.277362 -0.094863 True True False False True 885 2.460203e+06 -19731.220949 3.882936e+02 7.579540e+00 4.666417 -0.086906 -0.029724 True False False False True 909 2.460204e+06 -19730.230755 -4.636870e+02 -9.051228e+00 4.666059 0.103778 0.035494 True False False False True 933 2.460204e+06 -19706.866516 -1.315289e+03 -2.567460e+01 4.657606 0.294189 0.100619 True False False False True 957 2.460205e+06 -19661.149008 -2.165815e+03 -4.227698e+01 4.641094 0.483830 0.165480 True True False False True 981 2.460206e+06 -19593.118960 -3.014570e+03 -5.884478e+01 4.616590 0.672208 0.229909 True True False False True"},{"location":"components/2__mission_planning/mp_overview/#substituting-stk-for-openxnav-mission-planning","title":"Substituting STK for OpenXNAV Mission Planning","text":"As an alternative to the mission_planning
module built into OpenXNAV, users can also plan their missions and generate trajectory and pulsar access inputs using the mission planning software of their choice, such as ANSYS STK. STK is very visual, and a great option if you\u2019re starting your mission plan from scratch and want a lot of flexibility in designing your spacecraft trajectory.
STK users who choose this route should follow the following steps:
In this Python notebook, the event_generation
module from the PLANTS library is demonstrated.
We start by importing the necessary libraries:
%load_ext autoreload\n%autoreload 2\nfrom pint_backend import *\nimport pulsar_definitions\npint.logging.setup(level=\"INFO\")\n
Loads the ephemerides kernel
pint.solar_system_ephemerides.load_kernel(\"de440\")\n
\u001b[1mINFO \u001b[0m (pint.solar_system_ephemerides ): \u001b[1mSet solar system ephemeris to de440 through astropy\u001b[0m\n
Creates the time vector that the user hopes to observe at and adds additional error if desired. Specifies what pulsar model to use
time_interval = astrotime.Time( ['2022-07-11T16:00:01.000', '2022-07-11T16:21:01.000'], format='isot', scale='utc')\nobs_fs = 100e6\nn_toa = 10\nerror = 5 * u.us\napply_error = False\nmodel_name = \"j0218\" \n
Loads in the navigation data provided by STK and generates a spacecraft object off of that navigation data. Note that spacecraft is a custom class created in the PINT backend
time, pos, vel = load_nav_data(\"XNAVSAT_TEMEofDate_Position_Velocity_JD.txt\")\nmy_spacecraft = SpaceCraft(\"my_spacecraft\", time, pos, vel, overwrite = True)\n
Generates the times of arrival at the barycenter compared to the spacecraft. Note that printing the time of arrival generated is slightly different at the two locations
if(model_name == \"j0218\"): \n pulsar_of_interest = PulsarObj(pulsar_definitions.J0218)\nelif(model_name == \"b1821\"):\n pulsar_of_interest = PulsarObj(pulsar_definitions.B1821)\ntoas_barycenter = pint.simulation.make_fake_toas_uniform(\n time_interval[0].mjd, time_interval[1].mjd, n_toa, model=pulsar_of_interest.model, freq = 1e15*u.Hz, obs='barycenter', error=error)\ntoas_spacecraft = pint.simulation.make_fake_toas_uniform(\n time_interval[0].mjd, time_interval[1].mjd, n_toa, model=pulsar_of_interest.model, freq = 1e15*u.Hz, obs = \"my_spacecraft\", error=error)\n\n\n\nprint(\"Barycenter first TOA: \" + str(toas_barycenter.get_mjds(\"True\")[0].utc.iso))\nprint(\"Spacecraft first TOA: \" + str(toas_spacecraft.get_mjds(\"True\")[0].utc.iso))\n
\u001b[33m\u001b[1mWARNING \u001b[0m (pint.logging ): \u001b[33m\u001b[1m/opt/anaconda/lib/python3.10/site-packages/pint/models/timing_model.py:373 UserWarning: PINT only supports 'T2CMETHOD IAU2000B'\u001b[0m\n\u001b[33m\u001b[1mWARNING \u001b[0m (pint.logging ): \u001b[33m\u001b[1m/opt/anaconda/lib/python3.10/site-packages/pint/models/model_builder.py:139 UserWarning: Unrecognized parfile line 'EPHVER 2'\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.simulation ): \u001b[1mUsing CLOCK = TT(TAI), so setting include_bipm = False\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.models.absolute_phase ): \u001b[1mThe TZRSITE is set at the solar system barycenter.\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.models.absolute_phase ): \u001b[1mTZRFRQ was 0.0 or None. Setting to infinite frequency.\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.simulation ): \u001b[1mUsing CLOCK = TT(TAI), so setting include_bipm = False\u001b[0m\n\n\nBarycenter first TOA: 2022-07-11 15:58:51.816031354\nSpacecraft first TOA: 2022-07-11 15:58:51.815489874\n
%matplotlib inline\nresolution_factor = 1\npulsar_of_interest.lightcurve.plot_lightcurve()\ntime_vec, photon_vec, probability_arr = pulsar_of_interest.lightcurve.create_toa_vec(0, 10, k=4,resolution_factor= resolution_factor)\n
%matplotlib inline\nplt.plot(time_vec[0:10000], photon_vec[0:10000])\nplt.xlabel(\"Time (s)\")\nplt.ylabel(\"Number of photons received\")\nplt.title(\"Photon vector over 25 ms\")\n
Text(0.5, 1.0, 'Photon vector over 25 ms')\n
snip = time_vec\nindices = np.argwhere(photon_vec != 0).flatten()\nplt.hist(snip[indices], bins = 500)\nplt.title(\"Histogram of photons received with bin resolution = 60 ms\")\nplt.xlabel(\"Time (s)\")\nplt.ylabel(\"Number of photons received\")\n
Text(0, 0.5, 'Number of photons received')\n
%matplotlib inline\nbarycenter_toas = create_obs_time_vec(toas_barycenter.first_MJD, time_vec)\npulse_fold_barycenter = LightProfile.pulse_folding(barycenter_toas, photon_vec, pulsar_of_interest.lightcurve, mjd = True, start_offset = toas_barycenter.first_MJD, resolution_factor = resolution_factor)\n\nplt.plot(pulsar_of_interest.lightcurve.phase_list, pulse_fold_barycenter)\nplt.xlabel(\"Phase\")\nplt.ylabel(\"Count\")\nspacecraft_toas = create_obs_time_vec(toas_spacecraft.first_MJD, time_vec)\npulse_fold_spacecraft = LightProfile.pulse_folding(spacecraft_toas, photon_vec, pulsar_of_interest.lightcurve, mjd = True, start_offset = toas_barycenter.first_MJD, resolution_factor = resolution_factor)\nplt.plot(pulsar_of_interest.lightcurve.phase_list, pulse_fold_spacecraft)\nplt.legend(['Barycenter', 'Spacecraft'])\n# plt.plot(pulse_fold_spacecraft.phase_list, pulse_fold_barycenter)\n# plt.show()\n
<matplotlib.legend.Legend at 0x7f9a6dc79120>\n
An example of what happens if you fold the pulse with the wrong pulsar of interest
pulse_fold_barycenter = LightProfile.pulse_folding(barycenter_toas, photon_vec, pulsar_of_interest.lightcurve, mjd = True, start_offset = toas_barycenter.first_MJD, resolution_factor = resolution_factor)\nplt.plot(pulsar_of_interest.lightcurve.phase_list, pulse_fold_barycenter)\nplt.xlabel(\"Phase\")\nplt.ylabel(\"Count\")\n
Text(0, 0.5, 'Count')\n
np.save(\"pulsar_vec.npy\", np.asarray(photon_vec))\n
"},{"location":"components/3__custom_event_generation/ceg_overview/#hardware-in-the-loop-sdrs","title":"Hardware In The Loop (SDRs)","text":"In this notebook, we will walk through setup and operation of SDRs to transmit pulse train data for pulsar signal simulation.
"},{"location":"components/3__custom_event_generation/ceg_overview/#checking-sdr-connectivity","title":"Checking SDR Connectivity","text":"To start, it is necessary to make sure you have successfully mounted the SDRs to your PC via USB. You can check this by executing the following commands in a command prompt:
# ping 192.168.2.1\n# ping 192.168.2.2\n
For each of these commands, you should see four data packets successfully sent to the SDR and received back by the PC.
Next, it will be necessary to check that both SDRs can transmit and receive data to themselves. You can do so by executing the following code. Before running this code, make sure both SDRs are mounted to your PC and have been pinged successfully, and that each one has a BNC cable connecting its Rx port to its Tx port.
import numpy as np\nimport adi\nimport matplotlib.pyplot as plt\nimport sdr_io\n\nsample_rate = 1e6 # Hz\ncenter_freq = 915e6 # Hz\n\ntx_data = np.load('test_vec.npy')\ntx_length = 5000\n\nplot_rx_pulse_train = True\nplot_rx_samples = True\nplot_fft = True\ncheck_fid = True\n\nsdr1 = \"ip:192.168.2.1\"\nsdr2 = \"ip:192.168.2.2\"\n\nsdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr1,sdr1,\n tx_data,tx_length,\n False,False, False, False)\n\nsdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr2,sdr2,\n tx_data,tx_length,\n False,False, False, False)\n
Now that you have verified both SDRs can transmit and receive data to and from your PC, connect the Tx port of the \"X-RAY EMITTER\" SDR to the Rx port of the \"X_RAY DETECTOR\" SDR and execute the following code. This will transmit the same test pulse as above, but between the two SDRs instead of within each one.
sdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr1,sdr2,\n tx_data,tx_length,\n plot_rx_pulse_train,plot_rx_samples,plot_fft,check_fid)\n
Out of phase by 1141\nSuccess! Perfect Transmission!\n
Once you have verified that the SDRs can communicate with one another, you are ready to proceed with data transmission.
"},{"location":"components/3__custom_event_generation/ceg_overview/#transmitting-pulsar-simulation-data","title":"Transmitting Pulsar Simulation Data","text":"After generating a pulse train from the OpenXNAV software, transmit it from the \"Emitter\" SDR to the \"Detector\" SDR by loading it through the code below.
tx_data_name = \"pulsar_vec.npy\" # insert filename for pulse train here\ntx_data = np.load(tx_data_name)\ntx_data = tx_data[:5000]\ntx_length = len(tx_data)\n\nplot_rx_samples = True # once connectivity and fidelic transmission is verified,\n # this is redundant\nplot_fft = True # no longer concerned with freq domain - only concerned \n # with transmitted pulses\ncheck_fid = True # we have already verified connectivity - no need to check again\nprint(\"Transmitted data\")\nplt.plot(tx_data)\nplt.show()\nprint(\"Received data\")\nsdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr1,sdr2,\n tx_data,tx_length,\n plot_rx_pulse_train,plot_rx_samples,plot_fft,check_fid)\n
Transmitted data\n
Received data\n
Out of phase by 2890\nSuccess! Perfect Transmission!\n
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":"Open-source modular toolkit for simulating high-fidelity pulsar X-ray events.
"},{"location":"#introduction-to-openxnav","title":"Introduction to OpenXNAV","text":"OpenXNAV is designed to aid development and testing of Pulsar-based Autonomous Navigation (XNAV) Positioning, Navigation, and Timing (PNT) solutions.
This tool is a flexible, cost-effective testbed to enable the next generation of XNAV solutions. OpenXNAV includes three modular components that allow engineers simulate high-fidelity pulsar X-ray events along a flight trajectory over a user-defined mission timeline. Hardware-in-the-loop functionality can be configured using software-defined radios (SDRs), and users can query for pulsar candidates using the query tool (powered by the ATNF Pulsar Catalogue).
For more information, you can read our IEEE paper: IEEE.org.
"},{"location":"#components","title":"Components","text":"Each component of OpenXNAV can be used individually, or strung together as an end-to-end pipeline. To get started with OpenXNAV, select the module you are interested in leveraging and access the documentation.
"},{"location":"components/#pulsar-querying","title":"Pulsar Querying","text":"Query for all known pulsars catalogued in the ATNF database by providing a coordinate in space, and a search radius.
"},{"location":"components/#mission-planning","title":"Mission Planning","text":"Model the desired flight path, taking into account spacecraft orientation, viewing angle and line of sight.
"},{"location":"components/#timing-event-generation","title":"Timing & Event Generation","text":"Generate representative photon arrival events at the location of interest along the user-defined trajectory.
"},{"location":"components/1__pulsar_querying/pq_overview/","title":"Pulsar Querying","text":""},{"location":"components/1__pulsar_querying/pq_overview/#installation","title":"Installation","text":"Before using the OpenXNAV Pulsar Querying Tool, you will need to install the necessary python packages. You can do this using your preferred python package manager, but instructions are provided below for using Anaconda
Inside the open-xnav/1__pulsar_querying/
directory, there is an environment.yml
file. This file can be passed to your preferred package manager to create a new environment that can run the tool.
The default environment name in environment.yml
is openXNAV-gui
. If you wish to change the environment name, simply edit environment.yml
before creating the environment.
Open your Anaconda Prompt (or activate Anaconda in a terminal window). Navigate to open-xnav/`1__pulsar_querying/
and run the following command:
conda env create -f environment.yaml\n
To activate your environment, run the following command:
conda activate openXNAV-gui\n
NOTE: If you altered the environment name, you will need to alter the above command to reflect that new name. "},{"location":"components/1__pulsar_querying/pq_overview/#install-kivymd","title":"Install KivyMD","text":"The tool also depends on KivyMD, which is a library built off of the popular Kivy library. This package is not available through conda
, so you must install it using pip
.
First, make sure you have activated the openXNAV-gui
environment in your terminal. Then, run the following command:
pip install kivymd\n
If you are getting a ReadTimeoutError
response during installation, you might be using a VPN or have a slower internet connection. If this happens, try running the following instead:
pip install --default-timeout=1000 kivymd\n
If that still does not work, you can troubleshoot your issues further using the pip
documentation here.
Once you have successfully installed all the required packages, you are ready to run the tool!
First, ensure you have navigated to open-xnav/1__pulsar_querying/
in your terminal and have your openXNAV-gui
environment activated.
Then, simply run the following in your terminal:
python run.py\n
Upon startup, the tool will need to pull the current ATNF Pulsar Catalogue from the web. This process only need to be done the first time you open the application.
However, if you wish to update the database, simply delete the .../pulsar_database/
directory from your file system. Then, relaunch the application. Once the database has been generated, you can continue begin querying.
You should see the OpenXNAV launch window with the blue Launch Application
button active. In the future, when you open the application, the tool will locate the previously-generated database and you can quickly continue to the querying functionality.
After you click Launch Application
, the query tool will display.
There are three fields that you need to provide to execute a query:
These values will create a query, searching for all known pulsars that are within a given search radius of the provided coordinates in space.
The OpenXNAV tool leverages the open-source psrqpy
library. More information about this package, and how queries are executed, can be found here.
The query results will display to the GUI interface, and will also be stored in a sub-directory within .../query_results/
.
For information about how these sub-directories are tagged, see below.
"},{"location":"components/1__pulsar_querying/pq_overview/#tagging-results","title":"Tagging Results","text":"There is an optional Tag
field that OpenXNAV also provides. This allows you to specify the tag for this query you are about to execute. If a tag is provided, that will be used to name the sub-directory within .../query_results/
where all pulsars returned by the query are stored.
Sample Queries
Below are couple sample queries that you can run to get familiar with the OpenXNAV tool. There are additional sample query results contained in the .../query_results/
directory.
Right now, the OpenXNAV Pulsar Querying Tool only outputs pulsar data into the .st
format. This is the format that is required by STK to perform subsequent mission planning.
To display the format of this file, here is J00012_5431.st
as an example:
stk.v.12.0\nWrittenBy OpenXNAV\n\nBEGIN Star\n\n Name J0012_5431\n\n BEGIN PathDescription\n\n Epoch 58912.0\n RefFrame J2000\n RightAscension 3.097083333333333\n Declination +54:31:47\n ProperMotionRAPerYr 0\n ProperMotionDecPerYr 0\n Parallax 0\n RadialVelocity 0.0000000000000000e+00\n\n END PathDescription\n\n BEGIN PhysicalData\n\n Magnitude None\n\n END PhysicalData\n\n BEGIN IdentityData\n\n Id 0\n\n END IdentityData\n\n\n BEGIN Extensions\n\n BEGIN ExternData\n END ExternData\n\n BEGIN ADFFileData\n END ADFFileData\n\n BEGIN AccessConstraints\n LineOfSight IncludeIntervals\n\n UsePreferredMaxStep No\n PreferredMaxStep 360\n END AccessConstraints\n\n BEGIN Desc\n END Desc\n\n BEGIN Crdn\n END Crdn\n\n BEGIN Graphics\n\n BEGIN Attributes\n\n MarkerColor #00ff00\n LabelColor #00ff00\n MarkerStyle 2\n FontStyle 0\n\n END Attributes\n\n BEGIN Graphics\n\n Show On\n Inherit On\n ShowLabel On\n ShowMarker On\n\n END Graphics\n END Graphics\n\n BEGIN VO\n END VO\n\n END Extensions\n\nEND Star\n
You will need to provide these .st
file(s) to STK to successfully execute OpenXNAV's mission planning functionality.
However, if you would like pulsar information to be stored/saved in a different format, you can modify the source code.
Navigate to queryPulsar.py
and locate the following function:
def saveToFile(self, root_directory) ...\n
Here, you can alter/update the code to print the pulsar information into your desired format. Once this function has been updated, query results will automatically store in this new format. All query results will still appear in .../query_results/
."},{"location":"components/2__mission_planning/mp_overview/","title":"Mission Planning","text":"In this Python notebook, the mission_planning
module from the OpenXNAV library is demonstrated.
We start by importing the necessary libraries:
from astropy import units as u\nfrom astropy.coordinates import SkyCoord,get_body\nfrom astropy.time import Time,TimeDelta\nfrom astropy.table import Table,QTable\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\n
Next we define the necessary variables that we will use to instantiate the orbit. We will define an elliptical orbit directly by its orbital elements in this example. Other options, which are currently commented out, are a circular orbit (special case of elliptical orbit where eccentricity = 0) and an elliptical orbit defined by its burnout trajectory.
# User definitions for elliptical orbital elements\n\ne = 0.3 # eccentricity of the orbit\na = 750000*u.m # semi-major axis \ninc = 20*u.deg # inclination of orbit\nw = 90*u.deg # argument of periapsis\nv_0 = 0*u.deg # initial eccentric anomaly\nOmega = 90*u.deg # longitude of ascending node\n\nimport warnings\nwarnings.filterwarnings('ignore',message='leap-second file is expired')\nt = Time.now() + TimeDelta(120*u.day) + TimeDelta(np.linspace(0,800,51)*u.s)\n\n# Alternative: User definitions for elliptical orbital insertion \n# (note that this is just an example and would not generate the \n# same trajectory as the one above defined directly by orbital elements)\n\n#e = 0.1 # eccentricity of the orbit, also user defined\n#a = 750000*u.m # m semi-major axis \n#B = 70*u.deg # azimuth heading measured in degrees clockwise from north\n#dec = 20*u.deg # geocentric latitude in degrees (declination) of burnout\n#v_0 = 0*u.deg # initial eccentric anomaly\n
We can now do the same thing with a low-fidelity elliptical orbit, starting with the EllipticalOrbit
class:
from mission_planning import EllipticalOrbit\n\neo = EllipticalOrbit(t,a,e,v_0=v_0,\n inc=inc,w=w,\n Omega=Omega)\n\nplt.figure()\nplt.plot(eo.x.to(u.km),eo.y.to(u.km),'b.')\n#plt.xlim([-a/u.km,a/u.km])\n#plt.ylim([-a/u.km,a/u.km])\nplt.show()\n
While the above plot is represented in two dimensions as the projection of the orbit onto the XY-plane, this orbit was actually instantiated in 3D. To show the trajectory in the 3D Cartesian reference frame with the center of Earth at the origin, we just have to plot it in the 3D figure:
fig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(eo.x.to(u.km),eo.y.to(u.km),eo.z.to(u.km))\n\nplt.show()\n
"},{"location":"components/2__mission_planning/mp_overview/#creating-vectors-from-pulsar-to-space-objects-to-look-for-access","title":"Creating vectors from pulsar to space objects to look for access","text":"In order to better demonstrate the pulsar access calculation functions in the OpenXNAV mission_planning
module, we will create a SkyCoord object representing the moon, but with a distorted size and orbit to ensure some obfuscation of a chosen pulsar (whose coordinates are also exaggerated for demonstration purposes).
from mission_planning import plot_accesses\n\nmoon = SkyCoord(\n x = -284405*u.m,\n y = 249415*u.m,\n z = 0*u.m,\n frame='gcrs',\n representation_type = 'cartesian'\n)\n\n#moon_rad = 1740*u.km # actual radius of the moon \nmoon_rad = 150*u.km # fake news \n\n\n# assuming we have the location of the pulsars, using one as an example\npulsar = SkyCoord(\n x = -853215*u.m,\n y = 748245*u.m,\n z = 0*u.km,\n frame='gcrs',\n representation_type = 'cartesian'\n)\n\naccess = eo.pulsar_access(pulsar,moon,moon_rad)\n\n# create figure to plot pulsar access\nfig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(pulsar.x.to(u.km),pulsar.y.to(u.km),pulsar.z.to(u.km),label='pulsar')\nax.scatter(eo.x.to(u.km),eo.y.to(u.km),eo.z.to(u.km),label='spacecraft')\nax.scatter(moon.x.to(u.km),moon.y.to(u.km),moon.z.to(u.km),label='moon',s=500)\nplt.legend()\n\nplt.show()\n\nfig = plt.figure()\nax = fig.add_subplot()\nax = plot_accesses(ax,t,{'pulsar':access})\n
We can also instantiate an EllipticalOrbit
object with high fidelity. This version of the class calculates the eccentric anomaly at each time in the time array in order to calculate a more exact position.
There is currently a bug in this version that causes it to only output positive x
and positive z
values, so we'll need to fix that. (Negative values shown below are there because x
and z
are positive before coordinate rotation to account for longitude of ascending node, orbital inclination, and argument of periapsis)
eo_h = EllipticalOrbit(t,a,e,v_0=v_0,\n inc=inc,w=w,\n Omega=Omega,\n hifi=True)\n\nplt.figure()\nplt.plot(eo_h.x,eo_h.y,'b.')\nplt.xlim([-a/u.m,a/u.m])\nplt.ylim([-a/u.m,a/u.m])\nplt.show()\n\nfig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(eo_h.x,eo_h.y,eo_h.z)\n\nplt.show()\n
"},{"location":"components/2__mission_planning/mp_overview/#using-the-actual-position-of-known-celestial-bodies","title":"Using the actual position of known celestial bodies","text":""},{"location":"components/2__mission_planning/mp_overview/#example-1-circular-orbit","title":"Example 1 - Circular Orbit","text":"Next we will take advantage of the get_body
and pulsar_access_export
functionalities of Astropy and OpenXNAV Mission Planning, respectively.
Astropy allows us to create a SkyCoord object using the actual location of the moon at any time or array of times. We will make our moon much bigger than in real life to ensure that we obfuscate some pulsars for demonstration purposes. We will define a new orbit circular orbit using the CircularOrbit
class, which is a special case of the EllipticalOrbit
class in which eccentricity and orbital inclination are zero.
from mission_planning import CircularOrbit\n\n# User definitions for circular orbit\nr = 360000*u.km\nt0 = Time('2023-08-24 12:12:15.932083')\nt = t0 + TimeDelta(np.linspace(0,25,100)*u.day)\n\nmoon = get_body('moon',t)\nmoon.representation_type='cartesian'\nMOON_RAD = 1740*u.km\n\nearth = get_body('earth',t)\nearth.representation_type = 'cartesian'\nEARTH_RAD = 6378.14*u.km\n\nco = CircularOrbit(t,r)\n
Next we will create an astropy.table.Table
object with some pulsars that we want to load in to our pulsar access export function. The table is shown and created here for demonstration purposes; however, tables with this format can be created in the OpenXNAV pulsar query module.
name = [ 'J0002+6216' , 'J0006+1834' , 'J0007+7303' , 'J0011+08' , 'J0012+5431']\nraJ_deg = [ 0.74238 , 1.52 , 1.7571 , 2.9 , 3.0971 ] * u.deg\ndecJ_deg = [ 62.26928 , 18.5831 , 1.7571 , 8.17 , 54.5297 ] * u.deg\ndist_kpc = [ 6.357 , 0.86 , 1.4 , 5.399 , 5.425 ] * u.kpc\n\nt = Table([name,raJ_deg,decJ_deg,dist_kpc],\n names = ['NAME','RAJD','DECJD','DIST'])\n\nt.write('some_pulsars.fits',overwrite=True)\nprint(t)\n
NAME RAJD DECJD DIST\n deg deg kpc \n---------- ------- -------- -----\nJ0002+6216 0.74238 62.26928 6.357\nJ0006+1834 1.52 18.5831 0.86\nJ0007+7303 1.7571 1.7571 1.4\n J0011+08 2.9 8.17 5.399\nJ0012+5431 3.0971 54.5297 5.425\n
"},{"location":"components/2__mission_planning/mp_overview/#using-the-pulsar_access_export-method","title":"Using the pulsar_access_export
method","text":"Now that we have our spacecraft, moon, and pulsar table, we can use the pulsar_access_export
method to create a table and plot of pulsar accesses. Both this method and the pulsar_access
function allow you to account for obfuscation from multiple celestial bodies; just make sure to define them as shown below, entered before the keyword arguments and alternating between the SkyCoord
object representing the celestial body and the radius of the body represented as an astropy.units.Quantity
object.
pulsars_qtbl = QTable.read('some_pulsars.fits')\n\n# 3D plot of satellite orbit and moon\nfig1 = plt.figure()\nax1 = fig1.add_subplot(projection='3d')\nax1.scatter(moon.x.to(u.km),moon.y.to(u.km),moon.z.to(u.km),label='moon',s=100)\nax1.scatter(co.x.to(u.km),co.y.to(u.km),co.z.to(u.km),label='satellite in cirular orbit')\nax1.scatter(earth.x.to(u.km),earth.y.to(u.km),earth.z.to(u.km),label='earth',s=500)\nplt.legend(loc='upper left')\n\n# Plot of pulsar accesses\n_,(fig2,ax2) = co.pulsar_access_export(pulsars_qtbl,\n moon,MOON_RAD,\n earth,EARTH_RAD,\n make_csv=False,\n make_fig=True,save_fig=False)\n
"},{"location":"components/2__mission_planning/mp_overview/#example-2-elliptical-orbit","title":"Example 2 - Elliptical Orbit","text":"Next we will define a new elliptical orbit from the EllipticalOrbit
class in which eccentricity and orbital inclination are non-zero.
# User definitions for elliptical orbital elements\n\ne = 0.3 # eccentricity of the orbit\na = 30000*u.km # semi-major axis \ninc = 20*u.deg # inclination of orbit\nw = 90*u.deg # argument of periapsis\nv_0 = 0*u.deg # initial eccentric anomaly\nOmega = 90*u.deg # longitude of ascending node\n\nt0 = Time('2023-08-24 12:12:15.932')\nt = t0 + TimeDelta(np.linspace(0,25,1000)*u.day)\n\nmoon = get_body('moon',t)\nmoon.representation_type='cartesian'\nMOON_RAD = 1740*u.km\n\nearth = SkyCoord(x=0*u.m,y=0*u.m,z=0*u.m,frame='gcrs',representation_type='cartesian')\nEARTH_RAD = 6378.14*u.km\n\neo2 = EllipticalOrbit(t,a,e,v_0=v_0,\n inc=inc,w=w,\n Omega=Omega)\n\nfig = plt.figure()\n\nax = fig.add_subplot(projection='3d')\nax.scatter(eo2.x.to(u.km),eo2.y.to(u.km),eo2.z.to(u.km))\n\nplt.show()\n
Next we will create a CSV with some pulsars that we want to load in to our pulsar access export function. The CSV is shown and created here for demonstration purposes:
name = [ 'J0002+6216' , 'J0006+1834' , 'J0007+7303' , 'J0011+08' , 'J0012+5431']\nraJ_deg = [ 0.74238 , 1.52 , 1.7571 , 2.9 , 3.0971 ] * u.deg\ndecJ_deg = [ 62.26928 , 18.5831 , 1.7571 , 8.17 , 54.5297 ] * u.deg\ndist_kpc = [ 6.357 , 0.86 , 1.4 , 5.399 , 5.425 ] * u.kpc\n\ntbl = Table([name,raJ_deg,decJ_deg,dist_kpc],\n names = ['NAME','RAJD','DECJD','DIST'])\n\ntbl.write('some_pulsars.fits',overwrite=True)\nprint(tbl)\n
NAME RAJD DECJD DIST\n deg deg kpc \n---------- ------- -------- -----\nJ0002+6216 0.74238 62.26928 6.357\nJ0006+1834 1.52 18.5831 0.86\nJ0007+7303 1.7571 1.7571 1.4\n J0011+08 2.9 8.17 5.399\nJ0012+5431 3.0971 54.5297 5.425\n
"},{"location":"components/2__mission_planning/mp_overview/#using-the-pulsar_access_export-method_1","title":"Using the pulsar_access_export
method","text":"Now that we have our spacecraft, moon, and pulsar CSV, we can use the pulsar_access_export
method to create a table and plot of pulsar accesses. Both this method and the pulsar_access
function allow you to account for obfuscation from multiple celestial bodies; just make sure to define them as shown below, entered before the keyword arguments and alternating between the SkyCoord object representing the celestial body and the radius of the body represented as an astropy.units.Quantity object
.
As part of the pulsar_access_export
method call, we can write all pulsar access data to a CSV file. This CSV can then be used in the pulsar photon time-of-arrival simulation module to model the X-ray pulse sequence that the spacecraft would observe at those coordinates.
pulsars_qtbl = QTable.read('some_pulsars.fits')\n\n# 3D plot of satellite orbit\nfig1 = plt.figure()\nax1 = fig1.add_subplot(projection='3d')\nax1.scatter(eo2.x.to(u.km),eo2.y.to(u.km),eo2.z.to(u.km),label='satellite in elliptical orbit')\nax1.scatter(earth.x.to(u.km),earth.y.to(u.km),earth.z.to(u.km),label='earth',s=200)\nplt.legend(loc='upper left')\n\n# 3D plot of satellite orbit and moon\nfig2 = plt.figure()\nax2 = fig2.add_subplot(projection='3d')\nax2.scatter(moon.x.to(u.km),moon.y.to(u.km),moon.z.to(u.km),label='moon',s=100)\nax2.scatter(eo2.x.to(u.km),eo2.y.to(u.km),eo2.z.to(u.km),label='satellite in elliptical orbit')\nax2.scatter(earth.x.to(u.km),earth.y.to(u.km),earth.z.to(u.km),label='earth',s=200)\nplt.legend(loc='upper left')\n\n# Plot of pulsar accesses, and save pulsar access table to CSV\naccesses,(fig3,ax3) = eo2.pulsar_access_export(pulsars_qtbl,\n moon,MOON_RAD,\n earth,EARTH_RAD,\n make_csv=True,save_csv=True,\n make_fig=True,save_fig=False)\n
The breaks in the plot above might be hard to see since they are so small compared to the scale of the timeline displayed. We have therefore represented the breaks in tabular form below.
# Create DataFrame containing all times at which access to at least one pulsar is interrupted\naccess_breaks = accesses[accesses.all(axis=1) == False]\naccess_breaks\n
Time_JDate Spacecraft_pos_X_km Spacecraft_pos_Y_km Spacecraft_pos_Z_km Spacecraft_vel_X_kmps Spacecraft_vel_Y_kmps Spacecraft_vel_Z_kmps J0002+6216 J0006+1834 J0007+7303 J0011+08 J0012+5431 0 2.460181e+06 -19733.545037 2.343791e-12 4.132736e-13 4.667259 0.000000 0.000000 True False False False True 24 2.460182e+06 -19722.356542 -8.518943e+02 -1.662908e+01 4.663209 0.190622 0.065196 True False False False True 48 2.460182e+06 -19688.801001 -1.703092e+03 -3.324457e+01 4.651077 0.380744 0.130222 True False False False True 72 2.460183e+06 -19632.908273 -2.552897e+03 -4.983288e+01 4.630912 0.569870 0.194907 True True False False True 96 2.460183e+06 -19554.728216 -3.400614e+03 -6.638041e+01 4.602798 0.757509 0.259083 True True False False True 120 2.460184e+06 -19454.330828 -4.245547e+03 -8.287361e+01 4.566851 0.943180 0.322587 True True False False True 143 2.460185e+06 -19264.410077 5.493859e+03 1.072408e+02 4.499325 -1.214310 -0.415318 True True False True True 144 2.460185e+06 -19331.806438 -5.087002e+03 -9.929892e+01 4.523216 1.126412 0.385256 True True False False True 167 2.460185e+06 -19397.632920 4.654341e+03 9.085332e+01 4.546627 -1.032427 -0.353111 True True False False True 168 2.460185e+06 -19187.265950 -5.924286e+03 -1.156428e+02 4.472073 1.306749 0.446935 True True False False True 191 2.460186e+06 -19508.779991 3.811009e+03 7.439137e+01 4.586325 -0.847881 -0.289993 True True False False True 192 2.460186e+06 -19020.841125 -6.756708e+03 -1.318918e+02 4.413626 1.483752 0.507473 True True False False True 215 2.460186e+06 -19597.751010 2.964556e+03 5.786850e+01 4.618256 -0.661138 -0.226122 True True False False True 239 2.460187e+06 -19664.466140 2.115677e+03 4.129829e+01 4.642291 -0.472672 -0.161663 True True False False True 263 2.460188e+06 -19708.865772 1.265068e+03 2.469429e+01 4.658329 -0.282972 -0.096782 True True False False True 287 2.460188e+06 -19730.910356 4.134251e+02 8.070109e+00 4.666305 -0.092530 -0.031647 True False False False True 311 2.460189e+06 -19730.580291 -4.385562e+02 -8.560672e+00 4.666185 0.098154 0.033571 True False False False True 333 2.460189e+06 -13839.201092 1.850473e+04 3.612147e+02 2.807521 -3.552673 -1.215086 True True False True True 334 2.460189e+06 -18399.018428 9.194010e+03 1.794682e+02 4.199348 -1.985878 -0.679210 True True False True True 335 2.460189e+06 -19707.875871 -1.290179e+03 -2.518445e+01 4.657971 0.288581 0.098700 True False False False True 359 2.460190e+06 -19662.817284 -2.140747e+03 -4.178765e+01 4.641696 0.478252 0.163572 True True False False True 383 2.460191e+06 -19595.444668 -2.989564e+03 -5.835666e+01 4.617427 0.666673 0.228016 True True False False True 407 2.460191e+06 -19505.818229 -3.835935e+03 -7.487792e+01 4.585264 0.853359 0.291866 True True False False True 431 2.460192e+06 -19394.018402 -4.679164e+03 -9.133787e+01 4.545339 1.037832 0.354960 True True False False True 454 2.460192e+06 -19335.736565 5.062236e+03 9.881549e+01 4.524612 -1.121046 -0.383420 True True False True True 455 2.460192e+06 -19260.146075 -5.518559e+03 -1.077230e+02 4.497816 1.219630 0.417138 True True False False True 478 2.460193e+06 -19457.609710 4.220669e+03 8.238798e+01 4.568022 -0.937736 -0.320724 True True False False True 479 2.460193e+06 -19104.322848 -6.353426e+03 -1.240197e+02 4.442885 1.398305 0.478248 True True False False True 502 2.460194e+06 -19557.352895 3.375643e+03 6.589298e+01 4.603740 -0.752000 -0.257199 True True False False True 526 2.460194e+06 -19634.876395 2.527855e+03 4.934405e+01 4.631621 -0.564310 -0.193005 True True False False True 550 2.460195e+06 -19690.110807 1.677999e+03 3.275475e+01 4.651550 -0.375147 -0.128308 True True False False True 574 2.460195e+06 -19723.006865 8.267705e+02 1.613866e+01 4.663444 -0.185003 -0.063275 True True False False True 598 2.460196e+06 -19733.535299 -2.513369e+01 -4.906128e-01 4.667255 0.005626 0.001924 True False False False True 622 2.460197e+06 -19721.686753 -8.770174e+02 -1.711949e+01 4.662967 0.196240 0.067118 True False False False True 646 2.460197e+06 -19687.471756 -1.728184e+03 -3.373437e+01 4.650597 0.386340 0.132136 True False False False True 670 2.460198e+06 -19630.920755 -2.577938e+03 -5.032167e+01 4.630196 0.575429 0.196808 True True False False True 694 2.460198e+06 -19552.084202 -3.425582e+03 -6.686780e+01 4.601849 0.763017 0.260967 True True False False True 718 2.460199e+06 -19451.032689 -4.270423e+03 -8.335919e+01 4.565673 0.948623 0.324448 True True False False True 741 2.460200e+06 -19268.654973 5.469155e+03 1.067586e+02 4.500827 -1.208987 -0.413498 True True False True True 742 2.460200e+06 -19327.857151 -5.111765e+03 -9.978229e+01 4.521814 1.131776 0.387090 True True False False True 765 2.460200e+06 -19401.228226 4.629514e+03 9.036870e+01 4.547908 -1.027019 -0.351261 True True False False True 766 2.460200e+06 -19182.669102 -5.948915e+03 -1.161236e+02 4.470452 1.312021 0.448737 True True False False True 789 2.460201e+06 -19511.722453 3.786080e+03 7.390476e+01 4.587378 -0.842402 -0.288118 True True False False True 790 2.460201e+06 -19015.600922 -6.781183e+03 -1.323695e+02 4.411793 1.488919 0.509240 True True False False True 813 2.460201e+06 -19600.037983 2.939546e+03 5.738031e+01 4.619079 -0.655600 -0.224229 True True False False True 837 2.460202e+06 -19666.095577 2.090606e+03 4.080890e+01 4.642879 -0.467091 -0.159755 True True False False True 861 2.460203e+06 -19709.836218 1.239957e+03 2.420411e+01 4.658680 -0.277362 -0.094863 True True False False True 885 2.460203e+06 -19731.220949 3.882936e+02 7.579540e+00 4.666417 -0.086906 -0.029724 True False False False True 909 2.460204e+06 -19730.230755 -4.636870e+02 -9.051228e+00 4.666059 0.103778 0.035494 True False False False True 933 2.460204e+06 -19706.866516 -1.315289e+03 -2.567460e+01 4.657606 0.294189 0.100619 True False False False True 957 2.460205e+06 -19661.149008 -2.165815e+03 -4.227698e+01 4.641094 0.483830 0.165480 True True False False True 981 2.460206e+06 -19593.118960 -3.014570e+03 -5.884478e+01 4.616590 0.672208 0.229909 True True False False True"},{"location":"components/2__mission_planning/mp_overview/#substituting-stk-for-openxnav-mission-planning","title":"Substituting STK for OpenXNAV Mission Planning","text":"As an alternative to the mission_planning
module built into OpenXNAV, users can also plan their missions and generate trajectory and pulsar access inputs using the mission planning software of their choice, such as ANSYS STK. STK is very visual, and a great option if you\u2019re starting your mission plan from scratch and want a lot of flexibility in designing your spacecraft trajectory.
STK users who choose this route should follow the following steps:
In this Python notebook, the event_generation
module from the PLANTS library is demonstrated.
We start by importing the necessary libraries:
%load_ext autoreload\n%autoreload 2\nfrom pint_backend import *\nimport pulsar_definitions\npint.logging.setup(level=\"INFO\")\n
Loads the ephemerides kernel
pint.solar_system_ephemerides.load_kernel(\"de440\")\n
\u001b[1mINFO \u001b[0m (pint.solar_system_ephemerides ): \u001b[1mSet solar system ephemeris to de440 through astropy\u001b[0m\n
Creates the time vector that the user hopes to observe at and adds additional error if desired. Specifies what pulsar model to use
time_interval = astrotime.Time( ['2022-07-11T16:00:01.000', '2022-07-11T16:21:01.000'], format='isot', scale='utc')\nobs_fs = 100e6\nn_toa = 10\nerror = 5 * u.us\napply_error = False\nmodel_name = \"j0218\" \n
Loads in the navigation data provided by STK and generates a spacecraft object off of that navigation data. Note that spacecraft is a custom class created in the PINT backend
time, pos, vel = load_nav_data(\"XNAVSAT_TEMEofDate_Position_Velocity_JD.txt\")\nmy_spacecraft = SpaceCraft(\"my_spacecraft\", time, pos, vel, overwrite = True)\n
Generates the times of arrival at the barycenter compared to the spacecraft. Note that printing the time of arrival generated is slightly different at the two locations
if(model_name == \"j0218\"): \n pulsar_of_interest = PulsarObj(pulsar_definitions.J0218)\nelif(model_name == \"b1821\"):\n pulsar_of_interest = PulsarObj(pulsar_definitions.B1821)\ntoas_barycenter = pint.simulation.make_fake_toas_uniform(\n time_interval[0].mjd, time_interval[1].mjd, n_toa, model=pulsar_of_interest.model, freq = 1e15*u.Hz, obs='barycenter', error=error)\ntoas_spacecraft = pint.simulation.make_fake_toas_uniform(\n time_interval[0].mjd, time_interval[1].mjd, n_toa, model=pulsar_of_interest.model, freq = 1e15*u.Hz, obs = \"my_spacecraft\", error=error)\n\n\n\nprint(\"Barycenter first TOA: \" + str(toas_barycenter.get_mjds(\"True\")[0].utc.iso))\nprint(\"Spacecraft first TOA: \" + str(toas_spacecraft.get_mjds(\"True\")[0].utc.iso))\n
\u001b[33m\u001b[1mWARNING \u001b[0m (pint.logging ): \u001b[33m\u001b[1m/opt/anaconda/lib/python3.10/site-packages/pint/models/timing_model.py:373 UserWarning: PINT only supports 'T2CMETHOD IAU2000B'\u001b[0m\n\u001b[33m\u001b[1mWARNING \u001b[0m (pint.logging ): \u001b[33m\u001b[1m/opt/anaconda/lib/python3.10/site-packages/pint/models/model_builder.py:139 UserWarning: Unrecognized parfile line 'EPHVER 2'\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.simulation ): \u001b[1mUsing CLOCK = TT(TAI), so setting include_bipm = False\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.models.absolute_phase ): \u001b[1mThe TZRSITE is set at the solar system barycenter.\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.models.absolute_phase ): \u001b[1mTZRFRQ was 0.0 or None. Setting to infinite frequency.\u001b[0m\n\u001b[1mINFO \u001b[0m (pint.simulation ): \u001b[1mUsing CLOCK = TT(TAI), so setting include_bipm = False\u001b[0m\n\n\nBarycenter first TOA: 2022-07-11 15:58:51.816031354\nSpacecraft first TOA: 2022-07-11 15:58:51.815489874\n
%matplotlib inline\nresolution_factor = 1\npulsar_of_interest.lightcurve.plot_lightcurve()\ntime_vec, photon_vec, probability_arr = pulsar_of_interest.lightcurve.create_toa_vec(0, 10, k=4,resolution_factor= resolution_factor)\n
%matplotlib inline\nplt.plot(time_vec[0:10000], photon_vec[0:10000])\nplt.xlabel(\"Time (s)\")\nplt.ylabel(\"Number of photons received\")\nplt.title(\"Photon vector over 25 ms\")\n
Text(0.5, 1.0, 'Photon vector over 25 ms')\n
snip = time_vec\nindices = np.argwhere(photon_vec != 0).flatten()\nplt.hist(snip[indices], bins = 500)\nplt.title(\"Histogram of photons received with bin resolution = 60 ms\")\nplt.xlabel(\"Time (s)\")\nplt.ylabel(\"Number of photons received\")\n
Text(0, 0.5, 'Number of photons received')\n
%matplotlib inline\nbarycenter_toas = create_obs_time_vec(toas_barycenter.first_MJD, time_vec)\npulse_fold_barycenter = LightProfile.pulse_folding(barycenter_toas, photon_vec, pulsar_of_interest.lightcurve, mjd = True, start_offset = toas_barycenter.first_MJD, resolution_factor = resolution_factor)\n\nplt.plot(pulsar_of_interest.lightcurve.phase_list, pulse_fold_barycenter)\nplt.xlabel(\"Phase\")\nplt.ylabel(\"Count\")\nspacecraft_toas = create_obs_time_vec(toas_spacecraft.first_MJD, time_vec)\npulse_fold_spacecraft = LightProfile.pulse_folding(spacecraft_toas, photon_vec, pulsar_of_interest.lightcurve, mjd = True, start_offset = toas_barycenter.first_MJD, resolution_factor = resolution_factor)\nplt.plot(pulsar_of_interest.lightcurve.phase_list, pulse_fold_spacecraft)\nplt.legend(['Barycenter', 'Spacecraft'])\n# plt.plot(pulse_fold_spacecraft.phase_list, pulse_fold_barycenter)\n# plt.show()\n
<matplotlib.legend.Legend at 0x7f9a6dc79120>\n
An example of what happens if you fold the pulse with the wrong pulsar of interest
pulse_fold_barycenter = LightProfile.pulse_folding(barycenter_toas, photon_vec, pulsar_of_interest.lightcurve, mjd = True, start_offset = toas_barycenter.first_MJD, resolution_factor = resolution_factor)\nplt.plot(pulsar_of_interest.lightcurve.phase_list, pulse_fold_barycenter)\nplt.xlabel(\"Phase\")\nplt.ylabel(\"Count\")\n
Text(0, 0.5, 'Count')\n
np.save(\"pulsar_vec.npy\", np.asarray(photon_vec))\n
"},{"location":"components/3__custom_event_generation/ceg_overview/#hardware-in-the-loop-sdrs","title":"Hardware In The Loop (SDRs)","text":"In this notebook, we will walk through setup and operation of SDRs to transmit pulse train data for pulsar signal simulation.
"},{"location":"components/3__custom_event_generation/ceg_overview/#checking-sdr-connectivity","title":"Checking SDR Connectivity","text":"To start, it is necessary to make sure you have successfully mounted the SDRs to your PC via USB. You can check this by executing the following commands in a command prompt:
# ping 192.168.2.1\n# ping 192.168.2.2\n
For each of these commands, you should see four data packets successfully sent to the SDR and received back by the PC.
Next, it will be necessary to check that both SDRs can transmit and receive data to themselves. You can do so by executing the following code. Before running this code, make sure both SDRs are mounted to your PC and have been pinged successfully, and that each one has a BNC cable connecting its Rx port to its Tx port.
import numpy as np\nimport adi\nimport matplotlib.pyplot as plt\nimport sdr_io\n\nsample_rate = 1e6 # Hz\ncenter_freq = 915e6 # Hz\n\ntx_data = np.load('test_vec.npy')\ntx_length = 5000\n\nplot_rx_pulse_train = True\nplot_rx_samples = True\nplot_fft = True\ncheck_fid = True\n\nsdr1 = \"ip:192.168.2.1\"\nsdr2 = \"ip:192.168.2.2\"\n\nsdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr1,sdr1,\n tx_data,tx_length,\n False,False, False, False)\n\nsdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr2,sdr2,\n tx_data,tx_length,\n False,False, False, False)\n
Now that you have verified both SDRs can transmit and receive data to and from your PC, connect the Tx port of the \"X-RAY EMITTER\" SDR to the Rx port of the \"X_RAY DETECTOR\" SDR and execute the following code. This will transmit the same test pulse as above, but between the two SDRs instead of within each one.
sdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr1,sdr2,\n tx_data,tx_length,\n plot_rx_pulse_train,plot_rx_samples,plot_fft,check_fid)\n
Out of phase by 1141\nSuccess! Perfect Transmission!\n
Once you have verified that the SDRs can communicate with one another, you are ready to proceed with data transmission.
"},{"location":"components/3__custom_event_generation/ceg_overview/#transmitting-pulsar-simulation-data","title":"Transmitting Pulsar Simulation Data","text":"After generating a pulse train from the OpenXNAV software, transmit it from the \"Emitter\" SDR to the \"Detector\" SDR by loading it through the code below.
tx_data_name = \"pulsar_vec.npy\" # insert filename for pulse train here\ntx_data = np.load(tx_data_name)\ntx_data = tx_data[:5000]\ntx_length = len(tx_data)\n\nplot_rx_samples = True # once connectivity and fidelic transmission is verified,\n # this is redundant\nplot_fft = True # no longer concerned with freq domain - only concerned \n # with transmitted pulses\ncheck_fid = True # we have already verified connectivity - no need to check again\nprint(\"Transmitted data\")\nplt.plot(tx_data)\nplt.show()\nprint(\"Received data\")\nsdr_io.sdr_tx_rx(sample_rate,center_freq,\n sdr1,sdr2,\n tx_data,tx_length,\n plot_rx_pulse_train,plot_rx_samples,plot_fft,check_fid)\n
Transmitted data\n
Received data\n
Out of phase by 2890\nSuccess! Perfect Transmission!\n
"}]}
\ No newline at end of file
diff --git a/sitemap.xml.gz b/sitemap.xml.gz
index 7dd4fc4fe0ced3bde06c69c3a1a0700e223a3339..e8ad77346c9edac18df41ff487e58f6faaea816d 100644
GIT binary patch
delta 12
Tcmb=gXOr*d;5a&YB3mT@8RY~g
delta 12
Tcmb=gXOr*d;Mm(fk*yK{8I1%J