Glonass Ambiguity Resolution with RTKLIB Revisited

To get a high precision fixed solution in RTKLIB we need to resolve the integer ambiguities that come from the carrier phase measurements.  Resolving the integer ambiguities for the GLONASS satellites is more challenging than resolving them for the other constellations.  This is because, unlike the other constellations, the GLONASS satellites all transmit on slightly different frequencies.  This introduces an additional bias error in the receiver hardware.

These hardware biases are constant, generally the same for all receivers from the same manufacturer, are proportional to carrier frequency and are similar between L1 and L2.

In the demo5 version of RTKLIB, there are four choices for how to handle GLONASS ambiguity resolution (AR). I will cover all four briefly, but then focus on the “autocal” setting which I have enhanced in the most recent version (b29c) of the demo5 code.

Off:  If Glonass  AR is set to “Off”, then the raw measurements from the Glonass satellites will be used for the float solution but ambiguity resolution will be done only with satellites from the other constellations.  If you are not using the demo5 version of RTKLIB, this is usually your only choice when using receivers from different manufacturers for the rover and the base.  However, you are giving up a significant amount of information by ignoring the GLONASS ambiguities and so I would not recommend this setting if you are using the demo5 code, unless of course your receivers don’t support the Glonass satellites.

On:  If Glonass AR is set to “On” then RTKLIB will treat the Glonass ambiguities the same as the ambiguities from the other constellations and will not make any attempt to account for the additional hardware biases.  Use this setting if your base and rover receivers are from the same manufacturer, since in this case, the biases will cancel and can be ignored.  There are also some cases in which different manufacturers have equal or nearly equal biases as we will see later, in which case you can also use this setting.  This is your best solution for dealing with Glonass ambiguities.  I always try to use matched receivers for base and rover if possible.

Fix-and-Hold: This is an option I have added to the demo5 code for Glonass AR.  It is an extension to the “fix-and-hold” method used for other constellations but instead of using the additional feedback to track the ambiguities, it uses it to null out the hardware biases.  I recommend this setting when using the demo5 code with unmatched receivers.  It takes advantage of the additional information in the Glonass ambiguites most of the time.  However, fix-and-hold is not enabled until after a first fix has been achieved, and so the Glonass ambiguities are not available until then.  This can mean longer time to first fix and less robustness compared to the “On” option, so don’t use this option for matched receivers.

Auto-cal:  This option adds additional states to the kalman filter to estimate the receiver hardware biases as a function of carrier frequency, one state for L1, another for L2.  In previous experiments I had not had any success with it.  Recently, however, I discovered that if I adjusted the filter settings, it can be effective for a zero baseline case, where base and rover are both connected to the same antenna so that almost all other errors are completely cancelled.  With a little more experimentation I also found that for short baselines it can also be effective if the kalman filter state is pre-set to something close to the final value before the solution is started.  It will then usually converge to the correct bias value.  However, there is currently no mechanism in the code to adjust any of these values, so I have not found this mode to be useful in its current implementation.

To make the auto-cal option more flexible, and hopefully more useful, I made a few modifications to it in the b29c code.  I added the capability to pre-set the initial state value and also to adjust the internal filter settings, specifically the initial variance and process noise for this state.  The units for the state, and hence for the initial value are in meters per frequency channel and values generally are within +/-5 cm per channel.  I used some existing config parameters that are currently unused to reduce the amount of code I needed to change.  Unfortunately it means that the names are not as descriptive as they could be.  The new config parameters are:

pos2-arthres2 = relative GLONASS hardware bias in meters per frequency slot
pos2-arthres3 = initial variance of GLONASS hardware bias states
pos-arthres4 = process noise for GLONASS hardware bias states

Bias values have been published for some of the most popular geodetic quality receivers but are generally not available for lower-cost or less popular receivers.  Here is a table of values from a paper published by Lambert Wanninger in 2011 for nine receiver manufacturers.

biases

I was able to verify these results for Trimble, Leica, and Novatel, but I found a much lower value for Septentrio so I suspect the biases may have changed in their newer receivers.

To demonstrate the modified autocal option, I will start with a zero baseline case between a ComNav receiver and a Tersus receiver.  It is easiest to measure the hardware biases in the zero baseline case because most other errors will cancel and the hardware biases will be the dominant error.  In this case, I have significantly reduced the initial variance setting from the original value of 1.0 to 1E-7 and increased the process noise from 1E-6 to 1E-3.

I have run the solution several times with the initial bias value set to different numbers between -.05 and 0.06.  Here are the results for both L1 and L2.

biases1

The convergence occurs just after first fix is achieved.  If a fix is not achieved, then the state will not converge as you can see above for the 0.06 example.   In this case, the initial value was too far from the correct value and prevented getting a fix.  As you can see, all the other cases converged towards a single value around -.022, both for L1 and for L2.

Another way to visualize the error in the initial value is to look at the GLONASS residuals after first fix is achieved.  The plot below shows the GLONASS L1 carrier phase residuals  for different initial values, for 0.03 on the left, -0.05 in the middle, and what I believe is the correct value for this receiver combination of -.022 on the right.

 

acal1

Here are the same plots for the L2 carrier phase residuals.

acal2

