# Presentation: 028578

##  soc format

**Ron Muellerschoen**
**Jet Propulsion Laboratory**
**California Institute of Technology**

## background

**Designed to transport 1 Hz GPS data with minimal bandwidth over the Open Internet, July 1999**
**“****soc” from socket, which is the interface at the application layer**
**format stable since January, 2000 (when SNR numbers added)**
**follows little-endian byte order (PC order) **
**Transport layer is UDP, so format does not include:**
**sync bits**
**checksum**** or CRC**
**Efficient compression of 5 GPS observable (CA, P1, P2, L1,L2) down to 14 bytes and a nibble**
**1 mm range resolution**
**.02 mm phase resolution**
**Total size per station per 1 Hz epoch:**
**17+21*n_GPS bytes**

## soc attributes

**contains edit flags (or epochs) for cycle slips**
**stand alone time-tag per epoch**
**minimum representation of receiver’s clock solution**
**3 SNR numbers**
**unique site id**
**modulo 12 hour sequence number**
**flags:**
**receiver type**
**GPS health flag**
**simple structure**
**8 byte header**
**9 byte overhead ( timetag, number of gps, etc. )**
**21 data bytes per gps**

## soc format 

**header (8 bytes)**
**unsigned short type****	****max value 2^16 -1**
**type 0 - request for replay of packet, to remote site**
**type 1 - receiver data, from remote site**
**type 3 - broadcast ephemeris, from remote site**
**type 4 - heartbeat, among data collectors**
**type 5 - “will”, among data collectors**
**unsigned short length****	****max value 65535**
**unsigned short seqnum****	****max value 43200 ( 12 hours )**
**unsigned short site id****	****max value 65535**

## type 1 - receiver data

**overhead ( 9 bytes )**
**unsigned int ****	****gps_minutes****	****	****	****	****4 bytes**
**minutes past 6-jan-1980,  good until year 10151****	**
**unsigned short ****	****gps_millisecs****	****	****	****	****2 bytes**
**good for 65.535 seconds**
**short ****	****	****navt****	****	****	****	****	****2 bytes**
**units of 10*meters****	****	****	****	****	**
**range +/-327,680 meters ****	**
**unsigned char ****	****sats_and_status****	****	****	****1 byte**
**bits 7-4 indicates number of sats less 1 (max of 16)**
**bits 1-3 indicate receiver type ( max of 7 )**
**bit 0 reserved for health status**

## type 1 - receiver data

**data bytes ( 21 per gps )**
**unsigned char prn****	**** ****	****	****0 to 255****	****1 byte**
**unsigned short epoch_seq ****	****	****0 to 35999****	****2 bytes**
**10 hour non-ambiguous phase epoch**
**char ca_range[5]****	****	****	****	****	****5 bytes**
**highest 4 bits of first memory location are status bits **
**next 36 bits contain the ca_range in mm.**
**most significant bytes are now in lowest memory location**
**highest bits of byte are most significant**
**think big-endian order**
**2^36 -1 possible values, min 0, max 68,719.476735 kms **
**unsigned char snr (for ca_range)****	****0 to 255****	****1 byte**
**L2_block****	****	****	****	****	****	****6 bytes**
**L1_block****	****	****	****	****	****	****6 bytes**

## L1 & L2 block: overview

**L1 and L2 block ( 6 bytes each  )****	****	****	**
**first 2 bytes and 2 bits for range****	****	****	**
**range units in mms**
** ****2*(2^17 - 1) possible values**
**dynamic range <= 131.071 meters**
**next 6 bits and 2 bytes for phase**
**Phase units in 0.02 mms**
**2*(2^21- 1) possible values**
**dynamic range <= 41.94302 meters**
**extended w/ overflow bits to 83.88606 meters**
**last byte **
**unsigned char snr****	****0 to 255****	****	****	****	**

## L1 & L2 block: compression

**Align L2/L1 to within integer wavelength of P2/P1 w/ editor**
**L2_block first:**
**range_2  = P2 - CA_range **
**	****Like: R + 2.54 I - (R + 1.54 I) ~ I  ****	****                    ****	****<= 131.071 m.**
**phase_2 = L2 - CA_range + 4.09*range_2**
**	****Like: R - 2.54 I - (R + 1.54 I)  + 4.09 I ~ 0****	****     ****	****<= 83.88606 m. **
**L1_block second:**
**range_1 = P1 - CA_range**
**	****Like: R + 1.54 I - (R + 1.54 I) ~ 0 (ca-p code bias)  <= 131.071 m.**
**phase_1 = L1 - CA_range + 3.09*range_2**
**	****Like: R - 1.54 I - (R + 1.54 I) + 3.09 I ~ 0        ****	****<= 83.88606 m.**

