/* * $Id: refclock_ripencc.c,v 1.13 2002/06/18 14:20:55 marks Exp marks $ * * Copyright (c) 2002 RIPE NCC * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * * * This driver was developed for use with the RIPE NCC TTM project. * * * The initial driver was developed by Daniel Karrenberg * using the code made available by Trimble. This was for xntpd-3.x.x * * Rewrite of the driver for ntpd-4.x.x by Mark Santcroos * */ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #if defined(REFCLOCK) && defined(CLOCK_RIPENCC) #include "ntp_stdlib.h" #include "ntpd.h" #include "ntp_refclock.h" #include "ntp_unixtime.h" #include "ntp_io.h" #ifdef HAVE_PPSAPI # include "ppsapi_timepps.h" #endif /* * Definitions */ /* we are on little endian */ #define BYTESWAP /* * DEBUG statements: uncomment if necessary */ /* #define DEBUG_NCC */ /* general debug statements */ /* #define DEBUG_PPS */ /* debug pps */ /* #define DEBUG_RAW */ /* print raw packets */ #define TRIMBLE_OUTPUT_FUNC #define TSIP_VERNUM "7.12a" #ifndef FALSE #define FALSE (0) #define TRUE (!FALSE) #endif /* FALSE */ #define GPS_PI (3.1415926535898) #define GPS_C (299792458.) #define D2R (GPS_PI/180.0) #define R2D (180.0/GPS_PI) #define WEEK (604800.) #define MAXCHAN (8) /* control characters for TSIP packets */ #define DLE (0x10) #define ETX (0x03) #define MAX_RPTBUF (256) /* values of TSIPPKT.status */ #define TSIP_PARSED_EMPTY 0 #define TSIP_PARSED_FULL 1 #define TSIP_PARSED_DLE_1 2 #define TSIP_PARSED_DATA 3 #define TSIP_PARSED_DLE_2 4 #define UTCF_UTC_AVAIL (unsigned char) (1) /* UTC available */ #define UTCF_LEAP_SCHD (unsigned char) (1<<4) /* Leap scheduled */ #define UTCF_LEAP_PNDG (unsigned char) (1<<5) /* Leap pending, will occur at end of day */ #define DEVICE "/dev/gps%d" /* name of radio device */ #define PRECISION (-9) /* precision assumed (about 2 ms) */ #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ #define REFID "GPS\0" /* reference id */ #define REFID_LEN 4 #define DESCRIPTION "RIPE NCC GPS (Palisade)" /* Description */ #define SPEED232 B9600 /* 9600 baud */ #define NSAMPLES 3 /* stages of median filter */ /* Structures */ /* TSIP packets have the following structure, whether report or command. */ typedef struct { short counter, /* counter */ len; /* size of buf; < MAX_RPTBUF unsigned chars */ unsigned char status, /* TSIP packet format/parse status */ code, /* TSIP code */ buf[MAX_RPTBUF]; /* report or command string */ } TSIPPKT; /* TSIP binary data structures */ typedef struct { unsigned char t_oa_raw, SV_health; float e, t_oa, i_0, OMEGADOT, sqrt_A, OMEGA_0, omega, M_0, a_f0, a_f1, Axis, n, OMEGA_n, ODOT_n, t_zc; short weeknum, wn_oa; } ALM_INFO; typedef struct { /* Almanac health page (25) parameters */ unsigned char WN_a, SV_health[32], t_oa; } ALH_PARMS; typedef struct { /* Universal Coordinated Time (UTC) parms */ double A_0; float A_1; short delta_t_LS; float t_ot; short WN_t, WN_LSF, DN, delta_t_LSF; } UTC_INFO; typedef struct { /* Ionospheric info (float) */ float alpha_0, alpha_1, alpha_2, alpha_3, beta_0, beta_1, beta_2, beta_3; } ION_INFO; typedef struct { /* Subframe 1 info (float) */ short weeknum; unsigned char codeL2, L2Pdata, SVacc_raw, SV_health; short IODC; float T_GD, t_oc, a_f2, a_f1, a_f0, SVacc; } EPHEM_CLOCK; typedef struct { /* Ephemeris info (float) */ unsigned char IODE, fit_interval; float C_rs, delta_n; double M_0; float C_uc; double e; float C_us; double sqrt_A; float t_oe, C_ic; double OMEGA_0; float C_is; double i_0; float C_rc; double omega; float OMEGADOT, IDOT; double Axis, n, r1me2, OMEGA_n, ODOT_n; } EPHEM_ORBIT; typedef struct { /* Navigation data structure */ short sv_number; /* SV number (0 = no entry) */ float t_ephem; /* time of ephemeris collection */ EPHEM_CLOCK ephclk; /* subframe 1 data */ EPHEM_ORBIT ephorb; /* ephemeris data */ } NAV_INFO; typedef struct { unsigned char bSubcode, operating_mode, dgps_mode, dyn_code, trackmode; float elev_mask, cno_mask, dop_mask, dop_switch; unsigned char dgps_age_limit; } TSIP_RCVR_CFG; #ifdef TRIMBLE_OUTPUT_FUNC static char *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, old_baudnum[] = {0, 1, 4, 5, 6, 8, 9, 11, 28, 12}, *st_baud_text_app [] = {"", "", " 300", " 600", " 1200", " 2400", " 4800", " 9600", "19200", "38400"}, *old_parity_text[] = {"EVEN", "ODD", "", "", "NONE"}, *parity_text [] = {"NONE", "ODD", "EVEN"}, *old_input_ch[] = { "TSIP", "RTCM (6 of 8 bits)"}, *old_output_ch[] = { "TSIP", "No output", "", "", "", "NMEA 0183"}, *protocols_in_text[] = { "", "TSIP", "", ""}, *protocols_out_text[] = { "", "TSIP", "NMEA"}, *rcvr_port_text [] = { "Port A ", "Port B ", "Current Port"}, *dyn_text [] = {"Unchanged", "Land", "Sea", "Air", "Static"}, *NavModeText0xBB[] = {"automatic", "time only (0-D)", "", "2-D", "3-D", "", "", "OverDetermined Time"}, *PPSTimeBaseText[] = {"GPS", "UTC", "USER"}, *PPSPolarityText[] = {"Positive", "Negative"}, *MaskText[] = { "Almanac ", "Ephemeris", "UTC ", "Iono ", "GPS Msg ", "Alm Hlth ", "Time Fix ", "SV Select", "Ext Event", "Pos Fix ", "Raw Meas "}; #endif /* TRIMBLE_OUTPUT_FUNC */ /* * Unit control structure */ struct ripencc_unit { int unit; /* unit number */ int pollcnt; /* poll message counter */ int polled; /* Hand in a sample? */ char leapdelta; /* delta of next leap event */ unsigned char utcflags; /* delta of next leap event */ l_fp tstamp; /* timestamp of last poll */ struct timespec ts; /* last timestamp */ pps_params_t pps_params; /* pps parameters */ pps_info_t pps_info; /* last pps data */ pps_handle_t handle; /* pps handlebars */ }; /******************* PROTOYPES *****************/ /* prototypes for report parsing primitives */ short rpt_0x3D (TSIPPKT *rpt, unsigned char *tx_baud_index, unsigned char *rx_baud_index, unsigned char *char_format_index, unsigned char *stop_bits, unsigned char *tx_mode_index, unsigned char *rx_mode_index); short rpt_0x40 (TSIPPKT *rpt, unsigned char *sv_prn, short *week_num, float *t_zc, float *eccentricity, float *t_oa, float *i_0, float *OMEGA_dot, float *sqrt_A, float *OMEGA_0, float *omega, float *M_0); short rpt_0x41 (TSIPPKT *rpt, float *time_of_week, float *UTC_offset, short *week_num); short rpt_0x42 (TSIPPKT *rpt, float ECEF_pos[3], float *time_of_fix); short rpt_0x43 (TSIPPKT *rpt, float ECEF_vel[3], float *freq_offset, float *time_of_fix); short rpt_0x45 (TSIPPKT *rpt, unsigned char *major_nav_version, unsigned char *minor_nav_version, unsigned char *nav_day, unsigned char *nav_month, unsigned char *nav_year, unsigned char *major_dsp_version, unsigned char *minor_dsp_version, unsigned char *dsp_day, unsigned char *dsp_month, unsigned char *dsp_year); short rpt_0x46 (TSIPPKT *rpt, unsigned char *status1, unsigned char *status2); short rpt_0x47 (TSIPPKT *rpt, unsigned char *nsvs, unsigned char *sv_prn, float *snr); short rpt_0x48 (TSIPPKT *rpt, unsigned char *message); short rpt_0x49 (TSIPPKT *rpt, unsigned char *sv_health); short rpt_0x4A (TSIPPKT *rpt, float *lat, float *lon, float *alt, float *clock_bias, float *time_of_fix); short rpt_0x4A_2 (TSIPPKT *rpt, float *alt, float *dummy, unsigned char *alt_flag); short rpt_0x4B (TSIPPKT *rpt, unsigned char *machine_id, unsigned char *status3, unsigned char *status4); short rpt_0x4C (TSIPPKT *rpt, unsigned char *dyn_code, float *el_mask, float *snr_mask, float *dop_mask, float *dop_switch); short rpt_0x4D (TSIPPKT *rpt, float *osc_offset); short rpt_0x4E (TSIPPKT *rpt, unsigned char *response); short rpt_0x4F (TSIPPKT *rpt, double *a0, float *a1, float *time_of_data, short *dt_ls, short *wn_t, short *wn_lsf, short *dn, short *dt_lsf); short rpt_0x54 (TSIPPKT *rpt, float *clock_bias, float *freq_offset, float *time_of_fix); short rpt_0x55 (TSIPPKT *rpt, unsigned char *pos_code, unsigned char *vel_code, unsigned char *time_code, unsigned char *aux_code); short rpt_0x56 (TSIPPKT *rpt, float vel_ENU[3], float *freq_offset, float *time_of_fix); short rpt_0x57 (TSIPPKT *rpt, unsigned char *source_code, unsigned char *diag_code, short *week_num, float *time_of_fix); short rpt_0x58 (TSIPPKT *rpt, unsigned char *op_code, unsigned char *data_type, unsigned char *sv_prn, unsigned char *data_length, unsigned char *data_packet); short rpt_0x59 (TSIPPKT *rpt, unsigned char *code_type, unsigned char status_code[32]); short rpt_0x5A (TSIPPKT *rpt, unsigned char *sv_prn, float *sample_length, float *signal_level, float *code_phase, float *Doppler, double *time_of_fix); short rpt_0x5B (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *sv_health, unsigned char *sv_iode, unsigned char *fit_interval_flag, float *time_of_collection, float *time_of_eph, float *sv_accy); short rpt_0x5C (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *slot, unsigned char *chan, unsigned char *acq_flag, unsigned char *eph_flag, float *signal_level, float *time_of_last_msmt, float *elev, float *azim, unsigned char *old_msmt_flag, unsigned char *integer_msec_flag, unsigned char *bad_data_flag, unsigned char *data_collect_flag); short rpt_0x6D (TSIPPKT *rpt, unsigned char *manual_mode, unsigned char *nsvs, unsigned char *ndim, unsigned char sv_prn[], float *pdop, float *hdop, float *vdop, float *tdop); short rpt_0x82 (TSIPPKT *rpt, unsigned char *diff_mode); short rpt_0x83 (TSIPPKT *rpt, double ECEF_pos[3], double *clock_bias, float *time_of_fix); short rpt_0x84 (TSIPPKT *rpt, double *lat, double *lon, double *alt, double *clock_bias, float *time_of_fix); short rpt_Paly0xBB(TSIPPKT *rpt, TSIP_RCVR_CFG *TsipxBB); short rpt_0xBC (TSIPPKT *rpt, unsigned char *port_num, unsigned char *in_baud, unsigned char *out_baud, unsigned char *data_bits, unsigned char *parity, unsigned char *stop_bits, unsigned char *flow_control, unsigned char *protocols_in, unsigned char *protocols_out, unsigned char *reserved); /* prototypes for superpacket parsers */ short rpt_0x8F0B (TSIPPKT *rpt, unsigned short *event, double *tow, unsigned char *date, unsigned char *month, short *year, unsigned char *dim_mode, short *utc_offset, double *bias, double *drift, float *bias_unc, float *dr_unc, double *lat, double *lon, double *alt, char sv_id[8]); short rpt_0x8F14 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]); short rpt_0x8F15 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]); short rpt_0x8F20 (TSIPPKT *rpt, unsigned char *info, double *lat, double *lon, double *alt, double vel_enu[], double *time_of_fix, short *week_num, unsigned char *nsvs, unsigned char sv_prn[], short sv_IODC[], short *datum_index); short rpt_0x8F41 (TSIPPKT *rpt, unsigned char *bSearchRange, unsigned char *bBoardOptions, unsigned long *iiSerialNumber, unsigned char *bBuildYear, unsigned char *bBuildMonth, unsigned char *bBuildDay, unsigned char *bBuildHour, float *fOscOffset, unsigned short *iTestCodeId); short rpt_0x8F42 (TSIPPKT *rpt, unsigned char *bProdOptionsPre, unsigned char *bProdNumberExt, unsigned short *iCaseSerialNumberPre, unsigned long *iiCaseSerialNumber, unsigned long *iiProdNumber, unsigned short *iPremiumOptions, unsigned short *iMachineID, unsigned short *iKey); short rpt_0x8F45 (TSIPPKT *rpt, unsigned char *bSegMask); short rpt_0x8F4A_16 (TSIPPKT *rpt, unsigned char *pps_enabled, unsigned char *pps_timebase, unsigned char *pos_polarity, double *pps_offset, float *bias_unc_threshold); short rpt_0x8F4B (TSIPPKT *rpt, unsigned long *decorr_max); short rpt_0x8F4D (TSIPPKT *rpt, unsigned long *event_mask); short rpt_0x8FA5 (TSIPPKT *rpt, unsigned char *spktmask); short rpt_0x8FAD (TSIPPKT *rpt, unsigned short *COUNT, double *FracSec, unsigned char *Hour, unsigned char *Minute, unsigned char *Second, unsigned char *Day, unsigned char *Month, unsigned short *Year, unsigned char *Status, unsigned char *Flags); /**/ /* prototypes for command-encode primitives with suffix convention: */ /* c = clear, s = set, q = query, e = enable, d = disable */ void cmd_0x1F (TSIPPKT *cmd); void cmd_0x26 (TSIPPKT *cmd); void cmd_0x2F (TSIPPKT *cmd); void cmd_0x35s (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code, unsigned char time_code, unsigned char opts_code); void cmd_0x3C (TSIPPKT *cmd, unsigned char sv_prn); void cmd_0x3Ds (TSIPPKT *cmd, unsigned char baud_out, unsigned char baud_inp, unsigned char char_code, unsigned char stopbitcode, unsigned char output_mode, unsigned char input_mode); void cmd_0xBBq (TSIPPKT *cmd, unsigned char subcode) ; /* prototypes 8E commands */ void cmd_0x8E0Bq (TSIPPKT *cmd); void cmd_0x8E41q (TSIPPKT *cmd); void cmd_0x8E42q (TSIPPKT *cmd); void cmd_0x8E4Aq (TSIPPKT *cmd); void cmd_0x8E4As (TSIPPKT *cmd, unsigned char PPSOnOff, unsigned char TimeBase, unsigned char Polarity, double PPSOffset, float Uncertainty); void cmd_0x8E4Bq (TSIPPKT *cmd); void cmd_0x8E4Ds (TSIPPKT *cmd, unsigned long AutoOutputMask); void cmd_0x8EADq (TSIPPKT *cmd); /* header/source border XXXXXXXXXXXXXXXXXXXXXXXXXX */ /* Trimble parse functions */ static int parse0x8FAD (TSIPPKT *, struct peer *); static int parse0x8F0B (TSIPPKT *, struct peer *); #ifdef TRIMBLE_OUTPUT_FUNC static int parseany (TSIPPKT *, struct peer *); static void TranslateTSIPReportToText (TSIPPKT *, char *); #endif /* TRIMBLE_OUTPUT_FUNC */ static int parse0x5C (TSIPPKT *, struct peer *); static int parse0x4F (TSIPPKT *, struct peer *); static void tsip_input_proc (TSIPPKT *, int); /* Trimble helper functions */ static void bPutFloat (float *, unsigned char *); static void bPutDouble (double *, unsigned char *); static void bPutULong (unsigned long *, unsigned char *); static int print_msg_table_header (int rptcode, char *HdrStr, int force); static char * show_time (float time_of_week); /* RIPE NCC functions */ static void ripencc_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); static int ripencc_ppsapi (struct peer *, int, int); static int ripencc_get_pps_ts (struct ripencc_unit *, l_fp *); static int ripencc_start (int, struct peer *); static void ripencc_shutdown (int, struct peer *); static void ripencc_poll (int, struct peer *); static void ripencc_send (struct peer *, TSIPPKT spt); static void ripencc_receive (struct recvbuf *); /* fill in reflock structure for our clock */ struct refclock refclock_ripencc = { ripencc_start, /* start up driver */ ripencc_shutdown, /* shut down driver */ ripencc_poll, /* transmit poll message */ ripencc_control, /* control function */ noentry, /* initialize driver */ noentry, /* debug info */ NOFLAGS /* clock flags */ }; /* * Tables to compute the ddd of year form icky dd/mm timecode. Viva la * leap. */ static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* * ripencc_start - open the GPS devices and initialize data for processing */ static int ripencc_start(int unit, struct peer *peer) { register struct ripencc_unit *up; struct refclockproc *pp; char device[40]; int fd; struct termios tio; TSIPPKT spt; pp = peer->procptr; /* * Open serial port */ (void)snprintf(device, sizeof(device), DEVICE, unit); fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_RAW); if (fd <= 0) { pp->io.fd = -1; return (0); } pp->io.fd = fd; /* from refclock_palisade.c */ if (tcgetattr(fd, &tio) < 0) { msyslog(LOG_ERR, "Palisade(%d) tcgetattr(fd, &tio): %m",unit); return (0); } /* * set flags */ tio.c_cflag |= (PARENB|PARODD); tio.c_iflag &= ~ICRNL; if (tcsetattr(fd, TCSANOW, &tio) == -1) { msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit); return (0); } /* * Allocate and initialize unit structure */ up = emalloc_zero(sizeof(*up)); pp->io.clock_recv = ripencc_receive; pp->io.srcclock = peer; pp->io.datalen = 0; if (!io_addclock(&pp->io)) { pp->io.fd = -1; close(fd); free(up); return (0); } pp->unitptr = up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, REFID_LEN); up->pollcnt = 2; up->unit = unit; up->leapdelta = 0; up->utcflags = 0; /* * Initialize the Clock */ /* query software versions */ cmd_0x1F(&spt); ripencc_send(peer, spt); /* query receiver health */ cmd_0x26(&spt); ripencc_send(peer, spt); /* query serial numbers */ cmd_0x8E42q(&spt); ripencc_send(peer, spt); /* query manuf params */ cmd_0x8E41q(&spt); ripencc_send(peer, spt); /* i/o opts */ /* trimble manual page A30 */ cmd_0x35s(&spt, 0x1C, /* position */ 0x00, /* velocity */ 0x05, /* timing */ 0x0a); /* auxilary */ ripencc_send(peer, spt); /* turn off port A */ cmd_0x3Ds (&spt, 0x0B, /* baud_out */ 0x0B, /* baud_inp */ 0x07, /* char_code */ 0x07, /* stopbitcode */ 0x01, /* output_mode */ 0x00); /* input_mode */ ripencc_send(peer, spt); /* set i/o options */ cmd_0x8E4As (&spt, 0x01, /* PPS on */ 0x01, /* Timebase UTC */ 0x00, /* polarity positive */ 0., /* 100 ft. cable XXX make flag */ 1e-6 * GPS_C); /* turn of biasuncert. > (1us) */ ripencc_send(peer,spt); /* all outomatic packet output off */ cmd_0x8E4Ds(&spt, 0x00000000); /* AutoOutputMask */ ripencc_send(peer, spt); cmd_0xBBq (&spt, 0x00); /* query primary configuration */ ripencc_send(peer,spt); /* query PPS parameters */ cmd_0x8E4Aq (&spt); /* query PPS params */ ripencc_send(peer,spt); /* query survey limit */ cmd_0x8E4Bq (&spt); /* query survey limit */ ripencc_send(peer,spt); #ifdef DEBUG_NCC if (debug) printf("ripencc_start: success\n"); #endif /* DEBUG_NCC */ /* * Start the PPSAPI interface if it is there. Default to use * the assert edge and do not enable the kernel hardpps. */ if (time_pps_create(fd, &up->handle) < 0) { up->handle = 0; msyslog(LOG_ERR, "refclock_ripencc: time_pps_create failed: %m"); return (1); } return(ripencc_ppsapi(peer, 0, 0)); } /* * ripencc_control - fudge control */ static void ripencc_control( int unit, /* unit (not used) */ const struct refclockstat *in, /* input parameters (not used) */ struct refclockstat *out, /* output parameters (not used) */ struct peer *peer /* peer structure pointer */ ) { struct refclockproc *pp; #ifdef DEBUG_NCC msyslog(LOG_INFO,"%s()",__FUNCTION__); #endif /* DEBUG_NCC */ pp = peer->procptr; ripencc_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2, pp->sloppyclockflag & CLK_FLAG3); } /* * Initialize PPSAPI */ int ripencc_ppsapi( struct peer *peer, /* peer structure pointer */ int enb_clear, /* clear enable */ int enb_hardpps /* hardpps enable */ ) { struct refclockproc *pp; struct ripencc_unit *up; int capability; pp = peer->procptr; up = pp->unitptr; if (time_pps_getcap(up->handle, &capability) < 0) { msyslog(LOG_ERR, "refclock_ripencc: time_pps_getcap failed: %m"); return (0); } memset(&up->pps_params, 0, sizeof(pps_params_t)); if (enb_clear) up->pps_params.mode = capability & PPS_CAPTURECLEAR; else up->pps_params.mode = capability & PPS_CAPTUREASSERT; if (!up->pps_params.mode) { msyslog(LOG_ERR, "refclock_ripencc: invalid capture edge %d", !enb_clear); return (0); } up->pps_params.mode |= PPS_TSFMT_TSPEC; if (time_pps_setparams(up->handle, &up->pps_params) < 0) { msyslog(LOG_ERR, "refclock_ripencc: time_pps_setparams failed: %m"); return (0); } if (enb_hardpps) { if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS, up->pps_params.mode & ~PPS_TSFMT_TSPEC, PPS_TSFMT_TSPEC) < 0) { msyslog(LOG_ERR, "refclock_ripencc: time_pps_kcbind failed: %m"); return (0); } hardpps_enable = 1; } peer->precision = PPS_PRECISION; #if DEBUG_NCC if (debug) { time_pps_getparams(up->handle, &up->pps_params); printf( "refclock_ripencc: capability 0x%x version %d mode 0x%x kern %d\n", capability, up->pps_params.api_version, up->pps_params.mode, enb_hardpps); } #endif /* DEBUG_NCC */ return (1); } /* * This function is called every 64 seconds from ripencc_receive * It will fetch the pps time * * Return 0 on failure and 1 on success. */ static int ripencc_get_pps_ts( struct ripencc_unit *up, l_fp *tsptr ) { pps_info_t pps_info; struct timespec timeout, ts; double dtemp; l_fp tstmp; #ifdef DEBUG_PPS msyslog(LOG_INFO,"ripencc_get_pps_ts"); #endif /* DEBUG_PPS */ /* * Convert the timespec nanoseconds field to ntp l_fp units. */ if (up->handle == 0) return (0); timeout.tv_sec = 0; timeout.tv_nsec = 0; memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t)); if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info, &timeout) < 0) return (0); if (up->pps_params.mode & PPS_CAPTUREASSERT) { if (pps_info.assert_sequence == up->pps_info.assert_sequence) return (0); ts = up->pps_info.assert_timestamp; } else if (up->pps_params.mode & PPS_CAPTURECLEAR) { if (pps_info.clear_sequence == up->pps_info.clear_sequence) return (0); ts = up->pps_info.clear_timestamp; } else { return (0); } if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec)) return (0); up->ts = ts; tstmp.l_ui = ts.tv_sec + JAN_1970; dtemp = ts.tv_nsec * FRAC / 1e9; tstmp.l_uf = (u_int32)dtemp; #ifdef DEBUG_PPS msyslog(LOG_INFO,"ts.tv_sec: %d",(int)ts.tv_sec); msyslog(LOG_INFO,"ts.tv_nsec: %ld",ts.tv_nsec); #endif /* DEBUG_PPS */ *tsptr = tstmp; return (1); } /* * ripencc_shutdown - shut down a GPS clock */ static void ripencc_shutdown(int unit, struct peer *peer) { register struct ripencc_unit *up; struct refclockproc *pp; pp = peer->procptr; up = pp->unitptr; if (up != NULL) { if (up->handle != 0) time_pps_destroy(up->handle); free(up); } if (-1 != pp->io.fd) io_closeclock(&pp->io); return; } /* * ripencc_poll - called by the transmit procedure */ static void ripencc_poll(int unit, struct peer *peer) { register struct ripencc_unit *up; struct refclockproc *pp; TSIPPKT spt; #ifdef DEBUG_NCC if (debug) fprintf(stderr, "ripencc_poll(%d)\n", unit); #endif /* DEBUG_NCC */ pp = peer->procptr; up = pp->unitptr; if (up->pollcnt == 0) refclock_report(peer, CEVNT_TIMEOUT); else up->pollcnt--; pp->polls++; up->polled = 1; /* poll for UTC superpacket */ cmd_0x8EADq (&spt); ripencc_send(peer,spt); } /* * ripencc_send - send message to clock * use the structures being created by the trimble functions! * makes the code more readable/clean */ static void ripencc_send(struct peer *peer, TSIPPKT spt) { unsigned char *ip, *op; unsigned char obuf[512]; #ifdef DEBUG_RAW { register struct ripencc_unit *up; register struct refclockproc *pp; pp = peer->procptr; up = pp->unitptr; if (debug) printf("ripencc_send(%d, %02X)\n", up->unit, cmd); } #endif /* DEBUG_RAW */ ip = spt.buf; op = obuf; *op++ = 0x10; *op++ = spt.code; while (spt.len--) { if (op-obuf > sizeof(obuf)-5) { msyslog(LOG_ERR, "ripencc_send obuf overflow!"); refclock_report(peer, CEVNT_FAULT); return; } if (*ip == 0x10) /* byte stuffing */ *op++ = 0x10; *op++ = *ip++; } *op++ = 0x10; *op++ = 0x03; #ifdef DEBUG_RAW if (debug) { /* print raw packet */ unsigned char *cp; int i; printf("ripencc_send: len %d\n", op-obuf); for (i=1, cp=obuf; cpprocptr->io.fd, obuf, op-obuf) == -1) { refclock_report(peer, CEVNT_FAULT); } } /* * ripencc_receive() * * called when a packet is received on the serial port * takes care of further processing * */ static void ripencc_receive(struct recvbuf *rbufp) { register struct ripencc_unit *up; register struct refclockproc *pp; struct peer *peer; static TSIPPKT rpt; /* for current incoming TSIP report */ TSIPPKT spt; /* send packet */ int ns_since_pps; int i; char *cp; /* these variables hold data until we decide it's worth keeping */ char rd_lastcode[BMAX]; l_fp rd_tmp; u_short rd_lencode; /* msyslog(LOG_INFO, "%s",__FUNCTION__); */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); #ifdef DEBUG_RAW if (debug) fprintf(stderr, "ripencc_receive(%d)\n", up->unit); #endif /* DEBUG_RAW */ #ifdef DEBUG_RAW if (debug) { /* print raw packet */ int i; unsigned char *cp; printf("ripencc_receive: len %d\n", rbufp->recv_length); for (i=1, cp=(char*)&rbufp->recv_space; i <= rbufp->recv_length; i++, cp++) { printf(" %02X", *cp); if (i%10 == 0) printf("\n"); } printf("\n"); } #endif /* DEBUG_RAW */ cp = (char*) &rbufp->recv_space; i=rbufp->recv_length; while (i--) { /* loop over received chars */ tsip_input_proc(&rpt, (unsigned char) *cp++); if (rpt.status != TSIP_PARSED_FULL) continue; switch (rpt.code) { case 0x8F: /* superpacket */ switch (rpt.buf[0]) { case 0xAD: /* UTC Time */ /* ** When polling on port B the timecode is ** the time of the previous PPS. If we ** completed receiving the packet less than ** 150ms after the turn of the second, it ** may have the code of the previous second. ** We do not trust that and simply poll ** again without even parsing it. ** ** More elegant would be to re-schedule the ** poll, but I do not know (yet) how to do ** that cleanly. ** */ /* BLA ns_since_pps = ncc_tstmp(rbufp, &trtmp); */ /* if (up->polled && ns_since_pps > -1 && ns_since_pps < 150) { */ ns_since_pps = 200; if (up->polled && ns_since_pps < 150) { msyslog(LOG_INFO, "%s(): up->polled", __FUNCTION__); ripencc_poll(up->unit, peer); break; } /* * Parse primary utc time packet * and fill refclock structure * from results. */ if (parse0x8FAD(&rpt, peer) < 0) { msyslog(LOG_INFO, "%s(): parse0x8FAD < 0",__FUNCTION__); refclock_report(peer, CEVNT_BADREPLY); break; } /* * If the PPSAPI is working, rather use its * timestamps. * assume that the PPS occurs on the second * so blow any msec */ if (ripencc_get_pps_ts(up, &rd_tmp) == 1) { pp->lastrec = up->tstamp = rd_tmp; pp->nsec = 0; } else msyslog(LOG_INFO, "%s(): ripencc_get_pps_ts returns failure",__FUNCTION__); if (!up->polled) { msyslog(LOG_INFO, "%s(): unrequested packet",__FUNCTION__); /* unrequested packet */ break; } /* we have been polled ! */ up->polled = 0; up->pollcnt = 2; /* poll for next packet */ cmd_0x8E0Bq(&spt); ripencc_send(peer,spt); if (ns_since_pps < 0) { /* no PPS */ msyslog(LOG_INFO, "%s(): ns_since_pps < 0",__FUNCTION__); refclock_report(peer, CEVNT_BADTIME); break; } /* ** Process the new sample in the median ** filter and determine the reference clock ** offset and dispersion. */ if (!refclock_process(pp)) { msyslog(LOG_INFO, "%s(): !refclock_process",__FUNCTION__); refclock_report(peer, CEVNT_BADTIME); break; } refclock_receive(peer); break; case 0x0B: /* comprehensive time packet */ parse0x8F0B(&rpt, peer); break; default: /* other superpackets */ #ifdef DEBUG_NCC msyslog(LOG_INFO, "%s(): calling parseany", __FUNCTION__); #endif /* DEBUG_NCC */ #ifdef TRIMBLE_OUTPUT_FUNC parseany(&rpt, peer); #endif /* TRIMBLE_OUTPUT_FUNC */ break; } break; case 0x4F: /* UTC parameters, for leap info */ parse0x4F(&rpt, peer); break; case 0x5C: /* sat tracking data */ parse0x5C(&rpt, peer); break; default: /* other packets */ #ifdef TRIMBLE_OUTPUT_FUNC parseany(&rpt, peer); #endif /* TRIMBLE_OUTPUT_FUNC */ break; } rpt.status = TSIP_PARSED_EMPTY; } } /* * All trimble functions that are directly referenced from driver code * (so not from parseany) */ /* request software versions */ void cmd_0x1F( TSIPPKT *cmd ) { cmd->len = 0; cmd->code = 0x1F; } /* request receiver health */ void cmd_0x26( TSIPPKT *cmd ) { cmd->len = 0; cmd->code = 0x26; } /* request UTC params */ void cmd_0x2F( TSIPPKT *cmd ) { cmd->len = 0; cmd->code = 0x2F; } /* set serial I/O options */ void cmd_0x35s( TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code, unsigned char time_code, unsigned char opts_code ) { cmd->buf[0] = pos_code; cmd->buf[1] = vel_code; cmd->buf[2] = time_code; cmd->buf[3] = opts_code; cmd->len = 4; cmd->code = 0x35; } /* request tracking status */ void cmd_0x3C( TSIPPKT *cmd, unsigned char sv_prn ) { cmd->buf[0] = sv_prn; cmd->len = 1; cmd->code = 0x3C; } /* set Channel A configuration for dual-port operation */ void cmd_0x3Ds( TSIPPKT *cmd, unsigned char baud_out, unsigned char baud_inp, unsigned char char_code, unsigned char stopbitcode, unsigned char output_mode, unsigned char input_mode ) { cmd->buf[0] = baud_out; /* XMT baud rate */ cmd->buf[1] = baud_inp; /* RCV baud rate */ cmd->buf[2] = char_code; /* parity and #bits per byte */ cmd->buf[3] = stopbitcode; /* number of stop bits code */ cmd->buf[4] = output_mode; /* Ch. A transmission mode */ cmd->buf[5] = input_mode; /* Ch. A reception mode */ cmd->len = 6; cmd->code = 0x3D; } /* query primary configuration */ void cmd_0xBBq( TSIPPKT *cmd, unsigned char subcode ) { cmd->len = 1; cmd->code = 0xBB; cmd->buf[0] = subcode; } /**** Superpackets ****/ /* 8E-0B to query 8F-0B controls */ void cmd_0x8E0Bq( TSIPPKT *cmd ) { cmd->len = 1; cmd->code = 0x8E; cmd->buf[0] = 0x0B; } /* 8F-41 to query board serial number */ void cmd_0x8E41q( TSIPPKT *cmd ) { cmd->len = 1; cmd->code = 0x8E; cmd->buf[0] = 0x41; } /* 8F-42 to query product serial number */ void cmd_0x8E42q( TSIPPKT *cmd ) { cmd->len = 1; cmd->code = 0x8E; cmd->buf[0] = 0x42; } /* 8F-4A to query PPS parameters */ void cmd_0x8E4Aq( TSIPPKT *cmd ) { cmd->len = 1; cmd->code = 0x8E; cmd->buf[0] = 0x4A; } /* set i/o options */ void cmd_0x8E4As( TSIPPKT *cmd, unsigned char PPSOnOff, unsigned char TimeBase, unsigned char Polarity, double PPSOffset, float Uncertainty ) { cmd->len = 16; cmd->code = 0x8E; cmd->buf[0] = 0x4A; cmd->buf[1] = PPSOnOff; cmd->buf[2] = TimeBase; cmd->buf[3] = Polarity; bPutDouble (&PPSOffset, &cmd->buf[4]); bPutFloat (&Uncertainty, &cmd->buf[12]); } /* 8F-4B query survey limit */ void cmd_0x8E4Bq( TSIPPKT *cmd ) { cmd->len = 1; cmd->code = 0x8E; cmd->buf[0] = 0x4B; } /* poll for UTC superpacket */ /* 8E-AD to query 8F-AD controls */ void cmd_0x8EADq( TSIPPKT *cmd ) { cmd->len = 1; cmd->code = 0x8E; cmd->buf[0] = 0xAD; } /* all outomatic packet output off */ void cmd_0x8E4Ds( TSIPPKT *cmd, unsigned long AutoOutputMask ) { cmd->len = 5; cmd->code = 0x8E; cmd->buf[0] = 0x4D; bPutULong (&AutoOutputMask, &cmd->buf[1]); } /* * for DOS machines, reverse order of bytes as they come through the * serial port. */ #ifdef BYTESWAP static short bGetShort( unsigned char *bp ) { short outval; unsigned char *optr; optr = (unsigned char*)&outval + 1; *optr-- = *bp++; *optr = *bp; return outval; } #ifdef TRIMBLE_OUTPUT_FUNC static unsigned short bGetUShort( unsigned char *bp ) { unsigned short outval; unsigned char *optr; optr = (unsigned char*)&outval + 1; *optr-- = *bp++; *optr = *bp; return outval; } static long bGetLong( unsigned char *bp ) { long outval; unsigned char *optr; optr = (unsigned char*)&outval + 3; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr = *bp; return outval; } static unsigned long bGetULong( unsigned char *bp ) { unsigned long outval; unsigned char *optr; optr = (unsigned char*)&outval + 3; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr = *bp; return outval; } #endif /* TRIMBLE_OUTPUT_FUNC */ static float bGetSingle( unsigned char *bp ) { float outval; unsigned char *optr; optr = (unsigned char*)&outval + 3; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr = *bp; return outval; } static double bGetDouble( unsigned char *bp ) { double outval; unsigned char *optr; optr = (unsigned char*)&outval + 7; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr-- = *bp++; *optr = *bp; return outval; } #else /* not BYTESWAP */ #define bGetShort(bp) (*(short*)(bp)) #define bGetLong(bp) (*(long*)(bp)) #define bGetULong(bp) (*(unsigned long*)(bp)) #define bGetSingle(bp) (*(float*)(bp)) #define bGetDouble(bp) (*(double*)(bp)) #endif /* BYTESWAP */ /* * Byte-reversal is necessary for little-endian (Intel-based) machines. * TSIP streams are Big-endian (Motorola-based). */ #ifdef BYTESWAP void bPutFloat( float *in, unsigned char *out ) { unsigned char *inptr; inptr = (unsigned char*)in + 3; *out++ = *inptr--; *out++ = *inptr--; *out++ = *inptr--; *out = *inptr; } static void bPutULong( unsigned long *in, unsigned char *out ) { unsigned char *inptr; inptr = (unsigned char*)in + 3; *out++ = *inptr--; *out++ = *inptr--; *out++ = *inptr--; *out = *inptr; } static void bPutDouble( double *in, unsigned char *out ) { unsigned char *inptr; inptr = (unsigned char*)in + 7; *out++ = *inptr--; *out++ = *inptr--; *out++ = *inptr--; *out++ = *inptr--; *out++ = *inptr--; *out++ = *inptr--; *out++ = *inptr--; *out = *inptr; } #else /* not BYTESWAP */ void bPutShort (short a, unsigned char *cmdbuf) {*(short*) cmdbuf = a;} void bPutULong (long a, unsigned char *cmdbuf) {*(long*) cmdbuf = a;} void bPutFloat (float a, unsigned char *cmdbuf) {*(float*) cmdbuf = a;} void bPutDouble (double a, unsigned char *cmdbuf){*(double*) cmdbuf = a;} #endif /* BYTESWAP */ /* * Parse primary utc time packet * and fill refclock structure * from results. * * 0 = success * -1 = errors */ static int parse0x8FAD( TSIPPKT *rpt, struct peer *peer ) { register struct refclockproc *pp; register struct ripencc_unit *up; unsigned day, month, year; /* data derived from received timecode */ unsigned hour, minute, second; unsigned char trackstat, utcflags; static char logbuf[1024]; /* logging string buffer */ int i; unsigned char *buf; buf = rpt->buf; pp = peer->procptr; if (rpt->len != 22) return (-1); if (bGetShort(&buf[1]) != 0) { #ifdef DEBUG_NCC if (debug) printf("parse0x8FAD: event count != 0\n"); #endif /* DEBUG_NCC */ return(-1); } if (bGetDouble(&buf[3]) != 0.0) { #ifdef DEBUG_NCC if (debug) printf("parse0x8FAD: fracsecs != 0\n"); #endif /* DEBUG_NCC */ return(-1); } hour = (unsigned int) buf[11]; minute = (unsigned int) buf[12]; second = (unsigned int) buf[13]; day = (unsigned int) buf[14]; month = (unsigned int) buf[15]; year = bGetShort(&buf[16]); trackstat = buf[18]; utcflags = buf[19]; sprintf(logbuf, "U1 %d.%d.%d %02d:%02d:%02d %d %02x", day, month, year, hour, minute, second, trackstat, utcflags); #ifdef DEBUG_NCC if (debug) puts(logbuf); #endif /* DEBUG_NCC */ record_clock_stats(&peer->srcadr, logbuf); if (!utcflags & UTCF_UTC_AVAIL) return(-1); /* poll for UTC parameters once and then if UTC flag changed */ up = (struct ripencc_unit *) pp->unitptr; if (utcflags != up->utcflags) { TSIPPKT spt; /* local structure for send packet */ cmd_0x2F (&spt); /* request UTC params */ ripencc_send(peer,spt); up->utcflags = utcflags; } /* * If we hit the leap second, we choose to skip this sample * rather than rely on other code to be perfectly correct. * No offense, just defense ;-). */ if (second == 60) return(-1); /* now check and convert the time we received */ pp->year = year; if (month < 1 || month > 12 || day < 1 || day > 31) return(-1); if (pp->year % 4) { /* XXX: use is_leapyear() ? */ if (day > day1tab[month - 1]) return(-1); for (i = 0; i < month - 1; i++) day += day1tab[i]; } else { if (day > day2tab[month - 1]) return(-1); for (i = 0; i < month - 1; i++) day += day2tab[i]; } pp->day = day; pp->hour = hour; pp->minute = minute; pp-> second = second; pp->nsec = 0; if ((utcflags&UTCF_LEAP_PNDG) && up->leapdelta != 0) pp-> leap = (up->leapdelta > 0) ? LEAP_ADDSECOND : LEAP_DELSECOND; else pp-> leap = LEAP_NOWARNING; return (0); } /* * Parse comprehensive time packet * * 0 = success * -1 = errors */ int parse0x8F0B( TSIPPKT *rpt, struct peer *peer ) { register struct refclockproc *pp; unsigned day, month, year; /* data derived from received timecode */ unsigned hour, minute, second; unsigned utcoff; unsigned char mode; double bias, rate; float biasunc, rateunc; double lat, lon, alt; short lat_deg, lon_deg; float lat_min, lon_min; unsigned char north_south, east_west; char sv[9]; static char logbuf[1024]; /* logging string buffer */ unsigned char b; int i; unsigned char *buf; double tow; buf = rpt->buf; pp = peer->procptr; if (rpt->len != 74) return (-1); if (bGetShort(&buf[1]) != 0) return(-1);; tow = bGetDouble(&buf[3]); if (tow == -1.0) { return(-1); } else if ((tow >= 604800.0) || (tow < 0.0)) { return(-1); } else { if (tow < 604799.9) tow = tow + .00000001; second = (unsigned int) fmod(tow, 60.); minute = (unsigned int) fmod(tow/60., 60.); hour = (unsigned int )fmod(tow / 3600., 24.); } day = (unsigned int) buf[11]; month = (unsigned int) buf[12]; year = bGetShort(&buf[13]); mode = buf[15]; utcoff = bGetShort(&buf[16]); bias = bGetDouble(&buf[18]) / GPS_C * 1e9; /* ns */ rate = bGetDouble(&buf[26]) / GPS_C * 1e9; /* ppb */ biasunc = bGetSingle(&buf[34]) / GPS_C * 1e9; /* ns */ rateunc = bGetSingle(&buf[38]) / GPS_C * 1e9; /* ppb */ lat = bGetDouble(&buf[42]) * R2D; lon = bGetDouble(&buf[50]) * R2D; alt = bGetDouble(&buf[58]); if (lat < 0.0) { north_south = 'S'; lat = -lat; } else { north_south = 'N'; } lat_deg = (short)lat; lat_min = (lat - lat_deg) * 60.0; if (lon < 0.0) { east_west = 'W'; lon = -lon; } else { east_west = 'E'; } lon_deg = (short)lon; lon_min = (lon - lon_deg) * 60.0; for (i=0; i<8; i++) { sv[i] = buf[i + 66]; if (sv[i]) { TSIPPKT spt; /* local structure for sendpacket */ b = (unsigned char) (sv[i]<0 ? -sv[i] : sv[i]); /* request tracking status */ cmd_0x3C (&spt, b); ripencc_send(peer,spt); } } sprintf(logbuf, "C1 %02d%02d%04d %02d%02d%02d %d %7.0f %.1f %.0f %.1f %d %02d%09.6f %c %02d%09.6f %c %.0f %d %d %d %d %d %d %d %d", day, month, year, hour, minute, second, mode, bias, biasunc, rate, rateunc, utcoff, lat_deg, lat_min, north_south, lon_deg, lon_min, east_west, alt, sv[0], sv[1], sv[2], sv[3], sv[4], sv[5], sv[6], sv[7]); #ifdef DEBUG_NCC if (debug) puts(logbuf); #endif /* DEBUG_NCC */ record_clock_stats(&peer->srcadr, logbuf); return (0); } #ifdef TRIMBLE_OUTPUT_FUNC /* * Parse any packet using Trimble machinery */ int parseany( TSIPPKT *rpt, struct peer *peer ) { static char logbuf[1024]; /* logging string buffer */ TranslateTSIPReportToText (rpt, logbuf); /* anything else */ #ifdef DEBUG_NCC if (debug) puts(&logbuf[1]); #endif /* DEBUG_NCC */ record_clock_stats(&peer->srcadr, &logbuf[1]); return(0); } #endif /* TRIMBLE_OUTPUT_FUNC */ /* * Parse UTC Parameter Packet * * See the IDE for documentation! * * 0 = success * -1 = errors */ int parse0x4F( TSIPPKT *rpt, struct peer *peer ) { register struct ripencc_unit *up; double a0; float a1, tot; int dt_ls, wn_t, wn_lsf, dn, dt_lsf; static char logbuf[1024]; /* logging string buffer */ unsigned char *buf; buf = rpt->buf; if (rpt->len != 26) return (-1); a0 = bGetDouble (buf); a1 = bGetSingle (&buf[8]); dt_ls = bGetShort (&buf[12]); tot = bGetSingle (&buf[14]); wn_t = bGetShort (&buf[18]); wn_lsf = bGetShort (&buf[20]); dn = bGetShort (&buf[22]); dt_lsf = bGetShort (&buf[24]); sprintf(logbuf, "L1 %d %d %d %g %g %g %d %d %d", dt_lsf - dt_ls, dt_ls, dt_lsf, a0, a1, tot, wn_t, wn_lsf, dn); #ifdef DEBUG_NCC if (debug) puts(logbuf); #endif /* DEBUG_NCC */ record_clock_stats(&peer->srcadr, logbuf); up = (struct ripencc_unit *) peer->procptr->unitptr; up->leapdelta = dt_lsf - dt_ls; return (0); } /* * Parse Tracking Status packet * * 0 = success * -1 = errors */ int parse0x5C( TSIPPKT *rpt, struct peer *peer ) { unsigned char prn, channel, aqflag, ephstat; float snr, azinuth, elevation; static char logbuf[1024]; /* logging string buffer */ unsigned char *buf; buf = rpt->buf; if (rpt->len != 24) return(-1); prn = buf[0]; channel = (unsigned char)(buf[1] >> 3); if (channel == 0x10) channel = 2; else channel++; aqflag = buf[2]; ephstat = buf[3]; snr = bGetSingle(&buf[4]); elevation = bGetSingle(&buf[12]) * R2D; azinuth = bGetSingle(&buf[16]) * R2D; sprintf(logbuf, "S1 %02d %d %d %02x %4.1f %5.1f %4.1f", prn, channel, aqflag, ephstat, snr, azinuth, elevation); #ifdef DEBUG_NCC if (debug) puts(logbuf); #endif /* DEBUG_NCC */ record_clock_stats(&peer->srcadr, logbuf); return (0); } /******* Code below is from Trimble Tsipchat *************/ /* * ************************************************************************* * * Trimble Navigation, Ltd. * OEM Products Development Group * P.O. Box 3642 * 645 North Mary Avenue * Sunnyvale, California 94088-3642 * * Corporate Headquarter: * Telephone: (408) 481-8000 * Fax: (408) 481-6005 * * Technical Support Center: * Telephone: (800) 767-4822 (U.S. and Canada) * (408) 481-6940 (outside U.S. and Canada) * Fax: (408) 481-6020 * BBS: (408) 481-7800 * e-mail: trimble_support@trimble.com * ftp://ftp.trimble.com/pub/sct/embedded/bin * * ************************************************************************* * * ------- BYTE-SWAPPING ------- * TSIP is big-endian (Motorola) protocol. To use on little-endian (Intel) * systems, the bytes of all multi-byte types (shorts, floats, doubles, etc.) * must be reversed. This is controlled by the MACRO BYTESWAP; if defined, it * assumes little-endian protocol. * -------------------------------- * * T_PARSER.C and T_PARSER.H contains primitive functions that interpret * reports received from the receiver. A second source file pair, * T_FORMAT.C and T_FORMAT.H, contin the matching TSIP command formatters. * * The module is in very portable, basic C language. It can be used as is, or * with minimal changes if a TSIP communications application is needed separate * from TSIPCHAT. The construction of most argument lists avoid the use of * structures, but the developer is encouraged to reconstruct them using such * definitions to meet project requirements. Declarations of T_PARSER.C * functions are included in T_PARSER.H to provide prototyping definitions. * * There are two types of functions: a serial input processing routine, * tsip_input_proc() * which assembles incoming bytes into a TSIPPKT structure, and the * report parsers, rpt_0x??(). * * 1) The function tsip_input_proc() accumulates bytes from the receiver, * strips control bytes (DLE), and checks if the report end sequence (DLE ETX) * has been received. rpt.status is defined as TSIP_PARSED_FULL (== 1) * if a complete packet is available. * * 2) The functions rpt_0x??() are report string interpreters patterned after * the document called "Trimble Standard Interface Protocol". It should be * noted that if the report buffer is sent into the receiver with the wrong * length (byte count), the rpt_0x??() returns the Boolean equivalence for * TRUE. * * ************************************************************************* * */ /* * reads bytes until serial buffer is empty or a complete report * has been received; end of report is signified by DLE ETX. */ static void tsip_input_proc( TSIPPKT *rpt, int inbyte ) { unsigned char newbyte; if (inbyte < 0 || inbyte > 0xFF) return; newbyte = (unsigned char)(inbyte); switch (rpt->status) { case TSIP_PARSED_DLE_1: switch (newbyte) { case 0: case ETX: /* illegal TSIP IDs */ rpt->len = 0; rpt->status = TSIP_PARSED_EMPTY; break; case DLE: /* try normal message start again */ rpt->len = 0; rpt->status = TSIP_PARSED_DLE_1; break; default: /* legal TSIP ID; start message */ rpt->code = newbyte; rpt->len = 0; rpt->status = TSIP_PARSED_DATA; break; } break; case TSIP_PARSED_DATA: switch (newbyte) { case DLE: /* expect DLE or ETX next */ rpt->status = TSIP_PARSED_DLE_2; break; default: /* normal data byte */ rpt->buf[rpt->len] = newbyte; rpt->len++; /* no change in rpt->status */ break; } break; case TSIP_PARSED_DLE_2: switch (newbyte) { case DLE: /* normal data byte */ rpt->buf[rpt->len] = newbyte; rpt->len++; rpt->status = TSIP_PARSED_DATA; break; case ETX: /* end of message; return TRUE here. */ rpt->status = TSIP_PARSED_FULL; break; default: /* error: treat as TSIP_PARSED_DLE_1; start new report packet */ rpt->code = newbyte; rpt->len = 0; rpt->status = TSIP_PARSED_DATA; } break; case TSIP_PARSED_FULL: case TSIP_PARSED_EMPTY: default: switch (newbyte) { case DLE: /* normal message start */ rpt->len = 0; rpt->status = TSIP_PARSED_DLE_1; break; default: /* error: ignore newbyte */ rpt->len = 0; rpt->status = TSIP_PARSED_EMPTY; } break; } if (rpt->len > MAX_RPTBUF) { /* error: start new report packet */ rpt->status = TSIP_PARSED_EMPTY; rpt->len = 0; } } #ifdef TRIMBLE_OUTPUT_FUNC /**/ /* Channel A configuration for dual port operation */ short rpt_0x3D( TSIPPKT *rpt, unsigned char *tx_baud_index, unsigned char *rx_baud_index, unsigned char *char_format_index, unsigned char *stop_bits, unsigned char *tx_mode_index, unsigned char *rx_mode_index ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 6) return TRUE; *tx_baud_index = buf[0]; *rx_baud_index = buf[1]; *char_format_index = buf[2]; *stop_bits = (unsigned char)((buf[3] == 0x07) ? 1 : 2); *tx_mode_index = buf[4]; *rx_mode_index = buf[5]; return FALSE; } /**/ /* almanac data for specified satellite */ short rpt_0x40( TSIPPKT *rpt, unsigned char *sv_prn, short *week_num, float *t_zc, float *eccentricity, float *t_oa, float *i_0, float *OMEGA_dot, float *sqrt_A, float *OMEGA_0, float *omega, float *M_0 ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 39) return TRUE; *sv_prn = buf[0]; *t_zc = bGetSingle (&buf[1]); *week_num = bGetShort (&buf[5]); *eccentricity = bGetSingle (&buf[7]); *t_oa = bGetSingle (&buf[11]); *i_0 = bGetSingle (&buf[15]); *OMEGA_dot = bGetSingle (&buf[19]); *sqrt_A = bGetSingle (&buf[23]); *OMEGA_0 = bGetSingle (&buf[27]); *omega = bGetSingle (&buf[31]); *M_0 = bGetSingle (&buf[35]); return FALSE; } /* GPS time */ short rpt_0x41( TSIPPKT *rpt, float *time_of_week, float *UTC_offset, short *week_num ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 10) return TRUE; *time_of_week = bGetSingle (buf); *week_num = bGetShort (&buf[4]); *UTC_offset = bGetSingle (&buf[6]); return FALSE; } /* position in ECEF, single precision */ short rpt_0x42( TSIPPKT *rpt, float pos_ECEF[3], float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 16) return TRUE; pos_ECEF[0] = bGetSingle (buf); pos_ECEF[1]= bGetSingle (&buf[4]); pos_ECEF[2]= bGetSingle (&buf[8]); *time_of_fix = bGetSingle (&buf[12]); return FALSE; } /* velocity in ECEF, single precision */ short rpt_0x43( TSIPPKT *rpt, float ECEF_vel[3], float *freq_offset, float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 20) return TRUE; ECEF_vel[0] = bGetSingle (buf); ECEF_vel[1] = bGetSingle (&buf[4]); ECEF_vel[2] = bGetSingle (&buf[8]); *freq_offset = bGetSingle (&buf[12]); *time_of_fix = bGetSingle (&buf[16]); return FALSE; } /* software versions */ short rpt_0x45( TSIPPKT *rpt, unsigned char *major_nav_version, unsigned char *minor_nav_version, unsigned char *nav_day, unsigned char *nav_month, unsigned char *nav_year, unsigned char *major_dsp_version, unsigned char *minor_dsp_version, unsigned char *dsp_day, unsigned char *dsp_month, unsigned char *dsp_year ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 10) return TRUE; *major_nav_version = buf[0]; *minor_nav_version = buf[1]; *nav_day = buf[2]; *nav_month = buf[3]; *nav_year = buf[4]; *major_dsp_version = buf[5]; *minor_dsp_version = buf[6]; *dsp_day = buf[7]; *dsp_month = buf[8]; *dsp_year = buf[9]; return FALSE; } /* receiver health and status */ short rpt_0x46( TSIPPKT *rpt, unsigned char *status1, unsigned char *status2 ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 2) return TRUE; *status1 = buf[0]; *status2 = buf[1]; return FALSE; } /* signal levels for all satellites tracked */ short rpt_0x47( TSIPPKT *rpt, unsigned char *nsvs, unsigned char *sv_prn, float *snr ) { short isv; unsigned char *buf; buf = rpt->buf; if (rpt->len != 1 + 5*buf[0]) return TRUE; *nsvs = buf[0]; for (isv = 0; isv < (*nsvs); isv++) { sv_prn[isv] = buf[5*isv + 1]; snr[isv] = bGetSingle (&buf[5*isv + 2]); } return FALSE; } /* GPS system message */ short rpt_0x48( TSIPPKT *rpt, unsigned char *message ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 22) return TRUE; memcpy (message, buf, 22); message[22] = 0; return FALSE; } /* health for all satellites from almanac health page */ short rpt_0x49( TSIPPKT *rpt, unsigned char *sv_health ) { short i; unsigned char *buf; buf = rpt->buf; if (rpt->len != 32) return TRUE; for (i = 0; i < 32; i++) sv_health [i]= buf[i]; return FALSE; } /* position in lat-lon-alt, single precision */ short rpt_0x4A( TSIPPKT *rpt, float *lat, float *lon, float *alt, float *clock_bias, float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 20) return TRUE; *lat = bGetSingle (buf); *lon = bGetSingle (&buf[4]); *alt = bGetSingle (&buf[8]); *clock_bias = bGetSingle (&buf[12]); *time_of_fix = bGetSingle (&buf[16]); return FALSE; } /* reference altitude parameters */ short rpt_0x4A_2( TSIPPKT *rpt, float *alt, float *dummy, unsigned char *alt_flag ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 9) return TRUE; *alt = bGetSingle (buf); *dummy = bGetSingle (&buf[4]); *alt_flag = buf[8]; return FALSE; } /* machine ID code, status */ short rpt_0x4B( TSIPPKT *rpt, unsigned char *machine_id, unsigned char *status3, unsigned char *status4 ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 3) return TRUE; *machine_id = buf[0]; *status3 = buf[1]; *status4 = buf[2]; return FALSE; } /* operating parameters and masks */ short rpt_0x4C( TSIPPKT *rpt, unsigned char *dyn_code, float *el_mask, float *snr_mask, float *dop_mask, float *dop_switch ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 17) return TRUE; *dyn_code = buf[0]; *el_mask = bGetSingle (&buf[1]); *snr_mask = bGetSingle (&buf[5]); *dop_mask = bGetSingle (&buf[9]); *dop_switch = bGetSingle (&buf[13]); return FALSE; } /* oscillator offset */ short rpt_0x4D( TSIPPKT *rpt, float *osc_offset ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 4) return TRUE; *osc_offset = bGetSingle (buf); return FALSE; } /* yes/no response to command to set GPS time */ short rpt_0x4E( TSIPPKT *rpt, unsigned char *response ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 1) return TRUE; *response = buf[0]; return FALSE; } /* UTC data */ short rpt_0x4F( TSIPPKT *rpt, double *a0, float *a1, float *time_of_data, short *dt_ls, short *wn_t, short *wn_lsf, short *dn, short *dt_lsf ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 26) return TRUE; *a0 = bGetDouble (buf); *a1 = bGetSingle (&buf[8]); *dt_ls = bGetShort (&buf[12]); *time_of_data = bGetSingle (&buf[14]); *wn_t = bGetShort (&buf[18]); *wn_lsf = bGetShort (&buf[20]); *dn = bGetShort (&buf[22]); *dt_lsf = bGetShort (&buf[24]); return FALSE; } /**/ /* clock offset and frequency offset in 1-SV (0-D) mode */ short rpt_0x54( TSIPPKT *rpt, float *clock_bias, float *freq_offset, float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 12) return TRUE; *clock_bias = bGetSingle (buf); *freq_offset = bGetSingle (&buf[4]); *time_of_fix = bGetSingle (&buf[8]); return FALSE; } /* I/O serial options */ short rpt_0x55( TSIPPKT *rpt, unsigned char *pos_code, unsigned char *vel_code, unsigned char *time_code, unsigned char *aux_code ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 4) return TRUE; *pos_code = buf[0]; *vel_code = buf[1]; *time_code = buf[2]; *aux_code = buf[3]; return FALSE; } /* velocity in east-north-up coordinates */ short rpt_0x56( TSIPPKT *rpt, float vel_ENU[3], float *freq_offset, float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 20) return TRUE; /* east */ vel_ENU[0] = bGetSingle (buf); /* north */ vel_ENU[1] = bGetSingle (&buf[4]); /* up */ vel_ENU[2] = bGetSingle (&buf[8]); *freq_offset = bGetSingle (&buf[12]); *time_of_fix = bGetSingle (&buf[16]); return FALSE; } /* info about last computed fix */ short rpt_0x57( TSIPPKT *rpt, unsigned char *source_code, unsigned char *diag_code, short *week_num, float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 8) return TRUE; *source_code = buf[0]; *diag_code = buf[1]; *time_of_fix = bGetSingle (&buf[2]); *week_num = bGetShort (&buf[6]); return FALSE; } /* GPS system data or acknowledgment of GPS system data load */ short rpt_0x58( TSIPPKT *rpt, unsigned char *op_code, unsigned char *data_type, unsigned char *sv_prn, unsigned char *data_length, unsigned char *data_packet ) { unsigned char *buf, *buf4; short dl; ALM_INFO* alminfo; ION_INFO* ioninfo; UTC_INFO* utcinfo; NAV_INFO* navinfo; buf = rpt->buf; if (buf[0] == 2) { if (rpt->len < 4) return TRUE; if (rpt->len != 4+buf[3]) return TRUE; } else if (rpt->len != 3) { return TRUE; } *op_code = buf[0]; *data_type = buf[1]; *sv_prn = buf[2]; if (*op_code == 2) { dl = buf[3]; *data_length = (unsigned char)dl; buf4 = &buf[4]; switch (*data_type) { case 2: /* Almanac */ if (*data_length != sizeof (ALM_INFO)) return TRUE; alminfo = (ALM_INFO*)data_packet; alminfo->t_oa_raw = buf4[0]; alminfo->SV_health = buf4[1]; alminfo->e = bGetSingle(&buf4[2]); alminfo->t_oa = bGetSingle(&buf4[6]); alminfo->i_0 = bGetSingle(&buf4[10]); alminfo->OMEGADOT = bGetSingle(&buf4[14]); alminfo->sqrt_A = bGetSingle(&buf4[18]); alminfo->OMEGA_0 = bGetSingle(&buf4[22]); alminfo->omega = bGetSingle(&buf4[26]); alminfo->M_0 = bGetSingle(&buf4[30]); alminfo->a_f0 = bGetSingle(&buf4[34]); alminfo->a_f1 = bGetSingle(&buf4[38]); alminfo->Axis = bGetSingle(&buf4[42]); alminfo->n = bGetSingle(&buf4[46]); alminfo->OMEGA_n = bGetSingle(&buf4[50]); alminfo->ODOT_n = bGetSingle(&buf4[54]); alminfo->t_zc = bGetSingle(&buf4[58]); alminfo->weeknum = bGetShort(&buf4[62]); alminfo->wn_oa = bGetShort(&buf4[64]); break; case 3: /* Almanac health page */ if (*data_length != sizeof (ALH_PARMS) + 3) return TRUE; /* this record is returned raw */ memcpy (data_packet, buf4, dl); break; case 4: /* Ionosphere */ if (*data_length != sizeof (ION_INFO) + 8) return TRUE; ioninfo = (ION_INFO*)data_packet; ioninfo->alpha_0 = bGetSingle (&buf4[8]); ioninfo->alpha_1 = bGetSingle (&buf4[12]); ioninfo->alpha_2 = bGetSingle (&buf4[16]); ioninfo->alpha_3 = bGetSingle (&buf4[20]); ioninfo->beta_0 = bGetSingle (&buf4[24]); ioninfo->beta_1 = bGetSingle (&buf4[28]); ioninfo->beta_2 = bGetSingle (&buf4[32]); ioninfo->beta_3 = bGetSingle (&buf4[36]); break; case 5: /* UTC */ if (*data_length != sizeof (UTC_INFO) + 13) return TRUE; utcinfo = (UTC_INFO*)data_packet; utcinfo->A_0 = bGetDouble (&buf4[13]); utcinfo->A_1 = bGetSingle (&buf4[21]); utcinfo->delta_t_LS = bGetShort (&buf4[25]); utcinfo->t_ot = bGetSingle(&buf4[27]); utcinfo->WN_t = bGetShort (&buf4[31]); utcinfo->WN_LSF = bGetShort (&buf4[33]); utcinfo->DN = bGetShort (&buf4[35]); utcinfo->delta_t_LSF = bGetShort (&buf4[37]); break; case 6: /* Ephemeris */ if (*data_length != sizeof (NAV_INFO) - 1) return TRUE; navinfo = (NAV_INFO*)data_packet; navinfo->sv_number = buf4[0]; navinfo->t_ephem = bGetSingle (&buf4[1]); navinfo->ephclk.weeknum = bGetShort (&buf4[5]); navinfo->ephclk.codeL2 = buf4[7]; navinfo->ephclk.L2Pdata = buf4[8]; navinfo->ephclk.SVacc_raw = buf4[9]; navinfo->ephclk.SV_health = buf4[10]; navinfo->ephclk.IODC = bGetShort (&buf4[11]); navinfo->ephclk.T_GD = bGetSingle (&buf4[13]); navinfo->ephclk.t_oc = bGetSingle (&buf4[17]); navinfo->ephclk.a_f2 = bGetSingle (&buf4[21]); navinfo->ephclk.a_f1 = bGetSingle (&buf4[25]); navinfo->ephclk.a_f0 = bGetSingle (&buf4[29]); navinfo->ephclk.SVacc = bGetSingle (&buf4[33]); navinfo->ephorb.IODE = buf4[37]; navinfo->ephorb.fit_interval = buf4[38]; navinfo->ephorb.C_rs = bGetSingle (&buf4[39]); navinfo->ephorb.delta_n = bGetSingle (&buf4[43]); navinfo->ephorb.M_0 = bGetDouble (&buf4[47]); navinfo->ephorb.C_uc = bGetSingle (&buf4[55]); navinfo->ephorb.e = bGetDouble (&buf4[59]); navinfo->ephorb.C_us = bGetSingle (&buf4[67]); navinfo->ephorb.sqrt_A = bGetDouble (&buf4[71]); navinfo->ephorb.t_oe = bGetSingle (&buf4[79]); navinfo->ephorb.C_ic = bGetSingle (&buf4[83]); navinfo->ephorb.OMEGA_0 = bGetDouble (&buf4[87]); navinfo->ephorb.C_is = bGetSingle (&buf4[95]); navinfo->ephorb.i_0 = bGetDouble (&buf4[99]); navinfo->ephorb.C_rc = bGetSingle (&buf4[107]); navinfo->ephorb.omega = bGetDouble (&buf4[111]); navinfo->ephorb.OMEGADOT=bGetSingle (&buf4[119]); navinfo->ephorb.IDOT = bGetSingle (&buf4[123]); navinfo->ephorb.Axis = bGetDouble (&buf4[127]); navinfo->ephorb.n = bGetDouble (&buf4[135]); navinfo->ephorb.r1me2 = bGetDouble (&buf4[143]); navinfo->ephorb.OMEGA_n=bGetDouble (&buf4[151]); navinfo->ephorb.ODOT_n = bGetDouble (&buf4[159]); break; } } return FALSE; } /* satellite enable/disable or health heed/ignore list */ short rpt_0x59( TSIPPKT *rpt, unsigned char *code_type, unsigned char status_code[32] ) { short iprn; unsigned char *buf; buf = rpt->buf; if (rpt->len != 33) return TRUE; *code_type = buf[0]; for (iprn = 0; iprn < 32; iprn++) status_code[iprn] = buf[iprn + 1]; return FALSE; } /* raw measurement data - code phase/Doppler */ short rpt_0x5A( TSIPPKT *rpt, unsigned char *sv_prn, float *sample_length, float *signal_level, float *code_phase, float *Doppler, double *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 25) return TRUE; *sv_prn = buf[0]; *sample_length = bGetSingle (&buf[1]); *signal_level = bGetSingle (&buf[5]); *code_phase = bGetSingle (&buf[9]); *Doppler = bGetSingle (&buf[13]); *time_of_fix = bGetDouble (&buf[17]); return FALSE; } /* satellite ephorb status */ short rpt_0x5B( TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *sv_health, unsigned char *sv_iode, unsigned char *fit_interval_flag, float *time_of_collection, float *time_of_eph, float *sv_accy ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 16) return TRUE; *sv_prn = buf[0]; *time_of_collection = bGetSingle (&buf[1]); *sv_health = buf[5]; *sv_iode = buf[6]; *time_of_eph = bGetSingle (&buf[7]); *fit_interval_flag = buf[11]; *sv_accy = bGetSingle (&buf[12]); return FALSE; } /* satellite tracking status */ short rpt_0x5C( TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *slot, unsigned char *chan, unsigned char *acq_flag, unsigned char *eph_flag, float *signal_level, float *time_of_last_msmt, float *elev, float *azim, unsigned char *old_msmt_flag, unsigned char *integer_msec_flag, unsigned char *bad_data_flag, unsigned char *data_collect_flag ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 24) return TRUE; *sv_prn = buf[0]; *slot = (unsigned char)((buf[1] & 0x07) + 1); *chan = (unsigned char)(buf[1] >> 3); if (*chan == 0x10) *chan = 2; else (*chan)++; *acq_flag = buf[2]; *eph_flag = buf[3]; *signal_level = bGetSingle (&buf[4]); *time_of_last_msmt = bGetSingle (&buf[8]); *elev = bGetSingle (&buf[12]); *azim = bGetSingle (&buf[16]); *old_msmt_flag = buf[20]; *integer_msec_flag = buf[21]; *bad_data_flag = buf[22]; *data_collect_flag = buf[23]; return FALSE; } /**/ /* over-determined satellite selection for position fixes, PDOP, fix mode */ short rpt_0x6D( TSIPPKT *rpt, unsigned char *manual_mode, unsigned char *nsvs, unsigned char *ndim, unsigned char sv_prn[], float *pdop, float *hdop, float *vdop, float *tdop ) { short islot; unsigned char *buf; buf = rpt->buf; *nsvs = (unsigned char)((buf[0] & 0xF0) >> 4); if ((*nsvs)>8) return TRUE; if (rpt->len != 17 + (*nsvs) ) return TRUE; *manual_mode = (unsigned char)(buf[0] & 0x08); *ndim = (unsigned char)((buf[0] & 0x07)); *pdop = bGetSingle (&buf[1]); *hdop = bGetSingle (&buf[5]); *vdop = bGetSingle (&buf[9]); *tdop = bGetSingle (&buf[13]); for (islot = 0; islot < (*nsvs); islot++) sv_prn[islot] = buf[islot + 17]; return FALSE; } /**/ /* differential fix mode */ short rpt_0x82( TSIPPKT *rpt, unsigned char *diff_mode ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 1) return TRUE; *diff_mode = buf[0]; return FALSE; } /* position, ECEF double precision */ short rpt_0x83( TSIPPKT *rpt, double ECEF_pos[3], double *clock_bias, float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 36) return TRUE; ECEF_pos[0] = bGetDouble (buf); ECEF_pos[1] = bGetDouble (&buf[8]); ECEF_pos[2] = bGetDouble (&buf[16]); *clock_bias = bGetDouble (&buf[24]); *time_of_fix = bGetSingle (&buf[32]); return FALSE; } /* position, lat-lon-alt double precision */ short rpt_0x84( TSIPPKT *rpt, double *lat, double *lon, double *alt, double *clock_bias, float *time_of_fix ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 36) return TRUE; *lat = bGetDouble (buf); *lon = bGetDouble (&buf[8]); *alt = bGetDouble (&buf[16]); *clock_bias = bGetDouble (&buf[24]); *time_of_fix = bGetSingle (&buf[32]); return FALSE; } short rpt_Paly0xBB( TSIPPKT *rpt, TSIP_RCVR_CFG *TsipxBB ) { unsigned char *buf; buf = rpt->buf; /* Palisade is inconsistent with other TSIP, which has a length of 40 */ /* if (rpt->len != 40) return TRUE; */ if (rpt->len != 43) return TRUE; TsipxBB->bSubcode = buf[0]; TsipxBB->operating_mode = buf[1]; TsipxBB->dyn_code = buf[3]; TsipxBB->elev_mask = bGetSingle (&buf[5]); TsipxBB->cno_mask = bGetSingle (&buf[9]); TsipxBB->dop_mask = bGetSingle (&buf[13]); TsipxBB->dop_switch = bGetSingle (&buf[17]); return FALSE; } /* Receiver serial port configuration */ short rpt_0xBC( TSIPPKT *rpt, unsigned char *port_num, unsigned char *in_baud, unsigned char *out_baud, unsigned char *data_bits, unsigned char *parity, unsigned char *stop_bits, unsigned char *flow_control, unsigned char *protocols_in, unsigned char *protocols_out, unsigned char *reserved ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 10) return TRUE; *port_num = buf[0]; *in_baud = buf[1]; *out_baud = buf[2]; *data_bits = buf[3]; *parity = buf[4]; *stop_bits = buf[5]; *flow_control = buf[6]; *protocols_in = buf[7]; *protocols_out = buf[8]; *reserved = buf[9]; return FALSE; } /**** Superpackets ****/ short rpt_0x8F0B( TSIPPKT *rpt, unsigned short *event, double *tow, unsigned char *date, unsigned char *month, short *year, unsigned char *dim_mode, short *utc_offset, double *bias, double *drift, float *bias_unc, float *dr_unc, double *lat, double *lon, double *alt, char sv_id[8] ) { short local_index; unsigned char *buf; buf = rpt->buf; if (rpt->len != 74) return TRUE; *event = bGetShort(&buf[1]); *tow = bGetDouble(&buf[3]); *date = buf[11]; *month = buf[12]; *year = bGetShort(&buf[13]); *dim_mode = buf[15]; *utc_offset = bGetShort(&buf[16]); *bias = bGetDouble(&buf[18]); *drift = bGetDouble(&buf[26]); *bias_unc = bGetSingle(&buf[34]); *dr_unc = bGetSingle(&buf[38]); *lat = bGetDouble(&buf[42]); *lon = bGetDouble(&buf[50]); *alt = bGetDouble(&buf[58]); for (local_index=0; local_index<8; local_index++) sv_id[local_index] = buf[local_index + 66]; return FALSE; } /* datum index and coefficients */ short rpt_0x8F14( TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5] ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 43) return TRUE; *datum_idx = bGetShort(&buf[1]); datum_coeffs[0] = bGetDouble (&buf[3]); datum_coeffs[1] = bGetDouble (&buf[11]); datum_coeffs[2] = bGetDouble (&buf[19]); datum_coeffs[3] = bGetDouble (&buf[27]); datum_coeffs[4] = bGetDouble (&buf[35]); return FALSE; } /* datum index and coefficients */ short rpt_0x8F15( TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5] ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 43) return TRUE; *datum_idx = bGetShort(&buf[1]); datum_coeffs[0] = bGetDouble (&buf[3]); datum_coeffs[1] = bGetDouble (&buf[11]); datum_coeffs[2] = bGetDouble (&buf[19]); datum_coeffs[3] = bGetDouble (&buf[27]); datum_coeffs[4] = bGetDouble (&buf[35]); return FALSE; } #define MAX_LONG (2147483648.) /* 2**31 */ short rpt_0x8F20( TSIPPKT *rpt, unsigned char *info, double *lat, double *lon, double *alt, double vel_enu[], double *time_of_fix, short *week_num, unsigned char *nsvs, unsigned char sv_prn[], short sv_IODC[], short *datum_index ) { short isv; unsigned char *buf, prnx, iode; unsigned long ulongtemp; long longtemp; double vel_scale; buf = rpt->buf; if (rpt->len != 56) return TRUE; vel_scale = (buf[24]&1)? 0.020 : 0.005; vel_enu[0] = bGetShort (buf+2)*vel_scale; vel_enu[1] = bGetShort (buf+4)*vel_scale; vel_enu[2] = bGetShort (buf+6)*vel_scale; *time_of_fix = bGetULong (buf+8)*.001; longtemp = bGetLong (buf+12); *lat = longtemp*(GPS_PI/MAX_LONG); ulongtemp = bGetULong (buf+16); *lon = ulongtemp*(GPS_PI/MAX_LONG); if (*lon > GPS_PI) *lon -= 2.0*GPS_PI; *alt = bGetLong (buf+20)*.001; /* 25 blank; 29 = UTC */ (*datum_index) = (short)((short)buf[26]-1); *info = buf[27]; *nsvs = buf[28]; *week_num = bGetShort (&buf[30]); for (isv = 0; isv < 8; isv++) { prnx = buf[32+2*isv]; sv_prn[isv] = (unsigned char)(prnx&0x3F); iode = buf[33+2*isv]; sv_IODC[isv] = (short)(iode | ((prnx>>6)<<8)); } return FALSE; } short rpt_0x8F41( TSIPPKT *rpt, unsigned char *bSearchRange, unsigned char *bBoardOptions, unsigned long *iiSerialNumber, unsigned char *bBuildYear, unsigned char *bBuildMonth, unsigned char *bBuildDay, unsigned char *bBuildHour, float *fOscOffset, unsigned short *iTestCodeId ) { if (rpt->len != 17) return FALSE; *bSearchRange = rpt->buf[1]; *bBoardOptions = rpt->buf[2]; *iiSerialNumber = bGetLong(&rpt->buf[3]); *bBuildYear = rpt->buf[7]; *bBuildMonth = rpt->buf[8]; *bBuildDay = rpt->buf[9]; *bBuildHour = rpt->buf[10]; *fOscOffset = bGetSingle(&rpt->buf[11]); *iTestCodeId = bGetShort(&rpt->buf[15]); /* Tsipx8E41Data = *Tsipx8E41; */ return TRUE; } short rpt_0x8F42( TSIPPKT *rpt, unsigned char *bProdOptionsPre, unsigned char *bProdNumberExt, unsigned short *iCaseSerialNumberPre, unsigned long *iiCaseSerialNumber, unsigned long *iiProdNumber, unsigned short *iPremiumOptions, unsigned short *iMachineID, unsigned short *iKey ) { if (rpt->len != 19) return FALSE; *bProdOptionsPre = rpt->buf[1]; *bProdNumberExt = rpt->buf[2]; *iCaseSerialNumberPre = bGetShort(&rpt->buf[3]); *iiCaseSerialNumber = bGetLong(&rpt->buf[5]); *iiProdNumber = bGetLong(&rpt->buf[9]); *iPremiumOptions = bGetShort(&rpt->buf[13]); *iMachineID = bGetShort(&rpt->buf[15]); *iKey = bGetShort(&rpt->buf[17]); return TRUE; } short rpt_0x8F45( TSIPPKT *rpt, unsigned char *bSegMask ) { if (rpt->len != 2) return FALSE; *bSegMask = rpt->buf[1]; return TRUE; } /* Stinger PPS definition */ short rpt_0x8F4A_16( TSIPPKT *rpt, unsigned char *pps_enabled, unsigned char *pps_timebase, unsigned char *pos_polarity, double *pps_offset, float *bias_unc_threshold ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 16) return TRUE; *pps_enabled = buf[1]; *pps_timebase = buf[2]; *pos_polarity = buf[3]; *pps_offset = bGetDouble(&buf[4]); *bias_unc_threshold = bGetSingle(&buf[12]); return FALSE; } short rpt_0x8F4B( TSIPPKT *rpt, unsigned long *decorr_max ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 5) return TRUE; *decorr_max = bGetLong(&buf[1]); return FALSE; } short rpt_0x8F4D( TSIPPKT *rpt, unsigned long *event_mask ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 5) return TRUE; *event_mask = bGetULong (&buf[1]); return FALSE; } short rpt_0x8FA5( TSIPPKT *rpt, unsigned char *spktmask ) { unsigned char *buf; buf = rpt->buf; if (rpt->len != 5) return TRUE; spktmask[0] = buf[1]; spktmask[1] = buf[2]; spktmask[2] = buf[3]; spktmask[3] = buf[4]; return FALSE; } short rpt_0x8FAD( TSIPPKT *rpt, unsigned short *COUNT, double *FracSec, unsigned char *Hour, unsigned char *Minute, unsigned char *Second, unsigned char *Day, unsigned char *Month, unsigned short *Year, unsigned char *Status, unsigned char *Flags ) { if (rpt->len != 22) return TRUE; *COUNT = bGetUShort(&rpt->buf[1]); *FracSec = bGetDouble(&rpt->buf[3]); *Hour = rpt->buf[11]; *Minute = rpt->buf[12]; *Second = rpt->buf[13]; *Day = rpt->buf[14]; *Month = rpt->buf[15]; *Year = bGetUShort(&rpt->buf[16]); *Status = rpt->buf[18]; *Flags = rpt->buf[19]; return FALSE; } /* * ************************************************************************* * * Trimble Navigation, Ltd. * OEM Products Development Group * P.O. Box 3642 * 645 North Mary Avenue * Sunnyvale, California 94088-3642 * * Corporate Headquarter: * Telephone: (408) 481-8000 * Fax: (408) 481-6005 * * Technical Support Center: * Telephone: (800) 767-4822 (U.S. and Canada) * (408) 481-6940 (outside U.S. and Canada) * Fax: (408) 481-6020 * BBS: (408) 481-7800 * e-mail: trimble_support@trimble.com * ftp://ftp.trimble.com/pub/sct/embedded/bin * * ************************************************************************* * * T_REPORT.C consists of a primary function TranslateTSIPReportToText() * called by main(). * * This function takes a character buffer that has been received as a report * from a TSIP device and interprets it. The character buffer has been * assembled using tsip_input_proc() in T_PARSER.C. * * A large case statement directs processing to one of many mid-level * functions. The mid-level functions specific to the current report * code passes the report buffer to the appropriate report decoder * rpt_0x?? () in T_PARSER.C, which converts the byte stream in rpt.buf * to data values approporaite for use. * * ************************************************************************* * */ #define GOOD_PARSE 0 #define BADID_PARSE 1 #define BADLEN_PARSE 2 #define BADDATA_PARSE 3 #define B_TSIP 0x02 #define B_NMEA 0x04 /* pbuf is the pointer to the current location of the text output */ static char *pbuf; /* keep track of whether the message has been successfully parsed */ static short parsed; /* convert time of week into day-hour-minute-second and print */ char * show_time( float time_of_week ) { short days, hours, minutes; float seconds; double tow = 0; static char timestring [80]; if (time_of_week == -1.0) { sprintf(timestring, " "); } else if ((time_of_week >= 604800.0) || (time_of_week < 0.0)) { sprintf(timestring, " "); } else { if (time_of_week < 604799.9) tow = time_of_week + .00000001; seconds = (float)fmod(tow, 60.); minutes = (short) fmod(tow/60., 60.); hours = (short)fmod(tow / 3600., 24.); days = (short)(tow / 86400.0); sprintf(timestring, " %s %02d:%02d:%05.2f ", dayname[days], hours, minutes, seconds); } return timestring; } /**/ /* 0x3D */ static void rpt_chan_A_config( TSIPPKT *rpt ) { unsigned char tx_baud_index, rx_baud_index, char_format_index, stop_bits, tx_mode_index, rx_mode_index, databits, parity; int i, nbaud; /* unload rptbuf */ if (rpt_0x3D (rpt, &tx_baud_index, &rx_baud_index, &char_format_index, &stop_bits, &tx_mode_index, &rx_mode_index)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nChannel A Configuration"); nbaud = sizeof(old_baudnum); for (i = 0; i < nbaud; ++i) if (tx_baud_index == old_baudnum[i]) break; pbuf += sprintf(pbuf, "\n Transmit speed: %s at %s", old_output_ch[tx_mode_index], st_baud_text_app[i]); for (i = 0; i < nbaud; ++i) if (rx_baud_index == old_baudnum[i]) break; pbuf += sprintf(pbuf, "\n Receive speed: %s at %s", old_input_ch[rx_mode_index], st_baud_text_app[i]); databits = (unsigned char)((char_format_index & 0x03) + 5); parity = (unsigned char)(char_format_index >> 2); if (parity > 4) parity = 2; pbuf += sprintf(pbuf, "\n Character format (bits/char, parity, stop bits): %d-%s-%d", databits, old_parity_text[parity], stop_bits); } /**/ /* 0x40 */ static void rpt_almanac_data_page( TSIPPKT *rpt ) { unsigned char sv_prn; short week_num; float t_zc, eccentricity, t_oa, i_0, OMEGA_dot, sqrt_A, OMEGA_0, omega, M_0; /* unload rptbuf */ if (rpt_0x40 (rpt, &sv_prn, &week_num, &t_zc, &eccentricity, &t_oa, &i_0, &OMEGA_dot, &sqrt_A, &OMEGA_0, &omega, &M_0)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nAlmanac for SV %02d", sv_prn); pbuf += sprintf(pbuf, "\n Captured:%15.0f %s", t_zc, show_time (t_zc)); pbuf += sprintf(pbuf, "\n week:%15d", week_num); pbuf += sprintf(pbuf, "\n Eccentricity:%15g", eccentricity); pbuf += sprintf(pbuf, "\n T_oa:%15.0f %s", t_oa, show_time (t_oa)); pbuf += sprintf(pbuf, "\n i 0:%15g", i_0); pbuf += sprintf(pbuf, "\n OMEGA dot:%15g", OMEGA_dot); pbuf += sprintf(pbuf, "\n sqrt A:%15g", sqrt_A); pbuf += sprintf(pbuf, "\n OMEGA 0:%15g", OMEGA_0); pbuf += sprintf(pbuf, "\n omega:%15g", omega); pbuf += sprintf(pbuf, "\n M 0:%15g", M_0); } /* 0x41 */ static void rpt_GPS_time( TSIPPKT *rpt ) { float time_of_week, UTC_offset; short week_num; /* unload rptbuf */ if (rpt_0x41 (rpt, &time_of_week, &UTC_offset, &week_num)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nGPS time:%s GPS week: %d UTC offset %.1f", show_time(time_of_week), week_num, UTC_offset); } /* 0x42 */ static void rpt_single_ECEF_position( TSIPPKT *rpt ) { float ECEF_pos[3], time_of_fix; /* unload rptbuf */ if (rpt_0x42 (rpt, ECEF_pos, &time_of_fix)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nSXYZ: %15.0f %15.0f %15.0f %s", ECEF_pos[0], ECEF_pos[1], ECEF_pos[2], show_time(time_of_fix)); } /* 0x43 */ static void rpt_single_ECEF_velocity( TSIPPKT *rpt ) { float ECEF_vel[3], freq_offset, time_of_fix; /* unload rptbuf */ if (rpt_0x43 (rpt, ECEF_vel, &freq_offset, &time_of_fix)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nVelECEF: %11.3f %11.3f %11.3f %12.3f%s", ECEF_vel[0], ECEF_vel[1], ECEF_vel[2], freq_offset, show_time(time_of_fix)); } /* 0x45 */ static void rpt_SW_version( TSIPPKT *rpt ) { unsigned char major_nav_version, minor_nav_version, nav_day, nav_month, nav_year, major_dsp_version, minor_dsp_version, dsp_day, dsp_month, dsp_year; /* unload rptbuf */ if (rpt_0x45 (rpt, &major_nav_version, &minor_nav_version, &nav_day, &nav_month, &nav_year, &major_dsp_version, &minor_dsp_version, &dsp_day, &dsp_month, &dsp_year)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nFW Versions: Nav Proc %2d.%02d %2d/%2d/%2d Sig Proc %2d.%02d %2d/%2d/%2d", major_nav_version, minor_nav_version, nav_day, nav_month, nav_year, major_dsp_version, minor_dsp_version, dsp_day, dsp_month, dsp_year); } /* 0x46 */ static void rpt_rcvr_health( TSIPPKT *rpt ) { unsigned char status1, status2; const char *text; static const char const *sc_text[] = { "Doing position fixes", "Don't have GPS time yet", "Waiting for almanac collection", "DOP too high ", "No satellites available", "Only 1 satellite available", "Only 2 satellites available", "Only 3 satellites available", "No satellites usable ", "Only 1 satellite usable", "Only 2 satellites usable", "Only 3 satellites usable", "Chosen satellite unusable"}; /* unload rptbuf */ if (rpt_0x46 (rpt, &status1, &status2)) { parsed = BADLEN_PARSE; return; } text = (status1 < COUNTOF(sc_text)) ? sc_text[status1] : "(out of range)"; pbuf += sprintf(pbuf, "\nRcvr status1: %s (%02Xh); ", text, status1); pbuf += sprintf(pbuf, "status2: %s, %s (%02Xh)", (status2 & 0x01)?"No BBRAM":"BBRAM OK", (status2 & 0x10)?"No Ant":"Ant OK", status2); } /* 0x47 */ static void rpt_SNR_all_SVs( TSIPPKT *rpt ) { unsigned char nsvs, sv_prn[12]; short isv; float snr[12]; /* unload rptbuf */ if (rpt_0x47 (rpt, &nsvs, sv_prn, snr)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nSNR for satellites: %d", nsvs); for (isv = 0; isv < nsvs; isv++) { pbuf += sprintf(pbuf, "\n SV %02d %6.2f", sv_prn[isv], snr[isv]); } } /* 0x48 */ static void rpt_GPS_system_message( TSIPPKT *rpt ) { unsigned char message[23]; /* unload rptbuf */ if (rpt_0x48 (rpt, message)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nGPS message: %s", message); } /* 0x49 */ static void rpt_almanac_health_page( TSIPPKT *rpt ) { short iprn; unsigned char sv_health [32]; /* unload rptbuf */ if (rpt_0x49 (rpt, sv_health)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nAlmanac health page:"); for (iprn = 0; iprn < 32; iprn++) { if (!(iprn%5)) *pbuf++ = '\n'; pbuf += sprintf(pbuf, " SV%02d %2X", (iprn+1) , sv_health[iprn]); } } /* 0x4A */ static void rpt_single_lla_position( TSIPPKT *rpt ) { short lat_deg, lon_deg; float lat, lon, alt, clock_bias, time_of_fix; double lat_min, lon_min; unsigned char north_south, east_west; if (rpt_0x4A (rpt, &lat, &lon, &alt, &clock_bias, &time_of_fix)) { parsed = BADLEN_PARSE; return; } /* convert from radians to degrees */ lat *= (float)R2D; north_south = 'N'; if (lat < 0.0) { north_south = 'S'; lat = -lat; } lat_deg = (short)lat; lat_min = (lat - lat_deg) * 60.0; lon *= (float)R2D; east_west = 'E'; if (lon < 0.0) { east_west = 'W'; lon = -lon; } lon_deg = (short)lon; lon_min = (lon - lon_deg) * 60.0; pbuf += sprintf(pbuf, "\nSLLA: %4d: %06.3f %c%5d:%06.3f %c%10.2f %12.2f%s", lat_deg, lat_min, north_south, lon_deg, lon_min, east_west, alt, clock_bias, show_time(time_of_fix)); } /* 0x4A */ static void rpt_ref_alt( TSIPPKT *rpt ) { float alt, dummy; unsigned char alt_flag; if (rpt_0x4A_2 (rpt, &alt, &dummy, &alt_flag)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nReference Alt: %.1f m; %s", alt, alt_flag?"ON":"OFF"); } /* 0x4B */ static void rpt_rcvr_id_and_status( TSIPPKT *rpt ) { unsigned char machine_id, status3, status4; /* unload rptbuf */ if (rpt_0x4B (rpt, &machine_id, &status3, &status4)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nRcvr Machine ID: %d; Status3 = %s, %s (%02Xh)", machine_id, (status3 & 0x02)?"No RTC":"RTC OK", (status3 & 0x08)?"No Alm":"Alm OK", status3); } /* 0x4C */ static void rpt_operating_parameters( TSIPPKT *rpt ) { unsigned char dyn_code; float el_mask, snr_mask, dop_mask, dop_switch; /* unload rptbuf */ if (rpt_0x4C (rpt, &dyn_code, &el_mask, &snr_mask, &dop_mask, &dop_switch)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nOperating Parameters:"); pbuf += sprintf(pbuf, "\n Dynamics code = %d %s", dyn_code, dyn_text[dyn_code]); pbuf += sprintf(pbuf, "\n Elevation mask = %.2f", el_mask * R2D); pbuf += sprintf(pbuf, "\n SNR mask = %.2f", snr_mask); pbuf += sprintf(pbuf, "\n DOP mask = %.2f", dop_mask); pbuf += sprintf(pbuf, "\n DOP switch = %.2f", dop_switch); } /* 0x4D */ static void rpt_oscillator_offset( TSIPPKT *rpt ) { float osc_offset; /* unload rptbuf */ if (rpt_0x4D (rpt, &osc_offset)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nOscillator offset: %.2f Hz = %.3f PPM", osc_offset, osc_offset/1575.42); } /* 0x4E */ static void rpt_GPS_time_set_response( TSIPPKT *rpt ) { unsigned char response; /* unload rptbuf */ if (rpt_0x4E (rpt, &response)) { parsed = BADLEN_PARSE; return; } switch (response) { case 'Y': pbuf += sprintf(pbuf, "\nTime set accepted"); break; case 'N': pbuf += sprintf(pbuf, "\nTime set rejected or not required"); break; default: parsed = BADDATA_PARSE; } } /* 0x4F */ static void rpt_UTC_offset( TSIPPKT *rpt ) { double a0; float a1, time_of_data; short dt_ls, wn_t, wn_lsf, dn, dt_lsf; /* unload rptbuf */ if (rpt_0x4F (rpt, &a0, &a1, &time_of_data, &dt_ls, &wn_t, &wn_lsf, &dn, &dt_lsf)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nUTC Correction Data"); pbuf += sprintf(pbuf, "\n A_0 = %g ", a0); pbuf += sprintf(pbuf, "\n A_1 = %g ", a1); pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", dt_ls); pbuf += sprintf(pbuf, "\n t_ot = %.0f ", time_of_data); pbuf += sprintf(pbuf, "\n WN_t = %d ", wn_t ); pbuf += sprintf(pbuf, "\n WN_LSF = %d ", wn_lsf ); pbuf += sprintf(pbuf, "\n DN = %d ", dn ); pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", dt_lsf ); } /**/ /* 0x54 */ static void rpt_1SV_bias( TSIPPKT *rpt ) { float clock_bias, freq_offset, time_of_fix; /* unload rptbuf */ if (rpt_0x54 (rpt, &clock_bias, &freq_offset, &time_of_fix)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf (pbuf, "\nTime Fix Clock Bias: %6.2f m Freq Bias: %6.2f m/s%s", clock_bias, freq_offset, show_time (time_of_fix)); } /* 0x55 */ static void rpt_io_opt( TSIPPKT *rpt ) { unsigned char pos_code, vel_code, time_code, aux_code; /* unload rptbuf */ if (rpt_0x55 (rpt, &pos_code, &vel_code, &time_code, &aux_code)) { parsed = BADLEN_PARSE; return; } /* rptbuf unloaded */ pbuf += sprintf(pbuf, "\nI/O Options: %2X %2X %2X %2X", pos_code, vel_code, time_code, aux_code); if (pos_code & 0x01) { pbuf += sprintf(pbuf, "\n ECEF XYZ position output"); } if (pos_code & 0x02) { pbuf += sprintf(pbuf, "\n LLA position output"); } pbuf += sprintf(pbuf, (pos_code & 0x04)? "\n MSL altitude output (Geoid height) ": "\n WGS-84 altitude output"); pbuf += sprintf(pbuf, (pos_code & 0x08)? "\n MSL altitude input": "\n WGS-84 altitude input"); pbuf += sprintf(pbuf, (pos_code & 0x10)? "\n Double precision": "\n Single precision"); if (pos_code & 0x20) { pbuf += sprintf(pbuf, "\n All Enabled Superpackets"); } if (vel_code & 0x01) { pbuf += sprintf(pbuf, "\n ECEF XYZ velocity output"); } if (vel_code & 0x02) { pbuf += sprintf(pbuf, "\n ENU velocity output"); } pbuf += sprintf(pbuf, (time_code & 0x01)? "\n Time tags in UTC": "\n Time tags in GPS time"); if (time_code & 0x02) { pbuf += sprintf(pbuf, "\n Fixes delayed to integer seconds"); } if (time_code & 0x04) { pbuf += sprintf(pbuf, "\n Fixes sent only on request"); } if (time_code & 0x08) { pbuf += sprintf(pbuf, "\n Synchronized measurements"); } if (time_code & 0x10) { pbuf += sprintf(pbuf, "\n Minimize measurement propagation"); } pbuf += sprintf(pbuf, (time_code & 0x20) ? "\n PPS output at all times" : "\n PPS output during fixes"); if (aux_code & 0x01) { pbuf += sprintf(pbuf, "\n Raw measurement output"); } if (aux_code & 0x02) { pbuf += sprintf(pbuf, "\n Code-phase smoothed before output"); } if (aux_code & 0x04) { pbuf += sprintf(pbuf, "\n Additional fix status"); } pbuf += sprintf(pbuf, (aux_code & 0x08)? "\n Signal Strength Output as dBHz" : "\n Signal Strength Output as AMU"); } /* 0x56 */ static void rpt_ENU_velocity( TSIPPKT *rpt ) { float vel_ENU[3], freq_offset, time_of_fix; /* unload rptbuf */ if (rpt_0x56 (rpt, vel_ENU, &freq_offset, &time_of_fix)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nVel ENU: %11.3f %11.3f %11.3f %12.3f%s", vel_ENU[0], vel_ENU[1], vel_ENU[2], freq_offset, show_time (time_of_fix)); } /* 0x57 */ static void rpt_last_fix_info( TSIPPKT *rpt ) { unsigned char source_code, diag_code; short week_num; float time_of_fix; /* unload rptbuf */ if (rpt_0x57 (rpt, &source_code, &diag_code, &week_num, &time_of_fix)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\n source code %d; diag code: %2Xh", source_code, diag_code); pbuf += sprintf(pbuf, "\n Time of last fix:%s", show_time(time_of_fix)); pbuf += sprintf(pbuf, "\n Week of last fix: %d", week_num); } /* 0x58 */ static void rpt_GPS_system_data( TSIPPKT *rpt ) { unsigned char iprn, op_code, data_type, sv_prn, data_length, data_packet[250]; ALM_INFO *almanac; ALH_PARMS *almh; UTC_INFO *utc; ION_INFO *ionosphere; EPHEM_CLOCK *cdata; EPHEM_ORBIT *edata; NAV_INFO *nav_data; unsigned char curr_t_oa; unsigned short curr_wn_oa; static char *datname[] = {"", "", "Almanac Orbit", "Health Page & Ref Time", "Ionosphere", "UTC ", "Ephemeris"}; /* unload rptbuf */ if (rpt_0x58 (rpt, &op_code, &data_type, &sv_prn, &data_length, data_packet)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nSystem data [%d]: %s SV%02d", data_type, datname[data_type], sv_prn); switch (op_code) { case 1: pbuf += sprintf(pbuf, " Acknowledgment"); break; case 2: pbuf += sprintf(pbuf, " length = %d bytes", data_length); switch (data_type) { case 2: /* Almanac */ if (sv_prn == 0 || sv_prn > 32) { pbuf += sprintf(pbuf, " Binary PRN invalid"); return; } almanac = (ALM_INFO*)data_packet; pbuf += sprintf(pbuf, "\n t_oa_raw = % -12d SV_hlth = % -12d ", almanac->t_oa_raw , almanac->SV_health ); pbuf += sprintf(pbuf, "\n e = % -12g t_oa = % -12g ", almanac->e , almanac->t_oa ); pbuf += sprintf(pbuf, "\n i_0 = % -12g OMEGADOT = % -12g ", almanac->i_0 , almanac->OMEGADOT ); pbuf += sprintf(pbuf, "\n sqrt_A = % -12g OMEGA_0 = % -12g ", almanac->sqrt_A , almanac->OMEGA_0 ); pbuf += sprintf(pbuf, "\n omega = % -12g M_0 = % -12g ", almanac->omega , almanac->M_0 ); pbuf += sprintf(pbuf, "\n a_f0 = % -12g a_f1 = % -12g ", almanac->a_f0 , almanac->a_f1 ); pbuf += sprintf(pbuf, "\n Axis = % -12g n = % -12g ", almanac->Axis , almanac->n ); pbuf += sprintf(pbuf, "\n OMEGA_n = % -12g ODOT_n = % -12g ", almanac->OMEGA_n , almanac->ODOT_n ); pbuf += sprintf(pbuf, "\n t_zc = % -12g weeknum = % -12d ", almanac->t_zc , almanac->weeknum ); pbuf += sprintf(pbuf, "\n wn_oa = % -12d", almanac->wn_oa ); break; case 3: /* Almanac health page */ almh = (ALH_PARMS*)data_packet; pbuf += sprintf(pbuf, "\n t_oa = %d, wn_oa&0xFF = %d ", almh->t_oa, almh->WN_a); pbuf += sprintf(pbuf, "\nAlmanac health page:"); for (iprn = 0; iprn < 32; iprn++) { if (!(iprn%5)) *pbuf++ = '\n'; pbuf += sprintf(pbuf, " SV%02d %2X", (iprn+1) , almh->SV_health[iprn]); } curr_t_oa = data_packet[34]; curr_wn_oa = (unsigned short)((data_packet[35]<<8) + data_packet[36]); pbuf += sprintf(pbuf, "\n current t_oa = %d, wn_oa = %d ", curr_t_oa, curr_wn_oa); break; case 4: /* Ionosphere */ ionosphere = (ION_INFO*)data_packet; pbuf += sprintf(pbuf, "\n alpha_0 = % -12g alpha_1 = % -12g ", ionosphere->alpha_0, ionosphere->alpha_1); pbuf += sprintf(pbuf, "\n alpha_2 = % -12g alpha_3 = % -12g ", ionosphere->alpha_2, ionosphere->alpha_3); pbuf += sprintf(pbuf, "\n beta_0 = % -12g beta_1 = % -12g ", ionosphere->beta_0, ionosphere->beta_1); pbuf += sprintf(pbuf, "\n beta_2 = % -12g beta_3 = % -12g ", ionosphere->beta_2, ionosphere->beta_3); break; case 5: /* UTC */ utc = (UTC_INFO*)data_packet; pbuf += sprintf(pbuf, "\n A_0 = %g ", utc->A_0); pbuf += sprintf(pbuf, "\n A_1 = %g ", utc->A_1); pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", utc->delta_t_LS); pbuf += sprintf(pbuf, "\n t_ot = %.0f ", utc->t_ot ); pbuf += sprintf(pbuf, "\n WN_t = %d ", utc->WN_t ); pbuf += sprintf(pbuf, "\n WN_LSF = %d ", utc->WN_LSF ); pbuf += sprintf(pbuf, "\n DN = %d ", utc->DN ); pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", utc->delta_t_LSF ); break; case 6: /* Ephemeris */ if (sv_prn == 0 || sv_prn > 32) { pbuf += sprintf(pbuf, " Binary PRN invalid"); return; } nav_data = (NAV_INFO*)data_packet; pbuf += sprintf(pbuf, "\n SV_PRN = % -12d . t_ephem = % -12g . ", nav_data->sv_number , nav_data->t_ephem ); cdata = &(nav_data->ephclk); pbuf += sprintf(pbuf, "\n weeknum = % -12d . codeL2 = % -12d . L2Pdata = % -12d", cdata->weeknum , cdata->codeL2 , cdata->L2Pdata ); pbuf += sprintf(pbuf, "\n SVacc_raw = % -12d .SV_health = % -12d . IODC = % -12d", cdata->SVacc_raw, cdata->SV_health, cdata->IODC ); pbuf += sprintf(pbuf, "\n T_GD = % -12g . t_oc = % -12g . a_f2 = % -12g", cdata->T_GD, cdata->t_oc, cdata->a_f2 ); pbuf += sprintf(pbuf, "\n a_f1 = % -12g . a_f0 = % -12g . SVacc = % -12g", cdata->a_f1, cdata->a_f0, cdata->SVacc ); edata = &(nav_data->ephorb); pbuf += sprintf(pbuf, "\n IODE = % -12d .fit_intvl = % -12d . C_rs = % -12g", edata->IODE, edata->fit_interval, edata->C_rs ); pbuf += sprintf(pbuf, "\n delta_n = % -12g . M_0 = % -12g . C_uc = % -12g", edata->delta_n, edata->M_0, edata->C_uc ); pbuf += sprintf(pbuf, "\n ecc = % -12g . C_us = % -12g . sqrt_A = % -12g", edata->e, edata->C_us, edata->sqrt_A ); pbuf += sprintf(pbuf, "\n t_oe = % -12g . C_ic = % -12g . OMEGA_0 = % -12g", edata->t_oe, edata->C_ic, edata->OMEGA_0 ); pbuf += sprintf(pbuf, "\n C_is = % -12g . i_0 = % -12g . C_rc = % -12g", edata->C_is, edata->i_0, edata->C_rc ); pbuf += sprintf(pbuf, "\n omega = % -12g . OMEGADOT = % -12g . IDOT = % -12g", edata->omega, edata->OMEGADOT, edata->IDOT ); pbuf += sprintf(pbuf, "\n Axis = % -12g . n = % -12g . r1me2 = % -12g", edata->Axis, edata->n, edata->r1me2 ); pbuf += sprintf(pbuf, "\n OMEGA_n = % -12g . ODOT_n = % -12g", edata->OMEGA_n, edata->ODOT_n ); break; } } } /* 0x59: */ static void rpt_SVs_enabled( TSIPPKT *rpt ) { unsigned char numsvs, code_type, status_code[32]; short iprn; /* unload rptbuf */ if (rpt_0x59 (rpt, &code_type, status_code)) { parsed = BADLEN_PARSE; return; } switch (code_type) { case 3: pbuf += sprintf(pbuf, "\nSVs Disabled:\n"); break; case 6: pbuf += sprintf(pbuf, "\nSVs with Health Ignored:\n"); break; default: return; } numsvs = 0; for (iprn = 0; iprn < 32; iprn++) { if (status_code[iprn]) { pbuf += sprintf(pbuf, " %02d", iprn+1); numsvs++; } } if (numsvs == 0) pbuf += sprintf(pbuf, "None"); } /* 0x5A */ static void rpt_raw_msmt( TSIPPKT *rpt ) { unsigned char sv_prn; float sample_length, signal_level, code_phase, Doppler; double time_of_fix; /* unload rptbuf */ if (rpt_0x5A (rpt, &sv_prn, &sample_length, &signal_level, &code_phase, &Doppler, &time_of_fix)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\n %02d %5.0f %7.1f %10.2f %10.2f %12.3f %s", sv_prn, sample_length, signal_level, code_phase, Doppler, time_of_fix, show_time ((float)time_of_fix)); } /* 0x5B */ static void rpt_SV_ephemeris_status( TSIPPKT *rpt ) { unsigned char sv_prn, sv_health, sv_iode, fit_interval_flag; float time_of_collection, time_of_eph, sv_accy; /* unload rptbuf */ if (rpt_0x5B (rpt, &sv_prn, &sv_health, &sv_iode, &fit_interval_flag, &time_of_collection, &time_of_eph, &sv_accy)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\n SV%02d %s %2Xh %2Xh ", sv_prn, show_time (time_of_collection), sv_health, sv_iode); /* note: cannot use show_time twice in same call */ pbuf += sprintf(pbuf, "%s %1d %4.1f", show_time (time_of_eph), fit_interval_flag, sv_accy); } /* 0x5C */ static void rpt_SV_tracking_status( TSIPPKT *rpt ) { unsigned char sv_prn, chan, slot, acq_flag, eph_flag, old_msmt_flag, integer_msec_flag, bad_data_flag, data_collect_flag; float signal_level, time_of_last_msmt, elev, azim; /* unload rptbuf */ if (rpt_0x5C (rpt, &sv_prn, &slot, &chan, &acq_flag, &eph_flag, &signal_level, &time_of_last_msmt, &elev, &azim, &old_msmt_flag, &integer_msec_flag, &bad_data_flag, &data_collect_flag)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\n SV%2d %1d %1d %1d %4.1f %s %5.1f %5.1f", sv_prn, chan, acq_flag, eph_flag, signal_level, show_time(time_of_last_msmt), elev*R2D, azim*R2D); } /**/ /* 0x6D */ static void rpt_allSV_selection( TSIPPKT *rpt ) { unsigned char manual_mode, nsvs, sv_prn[8], ndim; short islot; float pdop, hdop, vdop, tdop; /* unload rptbuf */ if (rpt_0x6D (rpt, &manual_mode, &nsvs, &ndim, sv_prn, &pdop, &hdop, &vdop, &tdop)) { parsed = BADLEN_PARSE; return; } switch (ndim) { case 0: pbuf += sprintf(pbuf, "\nMode: Searching, %d-SV:", nsvs); break; case 1: pbuf += sprintf(pbuf, "\nMode: One-SV Timing:"); break; case 3: case 4: pbuf += sprintf(pbuf, "\nMode: %c-%dD, %d-SV:", manual_mode ? 'M' : 'A', ndim - 1, nsvs); break; case 5: pbuf += sprintf(pbuf, "\nMode: Timing, %d-SV:", nsvs); break; default: pbuf += sprintf(pbuf, "\nMode: Unknown = %d:", ndim); break; } for (islot = 0; islot < nsvs; islot++) { if (sv_prn[islot]) pbuf += sprintf(pbuf, " %02d", sv_prn[islot]); } if (ndim == 3 || ndim == 4) { pbuf += sprintf(pbuf, "; DOPs: P %.1f H %.1f V %.1f T %.1f", pdop, hdop, vdop, tdop); } } /**/ /* 0x82 */ static void rpt_DGPS_position_mode( TSIPPKT *rpt ) { unsigned char diff_mode; /* unload rptbuf */ if (rpt_0x82 (rpt, &diff_mode)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nFix is%s DGPS-corrected (%s mode) (%d)", (diff_mode&1) ? "" : " not", (diff_mode&2) ? "auto" : "manual", diff_mode); } /* 0x83 */ static void rpt_double_ECEF_position( TSIPPKT *rpt ) { double ECEF_pos[3], clock_bias; float time_of_fix; /* unload rptbuf */ if (rpt_0x83 (rpt, ECEF_pos, &clock_bias, &time_of_fix)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nDXYZ:%12.2f %13.2f %13.2f %12.2f%s", ECEF_pos[0], ECEF_pos[1], ECEF_pos[2], clock_bias, show_time(time_of_fix)); } /* 0x84 */ static void rpt_double_lla_position( TSIPPKT *rpt ) { short lat_deg, lon_deg; double lat, lon, lat_min, lon_min, alt, clock_bias; float time_of_fix; unsigned char north_south, east_west; /* unload rptbuf */ if (rpt_0x84 (rpt, &lat, &lon, &alt, &clock_bias, &time_of_fix)) { parsed = BADLEN_PARSE; return; } lat *= R2D; lon *= R2D; if (lat < 0.0) { north_south = 'S'; lat = -lat; } else { north_south = 'N'; } lat_deg = (short)lat; lat_min = (lat - lat_deg) * 60.0; if (lon < 0.0) { east_west = 'W'; lon = -lon; } else { east_west = 'E'; } lon_deg = (short)lon; lon_min = (lon - lon_deg) * 60.0; pbuf += sprintf(pbuf, "\nDLLA: %2d:%08.5f %c; %3d:%08.5f %c; %10.2f %12.2f%s", lat_deg, lat_min, north_south, lon_deg, lon_min, east_west, alt, clock_bias, show_time(time_of_fix)); } /* 0xBB */ static void rpt_complete_rcvr_config( TSIPPKT *rpt ) { TSIP_RCVR_CFG TsipxBB ; /* unload rptbuf */ if (rpt_Paly0xBB (rpt, &TsipxBB)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\n operating mode: %s", NavModeText0xBB[TsipxBB.operating_mode]); pbuf += sprintf(pbuf, "\n dynamics: %s", dyn_text[TsipxBB.dyn_code]); pbuf += sprintf(pbuf, "\n elev angle mask: %g deg", TsipxBB.elev_mask * R2D); pbuf += sprintf(pbuf, "\n SNR mask: %g AMU", TsipxBB.cno_mask); pbuf += sprintf(pbuf, "\n DOP mask: %g", TsipxBB.dop_mask); pbuf += sprintf(pbuf, "\n DOP switch: %g", TsipxBB.dop_switch); return ; } /* 0xBC */ static void rpt_rcvr_serial_port_config( TSIPPKT *rpt ) { unsigned char port_num, in_baud, out_baud, data_bits, parity, stop_bits, flow_control, protocols_in, protocols_out, reserved; unsigned char known; /* unload rptbuf */ if (rpt_0xBC (rpt, &port_num, &in_baud, &out_baud, &data_bits, &parity, &stop_bits, &flow_control, &protocols_in, &protocols_out, &reserved)) { parsed = BADLEN_PARSE; return; } /* rptbuf unloaded */ pbuf += sprintf(pbuf, "\n RECEIVER serial port %s config:", rcvr_port_text[port_num]); pbuf += sprintf(pbuf, "\n I/O Baud %s/%s, %d - %s - %d", st_baud_text_app[in_baud], st_baud_text_app[out_baud], data_bits+5, parity_text[parity], stop_bits=1); pbuf += sprintf(pbuf, "\n Input protocols: "); known = FALSE; if (protocols_in&B_TSIP) { pbuf += sprintf(pbuf, "%s ", protocols_in_text[1]); known = TRUE; } if (known == FALSE) pbuf += sprintf(pbuf, "No known"); pbuf += sprintf(pbuf, "\n Output protocols: "); known = FALSE; if (protocols_out&B_TSIP) { pbuf += sprintf(pbuf, "%s ", protocols_out_text[1]); known = TRUE; } if (protocols_out&B_NMEA) { pbuf += sprintf(pbuf, "%s ", protocols_out_text[2]); known = TRUE; } if (known == FALSE) pbuf += sprintf(pbuf, "No known"); reserved = reserved; } /* 0x8F */ /* 8F0B */ static void rpt_8F0B( TSIPPKT *rpt ) { const char *oprtng_dim[7] = { "horizontal (2-D)", "full position (3-D)", "single satellite (0-D)", "automatic", "N/A", "N/A", "overdetermined clock"}; char sv_id[8]; unsigned char month, date, dim_mode, north_south, east_west; unsigned short event; short utc_offset, year, local_index; short lat_deg, lon_deg; float bias_unc, dr_unc; double tow, bias, drift, lat, lon, alt, lat_min, lon_min; int numfix, numnotfix; if (rpt_0x8F0B(rpt, &event, &tow, &date, &month, &year, &dim_mode, &utc_offset, &bias, &drift, &bias_unc, &dr_unc, &lat, &lon, &alt, sv_id)) { parsed = BADLEN_PARSE; return; } if (event == 0) { pbuf += sprintf(pbuf, "\nNew partial+full meas"); } else { pbuf += sprintf(pbuf, "\nEvent count: %5d", event); } pbuf += sprintf(pbuf, "\nGPS time : %s %2d/%2d/%2d (DMY)", show_time(tow), date, month, year); pbuf += sprintf(pbuf, "\nMode : %s", oprtng_dim[dim_mode]); pbuf += sprintf(pbuf, "\nUTC offset: %2d", utc_offset); pbuf += sprintf(pbuf, "\nClock Bias: %6.2f m", bias); pbuf += sprintf(pbuf, "\nFreq bias : %6.2f m/s", drift); pbuf += sprintf(pbuf, "\nBias unc : %6.2f m", bias_unc); pbuf += sprintf(pbuf, "\nFreq unc : %6.2f m/s", dr_unc); lat *= R2D; /* convert from radians to degrees */ lon *= R2D; if (lat < 0.0) { north_south = 'S'; lat = -lat; } else { north_south = 'N'; } lat_deg = (short)lat; lat_min = (lat - lat_deg) * 60.0; if (lon < 0.0) { east_west = 'W'; lon = -lon; } else { east_west = 'E'; } lon_deg = (short)lon; lon_min = (lon - lon_deg) * 60.0; pbuf += sprintf(pbuf, "\nPosition :"); pbuf += sprintf(pbuf, " %4d %6.3f %c", lat_deg, lat_min, north_south); pbuf += sprintf(pbuf, " %5d %6.3f %c", lon_deg, lon_min, east_west); pbuf += sprintf(pbuf, " %10.2f", alt); numfix = numnotfix = 0; for (local_index=0; local_index<8; local_index++) { if (sv_id[local_index] < 0) numnotfix++; if (sv_id[local_index] > 0) numfix++; } if (numfix > 0) { pbuf += sprintf(pbuf, "\nSVs used in fix : "); for (local_index=0; local_index<8; local_index++) { if (sv_id[local_index] > 0) { pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]); } } } if (numnotfix > 0) { pbuf += sprintf(pbuf, "\nOther SVs tracked: "); for (local_index=0; local_index<8; local_index++) { if (sv_id[local_index] < 0) { pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]); } } } } /* 0x8F14 */ /* Datum parameters */ static void rpt_8F14( TSIPPKT *rpt ) { double datum_coeffs[5]; short datum_idx; /* unload rptbuf */ if (rpt_0x8F14 (rpt, &datum_idx, datum_coeffs)) { parsed = BADLEN_PARSE; return; } if (datum_idx == -1) { pbuf += sprintf(pbuf, "\nUser-Entered Datum:"); pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]); pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]); pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]); pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]); pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]); } else if (datum_idx == 0) { pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 "); } else { pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx); } } /* 0x8F15 */ /* Datum parameters */ static void rpt_8F15( TSIPPKT *rpt ) { double datum_coeffs[5]; short datum_idx; /* unload rptbuf */ if (rpt_0x8F15 (rpt, &datum_idx, datum_coeffs)) { parsed = BADLEN_PARSE; return; } if (datum_idx == -1) { pbuf += sprintf(pbuf, "\nUser-Entered Datum:"); pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]); pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]); pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]); pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]); pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]); } else if (datum_idx == 0) { pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 "); } else { pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx); } } /* 0x8F20 */ #define INFO_DGPS 0x02 #define INFO_2D 0x04 #define INFO_ALTSET 0x08 #define INFO_FILTERED 0x10 static void rpt_8F20( TSIPPKT *rpt ) { unsigned char info, nsvs, sv_prn[32]; short week_num, datum_index, sv_IODC[32]; double lat, lon, alt, time_of_fix; double londeg, latdeg, vel[3]; short isv; char datum_string[20]; /* unload rptbuf */ if (rpt_0x8F20 (rpt, &info, &lat, &lon, &alt, vel, &time_of_fix, &week_num, &nsvs, sv_prn, sv_IODC, &datum_index)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nFix at: %04d:%3s:%02d:%02d:%06.3f GPS (=UTC+%2ds) FixType: %s%s%s", week_num, dayname[(short)(time_of_fix/86400.0)], (short)fmod(time_of_fix/3600., 24.), (short)fmod(time_of_fix/60., 60.), fmod(time_of_fix, 60.), (char)rpt->buf[29], /* UTC offset */ (info & INFO_DGPS)?"Diff":"", (info & INFO_2D)?"2D":"3D", (info & INFO_FILTERED)?"-Filtrd":""); if (datum_index > 0) { sprintf(datum_string, "Datum%3d", datum_index); } else if (datum_index) { sprintf(datum_string, "Unknown "); } else { sprintf(datum_string, "WGS-84"); } /* convert from radians to degrees */ latdeg = R2D * fabs(lat); londeg = R2D * fabs(lon); pbuf += sprintf(pbuf, "\n Pos: %4d:%09.6f %c %5d:%09.6f %c %10.2f m HAE (%s)", (short)latdeg, fmod (latdeg, 1.)*60.0, (lat<0.0)?'S':'N', (short)londeg, fmod (londeg, 1.)*60.0, (lon<0.0)?'W':'E', alt, datum_string); pbuf += sprintf(pbuf, "\n Vel: %9.3f E %9.3f N %9.3f U (m/sec)", vel[0], vel[1], vel[2]); pbuf += sprintf(pbuf, "\n SVs: "); for (isv = 0; isv < nsvs; isv++) { pbuf += sprintf(pbuf, " %02d", sv_prn[isv]); } pbuf += sprintf(pbuf, " (IODEs:"); for (isv = 0; isv < nsvs; isv++) { pbuf += sprintf(pbuf, " %02X", sv_IODC[isv]&0xFF); } pbuf += sprintf(pbuf, ")"); } /* 0x8F41 */ static void rpt_8F41( TSIPPKT *rpt ) { unsigned char bSearchRange, bBoardOptions, bBuildYear, bBuildMonth, bBuildDay, bBuildHour; float fOscOffset; unsigned short iTestCodeId; unsigned long iiSerialNumber; if (!rpt_0x8F41(rpt, &bSearchRange, &bBoardOptions, &iiSerialNumber, &bBuildYear, &bBuildMonth, &bBuildDay, &bBuildHour, &fOscOffset, &iTestCodeId)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\n search range: %d", bSearchRange); pbuf += sprintf(pbuf, "\n board options: %d", bBoardOptions); pbuf += sprintf(pbuf, "\n board serial #: %ld", iiSerialNumber); pbuf += sprintf(pbuf, "\n build date/hour: %02d/%02d/%02d %02d:00", bBuildDay, bBuildMonth, bBuildYear, bBuildHour); pbuf += sprintf(pbuf, "\n osc offset: %.3f PPM (%.0f Hz)", fOscOffset/1575.42, fOscOffset); pbuf += sprintf(pbuf, "\n test code: %d", iTestCodeId); } /* 0x8F42 */ static void rpt_8F42( TSIPPKT *rpt ) { unsigned char bProdOptionsPre, bProdNumberExt; unsigned short iCaseSerialNumberPre, iPremiumOptions, iMachineID, iKey; unsigned long iiCaseSerialNumber, iiProdNumber; if (!rpt_0x8F42(rpt, &bProdOptionsPre, &bProdNumberExt, &iCaseSerialNumberPre, &iiCaseSerialNumber, &iiProdNumber, &iPremiumOptions, &iMachineID, &iKey)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nProduct ID 8F42"); pbuf += sprintf(pbuf, "\n extension: %d", bProdNumberExt); pbuf += sprintf(pbuf, "\n case serial # prefix: %d", iCaseSerialNumberPre); pbuf += sprintf(pbuf, "\n case serial #: %ld", iiCaseSerialNumber); pbuf += sprintf(pbuf, "\n prod. #: %ld", iiProdNumber); pbuf += sprintf(pbuf, "\n premium options: %Xh", iPremiumOptions); pbuf += sprintf(pbuf, "\n machine ID: %d", iMachineID); pbuf += sprintf(pbuf, "\n key: %Xh", iKey); } /* 0x8F45 */ static void rpt_8F45( TSIPPKT *rpt ) { unsigned char bSegMask; if (!rpt_0x8F45(rpt, &bSegMask)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nCleared Segment Mask: %Xh", bSegMask); } /* Stinger PPS def */ static void rpt_8F4A( TSIPPKT *rpt ) { unsigned char pps_enabled, pps_timebase, pps_polarity; float bias_unc_threshold; double pps_offset; if (rpt_0x8F4A_16 (rpt, &pps_enabled, &pps_timebase, &pps_polarity, &pps_offset, &bias_unc_threshold)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nPPS is %s", pps_enabled?"enabled":"disabled"); pbuf += sprintf(pbuf, "\n timebase: %s", PPSTimeBaseText[pps_timebase]); pbuf += sprintf(pbuf, "\n polarity: %s", PPSPolarityText[pps_polarity]); pbuf += sprintf(pbuf, "\n offset: %.1f ns, ", pps_offset*1.e9); pbuf += sprintf(pbuf, "\n biasunc: %.1f ns", bias_unc_threshold/GPS_C*1.e9); } /* fast-SA decorrolation time for self-survey */ static void rpt_8F4B( TSIPPKT *rpt ) { unsigned long decorr_max; if (rpt_0x8F4B(rpt, &decorr_max)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nMax # of position fixes for self-survey : %ld", decorr_max); } static void rpt_8F4D( TSIPPKT *rpt ) { static char *linestart; unsigned long OutputMask; static unsigned long MaskBit[] = { 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000100L, 0x00000800L, 0x00001000L, 0x40000000L, 0x80000000L}; int ichoice, numchoices; if (rpt_0x8F4D(rpt, &OutputMask)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nAuto-Report Mask: %02X %02X %02X %02X", (unsigned char)(OutputMask>>24), (unsigned char)(OutputMask>>16), (unsigned char)(OutputMask>>8), (unsigned char)OutputMask); numchoices = sizeof(MaskText)/sizeof(char*); pbuf += sprintf(pbuf, "\nAuto-Reports scheduled for Output:"); linestart = pbuf; for (ichoice = 0; ichoice < numchoices; ichoice++) { if (OutputMask&MaskBit[ichoice]) { pbuf += sprintf(pbuf, "%s %s", (pbuf==linestart)?"\n ":",", MaskText[ichoice]); if (pbuf-linestart > 60) linestart = pbuf; } } pbuf += sprintf(pbuf, "\nAuto-Reports NOT scheduled for Output:"); linestart = pbuf; for (ichoice = 0; ichoice < numchoices; ichoice++) { if (OutputMask&MaskBit[ichoice]) continue; pbuf += sprintf(pbuf, "%s %s", (pbuf==linestart)?"\n ":",", MaskText[ichoice]); if (pbuf-linestart > 60) linestart = pbuf; } } static void rpt_8FA5( TSIPPKT *rpt ) { unsigned char spktmask[4]; if (rpt_0x8FA5(rpt, spktmask)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\nSuperpacket auto-output mask: %02X %02X %02X %02X", spktmask[0], spktmask[1], spktmask[2], spktmask[3]); if (spktmask[0]&0x01) pbuf+= sprintf (pbuf, "\n PPS 8F-0B"); if (spktmask[0]&0x02) pbuf+= sprintf (pbuf, "\n Event 8F-0B"); if (spktmask[0]&0x10) pbuf+= sprintf (pbuf, "\n PPS 8F-AD"); if (spktmask[0]&0x20) pbuf+= sprintf (pbuf, "\n Event 8F-AD"); if (spktmask[2]&0x01) pbuf+= sprintf (pbuf, "\n ppos Fix 8F-20"); } static void rpt_8FAD( TSIPPKT *rpt ) { unsigned short Count, Year; double FracSec; unsigned char Hour, Minute, Second, Day, Month, Status, Flags; static char* Status8FADText[] = { "CODE_DOING_FIXES", "CODE_GOOD_1_SV", "CODE_APPX_1SV", "CODE_NEED_TIME", "CODE_NEED_INITIALIZATION", "CODE_PDOP_HIGH", "CODE_BAD_1SV", "CODE_0SVS", "CODE_1SV", "CODE_2SVS", "CODE_3SVS", "CODE_NO_INTEGRITY", "CODE_DCORR_GEN", "CODE_OVERDET_CLK", "Invalid Status"}, *LeapStatusText[] = { " UTC Avail", " ", " ", " ", " Scheduled", " Pending", " Warning", " In Progress"}; int i; if (rpt_0x8FAD (rpt, &Count, &FracSec, &Hour, &Minute, &Second, &Day, &Month, &Year, &Status, &Flags)) { parsed = BADLEN_PARSE; return; } pbuf += sprintf(pbuf, "\n8FAD Count: %d Status: %s", Count, Status8FADText[Status]); pbuf += sprintf(pbuf, "\n Leap Flags:"); if (Flags) { for (i=0; i<8; i++) { if (Flags&(1<code, rpt->len); } if (parsed == BADID_PARSE) { pbuf += sprintf(pbuf, "\nTSIP report packet ID %2Xh, length %d: translation not supported", rpt->code, rpt->len); } if (parsed == BADDATA_PARSE) { pbuf += sprintf(pbuf, "\nTSIP report packet ID %2Xh, length %d: data content incorrect", rpt->code, rpt->len); } for (i = 0; i < rpt->len; i++) { if ((i % 20) == 0) *pbuf++ = '\n'; pbuf += sprintf(pbuf, " %02X", rpt->buf[i]); } } /**/ /* ** main subroutine, called from ProcessInputBytesWhileWaitingForKBHit() */ void TranslateTSIPReportToText( TSIPPKT *rpt, char *TextOutputBuffer ) { /* pbuf is the pointer to the current location of the text output */ pbuf = TextOutputBuffer; /* keep track of whether the message has been successfully parsed */ parsed = GOOD_PARSE; /* print a header if this is the first of a series of messages */ pbuf += print_msg_table_header (rpt->code, pbuf, FALSE); /* process incoming TSIP report according to code */ switch (rpt->code) { case 0x3D: rpt_chan_A_config (rpt); break; case 0x40: rpt_almanac_data_page (rpt); break; case 0x41: rpt_GPS_time (rpt); break; case 0x42: rpt_single_ECEF_position (rpt); break; case 0x43: rpt_single_ECEF_velocity (rpt); break; case 0x45: rpt_SW_version (rpt); break; case 0x46: rpt_rcvr_health (rpt); break; case 0x47: rpt_SNR_all_SVs (rpt); break; case 0x48: rpt_GPS_system_message (rpt); break; case 0x49: rpt_almanac_health_page (rpt); break; case 0x4A: switch (rpt->len) { /* ** special case (=slip-up) in the TSIP protocol; ** parsing method depends on length */ case 20: rpt_single_lla_position (rpt); break; case 9: rpt_ref_alt (rpt); break; } break; case 0x4B: rpt_rcvr_id_and_status (rpt);break; case 0x4C: rpt_operating_parameters (rpt); break; case 0x4D: rpt_oscillator_offset (rpt); break; case 0x4E: rpt_GPS_time_set_response (rpt); break; case 0x4F: rpt_UTC_offset (rpt); break; case 0x54: rpt_1SV_bias (rpt); break; case 0x55: rpt_io_opt (rpt); break; case 0x56: rpt_ENU_velocity (rpt); break; case 0x57: rpt_last_fix_info (rpt); break; case 0x58: rpt_GPS_system_data (rpt); break; case 0x59: rpt_SVs_enabled (rpt); break; case 0x5A: rpt_raw_msmt (rpt); break; case 0x5B: rpt_SV_ephemeris_status (rpt); break; case 0x5C: rpt_SV_tracking_status (rpt); break; case 0x6D: rpt_allSV_selection (rpt); break; case 0x82: rpt_DGPS_position_mode (rpt); break; case 0x83: rpt_double_ECEF_position (rpt); break; case 0x84: rpt_double_lla_position (rpt); break; case 0xBB: rpt_complete_rcvr_config (rpt); break; case 0xBC: rpt_rcvr_serial_port_config (rpt); break; case 0x8F: switch (rpt->buf[0]) { /* superpackets; parsed according to subcodes */ case 0x0B: rpt_8F0B(rpt); break; case 0x14: rpt_8F14(rpt); break; case 0x15: rpt_8F15(rpt); break; case 0x20: rpt_8F20(rpt); break; case 0x41: rpt_8F41(rpt); break; case 0x42: rpt_8F42(rpt); break; case 0x45: rpt_8F45(rpt); break; case 0x4A: rpt_8F4A(rpt); break; case 0x4B: rpt_8F4B(rpt); break; case 0x4D: rpt_8F4D(rpt); break; case 0xA5: rpt_8FA5(rpt); break; case 0xAD: rpt_8FAD(rpt); break; default: parsed = BADID_PARSE; break; } break; default: parsed = BADID_PARSE; break; } if (parsed != GOOD_PARSE) { /* **The message has TSIP structure (DLEs, etc.) ** but could not be parsed by above routines */ unknown_rpt (rpt); } /* close TextOutputBuffer */ pbuf = '\0'; } #endif /* TRIMBLE_OUTPUT_FUNC */ #else /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */ NONEMPTY_TRANSLATION_UNIT #endif /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */