SV (Space Vehicle) Models

Load gnss_lib_py into the Python workspace

[1]:
import gnss_lib_py as glp

Adding SV States with Precise Ephemerides (SP3 & CLK)

This tutorial explains how to calculate satellite states using precise SP3 and CLK files.

The data required to calculate with precise ephemerides uses .sp3 and .clk files, which can be downloaded from CDDIS or CORS.

The .sp3 files provide post-processed, accurate, and precise information regarding 3-D satellite position in the Earth-Centered Earth-Fixed (ECEF) frame at intervals of 15mins each.

Similarly, the .clk files provide post-processed, accurate and precise information on satellite clock errors at intervals of 30secs each.

These .sp3 and .clk files are available for any GNSS constellation, and hence, provide a common processing platform for applications that involve multi-GNSS satellite signals (without requiring to parse the broadcast ephemeris from each constellation separately one at a time). Also, unlike broadcast ephemeris that can suffer from signal-in-space anomalies, the .sp3 and .clk files are guaranteed to provide accurate satellite information. However, note that, these files are only available in a post-processed manner, and not in real-time

We show how to analyze this precise ephemerides functionality for the Android derived dataset in the following cells,

  1. Load the derived data from AndroidDerived and remove the rows in NavData class that refer to satellite information (3-D satellite position, 3-D satellite velocity, clock bias and clock drift),

[2]:
import numpy as np

# load Android Google Challenge data
glp.make_dir("../data")
!wget https://raw.githubusercontent.com/Stanford-NavLab/gnss_lib_py/main/data/unit_test/google_decimeter_2021/Pixel4_derived_clkdiscnt.csv --quiet -nc -O "../data/Pixel4_derived_clkdiscnt.csv"
derived_data = glp.AndroidDerived2021("../data/Pixel4_derived_clkdiscnt.csv", remove_timing_outliers=False)
# Define the keys relevant for satellite information, and remove the data within these fields
SV_KEYS = ['x_sv_m', 'y_sv_m', 'z_sv_m', \
           'vx_sv_mps','vy_sv_mps','vz_sv_mps', \
           'b_sv_m', 'b_dot_sv_mps']
derived_data.remove(rows=SV_KEYS,inplace=True)
  1. Specify the paths to the .sp3 and .clk files using the file_path variable. If files are not specified, they will be automatically downloaded using the ephemeris downloader.

[3]:
# download .sp3 data file
!wget https://raw.githubusercontent.com/Stanford-NavLab/gnss_lib_py/main/data/unit_test/sp3/COD0MGXFIN_20211180000_01D_05M_ORB.SP3 --quiet -nc -O "../data/COD0MGXFIN_20211180000_01D_05M_ORB.SP3"
# Specify .sp3 file path to extract precise ephemerides
sp3_path = "../data/COD0MGXFIN_20211180000_01D_05M_ORB.SP3"

# download .clk data file
!wget https://raw.githubusercontent.com/Stanford-NavLab/gnss_lib_py/main/data/unit_test/clk/COD0MGXFIN_20211180000_01D_30S_CLK.CLK --quiet -nc -O "../data/COD0MGXFIN_20211180000_01D_30S_CLK.CLK"
# Specify .clk file path to extract precise ephemerides
clk_path = "../data/COD0MGXFIN_20211180000_01D_30S_CLK.CLK"
  1. Populate the columns of SV_KEYS with information extracted via precise ephemerides

[4]:
# Update derived_data class with satellite information computed via precise ephemerides
derived_multi_gnss = glp.add_sv_states(derived_data, source="precise", file_paths=[sp3_path, clk_path],
                                       verbose = False)

Check that all the desired fields related to satellite information have useful information and the norm of computed satellite position matches the altitude of GNSS constellations

[5]:

sat_alt = np.linalg.norm(derived_multi_gnss[["x_sv_m","y_sv_m","z_sv_m"],9:12],axis=0) print('Distance of two satellites from the center of the Earth (expected around 26000000 m)') print("Three GPS SVs calculated to be at:", sat_alt,"\n") print("Small section of calculated positions:") print(derived_multi_gnss.copy(cols=[2,3,4,5],rows=["gnss_id"]+SV_KEYS))
Distance of two satellites from the center of the Earth (expected around 26000000 m)
Three GPS SVs calculated to be at: [26911122.04449436 26524767.84699735 26807116.2420436 ]

Small section of calculated positions:
   gnss_id        x_sv_m        y_sv_m        z_sv_m    vx_sv_mps  \
0  glonass  1.028315e+06 -2.508196e+07  4.872502e+06   -47.496017
1   beidou -1.329501e+07  2.127309e+07  3.432704e+07 -1172.597461
2  galileo -1.741032e+07 -1.050844e+06  2.391041e+07  1073.905492
3      gps -2.025301e+07 -1.328622e+07 -1.118157e+07  1401.646017

     vy_sv_mps    vz_sv_mps         b_sv_m  b_dot_sv_mps
0  -680.864364 -3465.384568   -4315.205733     -0.000159
1  -378.022448  -247.266945   98014.980430      0.012253
2 -2146.931526   687.696232 -198441.105555     -0.000670
3   151.806778 -2738.652035   37633.913404      0.002177

Adding SV states to NavData with received measurements using broadcast ephemeris

This tutorial explains how to estimate SV states, both as estimation for a single time instance and measurements and as a wrapper for an entire set of received measurements.

For this tutorial, we will work with the AndroidDerived2022 dataset. This serves the dual purpose of showing how each functionality works and allowing us to compare the SV states estimated in sv_models.py to that estimated by Google. The latter verifies state computation from our method.

Load the test dataset for the Android Derived 2022 dataset

[6]:
import numpy as np
# download Android data file
glp.make_dir("../data")
!wget https://raw.githubusercontent.com/Stanford-NavLab/gnss_lib_py/main/data/unit_test/google_decimeter_2022/device_gnss.csv --quiet -nc -O "../data/device_gnss.csv"
# load Android Google Challenge data
derived_data = glp.AndroidDerived2022("../data/device_gnss.csv")

Create a copy of derived_data for comparison later on and remove Google computed SV states from derived_data.

These states will be added to derived_data using functions from gnss_lib_py.

Currently we only support GPS satellites for SV state estimation with broadcast ephemeris parameters. Support for other constellations is coming soon.

Now, estimate SV states and add them to derived_data using gnss_lib_py.

[7]:
derived_gps = derived_data.where("gnss_id", "gps")
derived_l1 = derived_gps.where("signal_type", "l1")
derived_reference = derived_l1.copy()
sv_state_rows = ['x_sv_m', 'y_sv_m', 'z_sv_m', 'vx_sv_mps', 'vy_sv_mps', 'vz_sv_mps', 'b_sv_m']
derived_l1.remove(rows=sv_state_rows, inplace=True)
derived_sv_states = glp.add_sv_states_rinex(derived_l1)

For sanity checking, we compare our estimated SV states to those computed by Google and print out the maximum absolute errors

[8]:
print('Estimating differences between estimated SV states and Google reference')
for row in sv_state_rows:
    mean_diff = np.mean(derived_reference[row] - derived_sv_states[row])
    max_diff = np.max(np.abs(derived_reference[row] - derived_sv_states[row]))
    print(f"For row {row}, the max error is {max_diff:.4f}, the mean error is {mean_diff:.4f}")
Estimating differences between estimated SV states and Google reference
For row x_sv_m, the max error is 1.6868, the mean error is 0.7418
For row y_sv_m, the max error is 1.8830, the mean error is -0.3220
For row z_sv_m, the max error is 2.1576, the mean error is -0.4968
For row vx_sv_mps, the max error is 0.0005, the mean error is 0.0001
For row vy_sv_mps, the max error is 0.0005, the mean error is 0.0000
For row vz_sv_mps, the max error is 0.0002, the mean error is 0.0001
For row b_sv_m, the max error is 0.0000, the mean error is 0.0000

Add SV states for visible satellites given a series of times and positions

