diff --git a/src/convrnx.c b/src/convrnx.c
index d017eded6056fbf0c7237409952369914ee9b92e..4cf6816c7ba28e013aca377bfddbe935948f3819 100644
--- a/src/convrnx.c
+++ b/src/convrnx.c
@@ -1004,7 +1004,12 @@ static void convobs(FILE **ofp, rnxopt_t *opt, strfile_t *str, int *staid,
         }
     }
     /* output rinex obs */
-    outrnxobsb(ofp[0],opt,str->obs->data,str->obs->n,0);
+    outrnxobsb(ofp[0],opt,str->obs);
+    /* n[NOUTFILE+1] - count of events converted to rinex */
+    if (str->obs->flag == 5)
+       n[NOUTFILE+1]++;
+    /* set to zero flag for the next iteration (initialization) */
+    str->obs->flag = 0;
     
     if (opt->tstart.time==0) opt->tstart=time;
     opt->tend=time;
@@ -1215,7 +1220,7 @@ static void setapppos(strfile_t *str, rnxopt_t *opt)
 /* show status message -------------------------------------------------------*/
 static int showstat(int sess, gtime_t ts, gtime_t te, int *n)
 {
-    const char type[]="ONGHQLCISE";
+    const char type[]="ONGHQLCISET";
     char msg[1024]="",*p=msg,s[64];
     int i;
     
@@ -1232,9 +1237,10 @@ static int showstat(int sess, gtime_t ts, gtime_t te, int *n)
     }
     p+=sprintf(p,": ");
     
-    for (i=0;i<NOUTFILE+1;i++) {
+    /* +2 to NOUTFILE for counters of errors and events */
+    for (i=0;i<NOUTFILE+2;i++) {
         if (n[i]==0) continue;
-        p+=sprintf(p,"%c=%d%s",type[i],n[i],i<NOUTFILE?" ":"");
+        p+=sprintf(p,"%c=%d%s",type[i],n[i],i<NOUTFILE+1?" ":"");
     }
     return showmsg(msg);
 }
