1 /* 2 * /src/NTP/REPOSITORY/ntp4-dev/libparse/clk_trimtsip.c,v 4.19 2009/11/01 10:47:49 kardel RELEASE_20091101_A 3 * 4 * clk_trimtsip.c,v 4.19 2009/11/01 10:47:49 kardel RELEASE_20091101_A 5 * 6 * Trimble TSIP support 7 * Thanks to Sven Dietrich for providing test hardware 8 * 9 * Copyright (c) 1995-2009 by Frank Kardel <kardel <AT> ntp.org> 10 * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the author nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 */ 37 38 #ifdef HAVE_CONFIG_H 39 # include <config.h> 40 #endif 41 42 #if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_TRIMTSIP) 43 44 #include "ntp_syslog.h" 45 #include "ntp_types.h" 46 #include "ntp_fp.h" 47 #include "timevalops.h" 48 #include "ntp_calendar.h" 49 #include "ntp_machine.h" 50 #include "ntp_stdlib.h" 51 52 #include "parse.h" 53 54 #ifndef PARSESTREAM 55 # include <stdio.h> 56 #else 57 # include "sys/parsestreams.h" 58 #endif 59 60 #include "ascii.h" 61 #include "binio.h" 62 #include "ieee754io.h" 63 #include "trimble.h" 64 65 /* 66 * Trimble low level TSIP parser / time converter 67 * 68 * The receiver uses a serial message protocol called Trimble Standard 69 * Interface Protocol (it can support others but this driver only supports 70 * TSIP). Messages in this protocol have the following form: 71 * 72 * <DLE><id> ... <data> ... <DLE><ETX> 73 * 74 * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled 75 * on transmission and compressed back to one on reception. Otherwise 76 * the values of data bytes can be anything. The serial interface is RS-422 77 * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits 78 * in total!), and 1 stop bit. The protocol supports byte, integer, single, 79 * and double datatypes. Integers are two bytes, sent most significant first. 80 * Singles are IEEE754 single precision floating point numbers (4 byte) sent 81 * sign & exponent first. Doubles are IEEE754 double precision floating point 82 * numbers (8 byte) sent sign & exponent first. 83 * The receiver supports a large set of messages, only a very small subset of 84 * which is used here. 85 * 86 * From this module the following are recognised: 87 * 88 * ID Description 89 * 90 * 41 GPS Time 91 * 46 Receiver health 92 * 4F UTC correction data (used to get leap second warnings) 93 * 94 * All others are accepted but ignored for time conversion - they are passed up to higher layers. 95 * 96 */ 97 98 static offsets_t trim_offsets = { 0, 1, 2, 3, 4, 5, 6, 7 }; 99 100 struct trimble 101 { 102 u_char t_in_pkt; /* first DLE received */ 103 u_char t_dle; /* subsequent DLE received */ 104 u_short t_week; /* GPS week */ 105 u_short t_weekleap; /* GPS week of next/last week */ 106 u_short t_dayleap; /* day in week */ 107 u_short t_gpsutc; /* GPS - UTC offset */ 108 u_short t_gpsutcleap; /* offset at next/last leap */ 109 u_char t_operable; /* receiver feels OK */ 110 u_char t_mode; /* actual operating mode */ 111 u_char t_leap; /* possible leap warning */ 112 u_char t_utcknown; /* utc offset known */ 113 }; 114 115 #define STATUS_BAD 0 /* BAD or UNINITIALIZED receiver status */ 116 #define STATUS_UNSAFE 1 /* not enough receivers for full precision */ 117 #define STATUS_SYNC 2 /* enough information for good operation */ 118 119 static unsigned long inp_tsip (parse_t *, char, timestamp_t *); 120 static unsigned long cvt_trimtsip (unsigned char *, int, struct format *, clocktime_t *, void *); 121 122 struct clockformat clock_trimtsip = 123 { 124 inp_tsip, /* Trimble TSIP input handler */ 125 cvt_trimtsip, /* Trimble TSIP conversion */ 126 pps_one, /* easy PPS monitoring */ 127 0, /* no configuration data */ 128 "Trimble TSIP", 129 400, /* input buffer */ 130 sizeof(struct trimble) /* private data */ 131 }; 132 133 #define ADDSECOND 0x01 134 #define DELSECOND 0x02 135 136 static unsigned long 137 inp_tsip( 138 parse_t *parseio, 139 char ch, 140 timestamp_t *tstamp 141 ) 142 { 143 struct trimble *t = (struct trimble *)parseio->parse_pdata; 144 145 if (!t) 146 return PARSE_INP_SKIP; /* local data not allocated - sigh! */ 147 148 if (!t->t_in_pkt && ch != DLE) { 149 /* wait for start of packet */ 150 return PARSE_INP_SKIP; 151 } 152 153 if ((parseio->parse_index >= (parseio->parse_dsize - 2)) || 154 (parseio->parse_dtime.parse_msglen >= (sizeof(parseio->parse_dtime.parse_msg) - 2))) 155 { /* OVERFLOW - DROP! */ 156 t->t_in_pkt = t->t_dle = 0; 157 parseio->parse_index = 0; 158 parseio->parse_dtime.parse_msglen = 0; 159 return PARSE_INP_SKIP; 160 } 161 162 switch (ch) { 163 case DLE: 164 if (!t->t_in_pkt) { 165 t->t_dle = 0; 166 t->t_in_pkt = 1; 167 parseio->parse_index = 0; 168 parseio->parse_data[parseio->parse_index++] = ch; 169 parseio->parse_dtime.parse_msglen = 0; 170 parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch; 171 parseio->parse_dtime.parse_stime = *tstamp; /* pick up time stamp at packet start */ 172 } else if (t->t_dle) { 173 /* Double DLE -> insert a DLE */ 174 t->t_dle = 0; 175 parseio->parse_data[parseio->parse_index++] = DLE; 176 parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE; 177 } else 178 t->t_dle = 1; 179 break; 180 181 case ETX: 182 if (t->t_dle) { 183 /* DLE,ETX -> end of packet */ 184 parseio->parse_data[parseio->parse_index++] = DLE; 185 parseio->parse_data[parseio->parse_index] = ch; 186 parseio->parse_ldsize = (u_short) (parseio->parse_index + 1); 187 memcpy(parseio->parse_ldata, parseio->parse_data, parseio->parse_ldsize); 188 parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE; 189 parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch; 190 t->t_in_pkt = t->t_dle = 0; 191 return PARSE_INP_TIME|PARSE_INP_DATA; 192 } 193 /*FALLTHROUGH*/ 194 195 default: /* collect data */ 196 t->t_dle = 0; 197 parseio->parse_data[parseio->parse_index++] = ch; 198 parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch; 199 } 200 201 return PARSE_INP_SKIP; 202 } 203 204 static short 205 getshort( 206 unsigned char *p 207 ) 208 { 209 return (short) get_msb_short(&p); 210 } 211 212 /* 213 * cvt_trimtsip 214 * 215 * convert TSIP type format 216 */ 217 static unsigned long 218 cvt_trimtsip( 219 unsigned char *buffer, 220 int size, 221 struct format *format, 222 clocktime_t *clock_time, 223 void *local 224 ) 225 { 226 register struct trimble *t = (struct trimble *)local; /* get local data space */ 227 #define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */ 228 register u_char cmd; 229 230 clock_time->flags = 0; 231 232 if (!t) { 233 return CVT_NONE; /* local data not allocated - sigh! */ 234 } 235 236 if ((size < 4) || 237 (buffer[0] != DLE) || 238 (buffer[size-1] != ETX) || 239 (buffer[size-2] != DLE)) 240 { 241 printf("TRIMBLE BAD packet, size %d:\n", size); 242 return CVT_NONE; 243 } 244 else 245 { 246 unsigned char *bp; 247 cmd = buffer[1]; 248 249 switch(cmd) 250 { 251 case CMD_RCURTIME: 252 { /* GPS time */ 253 l_fp secs; 254 u_int week = getshort((unsigned char *)&mb(4)); 255 l_fp utcoffset; 256 l_fp gpstime; 257 258 bp = &mb(0); 259 if (fetch_ieee754(&bp, IEEE_SINGLE, &secs, trim_offsets) != IEEE_OK) 260 return CVT_FAIL|CVT_BADFMT; 261 262 if ((secs.l_i <= 0) || 263 (t->t_utcknown == 0)) 264 { 265 clock_time->flags = PARSEB_POWERUP; 266 return CVT_OK; 267 } 268 week = basedate_expand_gpsweek(week); 269 270 /* time OK */ 271 272 /* fetch UTC offset */ 273 bp = &mb(6); 274 if (fetch_ieee754(&bp, IEEE_SINGLE, &utcoffset, trim_offsets) != IEEE_OK) 275 return CVT_FAIL|CVT_BADFMT; 276 277 L_SUB(&secs, &utcoffset); /* adjust GPS time to UTC time */ 278 279 gpstolfp((unsigned short)week, (unsigned short)0, 280 secs.l_ui, &gpstime); 281 282 gpstime.l_uf = secs.l_uf; 283 284 clock_time->utctime = gpstime.l_ui - JAN_1970; 285 286 TSFTOTVU(gpstime.l_uf, clock_time->usecond); 287 288 if (t->t_leap == ADDSECOND) 289 clock_time->flags |= PARSEB_LEAPADD; 290 291 if (t->t_leap == DELSECOND) 292 clock_time->flags |= PARSEB_LEAPDEL; 293 294 switch (t->t_operable) 295 { 296 case STATUS_SYNC: 297 clock_time->flags &= ~(PARSEB_POWERUP|PARSEB_NOSYNC); 298 break; 299 300 case STATUS_UNSAFE: 301 clock_time->flags |= PARSEB_NOSYNC; 302 break; 303 304 case STATUS_BAD: 305 clock_time->flags |= PARSEB_NOSYNC|PARSEB_POWERUP; 306 break; 307 } 308 309 if (t->t_mode == 0) 310 clock_time->flags |= PARSEB_POSITION; 311 312 clock_time->flags |= PARSEB_S_LEAP|PARSEB_S_POSITION; 313 314 return CVT_OK; 315 316 } /* case 0x41 */ 317 318 case CMD_RRECVHEALTH: 319 { 320 /* TRIMBLE health */ 321 u_char status = mb(0); 322 323 switch (status) 324 { 325 case 0x00: /* position fixes */ 326 t->t_operable = STATUS_SYNC; 327 break; 328 329 case 0x09: /* 1 satellite */ 330 case 0x0A: /* 2 satellites */ 331 case 0x0B: /* 3 satellites */ 332 t->t_operable = STATUS_UNSAFE; 333 break; 334 335 default: 336 t->t_operable = STATUS_BAD; 337 break; 338 } 339 t->t_mode = status; 340 } 341 break; 342 343 case CMD_RUTCPARAM: 344 { 345 l_fp t0t; 346 unsigned char *lbp; 347 348 /* UTC correction data - derive a leap warning */ 349 int tls = t->t_gpsutc = (u_short) getshort((unsigned char *)&mb(12)); /* current leap correction (GPS-UTC) */ 350 int tlsf = t->t_gpsutcleap = (u_short) getshort((unsigned char *)&mb(24)); /* new leap correction */ 351 352 t->t_weekleap = basedate_expand_gpsweek( 353 (u_short) getshort((unsigned char *)&mb(20))); /* week no of leap correction */ 354 355 t->t_dayleap = (u_short) getshort((unsigned char *)&mb(22)); /* day in week of leap correction */ 356 t->t_week = basedate_expand_gpsweek( 357 (u_short) getshort((unsigned char *)&mb(18))); /* current week no */ 358 359 lbp = (unsigned char *)&mb(14); /* last update time */ 360 if (fetch_ieee754(&lbp, IEEE_SINGLE, &t0t, trim_offsets) != IEEE_OK) 361 return CVT_FAIL|CVT_BADFMT; 362 363 t->t_utcknown = t0t.l_ui != 0; 364 365 if ((t->t_utcknown) && /* got UTC information */ 366 (tlsf != tls) && /* something will change */ 367 ((t->t_weekleap - t->t_week) < 5)) /* and close in the future */ 368 { 369 /* generate a leap warning */ 370 if (tlsf > tls) 371 t->t_leap = ADDSECOND; 372 else 373 t->t_leap = DELSECOND; 374 } 375 else 376 { 377 t->t_leap = 0; 378 } 379 } 380 break; 381 382 default: 383 /* it's validly formed, but we don't care about it! */ 384 break; 385 } 386 } 387 return CVT_SKIP; 388 } 389 390 #else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_TRIMTSIP && !PARSESTREAM) */ 391 NONEMPTY_TRANSLATION_UNIT 392 #endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_TRIMTSIP && !PARSESTREAM) */ 393 394 /* 395 * History: 396 * 397 * clk_trimtsip.c,v 398 * Revision 4.19 2009/11/01 10:47:49 kardel 399 * de-P() 400 * 401 * Revision 4.18 2009/11/01 08:46:46 kardel 402 * clarify case FALLTHROUGH 403 * 404 * Revision 4.17 2005/04/16 17:32:10 kardel 405 * update copyright 406 * 407 * Revision 4.16 2004/11/14 15:29:41 kardel 408 * support PPSAPI, upgrade Copyright to Berkeley style 409 * 410 * Revision 4.13 1999/11/28 09:13:51 kardel 411 * RECON_4_0_98F 412 * 413 * Revision 4.12 1999/02/28 13:00:08 kardel 414 * *** empty log message *** 415 * 416 * Revision 4.11 1999/02/28 11:47:54 kardel 417 * (struct trimble): new member t_utcknown 418 * (cvt_trimtsip): fixed status monitoring, bad receiver states are 419 * now recognized 420 * 421 * Revision 4.10 1999/02/27 15:57:15 kardel 422 * use mmemcpy instead of bcopy 423 * 424 * Revision 4.9 1999/02/21 12:17:42 kardel 425 * 4.91f reconcilation 426 * 427 * Revision 4.8 1998/11/15 20:27:58 kardel 428 * Release 4.0.73e13 reconcilation 429 * 430 * Revision 4.7 1998/08/16 18:49:20 kardel 431 * (cvt_trimtsip): initial kernel capable version (no more floats) 432 * (clock_trimtsip =): new format name 433 * 434 * Revision 4.6 1998/08/09 22:26:05 kardel 435 * Trimble TSIP support 436 * 437 * Revision 4.5 1998/08/02 10:37:05 kardel 438 * working TSIP parser 439 * 440 * Revision 4.4 1998/06/28 16:50:40 kardel 441 * (getflt): fixed ENDIAN issue 442 * (getdbl): fixed ENDIAN issue 443 * (getint): use get_msb_short() 444 * (cvt_trimtsip): use gpstolfp() for conversion 445 * 446 * Revision 4.3 1998/06/13 12:07:31 kardel 447 * fix SYSV clock name clash 448 * 449 * Revision 4.2 1998/06/12 15:22:30 kardel 450 * fix prototypes 451 * 452 * Revision 4.1 1998/05/24 09:39:54 kardel 453 * implementation of the new IO handling model 454 * 455 * Revision 4.0 1998/04/10 19:45:32 kardel 456 * Start 4.0 release version numbering 457 * 458 * from V3 1.8 loginfo deleted 1998/04/11 kardel 459 */ 460