Given a series of times and a trajectory, find visible satellites (assuming open sky conditions) for those positions and times

[9]:
# Construct a linear trajectory starting from the Durand building and
# moving North at 10 m/s for 10 steps
# This trajectory will not have any major differences in calculated
# satellite positions but is to demonstrate the functionality

from datetime import datetime, timezone
# Send time at which SV states are needed in GPS millis
start_time = datetime(year=2021,
                       month=4,
                       day=29,
                       hour=22,
                       minute=30,
                       second=0)
start_time = start_time.replace(tzinfo=timezone.utc)
start_gps_millis = glp.datetime_to_gps_millis(start_time)

# Create sequence of times
gps_millis_traj = start_gps_millis + 1000.*np.arange(10)

# Define receiver position in ECEF
rx_LLA_durand = np.reshape([37.427112, -122.1764146, 16], [3, 1])
rx_ecef_durand = np.reshape(glp.geodetic_to_ecef(rx_LLA_durand), [3, 1])

# Create sequence of moving receiver (using approximation of long to meters)
rx_LLA_traj = rx_LLA_durand + np.vstack((np.zeros(10),
                                         0.0001*10.*np.arange(10),
                                         np.zeros(10)))

# Convert trajectory to ECEF
rx_ecef_traj = glp.geodetic_to_ecef(rx_LLA_traj)

# Create state estimate with given trajectory
state_traj = glp.NavData()
state_traj['gps_millis'] = gps_millis_traj
state_traj['x_rx_m'] = rx_ecef_traj[0,:]
state_traj['y_rx_m'] = rx_ecef_traj[1,:]
state_traj['z_rx_m'] = rx_ecef_traj[2,:]

# Define all GPS satellites, so that all broadcast ephemeris parameters
# are downloaded
gps_all_sats = [f"G{sv:02}" for sv in range(1, 33)]

# Download ephemeris files for given time
ephem_all_sats = glp.get_time_cropped_rinex(start_gps_millis, gps_all_sats, ephemeris_directory="ephemeris")

With the setup done, we now pass these parameters to add_visible_svs_for_trajectory to add visible satellites corresponding to the times and positions of the trajectory.

[10]:
sv_posvel_traj = glp.add_visible_svs_for_trajectory(state_traj,
                                                    ephemeris_path="ephemeris")

For reference, we demonstrate the changing satellite positions for SV ID 30

[11]:
sv_posvel_traj_sv25 = sv_posvel_traj.where("sv_id", 25)

print('GPS milliseconds with first time subtracted\n',
        sv_posvel_traj_sv25['gps_millis'] - start_gps_millis)

print('Changing x ECEF SV positions\n',
        sv_posvel_traj_sv25['x_sv_m'] - sv_posvel_traj_sv25['x_sv_m', 0])

print('Consecutive change in x ECEF positions\n',
        sv_posvel_traj_sv25['x_sv_m', 1:] - sv_posvel_traj_sv25['x_sv_m', :-1])

print('Velocity along x ECEF for reference\n',
        sv_posvel_traj_sv25['vx_sv_mps'])
GPS milliseconds with first time subtracted
 [   0.    0.    0.    0.    0.    0.    0.    0.    0.    0.    0.    0.
    0.    0.    0.    0.    0.    0.    0.    0.    0.    0.    0.    0.
    0.    0. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000.
 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000. 1000.
 1000. 1000. 1000. 1000. 2000. 2000. 2000. 2000. 2000. 2000. 2000. 2000.
 2000. 2000. 2000. 2000. 2000. 2000. 2000. 2000. 2000. 2000. 2000. 2000.
 2000. 2000. 2000. 2000. 2000. 2000. 3000. 3000. 3000. 3000. 3000. 3000.
 3000. 3000. 3000. 3000. 3000. 3000. 3000. 3000. 3000. 3000. 3000. 3000.
 3000. 3000. 3000. 3000. 3000. 3000. 3000. 3000. 4000. 4000. 4000. 4000.
 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000.
 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000. 4000. 5000. 5000.
 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000.
 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000. 5000.
 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000.
 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000. 6000.
 6000. 6000. 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000.
 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000. 7000.
 7000. 7000. 7000. 7000. 8000. 8000. 8000. 8000. 8000. 8000. 8000. 8000.
 8000. 8000. 8000. 8000. 8000. 8000. 8000. 8000. 8000. 8000. 8000. 8000.
 8000. 8000. 8000. 8000. 8000. 8000. 9000. 9000. 9000. 9000. 9000. 9000.
 9000. 9000. 9000. 9000. 9000. 9000. 9000. 9000. 9000. 9000. 9000. 9000.
 9000. 9000. 9000. 9000. 9000. 9000. 9000. 9000.]
Changing x ECEF SV positions
 [   0.          234.76492867   73.69055995  -80.93005801 -255.17069472
 -302.47619301 -247.84016572 -116.79996231 -162.23311343  -92.87441976
  -93.02605316 -156.24739693 -154.42035792 -154.47798861 -139.23850923
 -178.65525596  -71.42746828  -37.86605901  -22.3085491   -76.346817
 -170.94506686 -321.46501522 -318.81113322 -319.21598593 -194.55346849
 -126.7235538   -23.79384851  211.03931444   49.96210593 -104.66431538
 -278.95154236 -326.2377893  -271.59714826 -140.52758908 -185.96853623
 -116.60717141 -116.75885808 -179.99420463 -178.16552979 -178.22313488
 -162.99104159 -202.40428212  -95.1705611   -61.59138068  -46.04755535
 -100.0901823  -194.7276346  -345.22105044 -342.56297194 -342.96777856
 -218.26524085 -150.46149226  -47.74405371  187.15734771   26.07729773
 -128.55492007 -302.88873539 -350.15572673 -295.51046878 -164.41155912
 -209.86030521 -140.49626921 -140.64800914 -203.89735928 -202.06704766
 -202.12462709 -186.89991756 -226.3096522  -119.07000032  -85.47305175
  -69.94291275 -123.98989595 -218.66654732 -369.13342643 -366.47115201
 -366.87591252 -242.1333579  -174.3557801   -71.85059263  163.11903977
    2.03614662 -152.60186075 -326.98226254 -374.229994   -319.5801218
 -188.4518612  -233.90840908 -164.54170187 -164.69349505 -227.95684964
 -226.12490028 -226.18245397 -210.96512589 -250.37135489 -143.12578055
 -109.51106679  -93.99461005 -148.04594664 -242.76179374 -393.20213189
 -390.53566216 -390.94037654 -266.15780837 -198.406406    -96.11345981
  138.92440195  -22.1613361  -176.80512614 -351.23211252 -398.46058575
 -343.806096   -212.64848397 -258.11283656 -188.74345808 -188.89530454
 -252.17266441 -250.33907631 -250.39660421 -235.18665526 -274.58937891
 -167.33787873 -133.70540277 -118.20263593 -172.25832312 -267.01336255
 -417.42715553 -414.75649109 -415.16115933 -290.33858091 -222.61335871
 -120.53264392  114.57344553  -46.51514505 -201.16470493 -375.63827996
 -422.84747886 -368.18838006 -237.00141618 -282.47358231 -213.10152653
 -213.25342625 -276.54479226 -274.70956447 -274.7670665  -259.56450032
 -298.96371295 -191.70628942 -158.05605422 -142.56697907 -196.62701404
 -291.42124248 -441.80848604 -439.13362747 -439.53824954 -314.67566421
 -246.97662689 -145.10813362   90.06618185  -71.02525707 -225.68059179
 -400.20074164 -447.39066786 -392.72696269 -261.51064644 -306.99062308
 -237.6158959  -237.76784891 -301.07322186 -299.23635941 -299.29383548
 -284.09863786 -323.49434566 -216.23100127 -182.56300982 -167.08762817
 -221.15200806 -315.98542214 -466.34611208 -463.66706001 -464.07163587
 -339.169047   -271.49619921 -169.8399176    65.40261623  -95.69166671
 -250.35276345 -424.91949217 -472.09014141 -417.42183254 -286.17616342
 -331.66395346 -262.28655486 -262.43856113 -325.75794793 -323.91943784
 -323.97688791 -308.78906247 -348.18126571 -240.91200293 -207.22625822
 -191.7645779  -245.83329389 -340.70589024 -491.04002233 -488.35677734
 -488.761307   -363.81871788 -296.17206435 -194.7279845    40.58277203
 -120.51436264 -275.18121448 -449.79452019 -496.94588819 -442.27297825
 -310.99795582 -356.49356214 -287.11349206 -287.2655516  -350.59894707
 -348.75879438 -348.81621837 -333.63576281 -373.02446785 -265.74928312
 -232.0457881  -216.5978049  -270.67086016 -365.58264148 -515.89020542
 -513.20276813 -513.60725155 -388.6246716  -321.00421096 -219.77232295
   15.6066546  -145.4933335  -300.16593354 -474.82581434 -521.95789684
 -467.28038847 -335.97601833 -381.47943775 -312.09669613 -312.24880896
 -375.59621395 -373.75441766 -373.81181551 -358.63872749 -398.0239286
 -290.74283042 -257.0215881  -241.5873038  -295.6647016  -390.61565239
 -540.89665612 -538.20502712 -538.60946428 -413.58688471 -345.99263381]
Consecutive change in x ECEF positions
 [ 2.34764929e+02 -1.61074369e+02 -1.54620618e+02 -1.74240637e+02
 -4.73054983e+01  5.46360273e+01  1.31040203e+02 -4.54331511e+01
  6.93586937e+01 -1.51633400e-01 -6.32213438e+01  1.82703902e+00
 -5.76306973e-02  1.52394794e+01 -3.94167467e+01  1.07227788e+02
  3.35614093e+01  1.55575099e+01 -5.40382679e+01 -9.45982499e+01
 -1.50519948e+02  2.65388201e+00 -4.04852711e-01  1.24662517e+02
  6.78299147e+01  1.02929705e+02  2.34833163e+02 -1.61077209e+02
 -1.54626421e+02 -1.74287227e+02 -4.72862469e+01  5.46406410e+01
  1.31069559e+02 -4.54409471e+01  6.93613648e+01 -1.51686674e-01
 -6.32353465e+01  1.82867483e+00 -5.76050878e-02  1.52320933e+01
 -3.94132405e+01  1.07233721e+02  3.35791804e+01  1.55438253e+01
 -5.40426270e+01 -9.46374523e+01 -1.50493416e+02  2.65807850e+00
 -4.04806618e-01  1.24702538e+02  6.78037486e+01  1.02717439e+02
  2.34901401e+02 -1.61080050e+02 -1.54632218e+02 -1.74333815e+02
 -4.72669913e+01  5.46452579e+01  1.31098910e+02 -4.54487461e+01
  6.93640360e+01 -1.51739936e-01 -6.32493501e+01  1.83031162e+00
 -5.75794335e-02  1.52247095e+01 -3.94097346e+01  1.07239652e+02
  3.35969486e+01  1.55301390e+01 -5.40469832e+01 -9.46766514e+01
 -1.50466879e+02  2.66227442e+00 -4.04760517e-01  1.24742555e+02
  6.77775778e+01  1.02505187e+02  2.34969632e+02 -1.61082893e+02
 -1.54638007e+02 -1.74380402e+02 -4.72477315e+01  5.46498722e+01
  1.31128261e+02 -4.54565479e+01  6.93667072e+01 -1.51793182e-01
 -6.32633546e+01  1.83194936e+00 -5.75536918e-02  1.52173281e+01
 -3.94062290e+01  1.07245574e+02  3.36147138e+01  1.55164567e+01
 -5.40513366e+01 -9.47158471e+01 -1.50440338e+02  2.66646973e+00
 -4.04714385e-01  1.24782568e+02  6.77514024e+01  1.02292946e+02
  2.35037862e+02 -1.61085738e+02 -1.54643790e+02 -1.74426986e+02
 -4.72284732e+01  5.46544898e+01  1.31157612e+02 -4.54643526e+01
  6.93693785e+01 -1.51846461e-01 -6.32773599e+01  1.83358810e+00
 -5.75279016e-02  1.52099490e+01 -3.94027237e+01  1.07251500e+02
  3.36324760e+01  1.55027668e+01 -5.40556872e+01 -9.47550394e+01
 -1.50413793e+02  2.67066444e+00 -4.04668240e-01  1.24822578e+02
  6.77252222e+01  1.02080715e+02  2.35106089e+02 -1.61088591e+02
 -1.54649560e+02 -1.74473575e+02 -4.72091989e+01  5.46590988e+01
  1.31186964e+02 -4.54721661e+01  6.93720558e+01 -1.51899712e-01
 -6.32913660e+01  1.83522778e+00 -5.75020239e-02  1.52025662e+01
 -3.93992126e+01  1.07257424e+02  3.36502352e+01  1.54890751e+01
 -5.40600350e+01 -9.47942284e+01 -1.50387244e+02  2.67485858e+00
 -4.04622074e-01  1.24862585e+02  6.76990373e+01  1.01868493e+02
  2.35174315e+02 -1.61091439e+02 -1.54655335e+02 -1.74520150e+02
 -4.71899262e+01  5.46637052e+01  1.31216316e+02 -4.54799766e+01
  6.93747272e+01 -1.51953001e-01 -6.33053730e+01  1.83686245e+00
 -5.74760772e-02  1.51951976e+01 -3.93957078e+01  1.07263344e+02
  3.36679914e+01  1.54753817e+01 -5.40643799e+01 -9.48334141e+01
 -1.50360690e+02  2.67905207e+00 -4.04575864e-01  1.24902589e+02
  6.76728478e+01  1.01656282e+02  2.35242534e+02 -1.61094283e+02
 -1.54661097e+02 -1.74566729e+02 -4.71706492e+01  5.46683089e+01
  1.31245669e+02 -4.54877900e+01  6.93773986e+01 -1.52006276e-01
 -6.33193868e+01  1.83851008e+00 -5.74500673e-02  1.51878254e+01
 -3.93922032e+01  1.07269263e+02  3.36857447e+01  1.54616803e+01
 -5.40687160e+01 -9.48725963e+01 -1.50334132e+02  2.68324499e+00
 -4.04529659e-01  1.24942589e+02  6.76466535e+01  1.01444080e+02
  2.35310757e+02 -1.61097135e+02 -1.54666852e+02 -1.74613306e+02
 -4.71513680e+01  5.46729099e+01  1.31275022e+02 -4.54956063e+01
  6.93800701e+01 -1.52059536e-01 -6.33333955e+01  1.84015269e+00
 -5.74239939e-02  1.51804556e+01 -3.93887050e+01  1.07275185e+02
  3.37034950e+01  1.54479832e+01 -5.40730553e+01 -9.49117813e+01
 -1.50307564e+02  2.68743729e+00 -4.04483413e-01  1.24982580e+02
  6.76204606e+01  1.01231888e+02  2.35378978e+02 -1.61099988e+02
 -1.54672600e+02 -1.74659881e+02 -4.71320825e+01  5.46775084e+01
  1.31304370e+02 -4.55034194e+01  6.93827416e+01 -1.52112830e-01
 -6.33474050e+01  1.84179628e+00 -5.73978517e-02  1.51730880e+01
 -3.93852011e+01  1.07281098e+02  3.37212423e+01  1.54342843e+01
 -5.40773978e+01 -9.49509508e+01 -1.50281004e+02  2.69162900e+00
 -4.04437164e-01  1.25022580e+02  6.75942509e+01]