@@ -1248,7 +1254,7 @@ static int convrnx_s(int sess, int format, rnxopt_t *opt, const char *file,
     halfc_t halfc={{{0}}};
     gtime_t ts={0},te={0},tend={0},time={0};
     unsigned char slips[MAXSAT][NFREQ+NEXOBS]={{0}};
-    int i,j,nf,type,n[NOUTFILE+1]={0},staid=-1,abort=0;
+    int i,j,nf,type,n[NOUTFILE+2]={0},staid=-1,abort=0;
     char path[1024],*paths[NOUTFILE],s[NOUTFILE][1024];
     char *epath[MAXEXFILE]={0},*staname=*opt->staid?opt->staid:"0000";
     
diff --git a/src/rcv/swiftnav.c b/src/rcv/swiftnav.c
index 6fe1989fde9a9c60d8abe3e2e8f2801467e88065..07fd6b5560b3890efc1d8d9aae9d115923d1be10 100644
--- a/src/rcv/swiftnav.c
+++ b/src/rcv/swiftnav.c
@@ -30,6 +30,7 @@ static const char rcsid[] = "$Id: Swiftnav SBP,v 1.0 2017/02/01 FT $";
 #define ID_MSGEPHGLO 0x008B      /* Glonass L1/L2 OF nav message */
 #define ID_MSGIONGPS 0x0090      /* GPS ionospheric parameters */
 #define ID_MSG_SBAS_RAW 0x7777   /* SBAS data */
+#define ID_MSG_EXT_EVENT 0x101   /* external event */
 
 #define SEC_DAY 86400.0
 
@@ -294,6 +295,15 @@ static int flushobuf(raw_t *raw) {
 
   trace(3, "flushobuf: n=%d\n", raw->obuf.n);
 
+  /* copy events from data buffer */
+  if (raw->obuf.eventime.time>0) {
+     raw->obs.eventime = raw->obuf.eventime;
+     raw->obs.timevalid = raw->obuf.timevalid;
+     raw->obs.flag = raw->obuf.flag;
+     raw->obs.rcvcount = raw->obuf.rcvcount;
+     raw->obs.tmcount=raw->obuf.tmcount;
+    }
+
   /* copy observation data buffer */
   for (i = 0; i < raw->obuf.n && i < MAXOBS; i++) {
     if (!satsys(raw->obuf.data[i].sat, NULL))
@@ -304,6 +314,11 @@ static int flushobuf(raw_t *raw) {
   }
   raw->obs.n = n;
 
+  /* clear events from data buffer */
+  raw->obuf.eventime = time0;
+  raw->obuf.flag = raw->obuf.timevalid=0;
+  raw->obuf.rcvcount = raw->obuf.tmcount=0;
+
   /* clear observation data buffer */
   for (i = 0; i < MAXOBS; i++) {
     raw->obuf.data[i].time = time0;
@@ -1132,6 +1147,39 @@ static int decode_snav(raw_t *raw) {
   return 3;
 }
 
+/* decode external event -----------------------------------------------*/
+static int decode_event(raw_t *raw) {
+  gtime_t eventime;
+  uint8_t *puiTmp = (raw->buff) + 6;
+  uint16_t wn;
+  uint32_t tow;
+  int32_t ns_residual;
+  uint8_t flags,pin;
+
+  trace(4, "MSG_EXT_EVENT: len=%d\n", raw->len);
+
+  if ((raw->len) < 20) {
+    trace(2, "SBP decode_event length error: len=%d\n", raw->len);
+    return -1;
+  }
+
+/* get message fields */
+  wn = U2(puiTmp + 0);
+  tow = U4(puiTmp + 2);
+  ns_residual = I4(puiTmp + 6);
+  flags = U1(puiTmp + 10);
+  pin = U1(puiTmp + 11);
+
+/* write to event variables */
+  eventime = gpst2time(wn,tow*1E-3+ns_residual*1E-9);
+  raw->obuf.flag = 5; /* event flag */
+  raw->obuf.eventime = eventime;
+  raw->obuf.tmcount++;
+  raw->obuf.timevalid = (flags & 0x02)>>1;
+
+  return 0;
+}
+
 /* decode SBF raw message --------------------------------------------------*/
 static int decode_sbp(raw_t *raw) {
   uint16_t crc, uCalcCrc;
@@ -1182,6 +1230,8 @@ static int decode_sbp(raw_t *raw) {
     return decode_gpsion(raw);
   case ID_MSG_SBAS_RAW:
     return decode_snav(raw);
+  case ID_MSG_EXT_EVENT:
+    return decode_event(raw);
 
   default:
     trace(4, "decode_sbp: unused frame type=%04x len=%d\n", type, raw->len);
diff --git a/src/rcv/ublox.c b/src/rcv/ublox.c
index e1dd2d4bc7239ed6d1879fa6ce22dbf2a0394997..c1b329caa4d0b1d6859ffde04c7132b09d78c990 100644
--- a/src/rcv/ublox.c
+++ b/src/rcv/ublox.c
@@ -71,6 +71,7 @@
 #define ID_TRKD5    0x030A      /* ubx message id: trace mesurement data */
 #define ID_TRKMEAS  0x0310      /* ubx message id: trace mesurement data */
 #define ID_TRKSFRBX 0x030F      /* ubx message id: trace subframe buffer */
+#define ID_TIMTM2   0x0D03      /* ubx message id: time mark data */
 
 #define FU1         1           /* ubx message field types */
 #define FU2         2
@@ -1071,6 +1072,52 @@ static int decode_trksfrbx(raw_t *raw)
     }
     return 0;
 }
+/* decode ubx-tim-tm2: time mark data ----------------------------------------*/
+static int decode_timtm2(raw_t *raw)
+{
+    gtime_t eventime;
+    char ch, flags;
+    unsigned int count, wnR, wnF;
+    unsigned long towMsR, towSubMsR, towMsF, towSubMsF, accEst;
+    int time, timeBase, newRisingEdge, newFallingEdge;
+    unsigned char *p=raw->buff+6;
+
+    trace(4, "decode_timtm2: len=%d\n", raw->len);
+
+    if (raw->outtype) {
+        sprintf(raw->msgtype, "UBX TIM-TM2 (%4d)", raw->len);
+    }
+    ch = U1(p);
+    flags = *(p+1);
+    count = U2(p+2);
+    wnR = U2(p+4);
+    wnF = U2(p+6);
+    towMsR = U4(p+8);
+    towSubMsR = U4(p+12);
+    towMsF = U4(p+16);
+    towSubMsF = U4(p+20);
+    accEst = U4(p+24);
+
+    /* extract flags to variables */
+    newFallingEdge = ((flags >> 2) & 0x01);
+    timeBase =       ((flags >> 3) & 0x03);
+    time =           ((flags >> 6) & 0x01);
+    newRisingEdge =  ((flags >> 7) & 0x01);
+
+    if (newFallingEdge)
+    {
+        eventime = gpst2time(wnF,towMsF*1E-3+towSubMsF*1E-9);
+        raw->obs.flag = 5; /* Event flag */
+        raw->obs.eventime = eventime;
+        raw->obs.rcvcount = count;
+        raw->obs.tmcount++;
+        raw->obs.timevalid = time;
+    } else {
+        raw->obs.flag = 0;
+    }
+    return 0;
+}
+
 /* decode ublox raw message --------------------------------------------------*/
 static int decode_ubx(raw_t *raw)
 {
@@ -1093,6 +1140,7 @@ static int decode_ubx(raw_t *raw)
         case ID_TRKMEAS : return decode_trkmeas (raw);
         case ID_TRKD5   : return decode_trkd5   (raw);
         case ID_TRKSFRBX: return decode_trksfrbx(raw);
+        case ID_TIMTM2  : return decode_timtm2  (raw);
     }
     if (raw->outtype) {
         sprintf(raw->msgtype,"UBX 0x%02X 0x%02X (%4d)",type>>8,type&0xF,
@@ -1107,7 +1155,7 @@ static int sync_ubx(unsigned char *buff, unsigned char data)
     return buff[0]==UBXSYNC1&&buff[1]==UBXSYNC2;
 }
 /* input ublox raw message from stream -----------------------------------------
-* fetch next ublox raw data and input a mesasge from stream
+* fetch next ublox raw data and input a message from stream
 * args   : raw_t *raw   IO     receiver raw data control struct
 *          unsigned char data I stream data (1 byte)
 * return : status (-1: error message, 0: no message, 1: input observation data,
diff --git a/src/rinex.c b/src/rinex.c
index ced5a615dd32e405f7f9e8d9517804d44a84fc04..a04c8e0d72a5e184481db66da5f779fb9e8deaa6 100644
--- a/src/rinex.c
+++ b/src/rinex.c
@@ -14,7 +14,7 @@
 *         Version 2.12, June 23, 2009
 *     [5] W.Gurtner and L.Estey, RINEX The Receiver Independent Exchange Format
 *         Version 3.01, June 22, 2009
-*     [6] J.Ray and W.Gurtner, RINEX extentions to handle clock information
+*     [6] J.Ray and W.Gurtner, RINEX extensions to handle clock information
 *         version 3.02, September 2, 2010
 *     [7] RINEX The Receiver Independent Exchange Format Version 3.02,
 *         International GNSS Service (IGS), RINEX Working Group and Radio
@@ -544,7 +544,7 @@ static void decode_gnavh(char *buff, nav_t *nav)
     
     trace(4,"decode_gnavh:\n");
     
-    if      (strstr(label,"CORR TO SYTEM TIME"  )) ; /* opt */
+    if      (strstr(label,"CORR TO SYSTEM TIME"  )) ; /* opt */
     else if (strstr(label,"LEAP SECONDS"        )) { /* opt */
         if (nav) nav->leaps=(int)str2num(buff,0,6);
     }
@@ -556,7 +556,7 @@ static void decode_hnavh(char *buff, nav_t *nav)
     
     trace(4,"decode_hnavh:\n");
     
-    if      (strstr(label,"CORR TO SYTEM TIME"  )) ; /* opt */
+    if      (strstr(label,"CORR TO SYSTEM TIME"  )) ; /* opt */
     else if (strstr(label,"D-UTC A0,A1,T,W,S,U" )) ; /* opt */
     else if (strstr(label,"LEAP SECONDS"        )) { /* opt */
         if (nav) nav->leaps=(int)str2num(buff,0,6);
@@ -2038,6 +2038,27 @@ static int obsindex(double ver, int sys, const unsigned char *code,
     }
     return -1;
 }
+/* output rinex event time ---------------------------------------------------*/
+static void outrinexevent(FILE *fp, const rnxopt_t *opt, const obs_t *obs,
+                          const double epdiff)
+{
+    int n;
+    double epe[6];
+
+    time2epoch(obs->eventime,epe);
+    n = obs->timevalid ? 0 : 1;
+
+    if (opt->rnxver<=2.99) { /* ver.2 */
+        if (epdiff < 0) fprintf(fp,"\n");
+        fprintf(fp," %02d %2.0f %2.0f %2.0f %2.0f%11.7f  %d%3d",
+                (int)epe[0]%100,epe[1],epe[2],epe[3],epe[4],epe[5],5,n);
+        if (epdiff >= 0) fprintf(fp,"\n");
+    } else { /* ver.3 */
+        fprintf(fp,"> %04.0f %2.0f %2.0f %2.0f %2.0f%11.7f  %d%3d\n",
+                epe[0],epe[1],epe[2],epe[3],epe[4],epe[5],5,n);
+    }
+    if (n) fprintf(fp,"%-60.60s%-20s\n"," Time mark is not valid","COMMENT");
+}
 /* output rinex obs body -------------------------------------------------------
 * output rinex obs body
 * args   : FILE   *fp       I   output file pointer
@@ -2047,22 +2068,21 @@ static int obsindex(double ver, int sys, const unsigned char *code,
 *          int    flag      I   epoch flag (0:ok,1:power failure,>1:event flag)
 * return : status (1:ok, 0:output error)
 *-----------------------------------------------------------------------------*/
-extern int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obsd_t *obs, int n,
-                      int flag)
+extern int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obs_t *obs)
 {
     const char *mask;
-    double ep[6];
+    double epdiff,ep[6];
     char sats[MAXOBS][4]={""};
     int i,j,k,m,ns,sys,ind[MAXOBS],s[MAXOBS]={0};
+
+    trace(3,"outrnxobsb: n=%d\n",obs->n);
+
+    time2epoch(obs->data[0].time,ep);
     
-    trace(3,"outrnxobsb: n=%d\n",n);
-    
-    time2epoch(obs[0].time,ep);
-    
-    for (i=ns=0;i<n&&ns<MAXOBS;i++) {
-        sys=satsys(obs[i].sat,NULL);
-        if (!(sys&opt->navsys)||opt->exsats[obs[i].sat-1]) continue;
-        if (!sat2code(obs[i].sat,sats[ns])) continue;
+    for (i=ns=0;i<obs->n&&ns<MAXOBS;i++) {
+        sys=satsys(obs->data[i].sat,NULL);
+        if (!(sys&opt->navsys)||opt->exsats[obs->data[i].sat-1]) continue;
+        if (!sat2code(obs->data[i].sat,sats[ns])) continue;
         switch (sys) {
             case SYS_GPS: s[ns]=0; break;
             case SYS_GLO: s[ns]=1; break;
@@ -2075,9 +2095,17 @@ extern int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obsd_t *obs, int n,
         if (!opt->nobs[opt->rnxver<=2.99?0:s[ns]]) continue;
         ind[ns++]=i;
     }
+
+    /* if epoch of event less than epoch of observation, then first output
+    time mark, else first output observation record */
+    epdiff = timediff(obs->data[0].time,obs->eventime);
+    if (obs->flag == 5 && epdiff >= 0) {
+        outrinexevent(fp, opt, obs, epdiff);
+    }
+
     if (opt->rnxver<=2.99) { /* ver.2 */
         fprintf(fp," %02d %2.0f %2.0f %2.0f %2.0f%11.7f  %d%3d",
-                (int)ep[0]%100,ep[1],ep[2],ep[3],ep[4],ep[5],flag,ns);
+                (int)ep[0]%100,ep[1],ep[2],ep[3],ep[4],ep[5],0,ns);
         for (i=0;i<ns;i++) {
             if (i>0&&i%12==0) fprintf(fp,"\n%32s","");
             fprintf(fp,"%-3s",sats[i]);
@@ -2085,10 +2113,10 @@ extern int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obsd_t *obs, int n,
     }
     else { /* ver.3 */
         fprintf(fp,"> %04.0f %2.0f %2.0f %2.0f %2.0f%11.7f  %d%3d%21s\n",
-                ep[0],ep[1],ep[2],ep[3],ep[4],ep[5],flag,ns,"");
+                ep[0],ep[1],ep[2],ep[3],ep[4],ep[5],0,ns,"");
     }
     for (i=0;i<ns;i++) {
-        sys=satsys(obs[ind[i]].sat,NULL);
+        sys=satsys(obs->data[ind[i]].sat,NULL);
         
         if (opt->rnxver<=2.99) { /* ver.2 */
             m=0;
@@ -2105,7 +2133,7 @@ extern int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obsd_t *obs, int n,
                 if (j%5==0) fprintf(fp,"\n");
             }
             /* search obs data index */
-            if ((k=obsindex(opt->rnxver,sys,obs[ind[i]].code,opt->tobs[m][j],
+            if ((k=obsindex(opt->rnxver,sys,obs->data[ind[i]].code,opt->tobs[m][j],
                             mask))<0) {
                 outrnxobsf(fp,0.0,-1,-1);
                 continue;
@@ -2113,14 +2141,19 @@ extern int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obsd_t *obs, int n,
             /* output field */
             switch (opt->tobs[m][j][0]) {
                 case 'C':
-                case 'P': outrnxobsf(fp,obs[ind[i]].P[k],-1,obs[ind[i]].qualP[k]); break;
-                case 'L': outrnxobsf(fp,obs[ind[i]].L[k],obs[ind[i]].LLI[k],obs[ind[i]].qualL[k]); break;
-                case 'D': outrnxobsf(fp,obs[ind[i]].D[k],-1,-1); break;
-                case 'S': outrnxobsf(fp,obs[ind[i]].SNR[k]*0.25,-1,-1); break;
+                case 'P': outrnxobsf(fp,obs->data[ind[i]].P[k],-1,obs->data[ind[i]].qualP[k]); break;
+                case 'L': outrnxobsf(fp,obs->data[ind[i]].L[k],obs->data[ind[i]].LLI[k],obs->data[ind[i]].qualL[k]); break;
+                case 'D': outrnxobsf(fp,obs->data[ind[i]].D[k],-1,-1); break;
+                case 'S': outrnxobsf(fp,obs->data[ind[i]].SNR[k]*0.25,-1,-1); break;
             }
         }
         if (opt->rnxver>2.99&&fprintf(fp,"\n")==EOF) return 0;
     }
+
+    if (obs->flag == 5 && epdiff < 0) {
+        outrinexevent(fp, opt, obs, epdiff);
+    }
+
     if (opt->rnxver>2.99) return 1;
     
     return fprintf(fp,"\n")!=EOF;
@@ -2581,7 +2614,7 @@ extern int outrnxlnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
     return fprintf(fp,"%60s%-20s\n","","END OF HEADER")!=EOF;
 }
 /* output rinex qzss nav header ------------------------------------------------
-* output rinex qzss nav file header (2.12 extention and 3.02)
+* output rinex qzss nav file header (2.12 extension and 3.02)
 * args   : FILE   *fp       I   output file pointer
 *          rnxopt_t *opt    I   rinex options
 *          nav_t  nav       I   navigation data (NULL: no input)
@@ -2609,7 +2642,7 @@ extern int outrnxqnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
     return fprintf(fp,"%60s%-20s\n","","END OF HEADER")!=EOF;
 }
 /* output rinex beidou nav header ----------------------------------------------
-* output rinex beidou nav file header (2.12 extention and 3.02)
+* output rinex beidou nav file header (2.12 extension and 3.02)
 * args   : FILE   *fp       I   output file pointer
 *          rnxopt_t *opt    I   rinex options
 *          nav_t  nav       I   navigation data (NULL: no input)
diff --git a/src/rtklib.h b/src/rtklib.h
index 30c592826480921b6ca677bf4974216cb9c2848b..f2307fbcf4719d1a05fc79781bdc15420eeaf3fc 100644
--- a/src/rtklib.h
+++ b/src/rtklib.h
@@ -557,6 +557,11 @@ typedef struct {        /* observation data record */
 } obsd_t;
 
 typedef struct {        /* observation data */
+    gtime_t eventime;   /* time of event (GPST) */
+    int timevalid;      /* time is valid (Valid GNSS fix) for time mark */
+    int flag;           /* epoch flag (0:ok,1:power failure,>1:event flag) */
+    int rcvcount;       /* count of rcv event */
+    int tmcount;        /* time mark count */
     int n,nmax;         /* number of obervation data/allocated */
     obsd_t *data;       /* observation data records */
 } obs_t;
@@ -1608,8 +1613,7 @@ EXPORT int readrnxt(const char *file, int rcv, gtime_t ts, gtime_t te,
                     sta_t *sta);
 EXPORT int readrnxc(const char *file, nav_t *nav);
 EXPORT int outrnxobsh(FILE *fp, const rnxopt_t *opt, const nav_t *nav);
-EXPORT int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obsd_t *obs, int n,
-                      int epflag);
+EXPORT int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obs_t *obs);
 EXPORT int outrnxnavh (FILE *fp, const rnxopt_t *opt, const nav_t *nav);
 EXPORT int outrnxgnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav);
 EXPORT int outrnxhnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav);