Added source code for RINEX to CRINEX conversion (taken on the net + minor changes)
authorBenoit Papillault <benoit.papillault@free.fr>
Fri, 23 Dec 2011 13:03:38 +0000 (14:03 +0100)
committerBenoit Papillault <benoit.papillault@free.fr>
Fri, 23 Dec 2011 13:03:38 +0000 (14:03 +0100)
crx2rnx.c [new file with mode: 0644]
rnx2crx.c [new file with mode: 0644]

diff --git a/crx2rnx.c b/crx2rnx.c
new file mode 100644 (file)
index 0000000..a457c53
--- /dev/null
+++ b/crx2rnx.c
@@ -0,0 +1,782 @@
+/***********************************************************************/
+/*     program name : CRX2RNX                                          */
+/*                                                                     */
+/*     Program to recover the RINEX file from Compact RINEX file       */
+/*     Created by Yuki HATANAKA / Geographical Survey Institute, Japan */
+/*                                                                     */
+/*     ver.                                                            */
+/*     4.0.0       2007.01.31 test version   Y. Hatanaka               */
+/*                  - CRINEX 1/3 for RINEX 2.x/3.x                     */
+/*     4.0.1       2007.05.08                Y. Hatanaka               */
+/*                  - elimination of supports for VMS and SUN OS 4.1.x */
+/*                  - output not to the current directory but the same */
+/*                    directory as the input file.                     */
+/*                  - the same code for DOS and UNIX                   */
+/*     4.0.2       2007.06.07                Y. Hatanaka               */
+/*                  - fixing incompatibility of argument and format    */
+/*                    string of printf.                                */
+/*     4.0.3       2007.06.21                Y. Hatanaka               */
+/*                  - fixing a bug on lack of carrying the number      */
+/*                    between lower and upper digits                   */
+/*                                                                     */
+/*     Copyright (c) 2007 Geographical Survey Institute                */
+/*     All rights reserved.                                            */
+/*                                                                     */
+/***********************************************************************/
+
+#define VERSION  "ver.4.0.3"
+
+/**** Exit codes are defined here. ****/
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+#define EXIT_WARNING 2
+
+/* Don't change the lines from here. */
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define CHOP_LF(q,p) p = strchr(q,'\n'); if( *(p-1) == '\r' && p>q )p--;*p = '\0';
+/* #define CHOP_LF(q,p) p = strchr(q,'\n'); *p = '\0'; */
+
+#define CHOP_BLANK(q,p) p = strchr(q,'\0');while(*--p == ' ' && p>q);*++p = '\0'
+
+/* define global constants */
+#define PROGNAME "CRX2RNX"
+#define MAXSAT     90         /* Maximum number of satellites observed at one epoch */
+#define MAXTYPE    30         /* Maximum number of data types   */
+#define MAXCLM   1024         /* Maximum columns in one line   (>MAXTYPE*19+3)  */
+#define MAX_BUFF_SIZE 131072  /* Muximum size of output buffer (>MAXSAT*(MAXTYPE*19+4)+60 */
+#define MAX_DIFF_ORDER 5      /* Muximum order of difference to be dealt with */
+
+/* define data structure for fields of clock offset and obsercvation records */
+typedef struct clock_format{
+    long u[MAX_DIFF_ORDER+1];      /* upper X digits for each diference order */
+    long l[MAX_DIFF_ORDER+1];      /* lower 8 digits */
+} clock_format;
+
+typedef struct data_format{
+    long u[MAX_DIFF_ORDER+1];      /* upper X digits for each diference order */
+    long l[MAX_DIFF_ORDER+1];      /* lower 5 digits */
+    int  order;
+    int  arc_order;
+} data_format;
+
+/* define global variables */
+clock_format clk1,clk0;
+data_format y1[MAXSAT][MAXTYPE],y0[MAXSAT][MAXTYPE];
+char flag1[MAXSAT][MAXTYPE*2+1],flag[MAXSAT][MAXTYPE*2+1];
+
+int rinex_version,crinex_version,ep_top_from,ep_top_to;
+int nsat,ntype,ntype_gnss[UCHAR_MAX],ntype_record[MAXSAT],clk_order = 0, clk_arc_order = 0;
+long nl_count=0;
+int skip = 0;
+int exit_status = EXIT_SUCCESS;
+
+char out_buff[MAX_BUFF_SIZE],*p_buff;
+
+/* declaration of functions */
+void fileopen(int argc, char *argv[]);
+void header();
+int  put_event_data(char *dline, char *p_event);
+void skip_to_next(char *dline);
+void process_clock(char *dline);
+void set_sat_table(char *p_new, char *p_old, int nsat1, int *sattbl);
+void data(char *p_sat_lst, int *sattbl, char dflag[][MAXTYPE*2]);
+void repair(char *s, char *ds);
+int  getdiff(data_format *y, data_format *y0, int i0, char *dflag);
+void putfield(data_format *y, char *flag);
+void read_clock(char *dline ,long *yu, long *yl);
+void print_clock(long yu, long yl, int shift_clk);
+int  read_chk_line(char *line);
+void error_exit(int error_no, char *string);
+
+/*---------------------------------------------------------------------*/
+main(int argc, char *argv[]){
+    static char line[MAXCLM]="", sat_lst_old[MAXSAT*3];
+    static int nsat1 = 0, n;
+
+    char dline[MAXCLM],*p;
+    int sattbl[MAXSAT],i,j,*i0;
+    size_t offset;
+    char dflag[MAXSAT][MAXTYPE*2];
+    char *p_event,*p_nsat,*p_satlst,*p_clock,shift_clk;
+       /* sattbl[i]: order (at the previous epoch) of i-th satellite */
+       /* (at the current epoch). -1 is set for the new satellites   */
+
+    fileopen(argc,argv);
+    for(i=0;i<UCHAR_MAX;i++)ntype_gnss[i]=-1;  /** -1 unless GNSS type is not defined **/
+    header();
+    if (rinex_version==2){
+        ep_top_from='&';
+        ep_top_to  =' ';
+        p_event  =&dline[28];  /** pointer to event flug **/
+        p_nsat   = &line[29];  /** pointer to n_sat **/
+        p_satlst = &line[32];  /** pointer to address to add satellite list **/
+        p_clock  = &line[68];  /** pointer to clock offset data **/
+        shift_clk = 1;
+        offset=3;
+    }else{
+        ep_top_from='>';
+        ep_top_to=  '>';
+        p_event  =&dline[31];
+        p_nsat   = &line[32];
+        p_satlst = &line[41];
+        p_clock  = &line[41];
+        shift_clk = 4;
+        offset=6;
+    }
+
+    while( fgets(dline,MAXCLM,stdin) != NULL ){      /*** exit program successfully ***/
+        nl_count++;
+        CHOP_LF(dline,p);
+        SKIP:
+        if(crinex_version == 3) { /*** skip escape lines of CRINEX version 3 ***/
+            while(dline[0] == '&'){
+                nl_count++;
+                if( fgets(dline,MAXCLM,stdin) == NULL ) return exit_status;
+                CHOP_LF(dline,p);
+            }
+        }
+        if(dline[0] == ep_top_from){
+            dline[0] = ep_top_to;
+            if(*p_event!='0' && *p_event!='1' ){
+                if(put_event_data(dline,p_event)!=0) skip_to_next(dline);
+                goto SKIP;
+            }
+            line[0] = '\0';          /**** initialize arc for epoch data ***/
+            nsat1 = 0;               /**** initialize the all satellite arcs ****/
+        }else if( dline[0] == '\032' ){
+            return exit_status;   /** DOS EOF **/
+        }
+        /****  read, repair the line  ****/
+        repair(line,dline);
+        p=&line[offset];  /** pointer to the space between year and month **/
+        if(line[0] != ep_top_to || strlen(line)<(26+offset) || *(p+23) != ' '
+                             || *(p+24) != ' ' || ! isdigit(*(p+25)) ) {
+            skip_to_next(dline);
+            goto SKIP;
+        }
+        CHOP_BLANK(line,p);
+
+        nsat=atoi(p_nsat);
+        if(nsat > MAXSAT) error_exit(6,p_nsat);
+
+        set_sat_table(p_satlst,sat_lst_old,nsat1,sattbl); /****  set satellite table  ****/
+        if(read_chk_line(dline) != 0) {skip_to_next(dline);goto SKIP;}
+        read_clock(dline,clk1.u,clk1.l);
+        for(i=0,i0=sattbl ; i<nsat ; i++,i0++){
+            ntype=ntype_record[i];
+            if( getdiff(y1[i],y0[*i0],*i0,dflag[i]) != 0 ) {skip_to_next(dline);goto SKIP;}
+        }
+
+        /*************************************/
+        /**** print the recovered line(s) ****/
+        /*************************************/
+        if(dline[0] != '\0') process_clock(dline);
+        p_buff = out_buff;
+
+        if(rinex_version == 2){
+            if(clk_order >= 0){
+                p_buff += sprintf(p_buff,"%-68.68s",line);
+                print_clock(clk1.u[clk_order],clk1.l[clk_order],shift_clk);
+            }else{
+                p_buff += sprintf(p_buff,"%.68s\n",line);
+            }
+            for(p = &line[68],n=nsat-12; n>0; n-=12,p+=36) p_buff += sprintf(p_buff,"%32.s%.36s\n"," ",p);
+        }else{
+            if(clk_order >= 0){
+                p_buff += sprintf(p_buff,"%.41s",line);
+                print_clock(clk1.u[clk_order],clk1.l[clk_order],shift_clk);
+            }else{
+                sprintf(p_buff,"%.41s",line);
+                CHOP_BLANK(p_buff,p);*p++='\n';p_buff=p;
+            }
+        }
+
+        data(p_satlst,sattbl,dflag);
+
+        *p_buff = '\0'; printf("%s",out_buff);
+        /****************************/
+        /**** save current epoch ****/
+        /****************************/
+        nsat1 = nsat;
+        clk0 = clk1;
+        strncpy(sat_lst_old,p_satlst,nsat*3);
+        for(i=0;i<nsat;i++){
+            strncpy(flag1[i],flag[i],ntype_record[i]*2);
+            for(j=0;j<ntype_record[i];j++) y0[i][j] = y1[i][j];
+        }
+    }
+    return exit_status;
+}
+/*---------------------------------------------------------------------*/
+void fileopen(int argc, char *argv[]){
+    char *p,*infile,outfile[256],*progname;
+    int nfile=0, force=0, help=0;
+    int nfout = 0;  /*** =0 default output file name ***/
+                    /*** =1 standaed output          ***/
+    FILE *ifp;
+
+    progname = argv[0];
+    argc--;argv++;
+    for(;argc>0;argc--,argv++){
+        if((*argv)[0] != '-'){
+            infile = *argv;
+            nfile++;
+        }else if(strcmp(*argv,"-")   == 0){
+            nfout  = 1;
+        }else if(strcmp(*argv,"-f")  == 0){
+            force = 1;
+        }else if(strcmp(*argv,"-s")  == 0){
+            skip  = 1;
+        }else if(strcmp(*argv,"-h")  == 0){
+            help = 1;
+        }else{
+            help = 1;
+        }
+    }
+
+    if(help == 1 || nfile > 1  || nfile < 0) error_exit(2,progname);
+    if(nfile == 0) return;       /*** stdin & stdout will be used ***/
+
+    /***********************/
+    /*** open input file ***/
+    /***********************/
+    p=strrchr(infile,'.');
+    if(p == NULL || (*(p+3) != 'D' && *(p+3) != 'd') || *(p+4) != '\0') error_exit(3,p);
+    if((ifp = fopen(infile,"r")) == NULL) error_exit(4,infile);
+
+    /************************/
+    /*** open output file ***/
+    /************************/
+    if(nfout == 0) {
+        strcpy(outfile,infile);
+        p=strrchr(outfile,'.');
+        if(*(p+3) == 'd') { *(p+3) = 'o';}
+        else              { *(p+3) = 'O';}
+        if((freopen(outfile,"r",stdout)) != NULL && force == 0){
+            fprintf(stderr,"The file %s already exists. Overwrite?(n)",outfile);
+            if(getchar() != 'y') exit(EXIT_SUCCESS);
+        }
+        freopen(outfile,"w",stdout);
+    }
+    fclose(ifp);
+    freopen(infile,"r",stdin);
+}
+/*---------------------------------------------------------------------*/
+void header(){
+    char line[MAXCLM],*p;
+    if( read_chk_line(line) == 1 ) error_exit(5,"1.0-2.0");
+    crinex_version=atoi(line);
+    if( strncmp(&line[0],"1.0",3) != 0 && strncmp(&line[0],"3.0",3) != 0 ||
+        strncmp(&line[60],"CRINEX VERS   / TYPE",19) != 0 ) error_exit(5,"1.0-2.0");
+    if( read_chk_line(line) == 1 ) error_exit(8,line);
+
+    if( read_chk_line(line) == 1 ) error_exit(8,line);
+    CHOP_BLANK(line,p);
+    printf("%s\n",line);
+    if(strncmp(&line[60],"RINEX VERSION / TYPE",20) != 0 ||
+       (line[5]!='2' && line[5]!='3') ) error_exit(15,"2.x or 3.x");
+    rinex_version=atoi(line);
+
+    do {
+        read_chk_line(line);
+        CHOP_BLANK(line,p);
+        printf("%s\n",line);
+        if       (strncmp(&line[60],"# / TYPES OF OBSERV",19) == 0 && line[5] != ' '){
+             ntype=atoi(line);                                       /** for RINEX2 **/
+        } else if(strncmp(&line[60],"SYS / # / OBS TYPES",19) == 0){ /** for RINEX3  **/
+             if (line[0] != ' ') ntype_gnss[line[0]]=atoi(&line[3]);
+             if (ntype_gnss[line[0]] > MAXTYPE) error_exit(15,line);
+        }
+    }while(strncmp(&line[60],"END OF HEADER",13) != 0);
+}
+/*---------------------------------------------------------------------*/
+void read_clock(char *dline, long *yu, long *yl){
+    char *p,*s,*p1;
+
+    p = dline;
+
+    if(*p == '\0'){
+        clk_order = -1;
+    }else{
+        if(*(p+1) == '&') {        /**** for the case of arc initialization ****/
+            sscanf(p,"%d&",&clk_arc_order);
+            if(clk_arc_order > MAX_DIFF_ORDER) error_exit(7,dline);
+            clk_order = -1;
+            p += 2;
+        }
+        p1=p; if(*p == '-') p1++;
+        s=strchr(p1,'\0');
+        if((s-p1) < 9 ){                /** s-p1 == strlen(p1) ***/
+            *yu = 0;
+            *yl = atol(p);
+        }else{
+            s -= 8;
+            *yl = atol(s);
+            *s = '\0';
+            *yu = atol(p);
+            if(*yu < 0) *yl = -*yl;
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+void process_clock(char *dline){
+    int i,j;
+    /****************************************/
+    /**** recover the clock offset value ****/
+    /****************************************/
+    if(clk_order < clk_arc_order){
+        clk_order++;
+        for(i=0,j=1 ; i<clk_order ; i++,j++){
+            clk1.u[j] = clk1.u[i]+clk0.u[i];
+            clk1.l[j] = clk1.l[i]+clk0.l[i];
+            clk1.u[j] += clk1.l[j]/100000000;  /*** to avoid overflow of y1.l ***/
+            clk1.l[j] %= 100000000;
+        }
+    }else{
+        for(i=0,j=1 ; i<clk_order ; i++,j++){
+            clk1.u[j] = clk1.u[i]+clk0.u[j];
+            clk1.l[j] = clk1.l[i]+clk0.l[j];
+            clk1.u[j] += clk1.l[j]/100000000;
+            clk1.l[j] %= 100000000;
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+int  put_event_data(char *dline, char *p_event){
+/***********************************************************************/
+/*  - Put event data for one event.                                    */
+/*  - This function is called when the event flag > 1.                 */
+/***********************************************************************/
+    int i,n;
+    char *p;
+    do {
+        dline[0] = ep_top_to;
+        CHOP_BLANK(dline,p);
+        printf("%s\n",dline);
+        if( strlen(dline) > 29 ){
+            n=atoi((p_event+1));
+            for(i=0;i<n;i++){
+                read_chk_line(dline);
+                CHOP_BLANK(dline,p);
+                printf("%s\n",dline);
+                if       (strncmp(&dline[60],"# / TYPES OF OBSERV",19) == 0 && dline[5] != ' ' ){
+                     ntype=atoi(dline);                                       /** for RINEX2 **/
+                } else if(strncmp(&dline[60],"SYS / # / OBS TYPES",19) == 0){ /** for RINEX3 **/
+                     if (dline[0] != ' ') ntype_gnss[dline[0]]=atoi(&dline[3]);
+                     if (ntype_gnss[dline[0]] > MAXTYPE) error_exit(15,dline);
+                }
+            }
+        }
+
+        do {
+            nl_count++;
+            if(fgets(dline,MAXCLM,stdin) == NULL) exit(exit_status);  /*** eof: exit program successfully ***/
+        } while (crinex_version == 3 && dline[0] == '&');
+        CHOP_LF(dline,p);
+
+        if(dline[0] != ep_top_from || strlen(dline)<29   || ! isdigit(*p_event) ) {
+            if( ! skip ) error_exit(9,dline);
+            fprintf(stderr,"WARNING :  The epoch should be initialized, but not.\n");
+            return 1;
+        }
+    }while(*p_event != '0' && *p_event != '1');
+    return 0;
+}
+/*---------------------------------------------------------------------*/
+void skip_to_next(char *dline){
+    char *p;
+    exit_status=EXIT_WARNING;
+    fprintf(stderr,"    line %ld : skip until an initialized epoch is found.",nl_count);
+    if(rinex_version == 2) {
+        p=dline+3;    /** pointer to the space between year and month **/
+    }else{
+        p=dline+6;
+    }
+
+    do {
+        nl_count++;
+        if(fgets(dline,MAXCLM,stdin) == NULL) {
+            fprintf(stderr,"  .....next epoch not found before EOF.\n");
+            printf("%29d%3d\n%-60sCOMMENT\n",4,1,"  *** Some epochs are skiped by CRX2RNX ***");
+            exit(exit_status);
+        }
+    }while(dline[0]!=ep_top_from || strlen(dline)<29   || *p != ' ' 
+              || *(p+3)  != ' ' || *(p+6)  != ' ' || *(p+9) != ' ' 
+              || *(p+12) != ' ' || *(p+23) != ' ' || *(p+24) != ' ' 
+              || ! isdigit(*(p+25)) );
+
+    CHOP_LF(dline,p);
+    fprintf(stderr,"  .....next epoch found at line %ld.\n",nl_count);
+    printf("%29d%3d\n%-60sCOMMENT\n",4,1,"  *** Some epochs are skiped by CRX2RNX ***");
+
+}
+/*---------------------------------------------------------------------*/
+void set_sat_table(char *p_new, char *p_old, int nsat1, int *sattbl){
+/***********************************************************************/
+/*  - Read number of satellites (nsat)                                 */
+/*  - Compare the satellite list at the epoch (*p_new) and that at the */
+/*    previous epoch(*p_old), and make index (*sattbl) for the         */
+/*    corresponding order of the satellites.                           */
+/*    *sattbl is set to -1 for new satellites.                         */
+/***********************************************************************/
+    int i,j;
+    char *ps;
+
+    /*** set # of data types for each satellite ***/
+    if(rinex_version == 2 ) {             /** for RINEX2 **/
+        for (i=0 ; i<nsat ; i++){ ntype_record[i]=ntype; }
+    }else{                                /** for RINEX3 **/
+        for (i=0,ps=p_new ; i<nsat ; i++,ps+=3){
+            ntype_record[i]=ntype_gnss[*ps];  /*** # of data type for the GNSS system ***/
+            if(ntype_record[i]<0)error_exit(20,p_new);
+        }
+    }
+
+    for (i=0,p_new ; i<nsat ; i++,p_new+=3){
+        *sattbl = -1;
+        for(j=0,ps=p_old ; j<nsat1 ; j++,ps+=3){
+            if(strncmp(p_new,ps,3) == 0){
+                *sattbl = j;
+                break;
+            }
+        }
+        sattbl++;
+    }
+}
+/*---------------------------------------------------------------------*/
+void data(char *p_sat_lst, int *sattbl, char dflag[][MAXTYPE*2]){
+/********************************************************************/
+/*  Functions                                                       */
+/*      (1) compose the original data from 3rd order difference     */
+/*      (2) repair the flags                                        */
+/*  sattbl : previous column on which the satellites are set        */
+/*           new satellites are set to -1                           */
+/*       u : upper X digits of the data                             */
+/*       l : lower 5 digits of the data                             */
+/*            ( y = u*100 + l/1000)                                 */
+/*   date of previous epoch are set to y0                           */
+/********************************************************************/
+    data_format *py1,*py0;
+    int  i,j,k,k1,*i0;
+    char *p;
+
+    for(i=0,i0=sattbl,p=p_sat_lst ; i<nsat ; i++,i0++,p+=3){
+        /**** set # of data types for the GNSS type    ****/
+        /**** and write satellite ID in case of RINEX3 ****/
+        /**** ---------------------------------------- ****/
+        if(rinex_version == 3 ){
+            ntype=ntype_record[i];
+            strncpy(p_buff,p,3);
+            p_buff += 3;
+        }
+        /**** repair the data frags ****/
+        /**** ----------------------****/
+        if(*i0 < 0){       /* new satellite */
+            if(rinex_version == 3 ){
+                *flag[i]='\0';
+            }else{
+                sprintf(flag[i],"%-*s",ntype*2,dflag[i]);
+            }
+        }else{
+            strncpy(flag[i],flag1[*i0],ntype*2);
+        }
+        repair(flag[i],dflag[i]);
+
+        /**** recover the date, and output ****/
+        /**** ---------------------------- ****/
+        for(j=0,py1=y1[i] ; j<ntype ; j++,py1++){
+            if(py1->arc_order >= 0){
+                py0 = &(y0[*i0][j]);
+                if(py1->order < py1->arc_order){
+                    (py1->order)++;
+                    for(k=0,k1=1; k<py1->order; k++,k1++){
+                        py1->u[k1] = py1->u[k] + py0->u[k];
+                        py1->l[k1] = py1->l[k] + py0->l[k];
+                        py1->u[k1] += py1->l[k1]/100000;  /*** to avoid overflow of y1.l ***/
+                        py1->l[k1] %= 100000;
+                    }
+                }else{
+                    for(k=0,k1=1; k<py1->order; k++,k1++){
+                        py1->u[k1] = py1->u[k] + py0->u[k1];
+                        py1->l[k1] = py1->l[k] + py0->l[k1];
+                        py1->u[k1] += py1->l[k1]/100000;
+                        py1->l[k1] %= 100000;
+                    }
+                }
+                putfield(py1,&flag[i][j*2]);
+            }else{
+                if (crinex_version == 1 ) {                       /*** CRINEX 1 assumes that flags are always ***/
+                    p_buff += sprintf(p_buff,"                "); /*** blank if data field is blank           ***/
+                    flag[i][j*2] = flag[i][j*2+1] = ' ';
+                }else{                                            /*** CRINEX 3 evaluate flags independently **/
+                    p_buff += sprintf(p_buff,"              %c%c",flag[i][j*2],flag[i][j*2+1]);
+                }
+            }
+            if((j+1) == ntype || (rinex_version==2 && (j+1)%5 == 0 ) ){
+                while(*--p_buff == ' '); p_buff++;  /*** cut spaces ***/
+                *p_buff++ = '\n';
+            }
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+void repair(char *s, char *ds){
+    for(; *s != '\0' && *ds != '\0' ; ds++,s++){
+        if(*ds == ' ')continue;
+        if(*ds == '&')
+            *s = ' ';
+        else
+            *s = *ds;
+    }
+    if(*ds != '\0') {
+        sprintf(s,"%s",ds);
+        for(; *s != '\0' ;s++) {
+            if(*s == '&') *s = ' ';
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+int  getdiff(data_format *y, data_format *y0, int i0, char *dflag){
+    int j,length;
+    char *s,*s1,*s2,line[MAXCLM];
+
+    /******************************************/
+    /****  separate the fields with '\0'   ****/
+    /******************************************/
+    if(read_chk_line(line)!=0) return 1;
+    for(j=0,s=line; j<ntype; s++){
+        if(*s == '\0') {
+            j++;
+            *(s+1) = '\0';
+        }else if(*s == ' '){
+            j++;
+            *s = '\0';
+        }
+    }
+    strcpy(dflag,s);
+
+    /************************************/
+    /*     read the differenced data    */
+    /************************************/
+    s1=line;
+    for(j=0;j<ntype;j++,y++,y0++){
+        if(*s1 == '\0'){
+            y->arc_order = -1;      /**** arc_order < 0 means that the feild is blank ****/
+            y->order = -1;
+            s1++;
+        }else{
+            if(*(s1+1) == '&'){     /**** arc initialization ****/
+                y->order = -1;
+                y->arc_order = atoi(s1);
+                s1 += 2;
+                if(y->arc_order > MAX_DIFF_ORDER) error_exit(7,line);
+            }else if(i0 < 0){
+                if( ! skip ) error_exit(11,line);
+                fprintf(stderr,"WARNING : New satellite, but data arc is not initialized.\n");
+                return 1;
+            }else if(y0->arc_order < 0){
+                if( ! skip ) error_exit(12,line);
+                fprintf(stderr,"WARNING : New data sequence but without initialization.\n");
+                return 1;
+            }else{
+                y->order = y0->order;
+                y->arc_order = y0->arc_order;
+            }
+            length=(s2=strchr(s1,'\0'))-s1;
+            if(*s1 == '-') length--;
+            if(length < 6){ 
+                y->u[0] = 0;
+                y->l[0] = atol(s1);
+            }else{
+                s = s2-5;
+                y->l[0] = atol(s); *s = '\0';
+                y->u[0] = atol(s1);
+                if(y->u[0] < 0) y->l[0] = -y->l[0];
+            }
+            s1 = s2+1;
+        }
+    }
+    return 0;
+}
+/*---------------------------------------------------------------------*/
+void putfield(data_format *y, char *flag){
+    int  i;
+
+    i=y->order;
+
+    if(y->u[i]<0 && y->l[i]>0){
+        y->u[i]++ ; y->l[i] -= 100000 ;
+    }else if(y->u[i]>0 && y->l[i]<0){
+        y->u[i]-- ; y->l[i] += 100000 ;
+    }
+
+    if(y->u[i]!=0){                                    /* ex) 123.456  -123.456 */
+       p_buff += sprintf(p_buff,"%8ld %05.5ld%c%c",y->u[i],labs(y->l[i]),*flag,*(flag+1));
+       p_buff[-8] = p_buff[-7];
+       p_buff[-7] = p_buff[-6];
+    }else{
+       p_buff += sprintf(p_buff,"         %05.5ld%c%c",labs(y->l[i]),*flag,*(flag+1));
+       if (p_buff[-7] != '0' ){                        /* ex)  12.345    -2.345 */
+           p_buff[-8] = p_buff[-7];
+           p_buff[-7] = p_buff[-6];
+           if(y->l[i] <0) p_buff[-9]='-';
+       }else if (p_buff[-6] != '0' ){                  /* ex)   1.234    -1.234 */
+           p_buff[-7] = p_buff[-6];
+           p_buff[-8] = (y->l[i] <0)? '-':' ';
+       }else{                                          /* ex)    .123     -.123 */
+           p_buff[-7] = (y->l[i] <0)? '-':' ';
+       }
+    }
+    p_buff[-6] = '.';
+}
+/*---------------------------------------------------------------------*/
+void print_clock(long yu, long yl, int shift_clk){
+    char tmp[8],*p_tmp,*p;
+    int n,sgn;
+
+    if(yu<0 && yl>0){
+        yu++ ; yl -= 100000000;
+    }else if(yu>0 && yl<0){
+        yu-- ; yl += 100000000;
+    }
+
+    /** add ond more digit to handle '-0'(RINEX2) or '-0000'(RINEX3) **/
+    sgn = (yl<0) ? -1:1;
+    n=sprintf(tmp,"%0.*ld",shift_clk+1,yu*10+sgn); /** AT LEAST fractional parts are filled with 0 **/
+    n--;                           /** n: number of digits excluding the additional digit **/
+    p_tmp=&tmp[n];
+    *p_tmp='\0';
+    p_tmp-=shift_clk;       /** pointer to the top of last "shift_clk" digits **/
+    p_buff += sprintf(p_buff,"  .%s",p_tmp);  /** print last "shift_clk" digits.  **/
+    if( n > shift_clk ){
+        p_tmp--;
+        p=p_buff-shift_clk-2;
+        *p=*p_tmp;
+
+        if( n > shift_clk+1 ){ *(p-1)=*(p_tmp-1); }
+    }
+
+    p_buff += sprintf(p_buff,"%08.8ld\n",labs(yl));
+}
+/*---------------------------------------------------------------------*/
+int  read_chk_line(char *line){
+    char *p;
+    nl_count++;
+    if( fgets(line,MAXCLM,stdin) == NULL ) error_exit(8,line);
+    if( (p = strchr(line,'\n')) == NULL) {
+        if( fgetc(stdin) == EOF ) {     /** check if EOF is there **/
+            error_exit(8,line);
+        }else{
+            if( ! skip ) error_exit(13,line);
+            return 1;
+        }
+    }
+    if( *(p-1) == '\n' )p--;
+    if( *(p-1) == '\r' )p--;   /*** check DOS CR/LF ***/
+    *p = '\0';
+    return 0;
+}
+/*---------------------------------------------------------------------*/
+void error_exit(int error_no, char *string){
+    if(error_no == 1 ){
+        fprintf(stderr,"Usage: %s input file [-o output file] [-f] [-s] [-h]\n",string);
+        fprintf(stderr,"    output file name can be omitted if input file name is *.[yy]d\n");
+    }else if(error_no == 2 ){
+        fprintf(stderr,"Usage: %s [file] [-] [-f] [-s] [-h]\n",string);
+        fprintf(stderr,"    stdin and stdout are used if input file name is not given.\n");
+    }
+    if(error_no == 1 || error_no == 2){
+        fprintf(stderr,"    -  : output to stdout\n");
+        fprintf(stderr,"    -f : force overwrite of output file\n");
+        fprintf(stderr,"    -s : skip strange epochs (default:stop with error)\n");
+        fprintf(stderr,"           This option may be used for salvaging usable data when middle of\n");
+        fprintf(stderr,"           the Compact RINEX file is missing. The data after the missing part,\n");
+        fprintf(stderr,"           are, however, useless until the compression operation of all data\n");
+        fprintf(stderr,"           are initialized at some epoch. Combination with use of -e option\n");
+        fprintf(stderr,"           of RNX2CRX (ver.4.0 or after) may be effective.\n");
+        fprintf(stderr,"           Caution : It is assumed that no change in # of data types\n");
+        fprintf(stderr,"                     happens in the lost part of the data.\n");
+        fprintf(stderr,"    -h : display help message\n\n");
+        fprintf(stderr,"    exit code = %d (success)\n",EXIT_SUCCESS);
+        fprintf(stderr,"              = %d (error)\n",  EXIT_FAILURE);
+        fprintf(stderr,"              = %d (warning)\n",EXIT_WARNING);
+        fprintf(stderr,"    [version : %s]\n",VERSION);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 3 ){
+        fprintf(stderr,"ERROR : invalid file name  %s\n",string);
+        fprintf(stderr,"The extention of the input file name should be [.xxd].\n");
+        fprintf(stderr,"If the file name doesn't obey this naming convention, use this program as a filter. \n");
+        fprintf(stderr,"    for example)  cat file.in | %s - > file.out\n",PROGNAME);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 4 ){
+        fprintf(stderr,"ERROR : can't open %s\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 5 ){
+        fprintf(stderr,"ERROR : The file format is not Compact RINEX or the version of\n");
+        fprintf(stderr,"        the format is not valid. This software can deal with\n");
+        fprintf(stderr,"        only Compact RINEX format ver.%s.\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 6 ){
+        fprintf(stderr,"ERROR at line %ld : exceed maximum number of satellites(%d)\n",nl_count,MAXSAT);
+        fprintf(stderr,"      start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 7 ){
+        fprintf(stderr,"ERROR at line %ld : exceed maximum order of difference (%d)\n",nl_count,MAX_DIFF_ORDER);
+        fprintf(stderr,"      start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 8 ){
+        fprintf(stderr,"ERROR : The file seems to be trancated in the middle.\n");
+        fprintf(stderr,"        The conversion is interrupted after reading the line %ld :\n",nl_count);
+        fprintf(stderr,"      start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 9 ){
+        fprintf(stderr,"ERROR at line %ld : The arc should be initialized, but not.\n",nl_count);
+        fprintf(stderr,"      start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 11){
+        fprintf(stderr,"ERROR at line %ld : New satellite, but data arc is not initialized.\n",nl_count);
+        fprintf(stderr,"      start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 12){
+        fprintf(stderr,"ERROR at line %ld : The data feild in previous epoch is blank, but the arc is not initialized.\n",nl_count);
+        fprintf(stderr,"      start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 13){
+        fprintf(stderr,"ERROR at line %ld : null character is found in the line or the line is too long (>%d) at line.\n",nl_count,MAXCLM);
+        fprintf(stderr,"      start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 15 ){
+        fprintf(stderr,"ERROR : The format version of the original RINEX file is not valid.\n");
+        fprintf(stderr,"         This software can deal with only (compressed) RINEX format ver.%s.\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 20 ){
+        fprintf(stderr,"ERROR at line %ld. : A GNSS type not defined in the header is found.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+
+}
diff --git a/rnx2crx.c b/rnx2crx.c
new file mode 100644 (file)
index 0000000..b528d06
--- /dev/null
+++ b/rnx2crx.c
@@ -0,0 +1,813 @@
+/***********************************************************************/
+/*     program name : RNX2CRX                                          */
+/*                                                                     */
+/*     RINEX file compression program for UNIX/MS-DOS system.          */
+/*     convert the RINEX format to Compact RINEX format                */
+/*     Created by Yuki HATANAKA / Geographical Survey Institute, Japan */
+/*                                                                     */
+/*     ver.                                                            */
+/*     4.0.0       2007.02.05 test version   Y. Hatanaka               */
+/*                  - CRINEX 1/3 for RINEX 2.x/3.x                     */
+/*     4.0.1       2007.05.08                                          */
+/*                  - elimination of supports for VMS and SUN OS 4.1.x */
+/*                  - output not to the current directory but the same */
+/*                    directory as the input file.                     */
+/*                  - the same code for DOS and UNIX                   */
+/*     4.0.2       2007.06.07                                          */
+/*                  - fixing incompatibility of argument and format    */
+/*                    string of printf.                                */
+/*     4.0.3       2007.06.21                                          */
+/*                  - nothing was changed from 4.0.2 except for the    */
+/*                    version string to be the same as crx2rnx.c       */
+/*                                                                     */
+/*     Copyright (c) 2007 Geographical Survey Institute                */
+/*     All rights reserved.                                            */
+/*                                                                     */
+/***********************************************************************/
+
+#define VERSION  "ver.4.0.3"
+
+/**** Exit codes are defined here.   ****/
+#define EXIT_WARNING 2
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+/****  Don't change the lines from here. ****/
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+/*** define macro to  ***/
+#define FLUSH_BUFF printf("%s",top_buff), *(p_buff = top_buff)='\0'
+#define CLEAR_BUFF *(p_buff = top_buff)='\0'
+
+#define CRX_VERSION1 "1.0"    /* CRINEX version for RINEX 2.x */
+#define CRX_VERSION2 "3.0"    /* CRINEX version for RINEX 3.x */
+#define PROGNAME "RNX2CRX"
+#define MAXSAT     90         /* Maximum number of satellites observed at one epoch */
+#define MAXTYPE    50         /* Maximum number of data types for a GNSS system */
+#define MAXCLM   1024         /* Maximum columns in one line   (>MAXTYPE*19+3)  */
+#define MAX_BUFF_SIZE 131072  /* Muximum size of output buffer (>MAXSAT*(MAXTYPE*19+4)+60 */
+#define ARC_ORDER 3           /* order of difference to take    */
+
+/* define data structure for fields of clock offset and obsercvation records */
+/* Those data will be handled as integers after eliminating decimal points.  */
+/* Since their size may exceeds range between LONG_MIN and LONG_MAX,         */
+/* they will be read with being divided properly into upper and lower digits */
+typedef struct clock_format{
+    long u[ARC_ORDER+1];      /* upper X digits */
+    long l[ARC_ORDER+1];      /* lower 8 digits (can be 9-10 digits for deltas)*/
+} clock_format;
+
+typedef struct data_format{
+    long u[ARC_ORDER+1];      /* upper X digits */
+    long l[ARC_ORDER+1];      /* lower 5 digits (can be 6-7 digits for deltas) */
+    int order;
+} data_format;
+
+/* define global variables */
+long ep_count=0;
+long ep_reset=0;
+long nl_count=0;
+int rinex_version;          /* =2 or 3 */
+int nsat,ntype,ntype_gnss[UCHAR_MAX],ntype_record[MAXSAT],clk_order = -1;
+int exit_status = EXIT_SUCCESS;
+int skip_strange_epoch = 0; /* default : stop with error */
+
+clock_format clk1,clk0 = {{0,0,0,0}, {0,0,0,0}};
+data_format y0[MAXSAT][MAXTYPE], y1[MAXSAT][MAXTYPE];
+char flag0[MAXSAT][MAXTYPE*2], flag[MAXSAT][MAXTYPE*2];
+char out_buff[MAX_BUFF_SIZE]= {'x','\0'};  /**** a character is put as a stopper to aviod memory overflow ****/
+char *top_buff=&out_buff[1],*p_buff;       /**** therefore, actual baffer start from the second character ****/
+
+char oldline[MAXCLM] = {'&','\0'};
+int nsat_old = 0;
+
+/* declaration of functions */
+void parse_args(int argc, char *argv[]);
+void header();
+int  get_next_epoch(char *p_line);
+void skip_to_next(char *p_line);
+void initialize_all(char *oldline,int *nsat_old, int count);
+void put_event_data(char *p_line);
+void read_clock(char *line,int shift_cl);
+void process_clock();
+int  set_sat_table(char *p_new, char *p_old, int nsat_old,int *sattbl);
+int  read_more_sat(int n, char *p);
+void data(int *sattbl);
+char *strdiff(char *s1, char *s2, char *ds);
+int  rnx_getline(data_format *py1, char *flag, char *sat_id, int *ntype_rec);
+void read_value(char *p, long *pu, long *pl);
+void take_diff(data_format *py1, data_format *py0);
+void putdiff(long dddu, long dddl);
+void put_clock(long du, long dl, int clk_order);
+int  read_chk_line(char *line);
+void error_exit(int error_no, char *string);
+void flush_buff();
+
+/*---------------------------------------------------------------------*/
+main(int argc, char *argv[]){
+    char newline[MAXCLM],dummy[MAXCLM];
+    char *p,*p_event,*p_nsat,*p_satlst,*p_satold,*p_clock;
+    int sattbl[MAXSAT],i,j,shift_clk;
+       /* sattbl[i]: order (at the previous epoch) of i-th satellite */
+       /* (at the current epoch). -1 is set for the new satellites   */
+
+    parse_args(argc,argv);
+    for(i=0;i<UCHAR_MAX;i++)ntype_gnss[i]=-1;  /** -1 unless GNSS type is not defined **/
+    header();
+    if (rinex_version==2){
+        p_event  =&newline[28];  /** pointer to event flug **/
+        p_nsat   =&newline[29];  /** pointer to n_sat **/
+        p_satlst =&newline[32];  /** pointer to satellite list **/
+        p_satold =&oldline[32];  /** pointer to n_sat **/
+        p_clock  =&newline[68];  /** pointer to clock offset data **/
+        shift_clk = 1;
+    }else{
+        p_event  =&newline[31];
+        p_nsat   =&newline[32];
+        p_satlst =&newline[41];
+        p_satold =&oldline[41];
+        p_clock  =&newline[41];
+        shift_clk = 4;
+    }
+
+    for(CLEAR_BUFF;;FLUSH_BUFF){
+        SKIP:
+        if( ! get_next_epoch(newline) ) return exit_status;
+
+        /*** if event flag > 1, then (1)output event data  */
+        /*** (2)initialize all data arcs, and continue to next epoch ***/
+        if( atoi(strncpy(dummy,p_event,1)) > 1) {
+            put_event_data(newline);
+            initialize_all(oldline,&nsat_old,0);
+            continue;
+        }
+
+        if(strchr(newline,'\0') > p_clock){
+            read_clock(p_clock,shift_clk);        /**** read clock offset ****/
+        }else{
+            clk_order = -1;                       /*** reset data arc for clock offset ***/
+        }
+
+        nsat=atoi(p_nsat);
+        if(nsat > MAXSAT) error_exit(8,newline);
+        if(nsat > 12 && rinex_version == 2) read_more_sat(nsat,p_satlst);  /*** read continuation lines ***/
+        if( ep_reset > 0 && ++ep_count > ep_reset ) initialize_all(oldline,&nsat_old,1);
+
+        /**** get observation ****/
+        for(i=0,p=p_satlst ; i<nsat ; i++,p+=3) {
+            if( rnx_getline(y1[i],flag[i],p,&ntype_record[i]) ) {
+                CLEAR_BUFF;
+                exit_status = EXIT_WARNING;
+                goto SKIP;
+            }
+        }
+        *p='\0';    /*** terminate satellite list ***/
+
+        if(set_sat_table(p_satlst,p_satold,nsat_old,sattbl) ){
+            CLEAR_BUFF;
+            exit_status = EXIT_WARNING;
+            continue;
+        }
+
+        /***********************************************************/
+        /**** print change of the line & clock offset differene ****/
+        /**** and data difference                               ****/
+        /***********************************************************/
+        p_buff=strdiff(oldline,newline,p_buff);
+        if(clk_order > -1) {
+            if(clk_order > 0) process_clock();            /**** process clock offset ****/
+            put_clock(clk1.u[clk_order],clk1.l[clk_order],clk_order);
+        }else{
+            *p_buff++ = '\n';
+        }
+        data(sattbl); *p_buff = '\0';
+        /**************************************/
+        /**** save current epoch to buffer ****/
+        /**************************************/
+        nsat_old = nsat;
+        sprintf(oldline,"%s",newline);
+        clk0 = clk1;
+        for(i=0;i<nsat;i++){
+            strcpy(flag0[i],flag[i]);
+            for(j=0;j<ntype_record[i];j++) y0[i][j] = y1[i][j];
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+void parse_args(int argc, char *argv[]){
+    char *p,*infile,outfile[MAXCLM],*progname;
+    int nfile=0, force=0, help=0;
+    int nfout = 0;  /*** =0 default output file name ***/
+                    /*** =1 standaed output          ***/
+    FILE *ifp;
+
+    progname = argv[0];
+    argc--;argv++;
+    for(;argc>0;argc--,argv++){
+        if((*argv)[0] != '-'){
+            infile = *argv;
+            nfile++;
+        }else if(strcmp(*argv,"-")   == 0){
+            nfout  = 1;                /* output to standard output */
+        }else if(strcmp(*argv,"-f")  == 0){
+            force = 1;                 /* overwrite if the output file exists */
+        }else if(strcmp(*argv,"-s")  == 0){
+            skip_strange_epoch = 1;
+        }else if(strcmp(*argv,"-e")  == 0){
+            argc--;argv++;
+            sscanf(*argv,"%ld",&ep_reset);
+        }else if(strcmp(*argv,"-h")  == 0){
+            help = 1;
+        }else{
+            help = 1;
+        }
+    }
+
+    if(help == 1 || nfile > 1 || nfile < 0) error_exit(2,progname);
+    if(nfile == 0) return;       /*** stdin & stdout will be used ***/
+
+    /***********************/
+    /*** open input file ***/
+    /***********************/
+    p=strrchr(infile,'.');
+    if(p == NULL || (*(p+3) != 'O' && *(p+3) != 'o') || *(p+4) != '\0') error_exit(4,p);
+    if((ifp=fopen(infile,"r")) == NULL) error_exit(5,infile);
+
+    /************************/
+    /*** open output file ***/
+    /************************/
+    if(nfout == 0){
+        strcpy(outfile,infile);
+        p=strrchr(outfile,'.');
+        if(*(p+3) == 'o'){ *(p+3) = 'd'; }
+        else             { *(p+3) = 'D'; }
+        if((freopen(outfile,"r",stdout)) != NULL && force == 0){
+            fprintf(stderr,"The file %s already exists. Overwrite?(n)",outfile);
+            if(getchar() != 'y') exit(EXIT_SUCCESS);
+        }
+        freopen(outfile,"w",stdout);
+    }
+    fclose(ifp);
+    freopen(infile,"r",stdin);
+}
+/*---------------------------------------------------------------------*/
+void header(){
+    char line[MAXCLM], line2[41], timestring[20];
+    time_t tc = time(NULL);
+    struct tm *tp;
+
+    if( (tp = gmtime(&tc)) == NULL) tp = localtime(&tc);
+    strftime(timestring, 20, "%d-%b-%y %H:%M", tp);
+
+    /*** Check RINEX VERSION / TYPE ***/
+    read_chk_line(line);
+    if(strncmp(&line[60],"RINEX VERSION / TYPE",20) != 0 ||
+       strncmp(&line[20],"O",1)     != 0 ) error_exit(15,line);
+
+    rinex_version=atoi(line);
+    if      ( rinex_version == 2 ){printf("%-20.20s",CRX_VERSION1);}
+    else if ( rinex_version == 3 ){printf("%-20.20s",CRX_VERSION2);}
+    else                          {error_exit(15,line);}
+    printf("%-40.40s%-20.20s\n","COMPACT RINEX FORMAT","CRINEX VERS   / TYPE");
+
+    sprintf(line2,"%s %s",PROGNAME,VERSION);
+    printf("%-40.40s%-20.20sCRINEX PROG / DATE\n",line2,timestring);
+    printf("%s\n",line);
+    do{
+        read_chk_line(line);
+        printf("%s\n",line);
+        if       (strncmp(&line[60],"# / TYPES OF OBSERV",19) == 0 && line[5] != ' '){
+            ntype=atoi(line);                                        /** for RINEX2 **/
+        } else if(strncmp(&line[60],"SYS / # / OBS TYPES",19) == 0){ /** for RINEX3 **/
+            if (line[0] != ' ') ntype_gnss[line[0]]=atoi(&line[3]);
+            if (ntype_gnss[line[0]] > MAXTYPE) error_exit(16,line);
+        }
+    }while( strncmp(&line[60],"END OF HEADER",13) != 0);
+}
+/*---------------------------------------------------------------------*/
+int  get_next_epoch(char *p_line){
+/**** find next epoch line.                                                          ****/
+/**** If the line seems to be abnormal(2), print warning message                     ****/
+/**** and skip until next epoch is found                                             ****/
+/**** return value  0 : end of the file                                              ****/
+/****               1 : normal end                                                   ****/
+/****               2 : trouble in the line (if 19th character isn't '.' , then      ****/
+/****                                then clear output buffer for previous epoch)    ****/
+    char *p;
+
+    nl_count++;
+    if( fgets(p_line,MAXCLM,stdin) == NULL ) return 0;  /*** EOF: exit program successfully ***/
+
+    if( (p = strchr(p_line,'\n')) == NULL) {
+        if( *p_line == '\032' ) return 0;              /** DOS EOF **/
+        if( *p_line != '\0' || feof(stdin) == 0 ) {
+             if( ! skip_strange_epoch ) error_exit(12,p_line);
+             skip_to_next(p_line);
+             return 2;
+        }
+        fprintf(stderr,"WARNING: null characters are detected at the end of file --> neglected.\n");
+        exit_status = EXIT_WARNING;
+        return 0;
+    }
+    if( *(p-1) == '\r' )p--;                      /*** check DOS CR/LF ***/
+
+    if (rinex_version == 2) {
+        while(*--p == ' ' && p>p_line);*++p = '\0';   /*** chop blank ***/
+        if( strlen(p_line)<29   || *p_line!=' '
+                || *(p_line+27) != ' ' || ! isdigit(*(p_line+28))
+                || (*(p_line+29) != ' ' && *(p_line+29) != '\0') ) {
+            /**** ------- something strange is found in the epoch line ****/
+            if( ! skip_strange_epoch ) error_exit(6,p_line);
+            if(*(p_line+18) != '.')  CLEAR_BUFF;
+            skip_to_next(p_line);
+            return 2;
+        }
+    }else{
+        if( *p_line!='>' ){
+            if( ! skip_strange_epoch ) error_exit(6,p_line);
+            CLEAR_BUFF;
+            skip_to_next(p_line);
+            return 2;
+        }
+        while(p<(p_line+41)) *p++=' '; /*** pad blank ***/
+        *p='\0';
+    }
+    return 1;
+}
+/*---------------------------------------------------------------------*/
+void skip_to_next(char *p_line){
+    int nchar;
+    fprintf(stderr," WARNING at line %ld: strange format. skip to next epoch.\n",nl_count);
+    exit_status = EXIT_WARNING;
+
+    if (rinex_version == 2) {
+        do {                               /**** try to find next epoch line ****/
+            read_chk_line(p_line);
+            nchar = strlen(p_line);
+           } while( nchar <29 || *p_line!=' ' || *(p_line+3)  != ' ' 
+                   || *(p_line+6)  != ' ' || *(p_line+9)  != ' ' 
+                   || *(p_line+12) != ' ' || *(p_line+15) != ' '
+                   || *(p_line+26) != ' ' || *(p_line+27) != ' ' 
+                   || ! isdigit(*(p_line+28)) || ! isspace(*(p_line+29))
+                   || (nchar>68 && *(p_line+70)!='.') );
+    }else{    /*** for RINEX3 ***/
+        do {
+            read_chk_line(p_line);
+            nchar = strlen(p_line);
+        } while( *p_line!='>');
+    }
+    initialize_all(oldline,&nsat_old,0);             /**** initialize all data ***/
+}
+/*---------------------------------------------------------------------*/
+void initialize_all(char *oldline,int *nsat_old,int count){
+    strcpy(oldline,"&");        /**** initialize the epoch data arc ****/
+    clk_order = -1;             /**** initialize the clock data arc ****/
+    *nsat_old = 0;              /**** initialize the all satellite arcs ****/
+    ep_count = count;
+}
+/*---------------------------------------------------------------------*/
+void put_event_data(char *p_line){
+/**** This routine is called when enent flag >1 is set.  ****/
+/****      read # of event information lines and output  ****/
+    int i,n;
+    char *p;
+
+    if (rinex_version == 2 ) {
+        if(*(p_line+26) == '.') error_exit(6,p_line);
+        printf("&%s\n",(p_line+1));
+        if( strlen(p_line) > 29 ){
+            n=atoi((p_line+29));     /** n: nnumber of lines to follow **/
+            for(i=0;i<n;i++){
+                read_chk_line(p_line);
+                printf("%s\n",p_line);
+                if(strncmp((p_line+60),"# / TYPES OF OBSERV",19) == 0 && *(p_line+5) != ' ') {
+                    *flag[0]='\0';
+                    ntype=atoi(p_line);
+                    if (ntype > MAXTYPE) error_exit(16,p_line);
+                }
+            }
+        }
+    } else {
+        if( strlen(p_line)<35 ||  *(p_line+29) == '.') error_exit(6,p_line);
+        /* chop blanks that were padded in get_next_epoch */
+        p = strchr(p_line+35,'\0');while(*--p == ' ');*++p = '\0';
+        printf("%s\n",p_line);
+        n=atoi((p_line+32));         /** n: number of lines to follow **/
+        for(i=0;i<n;i++){
+            read_chk_line(p_line);
+            printf("%s\n",p_line);
+            if(strncmp((p_line+60),"SYS / # / OBS TYPES",19) == 0 && *p_line != ' '){
+                *flag[0]='\0';
+                ntype_gnss[*p_line]=atoi((p_line+3));
+                if (ntype_gnss[*p_line] > MAXTYPE) error_exit(16,p_line);
+            }
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+void read_clock(char *p_clock,int shift_clk){
+/****  read the clock offset value ****/
+/**  *p_clock : pointer to begining of clock data **/
+    char *p_dot;      /** pointer for decimal point **/
+    p_dot = p_clock + 2;
+    if(*p_dot != '.')error_exit(7,p_clock);
+
+    strncpy(p_dot,p_dot+1,shift_clk);    /**** shift digits because of too  ****/
+    *(p_dot+shift_clk)= '.';             /**** many digits for fractional part ****/
+    sscanf(p_clock,"%ld.%ld",&clk1.u[0],&clk1.l[0]);
+    if(*p_clock == '-' || *(p_clock+1) == '-') clk1.l[0] = -clk1.l[0];
+    if(clk_order < ARC_ORDER) clk_order++;
+    *p_clock = '\0';
+}
+/*---------------------------------------------------------------------*/
+void process_clock(){
+    int i;
+    for(i=0;i<clk_order;i++){
+        clk1.u[i+1] = clk1.u[i]-clk0.u[i];
+        clk1.l[i+1] = clk1.l[i]-clk0.l[i];
+    }
+}
+/*---------------------------------------------------------------------*/
+int  set_sat_table(char *p_new, char *p_old, int nsat_old,int *sattbl){
+    /**** sattbl : order of the satellites in the previous epoch   ****/
+    /**** if *sattbl is set to  -1, the data arc for the satellite ****/
+    /**** will be initialized                                      ****/
+    int i,j;
+    char *ps;
+
+    for (i=0;i< nsat;i++,p_new+=3){
+        *sattbl = -1;
+        ps = p_old;
+        for(j=0;j<nsat_old;j++,ps+=3){
+            if(strncmp(p_new,ps,3) == 0){
+                *sattbl = j;
+                break;
+            }
+        }
+        /*** check double entry ***/
+        for(j=i+1,ps=p_new+3 ; j<nsat ; j++,ps+=3){
+            if(strncmp(p_new,ps,3) == 0){
+                if( ! skip_strange_epoch ) error_exit(13,p_new);
+                fprintf(stderr,"WARNING:Duplicated sattellite in one epoch at line %ld. ... skip\n",nl_count);
+                return 1;
+            }
+        }
+        sattbl++;
+    }
+    return 0;
+}
+/*---------------------------------------------------------------------*/
+int  read_more_sat(int n, char *p){
+/**** read continuation line of satellite list (for RINEX2) ****/
+    char line[MAXCLM];
+    
+    do {
+        p += 36;
+        if( read_chk_line(line) ) return 1;
+         /**** append satellite table ****/
+        if(line[2] == ' '){
+            sprintf(p,"%s",&line[32]);
+        }else{                        /*** for the files before clarification of format ***/
+            sprintf(p,"%s",&line[0]); /*** by W.Gurtner (IGS mail #1577)                ***/
+        }
+        n -= 12;
+    } while(n>12);
+    return 0;
+}
+/*---------------------------------------------------------------------*/
+void data(int *sattbl){
+/********************************************************************/
+/*  Function : output the 3rd order difference of data              */
+/*       u : upper X digits of the data                             */
+/*       l : lower 5 digits of the data                             */
+/*            ( y = u*100 + l/1000 )                                */
+/*   py->u : upper digits of the 3rd order difference of the data   */
+/*   py->l : lower digits of the 3rd order difference of the data   */
+/********************************************************************/
+    data_format *py1;
+    int  i,j,*i0;
+    char *p;
+
+    for(i=0,i0 = sattbl ; i<nsat ; i++,i0++){
+        for(j=0,py1=y1[i] ; j<ntype_record[i] ; j++,py1++){
+            if( py1->order >= 0 ){       /*** if the numerical data field is non-blank ***/
+                if(*i0 < 0 || y0[*i0][j].order == -1){
+                    /**** initialize the data arc ****/
+                    py1->order =0; p_buff += sprintf(p_buff,"%d&",ARC_ORDER);
+                }else{
+                    take_diff(py1,&(y0[*i0][j]));
+                    if(labs( py1->u[py1->order]) > 100000){
+                        /**** initialization of the arc for large cycle slip  ****/
+                        py1->order =0; p_buff += sprintf(p_buff,"%d&",ARC_ORDER);
+                    }
+                }
+                putdiff(py1->u[py1->order],py1->l[py1->order]);
+            }else if(*i0 >= 0 && rinex_version == 2){
+                /**** CRINEX1 (RINEX2) initialize flags for blank field, not put '&' ****/
+                flag0[*i0][j*2] = flag0[*i0][j*2+1] = ' ';
+            }
+            if(j < ntype_record[i]-1) *p_buff++ = ' ';   /** ' ' :field separator **/
+        }
+        *(p_buff++) = ' ';  /* write field separator */
+        if(*i0 < 0){             /* if new satellite initialize all LLI & SN flags */
+            if(rinex_version == 2){
+                p_buff=strdiff("",    flag[i],p_buff);
+            }else{          /*  replace space with '&' for CRINEX3(RINEX3)  */
+                for(p=flag[i]; *p != '\0' ; p++) *p_buff++ = (*p == ' ')? '&':*p;
+                *p_buff++ = '\n'; *p_buff = '\0';
+            }
+        }else{
+            p_buff=strdiff(flag0[*i0],flag[i],p_buff);
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+char *strdiff(char *s1, char *s2, char *ds){
+/********************************************************************/
+/**   copy only the difference of string s2 from string s1         **/
+/**   '&' is marked when some character changed to a space         **/
+/**   trailing blank is eliminated and '/n' is added               **/
+/********************************************************************/
+    for(; *s1 != '\0'  && *s2 != '\0' ; s2++){
+        if(*s2 == *(s1++))
+            *ds++ = ' ';
+        else if(*s2 == ' ')
+            *ds++ = '&';
+        else
+            *ds++ = *s2;
+    }
+    strcpy(ds,s1);
+    for(;*ds;ds++){ if(*ds != ' ') *ds = '&'; }
+    while(*s2) *ds++ = *s2++;
+
+    for(ds-- ; *ds == ' ' ; ds--);    /*** find pointer of last non-space character ***/
+    *++ds = '\n'; *++ds = '\0';       /*** chop spaces at the end of the line ***/
+    return ds;
+}
+/*---------------------------------------------------------------------*/
+int rnx_getline(data_format *py1, char *flag, char *sat_id, int *ntype_rec){
+/**** read data line for one satellite and       ****/
+/**** set data difference and flags to valiables ****/
+    char line[MAXCLM],*p,*pmax,*p_1st_rec;
+    int i,j,nfield,max_field;
+
+    if( read_chk_line(line) ) return 1;
+    if(rinex_version == 2 ) {             /** for RINEX2 **/
+        max_field=5;                               /** muximum data types in one line **/
+        *ntype_rec=ntype;                          /** # of data types for the satelite **/
+        p_1st_rec=line;                            /** pointer to the start of the first record **/
+    }else{                                /** for RINEX3 **/
+        strncpy(sat_id,line,3);                    /** put satellite ID to the list of satellites **/
+        max_field=*ntype_rec=ntype_gnss[line[0]];  /*** # of data type for the GNSS system ***/
+        if(max_field<0) error_exit(21,line);
+        p_1st_rec=line+3;
+    }
+    for(i=0,p=p_1st_rec;i<*ntype_rec;i+=max_field){                 /* for each line */
+        nfield = (*ntype_rec-i < max_field ? *ntype_rec-i:max_field); /*** expected # of data fields in the line ***/
+        pmax = p_1st_rec + 16*nfield;
+
+        /*** Cut or pad spaces. Detect error if there is any character after *pmax ***/
+        p = strchr(line,'\0');
+        if(p < pmax ) {
+            while(p<pmax) *p++ = ' ';*p='\0';
+        }else{
+           for(*p = ' ' ; p > pmax && *p == ' ' ; p--);
+            if(p > pmax) {
+                if( ! skip_strange_epoch ) error_exit(9,line);
+                fprintf(stderr,"WARNING: mismatch of number of the data types at line %ld. ... skip\n",nl_count);
+                return 1;
+            }
+        }
+
+        /*** parse the line (read value into py1) ***/
+        for(j=0,p=p_1st_rec; j<nfield; j++,p+=16,py1++){
+            if( *(p+10) == '.' ){
+                *flag++ = *(p+14);
+                *flag++ = *(p+15);
+                *(p+14) = '\0';
+                read_value(p,&(py1->u[0]),&(py1->l[0]));
+                py1->order = 0;
+            }else if( strncmp(p,"              ",14) == 0 ){
+                if( rinex_version == 2 && strncmp((p+14),"  ",2) != 0 ) error_exit(20,line);
+                *flag++ = *(p+14);
+                *flag++ = *(p+15);
+                py1->order = -1;
+            }else{
+                if( ! skip_strange_epoch ) error_exit(10,p);
+                fprintf(stderr,"WARNING: abnormal data field at line %ld....skip\n",nl_count);
+                return 1;
+            }
+        }
+        if(i+max_field < *ntype_rec){
+            if( read_chk_line(line) ) return 1;   /* read continuation line */
+        }
+    }
+    *flag = '\0';
+    return 0;
+}
+/*---------------------------------------------------------------------*/
+void read_value(char *p, long *pu, long *pl){
+/**** divide the data into lower 5 digits and upper digits     ****/
+/**** input p :  pointer to one record (14 characters + '\0')  ****/
+/**** output  *pu, *pl: upper and lower digits the data        ****/
+
+    char *p7,*p8,*p9;
+    p7 =p +7;
+    p8 =p7+1;
+    p9 =p8+1;
+
+    *(p9+1)=*p9;              /* shift two digits: ex. 123.456 -> 1223456,  -.345 ->   -345 */
+    *p9=*p8;                  /*                       -12.345 -> -112345, -1.234 -> --1234 */
+    *pl = atol(p9);           /*                         0.123 ->  . 0123, -0.123 -> --0123 */
+    
+    if(*p7 == ' '){
+        *pu = 0;
+    }else if(*p7 == '-'){
+        *pu = 0;
+        *pl = -*pl;
+    }else{
+        *p8='.';
+        *pu = atol(p);
+        if(*pu < 0) *pl = -*pl;
+    }
+}
+/*---------------------------------------------------------------------*/
+void take_diff(data_format *py1, data_format *py0){
+    int k;
+
+    py1->order = py0->order;
+    if(py1->order < ARC_ORDER) (py1->order)++;
+    if(py1->order > 0){
+        for(k=0;k<py1->order;k++){
+            py1->u[k+1] = py1->u[k] - py0->u[k];
+            py1->l[k+1] = py1->l[k] - py0->l[k];
+        }
+    }
+}
+/*---------------------------------------------------------------------*/
+void putdiff(long dddu, long dddl){
+
+    dddu += dddl/100000 ; dddl %= 100000;
+    if(dddu<0 && dddl>0){
+        dddu++ ; dddl -= 100000;
+    }else if(dddu>0 && dddl<0){
+               dddu-- ; dddl += 100000;
+    }
+
+    if(dddu == 0){
+        p_buff += sprintf(p_buff,"%ld",dddl);
+    }else{
+        p_buff += sprintf(p_buff,"%ld%05.5ld",dddu,labs(dddl));
+    }
+}
+/*---------------------------------------------------------------------*/
+void put_clock(long du, long dl, int c_order){
+/***********************************/
+/****  output clock diff. data  ****/
+/***********************************/
+    du += dl/100000000 ; dl %= 100000000;
+    if(du<0 && dl>0){
+        du++ ; dl -= 100000000;
+    }else if(du>0 && dl<0){
+        du-- ; dl += 100000000;
+    }
+    if(c_order == 0) p_buff += sprintf(p_buff,"%d&",ARC_ORDER);
+    if(du == 0){
+        p_buff += sprintf(p_buff,"%ld\n",dl);
+    }else{
+        p_buff += sprintf(p_buff,"%ld%08.8ld\n",du,labs(dl));
+    }
+}
+/*---------------------------------------------------------------------*/
+int  read_chk_line(char *line){
+    char *p;
+/***************************************/
+/* Read and check one line.            */
+/* The end of the line should be '\n'. */
+/***************************************/
+    nl_count++;
+    if( fgets(line,MAXCLM,stdin) == NULL ) error_exit(11,line);
+    if( (p = strchr(line,'\n')) == NULL) {
+        if( fgetc(stdin) == EOF ) {
+            error_exit(11,line);
+        }else{
+            if( ! skip_strange_epoch ) error_exit(12,line);
+            fprintf(stderr,"WARNING: null character is found or the line is too long (>%d) at line %ld.\n",MAXCLM,nl_count);
+            return 1;
+        }
+    }
+    if( *(p-1) == '\r' )p--;   /*** check DOS CR/LF ***/
+    while(*--p == ' ' && p>line); *++p = '\0';   /** Chop blank **/
+    return 0;
+}
+/*---------------------------------------------------------------------*/
+void error_exit(int error_no, char *string){
+    if(error_no == 1 ){
+        fprintf(stderr,"Usage: %s input file [-o output file] [-f] [-e # of epochs] [-s] [-h]\n",string);
+        fprintf(stderr,"    output file name can be omitted if input file name is *.[yy]o\n");
+    }else if(error_no == 2 ){
+        fprintf(stderr,"Usage: %s [file] [-] [-f] [-e # of epochs] [-s] [-h]\n",string);
+        fprintf(stderr,"    stdin and stdout are used if input file name is not given.\n");
+    }
+    if(error_no == 1 || error_no == 2){
+        fprintf(stderr,"    -       : output to stdout\n");
+        fprintf(stderr,"    -f      : force overwrite of output file\n");
+        fprintf(stderr,"    -e #    : initialize the compression operation at every # epochs\n");
+        fprintf(stderr,"              When some part of the Compact RINEX file is lost, the data\n");
+        fprintf(stderr,"              can not be recovered thereafter until all the data arc are\n");
+        fprintf(stderr,"              initialized for differential operation. This option may be used to\n");
+        fprintf(stderr,"              increase chances to recover parts of data by using an option of\n");
+        fprintf(stderr,"              CRX2RNX(ver. 4.0 or after) with cost of increase of file size.\n");
+        fprintf(stderr,"    -s      : warn and skip strange epochs (default: stop with error status)\n");
+        fprintf(stderr,"    -h      : display this message\n\n");
+        fprintf(stderr,"    exit code = %d (success)\n",EXIT_SUCCESS);
+        fprintf(stderr,"              = %d (error)\n",  EXIT_FAILURE);
+        fprintf(stderr,"              = %d (warning)\n",EXIT_WARNING);
+        fprintf(stderr,"    [version : %s]\n",VERSION);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 4 ){
+        fprintf(stderr,"ERROR : invalid file name  %s\n",string);
+        fprintf(stderr,"The extention of the input file name should be [.xxo].\n");
+        fprintf(stderr,"If the file name doesn't obey this naming convention, use this program as a filter. \n");
+        fprintf(stderr,"    for example)  cat file.in | %s - > file.out\n",PROGNAME);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 5 ){
+        fprintf(stderr,"ERROR : can't open %s\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 6 ){
+        fprintf(stderr,"ERROR when reading line %ld.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 7 ){
+        fprintf(stderr,"ERROR at line %ld: invalid format for clock offset.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 8 ){
+        fprintf(stderr,"ERROR at line %ld : exceeed maximum number of satellites(%d).\n",nl_count,MAXSAT);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 9 ){
+        fprintf(stderr,"ERROR at line %ld : mismatch of number of the data types.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 10){
+        fprintf(stderr,"ERROR at line %ld : abnormal data field.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 11){
+        fprintf(stderr,"ERROR : The RINEX file seems to be trancated in the middle.\n");
+        fprintf(stderr,"        The conversion is interrupted after reading line %ld :\n",nl_count);
+        fprintf(stderr,"        start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 12 ){
+        fprintf(stderr,"ERROR at line %ld. : null character is found or the line is too long (>%d).\n",nl_count,MAXCLM);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 13 ){
+        fprintf(stderr,"ERROR at line %ld. : Duplicated sattellite in one epoch.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 15 ){
+        fprintf(stderr,"The first line is :\n%s\n\n",string);
+        fprintf(stderr,"ERROR : The file format is not valid. This program is applicable\n");
+        fprintf(stderr,"        only to RINEX Version 2/3 Observation file.\n");
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 16 ){
+        fprintf(stderr,"ERROR at line %ld. : Number of data types exceed MAXTYPE(%d).\n",nl_count,MAXTYPE);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 20 ){
+        fprintf(stderr,"ERROR at line %ld. : data is blank but there is flag.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+    if(error_no == 21 ){
+        fprintf(stderr,"ERROR at line %ld. : GNSS type is not defined in the header.\n",nl_count);
+        fprintf(stderr,"     start>%s<end\n",string);
+        exit(EXIT_FAILURE);
+    }
+}