Velocity along x ECEF for reference
 [-23.71564038 -23.64740536 -23.65024427 -23.65605106 -23.70264222
 -23.68339305 -23.67877215 -23.64942246 -23.65721701 -23.65454591
 -23.65459916 -23.6686015  -23.6669662  -23.66694063 -23.67432785
 -23.6708215  -23.66488692 -23.64711434 -23.66079801 -23.66515847
 -23.70436253 -23.67783213 -23.67363532 -23.67358924 -23.6335674
 -23.65973114 -23.87199666 -23.8037633  -23.8066039  -23.81240381
 -23.85899307 -23.83973964 -23.83512142 -23.80577121 -23.81356864
 -23.8108975  -23.81095076 -23.82495394 -23.82331766 -23.82329203
 -23.83067693 -23.82717086 -23.82123876 -23.80346916 -23.81715463
 -23.82151227 -23.86071296 -23.83417834 -23.82998213 -23.82993603
 -23.78991753 -23.81608596 -24.02834171 -23.96010995 -23.96295226
 -23.96874528 -24.01533265 -23.99607494 -23.99145936 -23.96210868
 -23.969909   -23.96723781 -23.96729107 -23.98129509 -23.97965785
 -23.97963215 -23.98701473 -23.98350894 -23.97757933 -23.95981271
 -23.97349997 -23.97785479 -24.01705212 -23.99051327 -23.98631766
 -23.98627154 -23.94625637 -23.9724295  -24.18467541 -24.11644531
 -24.11928932 -24.12507545 -24.17166092 -24.15239895 -24.14778601
 -24.11843485 -24.12623806 -24.12356683 -24.12362009 -24.13762495
 -24.13598674 -24.13596097 -24.14334123 -24.13983572 -24.13390864
 -24.11614499 -24.12983401 -24.13418601 -24.17337998 -24.14683691
 -24.1426419  -24.14259576 -24.10258392 -24.12876174 -24.34099781
 -24.27276936 -24.27561508 -24.28139431 -24.3279779  -24.30871168
 -24.30410136 -24.27474972 -24.28255582 -24.27988454 -24.27993781
 -24.29394351 -24.29230432 -24.29227848 -24.29965643 -24.2961512
 -24.29022661 -24.27246594 -24.28615674 -24.29050593 -24.32969653
 -24.30314924 -24.29895483 -24.29890867 -24.25890017 -24.28508268
 -24.49730889 -24.4290821  -24.43192956 -24.43770186 -24.48428358
 -24.46501306 -24.46040538 -24.43105326 -24.43886229 -24.43619093
 -24.4362442  -24.45025074 -24.44861058 -24.44858468 -24.45596035
 -24.45245536 -24.44653325 -24.42877556 -24.44246816 -24.44681452
 -24.48600176 -24.45945025 -24.45525644 -24.45521026 -24.4152051
 -24.44139231 -24.65360864 -24.5853835  -24.58823267 -24.59399812
 -24.64057791 -24.62130311 -24.61669807 -24.58734548 -24.5951574
 -24.592486   -24.59253927 -24.60654665 -24.60490556 -24.60487958
 -24.61225289 -24.60874819 -24.60282857 -24.58507386 -24.59876825
 -24.60311179 -24.64229567 -24.61573993 -24.61154673 -24.61150052
 -24.5714987  -24.5976906  -24.80989704 -24.7416736  -24.74452445
 -24.750283   -24.79686088 -24.77758181 -24.77297942 -24.74362635
 -24.75144117 -24.74876972 -24.74882299 -24.76283125 -24.76118914
 -24.7611631  -24.7685341  -24.76502967 -24.75911254 -24.74136081
 -24.75505704 -24.75939771 -24.79857823 -24.77201827 -24.76782567
 -24.76777944 -24.72778095 -24.75397755 -24.96617409 -24.8979523
 -24.90080487 -24.90655652 -24.9531325  -24.93384916 -24.92924942
 -24.89989587 -24.90771358 -24.90504208 -24.90509536 -24.91910445
 -24.91746138 -24.91743527 -24.92480395 -24.92129984 -24.91538516
 -24.8976364  -24.91133443 -24.91567228 -24.95484947 -24.92828525
 -24.92409325 -24.92404701 -24.88405189 -24.91025315 -25.12243976
 -25.05421964 -25.05707392 -25.06281867 -25.10939275 -25.09010514
 -25.08550804 -25.05615406 -25.06397463 -25.06130308 -25.06135637
 -25.07536629 -25.07372224 -25.07369607 -25.08106243 -25.0775586
 -25.07164641 -25.05390063 -25.06760045 -25.07193551 -25.11110931
 -25.0845409  -25.08034951 -25.08030324 -25.04031143 -25.06651742]

