#include "dcltng.h" void lt_nprt() /************************************************************************ * lt_nprt * * * * This program decodes encrypted ENI and Vaisala lighting data and * * encodes the output into BUFR format. * * * ** * * Log: * * S. Guan/NCEP 10/08 Initial version * * S. Guan/NCEP 01/09 Reconstruct * * J. Ator/NCEP 10/13 Modified to use DC_GBUL and DC_GHDR and * * to work with encrypted Vaisala data * * J. Ator/NCEP 04/15 Modified to process ENI data * * J. Ator/NCEP 08/15 Modified to decode HEIT from ENI data * ***********************************************************************/ { int curr; int prev; unsigned char min_sec; unsigned char hrs_min; unsigned short rel_date; unsigned short ii, jj; time_t rep_seconds; int strikes; int strike; int in_cloud; unsigned int int_lat; unsigned int int_lon; double lat; double lon; int kamps; short kampss; unsigned int tenths; unsigned int multi; unsigned int hrs; unsigned int mins; unsigned int secs; unsigned int year; unsigned int month; unsigned int day; unsigned short flash_type, pulse_type; unsigned short npulse; unsigned short ichght; unsigned int nfbytes; unsigned int nrepts; float flat, flon; struct tm *current; double sconds; int polarity, bin_cloud, valid_time, ibfdt; f77r8 xdata[14]; int lunout, lundx, narray, times, nret, i, maxbyt, iret, iforce, ier; char cf[2]; char subtypel[10], subtypes[10], subtypee[10]; char message[120]; char bull[MXBLSZ], seqnum[9], buhd[9], cborg[9], bulldt[9], bbb[9]; unsigned char bullx[MXBLSZ]; int ifdtyp, isbtyp, iergbl, ierghd, lenb, ibptr, lenbx, ibxptr; static char hdrstr[] = {"YEAR MNTH DAYS HOUR MINU SECO CLATH CLONH AMPLS PLRTS OWEP NOFL RSRD EXPRSRD"}; static char hdrstr2[] = {"HEIT"}; /* couldn't add this to hdrstr due to 80 character limit in BUFRLIB */ static char mbstr[] = {"lightning"}; char providers[NKF][4] = { "VSL", /* Vaisala */ "ENI" /* Earth Networks */ }; FILE *kf[NKF]; mbedtls_aes_context ctx[NKF]; unsigned char keyfile[LENKF]; unsigned int lkf; char dbndir[240], basekfn[240]; unsigned short encrypted; strcpy(subtypes, "NC007001"); strcpy(subtypel, "NC007002"); strcpy(subtypee, "NC007003"); strcpy(cf, "Y"); maxbyt = 100000; lunout = 70; lundx = 40; narray = 14; times = 1; /* Read and set the decryption keys. */ if ( strcpy(dbndir,getenv("DBNROOT")) == NULL ) { dc_wclg( 0, "DC", 2, "DBNROOT environment variable is undefined!\n", &ier ); return; } strcat(dbndir,"/.kf/decod_dcltng_"); for ( ii = 0; ii < NKF; ii++ ) { strcpy(basekfn,dbndir); if ( ( kf[ii] = fopen(strcat(basekfn,providers[ii]),"r") ) == NULL ) { sprintf( message, "Couldn't open decryption keyfile %s!\n", basekfn ); dc_wclg( 2, "DC", 2, message, &ier ); return; } for ( lkf = 0; lkf < LENKF; lkf++ ) { keyfile[lkf] = fgetc(kf[ii]); } fclose(kf[ii]); if ( mbedtls_aes_setkey_dec( &ctx[ii], keyfile, LENKF*8 ) != 0 ) { sprintf( message, "Couldn't set %s decryption key in mbedTLS library!\n", providers[ii] ); dc_wclg( 2, "DC", 2, message, &ier ); return; } } /* Open the BUFR table and BUFR output file. */ lt_pnbf(bufrtable, &lunout, &lundx, strlen( bufrtable )); maxout(&maxbyt); cmpmsg(cf, strlen(cf)); /* Get the next bulletin from the input pipe. */ while (1) { dc_gbul( bull, &lenb, &ifdtyp, &iergbl ); if ( iergbl != 0 ) { /* Shut down and exit. */ dc_wclg( 0, "DC", iergbl, " ", &ier ); closbf(&lunout); return; } if ( ifdtyp == 0 ) { /* Get the bulletin header. */ dc_ghdr( bull, &lenb, seqnum, buhd, cborg, bulldt, bbb, &ibptr, &ierghd, sizeof(bull), sizeof(seqnum), sizeof(buhd), sizeof(cborg), sizeof(bulldt), sizeof(bbb) ); if ( ierghd != 0 ) { dc_wclg( 2, "DC", ierghd, " ", &ier ); } else { /* Write the bulletin header to the decoder log. */ dc_wclg( 0, "DC", 2, "########################################\n", &ier ); seqnum[8] = buhd[8] = cborg[8] = bulldt[8] = bbb[8] = '\0'; sprintf( message, "%s %s %s %s %s\n", seqnum, buhd, cborg, bulldt, bbb ); dc_wclg( 2, "DC", 2, message, &ier ); /* Store the remainder of the bulletin into an unsigned char array. */ for ( lenbx = 0; ibptr <= lenb; ) { bullx[lenbx++] = (unsigned char) bull[ibptr++]; } /* Determine the provider for this bulletin and check whether the bulletin is encrypted. If either of these cannot be determined, skip over the bulletin. Otherwise, if the bulletin is encrypted, decrypt it. */ ii = ( buhd[5] == '2' ? 1 : 0 ); if ( lt_dcrp( providers[ii], &ctx[ii], bullx, &lenbx, &encrypted ) == 0 ) { sprintf( message, "Bulletin contained %s data which was%s encrypted", providers[ii], (encrypted == 0 ? " not" : "" ) ); dc_wclg ( 2, "DC", 2, message, &ier ); prev = 0; iforce = 0; nrepts = 0; for ( ibxptr = 0; ibxptr <= lenbx; ibxptr++ ) { curr = bullx[ibxptr]; if ( encrypted && ii == 1 ) { /* encrypted Earth Networks data */ ibxptr+=2; /* skip over first two bytes */ nfbytes = bullx[ibxptr++]; /* number of bytes in flash packet */ flash_type = bullx[ibxptr]; if ( ibxptr + 25 > lenbx ) { /* there's no more usable data in the bulletin */ ibxptr = lenbx + 1; sprintf( message, "Found message with %u reports\n", nrepts ); dc_wclg ( 2, "DC", 2, message, &ier ); } else if ( flash_type != 0 && flash_type != 1 ) { /* there's no usable data in the flash packet */ ibxptr += nfbytes; } else { /* The data values within the main part of the flash packet will be repeated in one of the pulse sub-packets, so we won't store anything from the main part of the flash packet. */ ibxptr += 6; /* skip next 6 bytes containing flash time */ ibxptr += 8; /* skip next 8 bytes containing flash lat/long */ ibxptr += 4; /* skip next 4 bytes containing flash peak current */ ibxptr++; /* skip next byte which is reserved for future use */ ibxptr += 2; /* skip next 2 bytes containing flash IC height */ ibxptr++; /* skip next byte */ npulse = bullx[++ibxptr]; ibxptr++; /* skip next byte */ for ( jj = 0; jj < npulse; jj++ ) { ibxptr++; /* skip next byte which is always set to 26 = length of pulse packet */ pulse_type = bullx[++ibxptr]; xdata[10] = ( pulse_type == 0 ? 8192 : ( pulse_type == 1 ? 4096 : 10E10 ) ); /* stroke type */ lt_gtim( BIG_ENDN, bullx, &ibxptr, &ibfdt, &xdata[0], &xdata[1], &xdata[2], &xdata[3], &xdata[4], &xdata[5] ); xdata[6] = lt_sw2c( BIG_ENDN, bullx, &ibxptr ) / 10000000; xdata[7] = lt_sw2c( BIG_ENDN, bullx, &ibxptr ) / 10000000; sprintf( message, "datetime: %d, %.2u mins, %f secs, lat %f, lon %f\n", ibfdt, (unsigned) xdata[4], xdata[5], xdata[6], xdata[7] ); dc_wclg ( 2, "DC", 2, message, &ier ); kamps = (int) lt_sw2c( BIG_ENDN, bullx, &ibxptr ); xdata[8] = abs( kamps ); /* amplitude of strike, in Amperes */ xdata[9] = ( kamps == 0 ? 0 : ( kamps > 0 ? 1 : 2 ) ); /* polarity of strike */ ibxptr++; /* skip next byte which is reserved for future use */ /* the next two bytes contain the height estimate for intercloud lightning, in meters */ ichght = lt_shrt( BIG_ENDN, bullx, &ibxptr ); ibxptr++; /* skip next byte */ xdata[11] = bullx[++ibxptr]; /* multiplicity (= number of flashes) */ ibxptr++; /* skip next byte */ xdata[12] = 16; xdata[13] = 10E10; openmb(&lunout, subtypee, &ibfdt, strlen(subtypee)); ufbint(&lunout, xdata, &narray, ×, &nret, hdrstr, strlen(hdrstr)); if ( ichght > 50 ) { /* don't store unreasonably low ichght values, including 0 which ENI uses to denote "missing" */ xdata[0] = ichght; ufbint(&lunout, xdata, &narray, ×, &nret, hdrstr2, strlen(hdrstr2)); } ut_wbfr(&lunout, mbstr, &iforce, &iret, strlen(mbstr)); } ibxptr++; nrepts += npulse; } } else if ( encrypted && curr == 0xFF ) { /* encrypted Vaisala data */ ibxptr++; /* skip over 2nd byte */ lt_gtim( LTL_ENDN, bullx, &ibxptr, &ibfdt, &xdata[0], &xdata[1], &xdata[2], &xdata[3], &xdata[4], &xdata[5] ); memcpy( &flat, &bullx[ibxptr+1], 4); ibxptr += 4; xdata[6] = flat; memcpy( &flon, &bullx[ibxptr+1], 4); ibxptr += 4; xdata[7] = flon; sprintf( message, "datetime: %d, %.2u mins, %f secs, lat %f, lon %f\n", ibfdt, (unsigned) xdata[4], xdata[5], xdata[6], xdata[7] ); dc_wclg ( 2, "DC", 2, message, &ier ); isbtyp = lt_shrt( LTL_ENDN, bullx, &ibxptr ); xdata[10] = ( bullx[ibxptr+1] == 0x00 ? 8192 : ( bullx[ibxptr+2] == 0x00 ? 4096 : 10E10 ) ); /* stroke type */ ibxptr += 2; memcpy( &kampss, &bullx[ibxptr+1], 2 ); ibxptr += 2; xdata[8] = abs( (int) kampss * 1000 ); /* amplitude of strike, convert from kiloAmperes to Amperes */ xdata[9] = ( kampss == 0 ? 0 : ( kampss > 0 ? 1 : 2 ) ); /* polarity of strike */ xdata[11] = lt_shrt( LTL_ENDN, bullx, &ibxptr ); /* number of flashes */ ibxptr += 8; /* skip rest of report for now */ xdata[12] = 16; xdata[13] = 10E10; if ( isbtyp < 1 || isbtyp > 2 ) { sprintf(message, "skipped report with isbtyp value of %d\n", isbtyp); dc_wclg ( 2, "DC", 2, message, &ier ); } else { if ( isbtyp == 1 ) { /* short range report */ openmb(&lunout, subtypes, &ibfdt, strlen(subtypes)); if ( strncmp( buhd, "SFPA", 4 ) == 0 ) dc_wclg ( 2, "DC", 2, "isbtyp = 1 in SFPA message", &ier ); } else { /* long range report */ openmb(&lunout, subtypel, &ibfdt, strlen(subtypel)); if ( strncmp( buhd, "SFUS", 4 ) == 0 ) dc_wclg ( 2, "DC", 2, "isbtyp = 2 in SFUS message", &ier ); } ufbint(&lunout, xdata, &narray, ×, &nret, hdrstr, strlen(hdrstr)); ut_wbfr(&lunout, mbstr, &iforce, &iret, strlen(mbstr)); } if ( bullx[ibxptr] == 0x03 ) { /* we've reached the end of the bulletin */ sprintf(message, "Found message with %d reports\n", (int) ( ibxptr / 32 ) + 1 ); /* each report is 32 bytes */ dc_wclg ( 2, "DC", 2, message, &ier ); } } else if ( !encrypted && curr == 0x96 ) { strikes = prev; sprintf(message, "Found message with %d reports\n", strikes); dc_wclg ( 2, "DC", 2, message, &ier ); min_sec = bullx[++ibxptr]; hrs_min = bullx[++ibxptr]; rel_date = lt_shrt( LTL_ENDN, bullx, &ibxptr ); secs = min_sec & 0x3F; mins = ((hrs_min & 0x0F) << 2) + ((min_sec & 0xC0) >> 6); hrs = ((rel_date & 0x01) << 4) + ((hrs_min & 0xF0) >> 4); rep_seconds = REF_DATE + ((rel_date & 0xFFFE) >> 1) * 86400 + hrs * 3600 + mins * 60 + secs; current = gmtime ( &rep_seconds ); year = (*current).tm_year + 1900; month = (*current).tm_mon + 1; day = (*current).tm_mday; valid_time = year*1000000 + month*10000 + day*100 + hrs ; openmb(&lunout, subtypes, &valid_time, strlen(subtypes)); for (strike = 0; strike < strikes; strike++) { int_lon = lt_shrt( LTL_ENDN, bullx, &ibxptr ); lon = ((double)int_lon) * 0.001068115234 - 130.0; int_lat = lt_shrt( LTL_ENDN, bullx, &ibxptr ); lat = (double) (int_lat & 0x7FFF); lat = lat * 0.001281738 + 18.0; curr = bullx[++ibxptr]; if (curr > 127) { kamps = -((curr - 128) * 2); } else { kamps = (curr * 2); } curr = bullx[++ibxptr]; tenths = (curr & 0x00F0) >> 4; multi = (curr & 0x000F); polarity = 1; if (kamps < 0 ) { kamps = -1 * kamps; polarity = 2; } if (kamps == 0 ) polarity = 0; bin_cloud = 8192; /* (2.)**(NBITS-IBIT) NBITS=18, IBIT=5 */ sconds = secs + tenths/10.0; xdata[0] = year; xdata[1] = month; xdata[2] = day; xdata[3] = hrs; xdata[4] = mins; xdata[5] = sconds; xdata[6] = lat; xdata[7] = lon; xdata[8] = kamps*1000; /*convert from kiloAmps to Amps */ xdata[9] = polarity; xdata[10] = bin_cloud; xdata[11] = multi; xdata[12] = 16; xdata[13] = 10E10; sprintf(message,"datetime: %.4u%.2u%.2u%.2u, %.2u mins, %f secs, lat %f, lon %f\n", year, month, day, hrs, (unsigned) xdata[4], xdata[5], xdata[6], xdata[7] ); dc_wclg ( 2, "DC", 2, message, &ier ); ufbint(&lunout, xdata, &narray, ×, &nret, hdrstr, strlen(hdrstr)); ut_wbfr(&lunout, mbstr, &iforce, &iret, strlen(mbstr)); } } else if (!encrypted && curr == 0x97) { strikes = prev; sprintf(message, "Found message with %d reports\n", strikes); dc_wclg ( 2, "DC", 2, message, &ier ); min_sec = bullx[++ibxptr]; hrs_min = bullx[++ibxptr]; rel_date = lt_shrt( LTL_ENDN, bullx, &ibxptr ); secs = min_sec & 0x3F; mins = ((hrs_min & 0x0F) << 2) + ((min_sec & 0xC0) >> 6); hrs = ((rel_date & 0x01) << 4) + ((hrs_min & 0xF0) >> 4); rep_seconds = REF_DATE + ((rel_date & 0xFFFE) >> 1) * 86400 + hrs * 3600 + mins * 60 + secs; current = gmtime ( &rep_seconds ); year = (*current).tm_year + 1900; month = (*current).tm_mon + 1; day = (*current).tm_mday; valid_time = year*1000000 + month*10000 + day*100 + hrs ; openmb(&lunout, subtypel, &valid_time, strlen(subtypel)); for (strike = 0; strike < strikes; strike++) { int_lon = lt_word( LTL_ENDN, bullx, &ibxptr ); /* First 8 bits of the word are the kamps */ kamps = (int_lon & 0xFF000000) >> 24; if (kamps > 127) { kamps = -((kamps - 128) * 2); } else { kamps = (kamps * 2); } /* There is an in_cloud indicator in the last bit */ /* HOWEVER, according to docs, we are _NOT_ to shift */ /* The longitude over a bit, it will just have a */ /* resolution down to the nearest 2 meters */ in_cloud = (int_lon & 0x00000001); int_lon = (int_lon & 0x00FFFFFE); lon = ((double)(int_lon) / (double)(TWO_24TH)) * 360 - 180; /* Now the latitude */ int_lat = lt_word( LTL_ENDN, bullx, &ibxptr ); /* The deciseconds are in the high order 4 bits */ tenths = (int_lat & 0xF0000000) >> 28; /* The multiplicity is in the next 4 bits */ multi = (int_lat & 0x0F000000) >> 24; /* There is a quality bit at bit 24, we will ignore it */ /* for now and just mask out the latitude */ int_lat = (int_lat & 0x007FFFFF); lat = ((double)(int_lat) / (double)(TWO_24TH)) * 360 - 90; polarity = 1; if (kamps < 0 ) { kamps = -1 * kamps; polarity = 2; } if (kamps == 0 ) polarity = 0; if (in_cloud) bin_cloud = 4096; /* (2.)**(NBITS-IBIT) NBITS=18, IBIT=6 */ else bin_cloud = 8192; /* (2.)**(NBITS-IBIT) NBITS=18, IBIT=5 */ sconds = secs + tenths/10.0; xdata[0] = year; xdata[1] = month; xdata[2] = day; xdata[3] = hrs; xdata[4] = mins; xdata[5] = sconds; xdata[6] = lat; xdata[7] = lon; xdata[8] = kamps*1000; /*convert from kiloAmps to Amps */ xdata[9] = polarity; xdata[10] = bin_cloud; xdata[11] = multi; xdata[12] = 16; xdata[13] = 10E10; sprintf(message,"datetime: %.4u%.2u%.2u%.2u, %.2u mins, %f secs, lat %f, lon %f\n", year, month, day, hrs, (unsigned) xdata[4], xdata[5], xdata[6], xdata[7] ); dc_wclg ( 2, "DC", 2, message, &ier ); ufbint(&lunout, xdata, &narray, ×, &nret, hdrstr, strlen(hdrstr)); ut_wbfr(&lunout, mbstr, &iforce, &iret, strlen(mbstr)); } } else if (curr >= 0) { prev = curr; } } /* Make sure all BUFR output for this bulletin has been flushed to disk before going back to wait for the next bulletin via dc_gbul */ iforce = 1; ut_wbfr(&lunout, mbstr, &iforce, &iret, strlen(mbstr)); } } } } }