Through a slightly tedious process, I am fairly easily able to iterate the residuals down to near zero for different pairs of receivers in my possession.  Note that this gives me the relative difference in biases for each receiver pair, and not absolute values for each receiver, unlike Wanniger’s table which is for absolute biases.

Extending the table to receivers used in nearby CORS stations is a little more challenging because the initial bias value needs to be fairly close to get a first fix and hence a convergence, but still possible if the base station is not too distant.   I found data sets that included CORS data from Leica, Novatel, Trimble, and Septentrio receivers.  Using the above procedure to iterate the residuals down to near zero, I was then able to extend my table and make the values absolute by choosing the unknown offset to make my bias pairs align with Wanninger’s table.  This is the resulting table I created.

ComNav    =   2.3 cm
Leica          =   2.3 cm
Novatel      =  2.3 cm
Septentrio = -0.3 cm
SwiftNav   = -0.2 cm
Tersus        = -0.1 cm
Trimble      = -0.7 cm
u-blox         = -3.2 cm 

To generate an initial value for the bias state from this table for an RTKLIB solution, subtract the base station bias from the rover bias, then divide by 100 to convert from centimeters to meters.  This value can then be used to set the “pos2-arthres2” config parameter in the config file.  For the RTKPOST and RTKNAVI GUI option menus I have labeled this “Glo HW Bias”.

To test this code on an independent set of data after generating the table, I used a data set recently sent to me by a reader.  It consists of a u-blox  M8T receiver for rover and Leica receiver just a few kilometers away for base, and was collected in Europe.  The rover position was static but I ran the solution in kinematic mode to make the solution a little more challenging and to make any errors in the solution more visible.

To generate the correct config value for RTKLIB I  subtracted the Leica bias of 2.3 cm from the above table from the u-blox bias of -3.2 cm to get a relative bias between receivers of -5.5 cm or -0.055 m.  I added “pos2-arthres2=-0.055” to the config file and then ran the solution four times, with pos2-gloarmode set to “off”,”fix-and-hold”,”autocal”, and “on”.  Although I left the bias value set for all runs it is ignored unless gloarmode is set to autocal.

Here are the times to first fix, the number of satellite pairs used for the initial fix, and the number of satellite pairs being used for fix after 10 minutes.

  Time to # sat pairs used # sat pairs used for
GLO AR mode first fix for initial fix fix after 10 min
OFF 4:10 7 7
Fix&Hold 4:10 7 11
Autocal 1:05 14 14
On 6:47 14 14

As you would expect, the time to first fix for gloarmode=”off” was the same as “fix-and-hold” since “fix-and-hold” does not use the GLONASS satellites for initial fix.  After 10 minutes it was still only including four of the GLONASS satellites in the ambiguity resolution which was a little unusual, typically I would have expected more GLONASS satellites to be included.

With gloarmode=”autocal”, the time to first fix was reduced from 250 seconds to 65 seconds and the number of satellites included in the first fix increased from 7 to 14., both significant improvements.

The most surprising thing in this data is that when gloarmode was set to “on” it acquired a fix at all.  In many similar cases it will never get a fix.  The GLONASS carrier phase residuals after initial fix were very high though as can be seen below.  The left plot is with gloarmode set to “on”, and the right plot is with it set to “autocal”.

biases3

The ambiguity resolution ratio was also much higher when autocal was enabled as can be seen below (yellow/green=autocal, olive/blue=on) which improves robustness.

biases2

The large residuals did not affect the solution position, as the two solution did not differ by more than 2 mm at any time.  The autocal solution however is much more robust in the sense that it is less likely to lose fix.

Although I have found the results with autocal enabled are generally excellent with relatively short baselines (<10 km), I have found the results less encouraging for longer baselines (>25 km).  In these case I have found that I often get better results with pos-gloarmode set to “fix-and-hold” then I do with “autocal”.  I don’t understand exactly why this is, but suspect that the fix-and-hold correction is more general and may be correcting for more than just the GLONASS hardware biases.

The code changes for this feature are included both in my Github repository and in the newest (demo5 b29c) executables available to download from the rtkexplorer website.   If you choose to experiment with this feature, please let me know if you find any errors in my table, or can add values for any additional receivers.

[Note 6/17/18:  I had a issue with uploading the executables to the website.   If you downloaded them prior to 6/17/18, please download again to get the updated version.] 

Advertisements

Real-time solutions with RTKLIB and NTRIP using a cell phone as data link

As I mentioned in an earlier post, I’ve recently acquired access to some low cost dual frequency receivers, specifically a Tersus Precis BX306 and a pair of Swift Piksi Multis.  I have been playing with them over the past few weeks and plan to share my experiences with them over a series of posts.

Both receivers provide internal RTK solutions as well as raw measurements that can be processed with RTKLIB.  I’m interested in how the RTKLIB solutions compare to the internal solutions as well as how both of these compare to solutions derived from single frequency data collected simultaneously with the dual frequency data.

The first issue I ran into with this experiment, however, is that both receivers will only provide an RTK solution for real-time data, neither have the capability to post-process previously collected data.  This meant that I needed a way to provide a real-time stream of dual frequency base station data to the receivers.  I wanted to be able to  do this while driving a car around the local area so I needed more range than a low cost set of radios would give.