Finding PRNs and states for visible SVs for a given position and time

Consider the problem of states of SVs that would be visible from Durand building (considering open sky with an elevation mask of 5°) on the Stanford campus on 30th April, 2021.

In this section, we show how to estimate the visible satellites and then compute their states

[12]:
# Uses start_time, start_gps_millis, rx_ecef_durand, and ephem from previous
# section



# Use input time, Rx position and all broadcast ephemeris parameters to
# find ephemeris parameters for visible satellites

ephem_viz = glp.find_visible_ephem(start_gps_millis, rx_ecef_durand, ephem_all_sats)

print("SV IDs for visible satellites are ", ephem_viz['sv_id'])

SV IDs for visible satellites are  [ 2  5  6 12 19 24 25 29]

Finding SV states at given time and for specific PRNs

Using the ephemeris parameters, we can find SV states for those specific PRNs.

gnss_lib_py offers two options to find SV states: 1. Estimating SV states at precisely the given time 2. Estimating SV states for the given reception time. This subtracts the time of travel for the signal (based on the receiver position) and computes the SV states at that approximate transmission time. This method requires an estimate of the receiver’s position.

The time taken for signals to reach the Earth from satellites is roughly 70 ms and the difference between SV positions is roughly 200 m.

In this section, we show both methods of estimating SV states.

[13]:
# Using find_sv_states and a filtered ephem
# Option 1: Estimate SV states for the given transmission time (does not
# account for any signal travel time)
sv_states_tx = glp.find_sv_states(start_gps_millis, ephem_viz)

# Option 2: Estimate SV states for given reception time (factors and removes
# approximately the time taken by the signal to reach the receiver)
# This method requires an estimate of the receiver's position and also
# gives difference between positions and the estimated true range
sv_states_rx, del_pos, true_range = glp.find_sv_location(start_gps_millis, rx_ecef_durand, ephem_viz)

print('Difference between x positions estimated for Tx and Rx times \n',
      sv_states_tx['x_sv_m'] - sv_states_rx['x_sv_m'])
print('Difference between x velocities estimated for Tx and Rx times\n',
      sv_states_tx['vx_sv_mps'] - sv_states_rx['vx_sv_mps'])

Difference between x positions estimated for Tx and Rx times
 [161.14331937  20.35807583 186.48862199  17.49837726 108.88355493
  82.14614101  -1.67715885 105.61930209]
Difference between x velocities estimated for Tx and Rx times
 [ 0.01316121 -0.00101707  0.00355899 -0.00939447 -0.02923674  0.02636521
 -0.01107761  0.01876936]

Simulating SV positions given elevation and azimuth

When working in a local frame of reference, it can be faster to simulate satellite positions locally, based on elevation and azimuth angles.

In this section, we demonstrate how to achieve this by giving an np.ndarray containing elevation and azimuth angles to get an np.ndarray

[14]:
# Use svs_from_el_az
elevation = np.array([30., 45., 60., 30.])
azimuth = np.linspace(0, 360., 4, endpoint=False)
el_az = np.vstack((elevation, azimuth))
local_svs = glp.svs_from_el_az(el_az)

print('Given elevation angles are ', elevation)
print('Given azimuth angles are ', azimuth)
print('Local coordinates for generated satellites are \n', local_svs)

Given elevation angles are  [30. 45. 60. 30.]
Given azimuth angles are  [  0.  90. 180. 270.]
Local coordinates for generated satellites are
 [[ 0.00000000e+00  1.42835570e+07  1.23689327e-09 -1.74937132e+07]
 [ 1.74937132e+07  8.74615617e-10 -1.01000000e+07 -3.21354297e-09]
 [ 1.01000000e+07  1.42835570e+07  1.74937132e+07  1.01000000e+07]]