bufr2synop 0.24.0
bufr2tac_utils.c
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2013-2018 by Guillermo Ballester Valor *
3 * gbv@ogimet.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20/*!
21 \file bufr2tac_utils.c
22 \brief file with the utility routines for binary bufr2tac
23*/
24#include "bufr2tac.h"
25
26/*!
27 \fn three_bytes_to_uint(const unsigned char *source)
28 \brief returns the integer value from an array of three bytes, most significant first
29*/
30unsigned int three_bytes_to_uint ( const unsigned char *source )
31{
32 return ( source[2] + source[1] * 256 + source[0] * 65536 );
33}
34
35/*!
36 \fn int integer_to_descriptor(struct bufr_descriptor *d, int id)
37 \brief parse an integer with a descriptor fom bufr ECWMF libary
38 \param d pointer to a struct \ref bufr_descriptor where to set the result on output
39 \param id integer with the descriptor from ewcwf
40*/
41int integer_to_descriptor ( struct bufr_descriptor *d, int id )
42{
43 if ( d == NULL )
44 return 1;
45 d->f = id / 100000;
46 d->x = ( id % 100000 ) / 1000;
47 d->y = id % 1000;
48 sprintf ( d->c, "%06d", id );
49 return 0;
50}
51
52/*!
53 \fn int descriptor_to_integer(int *id, struct bufr_descriptor *d)
54 \brief parse a descriptor and sets an integer in the decimal formas fxxyyy
55 \param id pointer to target integer
56 \param d pointer to a struct \ref bufr_descriptor with the source
57*/
58int descriptor_to_integer ( int *id, struct bufr_descriptor *d )
59{
60 if ( d == NULL )
61 return 1;
62 *id = 100000 * d->f + 1000 * d->x + d->y;
63 return 0;
64}
65
66uint32_t get_flag_value ( uint8_t width, uint8_t index )
67{
68 if ( width == index )
69 return ( uint32_t ) 1;
70 else if ( width < index )
71 return ( uint32_t ) 0;
72 else if ( width <= 32 )
73 return ( ( ( uint32_t ) 1 ) << ( width - index ) );
74 else
75 return ( uint32_t ) 0;
76}
77
78/*!
79 \fn char * charray_to_string(char *s, unsigned char *buf, size_t size)
80 \brief get a null termitated c-string from an array of unsigned chars
81 \param s resulting string
82 \param buf pointer to first element in array
83 \param size number of chars in array
84*/
85char * charray_to_string ( char *s, unsigned char *buf, size_t size )
86{
87 // copy
88 memcpy ( s , buf, size );
89 // add final string mark
90 s[size] = '\0';
91 return s;
92}
93
94/*!
95 \fn char * adjust_string(char *s)
96 \brief Supress trailing blanks of a string
97 \param s string to process
98*/
99char * adjust_string ( char *s )
100{
101 size_t l;
102 l = strlen ( s );
103 while ( l && s[--l] == ' ' )
104 s[l] = '\0';
105 return s;
106}
107
108/*! \fn int tokenize_string(char *tk[], size_t ntk, char *target, size_t len, char *blanks)
109 \brief Split the report string into tokens
110 \param tk array of strings. Memory have to be allocated previously by caller
111 \param ntk max amount of elements in tk array.
112 \param target string which is the report to split
113 \param len lenght of \a target string
114 \param blanks string with the character considered as blanks. If null
115 the default " =\n\r\v\t" is considered
116 Returns the number of tokens \a target has been splitted into.
117 Retuns 0 in case of a void or too large target.
118 NOTE: \a target is changed after calling this routine. In fact, the pointers
119 in \a tk[] are linking to some char in string \a target
120*/
121size_t tokenize_string ( char *tk[], size_t ntk, char *target, size_t len, char *blanks )
122{
123 size_t i = 1, n;
124 char d[] = " =\n\r\v\t", *b, *aux;
125
126 // set the blanks array
127 if ( blanks == NULL )
128 b = &d[0];
129 else
130 b = blanks;
131
132 // check target string limits
133 n = strlen ( target );
134 if ( n == 0 || n > len )
135 return 0;
136
137 // the split loop
138 tk[0] = strtok_r ( target, b, &aux );
139 while ( i < ntk &&
140 ( ( tk[i] = strtok_r ( NULL, b, &aux ) ) != NULL ) &&
141 strlen ( tk[i] ) > 2 &&
142 strlen ( tk[i] ) < 10 )
143 i++;
144 if ( i < ntk )
145 return i;
146 else
147 return ntk - 1;
148}
149
150/*!
151 \fn int YYYYMMDDHHmm_to_met_datetime(struct met_datetime *t, const char *source)
152 \brief Parse the string YYYYMMDDHHmm[ss] and set a struct \ref met_datetime
153 \param source string with date in YYYYMMDDHHmm[ss] format
154 \param t pointer to a struct \ref met_datetime where to set the results
155*/
156int YYYYMMDDHHmm_to_met_datetime ( struct met_datetime *t, const char *source )
157{
158 if ( strlen ( source ) != 12 && strlen ( source ) != 14 )
159 return 1;
160 memset ( &t->tim, 0, sizeof ( struct tm ) );
161 if ( strlen ( source ) == 12 )
162 {
163 strptime ( source, "%Y%m%d%H%M", &t->tim );
164 }
165 else
166 {
167 strptime ( source, "%Y%m%d%H%M%S", &t->tim );
168 }
169 strcpy ( t->datime, source );
170 t->t = mktime ( &t->tim );
171 return 0;
172}
173
174/*!
175 \fn char *met_datetime_to_YYGG (char *target, struct met_datetime *t)
176 \brief Get YYGG from a struct \ref met_datetime
177 \param target string with result as output
178 \param t pointer to source struct \ref met_datetime
179*/
180char *met_datetime_to_YYGG ( char *target, struct met_datetime *t )
181{
182 time_t tx;
183 struct tm tim;
184
185 tx = ( ( t->t + 1800 ) / 3600 ) * 3600 ; // rounding to next whole hour
186 memset ( &tim, 0, sizeof ( struct tm ) );
187 gmtime_r ( &tx, &tim );
188
189 strftime ( target, 8, "%d%H", &tim );
190
191 return target;
192}
193
194int round_met_datetime_to_hour ( struct met_datetime *target, struct met_datetime *source )
195{
196 target->t = ( ( source->t + 1800 ) / 3600 ) * 3600 ; // rounding to next whole hour
197 memset ( &target->tim, 0, sizeof ( struct tm ) );
198 gmtime_r ( &target->t, &target->tim );
199 strftime ( target->datime, 16, "%Y%m%d%H%M", &target->tim );
200 return 0;
201}
202
203/*!
204 \fn char *guess_WMO_region(char *A1, char *Reg, const char *II, const char *iii)
205 \brief get WMO region A1 and Reg items from II and iii (WMO index)
206 \param A1 string woth resulting A1
207 \param Reg string with resulting Reg
208 \param II WMO block number
209 \param iii WMO number
210*/
211char *guess_WMO_region ( char *A1, char *Reg, const char *II, const char *iii )
212{
213 char aux[8];
214
215 // filter bad inputs
216 if ( II == NULL || iii == NULL )
217 return NULL;
218
219 if ( II[0] == 0 || iii[0] == 0 )
220 return NULL;
221
222 // aux string
223 sprintf ( aux,"%s%s", II, iii );
224
225 if ( ( II[0] == '0' && ( strstr ( aux,"042" ) != aux ) && ( strstr ( aux,"043" ) != aux ) &&
226 ( strstr ( aux,"044" ) != aux ) && ( strstr ( aux,"0858" ) != aux ) && ( strstr ( aux,"0859" ) != aux ) ) ||
227 II[0] == '1' || ( strstr ( aux,"201" ) == aux ) ||
228 strcmp ( II,"22" ) == 0 || strcmp ( II,"26" ) == 0 || strcmp ( II,"27" ) == 0 ||
229 strcmp ( II,"33" ) == 0 || strcmp ( II,"34" ) == 0 || strcmp ( II,"22" ) == 0 ||
230 ( strstr ( aux,"201" ) == aux ) )
231 {
232 // Reg 6. Europe
233 A1[0] = '6';
234 strcpy ( Reg,"VI" );
235 }
236 else if ( II[0] == '6' || ( strstr ( aux,"0858" ) == aux ) || ( strstr ( aux,"0859" ) == aux ) )
237 {
238 // Reg 1. Africa
239 A1[0] = '1';
240 strcpy ( Reg,"I" );
241 }
242 else if ( II[0] == '5' || ( strcmp ( II,"49" ) == 0 ) || ( strcmp ( II,"21" ) == 0 ) ||
243 ( strcmp ( II,"23" ) == 0 ) || ( strcmp ( II,"24" ) == 0 ) || ( strcmp ( II,"25" ) == 0 ) ||
244 ( strcmp ( II,"28" ) == 0 ) || ( strcmp ( II,"29" ) == 0 ) || ( strcmp ( II,"30" ) == 0 ) ||
245 ( strcmp ( II,"31" ) == 0 ) || ( strcmp ( II,"32" ) == 0 ) || ( strcmp ( II,"38" ) == 0 ) ||
246 ( strcmp ( II,"35" ) == 0 ) || ( strcmp ( II,"36" ) == 0 ) || ( strcmp ( II,"39" ) == 0 ) ||
247 ( strcmp ( aux, "20200" ) >= 0 && strcmp ( aux, "20999" ) <= 0 ) ||
248 ( strcmp ( aux, "40000" ) >= 0 && strcmp ( aux, "48599" ) <= 0 ) ||
249 ( strcmp ( aux, "48800" ) >= 0 && strcmp ( aux, "49999" ) <= 0 ) )
250 {
251 // Reg 2. Asia
252 A1[0] = '2';
253 strcpy ( Reg,"II" );
254 }
255 else if ( strcmp ( aux, "80000" ) >= 0 && strcmp ( aux, "88999" ) <= 0 )
256 {
257 // Reg 3. South america
258 A1[0] = '3';
259 strcpy ( Reg,"III" );
260 }
261 else if ( II[0] == '7' || strstr ( aux,"042" ) == aux ||
262 strstr ( aux,"043" ) == aux || strstr ( aux,"044" ) == aux )
263 {
264 // Reg 4. North and central america
265 A1[0] = '4';
266 strcpy ( Reg,"IV" );
267 }
268 else if ( ( strcmp ( aux, "48600" ) >= 0 && strcmp ( aux, "48799" ) <= 0 ) ||
269 ( strcmp ( aux, "90000" ) >= 0 && strcmp ( aux, "98999" ) <= 0 ) )
270 {
271 // Reg 5. Pacific South
272 A1[0] = '5';
273 strcpy ( Reg,"V" );
274 }
275 else if ( strcmp ( II,"89" ) == 0 )
276 {
277 // Reg 0. Antarctica
278 A1[0] = '0';
279 strcpy ( Reg,"0" );
280 }
281 return A1;
282}
283
284/*!
285 \fn int check_date_from_future(struct metreport *m)
286 \brief Check a estructure \ref metreport not from future
287 \param m pointer to a struct metreport to check about date and time
288
289 It resturns 1 if date/time is from future, and likely wrong
290 Returns 0 otherwise
291*/
293{
294 time_t now;
295
296 now = time ( NULL );
297 if ( m->t.t > ( now + 1800 ) ) // Still 1/2 hour courtesy
298 return 1;
299 else
300 return 0;
301}
302
303
304/*!
305 \fn int guess_gts_header(struct gts_header *h, const char *f)
306 \brief Guess the WMO GTS header from filename
307
308 \param h pointer to a struct \ref gts_header where to set the results
309 \param f pathname of bufrfile
310
311 It returns 1 if guessed, 0 otherwise
312
313 <pre>
314 This routine assume the filename format of a bufr file is
315
316 YYYYMMDDHHmmss_BULLNN_ICAO_YYGGgg_ORD.bufr
317
318 where
319
320 YYYYMMDDHHmmss
321
322 is the timestamp (UTC) of the file in GTS (as example, in NOAA gateway)
323
324 BULLNN is the bulletin identifier in GTS. It is a six chars lenght in the form T1 T2 A1 A2 N1 N2
325
326 For observations, T1 is 'I'
327 Possible values of T2 for T1 = 'I' are
328 O Oceanographic/limnographic (water properties)
329 P Pictorial
330 S Surface/sea level
331 T Text (plain language information)
332 U Upper air
333 X Other data types
334 Z Mixed data types
335
336
337 For T1T2 = 'IS' Then A1:
338
339 IS A 01–29 Routinely scheduled observations for n/a 000/006
340 distribution from automatic (fixed or mobile)
341 land stations (e.g. 0000, 0100, … or 0220, 0240,
342 0300, …, or 0715, 0745, ... UTC)
343 IS A 30–59 N-minute observations from automatic (fixed n/a 000/007
344 or mobile) land stations
345 IS B Radar reports (parts A and B) RADOB 006/003
346 IS C 01–45 Climatic observations from land stations CLIMAT 000/020
347 IS C 46–59 Climatic observations from marine stations CLIMAT SHIP 001/0202009 edition
348 ATTACHmENT II-5 I
349 IS D Radiological observation RADREP 010/001
350 IS E Ozone measurement at surface n/a 008/000
351 IS F Source of atmospherics SFAZI, SFLOC, SFAZU 000/030
352 IS I 01–45 Intermediate synoptic observations from fixed SYNOP (SIxx) 000/001
353 land stations 000/051
354 IS I 46–59 Intermediate synoptic observations from mobile SYNOP mOBIL 000/004
355 land stations
356 IS M 01–45 Main synoptic observations from fixed land SYNOP (SMxx) 000/002
357 stations 000/052
358 IS M 46–59 Main synoptic observations from mobile land SYNOP mOBIL 000/005
359 stations
360 IS N 01–45 Synoptic observations from fixed land stations SYNOP (SNxx) 000/000
361 at non-standard time (i.e. 0100, 0200, 0400, 000/050
362 0500, ... UTC)
363 IS N 46–59 Synoptic observations from mobile land stations SYNOP mOBIL 000/003
364 at non-standard time (i.e. 0100, 0200, 0400,
365 0500, ... UTC)
366 IS R Hydrologic reports HYDRA 000/040
367 IS S 01–19 Synoptic observations from marine stations SHIP 001/000
368 IS S 20–39 One-hour observations from automatic marine n/a 001/006
369 stations
370 IS S 40–59 N-minute observations from automatic marine n/a 001/007
371 stations
372 IS T 01–19 Tide gauge observations n/a 001/030
373 IS T 20–39 Observed water level time series n/a 001/031
374 IS V Special aeronautical observations (SPECI) SPECI 000/011
375 IS W Aviation routine weather observations (mETAR) mETAR 000/010
376 IS X Other surface data IAC, IAC FLEET
377
378 For T1T2 = 'IU' then A1:
379 IU A Single level aircraft reports (automatic) AmDAR 004/000
380 IU A Single level aircraft reports (manual) AIREP/PIREP 004/001
381 IU B Single level balloon reports n/a
382 IU C (used for single level satellite-derived SAREP/SATOB 005/000
383 reports – see Note 3)
384 IU D Dropsonde/Dropwindsondes TEmP DROP 002/007
385 IU E Ozone vertical sounding n/a 008/001
386 IU I Dispersal and transport analysis n/a 009/000
387 IU J 01–19 Upper wind from fixed land stations (entire PILOT (parts A, 002/001
388 sounding) B, C, D)
389 IU J 20–39 Upper wind from mobile land stations (entire PILOT mOBIL 002/003
390 sounding) (parts A, B, C, D)
391 IU J 40–59 Upper wind from marine stations (entire PILOT SHIP 002/002
392 sounding) (parts A, B, C, D)
393 IU K 01–19 Radio soundings from fixed land stations TEmP (parts A, B) 002/004
394 (up to 100 hPa)
395 IU K 20–39 Radio soundings from mobile land stations TEmP mOBIL 002/006
396 (up to 100 hPa) (parts A, B)
397 IU K 40–59 Radio soundings from marine stations TEmP SHIP (parts A, B) 002/005
398 (up to 100 hPa)
399 IU M Model derived sondes
400 IU N Rocketsondes
401 IU O Profiles of aircraft observations in ascending/ AmDAR 002/020
402 descending
403 IU P Profilers PILOT 002/010
404 IU Q RASS temperature profilers TEmP 002/011
405 IU R (used for radiance data – see Note 3)
406 IU S 01–19 Radiosondes/pibal reports from fixed land TEmP (parts A, B, C, D) 002/004
407 stations (entire sounding)
408 IU S 20–39 Radio soundings from mobile land stations TEmP mOBIL (parts A, 002/006
409 (entire sounding) B, C, D)
410 IU S 40–59 Radio soundings from marine stations TEmP SHIP (parts A, 002/005
411 (entire sounding) B, C, D)
412 IU T (used for satellite-derived sondes – see Note 3) SATEm, SARAD, SATOB
413 IU U 01–45 Monthly statistics of data from upper-air stations CLImAT TEmP 002/025
414 IU U 46–59 Monthly statistics of data from marine stations CLImAT TEmP, SHIP 002/026
415 IU W 01–19 Upper wind from fixed land stations (up to PILOT (parts A, B) 002/001
416 100 hPa)
417 IU W 20–39 Upper wind from mobile land stations (up to PILOT mOBIL 002/003
418 100 hPa) (parts A, B)
419 IU W 40–59 Upper wind from marine stations (up to PILOT SHIP 002/002
420 100 hPa) (parts A, B)
421 IU X Other upper-air reports
422
423 for T1T2 = 'IO' then A1:
424 IO B Buoy observations BUOY 001/025
425 IO I Sea ice
426 IO P Sub-surface profiling floats TESAC 031/004
427 IO R Sea surface observations TRACKOB 031/001
428 IO S Sea surface and below soundings BATHY, TESAC 031/005
429 IO T Sea surface temperature
430 IO W Sea surface waves WAVEOB 031/002
431 IO X Other sea environmental
432
433 About A2
434 A 0 - 90W northern hemisphere
435 B 90W - 180 northern hemisphere
436 C 180 - 90E northern hemisphere
437 D 90E - 0 northern hemisphere
438 E 0 - 90W tropical belt
439 F 90W - 180 tropical belt
440 G 180 - 90E tropical belt
441 H 90E - 0 tropical belt
442 I 0 - 90W southern hemisphere
443 J 90W - 180 southern hemisphere
444 K 180 - 90E southern hemisphere
445 L 90E - 0 southern hemisphere
446 M Lower left 10S 100E/upper right 70N 110W
447 N Northern hemisphere
448 P Area between 64.69N - 136.76W, 55.61N - 13.43W
449 64.69N - 156.76W, 55.61N - 33.43W
450 S Southern hemisphere
451 T 45W - 180 northern hemisphere
452 U Area between 21.0N - 128.1W, 36.0N - 130.9W
453 21.1N - 113.0W, 36.2N - 110.5W
454 V Area between 30.3N - 83.7W, 51.0N - 68.9W
455 19.8N - 64.5W, 33.3N - 47.1W
456 X Global Area (area not definable)
457
458
459 ICAO is the ICAO index for release center.
460
461 YYGGgg is the nominal release time (day, hour, minute) UTC
462
463 ORD is the order sequence as RRA, RRB, CCA .... is optional
464 </pre>
465*/
466int guess_gts_header ( struct gts_header *h, const char *f )
467{
468 size_t nt;
469 char aux[256], *tk[16], *c;
470
471 // parse file name
472 strcpy ( h->filename, f );
473 strcpy ( aux, f );
474
475 // check latest '/' in filename
476 if ( ( c = strrchr ( aux,'/' ) ) == NULL )
477 c = &aux[0];
478 else
479 c++;
480
481 nt = tokenize_string ( tk, 16, c, strlen ( c ), "_. " );
482 // parse filenames with format as example 'AAAAMMDDHHmmss_ISIE06_SBBR_012100_RRB.bufr'
483
484 // 5 or 6 items
485 if ( nt < 5 || nt > 6 )
486 return 0;
487
488 // extension bufr
489 if ( strcmp ( tk[nt - 1],"bufr" ) )
490 return 0;
491
492 // item 0 the timestamp of file in NOAA GTS gateway
493 if ( strlen ( tk[0] ) != 14 || strspn ( tk[0], "0123456789" ) != 14 )
494 return 0;
495 strcpy ( h->timestamp, tk[0] );
496
497 // item 1
498 if ( strlen ( tk[1] ) != 6 || strspn ( &tk[1][4],"0123456789" ) != 2 )
499 return 0;
500 strcpy ( h->bname, tk[1] );
501
502 // item 2
503 if ( strlen ( tk[2] ) != 4 )
504 return 0;
505 strcpy ( h->center, tk[2] );
506
507 if ( strlen ( tk[3] ) != 6 || strspn ( tk[3], "0123456789" ) != 6 )
508 return 0;
509 strcpy ( h->dtrel, tk[3] );
510
511 if ( nt == 5 )
512 {
513 strcpy ( h->order, "BBB" );
514 }
515 else
516 {
517 if ( strlen ( tk[4] ) == 3 )
518 strcpy ( h->order, tk[4] );
519 else
520 return 0;
521 }
522
523 return 1;
524}
525
Include header file for binary bufr2tac.
char * met_datetime_to_YYGG(char *target, struct met_datetime *t)
Get YYGG from a struct met_datetime.
int check_date_from_future(struct metreport *m)
Check a estructure metreport not from future.
int guess_gts_header(struct gts_header *h, const char *f)
Guess the WMO GTS header from filename.
char * guess_WMO_region(char *A1, char *Reg, const char *II, const char *iii)
get WMO region A1 and Reg items from II and iii (WMO index)
int integer_to_descriptor(struct bufr_descriptor *d, int id)
parse an integer with a descriptor fom bufr ECWMF libary
size_t tokenize_string(char *tk[], size_t ntk, char *target, size_t len, char *blanks)
Split the report string into tokens.
char * charray_to_string(char *s, unsigned char *buf, size_t size)
get a null termitated c-string from an array of unsigned chars
uint32_t get_flag_value(uint8_t width, uint8_t index)
int YYYYMMDDHHmm_to_met_datetime(struct met_datetime *t, const char *source)
Parse the string YYYYMMDDHHmm[ss] and set a struct met_datetime.
int round_met_datetime_to_hour(struct met_datetime *target, struct met_datetime *source)
unsigned int three_bytes_to_uint(const unsigned char *source)
returns the integer value from an array of three bytes, most significant first
int descriptor_to_integer(int *id, struct bufr_descriptor *d)
parse a descriptor and sets an integer in the decimal formas fxxyyy
char * adjust_string(char *s)
Supress trailing blanks of a string.
BUFR descriptor.
Definition: bufrdeco.h:409
char c[12]
Definition: bufrdeco.h:414
stores WMO GTS header info
Definition: bufrdeco.h:707
char center[8]
Definition: bufrdeco.h:709
char dtrel[16]
Definition: bufrdeco.h:710
char order[8]
Definition: bufrdeco.h:711
char bname[16]
Definition: bufrdeco.h:708
char filename[BUFRDECO_PATH_LENGTH]
Definition: bufrdeco.h:712
char timestamp[16]
Definition: bufrdeco.h:713
stores date and time reference of a report, commonly the observation time
Definition: metcommon.h:60
char datime[16]
Definition: metcommon.h:63
struct tm tim
Definition: metcommon.h:62
time_t t
Definition: metcommon.h:61
all the information for a meteorological report in WMO text format from a BUFR file
Definition: bufr2tac.h:309
struct met_datetime t
Definition: bufr2tac.h:314