Fortunately, I have fairly good cell phone coverage in this area so I was able to rely on my cell phone for the data link.  In this post I will explain how I did that, both for an external CORS reference station and for my own base station.  In both cases I used  NTRIP server/caster/clients to do this.  NTRIP is a protocol for streaming of DGPS or RTK correction data via the internet using TCP/IP.  The NTRIP server sends out the data to an NTRIP caster and the NTRIP client receives it. For more details, there is a good description here.

Using this setup I was able to run real-time solutions with RTKLIB as well as with the intenal RTK engines in the Swift and Tersus receivers.  Here’s a diagram from the RTKLIB manual showing the setup I used for running a real-time RTKLIB solution using RTKNAVI.  When I ran a Swift or Tersus solution, the configuration was similar, but the NTRIP caster streamed the base station data to STRSVR instead of RTKNAVI, and STRSVR then streamed it to the receiver where it was combined with the raw receiver observations to create an internal RTK solution.  Also missing in this diagram is the cell phone which should be in between the internet and the rover PC.

ntrip.rtklib

The amount of free base station reference data that is available online on a real-time basis is a fair bit more limited that what is available after the fact for post-processing.  Fortunately I was able to find a CORS reference station about 17 km away that is available real-time through the UNAVCO NTRIP caster.  The service is free if the data is used for educational purposes and appropriately attributed.   Most of their stations are on the west coast of the U.S. but they do have some scattered across the rest of the country as you can see in this map from their site.  There are other networks available in other parts of the world that can be found by searching online.

unavco_map

To access the UNAVCO data I had to request access through email but the process was very simple and within a couple hours of my request I was all setup with an account and password.

Once I had my account set up, I used RTKLIB on my laptop computer to collect the data from the internet and stream it to the rover receiver over a serial port.  If I were doing this experiment within range of a wireless router then I could leave the computer connected to the wireless.  In this case though, I wanted to roam outside the range of my home wireless.  To do this, I enabled a hot spot on my cell phone and logged into that with my computer.

I was able to access the raw observation data stream from the UNAVCO NTRIP caster directly using the NTRIP client option in RTKLIB.  If I had wanted to generate a real-time RTKLIB solution, I would have configured the input streams of RTKNAVI but in this case I want to stream the raw data directly to the receiver so it can use the observation data for it’s internal solution.  I did this using the STRSVR app in RTKLIB.  I specifed the “NTRIP Client” option as input type and then entered the information from my UNAVCO account into the “Ntrip Client Options” as shown below.

ntrip_client

In this case I wanted the data from station P041 in RTCM3 format so I had to specify the Mountpoint as “P041_RTCM3”.  For other networks, the mountpoint details may be a little different.  Most NTRIP casters use Port 2101, and that was the case for this one.  For the STRSVR output type, I specified “Serial” and then configured the serial port options for whichever rover receiver I was using.  Before doing the configuration, I had connected the receiver to the laptop using a USB cable.

I then had to configure the receiver to tell it to get its base station data from the COM port and specify that it is in RTCM3 format.  The details for doing this on the two receivers are a little different but fairly straightforward in both cases.  You may also need to specify the exact base station location manually or the receiver may be able to get it from the data stream depending on the receiver and NTRIP stream details.

And that’s it.  With this configuration, either receiver was able to fairly quickly lock to a fixed RTK solution and continue to receive base data as long as I stayed in range of cell reception.  Any lag in the base station observations appeared to be less than a second.

That worked great for using an existing external reference as base station.  However, I also wanted to run another real-time experiment where I used one Swift receiver as base and the other as rover.   To do this, I needed to set up an NTRIP server to stream the data to  a caster on the internet as well as an NTRIP client to receive it.

I started by connecting the second Swift receiver to an old laptop with a USB cable and then downloading RTKLIB, the Swift console app,  and the right USB drivers.  The base station antenna is on top of my roof and the laptop is in the house so I was able to connect the laptop to the internet using my home wireless.

For the NTRIP caster, I found it convenient to use RTK2GO which is a community caster available for anyone to use at no cost.  To send the data to the caster, I used the “NTRIP Server” as the STRSVR output type and configured it as shown below.

strsvr_server

Again, the port is 2101.  You can choose any name for the mountpoint.  If that name is already in use, then rtk2go will assign a suffix to it, so it is best to choose a name that is unlikely to already be in use.  The password at the current time is BETATEST but that may change from time to time so it’s worth verifying it is still correct.

For the STRSVR input, I selected “Serial” and specified the correct COM port for the base station receiver.  In this case the raw observations are in Swift binary format which RTKLIB does not support so it sends them unaltered.  If they were in a format that RTKLIB did support, then they could be converted to RTCM3 to reduce bandwidth and make them more easily usable by someone else not using a Swift receiver as rover.  You can specify the conversion to RTCM3 using the “Conv” menu on the STRSVR output.

Start STRSVR and your base station observations are now accessible to anyone in the world through RTK2GO.com!

On the rover side, the NTRIP client is set up as I previously described using STRSVR except you want to use the same caster/mountpint/password as you just did on the base station.  In this case the user-id is left blank.  Again, set the STRSVR output to “Serial” to send it to the receiver.   Then set up the receiver to get it’s base station data from the serial port and, in this case, specify that it is in the Swift Binary Protocol (sbp).  Start the receiver and it should fairly quickly get a fix.  If you are seeing baseline data but not a solution, then most likely you have not specified the base station location to the rover.

I was now able to drive around almost anywhere and get continuous real-time RTK solutions using either my own base station or the CORS reference station as base.  In the next post I will discuss some of the data I collected and analyzed.

 

 

 

 

Update to RTKLIB config file recommendations

I’ve just updated my “RTKLIB: Customizing the input configuration file” post from a few months ago with information on all of the new config parameters I have added to the demo5 code up through B26B.  I’ve also added more notes to some of the existing features based on my more recent experiences.

RTKLIB on a drone with u-blox M8T receivers

Drones are a popular application for RTKLIB and quite a few readers have shared their drone-collected data sets with me, usually with questions on how they can get better solutions. In many cases, the quality of this data has been fairly poor and it has been difficult to get satisfactory results. I was curious to understand why this environment tends to be so challenging since in theory a drone should have more open skies than just about any other application.

To do an experiment, I bought an inexpensive consumer drone from Amazon. I chose the X8C from Syma since it is beginner model and a little larger than some options. I figured the larger size should make it better able to carry some extra weight.

After a few practice flights to get the hang of flying it, I used some duct tape and double-sided foam adhesive to attach a u-blox antenna and 90 mm diameter ground plane to the top of the drone and a u-blox M8T receiver with my custom CHIP data logger underneath where the camera usually goes. I used the landing gear as a spool to wind the unnecessary five meters of antenna cable which was the heaviest part of the whole setup. From a weight perspective, the Emlid Reach units would have been a better choice, but I wanted to collect data from the Galileo constellation of satellites as well as GPS and GLONASS so I used my CSG receiver with the newer 3.0 firmware. I used a second CSG receiver mounted on top of my car as the base station.  Here’s a stock photo of the drone on the left and after my modifications on the right.

drone1drone2a

Although the drone was able to lift the extra weight fairly easily, it seemed to affect the stability of the flight control system and after a few minutes the prop motors would start to fight each other. At that point the drone would start to descend even at full throttle and the drone would land hard enough to usually bounce on its side or back. Still I was able to make a number of short flights which were adequate for testing purposes.

Here’s the observation data for the first set of flights, base station on the left and drone on the right. Red ticks are cycle-slips and gray ticks are half-cycle ambiguities. Ideally, the drone data would look as clean as the base but as you can see it is significantly worse and it turned out to be unusable for any sort of reliable position solution.  The regions without cycle-slips in the drone observations are the times in between flights in which the drone is sitting on the ground.

drone3

Clearly, while the drone is flying, something is interfering with the GPS receiver or antenna, most likely either EMI or mechanical vibration. I could have used a fancy test stand and RF sniffer to try and locate the source of interference but since this blog is focused on low-cost solutions I just used some duct tape, a steel bar, and the RTKLIB code instead.

I used two types of duct tape, both the polyester/fabric type that everyone calls duct tape, and also the metal foil type that is actually used to repair or install ducts. I first used the non-metal duct tape to securely attach the landing gear to the heavy steel bar. The steel bar was convenient because it was easy to attach but anything heavy enough to prevent the drone lifting off under full throttle would work fine.

I then started an instance of RTKNAVI on my laptop and connected it to the receiver on the drone.  The goal was to simulate a complete drone flight while the drone was sitting on the ground and at the same time watch the RTKNAVI observations to detect any degradation of the measurements.  I used a wireless connection but a USB cable would have worked too.

Unfortunately RTKNAVI won’t plot the observation data real-time, but by selecting the tiny “RTK Monitor” box in the bottom left corner of the main RTKNAVI screen, then choosing “Obs Data” from the menu, I was able to get a continuously updating listing of the observations.  Cycle-slips show up as non-zero values in the first column with the I heading. I chose a location outdoors with open enough skies that any degradation in the observation data would be obvious.

drone4

I first observed the cycle-slip column with the drone powered down to verify I wasn’t getting any cycle-slips on all but the lowest elevation satellites. I then continued to observe the cycle-slip column while sequencing through the steps required to fly the drone. I first powered on the drone, then powered on the transmitter, then issued the calibration/connection sequence, then turned on the throttle to low. So far, so good, no sign of cycle-slips. I then started moving the joysticks to issue steering commands which caused the motors to change speeds. All of a sudden I started getting cycle-slips, the more aggressive the steering commands, the more cycle-slips I saw. Aggressive changes in throttle also caused cycle-slips but full throttle with no adjustments or steering commands was fine.

Next I moved just the antenna, then just the receiver away from the drone while issuing steering commands. Moving the antenna away had no effect but moving the receiver away eliminated the cycle-slips.

At this point my guess was that the interference was coming from the relatively high power switching in the motor control circuits and that the antenna ground plane was shielding the antenna from this interference but nothing was shielding the receiver. To test this theory, I attached a layer of the metal duct tape to the bottom of the drone to act as a shield between the drone controller board and the receiver.  I then re-attached the receiver to the bottom of the drone and re-ran the experiment. This time there were almost no cycle-slips even with the most aggressive steering.