## L1 & L2 block: initial point

**Align L1 to integer wavelength of P1 w/ data editor**
**N_L1 = floor( (P1 - L1) / 0.190 + 0.5) at start of phase arc**
**L1 +=   N_L1 * 0.190 for a phase connected arc**
**	****But P1-L1 ~ R+ 1.54 I - ( R - 1.54 I + L1_phase_bias )**
**	****	****	****    ****~  3.09 I - L1_phase_bias**
**Must subtract also from N_L1:**
**	****N_L1 -= floor(3.09*(P2-P1)/0.190 + 0.5)**
**Likewise for L2**
**	**** ****N_L2 = floor( (P2 - L2) / 0.244 + 0.5) at start of phase arc**
**L2 +=   N_L2 * 0.244 for a phase connected arc**
**	****But P2-L2 ~ R+ 2.54 I - ( R - 2.54 I + L2_phase_bias )**
**	****	****	****    ****~  5.09 I - L2_phase_bias**
**Must subtract also from N_L2:**
**	****N_L2 -= floor(5.09*(P2-P1)/0.244 + 0.5)**

## /*    If phase break, then re-adjust the cycle count of the phase to align closer to the range. */
	 if ( (ll[0] == 1) || (ll[1] == 1) || force_break[SiteIDm1][prnj] ) {
		adjustct[SiteIDm1][0][prnj] = ((p1 - l1*LAML1) / LAML1) + 0.5;
            	adjustct[SiteIDm1][1][prnj] = ((p2 - l2*LAML2) / LAML2) + 0.5;
            	adjustct[SiteIDm1][0][prnj] = floor( adjustct[SiteIDm1][0][prnj] );
            	adjustct[SiteIDm1][1][prnj] = floor( adjustct[SiteIDm1][1][prnj] );
           	ll[0] = 1; ll[1] = 1;
            	force_break[SiteIDm1][prnj] = false;
          	/* further adjust for dynamic range, want only bias portion  */
		if ( send_to_socket ) {
            		first_iono = p2 - p1;  /* all in units of meters */
              		 l1_adjust = floor( ALPHA1*first_iono/LAML1 + .5 );
               		l2_adjust = floor( ALPHA2*first_iono/LAML2 + .5 );
               		adjustct[SiteIDm1][0][prnj] -= l1_adjust;
               		adjustct[SiteIDm1][1][prnj] -= l2_adjust;
            	}
        }   
/*   Add integer cycles to L1 and L2 to align closer to the range. */
     l1 += adjustct[SiteIDm1][0][prnj];
     l2 += adjustct[SiteIDm1][1][prnj];

**	**** ****if ( (ll[0] == 1) || (ll[1] == 1) || force_break[SiteIDm1][prnj] ) {**
**	****	****adjustct[SiteIDm1][0][prnj] = ((p1 - l1*LAML1) / LAML1) + 0.5;**
**            ****	****adjustct[SiteIDm1][1][prnj] = ((p2 - l2*LAML2) / LAML2) + 0.5;**
**            ****	****adjustct[SiteIDm1][0][prnj] = floor( adjustct[SiteIDm1][0][prnj] );**
**            ****	****adjustct[SiteIDm1][1][prnj] = floor( adjustct[SiteIDm1][1][prnj] );**
**           ****	****ll[0] = 1; ll[1] = 1;**
**            ****	****force_break[SiteIDm1][prnj] = false;**
**          ****	****/* further adjust for dynamic range, want only bias portion  */**
**	****	****if ( send_to_socket ) {**
**            ****	****	****first_iono = p2 - p1;  /* all in units of meters */**
**              ****	****	**** ****l1_adjust = floor( ALPHA1*first_iono/LAML1 + .5 );**
**               ****	****	****l2_adjust = floor( ALPHA2*first_iono/LAML2 + .5 );**
**               ****	****	****adjustct[SiteIDm1][0][prnj] -= l1_adjust;**
**               ****	****	****adjustct[SiteIDm1][1][prnj] -= l2_adjust;**
**            ****	****}**
**        ****}   **
**/*   Add integer cycles to L1 and L2 to align closer to the range. */**
**     ****l1 += adjustct[SiteIDm1][0][prnj];**
**     ****l2 += adjustct[SiteIDm1][1][prnj];**

**initial point, for example**