Sylphase SDGPS
The software-defined GPS(/GNSS) toolkit
|
NOTE: This page is out-of-date. Please refer to Getting started with the Infix-2
We have made two tutorials for those learning our software. The first one is entirely software based using our simulation package if your hardware has not arrived yet (or if you don't feel like gathering the necessary items from around your lab). The second tutorial requires an Infix-1, but we have provided a raw stream of data so that you don't need to obtain an antenna if your developement conditions don't allow for easy line of sight with GNSS satellites.
Let's talk about using SDGPS.
After running installing the prerequisites (found in README.md), building SDGPS, and running the set up commands you will be ready to use SDGPS.
The most simple command available to you is just:
this will produce the following output (truncated to the first 25 lines of the output for clarity):
There are a few things to note here:
The syntax for this is denoted as follows:
For example:
means that the kf2 node is an observables sink and solution source.
For example
is a valid node chain because the sylphase-usbgpsimu2 node outputs cooked packets, and the tracker node accepts cooked packets.
For example:
will not display nodes that are not cooked sinks.
Put simply:
For more details about the types of interfaces vist the Interfaces page.
Allowed options
.For example:
will produce the same result as:
!
symbol.For example:
produces the following output:
Usage:
kf2 [OPTION]...
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--output-rate arg | output solution packets at this rate (units: Hertz) (default: the highest integer divisor of the IMU sample rate that is <= 100 Hz) |
--offline | don't boost state to realtime. incurs delay of OFFSET seconds |
--print-phase-resets | print phase resets |
--ionospheric-model arg (=zero) | use specified ionospheric delay model (default: assume ionospheric delay to always be zero) |
--wait-for-ionospheric-model | wait for ionospheric model to be initialized to avoid transient behavior |
--tropospheric-model arg (=simple) | use specified tropospheric delay model |
--estimate-ztd-factor | estimate ZTD (zenith tropospheric delay) scale factor |
--estimate-ionospheric-delay | estimate ionospheric delay; with dual-frequency measurements, convergence is immediate, but with single-frequency measurements, convergence takes >= 1 hour |
--estimate-antenna-delays | estimate differential delay between antennas |
--elevation-mask arg | do not use observables from SVs with an elevation below this angle relative to the local horizon (units: degrees) |
--antenna-elevation-mask arg | do not use observables from SVs with an elevation below this angle relative to the plane defined by the antenna's "axis" vector (units: degrees) (multiantenna example: 'ANT0:20,ANT1:0') |
--p-ignore arg (=1e-9) | p-value threshold for ignoring an SV due to a large pseudorange residual; set to 0 to never ignore |
--ignore-duration arg (=1) | if --p-ignore is triggered, ignore all of SV's measurements for this many seconds |
--abort-signals-ignored-for arg | if an SV's measurements are --p-ignored for this many seconds, abort signal tracking via the AbortSignalTracking FunctionalityCallback |
--p-log arg (=1e-6) | p-value threshold for logging a message about a large measurement residual; set to 0 to never log |
--p-reset arg (=0) | p-value threshold for triggering a reset due to large pseudorange residual; set to 0 to never reset |
--p-phase-reset arg (=1e-6) | p-value threshold for resetting carrier phase tracking for a given SV; set to 0 to never reset |
--ignore-doppler | minimize use of Doppler observable |
--ignore-phase | don't use phase observable |
--clock-wander-rate arg (=1e-17) | diffusion constant for local oscillator frequency random walk (units: (s/s)^2/s) |
--phase-wander-rate arg (=0.02) | diffusion constant for unmodeled SV phase random walk (units: radian^2/s) |
--plot-residuals | plot measurement residuals |
--min-sample-period arg (=0) | minimum width between plot points in seconds; plot points closer than this will be averaged |
--x-axis-unit arg (=sec) | unit of X axis (options: sec, min, hour, day) |
--x-axis-absolute | plot absolute stream time on X axis instead of relative to now (implied by --wait) |
--detrend | <no effect> |
--wait | wait for pipeline to terminate, then plot (default: live plotting) |
--live-history arg (=60) | duration of live history buffer in seconds |
--title-suffix arg | append string to plot titles |
--sample-time-stream arg | name of Time stream to include in "time_streams" key of solution output; will also match a unique prefix of a name; special value of "*" will select all |
--include-sisre-in-covariance arg (=0) | add given SISRE contribution to position covariance matrix (units: meters rms) Note: SISRE (rms) = sqrt((--sv-ephemeris-error value)^2 + (--sv-clock-error value)^2) |
<TAB>
, but there are some nuances with files such as cooked2-to-raw and interfaces with the cooked interface as a source.--option-name
before the argument. For example:and
are equally valid and produce the same result.
For example:
produces:
Usage:
connect-observables-tcp [OPTION]... HOST PORT
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--host HOST | make connection to IP address or hostname HOST |
--port PORT | make connection to TCP port number PORT |
Here you can see the HOST
and PORT
options on the first line are not enclosed by []
which signifies that they are positional arguments in that order.
Now that the general format of using SDGPS should be fairly clear, let's jump into some examples.
Example:
Let's look at the outputs of each of these nodes.
The first node is the generate-simple-trajectory node.
Usage:
generate-simple-trajectory [OPTION]...
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--sample-rate SAMPLE_RATE (=100) | sample rate |
--rate RATE (=1) | generate at RATE times real time (inf works) |
--duration DURATION (=inf) | stop after DURATION seconds (inf works) |
--position-ecef arg (=[740883,-5497747,3136908]) | initial platform position as a 3-length JSON array |
--velocity-ecef arg (=[0,0,0]) | platform velocity as a 3-length JSON array |
--acceleration-ecef arg (=[0,0,0]) | platform acceleration as a 3-length JSON array (units: m/s^2) |
--orientation-enu arg (={"w":1,"x":0,"y":0,"z":0}) | initial platform orientation as a JSON object |
--angular-velocity-enu arg (=[0,0,0]) | platform angular velocity as a 3-length JSON array |
--start-time arg (=1298764809.869604401089357952) | GNSS time at start of trajectory |
--circle arg (={"radius":0,"axis_enu":[0,0,1],"period":1}) | circle around the point that would otherwise be output |
--bob arg | add superimposed bobbing motion of axis_body * sin(2*pi*t/period) (example: '{"axis_body":[0,1,0],"period":5}') |
Here we can see that this is node has quite a few options. The options we specified were the:
The next node is the simulate-raw node.
Usage:
simulate-raw [OPTION]... [CONFIG_STR (=repofile:configs/simple)] [ENVIRONMENT_FILENAME]
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--config CONFIG_STR (=repofile:configs/simple) | config string (run "sdgps help" for examples) |
--satellites SATELLITES_FILENAME | read satellite definitions from SATELLITES_FILENAME |
--environment ENVIRONMENT_FILENAME | read environment definitions from ENVIRONMENT_FILENAME; only simulate-raw supports this option currently |
--start-stream-time STREAM_TIME (=10000001234.141592025756835936) | first solution packet will correspond to this STREAM_TIME in output |
--disable-plot | disable plotting of the simulated scenario |
--bit-inner-corruption-probability arg (=0) | corrupt unprotected GNSS NAV bits this often (used for testing decoder robustness) |
--bit-outer-corruption-probability arg (=0) | corrupt ECC-protected GNSS NAV bits this often (used for testing decoder robustness) |
--gps-broadcast-ionospheric-model GPS_IONOSPHERIC_MODEL | JSON with Klobuchar ionospheric model parameters to be broadcast on GPS L1 and L2C signal; This will override the ionospheric model provided via SATELLITES_FILENAME; (e.g. the contents of data/sample_ionospheric_model.iono) |
--broadcast-utc UTC_PARAMETERS_FILENAME | JSON file with UTC parameters to be broadcast on GPS L2C signal (e.g. data/sample_utc_model.utc) |
--broadcast-eop EOP_PROVIDER | EOP provider used to generate EOP parameters to be broadcast on GPS L2C signal (e.g. dynamic:data/sample_eop_model.eop) |
--broadcast-ggto GGTO_PARAMETERS_FILENAME | JSON file with GGTO parameters to be broadcast on GAL_E1B and GAL_E5b signal (e.g. data/sample_ggto_model.ggto) |
--broadcast-gps-l1-ura GPS_L1_URA | URA value to be broadcast on GPS_L1 signal; default value is 2 meters (e.g. to simulate an invalid URA use '{"error":"invalid"}') |
--broadcast-gps-l2c-ura GPS_L2C_URA | URA value to be broadcast on GPS_L2C signal; default value is 2 meters (e.g. to simulate 2 meters use '{"result":2}') |
--broadcast-galileo-sisa GALILEO_SISA | SISA value to be broadcast on GAL_E1B and GAL_E5b signal; default value is 2 meters (uses same format as GPS_L1_URA and GPS_L2C_URA) |
--broadcast-glonass-ura GLONASS_URA | URA value to be broadcast on GLO_L1 and GLO_L2 signal; default value is 2 meters (uses same format as GPS_L1_URA and GPS_L2C_URA) |
--sv-ephemeris-error arg (=0) | 1D rms ephemeris error to simulate; each of the 3 spatial axes will have an independent 0-mean random offset with this standard deviation applied (units: meters) |
--sv-clock-error arg (=0) | rms clock error to simulate; SV clock will have a 0-mean random offset with this standard deviation applied (units: meters) Note: SISRE (rms) = sqrt((--sv-ephemeris-error value)^2 + (--sv-clock-error value)^2) |
--sv-error-relaxation-time RELAXATION_TIME (=3600) | autocorrelation decay time constant of SV ephemeris/clock error random walk (units: seconds) |
--sv-error-seed SEED (=default) | seed for SV ephemeris/clock error random number generator; can be any string |
--ztd-variation arg (=0) | rms relative zenith tropospheric delay wander to simulate; nominal tropospheric delay will be scaled by a random walk with mean 1 and this standard deviation (unitless) |
--ztd-relaxation-time RELAXATION_TIME (=3600) | autocorrelation decay time constant of ZTD wander (units: seconds) |
--cn0 CN0 | fix all signal C/N_0 values to CN0 dB-Hz (default: C/N_0 is calculated based on SV-receiver range) (supports antenna-specific syntax: 'ANT0:45,ANT1:40') |
--physical-ionospheric-model arg (=zero) | ionospheric model that is used for computing simulated observables |
--physical-tropospheric-model arg (=simple) | tropospheric model that is used for computing simulated observables |
--antenna-elevation-mask arg | obscure SVs that have an elevation below this angle (elevation is relative to the horizon defined by the antenna's body-frame "axis" config parameter) (units: degrees) (multiantenna example: 'ANT0:20,ANT1:0') |
--gnss-stream-group-delay arg | simulate a per-GNSS-stream group delay to model e.g. cable delay or differential antenna SAW filter delay (example: "[1e-9, 2e-9]" to delay stream 0 by 1 ns and stream 1 by 2 ns) |
--gain-pattern PROVIDER_NAME[:PROVIDER_ARGS...] (=isotropic) | gain pattern provider name and arguments (e.g. "isotropic" or "crossed-dipole:'--radius=0.04527') |
--sv-whitelist arg | only simulate these SVIDs (e.g. "1,2,3") |
--threads-per-gnss-stream THREADS (=4) | distribute GNSS stream generation over THREADS threads |
--gnss-interpolation-rate FREQUENCY (=100) | linearly interpolate SV signals over intervals of 1/FREQUENCY seconds; increased rate adds computational overhead |
--spur arg | generate spur at this baseband frequency |
--spur-cn0 arg (=60) | C/N_0 of spurs added by --spur |
--outage-start arg | start GNSS outage (all SV signals disappear) at this GNSS time |
--outage-end arg | end GNSS outage (all SV signals reappear) at this GNSS time |
--outage-mask arg | limit outage to signals which have "STREAM/SUBSTREAM/GNSS/SVID" match this regex (example: '.*/.*/GPS_L2C/.*' will match all GPS_L2C SVs on all (sub)streams |
--noise-seed arg (=default) | seed for noise floor generation |
--antialias | (EXPERIMENTAL, SLOW) antialias signal by bandlimiting it to the band defined by the config's "spectrum_info" value |
--oversampling-ratio arg (=3) | oversampling ratio to reduce artifacts from linear interpolation; = (sampling rate) / (Nyquist rate); only has an effect if --antialias is used |
--null arg | generate null at this baseband frequency (units: Hertz); only has an effect if --antialias is used |
--null-width arg (=1e5) | width of nulls added by --null (units: Hertz) |
--filter-rolloff arg | bandpass filter rolloff (units: dB/MHz) (example: 10 for -10 dB/MHz); only has an effect if --antialias is used |
This node, as its name would suggest: simulates raw packets (which consists of unprocessed sensor data) and is a raw source. It also has a required config parameter. There are a few config files in the sdgps/configs
directory. They each defines a number of parameters about our sensors (such as covariance for our IMU) and the type of GNSS signals we expect to receive (GPS_L1, GLO_L2, etc). We have several predefined configs with different parameters. For now, we are just going to use the simple
config.
The next node is the sw-correlator node.
Usage:
sw-correlator [OPTION]...
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--deterministic | run synchronously so that everything is deterministic |
--correlators CORRELATORS | number of correlator taps (default: minimum required by tracker node) |
--correlator-spacing CORRELATOR_SPACING | spacing of correlators (units: samples) (default: value preferred by tracker node) |
--correlator-config CONFIG_STR | get per-substream --correlators and --correlator-spacing values from config string (run "sdgps help" for examples) |
--validate | test correlator against reference implementation |
--benchmark | benchmark correlator |
--algorithm ALGORITHM (=fast) | use ALGORITHM algorithm (choices: fast, simple) |
--acq-effort ACQ_EFFORT (=1) | scale acquisition effort. only has an effect if --deterministic is used |
--output-samples | output GNSSSamplePackets into cooked stream |
--max-svs COUNT (=100) | don't try to track more than this many SVs per GNSS system |
Since we want to make this tutorial accessible to those who do not have hardware we used the sw-correlator rather than the hardware correlator we have implemeneted on our Infix-1. We added the --deterministic
to guarantee that our results will be the same as your during this tutorial and run it in a single thread.
You can turn this option off you desire.
The next node is the tracker node.
Usage:
tracker [OPTION]...
Description:
The tracker
node contains the GNSS signal tracking loops.
A given signal tracking loop generates code phase and Doppler error signals that get fed back to the correlator, which keeps it centered on the peak of the autocorrelation function of the tracked signal.
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--verbose-align | output extra debugging information for bit alignment |
--plot | plot live correlation waveforms |
--plot-all | plot live correlation waveforms (including from SVs which are not yet aligned) |
--plot-first | only plot first correlation waveform (useful for debugging acquisition) |
--min-sample-period arg (=0) | minimum width between plot points in seconds; plot points closer than this will be averaged |
--x-axis-unit arg (=sec) | unit of X axis (options: sec, min, hour, day) |
--x-axis-absolute | plot absolute stream time on X axis instead of relative to now (implied by --wait) |
--detrend | <no effect> |
--wait | wait for pipeline to terminate, then plot (default: live plotting) |
--live-history arg (=60) | duration of live history buffer in seconds |
--title-suffix arg | append string to plot titles |
--disable-side-peak-mitigation | disable automatic detection and correction of being locked onto side-peak of BOC correlation function |
--cn0-drop-threshold arg (=20) | drop signals with C/N_0 below this value (units: dB-Hz) |
--cn0-drop-grace-period arg (=3) | inhibit dropping signals with low C/N_0 for this long after start of tracking (units: seconds) |
It is used to run the signal tracking loops and synchronize to the navbits.
The next node is the decoder node.
Usage:
decoder [OPTION]...
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--verbose | output extra debugging information from GNSS bit decoders |
--read-knowledge FILENAME | read knowledge from FILENAME at start |
--write-knowledge FILENAME | write knowledge to FILENAME periodically |
--max-unconfirmed-time TIME (=300) | require new ephemerides to be confirmed (by being received twice and matching) after TIME seconds after startup (valid values: any positive number or inf) |
--grandfather-time TIME (=300) | allow SVs that had unconfirmed ephemerides before --max-unconfirmed-time to be treated as confirmed for TIME seconds afterwards to avoid discontinuity at --max-unconfirmed-time |
--confirmed-ephemeris-timeout TIMEOUT (=300) | abort tracking of a signal if no confirmed ephemeris has been received for TIMEOUT seconds (if AbortSignalTracking FunctionalityCallback is not available, observables will be dropped) (set to "inf" to disable) |
--global-info-period arg (=5) | duration between GNSSGlobalInfo packets (units: seconds) |
It decodes the satellites' ephemerides.
The next node is the kf2 node. This is the main node used for producing solutions from observables streams using a Kalman filter.
Usage:
kf2 [OPTION]...
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--output-rate arg | output solution packets at this rate (units: Hertz) (default: the highest integer divisor of the IMU sample rate that is <= 100 Hz) |
--offline | don't boost state to realtime. incurs delay of OFFSET seconds |
--print-phase-resets | print phase resets |
--ionospheric-model arg (=zero) | use specified ionospheric delay model (default: assume ionospheric delay to always be zero) |
--wait-for-ionospheric-model | wait for ionospheric model to be initialized to avoid transient behavior |
--tropospheric-model arg (=simple) | use specified tropospheric delay model |
--estimate-ztd-factor | estimate ZTD (zenith tropospheric delay) scale factor |
--estimate-ionospheric-delay | estimate ionospheric delay; with dual-frequency measurements, convergence is immediate, but with single-frequency measurements, convergence takes >= 1 hour |
--estimate-antenna-delays | estimate differential delay between antennas |
--elevation-mask arg | do not use observables from SVs with an elevation below this angle relative to the local horizon (units: degrees) |
--antenna-elevation-mask arg | do not use observables from SVs with an elevation below this angle relative to the plane defined by the antenna's "axis" vector (units: degrees) (multiantenna example: 'ANT0:20,ANT1:0') |
--p-ignore arg (=1e-9) | p-value threshold for ignoring an SV due to a large pseudorange residual; set to 0 to never ignore |
--ignore-duration arg (=1) | if --p-ignore is triggered, ignore all of SV's measurements for this many seconds |
--abort-signals-ignored-for arg | if an SV's measurements are --p-ignored for this many seconds, abort signal tracking via the AbortSignalTracking FunctionalityCallback |
--p-log arg (=1e-6) | p-value threshold for logging a message about a large measurement residual; set to 0 to never log |
--p-reset arg (=0) | p-value threshold for triggering a reset due to large pseudorange residual; set to 0 to never reset |
--p-phase-reset arg (=1e-6) | p-value threshold for resetting carrier phase tracking for a given SV; set to 0 to never reset |
--ignore-doppler | minimize use of Doppler observable |
--ignore-phase | don't use phase observable |
--clock-wander-rate arg (=1e-17) | diffusion constant for local oscillator frequency random walk (units: (s/s)^2/s) |
--phase-wander-rate arg (=0.02) | diffusion constant for unmodeled SV phase random walk (units: radian^2/s) |
--plot-residuals | plot measurement residuals |
--min-sample-period arg (=0) | minimum width between plot points in seconds; plot points closer than this will be averaged |
--x-axis-unit arg (=sec) | unit of X axis (options: sec, min, hour, day) |
--x-axis-absolute | plot absolute stream time on X axis instead of relative to now (implied by --wait) |
--detrend | <no effect> |
--wait | wait for pipeline to terminate, then plot (default: live plotting) |
--live-history arg (=60) | duration of live history buffer in seconds |
--title-suffix arg | append string to plot titles |
--sample-time-stream arg | name of Time stream to include in "time_streams" key of solution output; will also match a unique prefix of a name; special value of "*" will select all |
--include-sisre-in-covariance arg (=0) | add given SISRE contribution to position covariance matrix (units: meters rms) Note: SISRE (rms) = sqrt((--sv-ephemeris-error value)^2 + (--sv-clock-error value)^2) |
The last node is the write-solution-file node, its help page looks like this:
Usage:
write-solution-file [OPTION]... FILENAME
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--block | force stream source to wait to avoid buffer overrun (default: immediately abort pipeline unless --continue-on-error is used) |
--buffer-size BUFFER_SIZE (=128) | buffer BUFFER_SIZE MB of data in RAM for smoothing out disk IO latency |
--filename FILENAME | write to file named FILENAME |
--no-metadata | don't include metadata (sdgps version, command line) in output |
--stream STREAM | continually write to file with at least the last STREAM megabytes saved; maximum disk space used is approximately 3 * STREAM megabytes |
--allow-overwrite | don't exit if file already exists |
--continue-on-error | on error condition (e.g. overrun, disk full), stop writing to file, print warning every 5 seconds thereafter, and exit with nonzero exit code on pipeline termination (default: immediately abort pipeline) |
--async-startup | allow pipeline startup to proceed if open() call or write() of stream config header blocks (e.g. if writing to a pipe that does not yet have a reader) |
It's used to write files for later consumption, visit its node page for more examples on how to use its brother nodes.
With that we've used SDGPS to generate a solution file. Now lets look at some of our utility scripts to analyze it.
Let's run this:
sdgps read-solution-file foo.sol --rate inf ! plot-solution --live-history 1e9
which will generate the following image:
Here this will generate three plots showing the position over along each ECEF axis. Since we set the velocity to [1,0,0] we can see that the platform is translating at a fixed rate over time.
Just like that! We generated a trajectory, simulated an observables stream, processed it into a solution, saved that file, and then read that file to look at our results!
The simulation tools are powerful and can be used to perform a variety of tests. Visit the rest of the simulation nodes to see examples on how to use the remaining simulation nodes.
For this part you will need:
Optional:
If you use the raw stream, anywhere you see:
in the tutorial should be replaced with:
if you only have an Infix-1 or
(if you have zero hardware available to you)
as you go through the tutorial.
Remember, an Infix-1 and antenna can be substituted with the provided raw file for the purpose of this tutorial, if either item has not arrived yet, you can still start learning the ropes for SDGPS!
One thing to remember is that kf2 requires motion to initailize properly, reference synthesize-sensor-measurements if you don't feel like rotating your Infix-1 during the recording.
or if you are using the raw file we provided, add synthesize-sensor-measurements
to the node chain like so:
Here we have only four nodes running this time.
The first is: sylphase-usbgpsimu2:
Usage:
sylphase-usbgpsimu2 [OPTION]...
Allowed options:
Option | Description |
---|---|
--help | produce help message |
--device-id arg | use device with this device ID (default: any ID) |
--antenna-position arg (=[0,0,0]) | antenna position as a 3-length JSON array (example: [0.5,2,3]) |
--antenna-axis arg (=[0,0,1]) | antenna axis as a 3-length JSON array (example: [0,0,1]) |
--use-ant1 | use antenna connected to ANT1 instead of ANT0 |
--use-r0 | use receiver0 instead of receiver1 |
--list-devices | list devices present then exit |
--require-device-immediately | require device to be captured immediately or exit |
--use-pll FREQUENCY | lock internal VCTCXO to timing signal connected to GPIO port using PLL; FREQUENCY should be the frequency in Hz of the timing signal |
--gpio-stamp-falling | stamp falling edges of GPIO input (default: stamp rising edges) |
--gpio-stamp-decimation N (=1) | only stamp every Nth rising edge (i.e. 0th, Nth, 2*Nth, ...) |
--output-samples | output GNSSSamplePackets into cooked stream |
--max-svs arg (=15) | limit number of satellites being tracked e.g. to decrease CPU usage |
There are a number of parameters we could be using, but we'll keep it simple and only specify the antenna position relative to our Infix-1. In addition, we've chained a trim-stream node that stops the pipeline after a certain number of seconds have elapsed.
The next nodes are the tracker, decoder, kf2, and write-solution-file nodes which were already explained in the simulation portion of the tutorial.
With that we have a file captured from actual satellites!
Let's run it and see what ours looks like! Your plot may look a little different depending on what you do during the recording, our is generated using an static Infix-1 that was run for 120 seconds
sdgps read-solution-file foo2.sol --rate inf ! plot-solution --live-history 1e9