I then removed the steel bar and ran a second set of short flights with the layer of metal tape still in place. I was a little concerned that the new shield would interfere with commands sent from the transmitter to the drone so I first tested everything while still on the ground and then kept the drone fairly close during the flight. Fortunately I didn’t see any sign of commands not getting through.

The drone data looked much cleaner in this flight!  Unfortunately, this time the base data was no good with many simultaneous cycle-slips throughout the observation data. At this point I realized that I had placed the base station receiver directly on the top of the car when collecting the data which was very hot at the time. Usually I keep the receiver in the car to avoid this and only place the antenna on the roof. I have seen this kind of severe temperature effects cause simultaneous cycle-slips before but never to this extent. Again the data was completely unusable.

So, back out there again for a third round of flights. This time, everything looked much better. I still saw a few cycle-slips, especially when first applying the throttle at take-off, so my shielding was not perfect but a dramatic improvement over the first flight. The plots below show the results. The top two plots are position solutions for the z-axis. The top plot is with continuous ambiguity resolution and the middle plot is with fix-and-hold enabled. The bottom plot is the drone observation data.

drone5

I made three adjustments to the input configuration file from what I would normally use for my car based measurements.  First of all, since the drones have very open skies, I adjusted the minimum elevation angles from 15 degrees to 10 degrees.   Then, after plotting and observing the accelerations from an initial solution, I increased the vertical acceleration dynamics estimate (stats-prnaccelv) from 0.25 to 1.0.  Finally, because I was seeing slightly higher position variances in the initial solution than I usually do, I adjusted the position variance AR threshold (pos2-arthres1) from 0.004 to 0.1  Both of these last two changes would make sense if the level of vibration were higher in the drone than I am used to seeing, which is quite likely.

Each time the drone landed/crashed due to the unstable flight control system it would bounce to the side or upside-down and that is what is causing the cycle-slips and switch from fix to float at the end of each flight. As you can see though in every case I quickly get another fix after I put the drone upright again. The fixes are solid enough to hold through the entire flight even in continuous mode for all but one of the flights. With fix-and-hold enabled all flights maintained 100% fix rate. The data is as good as or better than similar experiments where I have mounted the rover on top of a car.

This is not surprising since the skies are more open in this experiment. Having over twenty satellites available for ambiguity resolution also helped. I used all the satellites (GPS/GLONASS/Galileo/SBAS) for ambiguity resolution and took advantage of the new feature in the demo5 b26 code that cycles through all the satellites and will throw a single one out if it is preventing a fix. This will automatically occur anytime the number of satellites available for ambiguity resolution is greater than the config parameter “pos2-mindropsats” which defaults to twenty.

I have added the raw data and the configuration file to the  sample data set section at rtkexplorer.com

I imagine different drones will have different issues and not all will be as easy to fix as this one, but the method described here or something similar should be helpful any time drone data is not looking as clean as the base station data.

The fix I chose was very easy to implement but a better fix would probably have been to wrap just the receiver in a shield rather than placing a shield between the control board and the receiver. This would protect the receiver better and avoid affecting commands sent from the transmitter.  In fact, based on these results, I suspect shielding the GPS receiver on a drone is always a good idea.

Pseudorange corrections for the u-blox TRK-MEAS command

When configuring u-blox GPS receivers to output raw pseudorange and carrier phase data, we normally use the RXM-RAWX command for the M8T receiver. For the M8N receiver, since it does not support the RXM-RAWX command, we normally use the undocumented TRK-MEAS command instead. These are what the “xxx.cmd” scripts in RTKNAVI or STR2STR are doing when starting up the receivers.

Something I did not realize until fairly recently was that the M8T also supports the TRK_MEAS command and furthermore, both commands can be enabled simultaneously.

This is normally not a good thing to do since it confuses the RTKLIB decode apps and will result in a cycle slip reported for every epoch. In fact I first discovered the capability to run both at once accidentally by running the M8N “cmd” script on an M8T after previously running the M8N script. I then spent an embarrassingly long time figuring out why the data was so garbled. If you ever see two outputs in your observation file for every epoch, this is probably the cause.

But, for learning purposes, this is actually quite an interesting configuration because it allows us to directly compare the TRK-MEAS output to the RXM-RAWX output and verify if the two are providing the same information.

To do this experiment I first combined the two command scripts to create one that would enable both raw measurement commands. I then collected some data with an M8T receiver using this script to configure the receiver. Running an unmodified version of RTKCONV on the resulting raw output file allowed me to confirm I was collecting output from both commands. Here’s one epoch from the observation file with output from both commands. I’ve deleted some of the satellites to make it easier to read. The last column is SNR which is reported with a resolution of 0.25 by the TRK-MEAS command and 1.0 by the RXM-RAWX command which makes results from the two commands easy to distinguish.

trkmeas1

Psuedorange and carrier phase are the two 13 digit fields. At first glance, the two command outputs look quite different. This is at least in part because the TRK-MEAS command reports transmission time rather than pseudorange which RTKLIB converts to a sort of pseudo-pseudorange by subtracting an arbitrary constant time off each satellite in the epoch. This error will be removed later as part of the receiver clock offset so it can be ignored. It will be constant for all satellites so we can determine it’s value by removing the common offset between all satellites.

