[35] | 1 | // -*- C++ -*-
|
---|
| 2 | // RTCM.C
|
---|
| 3 | // $Id: RTCM.cpp,v 1.1.1.1 2006/01/11 09:34:31 mervart Exp $
|
---|
| 4 | // 2005/04/11: counter 'iPCode' for indicating CA or P Code on L1 (BKG)
|
---|
| 5 |
|
---|
| 6 | #include "RTCM.h"
|
---|
| 7 |
|
---|
| 8 | #include <cmath>
|
---|
| 9 |
|
---|
| 10 | #if !defined(__GNUC__)
|
---|
| 11 | double rint(double val) {
|
---|
| 12 | return ((val < 0.0) ? -floor(-val+0.5) : floor(val+0.5));
|
---|
| 13 | }
|
---|
| 14 | #endif
|
---|
| 15 |
|
---|
| 16 | /* Org-ID: rtcm.c,v 1.11 1999/08/28 00:06:20 wolfgang Exp */
|
---|
| 17 |
|
---|
| 18 | /* rtcm.c - decode RTCM SC-104 data from a DGPS beacon receiver */
|
---|
| 19 |
|
---|
| 20 | /*
|
---|
| 21 | written by John Sager john.sager@btinternet.com
|
---|
| 22 | Copyright (C) 1999 John C Sager
|
---|
| 23 |
|
---|
| 24 | This program is free software; you can redistribute it and/or modify
|
---|
| 25 | it under the terms of the GNU General Public License as published by
|
---|
| 26 | the Free Software Foundation; either version 2 of the License, or
|
---|
| 27 | (at your option) any later version.
|
---|
| 28 |
|
---|
| 29 | This program is distributed in the hope that it will be useful,
|
---|
| 30 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 31 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 32 | GNU General Public License for more details.
|
---|
| 33 |
|
---|
| 34 | You should have received a copy of the GNU General Public License
|
---|
| 35 | along with this program; if not, write to the Free Software
|
---|
| 36 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
| 37 |
|
---|
| 38 | */
|
---|
| 39 |
|
---|
| 40 | /* History:
|
---|
| 41 |
|
---|
| 42 | v0.3 22nd February 1999
|
---|
| 43 | Incorrect scale factor on range error field fixed.
|
---|
| 44 | dx,dy,dz fields in Datum message are signed.
|
---|
| 45 |
|
---|
| 46 | v0.2 9th February 1999
|
---|
| 47 | Sundry improvements to the code by Wolfgang Rupprecht so
|
---|
| 48 | he could incorporate it in his dgpsip client.
|
---|
| 49 |
|
---|
| 50 | Addition of decode capability for message types 4 and 5.
|
---|
| 51 |
|
---|
| 52 | v0.1 4th February 1999
|
---|
| 53 | First version - reads RTCM data from standard input & sends
|
---|
| 54 | decoded output to standard output. Implements decoding of
|
---|
| 55 | types 1,3,6,7,9,16. This is original software and not
|
---|
| 56 | a modification of any other program.
|
---|
| 57 | */
|
---|
| 58 |
|
---|
| 59 | /* TODO
|
---|
| 60 | Add facility to decode from tty port input & write to a file
|
---|
| 61 | */
|
---|
| 62 |
|
---|
| 63 | /*
|
---|
| 64 | * The data comes across as bytes where the top 2 bits indicate the type
|
---|
| 65 | * of data and the bottom 6 bits are the data itself. Bytes 01xxxxxx
|
---|
| 66 | * are received RTCM data. Other bytes are other kinds of data/commands
|
---|
| 67 | * etc. The data framing can be at any bit position. Also the data is
|
---|
| 68 | * lsb first, so it needs to be bit-reversed.
|
---|
| 69 | *
|
---|
| 70 | * This program calculates parity over a 32-bit field starting at each
|
---|
| 71 | * bit position until a parity match is found. Then parity is checked
|
---|
| 72 | * at successive 30-bit boundaries until sufficient error-free words
|
---|
| 73 | * have been found. This gives word sync. Then frame sync is acquired
|
---|
| 74 | * by searching for the preamble and checking at subsequent frame boundaries
|
---|
| 75 | * until sufficient frames have been found. Then data is decoded.
|
---|
| 76 | *
|
---|
| 77 | * Parity errors are flagged in frames so that data before the parity
|
---|
| 78 | * error can be decoded, if possible. A parity error in the preamble
|
---|
| 79 | * word or the next word causes frame sync to be lost. Sufficient
|
---|
| 80 | * successive parity errors causes sync to be lost totally.
|
---|
| 81 | */
|
---|
| 82 |
|
---|
| 83 | #if 0
|
---|
| 84 | #include <stdio.h>
|
---|
| 85 | #include <stdlib.h>
|
---|
| 86 | #endif
|
---|
| 87 | #include <sys/types.h>
|
---|
| 88 |
|
---|
| 89 | #define DEBUG
|
---|
| 90 |
|
---|
| 91 |
|
---|
| 92 | /* state machine state */
|
---|
| 93 |
|
---|
| 94 | #define NO_SYNC 0
|
---|
| 95 | #define WORD_SYNCING 1
|
---|
| 96 | #define WORD_SYNC 2
|
---|
| 97 | #define FRAME_SYNCING 3
|
---|
| 98 | #define FULL_SYNC 4
|
---|
| 99 |
|
---|
| 100 | #define W_SYNC_TEST 6
|
---|
| 101 | #define F_SYNC_TEST 3
|
---|
| 102 | #define P_FAIL_TEST 10
|
---|
| 103 |
|
---|
| 104 | /* message types */
|
---|
| 105 |
|
---|
| 106 | #define MSG_FULLCOR 1
|
---|
| 107 | #define MSG_REFPARM 3
|
---|
| 108 | #define MSG_DATUM 4
|
---|
| 109 | #define MSG_CONHLTH 5
|
---|
| 110 | #define MSG_NULL 6
|
---|
| 111 | #define MSG_BEACALM 7
|
---|
| 112 | #define MSG_SUBSCOR 9
|
---|
| 113 | #define MSG_SPECIAL 16
|
---|
| 114 |
|
---|
| 115 | /* field scale factors */
|
---|
| 116 |
|
---|
| 117 | #define ZCOUNT_SCALE 0.6 /* sec */
|
---|
| 118 | #define RANGE_SMALL 0.02 /* metres */
|
---|
| 119 | #define RANGE_LARGE 0.32 /* metres */
|
---|
| 120 | #define RANGERATE_SMALL 0.002 /* metres/sec */
|
---|
| 121 | #define RANGERATE_LARGE 0.032 /* metres/sec */
|
---|
| 122 | #define XYZ_SCALE 0.01 /* metres */
|
---|
| 123 | #define DXYZ_SCALE 0.1 /* metres */
|
---|
| 124 | #define LA_SCALE 90.0/32767.0 /* degrees */
|
---|
| 125 | #define LO_SCALE 180.0/32767.0 /* degrees */
|
---|
| 126 | #define FREQ_SCALE 0.1 /* kHz */
|
---|
| 127 | #define FREQ_OFFSET 190.0 /* kHz */
|
---|
| 128 | #define CNR_OFFSET 24 /* dB */
|
---|
| 129 | #define TU_SCALE 5 /* minutes */
|
---|
| 130 |
|
---|
| 131 | char * RTCM::state_name[] = {
|
---|
| 132 | "NO_SYNC",
|
---|
| 133 | "WORD_SYNCING",
|
---|
| 134 | "WORD_SYNC",
|
---|
| 135 | "FRAME_SYNCING",
|
---|
| 136 | "FULL_SYNC"};
|
---|
| 137 |
|
---|
| 138 | u_int RTCM::tx_speed[] = { 25, 50, 100, 110, 150, 200, 250, 300 };
|
---|
| 139 |
|
---|
| 140 | /* parity stuff */
|
---|
| 141 |
|
---|
| 142 | #define P_30_MASK 0x40000000
|
---|
| 143 | #define P_31_MASK 0x80000000
|
---|
| 144 |
|
---|
| 145 | #define PARITY_25 0xbb1f3480
|
---|
| 146 | #define PARITY_26 0x5d8f9a40
|
---|
| 147 | #define PARITY_27 0xaec7cd00
|
---|
| 148 | #define PARITY_28 0x5763e680
|
---|
| 149 | #define PARITY_29 0x6bb1f340
|
---|
| 150 | #define PARITY_30 0x8b7a89c0
|
---|
| 151 |
|
---|
| 152 | #define W_DATA_MASK 0x3fffffc0
|
---|
| 153 |
|
---|
| 154 | u_char RTCM::parity_of[] = {
|
---|
| 155 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
|
---|
| 156 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
|
---|
| 157 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
|
---|
| 158 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
|
---|
| 159 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
|
---|
| 160 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
|
---|
| 161 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,
|
---|
| 162 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0};
|
---|
| 163 |
|
---|
| 164 | u_char RTCM::reverse_bits[] = {
|
---|
| 165 | 0,32,16,48,8,40,24,56,4,36,20,52,12,44,28,60,
|
---|
| 166 | 2,34,18,50,10,42,26,58,6,38,22,54,14,46,30,62,
|
---|
| 167 | 1,33,17,49,9,41,25,57,5,37,21,53,13,45,29,61,
|
---|
| 168 | 3,35,19,51,11,43,27,59,7,39,23,55,15,47,31,63};
|
---|
| 169 |
|
---|
| 170 |
|
---|
| 171 | #define DATA_SHIFT 6
|
---|
| 172 | #define B_DATA_MASK 0x3f
|
---|
| 173 | #define FILL_BASE 24
|
---|
| 174 |
|
---|
| 175 | #define PREAMBLE 0x19800000
|
---|
| 176 | #define PREAMBLE_MASK 0x3fc00000
|
---|
| 177 |
|
---|
| 178 | int RTCM::preamble() {
|
---|
| 179 | return ((data_word & PREAMBLE_MASK) == PREAMBLE);
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | /* state_change - change state & print a useful message */
|
---|
| 183 |
|
---|
| 184 | void RTCM::state_change(u_int s) {
|
---|
| 185 | #if 0
|
---|
| 186 | printf("M\tstate change: %s -> %s\n",
|
---|
| 187 | state_name[rtcm_state], state_name[s]);
|
---|
| 188 | fflush(stdout);
|
---|
| 189 | #endif
|
---|
| 190 | rtcm_state = s;
|
---|
| 191 | }
|
---|
| 192 |
|
---|
| 193 | /* check the parity on this_word. bits 31,30 are parity on previous word.
|
---|
| 194 | * bits 29-6 are data bits. Bits 5-0 are parity bits.
|
---|
| 195 | */
|
---|
| 196 |
|
---|
| 197 | u_int RTCM::parity_ok() {
|
---|
| 198 | u_int t, th, p;
|
---|
| 199 |
|
---|
| 200 | th = this_word;
|
---|
| 201 | if (th & P_30_MASK)
|
---|
| 202 | th ^= W_DATA_MASK;
|
---|
| 203 |
|
---|
| 204 | t = th & PARITY_25;
|
---|
| 205 | p = parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
|
---|
| 206 | parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)];
|
---|
| 207 | t = th & PARITY_26;
|
---|
| 208 | p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
|
---|
| 209 | parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
|
---|
| 210 | t = th & PARITY_27;
|
---|
| 211 | p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
|
---|
| 212 | parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
|
---|
| 213 | t = th & PARITY_28;
|
---|
| 214 | p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
|
---|
| 215 | parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
|
---|
| 216 | t = th & PARITY_29;
|
---|
| 217 | p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
|
---|
| 218 | parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
|
---|
| 219 | t = th & PARITY_30;
|
---|
| 220 | p = (p<<1) | (parity_of[t & 0xff] ^ parity_of[(t>>8)&0xff] ^
|
---|
| 221 | parity_of[(t>>16)&0xff] ^ parity_of[(t>>24)]);
|
---|
| 222 |
|
---|
| 223 |
|
---|
| 224 | if (!this_word || ((this_word &0x3f) != p)) {
|
---|
| 225 | if (rtcm_state > WORD_SYNCING)
|
---|
| 226 | pf_count++;
|
---|
| 227 | return 0;
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | return th;
|
---|
| 231 | }
|
---|
| 232 |
|
---|
| 233 | /* find word sync by a successful parity check */
|
---|
| 234 |
|
---|
| 235 | int RTCM::find_sync(u_char b) {
|
---|
| 236 | int i;
|
---|
| 237 |
|
---|
| 238 | b <<= 2;
|
---|
| 239 | i = 1;
|
---|
| 240 |
|
---|
| 241 | while (i <= DATA_SHIFT) {
|
---|
| 242 | this_word <<= 1;
|
---|
| 243 | this_word |= (b & 0x80) ? 1 : 0; /* next bit into 32 bits */
|
---|
| 244 | b <<= 1;
|
---|
| 245 | if ((data_word = parity_ok())) {
|
---|
| 246 | sync_bit = (i==DATA_SHIFT) ? 0 : i;
|
---|
| 247 | fill_shift = FILL_BASE - DATA_SHIFT + i;
|
---|
| 248 | next_bits = b >> 2;
|
---|
| 249 | return 1;
|
---|
| 250 | }
|
---|
| 251 | i++;
|
---|
| 252 | }
|
---|
| 253 | return 0;
|
---|
| 254 | }
|
---|
| 255 |
|
---|
| 256 | void RTCM::next_word() {
|
---|
| 257 | this_word = (this_word << 30) | (next_bits << 24);
|
---|
| 258 | }
|
---|
| 259 |
|
---|
| 260 | /* fill bits into this_word and indicate when filled. Put the residue bits in
|
---|
| 261 | next_bits.
|
---|
| 262 | */
|
---|
| 263 |
|
---|
| 264 | int RTCM::filled_word(u_char b) {
|
---|
| 265 |
|
---|
| 266 | if (fill_shift <= 0) { /* can complete fill */
|
---|
| 267 | if (fill_shift) {
|
---|
| 268 | next_bits = b << (DATA_SHIFT + fill_shift);
|
---|
| 269 | this_word |= b >> (-fill_shift);
|
---|
| 270 | }
|
---|
| 271 | else {
|
---|
| 272 | next_bits = 0;
|
---|
| 273 | this_word |= b;
|
---|
| 274 | }
|
---|
| 275 | fill_shift += FILL_BASE;
|
---|
| 276 | word_count++;
|
---|
| 277 | return 1;
|
---|
| 278 | }
|
---|
| 279 | this_word |= b << fill_shift;
|
---|
| 280 | fill_shift -= DATA_SHIFT;
|
---|
| 281 | return 0;
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 |
|
---|
| 285 | //
|
---|
| 286 | //
|
---|
| 287 | //
|
---|
| 288 | void RTCM::msgRTKUncorrectedCarrierPhases() {
|
---|
| 289 |
|
---|
| 290 | // const static char *freqIndicatorS[] = { "L1" , "?1" , "L2" , "?3" };
|
---|
| 291 | const u_int freqIndicator = (message[2]>>28)&0x3;
|
---|
| 292 |
|
---|
| 293 | const u_int gnssTimeofMeasurement = (message[2]>>6)&0xfffff;
|
---|
| 294 | const double expandedTimeOfMeasurement = z_count*ZCOUNT_SCALE + gnssTimeofMeasurement * .000001;
|
---|
| 295 |
|
---|
| 296 | int m;
|
---|
| 297 | for(m= 3 ; m < msg_len+2 ; m+=2 ) {
|
---|
| 298 | const int multipleMessageIndicator = (message[m] >> 29) & 0x1;
|
---|
| 299 | const int pCodeIndicator = (message[m] >> 28) & 0x1;
|
---|
| 300 | const int glonassIndicator = (message[m] >> 27) & 0x1;
|
---|
| 301 |
|
---|
| 302 | #if 0
|
---|
| 303 | const u_int svprn = (message[m] >> 22) & 0x1f
|
---|
| 304 | + (glonassIndicator ? glonass_svid : 0);
|
---|
| 305 | #else
|
---|
| 306 | const u_int svprn = (message[m] >> 22) & 0x1f;
|
---|
| 307 | #endif
|
---|
| 308 | if(!glonassIndicator) {
|
---|
| 309 | dumpOnNewTimeTag(expandedTimeOfMeasurement);
|
---|
| 310 |
|
---|
| 311 | const u_int dataQuality = (message[m] >> 18) & 0x7;
|
---|
| 312 | const u_int cumuLossOfCont = (message[m] >> 14) & 0x1f;
|
---|
| 313 |
|
---|
| 314 | obsMap[svprn].set_cumuLossOfCont(cumuLossOfCont);
|
---|
| 315 | obsMap[svprn].set_pCodeIndicator(pCodeIndicator);
|
---|
| 316 |
|
---|
| 317 | const int carrierPhase = (int)( (((message[m] >> 6) & 0xff) << 24)
|
---|
| 318 | | ((message[m+1]>> 6) & 0xffffff));
|
---|
| 319 | switch(freqIndicator) {
|
---|
| 320 | case 0: /* L1 */
|
---|
| 321 | // TBD unroll L1
|
---|
| 322 | obsMap[svprn].set_l1(carrierPhase);
|
---|
| 323 | if(pCodeIndicator > 0) {
|
---|
| 324 | iPCode++;
|
---|
| 325 | }
|
---|
| 326 | break;
|
---|
| 327 | case 2: /* L2 */
|
---|
| 328 | // TBD unroll L2
|
---|
| 329 | obsMap[svprn].set_l2(carrierPhase);
|
---|
| 330 | break;
|
---|
| 331 | default:
|
---|
| 332 | /* unknown frequency */;
|
---|
| 333 | }
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | #if 0
|
---|
| 337 | printf( "RTKPhase: %s prn:%2d %s %s %f %f %f %11.3f\n"
|
---|
| 338 | , glonassIndicator ? "GLONASS" : "GPS "
|
---|
| 339 | , svprn
|
---|
| 340 | , freqIndicatorS[freqIndicator]
|
---|
| 341 | , pCodeIndicator ? "P-Code " : "CA-Code"
|
---|
| 342 | , expandedTimeOfMeasurement
|
---|
| 343 | , r.timeTag
|
---|
| 344 | , r.clockError
|
---|
| 345 | , carrierPhase/256.
|
---|
| 346 | );
|
---|
| 347 | #endif
|
---|
| 348 | }
|
---|
| 349 | }
|
---|
| 350 |
|
---|
| 351 |
|
---|
| 352 | //
|
---|
| 353 | //
|
---|
| 354 | //
|
---|
| 355 | void RTCM::msgRTKUncorrectedPseudoranges() {
|
---|
| 356 | // const static char *freqIndicatorS[] = { "L1" , "?1" , "L2" , "?3" };
|
---|
| 357 | const u_int freqIndicator = (message[2]>>28)&0x3;
|
---|
| 358 | const u_int smoothingIntervall = (message[2]>>26)&0x3;
|
---|
| 359 | const u_int gnssTimeofMeasurement = (message[2]>>6)&0xfffff;
|
---|
| 360 | const double expandedTimeOfMeasurement = z_count*ZCOUNT_SCALE + gnssTimeofMeasurement * .000001;
|
---|
| 361 |
|
---|
| 362 | int m;
|
---|
| 363 | for(m= 3 ; m < msg_len+2 ; m+=2 ) {
|
---|
| 364 | const int multipleMessageIndicator = (message[m] >> 29) & 0x1;
|
---|
| 365 | const int pCodeIndicator = (message[m] >> 28) & 0x1;
|
---|
| 366 | const int glonassIndicator = (message[m] >> 27) & 0x1;
|
---|
| 367 | if(!glonassIndicator) {
|
---|
| 368 | dumpOnNewTimeTag(expandedTimeOfMeasurement);
|
---|
| 369 | const u_int svprn = (message[m] >> 22) & 0x1f
|
---|
| 370 | + (glonassIndicator ? glonass_svid : 0) ;
|
---|
| 371 | const u_int dataQual = (message[m] >> 18) & 0xf;
|
---|
| 372 | const u_int multiPathError = (message[m] >> 14) &0xf;
|
---|
| 373 | const u_int pseudoRange = ( (((message[m] >> 6) & 0xff) << 24)
|
---|
| 374 | | ((message[m+1]>> 6) & 0xffffff));
|
---|
| 375 | switch(freqIndicator) {
|
---|
| 376 | case 0: /* L1 */
|
---|
| 377 | obsMap[svprn].set_C1(pseudoRange*0.02);
|
---|
| 378 | if(pCodeIndicator > 0) {
|
---|
| 379 | iPCode++;
|
---|
| 380 | }
|
---|
| 381 | break;
|
---|
| 382 | case 2: /* L2 */
|
---|
| 383 | obsMap[svprn].set_P2(pseudoRange*0.02);
|
---|
| 384 | break;
|
---|
| 385 | default:
|
---|
| 386 | /* unknown frequency */;
|
---|
| 387 | }
|
---|
| 388 | }
|
---|
| 389 |
|
---|
| 390 | #if 0
|
---|
| 391 | printf( "RTKRange: %s prn:%2d %s %s %f %f %f %11.3f\n"
|
---|
| 392 | , glonassIndicator ? "GLONASS" : "GPS "
|
---|
| 393 | , svprn
|
---|
| 394 | , freqIndicatorS[freqIndicator]
|
---|
| 395 | , pCodeIndicator ? "P-Code " : "CA-Code"
|
---|
| 396 | , expandedTimeOfMeasurement
|
---|
| 397 | , r.timeTag
|
---|
| 398 | , r.clockError
|
---|
| 399 | , pseudoRange*0.02
|
---|
| 400 | );
|
---|
| 401 | #endif
|
---|
| 402 | }
|
---|
| 403 | }
|
---|
| 404 |
|
---|
| 405 |
|
---|
| 406 | /* printcor - print differential corrections - full or subset */
|
---|
| 407 |
|
---|
| 408 | void RTCM::printcor() {
|
---|
| 409 | int i, w;
|
---|
| 410 | u_int m, n;
|
---|
| 411 | int scale, udre, sat, range, rangerate, iod;
|
---|
| 412 |
|
---|
| 413 | i = 0;
|
---|
| 414 | w = 2;
|
---|
| 415 | m = 0;
|
---|
| 416 | if (pfptr) {
|
---|
| 417 | msg_len = (pfptr-message) - 2;
|
---|
| 418 | n = msg_len % 5;
|
---|
| 419 | if (n == 1 || n == 3) msg_len--;
|
---|
| 420 | if (msg_len < 2)
|
---|
| 421 | return;
|
---|
| 422 | }
|
---|
| 423 | while (w < msg_len+2) {
|
---|
| 424 | if ((i & 0x3) == 0){
|
---|
| 425 | m = message[w++] & W_DATA_MASK;
|
---|
| 426 | scale = m >> 29 & 0x1;
|
---|
| 427 | udre = (m >>27) & 0x3;
|
---|
| 428 | sat = (m >>22) & 0x1f;
|
---|
| 429 | range = (m >>6) & 0xffff;
|
---|
| 430 | if (range > 32767) range -= 65536;
|
---|
| 431 | m = message[w++] & W_DATA_MASK;
|
---|
| 432 | rangerate = (m >>22) & 0xff;
|
---|
| 433 | if (rangerate > 127) rangerate -= 256;
|
---|
| 434 | iod = (m >>14) & 0xff;
|
---|
| 435 | i++;
|
---|
| 436 | }
|
---|
| 437 | else if ((i & 0x3) == 1){
|
---|
| 438 | scale = m >> 13 & 0x1;
|
---|
| 439 | udre = (m >>11) & 0x3;
|
---|
| 440 | sat = (m >>6) & 0x1f;
|
---|
| 441 | m = message[w++] & W_DATA_MASK;
|
---|
| 442 | range = (m >>14) & 0xffff;
|
---|
| 443 | if (range > 32767) range -= 65536;
|
---|
| 444 | rangerate = (m >>6) & 0xff;
|
---|
| 445 | if (rangerate > 127) rangerate -= 256;
|
---|
| 446 | m = message[w++] & W_DATA_MASK;
|
---|
| 447 | iod = (m >>22) & 0xff;
|
---|
| 448 | i++;
|
---|
| 449 | }
|
---|
| 450 | else {
|
---|
| 451 | scale = m >> 21 & 0x1;
|
---|
| 452 | udre = (m >>19) & 0x3;
|
---|
| 453 | sat = (m >>14) & 0x1f;
|
---|
| 454 | range = (m <<2) & 0xff00;
|
---|
| 455 | m = message[w++] & W_DATA_MASK;
|
---|
| 456 | range |= (m >>22) & 0xff;
|
---|
| 457 | if (range > 32767) range -= 65536;
|
---|
| 458 | rangerate = (m >>14) & 0xff;
|
---|
| 459 | if (rangerate > 127) rangerate -= 256;
|
---|
| 460 | iod = (m >>6) & 0xff;
|
---|
| 461 | i+= 2;
|
---|
| 462 | }
|
---|
| 463 | #if 0
|
---|
| 464 | printf("S\t%d\t%d\t%d\t%.1f\t%.3f\t%.3f\n", sat, udre, iod,
|
---|
| 465 | z_count*ZCOUNT_SCALE, range*(scale?RANGE_LARGE:RANGE_SMALL),
|
---|
| 466 | rangerate*(scale?RANGERATE_LARGE:RANGERATE_SMALL));
|
---|
| 467 | #endif
|
---|
| 468 | }
|
---|
| 469 | }
|
---|
| 470 |
|
---|
| 471 | /* printref - print reference position. The transmitted xyz quantities are
|
---|
| 472 | integers scaled in units of 0.01m
|
---|
| 473 | */
|
---|
| 474 |
|
---|
| 475 | void RTCM::printref(void) {
|
---|
| 476 | int x, y, z;
|
---|
| 477 |
|
---|
| 478 | if (pfptr)
|
---|
| 479 | return;
|
---|
| 480 | x = ((message[2] & W_DATA_MASK) << 2) | ((message[3] & W_DATA_MASK) >> 22);
|
---|
| 481 | y = ((message[3] & W_DATA_MASK) << 10) | ((message[4] & W_DATA_MASK) >> 14);
|
---|
| 482 | z = ((message[4] & W_DATA_MASK) << 18) | ((message[5] & W_DATA_MASK) >> 6);
|
---|
| 483 |
|
---|
| 484 | onPosition(x*XYZ_SCALE, y*XYZ_SCALE, z*XYZ_SCALE);
|
---|
| 485 |
|
---|
| 486 | #if 0
|
---|
| 487 | printf("R\t%.2f\t%.2f\t%.2f\n", x*XYZ_SCALE, y*XYZ_SCALE, z*XYZ_SCALE);
|
---|
| 488 | #endif
|
---|
| 489 | }
|
---|
| 490 |
|
---|
| 491 | /* printba - print beacon almanac
|
---|
| 492 | */
|
---|
| 493 |
|
---|
| 494 | void RTCM::printba() {
|
---|
| 495 | int la, lo, range, freq, hlth, id, bitrate;
|
---|
| 496 |
|
---|
| 497 | if (pfptr)
|
---|
| 498 | return;
|
---|
| 499 | la = ((message[2] >> 14) & 0xffff);
|
---|
| 500 | if (la > 32767) la -= 65536;
|
---|
| 501 | lo = ((message[2] << 2) & 0xff00) | ((message[3] >> 22) & 0xff);
|
---|
| 502 | if (lo > 32767) lo -= 65536;
|
---|
| 503 | range = ((message[3] >> 12) & 0x3ff);
|
---|
| 504 | freq = ((message[3]) & 0xfc0) | ((message[4] >> 24) & 0x3f);
|
---|
| 505 | hlth = ((message[4] >> 22) & 0x3);
|
---|
| 506 | id = ((message[4] >> 12) & 0x3ff);
|
---|
| 507 | bitrate = ((message[4] >> 9) & 0x7);
|
---|
| 508 |
|
---|
| 509 | #if 0
|
---|
| 510 | printf("A\t%.4f\t%.4f\t%d\t%.1f\t%d\t%d\t%d\n", (la*LA_SCALE), (lo*LO_SCALE),
|
---|
| 511 | range, (freq*FREQ_SCALE+FREQ_OFFSET), hlth, id, tx_speed[bitrate]);
|
---|
| 512 | #endif
|
---|
| 513 | }
|
---|
| 514 |
|
---|
| 515 | /* printspec - print text of special message
|
---|
| 516 | */
|
---|
| 517 |
|
---|
| 518 | void RTCM::printspec() {
|
---|
| 519 | u_int i, d, c;
|
---|
| 520 | #if 0
|
---|
| 521 | if (pfptr)
|
---|
| 522 | msg_len = (pfptr-message)>>2;
|
---|
| 523 | printf("T\t");
|
---|
| 524 | for (i=0; i< msg_len; i++) {
|
---|
| 525 | d = message[i+2] & W_DATA_MASK;
|
---|
| 526 | if ((c=d>>22)) putchar(c); else break;
|
---|
| 527 | if ((c=((d>>14) & 0xff))) putchar(c); else break;
|
---|
| 528 | if ((c=((d>>6) & 0xff))) putchar(c); else break;
|
---|
| 529 | }
|
---|
| 530 | printf("\n");
|
---|
| 531 | #endif
|
---|
| 532 | }
|
---|
| 533 |
|
---|
| 534 | /* printnull - print a marker for a null message
|
---|
| 535 | */
|
---|
| 536 |
|
---|
| 537 | void RTCM::printnull() {
|
---|
| 538 | #if 0
|
---|
| 539 | printf("N\n");
|
---|
| 540 | #endif
|
---|
| 541 | }
|
---|
| 542 |
|
---|
| 543 | /* printdatum - print datum message
|
---|
| 544 | */
|
---|
| 545 |
|
---|
| 546 | void RTCM::printdatum() {
|
---|
| 547 | char dname[6];
|
---|
| 548 | char *dn;
|
---|
| 549 | u_int d, dgnss, dat;
|
---|
| 550 | int dx, dy, dz;
|
---|
| 551 |
|
---|
| 552 | if (pfptr)
|
---|
| 553 | return;
|
---|
| 554 | d = message[2] & W_DATA_MASK;
|
---|
| 555 | dgnss = d>>27;
|
---|
| 556 | dat = (d>>26) & 1;
|
---|
| 557 | dname[0] = (d>>14) & 0xff;
|
---|
| 558 | if (dname[0]) { /* not null */
|
---|
| 559 | dname[1] = (d>>6) & 0xff;
|
---|
| 560 | d = message[3] & W_DATA_MASK;
|
---|
| 561 | dname[2] = (d>>22) & 0xff;
|
---|
| 562 | dname[3] = (d>>14) & 0xff;
|
---|
| 563 | dname[4] = (d>>6) & 0xff;
|
---|
| 564 | dname[5] = '\0';
|
---|
| 565 | dn = dname;
|
---|
| 566 | }
|
---|
| 567 | else
|
---|
| 568 | dn = "NUL";
|
---|
| 569 | #if 0
|
---|
| 570 | printf("D\t%s\t%1d\t%s", (dgnss==0)?"GPS":((dgnss==1)?"GLONASS":"???"),
|
---|
| 571 | dat, dn);
|
---|
| 572 | #endif
|
---|
| 573 | if (msg_len > 2) {
|
---|
| 574 | d = message[4] & W_DATA_MASK;
|
---|
| 575 | dx = (d>>14) & 0xffff;
|
---|
| 576 | if (dx > 32767) dx -= 65536;
|
---|
| 577 | dy = (d<<2) & 0xff00;
|
---|
| 578 | d = message[5] & W_DATA_MASK;
|
---|
| 579 | dy |= (d>>22) & 0xff;
|
---|
| 580 | if (dy > 32767) dy -= 65536;
|
---|
| 581 | dz = (d>>6) & 0xffff;
|
---|
| 582 | if (dz > 32767) dz -= 65536;
|
---|
| 583 | #if 0
|
---|
| 584 | printf("\t%.1f\t%.1f\t%.1f", dx*DXYZ_SCALE, dy*DXYZ_SCALE, dz*DXYZ_SCALE);
|
---|
| 585 | #endif
|
---|
| 586 | }
|
---|
| 587 | #if 0
|
---|
| 588 | printf("\n");
|
---|
| 589 | #endif
|
---|
| 590 | }
|
---|
| 591 |
|
---|
| 592 | /* printconh - print constellation health message
|
---|
| 593 | */
|
---|
| 594 |
|
---|
| 595 | void RTCM::printconh() {
|
---|
| 596 | u_int i, d, sat, iodl, hlth, cnr, he, nd, lw, tu;
|
---|
| 597 |
|
---|
| 598 | if (pfptr) {
|
---|
| 599 | msg_len = (pfptr-message) - 2;
|
---|
| 600 | if (!msg_len)
|
---|
| 601 | return;
|
---|
| 602 | }
|
---|
| 603 | for (i=0; i< msg_len; i++) {
|
---|
| 604 | d = message[i+2] & W_DATA_MASK;
|
---|
| 605 | sat = (d>>24) & 0x1f;
|
---|
| 606 | if (!sat) sat = 32;
|
---|
| 607 | iodl = (d>>23) & 1;
|
---|
| 608 | hlth = (d>>20) & 0x7;
|
---|
| 609 | cnr = (d>>15) & 0x1f;
|
---|
| 610 | he = (d>>14) & 1;
|
---|
| 611 | nd = (d>>13) & 1;
|
---|
| 612 | lw = (d>>12) & 1;
|
---|
| 613 | tu = (d>>8) & 0x0f;
|
---|
| 614 | #if 0
|
---|
| 615 | printf("C\t%2d\t%1d %1d\t%2d\t%1d %1d %1d\t%2d\n",
|
---|
| 616 | sat, iodl, hlth, (cnr?(cnr+CNR_OFFSET):-1), he, nd, lw, tu*TU_SCALE);
|
---|
| 617 | #endif
|
---|
| 618 | }
|
---|
| 619 | }
|
---|
| 620 |
|
---|
| 621 | /* new_frame - called when a new frame is complete */
|
---|
| 622 |
|
---|
| 623 | void RTCM::new_frame() {
|
---|
| 624 | char s[8];
|
---|
| 625 |
|
---|
| 626 | frame_count++;
|
---|
| 627 | if (pfptr == message) /* dud frame */
|
---|
| 628 | return;
|
---|
| 629 |
|
---|
| 630 | msg_type = (message[0]>>16)&0x3f;
|
---|
| 631 | station_id = (message[0]>>6)&0x3ff;
|
---|
| 632 | z_count = (message[1]>>17)&0x1fff;
|
---|
| 633 | health = (message[1]>>6)&0x7;
|
---|
| 634 | #if 0
|
---|
| 635 | if (pfptr)
|
---|
| 636 | sprintf(s, "\tT\t%d", (pfptr-message)-2);
|
---|
| 637 |
|
---|
| 638 | printf("H\t%d\t%d\t%.1f\t%d\t%d\t%d%s\n", msg_type, station_id,
|
---|
| 639 | (z_count*ZCOUNT_SCALE), seqno, msg_len, health,
|
---|
| 640 | (pfptr)?s:"");
|
---|
| 641 | #endif
|
---|
| 642 | switch (msg_type) {
|
---|
| 643 |
|
---|
| 644 | case MSG_FULLCOR:
|
---|
| 645 | case MSG_SUBSCOR:
|
---|
| 646 | printcor();
|
---|
| 647 | break;
|
---|
| 648 |
|
---|
| 649 | case MSG_REFPARM:
|
---|
| 650 | printref();
|
---|
| 651 | break;
|
---|
| 652 |
|
---|
| 653 | case MSG_DATUM:
|
---|
| 654 | printdatum();
|
---|
| 655 | break;
|
---|
| 656 |
|
---|
| 657 | case MSG_CONHLTH:
|
---|
| 658 | printconh();
|
---|
| 659 | break;
|
---|
| 660 |
|
---|
| 661 | case MSG_NULL:
|
---|
| 662 | printnull();
|
---|
| 663 | break;
|
---|
| 664 |
|
---|
| 665 | case MSG_BEACALM:
|
---|
| 666 | printba();
|
---|
| 667 | break;
|
---|
| 668 |
|
---|
| 669 | case MSG_SPECIAL:
|
---|
| 670 | printspec();
|
---|
| 671 | break;
|
---|
| 672 |
|
---|
| 673 | case 18: /* RTK Uncorrected Carrier Phases */
|
---|
| 674 | msgRTKUncorrectedCarrierPhases();
|
---|
| 675 | break;
|
---|
| 676 |
|
---|
| 677 | case 19: /* RTK Uncorrected Pseudoranges */
|
---|
| 678 | msgRTKUncorrectedPseudoranges();
|
---|
| 679 | break;
|
---|
| 680 |
|
---|
| 681 | default:
|
---|
| 682 | #if 0
|
---|
| 683 | printf("?\t%d\n", msg_type);
|
---|
| 684 | #endif
|
---|
| 685 | break;
|
---|
| 686 | }
|
---|
| 687 | }
|
---|
| 688 |
|
---|
| 689 | void RTCM::buffer(u_int w) {
|
---|
| 690 | *fillptr++ = w;
|
---|
| 691 | }
|
---|
| 692 |
|
---|
| 693 | void RTCM::frame_start() {
|
---|
| 694 | frame_fill = -1;
|
---|
| 695 | fillptr = message;
|
---|
| 696 | pfptr = NULL;
|
---|
| 697 | buffer(data_word);
|
---|
| 698 | }
|
---|
| 699 |
|
---|
| 700 | void RTCM::find_start() {
|
---|
| 701 | if ((data_word = parity_ok()))
|
---|
| 702 | p_fail = 0;
|
---|
| 703 | else if (++p_fail >= P_FAIL_TEST) { /* too many consecutive parity fails */
|
---|
| 704 | state_change(NO_SYNC);
|
---|
| 705 | return;
|
---|
| 706 | }
|
---|
| 707 | next_word();
|
---|
| 708 | if (preamble()) {
|
---|
| 709 | seqno = -1; /* resync at next word */
|
---|
| 710 | frame_start();
|
---|
| 711 | state_change(FRAME_SYNCING);
|
---|
| 712 | }
|
---|
| 713 | }
|
---|
| 714 |
|
---|
| 715 | void RTCM::fill_frame() {
|
---|
| 716 | int seq;
|
---|
| 717 |
|
---|
| 718 | if ((data_word = parity_ok()))
|
---|
| 719 | p_fail=0;
|
---|
| 720 | else if (++p_fail >= P_FAIL_TEST) {
|
---|
| 721 | state_change(NO_SYNC);
|
---|
| 722 | return;
|
---|
| 723 | }
|
---|
| 724 | next_word();
|
---|
| 725 | frame_fill++; /* another word */
|
---|
| 726 | if (frame_fill == 0) { /* this is second header word */
|
---|
| 727 | if (!data_word) { /* parity fail - bad news! */
|
---|
| 728 | state_change(WORD_SYNC); /* lost frame sync */
|
---|
| 729 | frame_sync = F_SYNC_TEST-1; /* resync rapidly */
|
---|
| 730 | return;
|
---|
| 731 | }
|
---|
| 732 | buffer(data_word);
|
---|
| 733 | seq = (data_word>>14)&0x7;
|
---|
| 734 | msg_len = (data_word>>9)&0x1f;
|
---|
| 735 | #if 0
|
---|
| 736 | #ifdef DEBUG
|
---|
| 737 | if (debug)
|
---|
| 738 | fprintf(stderr, "ff=%d: %08x %08x %d %d %d %d %s\n", frame_fill,
|
---|
| 739 | (u_int)fillptr,
|
---|
| 740 | data_word, msg_len, word_count, pf_count, seqno, state_name[rtcm_state]);
|
---|
| 741 | #endif
|
---|
| 742 | #endif
|
---|
| 743 | if ((seqno < 0) || (((seqno +1) & 0x7) == seq))
|
---|
| 744 | seqno = seq; /* resync */
|
---|
| 745 | else { /* sequence error */
|
---|
| 746 | state_change(WORD_SYNC); /* to be on the safe side */
|
---|
| 747 | #if 0
|
---|
| 748 | fprintf(stderr,"2\n");
|
---|
| 749 | #endif
|
---|
| 750 | return;
|
---|
| 751 | }
|
---|
| 752 | }
|
---|
| 753 | else if (frame_fill > msg_len) { /* should be next preamble */
|
---|
| 754 | #if 0
|
---|
| 755 | #ifdef DEBUG
|
---|
| 756 | if (debug)
|
---|
| 757 | fprintf(stderr, "ff=%d: %08x %08x %d %d %d %d %s\n", frame_fill,
|
---|
| 758 | (u_int)fillptr,
|
---|
| 759 | data_word, msg_len, word_count, pf_count, seqno, state_name[rtcm_state]);
|
---|
| 760 | #endif
|
---|
| 761 | #endif
|
---|
| 762 | if (rtcm_state == FRAME_SYNCING) { /* be very tough */
|
---|
| 763 | if (!(data_word && preamble())) {
|
---|
| 764 | state_change(WORD_SYNC); /* start again */
|
---|
| 765 | }
|
---|
| 766 | else if (++frame_sync >= F_SYNC_TEST) {
|
---|
| 767 | /* frame_count = 0; */
|
---|
| 768 | state_change(FULL_SYNC);
|
---|
| 769 | new_frame(); /* output the last frame acquired before we start a new one */
|
---|
| 770 | }
|
---|
| 771 | frame_start(); /* new frame here */
|
---|
| 772 | }
|
---|
| 773 | else {
|
---|
| 774 | frame_start(); /* new frame here */
|
---|
| 775 | if (!data_word) /* parity error on preamble - keep sync but lose message */
|
---|
| 776 | pfptr = message; /* indicates dud message */
|
---|
| 777 | else if (!preamble()) { /* good word but no preamble! */
|
---|
| 778 | state_change(WORD_SYNC); /* can't carry on */
|
---|
| 779 | }
|
---|
| 780 | }
|
---|
| 781 | }
|
---|
| 782 | else { /* other message words */
|
---|
| 783 | #if 0
|
---|
| 784 | #ifdef DEBUG
|
---|
| 785 | if (debug)
|
---|
| 786 | fprintf(stderr, "ff=%d: %08x %08x %d %d %d %d %s\n", frame_fill,
|
---|
| 787 | (u_int)fillptr,
|
---|
| 788 | data_word, msg_len, word_count, pf_count, seqno, state_name[rtcm_state]);
|
---|
| 789 | #endif
|
---|
| 790 | #endif
|
---|
| 791 | if (!data_word && !pfptr)
|
---|
| 792 | pfptr = fillptr; /* mark the (first) error */
|
---|
| 793 | buffer(data_word);
|
---|
| 794 | if ((frame_fill == msg_len) && (rtcm_state == FULL_SYNC)) /* frame completed */
|
---|
| 795 | new_frame();
|
---|
| 796 | }
|
---|
| 797 | }
|
---|
| 798 |
|
---|
| 799 | void RTCM::status_byte(u_char b) {
|
---|
| 800 | #if 0
|
---|
| 801 | #if defined(PRINTSTATUS)
|
---|
| 802 | printf("-\tstatus\t-\t%d 0x%x\n", b, b);
|
---|
| 803 | #endif
|
---|
| 804 | #endif
|
---|
| 805 | }
|
---|
| 806 |
|
---|
| 807 |
|
---|
| 808 | /* take a data byte and process it. This will change state according to
|
---|
| 809 | * the data, place parity-checked data in a buffer and call a function to
|
---|
| 810 | * process completed frames.
|
---|
| 811 | */
|
---|
| 812 |
|
---|
| 813 | void RTCM::data_byte(u_char b) {
|
---|
| 814 |
|
---|
| 815 | b = reverse_bits[b&0x3f];
|
---|
| 816 |
|
---|
| 817 | if (rtcm_state == NO_SYNC) {
|
---|
| 818 | if(find_sync(b)) {
|
---|
| 819 | state_change(WORD_SYNCING);
|
---|
| 820 | #if 0
|
---|
| 821 | printf("M\tsync_bit: %d\n", sync_bit);
|
---|
| 822 | #endif
|
---|
| 823 | word_sync = 1;
|
---|
| 824 | next_word();
|
---|
| 825 | }
|
---|
| 826 | }
|
---|
| 827 | else if (filled_word(b)) {
|
---|
| 828 | switch (rtcm_state) {
|
---|
| 829 |
|
---|
| 830 | case WORD_SYNCING:
|
---|
| 831 | data_word = parity_ok();
|
---|
| 832 | next_word();
|
---|
| 833 | if (data_word) {
|
---|
| 834 | if (++word_sync >= W_SYNC_TEST) {
|
---|
| 835 | state_change(WORD_SYNC);
|
---|
| 836 | p_fail = 0;
|
---|
| 837 | frame_sync = 1;
|
---|
| 838 | if (preamble()) { /* just in case we hit one immediately */
|
---|
| 839 | frame_start();
|
---|
| 840 | state_change(FRAME_SYNCING);
|
---|
| 841 | }
|
---|
| 842 | }
|
---|
| 843 | }
|
---|
| 844 | else {
|
---|
| 845 | if (--word_sync <= 0) {
|
---|
| 846 | state_change(NO_SYNC);
|
---|
| 847 | return;
|
---|
| 848 | }
|
---|
| 849 | }
|
---|
| 850 | break;
|
---|
| 851 |
|
---|
| 852 | case WORD_SYNC: /* look for frame start */
|
---|
| 853 | find_start();
|
---|
| 854 | break;
|
---|
| 855 |
|
---|
| 856 | case FRAME_SYNCING:
|
---|
| 857 | fill_frame();
|
---|
| 858 | break;
|
---|
| 859 |
|
---|
| 860 | case FULL_SYNC:
|
---|
| 861 | fill_frame();
|
---|
| 862 | break;
|
---|
| 863 | }
|
---|
| 864 | }
|
---|
| 865 | }
|
---|
| 866 |
|
---|
| 867 |
|
---|
| 868 | void RTCM::new_byte(u_char b) {
|
---|
| 869 |
|
---|
| 870 | switch (b >> DATA_SHIFT) {
|
---|
| 871 |
|
---|
| 872 | case 0:
|
---|
| 873 | case 2:
|
---|
| 874 | #if 0
|
---|
| 875 | #ifdef DEBUG
|
---|
| 876 | fprintf(stderr, "unknown byte type %d (%d 0x%0x)\n", b >> DATA_SHIFT,
|
---|
| 877 | b, b);
|
---|
| 878 | #endif
|
---|
| 879 | #endif
|
---|
| 880 | return;
|
---|
| 881 |
|
---|
| 882 | case 3: /* status */
|
---|
| 883 | status_byte(b);
|
---|
| 884 | return;
|
---|
| 885 |
|
---|
| 886 | case 1: /* data */
|
---|
| 887 | data_byte(b);
|
---|
| 888 | return;
|
---|
| 889 | }
|
---|
| 890 | }
|
---|
| 891 |
|
---|
| 892 | #if 0
|
---|
| 893 | main()
|
---|
| 894 | {
|
---|
| 895 | int b;
|
---|
| 896 |
|
---|
| 897 | initialise();
|
---|
| 898 | while ((b=getchar()) != EOF)
|
---|
| 899 | new_byte(b);
|
---|
| 900 |
|
---|
| 901 | printf("M\tword count: %d\tparity failures: %d\tframe count: %d\n",
|
---|
| 902 | word_count, pf_count, frame_count);
|
---|
| 903 | }
|
---|
| 904 | #endif
|
---|
| 905 |
|
---|
| 906 |
|
---|