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