My analysis tools don’t have an easy way to separate the two sets of data from the combined observation file and I also wanted to run solutions on each data set separately so rather than work with the combined file I chose to modify RTKCONV to separate the two sets of data. I compiled two temporary versions of this app, one with the TRK-MEAS decode function disabled and one with the RXM-RAWX decode function disabled. I then ran each on the combined raw data file to create two observation files, one with the TRK-MEAS output and one with the RXM-RAWX output.

Here’s the difference between the pseudorange measurements from the two commands, the top plot is the GPS and SBAS satellites and the bottom plot is the GLONASS satellites. In both cases they are actually differenced with a reference satellite (GPS) to remove the common clock error mentioned above.

trkmeas2

What this plot shows is that once the common error is removed, the pseudorange outputs of TRK_MEAS and RXM-RAWX are identical for the GPS and SBAS satellites but differ by constant integer number of meters for the GLONASS satellites.

Halfway through this exercise I remembered someone had left a comment a few months ago with a link referencing something similar. At the time I hadn’t fully understand the significance of the comment but going back to it I realized they were talking about exactly the same thing. The link was to the OpenStreetMap project and this is the table from the link. Apparently someone there must have done a very similar experiment and their results match mine exactly.

trkmeas3

It turns out, not surprisingly, that the integer offsets correspond directly to the carrier frequency of each GLONASS satellite, and the above table can be expressed more concisely that way. They also found that the integer offsets are different between the 2.30 u-blox firmware and the 3.01 firmware which is what the column titles refer to.  My M8T receivers are running the 2.30 firmware.

trkmeas4

I find the arbitrariness of the numbers in this table quite strange but the data suggests very strongly that they are real. If they are real and unchanging then they can quite easily be corrected in the RTKLIB TRK-MEAS decode function. Of course, the data only shows they are different, it doesn’t show which is correct. It seems more likely that the RXM-RAWX would be more accurate since it is the documented command, but we can verify this.

To do that, I ran solutions for both data sets. In the plot below, the yellow/green trace is the solution for the RXM-RAWX data, and the green/blue trace is the solution for the TRK-MEAS data. The pseudorange measurements will have a steadily diminishing effect on the solution as the kalman filter converges and the carrier-phase measurements start to dominate. What this plot shows is that the initial errors, when psuedorange dominates, are smaller in all 3 axes with the RXM-RAWX data and presumably because of this, the fix occurs earlier with this data.

trkmeas5

I then modified the TRK-MEAS decode function in ublox.c to include an adjustment to the pseudorange measurements based on the values in the table above. Rerunning the solutions with this change gave the following result. As you can see the two commands now give virtually identical solutions.

 

trkmeas6

It would not be very useful if this correction to the TRK-MEAS results applied only to the M8T and not to the M8N, but since the two modules share the same hardware, it is very likely that the corrections apply to both.

I had collected some data with an M8N receiver running 2.01 firmware at the same time I collected the M8T data above, so let’s take a look at that data next to see if we can confirm this. I converted the M8N TRK-MEAS raw data to two sets of observation files using the unmodified and modified version of RTKCONV to give uncorrected and corrected measurements. I then ran solutions on both sets of data.

In the plot below, the yellow/green trace is the solution for the corrected measurements and the green/blue trace is the uncorrected measurements. Again all three axes show smaller initial errors with the corrected measurements, although in this case it didn’t result in an earlier fix. On average, smaller position errors result in earlier fixes, but the correlation is not 100% and the fix times tend to be quite discrete so I believe the reduced initial position errors are significant even though they did not result in an earlier fix in this particular example.

trkmeas7

Just to be sure though, let’s dig in a little deeper to see if we can find some more convincing data.  Let’s look at the pseudorange residuals first.  We would expect that reducing errors in the GLONASS pseudorange measurements should be most visible in the pseudorange residuals.  Plotting the pseudorange residuals for just the GLONASS satellites, uncorrected on the left, corrected on the right confirms this.

trkmeas9

The same plot for the GPS satellite pseudorange residuals shows little change between corrected and uncorrected, again as we would expect since we have not corrected those.

trkmeas10

All of this is quite encouraging and suggests it would be worthwhile to add this correction feature to the code. So I’ve gone ahead and done this to the demo5 RTKLIB code by adding a receiver option for the u-blox receivers. Receiver options are entered from the “Options” menu from RTKCONV or RTKNAVI and from the command line with CONVBIN. Below is an example of setting the new option with RTKCONV. The name of the option is “-TRKM_ADJ” and it is set to “2” to apply the 2.01 or 2.30 firmware offsets and “3” to apply the 3.01 firmware corrections. (Note:  the second dash in the option specified in the image below is incorrect, it should be an underscore.)

trkmeas8

The options used by RTKLIB during decoding are listed in the header of the observation file so you can verify which options were used by looking there.

This change should help the M8N solutions get to a first fix quicker and more reliably.

I’ve already uploaded the source code changes to my Github page and plan to update the executables soon.

 

Demo5 code features

I’ve had a couple questions about what’s different between my demo5 code and the 2.4.3 release code. Here’s a list of what I think are the significant differences.  Any changes that I have made but have then been ported back to the release code are not included here.

 1 Calculation of GPS Solution (RTKNAVI, RTKPOST, RNX2RTKP)

1.1 Significant Functional Differences

(1) The kalman filter state update has been re-written to remove all the zero-element multiplies. This significantly reduces the computation time when receiver dynamics is enabled.

(2) Code has been added to calibrate and manage inter-channel biases for the GLONASS and SBAS satellites based on an extension of the fix-and-hold method (direct feedback from the ambiguity resolution results to the kalman filter states).  This enables use of the GLONASS and SBAS satellites for ambiguity resolution with the M8N receiver or when the base and rover receivers are not identical.

(3) Integer ambiguity resolution has been enhanced to include up to three attempts per sample to resolve the ambiguities using different combinations of satellites. Additional attempts may remove GLONASS and SBAS satellites and/or remove newly acquired satellites depending on various conditions.

(4) Additional adjustable constraints for ambiguity resolution have been added to help minimize the chance of false fixes.

(5) The common component of the phase-biases has been moved to a separate variable instead of spreading it out among all the satellites. This makes the phase-bias states easier to interpret during debug and also removes some issues with improper adding of cycles from satellites with different carrier wavelengths.

(6) Comments have been added to most of the core positioning algorithm code.

(7) The trace output has been enhanced and modified to provide more relevant information for debugging poor solutions.

1.2 Additional Input Parameters and Options

(1) pos1-posmode = static-start: Begins solution in “static” mode but switches to “kinematic” mode after first fix. This mode will normally acquire first fix faster than kinematic mode if the rover is stationary but will fail if the rover starts moving before the first fix.

(2) pos2-gloarmode = fix-and-hold: Extends the fix-and-hold method to calibrate the inter-channel biases for the GLONASS satellites and similar errors for the SBAS satellites. Note that fix-and-hold feedback only occurs after the first fix.

(3) pos2-arfilter = on,off: Rejects new or recovered satellites from being used in ambiguity resolution if the AR ratio was significantly degraded by their addition. This is an alternative or enhancement to using the blind delay of “arlockcnt” since the satellite will only be left out of the solution as long as necessary instead of for a fixed length of time.

(4) pos2-arthres1 = x: Integer ambiguity resolution is delayed until the variance of the position state has reached this threshold. It is intended to avoid false fixes before the kalman filter has had time to converge. If you see AR ratios of zero extending too far into your solution, you may need to increase this value. The “arthres1” option exists in the release code config file but is not used for anything.

(5) pos2-minfixsats = n: Minimum number of sats necessary to get a fix. Used to avoid false fixes from a very small number of satellites, especially during periods of frequent cycle-slips.

(6) pos2-minholdsats = n: Minimum number of sats necessary to hold an integer ambiguity result. Used to avoid false holds from a very small number of satellites, especially during periods of frequent cycle-slips.

 

2 Conversion from raw data to RINEX (RTKCONV, CONVBIN, RTKNAVI)

2.1 Significant Functional Differences

(1) The M8T raw output to cycle-slip translation algorithm has been modified to more closely match that of the M8N. This insures that a cycle-slip is flagged after every loss-of-lock and is not flagged until the carrier phase measurement quality is sufficient for resetting the phase-bias estimate.

(2) The half-cycle invalid bit is set for the SBAS satellites based on lock-time since the half-cycle invalid bit from the receiver is not functional for these satellites. This change has been made for both the M8N and M8T in the demo5 code but has only been ported back to the 2.4.3 code for the M8N receiver.

(3) The receiver measurement quality metrics for each sample are recorded in the RINEX observation files. The single character SNR field in the pseudorange and carrier-phase measurements are used for this purpose. The M8T reports quality metrics for both pseudorange and carrier-phase, the M8N reports quality metrics only for the carrier-phase. This is for information purposes only, RTKLIB does not use these fields.

2.2 Receiver Specific Options (U-blox)

(1) -STD_SLIP = x: Carrier phase measurements are flagged as cycle-slips if the standard deviation of the carrier phase measurement (as reported by the receiver) is greater or equal to x (scale=0.04 meters/count). This feature now exists in both  the 2.4.3 and the demo5 codes but is not listed in the 2.4.3 documentation. It is only used for the M8T receiver. If this option is not set, then the same fixed threshold will be used for cycle-slips as for valid carrier-phase measurements, which in the release code can cause cycle-slips to fail to be flagged after the phase-bias estimate is valid.

Adding a radio link

In the last post I described setting up RTKNAVI in a simple configuration with both receivers connected directly to a laptop. While this is a good way to become familiar with RTKNAVI, it is not a useful configuration for actual measurement since the rover can’t rove for more than a few feet before running out of cable.

In this post I will describe adding a pair of HobbyKing SiK V2 Telemetry radios to separate the base from the laptop and rover. These radios are based on the same open-source design as the 3DR radios previously made by 3DRobotics and sell for $33 dollars for the pair. They are supposed to be good to up to about 300 m with the supplied antennas. There is a 915 Mhz version and a 433 Mhz version available, you will need to choose the one that is legal in your location. Both transceivers have both a USB connector and a UART connector. We will use the USB connector to connect one radio to the laptop and the UART connector on the other radio to connect to the GPS receiver. Here’s what they look like coming out of the box.

