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.
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.
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.
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.
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.
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.
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.
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.
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.
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.)
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.