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.


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.



24 thoughts on “Pseudorange corrections for the u-blox TRK-MEAS command”

  1. Hi, Thank you for both of your answers. I am using two M8T in an experiment, the base station is streaming to rtk2go using str2str on a Raspberry Pi with the TRKM_ADJ receiver option but the rover is using rtkrcv on another Raspberry Pi which can’t use the receiver opt. So I’ll try the rtknavi for the rover instead.


    1. Hi Rob. The TRKM_ADJ option is only used to adjust the pseudorange values in the TRKMEAS debug messages but is not necessary if you are using the RAWX messages. Although the TRKMEAS messages are available with the M8T, you would normally use the RAWX messages instead. The pseudorange values in the RAWX messages are correct as is and do not need to be adjusted. Also, if you are using the TRKMEAS messages on both base and receiver the errors in the pseudorange would be in both and would cancel.


    1. Hi Rob. Receiver options such as -TRKM_ADJ can be specified with RTKNAVI in the input stream menu but I don’t believe there is currently any way to do this with RTKRCV. It does seem like this would be a useful feature.


  2. Would receiver option work with str2str?
    For example streaming the receiver connected to serial on a raspberry pi:
    str2str -in serial://ttyAMA0:115200:8:n:1:#ubx -opt -TRKM-ADJ_2 -out tcpsvr://:5086#rtcm3


  3. Hello,

    You describe a lot of interesting information. What is more interesting to me is that RTKLIB 2.4.3 demo 5 b29 will work with parameter “-TRKM_ADJ” with admissible values ​​of 2 or 3, according to firmware u-blox (2.01 or 3.01). So I have a question – did I understand that RTKLIB demo version 5 works with firmware 3.01 with NEO M8N? So there is no need to make a downgrade of firmware from version 3.01 to version 2.01? I have 2 NEO M8Ns (both with ROM 3.01) – one connected to a serial port computer – like Rover, the other via Raspberry (str2str as TCP Server) as Base. RTKLIB application RTKNAVI.EXE 2.4.3 demo 5 b29c does nothing at all – does not show satellites, does not show signal strength. Maybe I did not understand it correctly, maybe I have not configured the configuration parameters for the NEO M8N. Can you please advise?


    1. Hi Cistic. The raw observation messages from the 3.01 firmware on the M8N are intentionally scrambled so RTKLIB will not work with it. I have only used the M8N with the older firmware. The pseudorange adjustment values for the 3.01 F/W came from the table on the OpenStreetMap website that I referenced in my post. I don’t know how they got these values … they may have unscrambled the raw messages.


      1. I used software I wrote to analyze a TRK-MEAS and RXM-RAWX stream from a NEO-M8T loaded with the 3.01 SPG firmware. I did similar work with the earlier TIM 2.30 firmware, which is also capable of delivering both messages, and the method was later applied to data from the 2.o1 ROM NEO-M8N and validated via post-processing, and a friend with a JAVAD and M8N on a splitter in a zero baseline test.

        The TRK-MEAS packets are encapsulated in a SEC-PACKET.

        The way the options are enabled, a NEO-M8T with ROM 3.01 FW can be erased, and you can double the multi-constellation output of RXM-RAWX from 5 Hz to 10 Hz, ie GPS, SBAS, GALILEO and GLONASS


        1. Hi Clive,

          Did you notice any difference in accuracy?

          Still running 2.30 on my m8t-0-01 ‘s as the TIM 1.10 EXT CORE 3.01 (111141) seems unavailable for end-users.
          And can’t find a definitive answer if updating to 3.01 SPG (or even HPG) , would still allow the use of raw measurements or render them useless after flashing.

          Main reason is gaining the benefit of the Galileo constellation in Europe. Would like to use them with RktlikExplorer’s version of str2str as ntrip base and rtknavi on the rover.


          1. Hi Paul,
            The SPG 3.01 is a viable route, it supports RXM-RAWX based on the optioning of the NEO/LEA-M8T part. It’s not recommended in the sense you can’t roll back with a download from uBlox. I and the aforementioned colleague who pointed out the TRK-MEAS obfuscation to me back in Jan 2016 both migrated NEO-M8T devices to the SPG 3.01 release. The HPG firmwares are not operable on M8N/M8T parts, but are recoverable.
            More interestingly is that you can erase the firmware on FW 3.01 ROM based NEO-M8T devices, and get 10 Hz multi-constellation from them.


  4. Hi, Great Blog as always, and promising sounding improvements to the code, so now testing your Demo5 using GPS plus Glonass plus Galileo on M8T. Now that E01 and E02 Galileo sats went active on 1st Dec bringing the total active to 11 with four more in commisioning, so getting useful fix and float solutions from Galileo. Unfortunately commissioning seems to be taking around six months. Right now I can see 5 Galileo sats all OK with Float status using baseline 266km though as thats my nearest NTRIP station broadcasting all three.
    Will try later with two receivers in short baseline.
    I had the same problem with ublox drivers on windows 10 but seems to be fixed now using your method. I think rebooting the computer is the key, once the changes has been made or the sensor drivers keep getting reloaded . However I am veering towards bluetooth connections now as potentially better solution.
    With Galileo now coming of age ( teenage?) I wonder whether M8N vs M8T has any mileage now, its fairly clearcut for M8T.
    Re your Glonass settings of fix and hold I assume you are using two identical receivers or it would not work.
    One oddity I get on demo 5 is the reported Fix/Float under Sat Galileo in RTK monitor (rtknavi) appears to be reversed to the value seen in RTKnavi main screen or RTKplot. The monitor report seems to be wrong. Its a minor bug/issue and is not seen in 2.4.3 b26 but I did not check 2.4.3 b24 to see if error occurred between those versions or your just demo5.
    PS sorry to here you missed the free C++ builder download, it won’t help you now but others might like to know they have now made the starter edition permanently free if your C++ builder app sales made with it are less than $1000 or for Hobby/Learning. (Credit to Sakura diaries for that)
    Re the AR recalculation if AR drops dramatically, this sounds a great idea, I often wondered with computing power increasing all the time why one should not try many different AR calculations, maybe dropping one or more satelites at a time per period ( starting with the low SNR’s?) just to see what effect it has on AR. I get 10 msecs computing time with RTKnavi, so plenty of room to add more tries. maybe 5-10 per 5Hz period? With more and more satelites available now it could be worth weeding out the weakest on a trial and error basis, and of course contantly retesting to see if they improve the solution.


    1. Hi Anthony. Good comments! I agree that as Galileo becomes more functional, M8N will be less interesting, but supplemented with some cheap CPU MIPS, I suspect it will still have a role for the most price-sensitive applications.
      -I still see the download version of 3.01 FW on the u-blox site is labelled for standard-precision only, is that what you are using for Galileo measurements or do you have access to a high-precision version?
      – Demo5 RTKLIB code nulls out GLONASS IC biases using an extension of fix-and-hold, so handles integer AR of GLONASS sats for non-identical receivers after initial lock.
      – I’ll see if I can duplicate your observation about incorrect Fix/Float in RTKNAVI.
      – I strongly agree there is great opportunity to enhance RTKLIB by taking advantage of all the unused MIPS and am actively pursuing it. I have test code that shows a reduction in time to first fix by a factor of 2 to 4 with fewer false fixes but I need to give it some more rigorous testing and clean up the hacked code before it’s ready to share.


  5. Hi,
    I tested your new code for a RTK surveying and it works great. Looking at the ublox.c source code, I noticed the right value for this option should be -TRKM_ADJ=2, please correct me if I’m wrong.
    Many thanks, I really appreciate your work!


  6. Hello, I express gratitude for improvements to ublox receivers and especially for RTKLib demo 5. With great interest I watch your blog and test demo 5 + M8N in RTK-mode.
    Little question about this article – which provides a value for the “TRKM-ADJ” for firmware version 2.01?
    Best regards.


    1. Hi Max. I should have explained that I have 2.30 FW on my M8T receivers and 2.01 FW on my M8N receivers. Setting the receiver option to “-TRKM_ADJ=2” will provide corrections for both. I’ll fix this in the post.


  7. In version 3.01 on the M8N, the TRK-MEAS message is no longer present.
    V3.01 on the M8N provides some improvements in operation as well as Galileo capability. It would be nice to be able to use the M8N running 3.01 for RTK.
    I found this post on the u-blox user forum on Recovering pseudorange from code phase (RXM-MEASX)
    Is there any possibility of using this message with some tweaks in place of TRK-MEAS?
    I have very little knowledge of the math involved in doing RTK so please be kind ;>)


    1. Hi Cynfab. You may be able to get phase measurements from this command but I don’t believe you can get pseudodrange measurements. RTKLIB requires both. I think the last comment (from the original poster) in the thread you link to also agrees with this assessment.


      1. Ok, that’s too bad, but not surprising. I’m sure u-blox wants to keep their timing and RTK capable modules at a premium price for as long as they can.


    2. Just an FYI, TRK-MEAS is present in FW 3.01, you just have to figure out how to use it.

      I figured the offsets by sieving two streams of data, a colleague looked at the “fixed” data and measured it against a zero baseline test with a JAVAD receiver on a split from the antenna feed.

      I did a bunch of the analysis posted on OSM, and have GPS+GLONASS+GALILEO RINEX data loggers built with the NEO-M8N. They are a lot cheaper than the M8T, and also have a TCXO rather than a crystal, which the cheaper NEO-6M had.


      1. clive1:
        I have tried to enable TRK-MEAS & TRK-SFRBX on my 3.01 M8N, and I don’t get any output. Is there a different command to enable them than there was on 2.01? My M8Ns with version 2.01 firmware do output those messages.
        I am having difficulty finding your posts on OSM. Probably not looking in the right place ;>P


      2. From your comparison, it is safe to assume the data is scrambled but not encrypted? I am thinking of working out the raw data from the debug messages


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: