Author: Antonio Tabernero (ant@fi.upm.es)
Last modified: 9/14/2000 (with Etrex info)


 Following the thread about obtaining raw data from a Garmin GPS12
via async messages, I have prepare a brief report concerning those 
messages more related to raw data. 

 Send comments, errors, suggestions to ant@fi.upm.es

  Antonio.


History
-----------------------------------------------------------------
The original starting point was the compilation by William Soley 
of the undocumented commands of the Garmin units. Then there was 
a post indicating that a commercial product (gringo) was available 
for postprocessing pseudorange and phase info acquired from a GPS12 
(or XL), followed by the post from Jose Maria Munoz describing most 
of the interesting async events.

The main expansion of this document over Jose Maria's post concerns 
messages 0x16, 0x37, 0x39 and a couple fields in 0x38.
 
I also would like to thank Sam Storm van Leeuwen, whose comments 
and explanations on the peculiarities of the GPS12 receiver made 
me waste a bit more time on my GPS12. He also found a more general
way of obtaining the fractional phase.

D.J. Johnson was kind enough to provide me with a binary log of an
Garmin Etrex from which I was able to (more or less) find out where 
the raw data is output in those units.


Programs
------------------------------------------------------------------
As I understand that the info presented is necessarily of a 
local (both in time and space) nature and that the best way to
learn more is to get more people involved, I have prepared the 
programs that I have been using so that they can be useful to 
other people.

Have a look at the following address to download or learn more 
about these programs:

   http://artico.lma.fi.upm.es/numerico/miembros/antonio/async

The following report also lives there.



                           REPORT
----------------------------------------------------------------
----------------------------------------------------------------
INDEX:

1. ASYNC EVENTS IN THE GARMIN GPS12 AND RAW DATA INFORMATION:
     
   1.1 Message 0x1a
   1.2 Message 0x33
   1.3 Message 0x38
   1.4 Message 0x39
   1.5 Message 0x16
   1.6 Message 0x37

2. TYPICAL POWER-ON PROCEDURE.


3. GENERATION OF RINEX2 FILES FROM YOUR GPS12.


4. RAW DATA IN THE GARMIN ETREX 
--------------------------------------------------------------------


1. ASYNC EVENTS IN THE GARMIN GPS12 AND RAW DATA INFORMATION


 I describe records 0x1a, 0x33, 0x38, 0x39, 0x16, and 0x37 (in that 
order).  I have named the different fields of each record (at least 
those that I think we know what they are) so that they can be 
referred in different parts of the document.


-----------------------------------------------------------------
1.1 Message ID: 0x1a (12 x 8 bytes)
-----------------------------------------------------------------

General description:
12 messages (one for each channel) indicating the SV that the channel
is tracking, its elevation, signal strenght, etc. Each message 
is composed of the following fields:

Name         svid 
Position     byte 1
Type         BYTE
Description  SVID (PRN-1). If ff, no sat is being tracked in that channel.
 
Name         elev
Position     byte 2
Type         BYTE 
Description  Elevation in degrees.

Name         Fractional Phase
Position     bytes 3-4
Type         unsigned int
Description  Only 11 bits are used. Divided by 2048 it will give us 
             the fractional carrier phase corresponding to the 
             time of the previous bunch of 0x38 records.
             BEWARE: there are reports that these field is version
                     dependent. The above interpretation is valid
                     for GPS12 ver 4.0 and XL ver 4.55, but it is not
                     for the XL ver 3.62

Name         signal_Q
Position     bytes 5-6
Type         unsigned int
Description  Signal strenght. Identical to the corresponding field of 
             the preceding 0x38 record.

