[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: 20000302: pqsurf with TEST metars



On Thu, 2 Mar 2000, Unidata Support wrote:

> 
> ------- Forwarded Message
> 
> >To: address@hidden
> >cc: address@hidden (Celia Chen),
> >cc: address@hidden
> >From: address@hidden (Celia Chen)
> >Subject: METAR data decoding for FXUS43
> >Organization: .
> >Keywords: 200003012338.QAA04944
> 
> We would like to be able to decode METAR data from 
> FXUS43 using pqsurf. We have been trying to figure
> out what the "mtype" is for this dataset without
> success - it is being handled as MESSAGE_TYPE_UNKNOWN. 
> Please help!
> 
Celia,

The metar reports were disregarded because of the FXUS prefix instead of
the SAUX prefix to the product headers.  I modified wmo_header.(c|h) and
surf_split.c to handle the FXUS prefix.  I'll attach the code so you can
build your release. I don't plan on checking in this code because the FXUS
prefix means that the reports are test reports only, not operational
reports. At some time in the future the FXUS bulletins will have a
header change to SXUS, meaning operational. For future LDM releases you
will have to merge the code into the release unless someone can convince 
me to include it in the release.

Robb...



> Thanks in advance.
> 
> Celia
> 
>  P. S. Here is a sample dataset:
> 
> 
> 
> 551 
> FXUS43 KGLD 012303
> MTTCBK
> METAR KCBK 012255Z AUTO 15022G28KT 10SM CLR 15/M01 A3005 RMK AO1
>  T01501011
> 
> 
> 
> 360 
> FXUS43 KGLD 012308
> MTTCBK
> METAR KCBK 012255Z AUTO 15022G28KT 10SM CLR 15/M01 A3005 RMK AO1
>  T01501011
> 
> 
> 
> 551 
> FXUS43 KGLD 012303
> MTTCBK
> METAR KCBK 012255Z AUTO 15022G28KT 10SM CLR 15/M01 A3005 RMK AO1
>  T01501011
> 
> 
> 
> 360 
> FXUS43 KGLD 012308
> MTTCBK
> METAR KCBK 012255Z AUTO 15022G28KT 10SM CLR 15/M01 A3005 RMK AO1
>  T01501011
> 
> 
> 
> 
> 
> ------- End of Forwarded Message
> 

===============================================================================
Robb Kambic                                Unidata Program Center
Software Engineer III                      Univ. Corp for Atmospheric Research
address@hidden             WWW: http://www.unidata.ucar.edu/
===============================================================================
/*
 *   Copyright 1993, University Corporation for Atmospheric Research
 *   See ../COPYRIGHT file for copying and redistribution conditions.
 */
/* $Id: wmo_header.h,v 1.11 1999/06/03 21:13:26 rkambic Exp $ */
#ifndef _WMO_HEADER_H_
#define _WMO_HEADER_H_

#include "xbuf.h"
#include "dtime.h"

typedef enum {
        MESSAGE_TYPE_UNKNOWN = 0 ,
        SYNOP,  /* FM 12 */
        SHIP,   /* FM 13 */
        METAR,  /* FM 15 or SAO!! */
        SPECI   /* FM 16 */
} message_type_t;


typedef enum {
        ORIGINAL = 0, 
        RTD ,   /* delayed */
        COR ,   /* correction */
        AMD ,   /* amended */
        PIE     /* pieces */
} retransmit_t;


/* abbreviated heading, 2.3.2 */
typedef struct {
        char TT[3];     /* Tsub1Tsub2 : Data type and/or form */
        char AA[3];     /* Asub1Asub2 : Geograph. and/or time */
        int ii;
        char CCCC[5];  /* station of origin or compilation */
        char PIL[10];
        dtime *time;
        retransmit_t retransmit; /* BBB delay, correction or amendment ind */
        int retrans_seq; /* the sequence from BBB */
} wmo_header_t;

extern void free_wmo_header(wmo_header_t *hdr);
extern wmo_header_t *new_wmo_header(char **line);
extern wmo_header_t *get_wmo_header(xbuf *buf, wmo_header_t *hdr);

extern char *
s_wmo_header(wmo_header_t *hdr);

/* 
extern int
fprint_wmo_header(FILE *fp , const wmo_header_t *hdr);
*/

extern char *sRetransmit(wmo_header_t *hdr );
extern char *sMessage_type(message_type_t type);
extern message_type_t decode_type(char *tt, char *aa, char *PIL);

#endif /* _WMO_HEADER_H_ */
/*
 *   Copyright 1993, University Corporation for Atmospheric Research
 *   See ../COPYRIGHT file for copying and redistribution conditions.
 */
/* $Id: wmo_header.c,v 1.30 1999/06/21 22:15:21 rkambic Exp $ */
#include <stdio.h>
#include <string.h>
#include "wmo_header.h"
#include "alloc.h"
#include "tokens.h"
#include "xbuf.h"

int usePil=0;


void
free_wmo_header(wmo_header_t *hdr)
{
        if(hdr == NULL) return;
        free_dtime(hdr->time);
        free(hdr);
}


static void
clear_wmo_header(wmo_header_t *hdr)
{
        hdr->TT[0]  = hdr->AA[0] = hdr->ii = 
                hdr->CCCC[0] = hdr->PIL[0] = 0;
        hdr->retransmit = ORIGINAL;
        hdr->retrans_seq = MESSAGE_TYPE_UNKNOWN;
        clear_dtime(hdr->time);
}


wmo_header_t *
get_wmo_header(xbuf *buf, wmo_header_t *hdr)
{

        clear_wmo_header(hdr);

        if( get_wstr(buf, hdr->TT, 2) == EOB ) return NULL;
        if( get_wstr(buf, hdr->AA, 2) == EOB ) return NULL;
        if( get_wnum(buf, &hdr->ii, 2) == EOB ) return NULL;
        if(hdr->ii == -1) hdr->ii = 0;
        if( get_wstr(buf, hdr->CCCC, 4) == EOB ) return NULL;

        {
        int YY, GG, gg;
        if( get_wnum(buf, &YY, 2) == EOB ) return NULL;
        if( get_wnum(buf, &GG, 2) == EOB ) return NULL;
        if( get_wnum(buf, &gg, 2) == EOB ) return NULL;
        /* uses current time on parse errors */
        hdr->time = set_dtime(hdr->time, YY,GG,gg);
        }


        /* decode BBB feild */
        { /* inline */
        char line[16];
        char pilstr[16];
        int allnum, nonalph, ich;
        
        if( get_wline(buf, line, sizeof(line)) == EOB ) return NULL;

        { /* inline inner */
        const char *cp = &line[0];
        const char *const end = &line[strlen(line)];
        

        /* N.B. twisted flow here */
        for(cp = &line[0]; *cp != 0 && *cp != CR && cp + 2 < end; cp++)
        {
                switch( cp[0] ) {
                case 'R' :
                        if(cp[1] == 'R')
                        {
                                hdr->retransmit = RTD;          
                                hdr->retrans_seq = cp[2];               
                        }
                        else if(cp[1] == 'T')
                        {
                                hdr->retransmit = RTD;          
                        }
                        break;
                case 'C' :
                        if(cp[1] == 'C')
                        {
                                hdr->retransmit = COR;          
                                hdr->retrans_seq = cp[2];               
                        }
                        else if(cp[1] == 'O')
                        {
                                hdr->retransmit = COR;          
                        }
                        break;
                case 'A' :
                        if(cp[1] == 'A')
                        {
                                hdr->retransmit = AMD;          
                                hdr->retrans_seq = cp[2];               
                        }
                        else if(cp[1] == 'M')
                        {
                                hdr->retransmit = AMD;          
                        }
                        break;
                case 'P' :
                        hdr->retransmit = PIE;          
                        hdr->retrans_seq = (cp[1] - 'A') * 26
                                 + (cp[2] - 'A');
                        goto done;
                case 0 :
                        break;
                default :
                        continue; /* loop, look some more */
                }
                /* only arrrive here if we got an acceptable BBB string */
                if(hdr->retrans_seq == 0)
                {
                        int tmp;
                        /* see if they used old style, eg RTD01 */
                        cp += 3;
                        tmp = atoi(cp);
                        /* Attachment II-14 */
                        if(tmp >= 0 && tmp < 24 )
                                hdr->retrans_seq = tmp + 'A';
                        else
                                hdr->retrans_seq = 'Y';
                }
done:
                break;
        } /* end for */
        } /* end inline inner */
        if((usePil == 1) && ( get_wstr(buf, pilstr, sizeof(pilstr)) != EOB ))
           {
           if((pilstr[0] != NULL) && ( get_wline(buf, line, sizeof(line)) != 
EOB ))
              {
              if((line[0] == NULL)&&(strlen(pilstr) > 4)&&(strlen(pilstr) < 7))
                 {
                 nonalph = 0; allnum = 1;
                 for(ich = 0;ich < strlen(pilstr);ich++)
                    {
                    if(isalnum(pilstr[ich]) == 0) nonalph = 1;
                    if(isdigit(pilstr[ich]) == 0) allnum = 0;
                    }
                 if((nonalph == 0)&&(allnum == 0)) sprintf(hdr->PIL," 
/p%s\0",pilstr);
                 }
              else if((line[0] == NULL)&&(strncmp(pilstr,"^NMC",4) == 0)&&
                 (strlen(pilstr+4) > 4)&&(strlen(pilstr+4) < 7))
                 sprintf(hdr->PIL," /p%s\0",pilstr+4);
              
              }
           }
        
        } /* end inline */

        return hdr;
}


char *
sRetransmit(wmo_header_t *hdr)
{
        static char buf[4];
        int seq = hdr->retrans_seq;
        if(hdr->retransmit == PIE)
        {
                if(seq < 0 || seq > 675) /* 26 * 26 -1 */
                        seq = 675;
        }
        else if(seq < 'A' || seq > 'Z')
                seq = 0;
        switch ( hdr->retransmit ) {
        case RTD :
                if(seq)
                {
                        sprintf(buf,"RR%c", seq);
                        return buf;
                }
                /* else */
                return "RTD";
        case COR :
                if(seq)
                {
                        sprintf(buf,"CC%c", seq);
                        return buf;
                }
                /* else */
                return "COR";
        case AMD :
                if(seq)
                {
                        sprintf(buf,"AA%c", seq);
                        return buf;
                }
                /* else */
                return "AMD";
        case PIE :
                {
                        const char c1 = seq/26 + 'A';
                        const char c2 = seq%26 + 'A';
                        sprintf(buf,"P%c%c", c1, c2);
                        return buf;
                }
        }
        /* default */
        return NULL;
}


char *
s_wmo_header(wmo_header_t *hdr)
{
#ifndef KEYSIZE
#define KEYSIZE 255
#endif
        static char sp[KEYSIZE+1];
        char *cp = sp;

        (void) memset(sp,0,sizeof(sp));

        sprintf(cp,
                "%2s%2s%02d %4s",
                hdr->TT, hdr->AA, hdr->ii, hdr->CCCC);
        cp += 11;
        if(hdr->time != NULL)
        {
                sprintf(cp,
                        " %02d%02d%02d",
                        hdr->time->mday, hdr->time->hour,
                        hdr->time->min );
        } 
        else
        {
                sprintf(cp, " DDHHMM");
        }
        cp += 7;

        if(hdr->retransmit != ORIGINAL)
                sprintf(cp, " %s", sRetransmit(hdr));

        if(hdr->PIL[0] != NULL)
           strcat(sp,hdr->PIL);

        return sp;
}


#if 0
int
fprint_wmo_header(FILE *fp, const wmo_header_t *hdr)
{
#if 1
        fprintf(fp,
                "%2s%2s%02d %4s",
                hdr->TT, hdr->AA, hdr->ii, hdr->CCCC);
        if(hdr->time != NULL)
        {
                fprintf(fp,
                        " %02d%02d%02d",
                        hdr->time->mday, hdr->time->hour,
                        hdr->time->min );
        } 
        else
        {
                
                fprintf(fp, " DDHHMM");
        }

        if(hdr->retransmit != ORIGINAL)
                fprintf(fp, " %s", sRetransmit(hdr));
#else
        fputs( s_wmo_header(hdr) , fp );
#endif

        return ferror(fp);
}
#endif


#if USED
static void 
clear_wmo_header(wmo_header_t *hdr)
{
        hdr->TT[0]  = hdr->AA[0] = hdr->ii = 
                hdr->CCCC[0] = hdr->retransmit = hdr->retrans_seq  = 0;
        free_dtime(hdr->time);
        hdr->time = NULL;
}
#endif /* USED */


char *
sMessage_type(message_type_t type)
{
        switch(type) {
        case SYNOP : return "SYNOP";
        case SHIP : return "SHIP";
        case METAR : return "METAR";
        case SPECI : return "SPECI";
        case MESSAGE_TYPE_UNKNOWN : return "MESSAGE_TYPE_UNKNOWN";
        }
        /* default */
        return NULL;
}


message_type_t
decode_type(char *tt, char *aa, char *pil)
{
        if( pil[0] == 'M' && pil[1] == 'T')
        {
                if( pil[2] == 'R' || pil[2] == 'T')
                        return METAR; 
        }
        if( tt[0] == 'S' )
        {
                /* Table B1 */
                switch( tt[1] ) {
                case 'I' :
                case 'M' :
                case 'N' :
                        if(aa[0] == 'W' || aa[0] == 'V' )
                        { 
                                /* table C2 */
                                switch( aa[1] ) {
                                case 'A' :
                                case 'B' :
                                case 'C' :
                                case 'D' :
                                case 'E' :
                                case 'F' :
                                case 'J' :
                                case 'X' :
                                        return SHIP;
                                }
                                /* else */
                        }
                        /* else */
                        return SYNOP;
                case 'A' :
                        return METAR; /* might be an 'sao' */
                case 'X' :
                        if(*aa == 'U' && *(aa +1) == 'S')
                                return METAR; /* 'sao' */
                        break;
                case 'P' :
                        return SPECI;
                }
                /* else */
        }
        /* else */
        return MESSAGE_TYPE_UNKNOWN;
        
}
/*
 *   Copyright 1993, University Corporation for Atmospheric Research
 *   See ../COPYRIGHT file for copying and redistribution conditions.
 */
/* $Id: surf_split.c,v 1.30 1999/12/02 23:21:26 rkambic Exp $   */

#include <ldmconfig.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ldm.h"
#include "ulog.h"
#include "wmo_header.h"
#include "tokens.h"
#include "xbuf.h"
#include "surface.h" /* wind_units_t, CALL_SIGN_LEN */

#include "md5.h"

static double md5ctx[16]; /* 88 would be big enough */
static MD5_CTX *md5ctxp = (MD5_CTX *)md5ctx;

static int
get_yygg(xbuf *buf, dtime *time)
{
        int status;
        int YY = -1;
        int GG = -1;

        if((status = dget_wnum(buf, &YY, 2)) < 0) return status;
        if((status = dget_num(buf, &GG, 2)) < 0) return status;

        set_dtime(time, YY, GG, 0);
        return status;
}

/* For METAR, check if the HHMMZ time string is present */
static int
whas_yyggZ(xbuf *buf)
{
        int ch;

        /* skip white space */
        do{
                ch = nextc(buf);
        }while((isascii(ch) && !isgraph(ch)));
        unnextc(buf,ch);

        if(buf->cnt < 5)
                return 0; /* not enough characters */
        if(buf->get[4] != 'Z'
                         || !isdigit(buf->get[3])
                         || !isdigit(buf->get[2])
                         || !isdigit(buf->get[1])
                         || !isdigit(buf->get[0]))
                return 0;
        return 1; /* passed */
}

/* For METAR, check if "NIL" */
static int
has_NIL(xbuf *buf)
{
        char nilstr[]  = "NIL";
        char *np = (char *)&buf->base[buf->bufsiz - 1 - (sizeof(nilstr) -1 -1)];

        if(strncmp(np, nilstr, sizeof(nilstr) -1) == 0)
                return 1;
        return 0;
}

/* For METAR, get the bulletin time, if possible */
static void
get_wyyggZ(xbuf *buf, dtime *time)
{
        int ch;
        if(!whas_yyggZ(buf))
                return;
        (void)get_yygg(buf, time);
        ch = nextc(buf); /* eat the 'Z' */
        return;
}


/*
 *      Takes a WMO format product which is a
 *   SAO, SYNOP, SHIP, METAR, or SPECI message, splits it into
 *   individual observations. The observations are each encapsulated in a
 *   new product which inherits most of its description from the
 *   original product.
 *  The new product pkey is derived from the observation type
 *   and has the following form:
 *
 *              SAO -   "sao tt ccc ddhhmm"
 *                      where:
 *                              tt is SA, SP or RS 
 *                              ccc is the station ID like SFO, LXV, etc
 *                              ddhhmm is the time stamp.
 *
 *              SYNOP - "aaxx nnnnn ddhhmm"
 *                      where:
 *                              nnnnn is the WMO station id (5 digit number)
 *
 *              SHIP -  "bbxx c* ddhhmm"
 *                      where:
 *                              c* is the call sign
 *
 *              METAR - "metar cccc ddhhmm"
 *                      where:
 *                              cccc is the call sign
 *
 *              SPECI - "speci cccc ddhhmm" 
 *
 *  The new product sequence number is original sequence number times 1000
 *   plus the sequence of the individual observation within the product.
 *
 *      'doit' is called on each of the new products. It is presumed
 * this function return  zero upon success.
 * 
 *  Returns the number of successful calls to 'doit', eg, the
 *  number of splits. Returns -1 on error.
 */
int
surf_split(const prod_info *infop, const void *datap,
                int (*doit)(const prod_info *, const void *))
{
        int action = -1;
        wmo_header_t hdr;
        message_type_t mtype;
        dtime dt;
        xbuf buf[1];
        unsigned char dbuf[8192]; /* TODO */
        int nsplit = 0;

        enum {
                SURFACE_BOGUS ,
                AAXX,
                US_AAXX,
                BBXX,
                SAO,
                sMETAR,
                sSPECI
        } subtype = SURFACE_BOGUS;

        hdr.time = &dt;

        if(infop->sz > sizeof(dbuf))
                return -1; /* TODO: too big */

        memcpy(dbuf, datap, infop->sz);

        if( cbuftoxbuf(buf, (unsigned char *)dbuf,
                        infop->sz) == NULL)
                return -1;
        
        skipline(buf, 4); /* SOH */
        skipline(buf, 12); /* start */

        if( get_wmo_header(buf, &hdr) == NULL)
        {
                return -1;
        } 
#if DEBUG
        fputs("\t", stderr);
        fprint_wmo_header(stderr, &hdr);
        fputs("\n", stderr);
#endif

        mtype = decode_type(hdr.TT,hdr.AA,hdr.PIL);
        
        /* #### */
        {
        char cbuf[8];
        int digit;
        dtime time;
        wind_units_t wind_units = WIND_UNAVAIL;

        time = *hdr.time; /* default the ob time to the time in the header */

        /* delve into section 0 */

        switch(mtype) {
        case SYNOP :
                if(get_wstr(buf, cbuf, 1) < 0 ) return -1;
                if(cbuf[0] == 'A')
                {
                        subtype = AAXX;
                        if(get_str(buf, &cbuf[1], 3) < 0 ) return -1;
                        if( cbuf[3] != 'X' )
                        {
                                /* punt */
                                uerror("surface_split: Unknown type: %s\n", 
cbuf);
                                return 0;
                        }
                        if(get_yygg(buf, &time) < 0 ) return -1; /* YYGG */
                        if(dget_num(buf, &digit, 1) < 0 ) return -1; /* isubw */
                        if(digit >= 0 && digit <= 4) wind_units = 
(wind_units_t)digit;
                }
                else if(isascii(cbuf[0]) && isdigit(cbuf[0])) /* US Stations 
7NNNN */
                {
                        unnextc(buf,cbuf[0]);
                        subtype = US_AAXX;
                        /* 
                         * Some US reports leave off AAXX YYGGisubw, so we use 
the
                         * time from the wmo header. 
                         */
                        wind_units = KNOTS;
                }
                else
                {
                        unnextc(buf,cbuf[0]);
                        return 0; /* ?? */
                }
                break;
        case SHIP :
                if(get_wstr(buf, cbuf, 4) < 0 ) return -1;
                if(cbuf[0] == 'B')
                {
                        if( cbuf[3] != 'X' )
                        {
                                /* punt */
                                uerror("surface_split: Unknown type: %s\n", 
cbuf);
                                return 0;
                        }
                        subtype = BBXX;
                        /* get time below */
                }
                else
                {
                        unnextc(buf,cbuf[0]);
                        return 0;
                }
                break;
        case METAR :
                if(whasSTR(buf, "METAR"))
                {
                        subtype = sMETAR;
                        get_wyyggZ(buf, &time);
                }
                else
                        subtype = SAO; /* may actually be a METAR, check below 
*/
                break;  
        case SPECI :
                if(whasSTR(buf, "SPECI"))
                {
                        subtype = sSPECI;
                        get_wyyggZ(buf, &time);
                }
                break;  
        default :
                uerror("surface_split: Can't handle %s", 
                        sMessage_type(mtype) );
                return -1;
        }

        { /* while block */
        static char newkey[KEYSIZE];
        xbuf subbuf[1];
        prod_info newinfo = *infop;
#define MAX_SURF_LEN 511
#undef MIN
#define MIN(a,b) ((a) <= (b) ? (a) : (b))
        char pbuf[MAX_SURF_LEN + 1];
        int l1, l2;
        static char ident[CALL_SIGN_LEN+1];
        static char type[4];
        u_int subseq = infop->seqno * 1000; 
        unsigned char *pp;

        while( get_weqxbuf(buf, subbuf) > 0 )
        {
                (void)memset(newkey,0,KEYSIZE);
                (void)memset(pbuf,0,MAX_SURF_LEN + 1);
                (void)memset(ident,0,CALL_SIGN_LEN+1);
                pp = subbuf->base;

                switch(subtype) {
                case AAXX :
                case US_AAXX :
                        strcpy(newkey, "aaxx ");
                        strcpy(pbuf, "AAXX");
                        sprintf(&pbuf[strlen(pbuf)], " %02d%02d%1d\r\r\n",
                                time.mday, time.hour, (int)wind_units);
                                        /* WMO station no. */
                        if(get_wstr(subbuf, ident, 5) < 0)
                                continue;
                        strcat(newkey, ident);
                        break;
                case BBXX :
                        strcpy(newkey, "bbxx ");
                        strcpy(pbuf, "BBXX\r\r\n");
                        /* call sign */
                        if(get_wstr(subbuf, ident, CALL_SIGN_LEN) < 0)
                                continue;
                        strcat(newkey, ident);
                        if(get_yygg(subbuf, &time) < 0) continue; /* YYGG */
                        break;
                case sSPECI :
                        /* call sign */
                        if(get_wstr(subbuf, ident, CALL_SIGN_LEN) < 0)
                                continue;
                        if(strcmp(ident, "SPECI") == 0)
                        {
                                /* They package each ob with a tag */
                                pp = (subbuf->get +1);
                                if(get_wstr(subbuf, ident, CALL_SIGN_LEN) < 0)
                                continue;
                        }
                        if(!whas_yyggZ(subbuf))
                        {
                                /* Have to insert the date */
                                sprintf(pbuf, "SPECI\r\r\n%s %02d%02dZ ",
                                        ident, time.hour, time.min);
                                pp = subbuf->get;
                        }
                        else
                                strcpy(pbuf, "SPECI\r\r\n");
                        strcpy(newkey, "speci ");
                        strcat(newkey, ident);
                        break;
                case sMETAR :
                        if(has_NIL(subbuf))
                                continue;
                        /* call sign */
                        if(get_wstr(subbuf, ident, CALL_SIGN_LEN) < 0)
                                continue;
                        if(strcmp(ident, "METAR") == 0)
                        {
                                /* They package each ob with a tag */
                                pp = (subbuf->get +1);
                                if(get_wstr(subbuf, ident, CALL_SIGN_LEN) < 0)
                                continue;
                        }
                        if(!whas_yyggZ(subbuf))
                        {
                                /* Have to insert the date */
                                sprintf(pbuf, "METAR\r\r\n%s %02d%02dZ ",
                                        ident, time.hour, time.min);
                                pp = subbuf->get;
                        }
                        else
                                strcpy(pbuf, "METAR\r\r\n");
                        strcpy(newkey, "metar ");
                        strcat(newkey, ident);
                        break;
                case SAO :
                        /* call sign */
                        if(get_wstr(subbuf, ident, CALL_SIGN_LEN) < 0)
                                continue;
                        if(hdr.AA[0] == 'U' && hdr.AA[1] == 'S'
                                        && strlen(ident) == 6)
                        {
                                /* skip 6 char US "AFOS code" */
                                if(get_wstr(subbuf, ident, CALL_SIGN_LEN) < 0)
                                        continue;
                        }
                                
                        /* SA, SP, RS, USP or XP */
                        if(get_wstr(subbuf, type, 3) < 0)
                                continue;
                        if((type[0] == 'S'
                                         && (type[1] == 'A' || type[1] == 'P'))
                                || (type[0] == 'R' && type[1] == 'S')
                                || (type[0] == 'U' && type[1] == 'S'
                                         && type[2] == 'P')
                                || (type[0] == 'X' && type[1] == 'P')
                                || (type[0] == 'T' &&
                                         (type[1] == 'A' || type[1] == 'S'))
                                )
                        {
                                strcpy(newkey, "sao ");
                                strcat(newkey, type);
                                strcat(newkey, " ");
                                strcat(newkey, ident);
                        } 
                        else if(isdigit(type[0]) && isdigit(type[1]))
                        {
                                /* it is a METAR really */
                                subtype = sMETAR;
                                strcpy(newkey, "metar ");
                                strcat(newkey, ident);
                                strcpy(pbuf, "METAR\r\r\n");
                        }
                        else
                                continue; /* don't know what it is, "NIL=" */
                        break;
                }

                /* safety net */
                if(strlen(ident) == 0)
                {
                        continue;
                }
                /* else */

                sprintf(&newkey[strlen(newkey)], " %02d%02d%02d",
                        time.mday, time.hour, time.min);
                if(hdr.retransmit != ORIGINAL)
                        sprintf(&newkey[strlen(newkey)], " %s",
                                sRetransmit(&hdr));
                newinfo.ident = newkey;
                newinfo.seqno = ++subseq;

                l1 = strlen(pbuf);
                l2 = MIN(MAX_SURF_LEN - l1 - 4, subbuf->bufsiz - (pp - 
subbuf->base));
                /* N.B.: silent truncation */
                strncat(pbuf, (char *)pp, l2 );
                strcat(pbuf,"=\r\r\n");

                newinfo.sz = l1 + l2 + 4;

#if DEBUG
                fprintf(stderr,"\t\t%s\n", newinfo.ident);
#endif
                
#if PRINT
                {
                        char *cp = pbuf;
                        char *end =  &cp[newinfo.sz];
                        while(cp < end)
                        {
                                putc(*cp, stderr);
                                cp++;
                        }
                }
                putc('\n', stderr);
#endif

                MD5Init(md5ctxp);
                MD5Update(md5ctxp, (const unsigned char *)pbuf, newinfo.sz);
                MD5Final(newinfo.signature, md5ctxp);
                
                /*
                 * process the single ob in the requested fashion
                 */
                if((*doit)(&newinfo, pbuf) == 0)
                        nsplit++;

        } /* end while */

#if PRINT
                putc('\n', stderr);
#endif
        } /* end while block */
        } /* end #### block */

        return nsplit;
}