The first thing I did after opening up the package was to screw the antennas onto the transceivers since it is possible to damage the radios if they are accidentally powered up without the antennas attached.

To create the base station, I connected one of my Ublox M8N receivers to the radio and to a USB battery pack by cutting and reconnecting the cables that came with the devices.  I connected VCC for all 3 cables together, and the same for all 3 GND wires. I then connected RX to TX and TX to RX between the GPS receiver and the radio. This is what it looked like when I was done.

radio2

If you haven’t already set the baud rate on the GPS receiver it is possible to set it through the radios but it is probably easier to do it beforehand with the receiver connected directly to the laptop. In my case, I had previously set it to 115K from the RTKNAVI demo in the previous post and continued to use that baud rate for this exercise.

I then plugged the second radio into the laptop using a USB cable. I also plugged the second GPS receiver, which will be the rover, into a second USB port on the laptop, using an FTDI board to convert from UART to USB as I’ve described before.

radio3

Next I downloaded MissionPlanner, an open-source software package developed for drone users. I used this to configure the radios. It’s fairly straightforward and there’s some good documentation here to help you through it so I won’t go through all the details. This is the configuration that I ended up using after a little experimentation:

radio4

It is important to match the baud rates for the different pieces of the link. Set the kilo-baud rate (and the port number) for the laptop com port up in the top right corner. This needs to match the “Baud” setting for the local radio on the left. The “Air Speed” setting is the kilo-baud rate the radios operate at, and the two radios (local and remote) need to have the same value. The “Baud” setting on the remote radio must match the kilo-baud rate of the base GPS receiver.

Often when I changed these settings, it was difficult for me to get the complete link working again and I had to fiddle with it. Sometimes this meant clicking on “Save Settings” more than once, sometimes I would restart the Mission Planner app, sometimes the RTKNAVI app, and at least once I had to reboot the laptop. This was all rather frustrating and I don’t really know which steps helped and which didn’t, but once I stopped changing the settings, things seemed to be more stable.

You will need to be careful not to overwhelm the data link with too much data. In the previous demo I had reduced the base station sample rate to 1 Hz which is where I left it for this exercise.  In many cases, people convert the raw measurement data to RTCM format to reduce its size before sending it over the radio but this is not an option in this case because the receiver won’t output the raw measurements in RTCM format and we do not have a CPU in the base station to do the conversion.  As long as we are careful not to exceed the bandwidth of the radio link this should be OK although our rover distances may be limited since higher data rates are supposed to decrease the range of the radio.

At this point you should be able to communicate with the GPS receiver in the base station through the radio link. I started up the Ublox u-center eval software at this point just to verify that I could communicate in both directions. Make sure you disconnect or close it when you are done, or it will prevent RTKNAVI from accessing the com port.

Once you have established the radio link is working, you should be able to startup RTKNAVI and follow the instructions from the previous post to configure and run it. The only difference will be that you will probably find the radio is using a different com port than the GPS receiver so you will need to change that in the Input data stream menu.

I placed my base station on a tripod for convenience and to get the radio antenna further off the ground. I used a 8” pizza pan (88 cents at Walmart) for a ground plane. Here’s a photo of the assembled base station.

tripod

I placed the radio underneath the ground plane and the antenna pointed down in case that helped reduce possible interference between the radio and the GPS receiver but I did not do any testing to evaluate how effective this was. I probably should have also mounted the USB battery pack underneath as well just to keep things cleaner but didn’t get around to it.

I then mounted the other radio and GPS receiver antennas on top of my car to use as the rover. As I do for all my data sets, I started the data collection and then remained stationary until I got a fix. Typically this takes about 3 or 4 minutes and that is what happened in all of my runs. After starting RTKNAVI, I opened two plot windows. In the first I selected “Gnd Trk” and in the second I selected the “Nsat” plot option because this option includes a plot of age of differential, the delay in time between the rover measurement and the base station measurement. When close to the base station the age of differential remained between 0.2 and 1.2 seconds which makes sense since the base station is sampling every second and there will be a short delay for the radio link. As I got further from the rover I started to see this number increase as the radio link started to breakdown and I started to lose base observations. Here is the plot with the age of differential shown in the middle window.

radio5

Here is the ground plot and position plot from the same run.

radio6

In general, I seemed to start losing the radio link at about 100 meters. This is less than the 300 meters I was expecting, but maybe optimization of the radio settings and antenna locations would help. I did spend a little time adjusting these without seeing much difference in the results, but it was far from an exhaustive effort.

Here’s another short run where I drove out 350 meters and back showing age of differential and position. In this case I again lost the radio connection at about 100 meters and the age of differential increased all the way to the “Max Age of Diff” option (75 sec) without losing fix. It then regained a fix immediately after the age of differential dropped back below 75 seconds.

radio7

In another run, I reduced the base station sampling rate from 1 Hz to 0.2 Hz and also reduced the air speed setting of the radio from 64 to 16 to see if this would affect either the range of the radios or the reliability of the solution. I did not find it made much difference to either one. I did lose the fix after exceeding the max age of differential in this run but that may just be because I exceeded it for a longer time than in the previous example. Here is the age of differential and position plots for this run:

radio8

Overall, the radios were a little frustrating to configure, and their range was a little disappointing, but otherwise the experiment was a success.