📄 refclock_mx4200.c
字号:
/* * "000" Initialization/Mode Control - Part A * Fix to our averaged position. */ if (up->central_meridian != NOT_INITIALIZED) { up->avg_lon += up->central_meridian; if (up->avg_lon < -180.0) up->avg_lon += 360.0; if (up->avg_lon > 180.0) up->avg_lon -= 360.0; } if (up->avg_lat >= 0.0) { lat = up->avg_lat; nsc = 'N'; } else { lat = up->avg_lat * (-1.0); nsc = 'S'; } if (up->avg_lon >= 0.0) { lon = up->avg_lon; ewc = 'E'; } else { lon = up->avg_lon * (-1.0); ewc = 'W'; } alt = up->avg_alt; minute = (lat - (double)(int)lat) * 60.0; sprintf(lats,"%02d%02.4f", (int)lat, minute); minute = (lon - (double)(int)lon) * 60.0; sprintf(lons,"%03d%02.4f", (int)lon, minute); mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg, PMVXG_S_INITMODEA, /* day of month */ /* month of year */ /* year */ /* gmt */ lats, /* latitude DDMM.MMMM */ nsc, /* north/south */ lons, /* longitude DDDMM.MMMM */ ewc, /* east/west */ alt, /* Altitude */ 1); /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/ msyslog(LOG_DEBUG, "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m", lats, nsc, lons, ewc, alt );}/* * mx4200_poll - mx4200 watchdog routine */static voidmx4200_poll( int unit, struct peer *peer ){ register struct mx4200unit *up; struct refclockproc *pp; pp = peer->procptr; up = (struct mx4200unit *)pp->unitptr; /* * You don't need to poll this clock. It puts out timecodes * once per second. If asked for a timestamp, take note. * The next time a timecode comes in, it will be fed back. */ /* * If we haven't had a response in a while, reset the receiver. */ if (up->pollcnt > 0) { up->pollcnt--; } else { refclock_report(peer, CEVNT_TIMEOUT); /* * Request a "000" status message which should trigger a * reconfig */ mx4200_send(peer, "%s,%03d", "CDGPQ", /* query from CDU to GPS */ PMVXG_D_STATUS); /* label of desired sentence */ } /* * polled every 64 seconds. Ask mx4200_receive to hand in * a timestamp. */ up->polled = 1; pp->polls++; /* * Output receiver status information. */ if ((up->log_time > 0) && (current_time > up->log_time)) { up->log_time = 0; /* * Output the following messages once, for debugging. * "004" Mode Data * "523" Time Recovery Parameters */ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA); mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE); }}static char char2hex[] = "0123456789ABCDEF";/* * mx4200_receive - receive gps data */static voidmx4200_receive( struct recvbuf *rbufp ){ register struct mx4200unit *up; struct refclockproc *pp; struct peer *peer; char *cp; int sentence_type; u_char ck; /* * Initialize pointers and read the timecode and timestamp. */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct mx4200unit *)pp->unitptr; /* * If operating mode has been changed, then reinitialize the receiver * before doing anything else. */ if ((pp->sloppyclockflag & CLK_FLAG2) != (up->sloppyclockflag & CLK_FLAG2)) { up->sloppyclockflag = pp->sloppyclockflag; mx4200_debug(peer, "mx4200_receive: mode switch: reset receiver\n"); mx4200_config(peer); return; } up->sloppyclockflag = pp->sloppyclockflag; /* * Read clock output. Automatically handles STREAMS, CLKLDISC. */ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); /* * There is a case where <cr><lf> generates 2 timestamps. */ if (pp->lencode == 0) return; up->pollcnt = 2; pp->a_lastcode[pp->lencode] = '\0'; record_clock_stats(&peer->srcadr, pp->a_lastcode); mx4200_debug(peer, "mx4200_receive: %d %s\n", pp->lencode, pp->a_lastcode); /* * The structure of the control port sentences is based on the * NMEA-0183 Standard for interfacing Marine Electronics * Navigation Devices (Version 1.5) * * $PMVXG,XXX, ....................*CK<cr><lf> * * $ Sentence Start Identifier (reserved char) * (Start-of-Sentence Identifier) * P Special ID (Proprietary) * MVX Originator ID (Magnavox) * G Interface ID (GPS) * , Field Delimiters (reserved char) * XXX Sentence Type * ...... Data * * Checksum Field Delimiter (reserved char) * CK Checksum * <cr><lf> Carriage-Return/Line Feed (reserved chars) * (End-of-Sentence Identifier) * * Reject if any important landmarks are missing. */ cp = pp->a_lastcode + pp->lencode - 3; if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) { mx4200_debug(peer, "mx4200_receive: bad format\n"); refclock_report(peer, CEVNT_BADREPLY); return; } /* * Check and discard the checksum */ ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4); if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) { mx4200_debug(peer, "mx4200_receive: bad checksum\n"); refclock_report(peer, CEVNT_BADREPLY); return; } *cp = '\0'; /* * Get the sentence type. */ sentence_type = 0; if ((cp = strchr(pp->a_lastcode, ',')) == NULL) { mx4200_debug(peer, "mx4200_receive: no sentence\n"); refclock_report(peer, CEVNT_BADREPLY); return; } cp++; sentence_type = strtol(cp, &cp, 10); /* * Process the sentence according to its type. */ switch (sentence_type) { /* * "000" Status message */ case PMVXG_D_STATUS: /* * XXX * Since we configure the receiver to not give us status * messages and since the receiver outputs status messages by * default after being reset to factory defaults when sent the * "$PMVXG,018,C\r\n" message, any status message we get * indicates the reciever needs to be initialized; thus, it is * not necessary to decode the status message. */ if ((cp = mx4200_parse_s(peer)) != NULL) { mx4200_debug(peer, "mx4200_receive: status: %s\n", cp); } mx4200_debug(peer, "mx4200_receive: reset receiver\n"); mx4200_config(peer); break; /* * "021" Position, Height, Velocity message, * if we are still averaging our position */ case PMVXG_D_PHV: if (!up->known) { /* * Parse the message, calculating our averaged position. */ if ((cp = mx4200_parse_p(peer)) != NULL) { mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp); return; } mx4200_debug(peer, "mx4200_receive: position avg %f %.9f %.9f %.4f\n", up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt); /* * Reinitialize as a reference station * if position is well known. */ if (current_time > up->clamp_time) { up->known++; mx4200_debug(peer, "mx4200_receive: reconfiguring!\n"); mx4200_ref(peer); } } break; /* * Print to the syslog: * "004" Mode Data * "030" Software Configuration * "523" Time Recovery Parameters Currently in Use */ case PMVXG_D_MODEDATA: case PMVXG_D_SOFTCONF: case PMVXG_D_TRECOVUSEAGE: if ((cp = mx4200_parse_s(peer)) != NULL) { mx4200_debug(peer, "mx4200_receive: multi-record: %s\n", cp); } break; /* * "830" Time Recovery Results message */ case PMVXG_D_TRECOVOUT: /* * Capture the last PPS signal. * Precision timestamp is returned in pp->lastrec */ if (mx4200_pps(peer) != NULL) { mx4200_debug(peer, "mx4200_receive: pps failure\n"); refclock_report(peer, CEVNT_FAULT); return; } /* * Parse the time recovery message, and keep the info * to print the pretty billboards. */ if ((cp = mx4200_parse_t(peer)) != NULL) { mx4200_debug(peer, "mx4200_receive: time: %s\n", cp); refclock_report(peer, CEVNT_BADREPLY); return; } /* * Add the new sample to a median filter. */ if (!refclock_process(pp)) { mx4200_debug(peer,"mx4200_receive: offset: %.6f\n", pp->offset); refclock_report(peer, CEVNT_BADTIME); return; } /* * The clock will blurt a timecode every second but we only * want one when polled. If we havn't been polled, bail out. */ if (!up->polled) return; /* * Return offset and dispersion to control module. We use * lastrec as both the reference time and receive time in * order to avoid being cute, like setting the reference time * later than the receive time, which may cause a paranoid * protocol module to chuck out the data. */ mx4200_debug(peer, "mx4200_receive: process time: "); mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n", pp->year, pp->day, pp->hour, pp->minute, pp->second, prettydate(&pp->lastrec), pp->offset); pp->lastref = pp->lastrec; refclock_receive(peer); /* * We have succeeded in answering the poll. * Turn off the flag and return */ up->polled = 0; break; /* * Ignore all other sentence types */ default: break; } /* switch (sentence_type) */ return;}/* * Parse a mx4200 time recovery message. Returns a string if error. * * A typical message looks like this. Checksum has already been stripped. * * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL * * Field Field Contents * ----- -------------- * Block Label: $PMVXG * Sentence Type: 830=Time Recovery Results * This sentence is output approximately 1 second * preceding the 1PPS output. It indicates the * exact time of the next pulse, whether or not the * time mark will be valid (based on operator-specified * error tolerance), the time to which the pulse is * synchronized, the receiver operating mode, * and the time error of the *last* 1PPS output. * 1 char Time Mark Valid: T=Valid, F=Not Valid * 2 int Year: 1993- * 3 int Month of Year: 1-12 * 4 int Day of Month: 1-31 * 5 int Time of Day: HH:MM:SS * 6 char Time Synchronization: U=UTC, G=GPS * 7 char Time Recovery Mode: D=Dynamic, S=Static, * K=Known Position, N=No Time Recovery * 8 int Oscillator Offset: The filter's estimate of the oscillator * frequency error, in parts per billion (ppb). * 9 int Time Mark Error: The computed error of the *last* pulse * output, in nanoseconds. * 10 int User Time Bias: Operator specified bias, in nanoseconds * 11 int Leap Second Flag: Indicates that a leap second will * occur. This value is usually zero, except during * the week prior to the leap second occurrence, when * this value will be set to +1 or -1. A value of * +1 indicates that GPS time will be 1 second * further ahead of UTC time. * */static char *mx4200_parse_t( struct peer *peer ){ struct refclockproc *pp; struct mx4200unit *up; char time_mark_valid, time_sync, op_mode; int sentence_type, valid; int year, day_of_year, month, day_of_month; int hour, minute, second, leapsec; int oscillator_offset, time_mark_error, time_bias; pp = peer->procptr; up = (struct mx4200unit *)pp->unitptr; leapsec = 0; /* Not all receivers output leap second warnings (!) */ sscanf(pp->a_lastcode, "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d", &sentence_type, &time_mark_valid, &year, &month, &day_of_month, &hour, &minute, &second, &time_sync, &op_mode, &oscillator_offset, &time_mark_error, &time_bias, &leapsec); if (sentence_type != PMVXG_D_TRECOVOUT) return ("wrong rec-type"); switch (time_mark_valid) { case 'T': valid = 1; break; case 'F': valid = 0; break; default: return ("bad pulse-valid"); } switch (time_sync) { case 'G': return ("synchronized to GPS; should be UTC"); case 'U': break; /* UTC -> ok */ default: return ("not synchronized to UTC"); } /* * Check for insane time (allow for possible leap seconds) */ if (second > 60 || minute > 59 || hour > 23 || second < 0 || minute < 0 || hour < 0) { mx4200_debug(peer, "mx4200_parse_t: bad time %02d:%02d:%02d", hour, minute, second); if (leapsec != 0) mx4200_debug(peer, " (leap %+d\n)", leapsec); mx4200_debug(peer, "\n"); refclock_report(peer, CEVNT_BADTIME); return ("bad time"); } if ( second == 60 ) { msyslog(LOG_DEBUG, "mx4200: leap second! %02d:%02d:%02d", hour, minute, second); } /* * Check for insane date * (Certainly can't be any year before this code was last altered!) */ if (day_of_month > 31 || month > 12 || day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED) { mx4200_debug(peer, "mx4200_parse_t: bad date (%4d-%02d-%02d)\n", year, month, day_of_month); refclock_report(peer, CEVNT_BADDATE); return ("bad date"); } /* * Silly Hack for MX4200: * ASCII message is for *next* 1PPS signal, but we have the * timestamp for the *last* 1PPS signal. So we have to subtract * a second. Discard if we are on a month boundary to avoid * possible leap seconds and leap days. */ second--; if (second < 0) { second = 59; minute--; if (minute < 0) { minute = 59; hour--; if (hour < 0) { hour = 23; day_of_month--; if (day_of_month < 1) { return ("sorry, month boundary"); } } } } /* * Calculate Julian date */ if (!(day_of_year = mx4200_jday(year, month, day_of_month))) { mx4200_debug(peer, "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n", day_of_year, year, month, day_of_month); refclock_report(peer, CEVNT_BADDATE); return("invalid julian date"); } /* * Setup leap second indicator */ switch (leapsec) { case 0: pp->leap = LEAP_NOWARNING; break; case 1: pp->leap = LEAP_ADDSECOND; break; case -1: pp->leap = LEAP_DELSECOND; break; default: pp->leap = LEAP_NOTINSYNC; } /* * Any change to the leap second warning status? */ if (leapsec != up->last_leap ) { msyslog(LOG_DEBUG, "mx4200: leap second warning: %d to %d (%d)", up->last_leap, leapsec, pp->leap); } up->last_leap = leapsec; /* * Copy time data for billboard monitoring. */ pp->year = year; pp->day = day_of_year; pp->hour = hour; pp->minute = minute;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -