1 /* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * refclock_oncore.c 10 * 11 * Driver for some of the various the Motorola Oncore GPS receivers. 12 * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T 13 * The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate 14 * than the others. 15 * The receivers without position hold (GT, GT+) will be less accurate. 16 * 17 * Tested with: 18 * 19 * (UT) (VP) 20 * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC. 21 * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P 22 * SOFTWARE VER # 2 SOFTWARE VER # 8 23 * SOFTWARE REV # 2 SOFTWARE REV # 8 24 * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996 25 * MODEL # R1121N1114 MODEL # B4121P1155 26 * HWDR P/N # 1 HDWR P/N # _ 27 * SERIAL # R0010A SERIAL # SSG0226478 28 * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02 29 * OPTIONS LIST IB 30 * 31 * (Basic) (M12) 32 * COPYRIGHT 1991-1994 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC. 33 * SFTW P/N # 98-P39949M SFTW P/N # 61-G10002A 34 * SOFTWARE VER # 5 SOFTWARE VER # 1 35 * SOFTWARE REV # 0 SOFTWARE REV # 3 36 * SOFTWARE DATE 20 JAN 1994 SOFTWARE DATE Mar 13 2000 37 * MODEL # A11121P116 MODEL # P143T12NR1 38 * HDWR P/N # _ HWDR P/N # 1 39 * SERIAL # SSG0049809 SERIAL # P003UD 40 * MANUFACTUR DATE 417AMA199 MANUFACTUR DATE 0C27 41 * OPTIONS LIST AB 42 * 43 * -------------------------------------------------------------------------- 44 * This code uses the two devices 45 * /dev/oncore.serial.n 46 * /dev/oncore.pps.n 47 * which may be linked to the same device. 48 * and can read initialization data from the file 49 * /etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where 50 * n or N are the unit number, viz 127.127.30.N. 51 * -------------------------------------------------------------------------- 52 * Reg.Clemens <reg@dwf.com> Sep98. 53 * Original code written for FreeBSD. 54 * With these mods it works on FreeBSD, SunOS, Solaris and Linux 55 * (SunOS 4.1.3 + ppsclock) 56 * (Solaris7 + MU4) 57 * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later). 58 * 59 * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the 60 * state machine state) are printed to CLOCKSTATS if that file is enabled 61 * in /etc/ntp.conf. 62 * 63 * -------------------------------------------------------------------------- 64 * 65 * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13) 66 * doing an average of 10000 valid 2D and 3D fixes is what the automatic 67 * site survey mode does. Looking at the output from the receiver 68 * it seems like it is only using 3D fixes. 69 * When we do it ourselves, take 10000 3D fixes. 70 */ 71 72 #define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */ 73 74 /* 75 * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a 76 * "STATUS" line in the oncore config file, which contains the most recent 77 * copy of all types of messages we recognize. This file can be mmap(2)'ed 78 * by monitoring and statistics programs. 79 * 80 * See separate HTML documentation for this option. 81 */ 82 83 #ifdef HAVE_CONFIG_H 84 #include <config.h> 85 #endif 86 87 #if defined(REFCLOCK) && defined(CLOCK_ONCORE) && defined(HAVE_PPSAPI) 88 89 #include "ntpd.h" 90 #include "ntp_io.h" 91 #include "ntp_unixtime.h" 92 #include "ntp_refclock.h" 93 #include "ntp_stdlib.h" 94 95 #include <stdio.h> 96 #include <ctype.h> 97 #include <sys/stat.h> 98 #ifdef ONCORE_SHMEM_STATUS 99 # ifdef HAVE_SYS_MMAN_H 100 # include <sys/mman.h> 101 # ifndef MAP_FAILED 102 # define MAP_FAILED ((u_char *) -1) 103 # endif /* not MAP_FAILED */ 104 # endif /* HAVE_SYS_MMAN_H */ 105 #endif /* ONCORE_SHMEM_STATUS */ 106 107 #ifdef HAVE_PPSAPI 108 # ifdef HAVE_TIMEPPS_H 109 # include <timepps.h> 110 # else 111 # ifdef HAVE_SYS_TIMEPPS_H 112 # include <sys/timepps.h> 113 # endif 114 # endif 115 #endif 116 117 #ifdef HAVE_SYS_SIO_H 118 # include <sys/sio.h> 119 #endif 120 121 #ifdef HAVE_SYS_TERMIOS_H 122 # include <sys/termios.h> 123 #endif 124 125 #ifdef HAVE_SYS_PPSCLOCK_H 126 # include <sys/ppsclock.h> 127 #endif 128 129 #ifndef HAVE_STRUCT_PPSCLOCKEV 130 struct ppsclockev { 131 # ifdef HAVE_STRUCT_TIMESPEC 132 struct timespec tv; 133 # else 134 struct timeval tv; 135 # endif 136 u_int serial; 137 }; 138 #endif /* not HAVE_STRUCT_PPSCLOCKEV */ 139 140 enum receive_state { 141 ONCORE_NO_IDEA, 142 ONCORE_CHECK_ID, 143 ONCORE_CHECK_CHAN, 144 ONCORE_HAVE_CHAN, 145 ONCORE_RESET_SENT, 146 ONCORE_TEST_SENT, 147 ONCORE_INIT, 148 ONCORE_ALMANAC, 149 ONCORE_RUN 150 }; 151 152 enum site_survey_state { 153 ONCORE_SS_UNKNOWN, 154 ONCORE_SS_TESTING, 155 ONCORE_SS_HW, 156 ONCORE_SS_SW, 157 ONCORE_SS_DONE 158 }; 159 160 enum antenna_state { 161 ONCORE_ANTENNA_UNKNOWN = -1, 162 ONCORE_ANTENNA_OK = 0, 163 ONCORE_ANTENNA_OC = 1, 164 ONCORE_ANTENNA_UC = 2, 165 ONCORE_ANTENNA_NV = 3 166 }; 167 168 /* Model Name, derived from the @@Cj message. 169 * Used to initialize some variables. 170 */ 171 172 enum oncore_model { 173 ONCORE_BASIC, 174 ONCORE_PVT6, 175 ONCORE_VP, 176 ONCORE_UT, 177 ONCORE_UTPLUS, 178 ONCORE_GT, 179 ONCORE_GTPLUS, 180 ONCORE_SL, 181 ONCORE_M12, 182 ONCORE_UNKNOWN 183 }; 184 185 /* the bits that describe these properties are in the same place 186 * on the VP/UT, but have moved on the M12. As such we extract 187 * them, and use them from this struct. 188 * 189 */ 190 191 struct RSM { 192 u_char posn0D; 193 u_char posn2D; 194 u_char posn3D; 195 u_char bad_almanac; 196 u_char bad_fix; 197 }; 198 199 /* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to 200 * see what mode it is in. The bits on the M12 are multiplexed with 201 * other messages, so we have to 'keep' the last known mode here. 202 */ 203 204 enum posn_mode { 205 MODE_UNKNOWN, 206 MODE_0D, 207 MODE_2D, 208 MODE_3D 209 }; 210 211 struct instance { 212 int unit; /* 127.127.30.unit */ 213 struct refclockproc *pp; 214 struct peer *peer; 215 216 int ttyfd; /* TTY file descriptor */ 217 int ppsfd; /* PPS file descriptor */ 218 int shmemfd; /* Status shm descriptor */ 219 #ifdef HAVE_PPSAPI 220 pps_handle_t pps_h; 221 pps_params_t pps_p; 222 #endif 223 enum receive_state o_state; /* Receive state */ 224 enum posn_mode mode; /* 0D, 2D, 3D */ 225 enum site_survey_state site_survey; /* Site Survey state */ 226 enum antenna_state ant_state; /* antenna state */ 227 228 int Bj_day; 229 230 u_long delay; /* ns */ 231 long offset; /* ns */ 232 233 u_char *shmem; 234 char *shmem_fname; 235 u_int shmem_Cb; 236 u_int shmem_Ba; 237 u_int shmem_Ea; 238 u_int shmem_Ha; 239 u_char shmem_reset; 240 u_char shmem_Posn; 241 u_char shmem_bad_Ea; 242 u_char almanac_from_shmem; 243 244 double ss_lat; 245 double ss_long; 246 double ss_ht; 247 double dH; 248 int ss_count; 249 u_char posn_set; 250 251 enum oncore_model model; 252 u_int version; 253 u_int revision; 254 255 u_char chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */ 256 s_char traim; /* do we have traim? yes UT/VP, no BASIC, GT, M12+T, -1 unknown, 0 no, +1 yes */ 257 258 /* the following 7 are all timing counters */ 259 u_char traim_delay; /* seconds counter, waiting for reply */ 260 u_char count; /* cycles thru Ea before starting */ 261 u_char count1; /* cycles thru Ea after SS_TESTING, waiting for SS_HW */ 262 u_char count2; /* cycles thru Ea after count, to check for @@Ea */ 263 u_char count3; /* cycles thru Ea checking for # channels */ 264 u_char count4; /* cycles thru leap after Gj to issue Bj */ 265 u_char pollcnt; 266 u_char timeout; /* count to retry Cj after Fa self-test */ 267 268 struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */ 269 u_char printed; 270 u_char polled; 271 u_long ev_serial; 272 int Rcvptr; 273 u_char Rcvbuf[500]; 274 u_char BEHa[160]; /* Ba, Ea or Ha */ 275 u_char BEHn[80]; /* Bn , En , or Hn */ 276 u_char Cj[300]; 277 u_char Ag; /* Satellite mask angle */ 278 u_char saw_At; 279 u_char saw_Ay; 280 u_char saw_Az; 281 s_char saw_Gj; 282 u_char have_dH; 283 u_char init_type; 284 s_char saw_tooth; 285 s_char chan_in; /* chan number from INPUT, will always use it */ 286 u_char chan_id; /* chan number determined from part number */ 287 u_char chan_ck; /* chan number determined by sending commands to hardware */ 288 s_char traim_in; /* TRAIM from INPUT, will always use it */ 289 s_char traim_id; /* TRAIM determined from part number */ 290 u_char traim_ck; /* TRAIM determined by sending commands to hardware */ 291 u_char once; /* one pass code at top of BaEaHa */ 292 s_char assert; 293 u_char hardpps; 294 }; 295 296 #define rcvbuf instance->Rcvbuf 297 #define rcvptr instance->Rcvptr 298 299 static int oncore_start P((int, struct peer *)); 300 static void oncore_control P((int, struct refclockstat *, struct refclockstat *, struct peer *)); 301 static void oncore_poll P((int, struct peer *)); 302 static void oncore_shutdown P((int, struct peer *)); 303 static void oncore_consume P((struct instance *)); 304 static void oncore_read_config P((struct instance *)); 305 static void oncore_receive P((struct recvbuf *)); 306 static int oncore_ppsapi P((struct instance *)); 307 static void oncore_get_timestamp P((struct instance *, long, long)); 308 static void oncore_init_shmem P((struct instance *)); 309 310 static void oncore_antenna_report P((struct instance *, enum antenna_state)); 311 static void oncore_chan_test P((struct instance *)); 312 static void oncore_check_almanac P((struct instance *)); 313 static void oncore_check_antenna P((struct instance *)); 314 static void oncore_check_leap_sec P((struct instance *)); 315 static int oncore_checksum_ok P((u_char *, int)); 316 static void oncore_compute_dH P((struct instance *)); 317 static void oncore_load_almanac P((struct instance *)); 318 static void oncore_print_Cb P((struct instance *, u_char *)); 319 /* static void oncore_print_array P((u_char *, int)); */ 320 static void oncore_print_posn P((struct instance *)); 321 static void oncore_sendmsg P((int, u_char *, size_t)); 322 static void oncore_set_posn P((struct instance *)); 323 static void oncore_set_traim P((struct instance *)); 324 static void oncore_shmem_get_3D P((struct instance *)); 325 static void oncore_ss P((struct instance *)); 326 static int oncore_wait_almanac P((struct instance *)); 327 328 static void oncore_msg_any P((struct instance *, u_char *, size_t, int)); 329 static void oncore_msg_Adef P((struct instance *, u_char *, size_t)); 330 static void oncore_msg_Ag P((struct instance *, u_char *, size_t)); 331 static void oncore_msg_As P((struct instance *, u_char *, size_t)); 332 static void oncore_msg_At P((struct instance *, u_char *, size_t)); 333 static void oncore_msg_Ay P((struct instance *, u_char *, size_t)); 334 static void oncore_msg_Az P((struct instance *, u_char *, size_t)); 335 static void oncore_msg_BaEaHa P((struct instance *, u_char *, size_t)); 336 static void oncore_msg_Bd P((struct instance *, u_char *, size_t)); 337 static void oncore_msg_Bj P((struct instance *, u_char *, size_t)); 338 static void oncore_msg_BnEnHn P((struct instance *, u_char *, size_t)); 339 static void oncore_msg_CaFaIa P((struct instance *, u_char *, size_t)); 340 static void oncore_msg_Cb P((struct instance *, u_char *, size_t)); 341 static void oncore_msg_Cf P((struct instance *, u_char *, size_t)); 342 static void oncore_msg_Cj P((struct instance *, u_char *, size_t)); 343 static void oncore_msg_Cj_id P((struct instance *, u_char *, size_t)); 344 static void oncore_msg_Cj_init P((struct instance *, u_char *, size_t)); 345 static void oncore_msg_Ga P((struct instance *, u_char *, size_t)); 346 static void oncore_msg_Gb P((struct instance *, u_char *, size_t)); 347 static void oncore_msg_Gd P((struct instance *, u_char *, size_t)); 348 static void oncore_msg_Gj P((struct instance *, u_char *, size_t)); 349 static void oncore_msg_Sz P((struct instance *, u_char *, size_t)); 350 351 struct refclock refclock_oncore = { 352 oncore_start, /* start up driver */ 353 oncore_shutdown, /* shut down driver */ 354 oncore_poll, /* transmit poll message */ 355 oncore_control, /* fudge (flag) control messages */ 356 noentry, /* not used */ 357 noentry, /* not used */ 358 NOFLAGS /* not used */ 359 }; 360 361 /* 362 * Understanding the next bit here is not easy unless you have a manual 363 * for the the various Oncore Models. 364 */ 365 366 static struct msg_desc { 367 const char flag[3]; 368 const int len; 369 void (*handler) P((struct instance *, u_char *, size_t)); 370 const char *fmt; 371 int shmem; 372 } oncore_messages[] = { 373 /* Ea and En first since they're most common */ 374 { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" }, 375 { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC" }, 376 { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC" }, 377 { "Bn", 59, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC" }, 378 { "En", 69, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" }, 379 { "Hn", 78, oncore_msg_BnEnHn, "" }, 380 { "Ab", 10, 0, "" }, 381 { "Ac", 11, 0, "" }, 382 { "Ad", 11, oncore_msg_Adef, "" }, 383 { "Ae", 11, oncore_msg_Adef, "" }, 384 { "Af", 15, oncore_msg_Adef, "" }, 385 { "Ag", 8, oncore_msg_Ag, "" }, /* Satellite mask angle */ 386 { "As", 20, oncore_msg_As, "" }, 387 { "At", 8, oncore_msg_At, "" }, 388 { "Au", 12, 0, "" }, 389 { "Av", 8, 0, "" }, 390 { "Aw", 8, 0, "" }, 391 { "Ay", 11, oncore_msg_Ay, "" }, 392 { "Az", 11, oncore_msg_Az, "" }, 393 { "AB", 8, 0, "" }, 394 { "Bb", 92, 0, "" }, 395 { "Bd", 23, oncore_msg_Bd, "" }, 396 { "Bj", 8, oncore_msg_Bj, "" }, 397 { "Ca", 9, oncore_msg_CaFaIa, "" }, 398 { "Cb", 33, oncore_msg_Cb, "" }, 399 { "Cf", 7, oncore_msg_Cf, "" }, 400 { "Cg", 8, 0, "" }, 401 { "Ch", 9, 0, "" }, 402 { "Cj", 294, oncore_msg_Cj, "" }, 403 { "Ek", 71, 0, "" }, 404 { "Fa", 9, oncore_msg_CaFaIa, "" }, 405 { "Ga", 20, oncore_msg_Ga, "" }, 406 { "Gb", 17, oncore_msg_Gb, "" }, 407 { "Gc", 8, 0, "" }, 408 { "Gd", 8, oncore_msg_Gd, "" }, 409 { "Ge", 8, 0, "" }, 410 { "Gj", 21, oncore_msg_Gj, "" }, 411 { "Ia", 10, oncore_msg_CaFaIa, "" }, 412 { "Sz", 8, oncore_msg_Sz, "" }, 413 { {0}, 7, 0, "" } 414 }; 415 416 417 static u_char oncore_cmd_Aa[] = { 'A', 'a', 0, 0, 0 }; /* 6/8 Time of Day */ 418 static u_char oncore_cmd_Ab[] = { 'A', 'b', 0, 0, 0 }; /* 6/8 GMT Correction */ 419 static u_char oncore_cmd_AB[] = { 'A', 'B', 4 }; /* VP Application Type: Static */ 420 static u_char oncore_cmd_Ac[] = { 'A', 'c', 0, 0, 0, 0 }; /* 6/8 Date */ 421 static u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; /* 6/8 Latitude */ 422 static u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; /* 6/8 Longitude */ 423 static u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; /* 6/8 Height */ 424 static u_char oncore_cmd_Ag[] = { 'A', 'g', 0 }; /* 6/8/12 Satellite Mask Angle */ 425 static u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff }; /* 6/8/12 Satellite Mask Angle: read */ 426 static u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 6/8/12 Posn Hold Parameters */ 427 static u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff, /* 6/8/12 Posn Hold Readback */ 428 0x7f,0xff,0xff,0xff, /* on UT+ this doesnt work with 0xff */ 429 0x7f,0xff,0xff,0xff, 0xff }; /* but does work with 0x7f (sigh). */ 430 static u_char oncore_cmd_At0[] = { 'A', 't', 0 }; /* 6/8 Posn Hold: off */ 431 static u_char oncore_cmd_At1[] = { 'A', 't', 1 }; /* 6/8 Posn Hold: on */ 432 static u_char oncore_cmd_At2[] = { 'A', 't', 2 }; /* 6/8 Posn Hold: Start Site Survey */ 433 static u_char oncore_cmd_Atx[] = { 'A', 't', 0xff }; /* 6/8 Posn Hold: Read Back */ 434 static u_char oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0, 0 }; /* GT/M12 Altitude Hold Ht. */ 435 static u_char oncore_cmd_Av0[] = { 'A', 'v', 0 }; /* VP/GT Altitude Hold: off */ 436 static u_char oncore_cmd_Av1[] = { 'A', 'v', 1 }; /* VP/GT Altitude Hold: on */ 437 static u_char oncore_cmd_Aw[] = { 'A', 'w', 1 }; /* 6/8/12 UTC/GPS time selection */ 438 static u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; /* Timing 1PPS time offset: set */ 439 static u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; /* Timing 1PPS time offset: Read */ 440 static u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; /* 6/8UT/12 1PPS Cable Delay: set */ 441 static u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; /* 6/8UT/12 1PPS Cable Delay: Read */ 442 static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 }; /* 6 Position/Data/Status: off */ 443 static u_char oncore_cmd_Ba[] = { 'B', 'a', 1 }; /* 6 Position/Data/Status: on */ 444 static u_char oncore_cmd_Bb[] = { 'B', 'b', 1 }; /* 6/8/12 Visible Satellites */ 445 static u_char oncore_cmd_Bd[] = { 'B', 'd', 1 }; /* 6/8/12? Almanac Status Msg. */ 446 static u_char oncore_cmd_Be[] = { 'B', 'e', 1 }; /* 6/8/12 Request Almanac Data */ 447 static u_char oncore_cmd_Bj[] = { 'B', 'j', 0 }; /* 6/8 Leap Second Pending */ 448 static u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim on */ 449 static u_char oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on traim on */ 450 static u_char oncore_cmd_Bnx[] = { 'B', 'n', 1, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on traim off */ 451 static u_char oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Self Test */ 452 static u_char oncore_cmd_Cf[] = { 'C', 'f' }; /* 6/8/12 Set to Defaults */ 453 static u_char oncore_cmd_Cg[] = { 'C', 'g', 1 }; /* VP Posn Fix/Idle Mode */ 454 static u_char oncore_cmd_Cj[] = { 'C', 'j' }; /* 6/8/12 Receiver ID */ 455 static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 }; /* 8 Position/Data/Status: off */ 456 static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 }; /* 8 Position/Data/Status: on */ 457 static u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ /* 8 Posn/Status/Data - extension */ 458 static u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim on */ 459 static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on traim on */ 460 static u_char oncore_cmd_Enx[] = { 'E', 'n', 1, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on traim off */ 461 static u_char oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Self Test */ 462 static u_char oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 12 Position Set */ 463 static u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff, /* 12 Position Set: Read */ 464 0xff, 0xff, 0xff, 0xff, /* */ 465 0xff, 0xff, 0xff, 0xff, 0xff }; /* */ 466 static u_char oncore_cmd_Gb[] = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* 12 set Date/Time */ 467 static u_char oncore_cmd_Gc[] = { 'G', 'c', 1 }; /* 12 PPS Control: On Cont */ 468 static u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 12 Position Control: 3D (no hold) */ 469 static u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 12 Position Control: 0D (3D hold) */ 470 static u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 12 Position Control: 2D (Alt Hold) */ 471 static u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 }; /* 12 Position Coltrol: Start Site Survey */ 472 static u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 }; /* M12+T TRAIM: off */ 473 static u_char oncore_cmd_Ge[] = { 'G', 'e', 1 }; /* M12+T TRAIM: on */ 474 static u_char oncore_cmd_Gj[] = { 'G', 'j' }; /* 8?/12 Leap Second Pending */ 475 static u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 }; /* 12 Position/Data/Status: off */ 476 static u_char oncore_cmd_Ha[] = { 'H', 'a', 1 }; /* 12 Position/Data/Status: on */ 477 static u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 }; /* 12 TRAIM Status: off */ 478 static u_char oncore_cmd_Hn[] = { 'H', 'n', 1 }; /* 12 TRAIM Status: on */ 479 static u_char oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Self Test */ 480 481 /* it appears that as of 1997/1998, the UT had As,At, but not Au,Av 482 * the GT had Au,Av, but not As,At 483 * This was as of v2.0 of both firmware sets. possibly 1.3 for UT. 484 * Bj in UT at v1.3 485 * dont see Bd in UT/GT thru 1999 486 * Gj in UT as of 3.0, 1999 , Bj as of 1.3 487 */ 488 489 static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jly", 490 "Aug", "Sep", "Oct", "Nov", "Dec" }; 491 492 #define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */ 493 #define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */ 494 #define INIT_FILE "/etc/ntp.oncore" /* optional init file */ 495 496 #define SPEED B9600 /* Oncore Binary speed (9600 bps) */ 497 498 /* 499 * Assemble and disassemble 32bit signed quantities from a buffer. 500 * 501 */ 502 503 /* to buffer, int w, u_char *buf */ 504 #define w32_buf(buf,w) { u_int i_tmp; \ 505 i_tmp = (w<0) ? (~(-w)+1) : (w); \ 506 (buf)[0] = (i_tmp >> 24) & 0xff; \ 507 (buf)[1] = (i_tmp >> 16) & 0xff; \ 508 (buf)[2] = (i_tmp >> 8) & 0xff; \ 509 (buf)[3] = (i_tmp ) & 0xff; \ 510 } 511 512 #define w32(buf) (((buf)[0]&0xff) << 24 | \ 513 ((buf)[1]&0xff) << 16 | \ 514 ((buf)[2]&0xff) << 8 | \ 515 ((buf)[3]&0xff) ) 516 517 /* from buffer, char *buf, result to an int */ 518 #define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf)) 519 520 521 /* 522 * oncore_start - initialize data for processing 523 */ 524 525 static int 526 oncore_start( 527 int unit, 528 struct peer *peer 529 ) 530 { 531 register struct instance *instance; 532 struct refclockproc *pp; 533 int fd1, fd2; 534 char device1[30], device2[30]; 535 const char *cp; 536 struct stat stat1, stat2; 537 538 /* OPEN DEVICES */ 539 /* opening different devices for fd1 and fd2 presents no problems */ 540 /* opening the SAME device twice, seems to be OS dependent. 541 (a) on Linux (no streams) no problem 542 (b) on SunOS (and possibly Solaris, untested), (streams) 543 never see the line discipline. 544 Since things ALWAYS work if we only open the device once, we check 545 to see if the two devices are in fact the same, then proceed to 546 do one open or two. 547 */ 548 549 (void)sprintf(device1, DEVICE1, unit); 550 (void)sprintf(device2, DEVICE2, unit); 551 552 if (stat(device1, &stat1)) { 553 perror("ONCORE: stat fd1"); 554 exit(1); 555 } 556 557 if (stat(device2, &stat2)) { 558 perror("ONCORE: stat fd2"); 559 exit(1); 560 } 561 562 /* create instance structure for this unit */ 563 564 if (!(instance = (struct instance *) malloc(sizeof *instance))) { 565 perror("malloc"); 566 return (0); 567 } 568 memset((char *) instance, 0, sizeof *instance); 569 570 if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) { 571 /* same device here */ 572 if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW 573 #if !defined(HAVE_PPSAPI) && !defined(TIOCDCDTIMESTAMP) 574 | LDISC_PPS 575 #endif 576 ))) { 577 perror("ONCORE: fd1"); 578 exit(1); 579 } 580 fd2 = fd1; 581 } else { /* different devices here */ 582 if (!(fd1=refclock_open(device1, SPEED, LDISC_RAW))) { 583 perror("ONCORE: fd1"); 584 exit(1); 585 } 586 if ((fd2=open(device2, O_RDWR)) < 0) { 587 perror("ONCORE: fd2"); 588 exit(1); 589 } 590 } 591 592 /* initialize miscellaneous variables */ 593 594 pp = peer->procptr; 595 pp->unitptr = (caddr_t) instance; 596 instance->pp = pp; 597 instance->unit = unit; 598 instance->peer = peer; 599 instance->assert = 1; 600 instance->once = 1; 601 602 cp = "ONCORE DRIVER -- CONFIGURING"; 603 record_clock_stats(&(instance->peer->srcadr), cp); 604 605 instance->o_state = ONCORE_NO_IDEA; 606 cp = "state = ONCORE_NO_IDEA"; 607 record_clock_stats(&(instance->peer->srcadr), cp); 608 609 instance->ttyfd = fd1; 610 instance->ppsfd = fd2; 611 612 instance->Bj_day = -1; 613 instance->traim = -1; 614 instance->traim_in = -1; 615 instance->chan_in = -1; 616 instance->model = ONCORE_UNKNOWN; 617 instance->mode = MODE_UNKNOWN; 618 instance->site_survey = ONCORE_SS_UNKNOWN; 619 instance->Ag = 0xff; /* Satellite mask angle, unset by user */ 620 instance->ant_state = ONCORE_ANTENNA_UNKNOWN; 621 622 peer->precision = -26; 623 peer->minpoll = 4; 624 peer->maxpoll = 4; 625 pp->clockdesc = "Motorola Oncore GPS Receiver"; 626 memcpy((char *)&pp->refid, "GPS\0", (size_t) 4); 627 628 /* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */ 629 630 oncore_read_config(instance); 631 632 #ifdef HAVE_PPSAPI 633 if (time_pps_create(fd2, &instance->pps_h) < 0) { 634 perror("time_pps_create"); 635 return(0); 636 } 637 638 if (instance->assert) 639 cp = "Initializing timing to Assert."; 640 else 641 cp = "Initializing timing to Clear."; 642 record_clock_stats(&(instance->peer->srcadr), cp); 643 644 if (instance->hardpps) { 645 cp = "HARDPPS Set."; 646 record_clock_stats(&(instance->peer->srcadr), cp); 647 } 648 649 if (!oncore_ppsapi(instance)) 650 return(0); 651 #endif 652 653 pp->io.clock_recv = oncore_receive; 654 pp->io.srcclock = (caddr_t)peer; 655 pp->io.datalen = 0; 656 pp->io.fd = fd1; 657 if (!io_addclock(&pp->io)) { 658 perror("io_addclock"); 659 (void) close(fd1); 660 free(instance); 661 return (0); 662 } 663 664 #ifdef ONCORE_SHMEM_STATUS 665 /* 666 * Before starting ONCORE, lets setup SHMEM 667 * This will include merging an old SHMEM into the new one if 668 * an old one is found. 669 */ 670 671 oncore_init_shmem(instance); 672 #endif 673 674 /* 675 * This will return the Model of the Oncore receiver. 676 * and start the Initialization loop in oncore_msg_Cj. 677 */ 678 679 instance->o_state = ONCORE_CHECK_ID; 680 cp = "state = ONCORE_CHECK_ID"; 681 record_clock_stats(&(instance->peer->srcadr), cp); 682 683 instance->timeout = 4; 684 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */ 685 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 686 687 instance->pollcnt = 2; 688 return (1); 689 } 690 691 692 /* 693 * Fudge control (get Flag2 and Flag3, not available at oncore_start time. 694 */ 695 696 static void 697 oncore_control( 698 int unit, /* unit (not used) */ 699 struct refclockstat *in, /* input parameters (not used) */ 700 struct refclockstat *out, /* output parameters (not used) */ 701 struct peer *peer /* peer structure pointer */ 702 ) 703 { 704 char *cp; 705 struct refclockproc *pp; 706 struct instance *instance; 707 708 pp = peer->procptr; 709 instance = (struct instance *) pp->unitptr; 710 711 instance->assert = !(pp->sloppyclockflag & CLK_FLAG2); 712 instance->hardpps = pp->sloppyclockflag & CLK_FLAG3; 713 714 if (instance->assert) 715 cp = "Resetting timing to Assert."; 716 else 717 cp = "Resetting timing to Clear."; 718 record_clock_stats(&(instance->peer->srcadr), cp); 719 720 if (instance->hardpps) { 721 cp = "HARDPPS Set."; 722 record_clock_stats(&(instance->peer->srcadr), cp); 723 } 724 725 (void) oncore_ppsapi(instance); 726 } 727 728 729 730 /* 731 * oncore_shutdown - shut down the clock 732 */ 733 734 static void 735 oncore_shutdown( 736 int unit, 737 struct peer *peer 738 ) 739 { 740 register struct instance *instance; 741 struct refclockproc *pp; 742 743 pp = peer->procptr; 744 instance = (struct instance *) pp->unitptr; 745 746 io_closeclock(&pp->io); 747 748 close(instance->ttyfd); 749 close(instance->ppsfd); 750 if (instance->shmemfd) 751 close(instance->shmemfd); 752 free(instance); 753 } 754 755 756 757 /* 758 * oncore_poll - called by the transmit procedure 759 */ 760 761 static void 762 oncore_poll( 763 int unit, 764 struct peer *peer 765 ) 766 { 767 struct instance *instance; 768 769 instance = (struct instance *) peer->procptr->unitptr; 770 if (instance->timeout) { 771 char *cp; 772 773 instance->timeout--; 774 if (instance->timeout == 0) { 775 cp = "Oncore: No response from @@Cj, shutting down driver"; 776 record_clock_stats(&(instance->peer->srcadr), cp); 777 oncore_shutdown(unit, peer); 778 } else { 779 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 780 cp = "Oncore: Resend @@Cj"; 781 record_clock_stats(&(instance->peer->srcadr), cp); 782 } 783 return; 784 } 785 786 if (!instance->pollcnt) 787 refclock_report(peer, CEVNT_TIMEOUT); 788 else 789 instance->pollcnt--; 790 peer->procptr->polls++; 791 instance->polled = 1; 792 } 793 794 795 796 /* 797 * Initialize PPSAPI 798 */ 799 800 #ifdef HAVE_PPSAPI 801 static int 802 oncore_ppsapi( 803 struct instance *instance 804 ) 805 { 806 int mode; 807 808 if (time_pps_getcap(instance->pps_h, &mode) < 0) { 809 msyslog(LOG_ERR, "refclock_ioctl: time_pps_getcap failed: %m"); 810 return (0); 811 } 812 813 if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) { 814 msyslog(LOG_ERR, "refclock_ioctl: time_pps_getparams failed: %m"); 815 return (0); 816 } 817 818 /* nb. only turn things on, if someone else has turned something 819 * on before we get here, leave it alone! 820 */ 821 822 if (instance->assert) { /* nb, default or ON */ 823 instance->pps_p.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; 824 instance->pps_p.assert_offset.tv_sec = 0; 825 instance->pps_p.assert_offset.tv_nsec = 0; 826 } else { 827 instance->pps_p.mode = PPS_CAPTURECLEAR | PPS_OFFSETCLEAR; 828 instance->pps_p.clear_offset.tv_sec = 0; 829 instance->pps_p.clear_offset.tv_nsec = 0; 830 } 831 instance->pps_p.mode |= PPS_TSFMT_TSPEC; 832 instance->pps_p.mode &= mode; /* only set what is legal */ 833 834 if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) { 835 perror("time_pps_setparams"); 836 exit(1); 837 } 838 839 /* If HARDPPS is on, we tell kernel */ 840 841 if (instance->hardpps) { 842 int i; 843 844 if (instance->assert) 845 i = PPS_CAPTUREASSERT; 846 else 847 i = PPS_CAPTURECLEAR; 848 849 if (i&mode) { 850 if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i, 851 PPS_TSFMT_TSPEC) < 0) { 852 msyslog(LOG_ERR, "refclock_ioctl: time_pps_kcbind failed: %m"); 853 return (0); 854 } 855 pps_enable = 1; 856 } 857 } 858 return(1); 859 } 860 #endif 861 862 863 864 #ifdef ONCORE_SHMEM_STATUS 865 static void 866 oncore_init_shmem( 867 struct instance *instance 868 ) 869 { 870 int i, l, n, fd, shmem_old_size, n1; 871 char *buf, Msg[160]; 872 u_char *cp, *cp1, *shmem_old; 873 struct msg_desc *mp; 874 struct stat sbuf; 875 size_t shmem_length; 876 877 /* 878 * The first thing we do is see if there is an instance->shmem_fname file (still) 879 * out there from a previous run. If so, we copy it in and use it to initialize 880 * shmem (so we won't lose our almanac if we need it). 881 */ 882 883 shmem_old = 0; 884 if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0) 885 perror("LOAD:SHMEM"); 886 else { 887 fstat(fd, &sbuf); 888 shmem_old_size = sbuf.st_size; 889 shmem_old = (u_char *) malloc((unsigned) sbuf.st_size); 890 if (shmem_old == NULL) { 891 perror("malloc"); 892 close(fd); 893 return; 894 } 895 896 read(fd, shmem_old, shmem_old_size); 897 close(fd); 898 } 899 900 /* OK, we now create the NEW SHMEM. */ 901 902 if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { 903 perror(instance->shmem_fname); 904 return; 905 } 906 907 /* see how big it needs to be */ 908 909 n = 1; 910 for (mp=oncore_messages; mp->flag[0]; mp++) { 911 mp->shmem = n; 912 /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */ 913 if (!strcmp(mp->flag, "Cb")) { 914 instance->shmem_Cb = n; 915 n += (mp->len + 3) * 34; 916 } 917 if (!strcmp(mp->flag, "Ba")) { 918 instance->shmem_Ba = n; 919 n += (mp->len + 3) * 3; 920 } 921 if (!strcmp(mp->flag, "Ea")) { 922 instance->shmem_Ea = n; 923 n += (mp->len + 3) * 3; 924 } 925 if (!strcmp(mp->flag, "Ha")) { 926 instance->shmem_Ha = n; 927 n += (mp->len + 3) * 3; 928 } 929 n += (mp->len + 3); 930 } 931 shmem_length = n + 2; 932 fprintf(stderr, "ONCORE: SHMEM length: %d bytes\n", (int) shmem_length); 933 934 buf = malloc(shmem_length); 935 if (buf == NULL) { 936 perror("malloc"); 937 close(instance->shmemfd); 938 return; 939 } 940 941 memset(buf, 0, shmem_length); 942 943 /* next build the new SHMEM buffer in memory */ 944 945 for (mp=oncore_messages; mp->flag[0]; mp++) { 946 l = mp->shmem; 947 buf[l + 0] = mp->len >> 8; 948 buf[l + 1] = mp->len & 0xff; 949 buf[l + 2] = 0; 950 buf[l + 3] = '@'; 951 buf[l + 4] = '@'; 952 buf[l + 5] = mp->flag[0]; 953 buf[l + 6] = mp->flag[1]; 954 if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) { 955 if (!strcmp(mp->flag, "Cb")) 956 n = 35; 957 else 958 n = 4; 959 for (i=1; i<n; i++) { 960 buf[l + i * (mp->len+3) + 0] = mp->len >> 8; 961 buf[l + i * (mp->len+3) + 1] = mp->len & 0xff; 962 buf[l + i * (mp->len+3) + 2] = 0; 963 buf[l + i * (mp->len+3) + 3] = '@'; 964 buf[l + i * (mp->len+3) + 4] = '@'; 965 buf[l + i * (mp->len+3) + 5] = mp->flag[0]; 966 buf[l + i * (mp->len+3) + 6] = mp->flag[1]; 967 } 968 } 969 } 970 971 /* we now walk thru the two buffers (shmem_old and buf, soon to become shmem) 972 * copying the data in shmem_old to buf. When we are done we write it out 973 * and free both buffers. 974 * If the structures change (an addition or deletion) I will stop copying. 975 * The two will be the same unless we add/subtract from the oncore_messages list 976 * so this should work most of the time, and takes a lot less code than doing it right. 977 */ 978 979 if (shmem_old) { 980 for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3), cp1+=(n+3)) { 981 n1 = 256*(*(cp1-3)) + *(cp1-2); 982 if (n1 != n || strncmp(cp, cp1, 4)) 983 break; 984 985 memcpy(cp, cp1, (size_t) n); 986 } 987 free(shmem_old); 988 } 989 990 i = write(instance->shmemfd, buf, shmem_length); 991 free(buf); 992 993 if (i != shmem_length) { 994 perror(instance->shmem_fname); 995 close(instance->shmemfd); 996 return; 997 } 998 999 instance->shmem = (u_char *) mmap(0, shmem_length, 1000 PROT_READ | PROT_WRITE, 1001 #ifdef MAP_HASSEMAPHORE 1002 MAP_HASSEMAPHORE | 1003 #endif 1004 MAP_SHARED, instance->shmemfd, (off_t)0); 1005 1006 if (instance->shmem == (u_char *)MAP_FAILED) { 1007 instance->shmem = 0; 1008 close(instance->shmemfd); 1009 return; 1010 } 1011 1012 sprintf(Msg, "SHMEM (size = %d) is CONFIGURED and available as %s", shmem_length, instance->shmem_fname); 1013 record_clock_stats(&(instance->peer->srcadr), Msg); 1014 } 1015 #endif /* ONCORE_SHMEM_STATUS */ 1016 1017 1018 1019 /* 1020 * Read Input file if it exists. 1021 */ 1022 1023 static void 1024 oncore_read_config( 1025 struct instance *instance 1026 ) 1027 { 1028 /* 1029 * First we try to open the configuration file 1030 * /etc/oncoreN 1031 * where N is the unit number viz 127.127.30.N. 1032 * If we don't find it we try 1033 * /etc/ntp.oncore.N 1034 * and then 1035 * /etc/ntp.oncore 1036 * 1037 * If we don't find any then we don't have the cable delay or PPS offset 1038 * and we choose MODE (4) below. 1039 * 1040 * Five Choices for MODE 1041 * (0) ONCORE is preinitialized, don't do anything to change it. 1042 * nb, DON'T set 0D mode, DON'T set Delay, position... 1043 * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode. 1044 * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position, 1045 * lock this in, go to 0D mode. 1046 * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode. 1047 * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position, 1048 * lock this in, go to 0D mode. 1049 * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY] 1050 * then this position is set as the INITIAL position of the ONCORE. 1051 * This can reduce the time to first fix. 1052 * ------------------------------------------------------------------------------- 1053 * Note that an Oncore UT without a battery backup retains NO information if it is 1054 * power cycled, with a Battery Backup it remembers the almanac, etc. 1055 * For an Oncore VP, there is an eeprom that will contain this data, along with the 1056 * option of Battery Backup. 1057 * So a UT without Battery Backup is equivalent to doing a HARD RESET on each 1058 * power cycle, since there is nowhere to store the data. 1059 * ------------------------------------------------------------------------------- 1060 * 1061 * If we open one or the other of the files, we read it looking for 1062 * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS, 1063 * STATUS, POSN3D, POSN2D, CHAN, TRAIM 1064 * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must 1065 * be present or mode reverts to (2,4). 1066 * 1067 * Read input file. 1068 * 1069 * # is comment to end of line 1070 * = allowed between 1st and 2nd fields. 1071 * 1072 * Expect to see one line with 'MODE' as first field, followed by an integer 1073 * in the range 0-4 (default = 4). 1074 * 1075 * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields. 1076 * All numbers are floating point. 1077 * DDD.ddd 1078 * DDD MMM.mmm 1079 * DDD MMM SSS.sss 1080 * 1081 * Expect to see one line with 'HT' as first field, 1082 * followed by 1-2 fields. First is a number, the second is 'FT' or 'M' 1083 * for feet or meters. HT is the height above the GPS ellipsoid. 1084 * If the receiver reports height in both GPS and MSL, then we will report 1085 * the difference GPS-MSL on the clockstats file. 1086 * 1087 * There is an optional line, starting with DELAY, followed 1088 * by 1 or two fields. The first is a number (a time) the second is 1089 * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds. 1090 * DELAY is cable delay, typically a few tens of ns. 1091 * 1092 * There is an optional line, starting with OFFSET, followed 1093 * by 1 or two fields. The first is a number (a time) the second is 1094 * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds. 1095 * OFFSET is the offset of the PPS pulse from 0. (only fully implemented 1096 * with the PPSAPI, we need to be able to tell the Kernel about this 1097 * offset if the Kernel PLL is in use, but can only do this presently 1098 * when using the PPSAPI interface. If not using the Kernel PLL, 1099 * then there is no problem. 1100 * 1101 * There is an optional line, with either ASSERT or CLEAR on it, which 1102 * determine which transition of the PPS signal is used for timing by the 1103 * PPSAPI. If neither is present, then ASSERT is assumed. 1104 * ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input. 1105 * For Flag2, ASSERT=0, and hence is default. 1106 * 1107 * There is an optional line, with HARDPPS on it. Including this line causes 1108 * the PPS signal to control the kernel PLL. 1109 * HARDPPS can also be set with FLAG3 of the ntp.conf input. 1110 * For Flag3, 0 is disabled, and the default. 1111 * 1112 * There are three options that have to do with using the shared memory option. 1113 * First, to enable the option there must be a SHMEM line with a file name. 1114 * The file name is the file associated with the shared memory. 1115 * 1116 * In shared memory, there is one 'record' for each returned variable. 1117 * For the @@Ea data there are three 'records' containing position data. 1118 * There will always be data in the record corresponding to the '0D' @@Ea record, 1119 * and the user has a choice of filling the '3D' record by specifying POSN3D, 1120 * or the '2D' record by specifying POSN2D. In either case the '2D' or '3D' 1121 * record is filled once every 15s. 1122 * 1123 * Two additional variables that can be set are CHAN and TRAIM. These should be 1124 * set correctly by the code examining the @@Cj record, but we bring them out here 1125 * to allow the user to override either the # of channels, or the existence of TRAIM. 1126 * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be 1127 * followed by YES or NO. 1128 * 1129 * There is an optional line with MASK on it followed by one integer field in the 1130 * range 0 to 89. This sets the satellite mask angle and will determine the minimum 1131 * elevation angle for satellites to be tracked by the receiver. The default value 1132 * is 10 deg for the VP and 0 deg for all other receivers. 1133 * 1134 * So acceptable input would be 1135 * # these are my coordinates (RWC) 1136 * LON -106 34.610 1137 * LAT 35 08.999 1138 * HT 1589 # could equally well say HT 5215 FT 1139 * DELAY 60 ns 1140 */ 1141 1142 FILE *fd; 1143 char *cp, *cc, *ca, line[100], units[2], device[20], Msg[160]; 1144 int i, sign, lat_flg, long_flg, ht_flg, mode, mask; 1145 double f1, f2, f3; 1146 1147 sprintf(device, "%s%d", INIT_FILE, instance->unit); /* try "ntp.oncore0" first */ 1148 if ((fd=fopen(device, "r")) == NULL) { /* it was in the original documentation */ 1149 sprintf(device, "%s.%d", INIT_FILE, instance->unit); /* then try "ntp.oncore.0 */ 1150 if ((fd=fopen(device, "r")) == NULL) { 1151 if ((fd=fopen(INIT_FILE, "r")) == NULL) { /* and finally "ntp.oncore" */ 1152 instance->init_type = 4; 1153 return; 1154 } 1155 } 1156 } 1157 1158 mode = mask = 0; 1159 lat_flg = long_flg = ht_flg = 0; 1160 while (fgets(line, 100, fd)) { 1161 1162 /* Remove comments */ 1163 if ((cp = strchr(line, '#'))) 1164 *cp = '\0'; 1165 1166 /* Remove trailing space */ 1167 for (i = strlen(line); 1168 i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]); 1169 ) 1170 line[--i] = '\0'; 1171 1172 /* Remove leading space */ 1173 for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++) 1174 continue; 1175 1176 /* Stop if nothing left */ 1177 if (!*cc) 1178 continue; 1179 1180 /* Uppercase the command and find the arg */ 1181 for (ca = cc; *ca; ca++) { 1182 if (isascii((int)*ca)) { 1183 if (islower((int)*ca)) { 1184 *ca = toupper(*ca); 1185 } else if (isspace((int)*ca) || (*ca == '=')) 1186 break; 1187 } 1188 } 1189 1190 /* Remove space (and possible =) leading the arg */ 1191 for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++) 1192 continue; 1193 1194 if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) { 1195 i = strlen(ca); 1196 instance->shmem_fname = (char *) malloc((unsigned) (i+1)); 1197 strcpy(instance->shmem_fname, ca); 1198 continue; 1199 } 1200 1201 /* Uppercase argument as well */ 1202 for (cp = ca; *cp; cp++) 1203 if (isascii((int)*cp) && islower((int)*cp)) 1204 *cp = toupper(*cp); 1205 1206 if (!strncmp(cc, "LAT", (size_t) 3)) { 1207 f1 = f2 = f3 = 0; 1208 sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3); 1209 sign = 1; 1210 if (f1 < 0) { 1211 f1 = -f1; 1212 sign = -1; 1213 } 1214 instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/ 1215 lat_flg++; 1216 } else if (!strncmp(cc, "LON", (size_t) 3)) { 1217 f1 = f2 = f3 = 0; 1218 sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3); 1219 sign = 1; 1220 if (f1 < 0) { 1221 f1 = -f1; 1222 sign = -1; 1223 } 1224 instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/ 1225 long_flg++; 1226 } else if (!strncmp(cc, "HT", (size_t) 2)) { 1227 f1 = 0; 1228 units[0] = '\0'; 1229 sscanf(ca, "%lf %1s", &f1, units); 1230 if (units[0] == 'F') 1231 f1 = 0.3048 * f1; 1232 instance->ss_ht = 100 * f1; /* cm */ 1233 ht_flg++; 1234 } else if (!strncmp(cc, "DELAY", (size_t) 5)) { 1235 f1 = 0; 1236 units[0] = '\0'; 1237 sscanf(ca, "%lf %1s", &f1, units); 1238 if (units[0] == 'N') 1239 ; 1240 else if (units[0] == 'U') 1241 f1 = 1000 * f1; 1242 else if (units[0] == 'M') 1243 f1 = 1000000 * f1; 1244 else 1245 f1 = 1000000000 * f1; 1246 if (f1 < 0 || f1 > 1.e9) 1247 f1 = 0; 1248 if (f1 < 0 || f1 > 999999) { 1249 sprintf(Msg, "PPS Cable delay of %fns out of Range, ignored", f1); 1250 record_clock_stats(&(instance->peer->srcadr), Msg); 1251 } else 1252 instance->delay = f1; /* delay in ns */ 1253 } else if (!strncmp(cc, "OFFSET", (size_t) 6)) { 1254 f1 = 0; 1255 units[0] = '\0'; 1256 sscanf(ca, "%lf %1s", &f1, units); 1257 if (units[0] == 'N') 1258 ; 1259 else if (units[0] == 'U') 1260 f1 = 1000 * f1; 1261 else if (units[0] == 'M') 1262 f1 = 1000000 * f1; 1263 else 1264 f1 = 1000000000 * f1; 1265 if (f1 < 0 || f1 > 1.e9) 1266 f1 = 0; 1267 if (f1 < 0 || f1 > 999999999.) { 1268 sprintf(Msg, "PPS Offset of %fns out of Range, ignored", f1); 1269 record_clock_stats(&(instance->peer->srcadr), Msg); 1270 } else 1271 instance->offset = f1; /* offset in ns */ 1272 } else if (!strncmp(cc, "MODE", (size_t) 4)) { 1273 sscanf(ca, "%d", &mode); 1274 if (mode < 0 || mode > 4) 1275 mode = 4; 1276 } else if (!strncmp(cc, "ASSERT", (size_t) 6)) { 1277 instance->assert = 1; 1278 } else if (!strncmp(cc, "CLEAR", (size_t) 5)) { 1279 instance->assert = 0; 1280 } else if (!strncmp(cc, "HARDPPS", (size_t) 7)) { 1281 instance->hardpps = 1; 1282 } else if (!strncmp(cc, "POSN2D", (size_t) 6)) { 1283 instance->shmem_Posn = 2; 1284 } else if (!strncmp(cc, "POSN3D", (size_t) 6)) { 1285 instance->shmem_Posn = 3; 1286 } else if (!strncmp(cc, "CHAN", (size_t) 4)) { 1287 sscanf(ca, "%d", &i); 1288 if ((i == 6) || (i == 8) || (i == 12)) 1289 instance->chan_in = i; 1290 } else if (!strncmp(cc, "TRAIM", (size_t) 5)) { 1291 instance->traim_in = 1; /* so TRAIM alone is YES */ 1292 if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */ 1293 instance->traim_in = 0; 1294 } else if (!strncmp(cc, "MASK", (size_t) 4)) { 1295 sscanf(ca, "%d", &mask); 1296 if (mask > -1 && mask < 90) 1297 instance->Ag = mask; /* Satellite mask angle */ 1298 } 1299 } 1300 fclose(fd); 1301 1302 /* 1303 * OK, have read all of data file, and extracted the good stuff. 1304 * If lat/long/ht specified they ALL must be specified for mode = (1,3). 1305 */ 1306 1307 instance->posn_set = 1; 1308 if (!( lat_flg && long_flg && ht_flg )) { 1309 printf("ONCORE: incomplete data on %s\n", INIT_FILE); 1310 instance->posn_set = 0; 1311 if (mode == 1 || mode == 3) { 1312 sprintf(Msg, "Input Mode = %d, but no/incomplete position, mode set to %d", mode, mode+1); 1313 record_clock_stats(&(instance->peer->srcadr), Msg); 1314 mode++; 1315 } 1316 } 1317 instance->init_type = mode; 1318 1319 sprintf(Msg, "Input mode = %d", mode); 1320 record_clock_stats(&(instance->peer->srcadr), Msg); 1321 } 1322 1323 1324 1325 /* 1326 * move data from NTP to buffer (toss the extra in the unlikely case it won't fit) 1327 */ 1328 1329 static void 1330 oncore_receive( 1331 struct recvbuf *rbufp 1332 ) 1333 { 1334 size_t i; 1335 u_char *p; 1336 struct peer *peer; 1337 struct instance *instance; 1338 1339 peer = (struct peer *)rbufp->recv_srcclock; 1340 instance = (struct instance *) peer->procptr->unitptr; 1341 p = (u_char *) &rbufp->recv_space; 1342 1343 #if 0 1344 if (debug > 4) { 1345 int i; 1346 printf("ONCORE: >>>"); 1347 for(i=0; i<rbufp->recv_length; i++) 1348 printf("%02x ", p[i]); 1349 printf("\n"); 1350 printf("ONCORE: >>>"); 1351 for(i=0; i<rbufp->recv_length; i++) 1352 printf("%03o ", p[i]); 1353 printf("\n"); 1354 } 1355 #endif 1356 1357 i = rbufp->recv_length; 1358 if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf]) 1359 i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */ 1360 memcpy(rcvbuf+rcvptr, p, i); 1361 rcvptr += i; 1362 oncore_consume(instance); 1363 } 1364 1365 1366 1367 /* 1368 * Deal with any complete messages 1369 */ 1370 1371 static void 1372 oncore_consume( 1373 struct instance *instance 1374 ) 1375 { 1376 int i, m; 1377 unsigned l; 1378 1379 while (rcvptr >= 7) { 1380 if (rcvbuf[0] != '@' || rcvbuf[1] != '@') { 1381 /* We're not in sync, lets try to get there */ 1382 for (i=1; i < rcvptr-1; i++) 1383 if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@') 1384 break; 1385 if (debug > 4) 1386 printf("ONCORE[%d]: >>> skipping %d chars\n", instance->unit, i); 1387 if (i != rcvptr) 1388 memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i)); 1389 rcvptr -= i; 1390 continue; 1391 } 1392 1393 /* Ok, we have a header now */ 1394 l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1; 1395 for(m=0; m<l; m++) 1396 if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2)) 1397 break; 1398 if (m == l) { 1399 if (debug > 4) 1400 printf("ONCORE[%d]: >>> Unknown MSG, skipping 4 (%c%c)\n", instance->unit, rcvbuf[2], rcvbuf[3]); 1401 memcpy(rcvbuf, rcvbuf+4, (size_t) 4); 1402 rcvptr -= 4; 1403 continue; 1404 } 1405 1406 l = oncore_messages[m].len; 1407 #if 0 1408 if (debug > 3) 1409 printf("ONCORE[%d]: GOT: %c%c %d of %d entry %d\n", instance->unit, rcvbuf[2], rcvbuf[3], rcvptr, l, m); 1410 #endif 1411 /* Got the entire message ? */ 1412 1413 if (rcvptr < l) 1414 return; 1415 1416 /* are we at the end of message? should be <Cksum><CR><LF> */ 1417 1418 if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') { 1419 if (debug) 1420 printf("ONCORE[%d]: NO <CR><LF> at end of message\n", instance->unit); 1421 } else { /* check the CheckSum */ 1422 if (oncore_checksum_ok(rcvbuf, l)) { 1423 if (instance->shmem != NULL) { 1424 instance->shmem[oncore_messages[m].shmem + 2]++; 1425 memcpy(instance->shmem + oncore_messages[m].shmem + 3, 1426 rcvbuf, (size_t) l); 1427 } 1428 oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m); 1429 if (oncore_messages[m].handler) 1430 oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3)); 1431 } else if (debug) { 1432 printf("ONCORE[%d]: Checksum mismatch!\n", instance->unit); 1433 printf("ONCORE[%d]: @@%c%c ", instance->unit, rcvbuf[2], rcvbuf[3]); 1434 for (i=4; i<l; i++) 1435 printf("%03o ", rcvbuf[i]); 1436 printf("\n"); 1437 } 1438 } 1439 1440 if (l != rcvptr) 1441 memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l)); 1442 rcvptr -= l; 1443 } 1444 } 1445 1446 1447 1448 static void 1449 oncore_get_timestamp( 1450 struct instance *instance, 1451 long dt1, /* tick offset THIS time step */ 1452 long dt2 /* tick offset NEXT time step */ 1453 ) 1454 { 1455 int Rsm; 1456 u_long i, j; 1457 l_fp ts, ts_tmp; 1458 double dmy; 1459 #ifdef HAVE_STRUCT_TIMESPEC 1460 struct timespec *tsp = 0; 1461 #else 1462 struct timeval *tsp = 0; 1463 #endif 1464 #ifdef HAVE_PPSAPI 1465 int current_mode; 1466 pps_params_t current_params; 1467 struct timespec timeout; 1468 pps_info_t pps_i; 1469 #else /* ! HAVE_PPSAPI */ 1470 #ifdef HAVE_CIOGETEV 1471 struct ppsclockev ev; 1472 int r = CIOGETEV; 1473 #endif 1474 #ifdef HAVE_TIOCGPPSEV 1475 struct ppsclockev ev; 1476 int r = TIOCGPPSEV; 1477 #endif 1478 #if TIOCDCDTIMESTAMP 1479 struct timeval tv; 1480 #endif 1481 #endif /* ! HAVE_PPS_API */ 1482 1483 #if 1 1484 /* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru. 1485 * If we have Finished the SiteSurvey, then we fall thru for the 14/15 1486 * times we get here in 0D mode (the 1/15 is in 3D for SHMEM). 1487 * This gives good time, which gets better when the SS is done. 1488 */ 1489 1490 if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D)) 1491 #else 1492 /* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */ 1493 1494 if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D)) 1495 #endif 1496 return; 1497 1498 /* Don't do anything without an almanac to define the GPS->UTC delta */ 1499 1500 if (instance->rsm.bad_almanac) 1501 return; 1502 1503 #ifdef HAVE_PPSAPI 1504 j = instance->ev_serial; 1505 timeout.tv_sec = 0; 1506 timeout.tv_nsec = 0; 1507 if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i, 1508 &timeout) < 0) { 1509 printf("ONCORE: time_pps_fetch failed\n"); 1510 return; 1511 } 1512 1513 if (instance->assert) { 1514 tsp = &pps_i.assert_timestamp; 1515 1516 if (debug > 2) { 1517 i = (u_long) pps_i.assert_sequence; 1518 #ifdef HAVE_STRUCT_TIMESPEC 1519 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n", 1520 instance->unit, i, j, 1521 (long)tsp->tv_sec, (long)tsp->tv_nsec); 1522 #else 1523 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n", 1524 instance->unit, i, j, 1525 (long)tsp->tv_sec, (long)tsp->tv_usec); 1526 #endif 1527 } 1528 1529 if (pps_i.assert_sequence == j) { 1530 printf("ONCORE: oncore_get_timestamp, error serial pps\n"); 1531 return; 1532 } 1533 instance->ev_serial = pps_i.assert_sequence; 1534 } else { 1535 tsp = &pps_i.clear_timestamp; 1536 1537 if (debug > 2) { 1538 i = (u_long) pps_i.clear_sequence; 1539 #ifdef HAVE_STRUCT_TIMESPEC 1540 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n", 1541 instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_nsec); 1542 #else 1543 printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n", 1544 instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_usec); 1545 #endif 1546 } 1547 1548 if (pps_i.clear_sequence == j) { 1549 printf("ONCORE: oncore_get_timestamp, error serial pps\n"); 1550 return; 1551 } 1552 instance->ev_serial = pps_i.clear_sequence; 1553 } 1554 1555 /* convert timespec -> ntp l_fp */ 1556 1557 dmy = tsp->tv_nsec; 1558 dmy /= 1e9; 1559 ts.l_uf = dmy * 4294967296.0; 1560 ts.l_ui = tsp->tv_sec; 1561 #if 0 1562 alternate code for previous 4 lines is 1563 dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */ 1564 DTOLFP(dmy, &ts); 1565 dmy = tsp->tv_sec; /* integer part */ 1566 DTOLFP(dmy, &ts_tmp); 1567 L_ADD(&ts, &ts_tmp); 1568 or more simply 1569 dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */ 1570 DTOLFP(dmy, &ts); 1571 ts.l_ui = tsp->tv_sec; 1572 #endif /* 0 */ 1573 #else 1574 # if defined(HAVE_TIOCGPPSEV) || defined(HAVE_CIOGETEV) 1575 j = instance->ev_serial; 1576 if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) { 1577 perror("ONCORE: IOCTL:"); 1578 return; 1579 } 1580 1581 tsp = &ev.tv; 1582 1583 if (debug > 2) 1584 #ifdef HAVE_STRUCT_TIMESPEC 1585 printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n", 1586 ev.serial, j, tsp->tv_sec, tsp->tv_nsec); 1587 #else 1588 printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n", 1589 ev.serial, j, tsp->tv_sec, tsp->tv_usec); 1590 #endif 1591 1592 if (ev.serial == j) { 1593 printf("ONCORE: oncore_get_timestamp, error serial pps\n"); 1594 return; 1595 } 1596 instance->ev_serial = ev.serial; 1597 1598 /* convert timeval -> ntp l_fp */ 1599 1600 TVTOTS(tsp, &ts); 1601 # else 1602 # if defined(TIOCDCDTIMESTAMP) 1603 if(ioctl(instance->ppsfd, TIOCDCDTIMESTAMP, &tv) < 0) { 1604 perror("ONCORE: ioctl(TIOCDCDTIMESTAMP)"); 1605 return; 1606 } 1607 tsp = &tv; 1608 TVTOTS(tsp, &ts); 1609 # else 1610 #error "Cannot compile -- no PPS mechanism configured!" 1611 # endif 1612 # endif 1613 #endif 1614 /* now have timestamp in ts */ 1615 /* add in saw_tooth and offset, these will be ZERO if no TRAIM */ 1616 1617 /* saw_tooth not really necessary if using TIMEVAL */ 1618 /* since its only precise to us, but do it anyway. */ 1619 1620 /* offset in ns, and is positive (late), we subtract */ 1621 /* to put the PPS time transition back where it belongs */ 1622 1623 #ifdef HAVE_PPSAPI 1624 /* must hand the offset for the NEXT sec off to the Kernel to do */ 1625 /* the addition, so that the Kernel PLL sees the offset too */ 1626 1627 if (instance->assert) 1628 instance->pps_p.assert_offset.tv_nsec = -dt2; 1629 else 1630 instance->pps_p.clear_offset.tv_nsec = -dt2; 1631 1632 /* The following code is necessary, and not just a time_pps_setparams, 1633 * using the saved instance->pps_p, since some other process on the 1634 * machine may have diddled with the mode bits (say adding something 1635 * that it needs). We take what is there and ADD what we need. 1636 * [[ The results from the time_pps_getcap is unlikely to change so 1637 * we could probably just save it, but I choose to do the call ]] 1638 * Unfortunately, there is only ONE set of mode bits in the kernel per 1639 * interface, and not one set for each open handle. 1640 * 1641 * There is still a race condition here where we might mess up someone 1642 * elses mode, but if he is being careful too, he should survive. 1643 */ 1644 1645 if (time_pps_getcap(instance->pps_h, ¤t_mode) < 0) { 1646 msyslog(LOG_ERR, "refclock_ioctl: time_pps_getcap failed: %m"); 1647 return; 1648 } 1649 1650 if (time_pps_getparams(instance->pps_h, ¤t_params) < 0) { 1651 msyslog(LOG_ERR, "refclock_ioctl: time_pps_getparams failed: %m"); 1652 return; 1653 } 1654 1655 /* or current and mine */ 1656 current_params.mode |= instance->pps_p.mode; 1657 /* but only set whats legal */ 1658 current_params.mode &= current_mode; 1659 1660 current_params.assert_offset.tv_sec = 0; 1661 current_params.assert_offset.tv_nsec = -dt2; 1662 current_params.clear_offset.tv_sec = 0; 1663 current_params.clear_offset.tv_nsec = -dt2; 1664 1665 if (time_pps_setparams(instance->pps_h, ¤t_params)) 1666 perror("time_pps_setparams"); 1667 #else 1668 /* if not PPSAPI, no way to inform kernel of OFFSET, just add the */ 1669 /* offset for THIS second */ 1670 1671 dmy = -1.0e-9*dt1; 1672 DTOLFP(dmy, &ts_tmp); 1673 L_ADD(&ts, &ts_tmp); 1674 #endif 1675 /* have time from UNIX origin, convert to NTP origin. */ 1676 1677 ts.l_ui += JAN_1970; 1678 instance->pp->lastrec = ts; 1679 1680 /* print out information about this timestamp (long line) */ 1681 1682 ts_tmp = ts; 1683 ts_tmp.l_ui = 0; /* zero integer part */ 1684 LFPTOD(&ts_tmp, dmy); /* convert fractional part to a double */ 1685 j = 1.0e9*dmy; /* then to integer ns */ 1686 1687 Rsm = 0; 1688 if (instance->chan == 6) 1689 Rsm = instance->BEHa[64]; 1690 else if (instance->chan == 8) 1691 Rsm = instance->BEHa[72]; 1692 else if (instance->chan == 12) 1693 Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]); 1694 1695 if (instance->chan == 6 || instance->chan == 8) { 1696 sprintf(instance->pp->a_lastcode, /* MAX length 128, currently at 117 */ 1697 "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d sigma %2d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d", 1698 ts.l_ui, j, 1699 instance->pp->year, instance->pp->day, 1700 instance->pp->hour, instance->pp->minute, instance->pp->second, 1701 (long) tsp->tv_sec % 60, 1702 Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]), 1703 /*rsat dop */ 1704 instance->BEHa[38], instance->BEHa[39], instance->BEHn[21], 1705 /* nsat visible, nsat tracked, traim */ 1706 instance->BEHn[23]*256+instance->BEHn[24], (s_char) instance->BEHn[25], 1707 /* sigma neg-sawtooth */ 1708 /*sat*/ instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53], 1709 instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69] 1710 ); /* will be 0 for 6 chan */ 1711 } else if (instance->chan == 12) { 1712 sprintf(instance->pp->a_lastcode, 1713 "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d sigma %d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d%d%d%d%d", 1714 ts.l_ui, j, 1715 instance->pp->year, instance->pp->day, 1716 instance->pp->hour, instance->pp->minute, instance->pp->second, 1717 (long) tsp->tv_sec % 60, 1718 Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]), 1719 /*rsat dop */ 1720 instance->BEHa[55], instance->BEHa[56], instance->BEHn[6], 1721 /* nsat visible, nsat tracked traim */ 1722 instance->BEHn[12]*256+instance->BEHn[13], (s_char) instance->BEHn[14], 1723 /* sigma neg-sawtooth */ 1724 /*sat*/ instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76], 1725 instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100], 1726 instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124] 1727 ); 1728 } 1729 1730 if (debug > 2) { 1731 int n; 1732 n = strlen(instance->pp->a_lastcode); 1733 printf("ONCORE[%d]: len = %d %s\n", instance->unit, n, instance->pp->a_lastcode); 1734 } 1735 1736 /* and some things I dont understnd (magic ntp things) */ 1737 1738 if (!refclock_process(instance->pp)) { 1739 refclock_report(instance->peer, CEVNT_BADTIME); 1740 return; 1741 } 1742 1743 record_clock_stats(&(instance->peer->srcadr), instance->pp->a_lastcode); 1744 instance->pollcnt = 2; 1745 1746 if (instance->polled) { 1747 instance->polled = 0; 1748 /* 1749 instance->pp->dispersion = instance->pp->skew = 0; 1750 */ 1751 instance->pp->lastref = instance->pp->lastrec; 1752 refclock_receive(instance->peer); 1753 } 1754 } 1755 1756 1757 /*************** oncore_msg_XX routines start here *******************/ 1758 1759 1760 /* 1761 * print Oncore response message. 1762 */ 1763 1764 static void 1765 oncore_msg_any( 1766 struct instance *instance, 1767 u_char *buf, 1768 size_t len, 1769 int idx 1770 ) 1771 { 1772 int i; 1773 const char *fmt = oncore_messages[idx].fmt; 1774 const char *p; 1775 #ifdef HAVE_GETCLOCK 1776 struct timespec ts; 1777 #endif 1778 struct timeval tv; 1779 1780 if (debug > 3) { 1781 #ifdef HAVE_GETCLOCK 1782 (void) getclock(TIMEOFDAY, &ts); 1783 tv.tv_sec = ts.tv_sec; 1784 tv.tv_usec = ts.tv_nsec / 1000; 1785 #else 1786 GETTIMEOFDAY(&tv, 0); 1787 #endif 1788 printf("ONCORE[%d]: %ld.%06ld\n", instance->unit, (long) tv.tv_sec, (long) tv.tv_usec); 1789 1790 if (!*fmt) { 1791 printf(">>@@%c%c ", buf[2], buf[3]); 1792 for(i=2; i < len && i < 2400 ; i++) 1793 printf("%02x", buf[i]); 1794 printf("\n"); 1795 return; 1796 } else { 1797 printf("##"); 1798 for (p = fmt; *p; p++) { 1799 putchar(*p); 1800 putchar('_'); 1801 } 1802 printf("\n%c%c", buf[2], buf[3]); 1803 i = 4; 1804 for (p = fmt; *p; p++) { 1805 printf("%02x", buf[i++]); 1806 } 1807 printf("\n"); 1808 } 1809 } 1810 } 1811 1812 1813 1814 /* Latitude, Longitude, Height */ 1815 1816 static void 1817 oncore_msg_Adef( 1818 struct instance *instance, 1819 u_char *buf, 1820 size_t len 1821 ) 1822 { 1823 } 1824 1825 1826 1827 /* Mask Angle */ 1828 1829 static void 1830 oncore_msg_Ag( 1831 struct instance *instance, 1832 u_char *buf, 1833 size_t len 1834 ) 1835 { char Msg[160], *cp; 1836 1837 cp = "set to"; 1838 if (instance->o_state == ONCORE_RUN) 1839 cp = "is"; 1840 1841 instance->Ag = buf[4]; 1842 sprintf(Msg, "Satellite mask angle %s %d degrees", cp, (int) instance->Ag); 1843 record_clock_stats(&(instance->peer->srcadr), Msg); 1844 } 1845 1846 1847 1848 /* 1849 * get Position hold position 1850 */ 1851 1852 static void 1853 oncore_msg_As( 1854 struct instance *instance, 1855 u_char *buf, 1856 size_t len 1857 ) 1858 { 1859 instance->ss_lat = buf_w32(&buf[4]); 1860 instance->ss_long = buf_w32(&buf[8]); 1861 instance->ss_ht = buf_w32(&buf[12]); 1862 1863 /* Print out Position */ 1864 oncore_print_posn(instance); 1865 } 1866 1867 1868 1869 /* 1870 * Try to use Oncore UT+ Auto Survey Feature 1871 * If its not there (VP), set flag to do it ourselves. 1872 */ 1873 1874 static void 1875 oncore_msg_At( 1876 struct instance *instance, 1877 u_char *buf, 1878 size_t len 1879 ) 1880 { 1881 char *cp; 1882 1883 instance->saw_At = 1; 1884 if (instance->site_survey == ONCORE_SS_TESTING) { 1885 if (buf[4] == 2) { 1886 record_clock_stats(&(instance->peer->srcadr), 1887 "Initiating hardware 3D site survey"); 1888 1889 cp = "SSstate = ONCORE_SS_HW"; 1890 record_clock_stats(&(instance->peer->srcadr), cp); 1891 instance->site_survey = ONCORE_SS_HW; 1892 } 1893 } 1894 } 1895 1896 1897 1898 /* 1899 * get PPS Offset 1900 * Nb. @@Ay is not supported for early UT (no plus) model 1901 */ 1902 1903 static void 1904 oncore_msg_Ay( 1905 struct instance *instance, 1906 u_char *buf, 1907 size_t len 1908 ) 1909 { 1910 char Msg[120]; 1911 1912 if (instance->saw_Ay) 1913 return; 1914 1915 instance->saw_Ay = 1; 1916 1917 instance->offset = buf_w32(&buf[4]); 1918 1919 sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset); 1920 record_clock_stats(&(instance->peer->srcadr), Msg); 1921 } 1922 1923 1924 1925 /* 1926 * get Cable Delay 1927 */ 1928 1929 static void 1930 oncore_msg_Az( 1931 struct instance *instance, 1932 u_char *buf, 1933 size_t len 1934 ) 1935 { 1936 char Msg[120]; 1937 1938 if (instance->saw_Az) 1939 return; 1940 1941 instance->saw_Az = 1; 1942 1943 instance->delay = buf_w32(&buf[4]); 1944 1945 sprintf(Msg, "Cable delay is set to %ld ns", instance->delay); 1946 record_clock_stats(&(instance->peer->srcadr), Msg); 1947 } 1948 1949 1950 1951 /* Ba, Ea and Ha come here, these contain Position */ 1952 1953 static void 1954 oncore_msg_BaEaHa( 1955 struct instance *instance, 1956 u_char *buf, 1957 size_t len 1958 ) 1959 { 1960 const char *cp; 1961 char Msg[160]; 1962 int mode; 1963 1964 /* OK, we are close to the RUN state now. 1965 * But we have a few more items to initialize first. 1966 * 1967 * At the beginning of this routine there are several 'timers'. 1968 * We enter this routine 1/sec, and since the upper levels of NTP have usurped 1969 * the use of timers, we use the 1/sec entry to do things that 1970 * we would normally do with timers... 1971 */ 1972 1973 if (instance->o_state == ONCORE_CHECK_CHAN) { /* here while checking for the # chan */ 1974 if (buf[2] == 'B') { /* 6chan */ 1975 if (instance->chan_ck < 6) instance->chan_ck = 6; 1976 } else if (buf[2] == 'E') { /* 8chan */ 1977 if (instance->chan_ck < 8) instance->chan_ck = 8; 1978 } else if (buf[2] == 'H') { /* 12chan */ 1979 if (instance->chan_ck < 12) instance->chan_ck = 12; 1980 } 1981 1982 if (instance->count3++ < 5) 1983 return; 1984 1985 instance->count3 = 0; 1986 1987 if (instance->chan_in != -1) /* set in Input */ 1988 instance->chan = instance->chan_in; 1989 else /* set from test */ 1990 instance->chan = instance->chan_ck; 1991 1992 sprintf(Msg, "Input says chan = %d", instance->chan_in); 1993 record_clock_stats(&(instance->peer->srcadr), Msg); 1994 sprintf(Msg, "Model # says chan = %d", instance->chan_id); 1995 record_clock_stats(&(instance->peer->srcadr), Msg); 1996 sprintf(Msg, "Testing says chan = %d", instance->chan_ck); 1997 record_clock_stats(&(instance->peer->srcadr), Msg); 1998 sprintf(Msg, "Using chan = %d", instance->chan); 1999 record_clock_stats(&(instance->peer->srcadr), Msg); 2000 2001 instance->o_state = ONCORE_HAVE_CHAN; 2002 cp = "state = ONCORE_HAVE_CHAN"; 2003 record_clock_stats(&(instance->peer->srcadr), cp); 2004 2005 instance->timeout = 4; 2006 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 2007 return; 2008 } 2009 2010 if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN) 2011 return; 2012 2013 /* PAUSE 5sec */ 2014 2015 if (instance->count) { 2016 if (instance->count++ < 5) /* make sure results are stable, using position */ 2017 return; 2018 instance->count = 0; 2019 } 2020 2021 memcpy(instance->BEHa, buf, (size_t) (len+3)); /* Ba, Ea or Ha */ 2022 2023 /* check the antenna and almanac for changes (did it get unplugged, is it ready?) */ 2024 2025 oncore_check_almanac(instance); 2026 oncore_check_antenna(instance); 2027 2028 /* Almanac mode, waiting for Almanac, we can't do anything till we have it */ 2029 /* When we have an almanac, we will start the Bn/En/@@Hn messages */ 2030 2031 if (instance->o_state == ONCORE_ALMANAC) 2032 if (oncore_wait_almanac(instance)) 2033 return; 2034 2035 /* do some things once when we get this far in BaEaHa */ 2036 2037 if (instance->once) { 2038 instance->once = 0; 2039 instance->count2 = 1; 2040 2041 /* Have we seen an @@At (position hold) command response */ 2042 /* if not, message out */ 2043 2044 if (instance->chan != 12 && !instance->saw_At) { 2045 cp = "Not Good, no @@At command (no Position Hold), must be a GT/GT+"; 2046 record_clock_stats(&(instance->peer->srcadr), cp); 2047 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); 2048 } 2049 2050 /* have an Almanac, can start the SiteSurvey 2051 * (actually only need to get past the almanac_load where we diddle with At 2052 * command,- we can't change it after we start the HW_SS below 2053 */ 2054 2055 mode = instance->init_type; 2056 switch (mode) { 2057 case 0: /* NO initialization, don't change anything */ 2058 case 1: /* Use given Position */ 2059 case 3: 2060 instance->site_survey = ONCORE_SS_DONE; 2061 cp = "SSstate = ONCORE_SS_DONE"; 2062 record_clock_stats(&(instance->peer->srcadr), cp); 2063 break; 2064 2065 case 2: 2066 case 4: /* Site Survey */ 2067 cp = "SSstate = ONCORE_SS_TESTING"; 2068 record_clock_stats(&(instance->peer->srcadr), cp); 2069 instance->site_survey = ONCORE_SS_TESTING; 2070 instance->count1 = 1; 2071 if (instance->chan == 12) 2072 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd3, sizeof(oncore_cmd_Gd3)); /* M12+T */ 2073 else 2074 oncore_sendmsg(instance->ttyfd, oncore_cmd_At2, sizeof(oncore_cmd_At2)); /* not GT, arg not VP */ 2075 break; 2076 } 2077 2078 /* Read back PPS Offset for Output */ 2079 /* Nb. This will fail silently for early UT (no plus) and M12 models */ 2080 2081 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ayx, sizeof(oncore_cmd_Ayx)); 2082 2083 /* Read back Cable Delay for Output */ 2084 2085 oncore_sendmsg(instance->ttyfd, oncore_cmd_Azx, sizeof(oncore_cmd_Azx)); 2086 2087 /* Read back Satellite Mask Angle for Output */ 2088 2089 oncore_sendmsg(instance->ttyfd, oncore_cmd_Agx, sizeof(oncore_cmd_Agx)); 2090 } 2091 2092 if (instance->count1) { 2093 if (instance->count1++ > 5 || instance->site_survey == ONCORE_SS_HW) { 2094 instance->count1 = 0; 2095 if (instance->site_survey == ONCORE_SS_TESTING) { 2096 /* 2097 * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec 2098 * wait after the @@At2/@@Gd3 command we have not changed the state to 2099 * ONCORE_SS_HW. If the Hardware is capable of doing a Site Survey, then 2100 * the variable would have been changed by now. 2101 * There are three possibilities: 2102 * 6/8chan 2103 * (a) We did not get a response to the @@At0 or @@At2 commands, 2104 * and it must be a GT/GT+/SL with no position hold mode. 2105 * We will have to do it ourselves. 2106 * (b) We saw the @@At0, @@At2 commands, but @@At2 failed, 2107 * must be a VP or older UT which doesn't have Site Survey mode. 2108 * We will have to do it ourselves. 2109 * 12chan 2110 * (c) We saw the @@Gd command, but @@Gd3 failed, 2111 * We will have to do it ourselves. 2112 */ 2113 2114 sprintf(Msg, "Initiating software 3D site survey (%d samples)", 2115 POS_HOLD_AVERAGE); 2116 record_clock_stats(&(instance->peer->srcadr), Msg); 2117 2118 record_clock_stats(&(instance->peer->srcadr), "SSstate = ONCORE_SS_SW"); 2119 instance->site_survey = ONCORE_SS_SW; 2120 2121 instance->ss_lat = instance->ss_long = instance->ss_ht = 0; 2122 if (instance->chan == 12) 2123 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */ 2124 else { 2125 oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */ 2126 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */ 2127 } 2128 } 2129 } 2130 } 2131 2132 /* check the mode we are in 0/2/3D */ 2133 2134 if (instance->chan == 6) { 2135 if (instance->BEHa[64]&0x8) 2136 instance->mode = MODE_0D; 2137 else if (instance->BEHa[64]&0x10) 2138 instance->mode = MODE_2D; 2139 else if (instance->BEHa[64]&0x20) 2140 instance->mode = MODE_3D; 2141 } else if (instance->chan == 8) { 2142 if (instance->BEHa[72]&0x8) 2143 instance->mode = MODE_0D; 2144 else if (instance->BEHa[72]&0x10) 2145 instance->mode = MODE_2D; 2146 else if (instance->BEHa[72]&0x20) 2147 instance->mode = MODE_3D; 2148 } else if (instance->chan == 12) { 2149 int bits; 2150 2151 bits = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */ 2152 if (bits == 0x4) 2153 instance->mode = MODE_0D; 2154 else if (bits == 0x6) 2155 instance->mode = MODE_2D; 2156 else if (bits == 0x7) 2157 instance->mode = MODE_3D; 2158 } 2159 2160 /* copy the record to the (extra) location in SHMEM */ 2161 2162 if (instance->shmem) { 2163 int i; 2164 u_char *smp; /* pointer to start of shared mem for Ba/Ea/Ha */ 2165 2166 switch(instance->chan) { 2167 case 6: smp = &instance->shmem[instance->shmem_Ba]; break; 2168 case 8: smp = &instance->shmem[instance->shmem_Ea]; break; 2169 case 12: smp = &instance->shmem[instance->shmem_Ha]; break; 2170 default: smp = (u_char) 0; break; 2171 } 2172 2173 switch (instance->mode) { 2174 case MODE_0D: i = 1; break; /* 0D, Position Hold */ 2175 case MODE_2D: i = 2; break; /* 2D, Altitude Hold */ 2176 case MODE_3D: i = 3; break; /* 3D fix */ 2177 default: i = 0; break; 2178 } 2179 2180 if (i) { 2181 i *= (len+6); 2182 smp[i + 2]++; 2183 memcpy(&smp[i+3], buf, (size_t) (len+3)); 2184 } 2185 } 2186 2187 /* 2188 * check if timer active 2189 * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond 2190 */ 2191 2192 if (instance->traim_delay) { 2193 if (instance->traim_delay++ > 5) { 2194 instance->traim = 0; 2195 instance->traim_delay = 0; 2196 cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF"; 2197 record_clock_stats(&(instance->peer->srcadr), cp); 2198 2199 oncore_set_traim(instance); 2200 } else 2201 return; 2202 2203 } 2204 2205 /* by now should have a @@Ba/@@Ea/@@Ha with good data in it */ 2206 2207 if (!instance->have_dH && !instance->traim_delay) 2208 oncore_compute_dH(instance); 2209 2210 /* 2211 * must be ONCORE_RUN if we are here. 2212 * Have # chan and TRAIM by now. 2213 */ 2214 2215 instance->pp->year = buf[6]*256+buf[7]; 2216 instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]); 2217 instance->pp->hour = buf[8]; 2218 instance->pp->minute = buf[9]; 2219 instance->pp->second = buf[10]; 2220 2221 /* 2222 * Are we doing a Hardware or Software Site Survey? 2223 */ 2224 2225 if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW) 2226 oncore_ss(instance); 2227 2228 /* see if we ever saw a response from the @@Ayx above */ 2229 2230 if (instance->count2) { 2231 if (instance->count2++ > 5) { /* this delay to check on @@Ay command */ 2232 instance->count2 = 0; 2233 2234 /* Have we seen an Ay (1PPS time offset) command response */ 2235 /* if not, and non-zero offset, zero the offset, and send message */ 2236 2237 if (!instance->saw_Ay && instance->offset) { 2238 cp = "No @@Ay command, PPS OFFSET ignored"; 2239 record_clock_stats(&(instance->peer->srcadr), cp); 2240 instance->offset = 0; 2241 } 2242 } 2243 } 2244 2245 /* 2246 * Check the leap second status once per day. 2247 */ 2248 2249 oncore_check_leap_sec(instance); 2250 2251 /* 2252 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn. 2253 */ 2254 2255 if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE)) 2256 oncore_shmem_get_3D(instance); 2257 2258 if (!instance->traim) /* NO traim, no BnEnHn, go get tick */ 2259 oncore_get_timestamp(instance, instance->offset, instance->offset); 2260 } 2261 2262 2263 2264 /* Almanac Status */ 2265 2266 static void 2267 oncore_msg_Bd( 2268 struct instance *instance, 2269 u_char *buf, 2270 size_t len 2271 ) 2272 { 2273 char Msg[160]; 2274 2275 sprintf(Msg, "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x", 2276 ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6], buf[7], w32(&buf[8]) ); 2277 record_clock_stats(&(instance->peer->srcadr), Msg); 2278 } 2279 2280 2281 2282 /* get leap-second warning message */ 2283 2284 /* 2285 * @@Bj does NOT behave as documented in current Oncore firmware. 2286 * It turns on the LEAP indicator when the data is set, and does not, 2287 * as documented, wait until the beginning of the month when the 2288 * leap second will occur. 2289 * Since this firmware bug will never be fixed in all the outstanding Oncore receivers 2290 * @@Bj is only called in June/December. 2291 */ 2292 2293 static void 2294 oncore_msg_Bj( 2295 struct instance *instance, 2296 u_char *buf, 2297 size_t len 2298 ) 2299 { 2300 const char *cp; 2301 2302 switch(buf[4]) { 2303 case 1: 2304 instance->peer->leap = LEAP_ADDSECOND; 2305 cp = "Set peer.leap to LEAP_ADDSECOND"; 2306 break; 2307 case 2: 2308 instance->peer->leap = LEAP_DELSECOND; 2309 cp = "Set peer.leap to LEAP_DELSECOND"; 2310 break; 2311 case 0: 2312 default: 2313 instance->peer->leap = LEAP_NOWARNING; 2314 cp = "Set peer.leap to LEAP_NOWARNING"; 2315 break; 2316 } 2317 record_clock_stats(&(instance->peer->srcadr), cp); 2318 } 2319 2320 2321 2322 static void 2323 oncore_msg_BnEnHn( 2324 struct instance *instance, 2325 u_char *buf, 2326 size_t len 2327 ) 2328 { 2329 long dt1, dt2; 2330 char *cp; 2331 2332 if (instance->o_state != ONCORE_RUN) 2333 return; 2334 2335 if (instance->traim_delay) { /* flag that @@Bn/@@En/Hn returned */ 2336 instance->traim_ck = 1; 2337 instance->traim_delay = 0; 2338 cp = "ONCORE: Detected TRAIM, TRAIM = ON"; 2339 record_clock_stats(&(instance->peer->srcadr), cp); 2340 2341 oncore_set_traim(instance); 2342 } 2343 2344 memcpy(instance->BEHn, buf, (size_t) len); /* Bn or En or Hn */ 2345 2346 /* If Time RAIM doesn't like it, don't trust it */ 2347 2348 if (buf[2] == 'H') { 2349 if (instance->BEHn[6]) /* bad TRAIM */ 2350 return; 2351 2352 dt1 = instance->saw_tooth + instance->offset; /* dt this time step */ 2353 instance->saw_tooth = (s_char) instance->BEHn[10]; /* update for next time Hn[10] */ 2354 dt2 = instance->saw_tooth + instance->offset; /* dt next time step */ 2355 } else { 2356 if (instance->BEHn[21]) /* bad TRAIM */ 2357 return; 2358 2359 dt1 = instance->saw_tooth + instance->offset; /* dt this time step */ 2360 instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time */ 2361 dt2 = instance->saw_tooth + instance->offset; /* dt next time step */ 2362 } 2363 2364 oncore_get_timestamp(instance, dt1, dt2); 2365 } 2366 2367 2368 2369 /* Here for @@Ca, @@Fa and @@Ia messages */ 2370 2371 /* These are Self test Commands for 6, 8, and 12 chan receivers. 2372 * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE. 2373 * It was found that under some circumstances the following 2374 * command would fail if issued immediately after the return from the 2375 * @@Fa, but a 2sec delay seemed to fix things. Since simply calling 2376 * sleep(2) is wasteful, and may cause trouble for some OS's, repeating 2377 * itimer, we set a flag, and test it at the next POLL. If it hasn't 2378 * been cleared, we reissue the @@Cj that is issued below. 2379 * Note that we do a @@Cj at the beginning, and again here. 2380 * The first is to get the info, the 2nd is just used as a safe command 2381 * after the @@Fa for all Oncores (and it was in this posn in the 2382 * original code). 2383 */ 2384 2385 static void 2386 oncore_msg_CaFaIa( 2387 struct instance *instance, 2388 u_char *buf, 2389 size_t len 2390 ) 2391 { 2392 char *cp; 2393 int i; 2394 2395 if (instance->o_state == ONCORE_TEST_SENT) { 2396 enum antenna_state antenna; 2397 2398 instance->timeout = 0; 2399 2400 if (debug > 2) { 2401 if (buf[2] == 'I') 2402 printf("ONCORE[%d]: >>@@%ca %x %x %x\n", instance->unit, buf[2], buf[4], buf[5], buf[6]); 2403 else 2404 printf("ONCORE[%d]: >>@@%ca %x %x\n", instance->unit, buf[2], buf[4], buf[5]); 2405 } 2406 2407 antenna = (buf[4] & 0xc0) >> 6; 2408 buf[4] &= ~0xc0; 2409 2410 i = buf[4] || buf[5]; 2411 if (buf[2] == 'I') i = i || buf[6]; 2412 if (i) { 2413 if (buf[2] == 'I') { 2414 msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x %02x", 2415 instance->unit, buf[4], buf[5], buf[6]); 2416 } else { 2417 msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x", 2418 instance->unit, buf[4], buf[5]); 2419 } 2420 cp = "ONCORE: self test failed, shutting down driver"; 2421 record_clock_stats(&instance->peer->srcadr, cp); 2422 2423 refclock_report(instance->peer, CEVNT_FAULT); 2424 oncore_shutdown(instance->unit, instance->peer); 2425 return; 2426 } 2427 2428 /* report the current antenna state */ 2429 2430 oncore_antenna_report(instance, antenna); 2431 2432 instance->o_state = ONCORE_INIT; 2433 cp = "state = ONCORE_INIT"; 2434 record_clock_stats(&(instance->peer->srcadr), cp); 2435 2436 instance->timeout = 4; 2437 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 2438 } 2439 } 2440 2441 2442 2443 /* 2444 * Demultiplex the almanac into shmem 2445 */ 2446 2447 static void 2448 oncore_msg_Cb( 2449 struct instance *instance, 2450 u_char *buf, 2451 size_t len 2452 ) 2453 { 2454 int i; 2455 2456 if (instance->shmem == NULL) 2457 return; 2458 2459 if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26) 2460 i = buf[5]; 2461 else if (buf[4] == 4 && buf[5] <= 5) 2462 i = buf[5] + 24; 2463 else if (buf[4] == 4 && buf[5] <= 10) 2464 i = buf[5] + 23; 2465 else if (buf[4] == 4 && buf[5] == 25) 2466 i = 34; 2467 else { 2468 char *cp; 2469 2470 cp = "Cb: Response is NO ALMANAC"; 2471 record_clock_stats(&(instance->peer->srcadr), cp); 2472 return; 2473 } 2474 2475 i *= 36; 2476 instance->shmem[instance->shmem_Cb + i + 2]++; 2477 memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3)); 2478 2479 #if 1 2480 { 2481 char Msg[160]; 2482 sprintf(Msg, "See Cb [%d,%d]", buf[4], buf[5]); 2483 record_clock_stats(&(instance->peer->srcadr), Msg); 2484 } 2485 #endif 2486 } 2487 2488 2489 2490 /* 2491 * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup 2492 * not so for VP (eeprom) or any unit with a battery 2493 */ 2494 2495 static void 2496 oncore_msg_Cf( 2497 struct instance *instance, 2498 u_char *buf, 2499 size_t len 2500 ) 2501 { 2502 const char *cp; 2503 2504 if (instance->o_state == ONCORE_RESET_SENT) { 2505 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */ 2506 /* Reset set VP to IDLE */ 2507 instance->o_state = ONCORE_TEST_SENT; 2508 cp = "state = ONCORE_TEST_SENT"; 2509 record_clock_stats(&(instance->peer->srcadr), cp); 2510 2511 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); 2512 } 2513 } 2514 2515 2516 2517 /* 2518 * This is the Grand Central Station for the Preliminary Initialization. 2519 * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running. 2520 * 2521 * We do an @@Cj whenever we need a safe command for all Oncores. 2522 * The @@Cj gets us back here where we can switch to the next phase of setup. 2523 * 2524 * o Once at the very beginning (in start) to get the Model number. 2525 * This info is printed, but no longer used. 2526 * o Again after we have determined the number of Channels in the receiver. 2527 * o And once later after we have done a reset and test, (which may hang), 2528 * as we are about to initialize the Oncore and start it running. 2529 * o We have one routine below for each case. 2530 */ 2531 2532 static void 2533 oncore_msg_Cj( 2534 struct instance *instance, 2535 u_char *buf, 2536 size_t len 2537 ) 2538 { 2539 int mode; 2540 char *cp; 2541 2542 memcpy(instance->Cj, buf, len); 2543 2544 instance->timeout = 0; 2545 if (instance->o_state == ONCORE_CHECK_ID) { 2546 oncore_msg_Cj_id(instance, buf, len); 2547 oncore_chan_test(instance); 2548 } else if (instance->o_state == ONCORE_HAVE_CHAN) { 2549 mode = instance->init_type; 2550 if (mode == 3 || mode == 4) { /* Cf will return here to check for TEST */ 2551 instance->o_state = ONCORE_RESET_SENT; 2552 cp = "state = ONCORE_RESET_SENT"; 2553 record_clock_stats(&(instance->peer->srcadr), cp); 2554 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof(oncore_cmd_Cf)); 2555 } else { 2556 instance->o_state = ONCORE_TEST_SENT; 2557 cp = "state = ONCORE_TEST_SENT"; 2558 record_clock_stats(&(instance->peer->srcadr), cp); 2559 } 2560 } 2561 2562 if (instance->o_state == ONCORE_TEST_SENT) { 2563 if (instance->chan == 6) 2564 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ca, sizeof(oncore_cmd_Ca)); 2565 else if (instance->chan == 8) 2566 oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof(oncore_cmd_Fa)); 2567 else if (instance->chan == 12) 2568 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ia, sizeof(oncore_cmd_Ia)); 2569 } else if (instance->o_state == ONCORE_INIT) 2570 oncore_msg_Cj_init(instance, buf, len); 2571 } 2572 2573 2574 2575 /* The information on determining a Oncore 'Model', viz VP, UT, etc, from 2576 * the Model Number comes from "Richard M. Hambly" <rick@cnssys.com> 2577 * and from Motorola. Until recently Rick was the only source of 2578 * this information as Motorola didn't give the information out. 2579 * 2580 * Determine the Type from the Model #, this determines #chan and if TRAIM is 2581 * available. 2582 * 2583 * The Information from this routine is NO LONGER USED. 2584 * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED 2585 */ 2586 2587 static void 2588 oncore_msg_Cj_id( 2589 struct instance *instance, 2590 u_char *buf, 2591 size_t len 2592 ) 2593 { 2594 char *cp, *cp1, *cp2, Model[21], Msg[160]; 2595 2596 /* Write Receiver ID message to clockstats file */ 2597 2598 instance->Cj[294] = '\0'; 2599 for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) { 2600 cp1 = strchr(cp, '\r'); 2601 if (!cp1) 2602 cp1 = (char *)&instance->Cj[294]; 2603 *cp1 = '\0'; 2604 record_clock_stats(&(instance->peer->srcadr), cp); 2605 *cp1 = '\r'; 2606 cp = cp1+2; 2607 } 2608 2609 /* next, the Firmware Version and Revision numbers */ 2610 2611 instance->version = atoi(&instance->Cj[83]); 2612 instance->revision = atoi(&instance->Cj[111]); 2613 2614 /* from model number decide which Oncore this is, 2615 and then the number of channels */ 2616 2617 for (cp=&instance->Cj[160]; *cp == ' '; cp++) /* start right after 'Model #' */ 2618 ; 2619 cp1 = cp; 2620 cp2 = Model; 2621 for (; !isspace((int)*cp) && cp-cp1 < 20; cp++, cp2++) 2622 *cp2 = *cp; 2623 *cp2 = '\0'; 2624 2625 cp = 0; 2626 if (!strncmp(Model, "PVT6", (size_t) 4)) { 2627 cp = "PVT6"; 2628 instance->model = ONCORE_PVT6; 2629 } else if (Model[0] == 'A') { 2630 cp = "Basic"; 2631 instance->model = ONCORE_BASIC; 2632 } else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) { 2633 cp = "VP"; 2634 instance->model = ONCORE_VP; 2635 } else if (Model[0] == 'P') { 2636 cp = "M12"; 2637 instance->model = ONCORE_M12; 2638 } else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') { 2639 if (Model[5] == 'N') { 2640 cp = "GT"; 2641 instance->model = ONCORE_GT; 2642 } else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') { 2643 cp = "GT+"; 2644 instance->model = ONCORE_GTPLUS; 2645 } else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) { 2646 cp = "UT"; 2647 instance->model = ONCORE_UT; 2648 } else if (Model[1] == '5' && Model[5] == 'G') { 2649 cp = "UT+"; 2650 instance->model = ONCORE_UTPLUS; 2651 } else if (Model[1] == '6' && Model[5] == 'G') { 2652 cp = "SL"; 2653 instance->model = ONCORE_SL; 2654 } else { 2655 cp = "Unknown"; 2656 instance->model = ONCORE_UNKNOWN; 2657 } 2658 } else { 2659 cp = "Unknown"; 2660 instance->model = ONCORE_UNKNOWN; 2661 } 2662 2663 /* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */ 2664 2665 sprintf(Msg, "This looks like an Oncore %s with version %d.%d firmware.", cp, instance->version, instance->revision); 2666 record_clock_stats(&(instance->peer->srcadr), Msg); 2667 2668 instance->chan_id = 8; /* default */ 2669 if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6) 2670 instance->chan_id = 6; 2671 else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS) 2672 instance->chan_id = 8; 2673 else if (instance->model == ONCORE_M12) 2674 instance->chan_id = 12; 2675 2676 instance->traim_id = 0; /* default */ 2677 if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6) 2678 instance->traim_id = 0; 2679 else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS) 2680 instance->traim_id = 1; 2681 else if (instance->model == ONCORE_M12) 2682 instance->traim_id = -1; 2683 2684 sprintf(Msg, "Channels = %d, TRAIM = %s", instance->chan_id, 2685 ((instance->traim_id < 0) ? "UNKNOWN" : ((instance->traim_id > 0) ? "ON" : "OFF"))); 2686 record_clock_stats(&(instance->peer->srcadr), Msg); 2687 } 2688 2689 2690 2691 /* OK, know type of Oncore, have possibly reset it, and have tested it. 2692 * We know the number of channels. 2693 * We will determine whether we have TRAIM before we actually start. 2694 * Now initialize. 2695 */ 2696 2697 static void 2698 oncore_msg_Cj_init( 2699 struct instance *instance, 2700 u_char *buf, 2701 size_t len 2702 ) 2703 { 2704 char *cp, Cmd[20], Msg[160]; 2705 int mode; 2706 2707 2708 /* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to 2709 * start again if we go from 0D -> 3D, then loses them again when we 2710 * go from 3D -> 0D. We do this to get a @@Ea message for SHMEM. 2711 * For NOW we will turn this aspect of filling SHMEM off for the M12 2712 */ 2713 2714 if (instance->chan == 12) { 2715 instance->shmem_bad_Ea = 1; 2716 sprintf(Msg, "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***", instance->version, instance->revision); 2717 record_clock_stats(&(instance->peer->srcadr), Msg); 2718 } 2719 2720 oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */ 2721 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */ 2722 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */ 2723 oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */ 2724 oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */ 2725 oncore_sendmsg(instance->ttyfd, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */ 2726 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */ 2727 2728 mode = instance->init_type; 2729 2730 /* If there is Position input in the Config file 2731 * and mode = (1,3) set it as posn hold posn, goto 0D mode. 2732 * or mode = (2,4) set it as INITIAL position, and do Site Survey. 2733 */ 2734 2735 if (instance->posn_set) { 2736 record_clock_stats(&(instance->peer->srcadr), "Setting Posn from input data"); 2737 oncore_set_posn(instance); /* this should print posn indirectly thru the As cmd */ 2738 } else /* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */ 2739 if (instance->chan != 12) 2740 oncore_sendmsg(instance->ttyfd, oncore_cmd_Atx, sizeof(oncore_cmd_Atx)); 2741 2742 if (mode != 0) { 2743 /* cable delay in ns */ 2744 memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az)); 2745 w32_buf(&Cmd[-2+4], instance->delay); 2746 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Az)); /* 6,8,12 */ 2747 2748 /* PPS offset in ns */ 2749 if (instance->offset) { 2750 memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay)); /* some have it, some don't */ 2751 w32_buf(&Cmd[-2+4], instance->offset); /* will check for hw response */ 2752 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ay)); 2753 } 2754 2755 /* Satellite mask angle */ 2756 2757 if (instance->Ag != 0xff) { /* will have 0xff in it if not set by user */ 2758 memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag)); 2759 Cmd[-2+4] = instance->Ag; 2760 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ag)); 2761 } 2762 } 2763 2764 /* 6, 8 12 chan - Position/Status/Data Output Message, 1/s 2765 * now we're really running 2766 * these were ALL started in the chan test, 2767 * However, if we had mode=3,4 then commands got turned off, so we turn 2768 * them on again here just in case 2769 */ 2770 2771 if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */ 2772 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0)); 2773 oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0)); 2774 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0)); 2775 oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); 2776 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba )); 2777 } else if (instance->chan == 8) { /* start 8chan, kill 6,12chan commands */ 2778 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0)); 2779 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0)); 2780 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0)); 2781 oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); 2782 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea )); 2783 } else if (instance->chan == 12){ /* start 12chan, kill 6,12chan commands */ 2784 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0)); 2785 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0)); 2786 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0)); 2787 oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0)); 2788 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha )); 2789 } 2790 2791 instance->count = 1; 2792 instance->o_state = ONCORE_ALMANAC; 2793 cp = "state = ONCORE_ALMANAC"; 2794 record_clock_stats(&(instance->peer->srcadr), cp); 2795 } 2796 2797 2798 2799 /* 12chan position */ 2800 2801 static void 2802 oncore_msg_Ga( 2803 struct instance *instance, 2804 u_char *buf, 2805 size_t len 2806 ) 2807 { 2808 char Msg[160]; 2809 long lat, lon, ht; 2810 double Lat, Lon, Ht; 2811 2812 2813 lat = buf_w32(&buf[4]); 2814 lon = buf_w32(&buf[8]); 2815 ht = buf_w32(&buf[12]); /* GPS ellipsoid */ 2816 2817 Lat = lat; 2818 Lon = lon; 2819 Ht = ht; 2820 2821 Lat /= 3600000; 2822 Lon /= 3600000; 2823 Ht /= 100; 2824 2825 2826 sprintf(Msg, "Ga Posn Lat = %.7f, Lon = %.7f, Ht = %.2f", Lat, Lon, Ht); 2827 record_clock_stats(&(instance->peer->srcadr), Msg); 2828 2829 instance->ss_lat = lat; 2830 instance->ss_long = lon; 2831 instance->ss_ht = ht; 2832 2833 oncore_print_posn(instance); 2834 } 2835 2836 2837 2838 /* 12 chan time/date */ 2839 2840 static void 2841 oncore_msg_Gb( 2842 struct instance *instance, 2843 u_char *buf, 2844 size_t len 2845 ) 2846 { 2847 char Msg[160], *gmts; 2848 int mo, d, y, h, m, s, gmth, gmtm; 2849 2850 mo = buf[4]; 2851 d = buf[5]; 2852 y = 256*buf[6]+buf[7]; 2853 2854 h = buf[8]; 2855 m = buf[9]; 2856 s = buf[10]; 2857 2858 gmts = ((buf[11] == 0) ? "+" : "-"); 2859 gmth = buf[12]; 2860 gmtm = buf[13]; 2861 2862 sprintf(Msg, "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)", 2863 d, Month[mo+1], y, h, m, s, gmts, gmth, gmtm); 2864 record_clock_stats(&(instance->peer->srcadr), Msg); 2865 } 2866 2867 2868 2869 /* 2870 * Try to use Oncore M12+Timing Auto Survey Feature 2871 * If its not there (M12), set flag to do it ourselves. 2872 */ 2873 2874 static void 2875 oncore_msg_Gd( 2876 struct instance *instance, 2877 u_char *buf, 2878 size_t len 2879 ) 2880 { 2881 char *cp; 2882 2883 if (instance->site_survey == ONCORE_SS_TESTING) { 2884 if (buf[4] == 3) { 2885 record_clock_stats(&(instance->peer->srcadr), 2886 "Initiating hardware 3D site survey"); 2887 2888 cp = "SSstate = ONCORE_SS_HW"; 2889 record_clock_stats(&(instance->peer->srcadr), cp); 2890 instance->site_survey = ONCORE_SS_HW; 2891 } 2892 } 2893 } 2894 2895 2896 2897 /* Leap Second for M12, gives all info from satellite message */ 2898 /* also in UT v3.0 */ 2899 2900 static void 2901 oncore_msg_Gj( 2902 struct instance *instance, 2903 u_char *buf, 2904 size_t len 2905 ) 2906 { 2907 int dt; 2908 char Msg[160], *cp; 2909 2910 instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */ 2911 2912 /* print the message to verify whats there */ 2913 2914 dt = buf[5] - buf[4]; 2915 2916 #if 1 2917 sprintf(Msg, "ONCORE[%d]: Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d", 2918 instance->unit, 2919 buf[4], buf[5], 256*buf[6]+buf[7], buf[8], buf[9], buf[10], 2920 (buf[14]+256*(buf[13]+256*(buf[12]+256*buf[11]))), 2921 buf[15], buf[16], buf[17]); 2922 record_clock_stats(&(instance->peer->srcadr), Msg); 2923 #endif 2924 if (dt) { 2925 sprintf(Msg, "ONCORE[%d]: Leap second (%d) scheduled for %d%s%d at %d:%d:%d", 2926 instance->unit, 2927 dt, buf[9], Month[buf[8]], 256*buf[6]+buf[7], 2928 buf[15], buf[16], buf[17]); 2929 record_clock_stats(&(instance->peer->srcadr), Msg); 2930 } 2931 2932 /* Only raise warning within a month of the leap second */ 2933 2934 instance->peer->leap = LEAP_NOWARNING; 2935 cp = "Set peer.leap to LEAP_NOWARNING"; 2936 2937 if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */ 2938 buf[8] == instance->BEHa[4]) { /* month */ 2939 if (dt) { 2940 if (dt < 0) { 2941 instance->peer->leap = LEAP_DELSECOND; 2942 cp = "Set peer.leap to LEAP_DELSECOND"; 2943 } else { 2944 instance->peer->leap = LEAP_ADDSECOND; 2945 cp = "Set peer.leap to LEAP_ADDSECOND"; 2946 } 2947 } 2948 } 2949 record_clock_stats(&(instance->peer->srcadr), cp); 2950 } 2951 2952 2953 2954 /* Power on failure */ 2955 2956 static void 2957 oncore_msg_Sz( 2958 struct instance *instance, 2959 u_char *buf, 2960 size_t len 2961 ) 2962 { 2963 const char *cp; 2964 2965 cp = "Oncore: System Failure at Power On"; 2966 if (instance && instance->peer) { 2967 record_clock_stats(&(instance->peer->srcadr), cp); 2968 oncore_shutdown(instance->unit, instance->peer); 2969 } 2970 } 2971 2972 /************** Small Subroutines ***************/ 2973 2974 2975 static void 2976 oncore_antenna_report( 2977 struct instance *instance, 2978 enum antenna_state new_state) 2979 { 2980 char *cp; 2981 2982 if (instance->ant_state == new_state) 2983 return; 2984 2985 switch (new_state) { 2986 case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK"; break; 2987 case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)"; break; 2988 case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break; 2989 case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)"; break; 2990 default: cp = "GPS antenna: ?"; break; 2991 } 2992 2993 instance->ant_state = new_state; 2994 record_clock_stats(&instance->peer->srcadr, cp); 2995 } 2996 2997 2998 2999 static void 3000 oncore_chan_test( 3001 struct instance *instance 3002 ) 3003 { 3004 char *cp; 3005 3006 /* subroutine oncore_Cj_id has determined the number of channels from the 3007 * model number of the attached oncore. This is not always correct since 3008 * the oncore could have non-standard firmware. Here we check (independently) by 3009 * trying a 6, 8, and 12 chan command, and see which responds. 3010 * Caution: more than one CAN respond. 3011 * 3012 * This #chan is used by the code rather than that calculated from the model number. 3013 */ 3014 3015 instance->o_state = ONCORE_CHECK_CHAN; 3016 cp = "state = ONCORE_CHECK_CHAN"; 3017 record_clock_stats(&(instance->peer->srcadr), cp); 3018 3019 instance->count3 = 1; 3020 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba)); 3021 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea)); 3022 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha)); 3023 } 3024 3025 3026 3027 /* check for a GOOD Almanac, have we got one yet? */ 3028 3029 static void 3030 oncore_check_almanac( 3031 struct instance *instance 3032 ) 3033 { 3034 if (instance->chan == 6) { 3035 instance->rsm.bad_almanac = instance->BEHa[64]&0x1; 3036 instance->rsm.bad_fix = instance->BEHa[64]&0x52; 3037 } else if (instance->chan == 8) { 3038 instance->rsm.bad_almanac = instance->BEHa[72]&0x1; 3039 instance->rsm.bad_fix = instance->BEHa[72]&0x52; 3040 } else if (instance->chan == 12) { 3041 int bits1, bits2; 3042 3043 bits1 = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */ 3044 bits2 = instance->BEHa[130]; 3045 instance->rsm.bad_almanac = (bits2 & 0x80); 3046 instance->rsm.bad_fix = (bits2 & 0x8) || (bits1 == 0x2); 3047 /* too few sat Bad Geom */ 3048 #if 0 3049 fprintf(stderr, "ONCORE[%d]: DEBUG BITS: (%x %x), (%x %x), %x %x %x %x %x\n", 3050 instance->unit, 3051 instance->BEHa[129], instance->BEHa[130], bits1, bits2, instance->mode == MODE_0D, 3052 instance->mode == MODE_2D, instance->mode == MODE_3D, 3053 instance->rsm.bad_almanac, instance->rsm.bad_fix); 3054 #endif 3055 } 3056 } 3057 3058 3059 3060 /* check the antenna for changes (did it get unplugged?) */ 3061 3062 static void 3063 oncore_check_antenna( 3064 struct instance *instance 3065 ) 3066 { 3067 enum antenna_state antenna; /* antenna state */ 3068 3069 antenna = instance->ant_state; 3070 if (instance->chan == 12) 3071 antenna = (instance->BEHa[130] & 0x6 ) >> 1; 3072 else 3073 antenna = (instance->BEHa[37] & 0xc0) >> 6; /* prob unset 6, set GT, UT unset VP */ 3074 3075 oncore_antenna_report (instance, antenna); 3076 } 3077 3078 3079 3080 /* 3081 * Check the leap second status once per day. 3082 * 3083 * Note that the ONCORE firmware for the Bj command is wrong at 3084 * least in the VP. 3085 * It starts advertising a LEAP SECOND as soon as the GPS satellite 3086 * data message (page 18, subframe 4) is updated to a date in the 3087 * future, and does not wait for the month that it will occur. 3088 * The event will usually be advertised several months in advance. 3089 * Since there is a one bit flag, there is no way to tell if it is 3090 * this month, or when... 3091 * 3092 * As such, we have the workaround below, of only checking for leap 3093 * seconds with the Bj command in June/December. 3094 * 3095 * The Gj command gives more information, and we can tell in which 3096 * month to apply the correction. 3097 * 3098 * Note that with the VP we COULD read the raw data message, and 3099 * interpret it ourselves, but since this is specific to this receiver 3100 * only, and the above workaround is adequate, we don't bother. 3101 */ 3102 3103 static void 3104 oncore_check_leap_sec( 3105 struct instance *instance 3106 ) 3107 { 3108 if (instance->Bj_day != instance->BEHa[5]) { /* do this 1/day */ 3109 instance->Bj_day = instance->BEHa[5]; 3110 3111 if (instance->saw_Gj < 0) { /* -1 DONT have Gj use Bj */ 3112 if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) 3113 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj)); 3114 return; 3115 } 3116 3117 if (instance->saw_Gj == 0) /* 0 is dont know if we have Gj */ 3118 instance->count4 = 1; 3119 3120 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gj, sizeof(oncore_cmd_Gj)); 3121 return; 3122 } 3123 3124 /* Gj works for some 6/8 chan UT and the M12 */ 3125 /* if no response from Gj in 5 sec, we try Bj */ 3126 /* which isnt implemented in all the GT/UT either */ 3127 3128 if (instance->count4) { /* delay, waiting for Gj response */ 3129 if (instance->saw_Gj == 1) 3130 instance->count4 = 0; 3131 else if (instance->count4++ > 5) { /* delay, waiting for Gj response */ 3132 instance->saw_Gj = -1; /* didnt see it, will use Bj */ 3133 instance->count4 = 0; 3134 if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) 3135 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj)); 3136 } 3137 } 3138 } 3139 3140 3141 3142 /* check the message checksum, 3143 * buf points to START of message ( @@ ) 3144 * len is length WITH CR/LF. 3145 */ 3146 3147 static int 3148 oncore_checksum_ok( 3149 u_char *buf, 3150 int len 3151 ) 3152 { 3153 int i, j; 3154 3155 j = 0; 3156 for (i = 2; i < len-3; i++) 3157 j ^= buf[i]; 3158 3159 return(j == buf[len-3]); 3160 } 3161 3162 3163 3164 static void 3165 oncore_compute_dH( 3166 struct instance *instance 3167 ) 3168 { 3169 int GPS, MSL; 3170 char Msg[160]; 3171 3172 /* Here calculate dH = GPS - MSL for output message */ 3173 /* also set Altitude Hold mode if GT */ 3174 3175 instance->have_dH = 1; 3176 if (instance->chan == 12) { 3177 GPS = buf_w32(&instance->BEHa[39]); 3178 MSL = buf_w32(&instance->BEHa[43]); 3179 } else { 3180 GPS = buf_w32(&instance->BEHa[23]); 3181 MSL = buf_w32(&instance->BEHa[27]); 3182 } 3183 instance->dH = GPS - MSL; 3184 instance->dH /= 100.; 3185 3186 /* if MSL is not set, the calculation is meaningless */ 3187 3188 if (MSL) { /* not set ! */ 3189 sprintf(Msg, "dH = (GPS - MSL) = %.2fm", instance->dH); 3190 record_clock_stats(&(instance->peer->srcadr), Msg); 3191 } 3192 } 3193 3194 3195 3196 /* 3197 * try loading Almanac from shmem (where it was copied from shmem_old 3198 */ 3199 3200 static void 3201 oncore_load_almanac( 3202 struct instance *instance 3203 ) 3204 { 3205 u_char *cp, Cmd[20]; 3206 int n; 3207 struct timeval tv; 3208 struct tm *tm; 3209 3210 if (!instance->shmem) 3211 return; 3212 3213 #if 1 3214 for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) { 3215 if (!strncmp(cp, "@@Cb", 4) && 3216 oncore_checksum_ok(cp, 33) && 3217 (*(cp+4) == 4 || *(cp+4) == 5)) { 3218 write(instance->ttyfd, cp, n); 3219 #if 1 3220 oncore_print_Cb(instance, cp); 3221 #endif 3222 } 3223 } 3224 #else 3225 /************DEBUG************/ 3226 for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) { 3227 char Msg[160]; 3228 3229 sprintf(Msg, "See %c%c%c%c %d", *(cp), *(cp+1), *(cp+2), *(cp+3), *(cp+4)); 3230 record_clock_stats(&(instance->peer->srcadr), Msg); 3231 3232 if (!strncmp(cp, "@@Cb", 4)) { 3233 oncore_print_Cb(instance, cp); 3234 if (oncore_checksum_ok(cp, 33)) { 3235 if (*(cp+4) == 4 || *(cp+4) == 5) { 3236 record_clock_stats(&(instance->peer->srcadr), "GOOD SF"); 3237 write(instance->ttyfd, cp, n); 3238 } else 3239 record_clock_stats(&(instance->peer->srcadr), "BAD SF"); 3240 } else 3241 record_clock_stats(&(instance->peer->srcadr), "BAD CHECKSUM"); 3242 } 3243 } 3244 /************DEBUG************/ 3245 #endif 3246 3247 /* Must load position and time or the Almanac doesn't do us any good */ 3248 3249 if (!instance->posn_set) { /* if we input a posn use it, else from SHMEM */ 3250 record_clock_stats(&(instance->peer->srcadr), "Loading Posn from SHMEM"); 3251 for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) { 3252 if ((instance->chan == 6 && (!strncmp(cp, "@@Ba", 4) && oncore_checksum_ok(cp, 68))) || 3253 (instance->chan == 8 && (!strncmp(cp, "@@Ea", 4) && oncore_checksum_ok(cp, 76))) || 3254 (instance->chan == 12 && (!strncmp(cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) { 3255 int ii, jj, kk; 3256 3257 instance->posn_set = 1; 3258 ii = buf_w32(cp + 15); 3259 jj = buf_w32(cp + 19); 3260 kk = buf_w32(cp + 23); 3261 { 3262 char Msg[160]; 3263 sprintf(Msg, "SHMEM posn = %d (%d, %d, %d)", cp-instance->shmem, ii, jj, kk); 3264 record_clock_stats(&(instance->peer->srcadr), Msg); 3265 } 3266 if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */ 3267 instance->ss_lat = ii; 3268 instance->ss_long = jj; 3269 instance->ss_ht = kk; 3270 } 3271 } 3272 } 3273 } 3274 oncore_set_posn(instance); 3275 3276 /* and set time to time from Computer clock */ 3277 3278 gettimeofday(&tv, 0); 3279 tm = gmtime((const time_t *) &tv.tv_sec); 3280 #if 1 3281 { 3282 char Msg[160]; 3283 sprintf(Msg, "DATE %d %d %d, %d %d %d", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday, 3284 tm->tm_hour, tm->tm_min, tm->tm_sec); 3285 record_clock_stats(&(instance->peer->srcadr), Msg); 3286 } 3287 #endif 3288 if (instance->chan == 12) { 3289 memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb)); 3290 Cmd[-2+4] = tm->tm_mon; 3291 Cmd[-2+5] = tm->tm_mday; 3292 Cmd[-2+6] = (1900+tm->tm_year)/256; 3293 Cmd[-2+7] = (1900+tm->tm_year)%256; 3294 Cmd[-2+8] = tm->tm_hour; 3295 Cmd[-2+9] = tm->tm_min; 3296 Cmd[-2+10] = tm->tm_sec; 3297 Cmd[-2+11] = 0; 3298 Cmd[-2+12] = 0; 3299 Cmd[-2+13] = 0; 3300 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Gb)); 3301 } else { 3302 /* First set GMT offset to zero */ 3303 3304 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ab, sizeof(oncore_cmd_Ab)); 3305 3306 memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac)); 3307 Cmd[-2+4] = tm->tm_mon; 3308 Cmd[-2+5] = tm->tm_mday; 3309 Cmd[-2+6] = (1900+tm->tm_year)/256; 3310 Cmd[-2+7] = (1900+tm->tm_year)%256; 3311 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ac)); 3312 3313 memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa)); 3314 Cmd[-2+4] = tm->tm_hour; 3315 Cmd[-2+5] = tm->tm_min; 3316 Cmd[-2+6] = tm->tm_sec; 3317 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Aa)); 3318 } 3319 3320 record_clock_stats(&(instance->peer->srcadr), "Setting Posn and Time after Loading Almanac"); 3321 } 3322 3323 3324 3325 /* Almanac data input */ 3326 3327 static void 3328 oncore_print_Cb( 3329 struct instance *instance, 3330 u_char *cp 3331 ) 3332 { 3333 #if 0 3334 int ii; 3335 char Msg[160]; 3336 3337 printf("DEBUG: See: %c%c%c%c\n", *(cp), *(cp+1), *(cp+2), *(cp+3)); 3338 printf("DEBUG: Cb: [%d,%d]", *(cp+4), *(cp+5)); 3339 for(ii=0; ii<33; ii++) 3340 printf(" %d", *(cp+ii)); 3341 printf("\n"); 3342 3343 sprintf(Msg, "Debug: Cb: [%d,%d]", *(cp+4), *(cp+5)); 3344 record_clock_stats(&(instance->peer->srcadr), Msg); 3345 #endif 3346 } 3347 3348 3349 #if 0 3350 static void 3351 oncore_print_array( 3352 u_char *cp, 3353 int n 3354 ) 3355 { 3356 int jj, i, j, nn; 3357 3358 nn = 0; 3359 printf("\nTOP\n"); 3360 jj = n/16; 3361 for (j=0; j<jj; j++) { 3362 printf("%4d: ", nn); 3363 nn += 16; 3364 for (i=0; i<16; i++) 3365 printf(" %o", *cp++); 3366 printf("\n"); 3367 } 3368 } 3369 #endif 3370 3371 3372 static void 3373 oncore_print_posn( 3374 struct instance *instance 3375 ) 3376 { 3377 char Msg[120], ew, ns; 3378 double xd, xm, xs, yd, ym, ys, hm, hft; 3379 int idx, idy, is, imx, imy; 3380 long lat, lon; 3381 3382 record_clock_stats(&(instance->peer->srcadr), "Posn:"); 3383 ew = 'E'; 3384 lon = instance->ss_long; 3385 if (lon < 0) { 3386 ew = 'W'; 3387 lon = -lon; 3388 } 3389 3390 ns = 'N'; 3391 lat = instance->ss_lat; 3392 if (lat < 0) { 3393 ns = 'S'; 3394 lat = -lat; 3395 } 3396 3397 hm = instance->ss_ht/100.; 3398 hft= hm/0.3048; 3399 3400 xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */ 3401 yd = lon/3600000.; 3402 sprintf(Msg, "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) GPS", ns, xd, ew, yd, hm, hft); 3403 record_clock_stats(&(instance->peer->srcadr), Msg); 3404 3405 idx = xd; 3406 idy = yd; 3407 imx = lat%3600000; 3408 imy = lon%3600000; 3409 xm = imx/60000.; 3410 ym = imy/60000.; 3411 sprintf(Msg, 3412 "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %7.2fm (%7.2fft) GPS", ns, idx, xm, ew, idy, ym, hm, hft); 3413 record_clock_stats(&(instance->peer->srcadr), Msg); 3414 3415 imx = xm; 3416 imy = ym; 3417 is = lat%60000; 3418 xs = is/1000.; 3419 is = lon%60000; 3420 ys = is/1000.; 3421 sprintf(Msg, 3422 "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft); 3423 record_clock_stats(&(instance->peer->srcadr), Msg); 3424 } 3425 3426 3427 3428 /* 3429 * write message to Oncore. 3430 */ 3431 3432 static void 3433 oncore_sendmsg( 3434 int fd, 3435 u_char *ptr, 3436 size_t len 3437 ) 3438 { 3439 u_char cs = 0; 3440 3441 if (debug > 4) 3442 printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], (int) len); 3443 write(fd, "@@", (size_t) 2); 3444 write(fd, ptr, len); 3445 while (len--) 3446 cs ^= *ptr++; 3447 write(fd, &cs, (size_t) 1); 3448 write(fd, "\r\n", (size_t) 2); 3449 } 3450 3451 3452 3453 static void 3454 oncore_set_posn( 3455 struct instance *instance 3456 ) 3457 { 3458 int mode; 3459 char Cmd[20]; 3460 3461 /* Turn OFF position hold, it needs to be off to set position (for some units), 3462 will get set ON in @@Ea later */ 3463 3464 if (instance->chan == 12) 3465 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */ 3466 else { 3467 oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */ 3468 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */ 3469 } 3470 3471 mode = instance->init_type; 3472 3473 if (mode != 0) { /* first set posn hold position */ 3474 memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As)); /* don't modify static variables */ 3475 w32_buf(&Cmd[-2+4], (int) instance->ss_lat); 3476 w32_buf(&Cmd[-2+8], (int) instance->ss_long); 3477 w32_buf(&Cmd[-2+12], (int) instance->ss_ht); 3478 Cmd[-2+16] = 0; 3479 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_As)); /* posn hold 3D posn (6/8/12) */ 3480 3481 memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au)); 3482 w32_buf(&Cmd[-2+4], (int) instance->ss_ht); 3483 Cmd[-2+8] = 0; 3484 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Au)); /* altitude hold (6/8/12 not UT, M12T) */ 3485 3486 /* next set current position */ 3487 3488 if (instance->chan == 12) { 3489 memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga)); 3490 w32_buf(&Cmd[-2+4], (int) instance->ss_lat); 3491 w32_buf(&Cmd[-2+8], (int) instance->ss_long); 3492 w32_buf(&Cmd[-2+12],(int) instance->ss_ht); 3493 Cmd[-2+16] = 0; 3494 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ga)); /* 3d posn (12) */ 3495 } else { 3496 memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad)); 3497 w32_buf(&Cmd[-2+4], (int) instance->ss_lat); 3498 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ad)); /* lat (6/8) */ 3499 3500 memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae)); 3501 w32_buf(&Cmd[-2+4], (int) instance->ss_long); 3502 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ae)); /* long (6/8) */ 3503 3504 memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af)); 3505 w32_buf(&Cmd[-2+4], (int) instance->ss_ht); 3506 Cmd[-2+8] = 0; 3507 oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Af)); /* ht (6/8) */ 3508 } 3509 3510 /* Finally, turn on position hold */ 3511 3512 if (instance->chan == 12) 3513 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); 3514 else 3515 oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1)); 3516 } 3517 } 3518 3519 3520 3521 static void 3522 oncore_set_traim( 3523 struct instance *instance 3524 ) 3525 { 3526 char Msg[160]; 3527 3528 if (instance->traim_in != -1) /* set in Input */ 3529 instance->traim = instance->traim_in; 3530 else 3531 instance->traim = instance->traim_ck; 3532 3533 sprintf(Msg, "Input says TRAIM = %d", instance->traim_in); 3534 record_clock_stats(&(instance->peer->srcadr), Msg); 3535 sprintf(Msg, "Model # says TRAIM = %d", instance->traim_id); 3536 record_clock_stats(&(instance->peer->srcadr), Msg); 3537 sprintf(Msg, "Testing says TRAIM = %d", instance->traim_ck); 3538 record_clock_stats(&(instance->peer->srcadr), Msg); 3539 sprintf(Msg, "Using TRAIM = %d", instance->traim); 3540 record_clock_stats(&(instance->peer->srcadr), Msg); 3541 3542 if (instance->traim_ck == 1 && instance->traim == 0) { 3543 /* if it should be off, and I turned it on during testing, 3544 then turn it off again */ 3545 if (instance->chan == 6) 3546 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx)); 3547 else if (instance->chan == 8) 3548 oncore_sendmsg(instance->ttyfd, oncore_cmd_Enx, sizeof(oncore_cmd_Enx)); 3549 else /* chan == 12 */ 3550 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0)); 3551 } 3552 } 3553 3554 3555 3556 /* 3557 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn. 3558 */ 3559 3560 static void 3561 oncore_shmem_get_3D( 3562 struct instance *instance 3563 ) 3564 { 3565 if (instance->pp->second%15 == 3) { /* start the sequence */ /* by changing mode */ 3566 instance->shmem_reset = 1; 3567 if (instance->chan == 12) { 3568 if (instance->shmem_Posn == 2) 3569 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd2, sizeof(oncore_cmd_Gd2)); /* 2D */ 3570 else 3571 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* 3D */ 3572 } else { 3573 if (instance->saw_At) { /* out of 0D -> 3D mode */ 3574 oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); 3575 if (instance->shmem_Posn == 2) /* 3D -> 2D mode */ 3576 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); 3577 } else 3578 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); 3579 } 3580 } else if (instance->shmem_reset || (instance->mode != MODE_0D)) { 3581 instance->shmem_reset = 0; 3582 if (instance->chan == 12) 3583 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); /* 0D */ 3584 else { 3585 if (instance->saw_At) { 3586 if (instance->mode == MODE_2D) /* 2D -> 3D or 0D mode */ 3587 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); 3588 oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1)); /* to 0D mode */ 3589 } else 3590 oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); 3591 } 3592 } 3593 } 3594 3595 3596 3597 /* 3598 * Here we do the Software SiteSurvey. 3599 * We have to average our own position for the Position Hold Mode 3600 * We use Heights from the GPS ellipsoid. 3601 * We check for the END of either HW or SW SiteSurvey. 3602 */ 3603 3604 static void 3605 oncore_ss( 3606 struct instance *instance 3607 ) 3608 { 3609 char *cp, Msg[160]; 3610 double lat, lon, ht; 3611 3612 3613 if (instance->site_survey == ONCORE_SS_HW) { 3614 3615 /* 3616 * Check to see if Hardware SiteSurvey has Finished. 3617 */ 3618 3619 if ((instance->chan == 8 && !(instance->BEHa[37] & 0x20)) || 3620 (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) { 3621 record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode"); 3622 3623 if (instance->chan == 12) 3624 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gax, sizeof(oncore_cmd_Gax)); 3625 else 3626 oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof(oncore_cmd_Asx)); 3627 3628 cp = "SSstate = ONCORE_SS_DONE"; 3629 record_clock_stats(&(instance->peer->srcadr), cp); 3630 instance->site_survey = ONCORE_SS_DONE; 3631 } 3632 } else { 3633 /* 3634 * Must be a Software Site Survey. 3635 */ 3636 3637 if (instance->rsm.bad_fix) /* Not if poor geometry or less than 3 sats */ 3638 return; 3639 3640 if (instance->mode != MODE_3D) /* Use only 3D Fixes */ 3641 return; 3642 3643 instance->ss_lat += buf_w32(&instance->BEHa[15]); 3644 instance->ss_long += buf_w32(&instance->BEHa[19]); 3645 instance->ss_ht += buf_w32(&instance->BEHa[23]); /* GPS ellipsoid */ 3646 instance->ss_count++; 3647 3648 if (instance->ss_count != POS_HOLD_AVERAGE) 3649 return; 3650 3651 instance->ss_lat /= POS_HOLD_AVERAGE; 3652 instance->ss_long /= POS_HOLD_AVERAGE; 3653 instance->ss_ht /= POS_HOLD_AVERAGE; 3654 3655 sprintf(Msg, "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)", 3656 instance->ss_lat, instance->ss_long, instance->ss_ht); 3657 record_clock_stats(&(instance->peer->srcadr), Msg); 3658 lat = instance->ss_lat/3600000.; 3659 lon = instance->ss_long/3600000.; 3660 ht = instance->ss_ht/100; 3661 sprintf(Msg, "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)", 3662 lat, lon, ht); 3663 record_clock_stats(&(instance->peer->srcadr), Msg); 3664 3665 oncore_set_posn(instance); 3666 3667 record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode"); 3668 3669 cp = "SSstate = ONCORE_SS_DONE"; 3670 record_clock_stats(&(instance->peer->srcadr), cp); 3671 instance->site_survey = ONCORE_SS_DONE; 3672 } 3673 } 3674 3675 3676 3677 static int 3678 oncore_wait_almanac( 3679 struct instance *instance 3680 ) 3681 { 3682 if (instance->rsm.bad_almanac) { 3683 if (debug) 3684 printf("ONCORE[%d]: waiting for almanac\n", instance->unit); 3685 3686 /* 3687 * If we get here (first time) then we don't have an almanac in memory. 3688 * Check if we have a SHMEM, and if so try to load whatever is there. 3689 */ 3690 3691 if (!instance->almanac_from_shmem) { 3692 instance->almanac_from_shmem = 1; 3693 oncore_load_almanac(instance); 3694 } 3695 return(1); 3696 } else { /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn 3697 commands, and can finally check for TRAIM. Again, we set a delay 3698 (5sec) and wait for things to settle down */ 3699 3700 if (instance->chan == 6) 3701 oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn, sizeof(oncore_cmd_Bn)); 3702 else if (instance->chan == 8) 3703 oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof(oncore_cmd_En)); 3704 else if (instance->chan == 12) { 3705 oncore_sendmsg(instance->ttyfd, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */ 3706 oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */ 3707 oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */ 3708 } 3709 instance->traim_delay = 1; 3710 3711 record_clock_stats(&(instance->peer->srcadr), "Have now loaded an ALMANAC"); 3712 3713 instance->o_state = ONCORE_RUN; 3714 record_clock_stats(&(instance->peer->srcadr), "state = ONCORE_RUN"); 3715 } 3716 return(0); 3717 } 3718 3719 3720 3721 #else 3722 int refclock_oncore_bs; 3723 #endif /* REFCLOCK */ 3724