Name         tracking_status
Position     bytes 7-8
Type         BYTE[2]
Description  Two bytes that seems to describe the tracking status 
             of the satellite. The first one can vary betweem 1 and 4.
             The second one seems to be a flag (0-1).  The most 
             important value seems to be the first one. 
             Observed values:

             **  Correct tracking status: (4,1) or (4,0) 
             The sat is properly tracked, and the tracked_byte of 
             the 0x38 record is non zero. It seems that (4,1) is a 
             previous step to 4,0 (normal situation).
 
             ** Abnormal tracking status: (2,0) (3,0) (4,0)
             In this cases, the tracked_byte of the corresponding 
             0x38 records is zero, and a new record 0x37 is sent 
             just after each 0x38.
 
             (2,0) Initial status at power-on of the sats with 
             low signal. It seems that the GPS don't pay attention
             to them at that point. Few 0x38 are sent and those 
             show an null tracked_byte.

             (1,0) Similar results to (2,0) (tracked_byte=0 in 0x38),
             but it is not an initial situation. It looks as though 
             that sat has been declared unusable.
             
             (3,0) Special status in which a sat can fall after
             being tracked for a while. 
              

--------------------------------------------------------------------
1.2 Message ID: 0x33 (64 bytes)
--------------------------------------------------------------------
Corresponds to Garmin documented D800_Pvt_Data_Type. So no guess here,
but not useful for postprocessing either. It is interesting to 
monitor, though, to check when the GPS is able to get a fix and 
what kind of a fix.

4 bytes float   altitude above WGS84 ellipsoid
4 bytes float   Position error
4 bytes float   Horizontal pos. error
4 bytes float   Vertical pos. error
2 bytes int     Type of fix
8 bytes double  time of week (sec)
8 bytes double  latitude
8 bytes double  longitude (radians)
4 bytes float   velocity east
4 bytes float   vel north
4 bytes float   vel up
2 bytes int     leap seconds
4 bytes long    week number days



------------------------------------------------------------------
1.3 Message ID: 0x38 (37 byte long)
-------------------------------------------------------------------
Data related to a particular sat (indicated in the last byte). 
It is in this record where the most interesting info seems to be sent.


Name         phase_counter   
Position     bytes 1-4
Type         unsigned long 
Description  A counter that gets increased by about 60-70 millions
             per seconnd. The lower 11 bits give us the fractional 
             phase (it mirrors the corresponding field in the 0x1a
             record), while the upper bits (12-32) represent the integer 
             number of cycles. Just after starting the unit those 
             bits will be equal to the Integrated_phase field (see 
             below), but after a while the won't, because the phase_counter
             will roll over. The best way to compute the carrier phase 
             is thus: 
                     
                carrier_phase = integrated_phase (whole cycles) +
                                (phase_counter AND 2047)/2048.

             that is, the integrated_phase field keeps track of the 
             integer number of cycles, so we don't have to worry
             about the rollover of phase_counter. From phase_counter
             we use only its lower 11 bits to get the fractional phase.

Name         track_byte
Position     byte 5
Type         Byte  
Description  0x00 indicates some sort or problem with the sat. 
             This is the case for sats with low elevation and weak 
             signal. In that case, the other data may be unreliable. 

Name         unknown   
Position     bytes 6-8
Type         byte[3]
Description  Seem to be grouped into an integer (bytes 6-7) and a 
             flag byte (byte 8) that is always? 00 or ff.
             The integer?? moves while the tracked_byte is 00 
             (the sat is not properly tracked). 
             When the sat is locked, the number gets frozen.

Name         delta_f
Position     bytes  9-10 
Type         unsigned int (interpreted as signed by substracting 32768)
Description  If we consider this field a signed integer (not as usually 
             interpreted by the machine, but considering 32768 as the 
             zero so that the value we use is actually delta_f-32768),
             it is positive for those sats with larger increments of 
             pseudoranges (going away from us) and negative for those 
             with smaller increments of pseudoranges (approaching us):

             Sat Id    delta_pseudorange   delta_f-32768
             -------------------------------------------
               10           6329               1323
               02           5638              -2402
               05           6534               2428
               15           5863              -1186
               
             The relation between those two quantities is linear and the 
             constant relating them (in a least squares fit) is very 
             close to the L1 wavelenght. That would mean that this 
             field could be a measurement/estimation? of the Doppler 
             shift (in Hertz) for each sat.

             For the above data an aproximate fit is:

               delta_f = (delta_pr - 6080) / lambda

             delta_pr is obviouly a measure of the relative speed of the
             satellite, and the origin 6080 m/s would be the false speed 
             caused by the clock drift. Once that bias is removed, the 
             relation would be:
             
               delta_f = relative_speed / lambda 
                       = L1 * (relative_speed/c)  = Doppler shift (Hz)

Name         Integrated Phase
Position     Bytes 11-14
Type         unsigned long
Description  integrated phase (whole cycles). The ratio of its increment 
             with the increment of the pseudorange field corresponds 
             with the inverse of the L1 wavelenght. Once locked, 
             the difference between both fields rarely exceed 100 cm. 

          
Name         pseudorange
Position     Bytes 15-22
Type         double
Description  Pseudorange in meters. Due to the particular idiosincrasy 
             of the GPS12, these numbers can get real large, because 
             the GPS12 allows the clock error to accumulate in them, 
             at a rate (in my unit) of about 6000 m/s.

Name         c_511500
Position     Bytes 23-26
Type         Unsigned long
Description  511500 Hz timer. 511500 ticks corresponds to a receiver's 
             second.  At the start it is incremented by exactly 511500 
             units, but once enough sats have been acquired, it can 
             vary one or two units (probably as the GPS tries to sync 
             its measurements to an exact GPS system time second.     

Name         signal_Q
Position     Bytes 27-28 
Type         Unsigned int.
Description  Measure of the signal strength or quality for that 
             satellite.  It has the same value as in the corresponding 
             field of the next 0x1a record.

Name         tow  
Position     Bytes 29-36 
Type         Double
Description  Time of week in seconds. At the start it is incremented 
             exactly by one second (511500 units of c_511500). 
             However, after enough sats have been acquired, the GPS 
             gets an idea of its internal clock drift, and (in my unit)
             511500 units of c_511500 correspond to about 1.00002 sec 
             of the receiver time. These 20 microseconds per second
             correspond to aprox. 6000 m/sec, the drift of the internal
             clock reflected in the pseudoranges. Monitoring this field
             we can check our clock drift. Also, as I have already said,
             the increments of c_511500 are not exactly 511500, as the 
             GPS tries to take each measurement synced to a GPS second.   

Name         svid
Position     Byte 37  
Type         Byte
Description  Space Vehicle ID (PRN-1) 
   

------------------------------------------------------------------
1.3 Message ID: 0x39 (35 byte long)
-------------------------------------------------------------------

BYTES 1-2  : changing.
BYTES 3-10 : fixed for each start.
BYTES 11-19: changing.
BYTES 20-34: fixed for each start. 
BYTE  35   : SVID (PRN-1)

General description:
This record is sent once per satellite when the GPS first locks to 
that satellite (svid field) and it's able to compute a valid
pseudorange. I don't know if it is sent again if the sat is 
lost and later re-acquired.

Tipycally, you will see a 0x38 record with an invalid pseudorange
field, then a record 0x39, and the the same 0x38 record with all
its counters unmodified except for the pseudorange field, that nows 
is something around 22,000,000 mts.

More on the relation of this event with others is explained later,
in the section about the start procedure.


--------------------------------------------------------------
1.4 Message ID: 0x36
---------------------------------------------------------------

General description: 
timing info + something else. These records are only sent once we 
have computed a valid pseudorange for a satellite (see power-on 
description below), and disappear if there is troubles (when
tracked_byte of record 0x38 becomes 0).

Name         c_50     
Position     Bytes 1-4
Type         unsigned long
Description  50 Hz counter, STARTING from the beginning of the week, 
             that is, c_50/50 corresponds to TOW. It gets incremented 
             in 30 count intervals, so that the resolution is 0.6 sec.
             
Name         unknown
Position     bytes 5-8 
Type         BYTE[4]
Description  seem to vary randomly.

Name         svid
Position     byte 9
Type         BYTE
Description  SVID (PRN-1)


-----------------------------------------------------------------
1.5 Message ID: 0x16
-----------------------------------------------------------------

General description:  
several hints indicate that this record could be related to 
velocity??/Doppler???  information:

* The delta_pseudorange_rate field is very similar to the difference 
  in pseudoranges divided by the time increment.

* Also, using the phase field of the 0x38 record as a reference,
  and studying the behaviour of this field compared to the 
  rate of increase of that phase we found the following:
 
                   Sat Id   Signal_Q   Mean(cm)   std (cm)
                     10      12000      2.0         19 
                     02       8500     -1.3         16
                     05       6000      0.3         18
                     15      12500      0.8         17
                     18       6500     -0.2         18

  We find that this delta_pseudorange rate follows the phase rate 
  a bit more closely than the differences
  of pseudoranges shown in the other table. The improvement is 
  more clearly seen in those sats with a lower signal. 
  That would be consistent with its being a more precise 
  measurement (integrated Doppler??) of the same quantity. Again, 
  beware that this has been obtained by examining a couple 
  minutes of data, and this interpretation could be wrong.

* These 0x16 records are not sent until enough sats are acquired.

* F1 and F2 seems to be some sort of correction (Kalman filter 
  parameters/variances??) that is used when aditional info is 
  gathered.  Some observations:

  - F1 has similar values (usually 0 point something) for each group 
    of messages that are sent together for the different sats.
  - F2 can be real big (in the order of thousands) in a cold start for 
    a different position for the FIRST satellites. After a while, 
    F2 drops to its normal values of about 5.
  - In a warm start (same position,a bit later) F2 values are very 
    low from the beginning.
  - In its stable state, F2 used to be larger (about 20-30 as opposed 
    to 3-5) with SA on.

 
Name         delta_pseudorange_rate (m/s)
Position     bytes 1-4
Type         float
Description  A float that closely (usually within a meter) resembles
             the increment of the pseudorange for that sat in a second.

Name         f1
Position     bytes 5-8
Type         float
Description  Similar values for a group of messages that are sent 
             together for the different sats. 
             See general description above.

Name         pseudorange
Position     bytes 9-16
Type         double
Description  Pseudorange (ms). Same as in the preceding 0x38 record.

Name         f2
Position     bytes 17-20
Type         float 
Description  See general description above.

Name         svid
Position     byte 21
Type         BYTE
Description  SVID (PRN-1)



-----------------------------------------------------------------
1.5 Message ID: 0x37 (33 bytes)
-----------------------------------------------------------------

General description:
This message is associated to a abnormal tracking status of a 
satellite, namely, (3,0) (2,0) or (1,0) in the tracking_status 
bytes of a 0x1a record. When this situation appears, the tracked_byte 
field of the 0x38 records is put to 00, and a 0x37 record follows 
each 0x38 (at that point the 0x36 records disappear).


Name         c1
Position     bytes 1-2
Type         unsigned int
Description  (??) counter, increasing, but not exceeding a relatively
             small value (depending on the tracking status). 
             The values within a group of 0x37 messages with the 
             same time tag are strongly correlated.

Name         ??
Position     bytes 3-4
Type         BYTE[2]
Description  flags?? Mostly 00 00

Name         c2
Position     bytes 5-6
Type         unsigned int
Description  similar description as previous field c1. At power-on,
             this field is the same for all 0x37 messages with 
             the same time-tagging (for different sats), but later 
             on they differ.
 
Name         delta_f
Position     bytes 7-8
Type         unsigned int (interpreted as signed by substracting 32768)
Description  Correlated with the delta_f field of the 0x38 record. 
             In that case it would be the Doppler shift?? in Hz. 
             When 0x37 records are being sent, this field (in normal 
             conditions a slow drifter) is frozen, and is the same in 
             both (0x38 and 0x37) records. 
             When it changes, the change is first seen on this field, 
             and reflected on the next 0x38 record.
             
 
Name         countdown
Position     bytes 9-10
Type         unsigned int
Description  (??) Similar as c1 and c2 above. It also shows same
             values for each group of 0x37 records with the same
             timing at power-on. Later this sinchrony dissappears.
             
             A particular case is when the sat falls in a (3,0)
             status. In that case, the value is always? between 0 
             and 1024. If it is lower than 64, the situation remains 
             unchanged.  When it gets larger than 64, it increases 
             at a rate of 16 per second (60'' countdown). 
             When it reaches 1024 (but not before) the sat can be 
             either correctly tracked again, or a new cycle starts.

Name         pseudorange 
Position     bytes 11-18
Type         double
Description  Pseudorange looking value, but slightly different from 
             the value in the same field of the 0x38 record. My guess
             is that this value is a sort of dead-reckoning when the
             sat is not being tracked. This is based on the fact that,
             when first powered, while the 0x38 (tracked_byte=00) may 
             show an incorrect pseudorange field for the sats with a  
             tracking_status of (2,0), the pseudorange field in the 
             0x37 record has at least the ``right'' aspect (it could 
             be an educated guess based on the stored almanaq info).

Name         c511
Position     bytes 19-22
Type         unsigned long
Description  511500 Hz counter. Same value as the preceding 0x38
             record.

Name         tow
Position     bytes 23-30
Type         double
Description  time of week. Same  value as in the corresponding 
             field of the  preceding 0x38 record.

Name         ??
Position     bytes 31-32
Type         BYTE[2]
Description  flags?? Mostly 00 00, sometimes 01 or ff

Name         svid
Position     byte 33
Type         BYTE
Description  SVID (PRN-1)


-------------------------------------------------------------------- 
2. TYPICAL POWER-ON PROCEDURE:

The GPS starts sending 0x38 records for the most visible/strong 
sats.  The pseudorange fields are invalid (although its increments
are correct and coherent with the phase increments).
The increment of the c_511500 counter and tow are exactly 
511500 units and 1'' respectively.

At about 5 secs the first 0x1a (combining several sats info) is sent.

A 0x39 record is sent for a particalar sat. The pseudorange field
of the 0x38 record is replaced by a valid number:

0x38 ----------------------------------------------------------------
SVID 10: TRACK_BYTE 49 Sgn Q  9580   CC2 1684  FLAG 00
         PseudoRange     24262.1 (Counter =  593122866) Phase 289610
         TOW 201131.00703 (Counter 0.5 Mhz =  5115000)  Cont (?) 34323
0x39 ---------------------------------------------------------
SVID 10: Meaningful Pseudorange
0x38 ----------------------------------------------------------------
SVID 10: TRACK_BYTE 49 Sgn Q  9580   CC2 1684  FLAG 00
         PseudoRange  21548541.7 (Counter =  593122866) Phase 289610
         TOW 201127.21737 (Counter 0.5 Mhz =  5115000)  Cont (?) 34323  
----------------------------------------------------------------------

Inmediately after a 0x39 record is sent, we start receiving 
records 0x36 for that particular sat.
Also the tow is reset to something more close to real GPS time,
in the above example the GPS clock was almost 4'' fast (it was 
a cold start).


Only when 3 0x39 records for three different sats have been sent,
the GPS is able to get a (2D) fix, and the corresponding 0x33 records
(position/vel,etc) start coming once a second. If a fourth
satellite is locked (another 0x39 record), the fixes are 3D:

0x39 ---------------------------------------------------------
SVID 15: Meaningful Pseudorange
0x39 ---------------------------------------------------------
SVID 16: Meaningful Pseudorange
0x39 ---------------------------------------------------------
SVID 10: Meaningful Pseudorange
0x33 ----------------------------------------------------------------
Pos:  (N,E,H )  (0.7048 -0.0651 686.2358)  H_ellip=-52  FIX 1D
Wdays 3808  TOW 118123.631  Leap 13
0x33 ----------------------------------------------------------------
Pos:  (N,E,H )  (0.7048 -0.0651 686.2358)  H_ellip=-52  FIX 2D
Wdays 3808  TOW 118124.631  Leap 13
0x33 ----------------------------------------------------------------
Pos:  (N,E,H )  (0.7048 -0.0651 686.2358)  H_ellip=-52  FIX 2D
Wdays 3808  TOW 118126.000  Leap 13
0x33 ----------------------------------------------------------------
Pos:  (N,E,H )  (0.7048 -0.0651 686.2358)  H_ellip=-52  FIX 2D
Wdays 3808  TOW 118129.000  Leap 13
0x39 ---------------------------------------------------------
SVID 14: Meaningful Pseudorange
0x33 ----------------------------------------------------------------
Pos:  (N,E,H )  (0.7048 -0.0651 691.3297)  H_ellip=-52  FIX 3D
--------------------------------------------------------------------

Note the frozen height while in a 2D fix.
Also when the GPS has sent 4 0x39 records (four sats locked) the 
GPS is able to precisely estimate its clock error, and the tow is 
synced more precisely to GPS time.

From now on, the increments of tow begins to reflect the clock drift,
being in my case of about 1.00002 seconds. The increment of the 
511500 counter can change in 1 or 2 units, as the GPS tries to 
takes measurements in full GPS time seconds.

Another record that only shows when at least 3 sas are locked are the
0x16 records with ???Doppler??? info. 


---------------------------------------------------------------------
3. GENERATION OF RINEX2 FILES FROM YOUR GPS12.

Once we think we know where pseudorange and phase info is sent in the 
async messages, it is time to try to see if something useful can be 
done with it.  Writing a postprocessing software without knowing 
wheather or not we have something real to work with seems a waste of 
time.

The easiest way to check things out would be to take advantage of the
postprocessing soft available. Usually those packages work with 
something called the RINEX (Receiver INdependent Exchange) format. 
As its name implies, this RINEX format is simply a way of putting all 
the pseudorange, phase, timing info into a file, so that postprocessing
packages can work with it independently of the receiver which collected 
the data.  The advantage of the RINEX format is that the reference 
stations (CORS, EUREF, CDDIS) around the globe also publish their data 
in RINEX format. IN this way (with the proper software) our G12 generated
RINEX files could be directly postprocessed against those reference
stations.

If you want to know the state-of-the-art in this RINEX-from-GPS12
bussiness, check this 
\hlink{link}{http://artico.lma.fi.upm.es/numerico/miembros/antonio/async}


-----------------------------------------------------------------------


4. RAW DATA IN THE GARMIN ETREX.

As it was pointed out the Garmin Etrex doesnt output raw data 
information in the same format as the G12 family. However, after 
a brief examination of a binary log kindly supplied by D.J. Johnson
it seems that very similar info is present in the async messages
sent by the Etrex.

 It only happens that Garmin thought to improve on its undocumented 
messages by shuffling the fields within most messages. Here comes a 
brief description of where the interesting info is to be found in 
the Etrex. There is no description of the fields as they are common 
with the already described above.

Since the interesting info seems to be there all right it would also be 
possible to generate a Rinex file from a log of the Etrex. In order 
to do so the latest version of gar2rnx has an -etrex option.

I havent done any testing whatsoever since I dont ve access to an Etrex. 
Let me know if this is working for you.


---------------------------------------------
RECORD 0x1a (L=12 x 8 bytes)
---------------------------------------------

Same as in the G12 family. Twelve eight-byte records, one for each channel.
The fields are exactly the same though most are in different positions:

Bytes 1-2: Fractional Phase
Bytes 3-4: signal_Q
Bytes 5  : elev
Bytes 6  : svid
Bytes 7-8: tracking status

--------------------------------------------
RECORD 0x36 (L=12 bytes)
--------------------------------------------

Bytes  1-4 : c_50
Bytes  5-8 : unknown
Byte    9  : svid
Bytes 10-12: three aditional new bytes. In my (very) short piece of evidence
             they are fixed for all sats.

--------------------------------------------
RECORD 0x38 (L=40 bytes)
-------------------------------------------

Same info as in the G12 messages with three additional bytes added at the
end. The position of the old fields is as follows:

Bytes 1 - 8 : Pseudorange
Bytes 9 -16 : Tow
Bytes 17-20 : Phse_counter
Bytes 21-24 : tracked
Bytes 25-28 : Integer Integrated phase
Bytes 29-32 : c_511500
Bytes 33-34 : delta_f
Bytes 35-36 : signal_Q
Bytes 37    : svid
Bytes 38-40 : new bytes. 


--------------------------------------------
RECORD 0x16 (L=24 bytes)
--------------------------------------------

Bytes 1 - 8 : Pseudorange
Bytes 9 -12 : Delta Pseudorange
Bytes 13-16 : f1
Bytes 17-20 : f2
Bytes 21    : svid
Bytes 22-24 : new bytes



