RCube
Rcube Rest Server calculates sail routes based on Grib files and sailing boat polar files
Loading...
Searching...
No Matches
readgriballwithouteccodes.c
Go to the documentation of this file.
1#include <stdio.h>
2#include <stdlib.h>
3#include <stdint.h>
4#include <stdbool.h>
5#include <string.h>
6#include <math.h>
7#include <sys/stat.h>
8
9#include "../csources/glibwrapper.h"
10#include "../csources/r3types.h"
11#include "../csources/r3util.h"
12#include "../csources/inline.h"
13
14FlowP *tGribData [2] = {NULL, NULL}; // wind, current
15
36#ifndef GRIB_DEBUG
37#define GRIB_DEBUG 0 /* set to 0 to disable diagnostics */
38#endif
39
41char *gribReaderVersion (char *str, size_t maxLen) {
42 strlcpy (str, "In house", maxLen);
43 return str;
44}
45
46// ========================== Big-endian readers ===============================
47static inline uint8_t rdU8(const uint8_t *p) { return p[0]; }
48static inline uint16_t rdU16BE(const uint8_t *p){ return (uint16_t)((p[0]<<8)|p[1]); }
49static inline uint32_t rdU32BE(const uint8_t *p){ return (uint32_t)((p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]); }
50static inline int32_t rdI32BE(const uint8_t *p){ return (int32_t)rdU32BE(p); }
51static inline uint64_t rdU64BE(const uint8_t *p){ return ((uint64_t)rdU32BE(p)<<32) | (uint64_t)rdU32BE(p+4); }
52
53// Remplace la lecture brute int16 par une lecture "flexible":
54// - si MSB=1 et la magnitude est raisonnable (≤1000), on traite en sign-magnitude
55// - sinon on garde le complément-à-deux classique
56static int16_t readS16beFlexible(const uint8_t *p){
57 uint16_t u = rdU16BE(p);
58 int16_t t2 = (int16_t)u; // interprétation "complément à deux"
59 if (u & 0x8000) {
60 int16_t sm = -(int16_t)(u & 0x7FFF); // interprétation "sign-magnitude"
61 // Les facteurs d'échelle réels sont petits ([-100..100] typiquement).
62 // Si le "sm" est raisonnable, on le préfère.
63 if (sm >= -1000 && sm <= 1000) return sm;
64 }
65 return t2;
66}
67
68static inline float rdIEEE32BE(const uint8_t *p){
69 uint32_t u = rdU32BE(p);
70 float f; memcpy(&f, &u, sizeof(float));
71 return f;
72}
73
74// ============================= Bit reader (S7) ===============================
75typedef struct {
76 const uint8_t *buf;
77 size_t len; // bytes
78 size_t bitpos; // next bit index
79} BitReader;
80
81static void bitInit(BitReader *br, const uint8_t *buf, size_t len){
82 br->buf = buf; br->len = len; br->bitpos = 0;
83}
84static uint32_t bitGet(BitReader *br, int nbits){
85 uint32_t v = 0;
86 for(int i=0;i<nbits;i++){
87 size_t byte = (br->bitpos >> 3);
88 int offs = 7 - (int)(br->bitpos & 7);
89 br->bitpos++;
90 if(byte >= br->len) return v;
91 uint8_t b = br->buf[byte];
92 v = (v<<1) | ((b>>offs)&1u);
93 }
94 return v;
95}
96
97#if GRIB_DEBUG
98static void debugPeekS7Integers(const uint8_t *s7, size_t s7Len, int nbits, int howMany){
99 if(!s7 || s7Len==0 || nbits<=0) return;
100 BitReader br; bitInit(&br, s7, s7Len);
101 fprintf(stderr, "[S7] first %d raw ints (nbits=%d):", howMany, nbits);
102 for(int i=0;i<howMany;i++){
103 uint32_t x = bitGet(&br, nbits);
104 fprintf(stderr, " %u", (unsigned)x);
105 }
106 fprintf(stderr, "\n");
107}
108#endif
109
110// ============================ Utilities (small) ==============================
111static void strCopySafe(char *dst, size_t cap, const char *src){
112 if(cap==0){ return; }
113 if(!src){ dst[0]=0; return; }
114 strncpy(dst, src, cap-1);
115 dst[cap-1] = 0;
116}
117
118static size_t updateLongUnique(long value, size_t n, size_t maxN, long arr[]){
119 for(size_t i=0;i<n;i++) if(arr[i]==value) return n;
120 if(n < maxN){ arr[n] = value; return n+1; }
121 return n;
122}
123
124static void sortLongAsc(long *a, size_t n){
125 for(size_t i=1;i<n;i++){
126 long key=a[i]; size_t j=i;
127 while(j>0 && a[j-1] > key){ a[j]=a[j-1]; j--; }
128 a[j]=key;
129 }
130}
131
132static const uint8_t* findNextGrib(const uint8_t *p, const uint8_t *end){
133 while(p + 16 <= end){
134 if(p[0]=='G' && p[1]=='R' && p[2]=='I' && p[3]=='B') return p;
135 p++;
136 }
137 return NULL;
138}
139
140static bool isCurrentTriplet(int disc, int cat, int par, bool *isU){
141 if (disc != 10) return false; // Oceanography
142 if (!(cat==1 || cat==2 || cat==0)) return false; // centres variés: 1/2 (voire 0)
143
144 switch(par){
145 // U components: WMO U(2), eastward(5), locaux 192/194
146 case 2: case 5: case 192: case 194:
147 if (isU) *isU = true;
148 return true;
149 // V components: WMO V(3), northward(6), locaux 193/195
150 case 3: case 6: case 193: case 195:
151 if (isU) *isU = false;
152 return true;
153 default: return false;
154 }
155}
156
157// ======================== ShortName mapping (minimal) ========================
158// Discipline 0 = meteorological, 10 = oceanographic (WW3)
159static const char* shortNameFor(int disc, int cat, int par){
160 if(disc==0 && cat==2 && par==2) return "10u";
161 if(disc==0 && cat==2 && par==3) return "10v";
162 if(disc==0 && cat==2 && par==22) return "gust";
163 if(disc==10 && cat==0 && (par==3 || par==5 || par==8)) return "swh";
164
165 bool isU=false;
166 if (isCurrentTriplet(disc,cat,par,&isU)) return isU ? "ucurr" : "vcurr";
167
168 return "unknown";
169}
170
171// =========================== Time units -> hours =============================
172static int unitToHours(uint8_t unit, int32_t val){
173 switch(unit){
174 case 13: return val / 3600; // seconds
175 case 0: return val / 60; // minutes
176 case 1: return val; // hours
177 case 10: return val * 3; // 3 hours
178 case 11: return val * 6; // 6 hours
179 case 12: return val * 12; // 12 hours
180 case 2: return val * 24; // days
181 case 3: return val * 24 * 30; // months approx
182 case 4: return val * 24 * 365; // years approx
183 default: return -1;
184 }
185}
186
187static int scoreHours(int h){
188 if(h < 0) return -1000000;
189 int sc=0;
190 if(h <= 384) sc += 10; else if(h <= 1000) sc += 5; else sc -= 5;
191 if((h % 3)==0) sc++;
192 if((h % 6)==0) sc++;
193 if((h % 12)==0) sc++;
194 return sc;
195}
196
197// ================= PDT lead time (robust; tuned for GFS PDT 4.x) =============
198/* PDT pointer `pdt` starts at octet 10 of Section 4 (WMO numbering).
199 For PDT 4.0/4.1/4.2 (common style):
200 - indicator of unit of time range -> pdt[8]
201 - forecast time in units (signed) -> pdt[9..12]
202 Fallback: pdt[18], pdt[19..22].
203 Final fallback: short scan on first ~64 bytes, accepting 0..384 h.
204*/
205static int pdtLeadHours(int pdtn, const uint8_t *pdt, uint32_t pdtLen){
206 if(!pdt || pdtLen < 12) return -1;
207
208 { /* Primary: pdt[8] / pdt[9..12] */
209 uint8_t unit = pdt[8];
210 int32_t val = (int32_t)((pdt[9]<<24)|(pdt[10]<<16)|(pdt[11]<<8)|pdt[12]);
211 int h = unitToHours(unit, val);
212 if(h >= 0) return h;
213 }
214
215 if(pdtLen >= 23){ /* Fallback: pdt[18]/pdt[19..22] */
216 uint8_t unit = pdt[18];
217 int32_t val = (int32_t)((pdt[19]<<24)|(pdt[20]<<16)|(pdt[21]<<8)|pdt[22]);
218 int h = unitToHours(unit, val);
219 if(h >= 0) return h;
220 }
221
222 /* Short conservative scan */
223 int best=-1, bestScore=-1000000;
224 int limit = (pdtLen > 64) ? 64 : (int)pdtLen;
225 for(int uPos=0; uPos+5<=limit; uPos++){
226 uint8_t unit = pdt[uPos];
227 if(!(unit==0||unit==1||unit==2||unit==3||unit==4||unit==10||unit==11||unit==12||unit==13)) continue;
228 int32_t val = (int32_t)((pdt[uPos+1]<<24)|(pdt[uPos+2]<<16)|(pdt[uPos+3]<<8)|pdt[uPos+4]);
229 int h = unitToHours(unit, val);
230 if(h < 0 || h > 384) continue;
231 int sc = scoreHours(h);
232 if(sc > bestScore || (sc==bestScore && (best<0 || h<best))){
233 bestScore = sc; best = h;
234 }
235 }
236 (void)pdtn;
237 return best;
238}
239
240// =============================== Section 1 ==================================
241typedef struct {
242 int centerId, subcenterId, tableVersion;
243 int refYear, refMonth, refDay, refHour, refMinute, refSecond;
244} Sec1Info;
245
246static bool parseSec1(const uint8_t *s, uint32_t bodyLen, Sec1Info *out){
247 if(!s || bodyLen < 16 || !out) return false;
248 Sec1Info x = {0};
249 x.centerId = (int)rdU16BE(s+0);
250 x.subcenterId = (int)rdU16BE(s+2);
251 x.tableVersion = (int)rdU8(s+4);
252 x.refYear = (int)rdU16BE(s+7);
253 x.refMonth = (int)rdU8(s+9);
254 x.refDay = (int)rdU8(s+10);
255 x.refHour = (int)rdU8(s+11);
256 x.refMinute = (int)rdU8(s+12);
257 x.refSecond = (int)rdU8(s+13);
258 *out = x;
259 return true;
260}
261
262// =============================== Section 3 ==================================
263typedef struct {
264 int Ni, Nj;
265 double lat1, lon1; // degrees
266 double di, dj; // degrees (signs from La2/Lo2)
267 int scanFlags; // for the S7 order only
268} Sec3Grid;
269
270/* Parse GDT 3.0 (regular lat/lon) */
271static bool parseSec3LatLon(const uint8_t *s, uint32_t bodyLen, Sec3Grid *out){
272 if(!s || bodyLen < 9+58 || !out) return false;
273
274 int gdt = (int)rdU16BE(s+7);
275 if(gdt != 0) return false; // only 3.0 supported
276
277 const uint8_t *g = s+9;
278
279 int Ni = (int)rdU32BE(g+16);
280 int Nj = (int)rdU32BE(g+20);
281 uint32_t basicAngle = rdU32BE(g+24);
282 uint32_t subAngle = rdU32BE(g+28);
283
284 int32_t la1i = (int32_t)rdU32BE(g+32);
285 uint32_t lo1u = rdU32BE(g+36);
286 int32_t la2i = (int32_t)rdU32BE(g+41);
287 uint32_t lo2u = rdU32BE(g+45);
288
289 uint32_t DiU = rdU32BE(g+49);
290 uint32_t DjU = rdU32BE(g+53);
291 int scan = (int)rdU8(g+57);
292
293 if(Ni<=0 || Nj<=0) return false;
294 if(DiU==0xFFFFFFFFu || DjU==0xFFFFFFFFu) return false; // quasi-regular not supported
295
296 double unit = 1e-6;
297 if(basicAngle!=0 && subAngle!=0 && subAngle!=0xFFFFFFFFu){
298 unit = (double)basicAngle / (double)subAngle;
299 }
300
301 double lat1 = (double)la1i * unit;
302 double lon1 = (double)lo1u * unit;
303 double lat2 = (double)la2i * unit;
304 double lon2 = (double)lo2u * unit;
305
306 // Amplitudes positives
307 double diAbs = (double)DiU * unit;
308 double djAbs = (double)DjU * unit;
309
310 // dj sign from (lat2 - lat1)
311 double dj = (lat2 >= lat1) ? djAbs : -djAbs;
312
313 // di sign from shortest path lon2-lon1 (wrap around)
314 double dLon = lon2 - lon1;
315 if (dLon > 180.0) dLon -= 360.0;
316 else if(dLon < -180.0) dLon += 360.0;
317 double di = (dLon >= 0.0) ? diAbs : -diAbs;
318
319 // Repair La1 if producer wrote junk but (lat2,Nj,dj) consistent
320 if(!(fabs(lat1) <= 90.0) && fabs(lat2) <= 90.0 && Nj>1){
321 lat1 = lat2 - (double)(Nj-1) * dj;
322 }
323
324 out->Ni = Ni;
325 out->Nj = Nj;
326 out->lat1 = lat1;
327 out->lon1 = lon1;
328 out->di = di;
329 out->dj = dj;
330 out->scanFlags = scan;
331 return true;
332}
333
334// ---- Scan flags helpers (WMO): bit=1 means negative direction for i/j ------
335static inline bool scanIPlus(int scan){ return ( (scan & 0x80) == 0 ); } // 0 => +i
336static inline bool scanJPlus(int scan){ return ( (scan & 0x40) == 0 ); } // 0 => +j
337static inline bool scanAdjI (int scan){ return ( (scan & 0x20) == 0 ); } // 0 => adjacent along i
338static inline bool scanBoustro(int scan){ return ( (scan & 0x10) != 0 ); } // 1 => boustrophedon
339
340// =============================== Section 4 ==================================
341typedef struct {
342 int pdtn; // Product Definition Template Number (4.N)
343 const uint8_t *pdt; // pointer to PDT body (after pdtn)
344 uint32_t pdtLen;
345 int discipline, category, parameter;
346} Sec4Info;
347
348static bool parseSec4(const uint8_t *s, uint32_t bodyLen, int discipline, Sec4Info *out){
349 if(!s || bodyLen < 6 || !out) return false;
350 Sec4Info x;
351 x.pdtn = (int)rdU16BE(s+2);
352 x.pdt = s+4; /* pdt[0] == octet 10 of section 4 */
353 x.pdtLen = bodyLen - 4;
354 x.discipline = discipline;
355 x.category = (int)rdU8(x.pdt+0);
356 x.parameter = (int)rdU8(x.pdt+1);
357 *out = x;
358 return true;
359}
360
361// =============================== Section 5 ==================================
362// DRT 5.0 (simple packing) and 5.3 (IEEE float)
363typedef struct {
364 int drt; // 0: simple packing, 3: IEEE float, else: unsupported
365 float refV;
366 int16_t bScale;
367 int16_t dScale;
368 int nBits;
369} Sec5Info;
370
371static bool parseSec5(const uint8_t *s, uint32_t bodyLen, Sec5Info *out){
372 if(!s || bodyLen < 5 || !out) return false;
373 int drt = (int)rdU16BE(s+4);
374 if(drt == 0){
375 if(bodyLen < 16) return false;
376 Sec5Info d;
377 d.drt = 0;
378 d.refV = rdIEEE32BE(s+6);
379
380 d.bScale = readS16beFlexible(s+10);
381 d.dScale = readS16beFlexible(s+12);
382
383 d.nBits = (int)rdU8(s+14);
384 *out = d;
385 return true;
386 } else if(drt == 3){
387 Sec5Info d = {0};
388 d.drt = 3; // IEEE floats in Section 7
389 *out = d;
390 return true;
391 } else {
392 return false; // unsupported (e.g. 5.2, 5.40/41)
393 }
394}
395
396// =============================== Section 6 ==================================
397typedef struct {
398 int indicator; // 255: none, 0: bitmap here, 254: previous (unsupported)
399 const uint8_t *bitmap; // when indicator==0
400 uint32_t bitmapLen;
401} Sec6Info;
402
403// ============================== Message parse ===============================
404typedef struct {
410 const uint8_t *sec7; uint32_t sec7Len;
411 int haveS1, haveS3, haveS4, haveS5, haveS6, haveS7;
412} MsgParse;
413
414static int bmBitAt(const uint8_t *bm, size_t bmBytes, size_t k){
415 if(!bm) return 1; // no bitmap => all present
416 size_t byte = k >> 3;
417 if(byte >= bmBytes) return 1; // safety
418 int bit = 7 - (int)(k & 7);
419 return ( (bm[byte] >> bit) & 1u );
420}
421
422static int parseGrib2Message(const uint8_t *buf, size_t len, int wantData,
423 MsgParse *out, float **outValues)
424{
425 if(len < 16 || memcmp(buf,"GRIB",4)!=0) return -1;
426 if(buf[7] != 2) return -1;
427 uint64_t totalLen = rdU64BE(buf+8);
428 if(totalLen > len || totalLen < 16) return -1;
429
430 MsgParse mp; memset(&mp, 0, sizeof(mp));
431 size_t off = 16;
432
433 while(off + 5 <= totalLen){
434 uint32_t sLen = rdU32BE(buf+off);
435 if(sLen < 5 || off + sLen > totalLen) break;
436 uint8_t sNum = rdU8(buf+off+4);
437 const uint8_t *s = buf + off + 5;
438 uint32_t bodyLen = sLen - 5;
439
440 if(sNum == 1){
441 if(parseSec1(s, bodyLen, &mp.s1)) mp.haveS1 = 1;
442 }
443 else if(sNum == 3){
444 if(parseSec3LatLon(s, bodyLen, &mp.s3)) mp.haveS3 = 1;
445 }
446 else if(sNum == 4){
447 int disc = (int)rdU8(buf+6);
448 if(parseSec4(s, bodyLen, disc, &mp.s4)) mp.haveS4 = 1;
449 }
450 else if(sNum == 5){
451 if(parseSec5(s, bodyLen, &mp.s5)) mp.haveS5 = 1;
452 }
453 else if(sNum == 6){
454 if(bodyLen < 1) return -1;
455 mp.s6.indicator = (int)rdU8(s+0);
456 if(mp.s6.indicator == 0){
457 mp.s6.bitmap = s + 1;
458 mp.s6.bitmapLen = bodyLen - 1;
459 mp.haveS6 = 1;
460 } else if(mp.s6.indicator == 255){
461 mp.s6.bitmap = NULL; mp.s6.bitmapLen = 0; mp.haveS6 = 1;
462 } else if(mp.s6.indicator == 254){
463 return -1; // "previous bitmap" not supported
464 } else {
465 return -1;
466 }
467 }
468 else if(sNum == 7){
469 mp.sec7 = s; mp.sec7Len = bodyLen; mp.haveS7 = 1;
470 }
471
472 off += sLen;
473 }
474
475#if GRIB_DEBUG
476 if(mp.haveS3){
477 fprintf(stderr, "[S3] GDT=3.0 Ni=%d Nj=%d lat1=%g lon1=%g di=%g dj=%g scan=0x%02X\n",
478 mp.s3.Ni, mp.s3.Nj, mp.s3.lat1, mp.s3.lon1, mp.s3.di, mp.s3.dj, mp.s3.scanFlags);
479 }
480 if(mp.haveS4 && mp.haveS5){
481 const char *sn = shortNameFor(rdU8(buf+6), mp.s4.category, mp.s4.parameter);
482 fprintf(stderr, "[S5] var=%s drt=%d R=%g E=%d D=%d nbits=%d (sec7=%u bytes)\n",
483 sn, mp.s5.drt, (double)mp.s5.refV, (int)mp.s5.bScale,
484 (int)mp.s5.dScale, (int)mp.s5.nBits, (unsigned)mp.sec7Len);
485 if(mp.s5.drt==0 && mp.haveS7) debugPeekS7Integers(mp.sec7, mp.sec7Len, mp.s5.nBits, 12);
486 }
487#endif
488
489 if(out) *out = mp;
490 if(!wantData) { if(outValues) *outValues = NULL; return 0; }
491
492 /* Need sections 3,5,7; section 6 optional (bitmap) */
493 if(!(mp.haveS3 && mp.haveS5 && mp.haveS7)) return -1;
494
495 int Ni = mp.s3.Ni, Nj = mp.s3.Nj;
496 size_t nPts = (size_t)Ni * (size_t)Nj;
497
498 float *vals = (float*)malloc(nPts * sizeof(float));
499 if(!vals) return -1;
500
501 const uint8_t *bm = (mp.haveS6 && mp.s6.indicator==0) ? mp.s6.bitmap : NULL;
502 size_t bmBytes = (mp.haveS6 && mp.s6.indicator==0) ? mp.s6.bitmapLen : 0;
503
504 if(mp.s5.drt == 0){
505 /* Simple packing (5.0) with optional bitmap */
506 BitReader br; bitInit(&br, mp.sec7, mp.sec7Len);
507 const double twoE = ldexp(1.0, mp.s5.bScale);
508 const double dec = pow(10.0, -(double)mp.s5.dScale);
509
510 if(mp.s5.nBits == 0){
511 float v = (float)(mp.s5.refV * dec);
512 for(size_t k=0;k<nPts;k++){
513 vals[k] = bmBitAt(bm, bmBytes, k) ? v : NAN;
514 }
515 } else {
516 for(size_t k=0;k<nPts;k++){
517 if(bmBitAt(bm, bmBytes, k)){
518 uint32_t D = bitGet(&br, mp.s5.nBits);
519 double X = mp.s5.refV + (double)D * twoE;
520 vals[k] = (float)(X * dec);
521 } else {
522 vals[k] = NAN;
523 }
524 }
525 }
526 }
527 else if(mp.s5.drt == 3){
528 /* IEEE floats in S7 (5.3), with optional bitmap */
529 const uint8_t *q = mp.sec7;
530 size_t off7 = 0, nAvail = mp.sec7Len;
531 for(size_t k=0;k<nPts;k++){
532 if(bmBitAt(bm, bmBytes, k)){
533 if(off7 + 4 > nAvail){ free(vals); return -1; }
534 vals[k] = rdIEEE32BE(q + off7);
535 off7 += 4;
536 } else {
537 vals[k] = NAN;
538 }
539 }
540 }
541 else {
542 free(vals);
543 return -1; // unsupported packing (e.g. 5.2 / 5.40 / 5.41)
544 }
545
546 if(outValues) *outValues = vals; else free(vals);
547 return 0;
548}
549
550// ======================== Indexing on Zone regular grid =====================
551static inline long indLat(double lat, const Zone *zone){
552 return (long)llround( (lat - zone->latMin) / zone->latStep );
553}
554
555// Wrap-aware longitude indexer around lonLeft in [lonLeft, lonLeft+360)
556static inline long indLonWrap(double lon, const Zone *zone){
557 double L = lon;
558 double base = zone->lonLeft;
559 // Normalise delta in [-180,180)
560 double d = L - base;
561 while(d < -180.0) d += 360.0;
562 while(d >= 180.0) d -= 360.0;
563 long i = (long)llround( d / zone->lonStep );
564 if(i < 0) i += zone->nbLon;
565 if(i >= zone->nbLon) i -= zone->nbLon;
566 return i;
567}
568
569static inline long idxTij(int iT, int iLon, int iLat, const Zone *zone){
570 return (long)iT * (long)zone->nbLat * (long)zone->nbLon
571 + (long)iLat * (long)zone->nbLon
572 + (long)iLon;
573}
574
575static int findTimeIndex(long timeStep, const Zone *zone){
576 for(size_t i=0;i<zone->nTimeStamp;i++){
577 if(zone->timeStamp[i] == timeStep) return (int)i;
578 }
579 return -1;
580}
581
582// ============================== Public: Lists ================================
583bool readGribLists(const char *fileName, Zone *zone){
584 if(!fileName || !zone) return false;
585
586 struct stat st;
587 if(stat(fileName, &st) != 0){
588 fprintf(stderr, "readGribLists: cannot stat %s\n", fileName);
589 return false;
590 }
591 FILE *f = fopen(fileName, "rb");
592 if(!f){
593 fprintf(stderr, "readGribLists: cannot open %s\n", fileName);
594 return false;
595 }
596
597 uint8_t *buf = (uint8_t*)malloc((size_t)st.st_size);
598 if(!buf){ fclose(f); fprintf(stderr,"readGribLists: malloc failed\n"); return false; }
599 if(fread(buf,1,(size_t)st.st_size,f) != (size_t)st.st_size){
600 fclose(f); free(buf); fprintf(stderr,"readGribLists: short read\n"); return false;
601 }
602 fclose(f);
603
604 memset(zone, 0, sizeof(*zone));
605 zone->editionNumber = 2;
606 zone->stepUnits = 1; /* hours */
607
608 const uint8_t *p = buf, *end = buf + st.st_size;
609 while((p = findNextGrib(p, end)) != NULL){
610 if(p + 16 > end) break;
611 if(memcmp(p,"GRIB",4)!=0 || p[7]!=2){ p++; continue; }
612
613 int discipline = (int)rdU8(p+6);
614 uint64_t totalLen = rdU64BE(p+8);
615 if(totalLen < 16 || p + totalLen > end){ break; }
616
617 size_t off = 16;
618 Sec1Info s1={0}; bool haveS1=false;
619 Sec4Info s4={0}; bool haveS4=false;
620
621 while(off + 5 <= totalLen){
622 uint32_t sLen = rdU32BE(p+off);
623 if(sLen < 5 || off + sLen > totalLen) break;
624 uint8_t sNum = rdU8(p+off+4);
625 const uint8_t *s = p + off + 5;
626 uint32_t bodyLen = sLen - 5;
627
628 if(sNum == 1 && !haveS1){
629 haveS1 = parseSec1(s, bodyLen, &s1);
630 } else if(sNum == 4 && !haveS4){
631 haveS4 = parseSec4(s, bodyLen, discipline, &s4);
632 }
633
634 if(haveS1 && haveS4) break;
635 off += sLen;
636 }
637
638 if(haveS1){
639 zone->centreId = s1.centerId;
640 long dataDate = (long)(s1.refYear*10000 + s1.refMonth*100 + s1.refDay);
641 long dataTime = (long)(s1.refHour*100 + s1.refMinute);
644 }
645
646 if(haveS4){
647 const char *sn = shortNameFor(s4.discipline, s4.category, s4.parameter);
648
649 bool found=false;
650 for(size_t i=0;i<zone->nShortName;i++){
651 if(strcmp(zone->shortName[i], sn)==0){ found=true; break; }
652 }
653 if(!found && zone->nShortName < MAX_N_SHORT_NAME){
655 zone->nShortName++;
656 }
657
658 // sn = shortName calculé via shortNameFor(s0.discipline, s4.category, s4.parameter)
659 bool isCur = isCurrentTriplet(discipline, s4.category, s4.parameter, NULL);
660
661 bool keepForTime = (
662 strcmp(sn,"10u")==0 || strcmp(sn,"10v")==0 ||
663 strcmp(sn,"gust")==0 || strcmp(sn,"swh")==0 ||
664 strcmp(sn,"ucurr")==0 || strcmp(sn,"vcurr")==0 || // via shortName
665 isCur // sécurité si shortName reste "unknown"
666 );
667
668 if(keepForTime){
669 int h = pdtLeadHours(s4.pdtn, s4.pdt, s4.pdtLen);
670 if(h >= 0){
672 }
673 }
674 }
675
676 p += totalLen;
677 }
678
679 for(size_t i=0;i<zone->nShortName;i++){
680 if(strcmp(zone->shortName[i], "unknown")==0){
682 }
683 }
684
686
687 zone->intervalLimit = 0;
688 if(zone->nTimeStamp > 1){
691 for(size_t i=1;i<zone->nTimeStamp; i++){
692 if((zone->timeStamp[i] - zone->timeStamp[i-1]) == zone->intervalEnd){
693 zone->intervalLimit = i;
694 break;
695 }
696 }
697 } else {
699 }
700
701 free(buf);
702 return true;
703}
704
705// =========================== Public: Parameters =============================
706bool readGribParameters(const char *fileName, Zone *zone){
707 if(!fileName || !zone) return false;
708
709 struct stat st;
710 if(stat(fileName, &st) != 0){ fprintf(stderr,"readGribParameters: stat fail\n"); return false; }
711
712 FILE *f = fopen(fileName, "rb");
713 if(!f){ fprintf(stderr,"readGribParameters: open fail\n"); return false; }
714
715 uint8_t hdr[16];
716 if(fread(hdr,1,16,f)!=16){ fclose(f); fprintf(stderr,"readGribParameters: short read\n"); return false; }
717 if(memcmp(hdr,"GRIB",4)!=0 || hdr[7]!=2){ fclose(f); fprintf(stderr,"readGribParameters: not GRIB2\n"); return false; }
718 uint64_t totalLen = rdU64BE(hdr+8);
719
720 uint8_t *msg = (uint8_t*)malloc((size_t)totalLen);
721 if(!msg){ fclose(f); fprintf(stderr,"readGribParameters: malloc fail\n"); return false; }
722 fseek(f,0,SEEK_SET);
723 if(fread(msg,1,(size_t)totalLen,f)!=(size_t)totalLen){ free(msg); fclose(f); fprintf(stderr,"readGribParameters: short read 2\n"); return false; }
724 fclose(f);
725
726 size_t off = 16;
727 Sec1Info s1={0}; bool haveS1=false;
728 Sec3Grid g3={0}; bool haveS3=false;
729
730 while(off + 5 <= totalLen){
731 uint32_t sLen = rdU32BE(msg+off);
732 if(sLen < 5 || off + sLen > totalLen) break;
733 uint8_t sNum = rdU8(msg+off+4);
734 const uint8_t *s = msg + off + 5;
735 uint32_t bodyLen = sLen - 5;
736
737 if(sNum == 1 && !haveS1){
738 haveS1 = parseSec1(s, bodyLen, &s1);
739 } else if(sNum == 3 && !haveS3){
740 haveS3 = parseSec3LatLon(s, bodyLen, &g3);
741 }
742
743 if(haveS1 && haveS3) break;
744 off += sLen;
745 }
746
747 if(haveS1) zone->centreId = s1.centerId;
748 zone->editionNumber = 2;
749 zone->stepUnits = 1; // hours
750
751 if(!haveS3){ free(msg); return false; }
752
753 zone->nbLon = g3.Ni;
754 zone->nbLat = g3.Nj;
755 zone->lonStep = fabs(g3.di);
756 zone->latStep = fabs(g3.dj);
757
758 double latStart = g3.lat1;
759 double latEnd = g3.lat1 + (g3.Nj - 1) * g3.dj;
760 double lonStart = g3.lon1;
761 double lonEnd = g3.lon1 + (g3.Ni - 1) * g3.di;
762
763 zone->latMin = fmin(latStart, latEnd);
764 zone->latMax = fmax(latStart, latEnd);
765
766 zone->lonLeft = lonCanonize(lonStart);
767 zone->lonRight = lonCanonize(lonEnd);
768
769 if(lonCanonize(zone->lonLeft) > 0.0 && lonCanonize(zone->lonRight) < 0.0){
770 zone->anteMeridian = true; // crosses +180/-180
771 } else {
772 zone->anteMeridian = false;
775 }
776
777 zone->numberOfValues = (long)zone->nbLon * (long)zone->nbLat;
778
779 free(msg);
780 return true;
781}
782
783// ============================== Public: All data ============================
784/* Decode entire file and fill tGribData[iFlow] with FlowP.
785 - lat/lon are pre-filled for each (t,i,j) from Zone geometry
786 - values mapped by shortName to u/v/g/w
787 - waves: missing values forced to 0.0 (cosmetic)
788*/
789bool readGribAll (const char *fileName, Zone *zone, int iFlow){
790 if(!fileName || !zone) return false;
791
792 zone->wellDefined = false;
793
794 if(!readGribLists(fileName, zone)) return false;
795 if(!readGribParameters(fileName, zone)) return false;
796
797 if(zone->nDataDate > 1){
798 fprintf(stderr, "readGribAll: more than 1 dataDate not supported (n=%zu)\n", zone->nDataDate);
799 return false;
800 }
801
802 size_t totalPts = (size_t)zone->nTimeStamp * (size_t)zone->nbLat * (size_t)zone->nbLon;
803 if(tGribData[iFlow]){ free(tGribData[iFlow]); tGribData[iFlow] = NULL; }
804 tGribData[iFlow] = (FlowP*)calloc(totalPts, sizeof(FlowP));
805 if(!tGribData[iFlow]){
806 fprintf(stderr, "readGribAll: calloc tGribData failed\n");
807 return false;
808 }
809
810 /* Pre-fill lat/lon for all grid points (time-invariant geometry) */
811 for(int j=0;j<zone->nbLat;j++){
812 double lat = zone->latMin + j * zone->latStep * ((zone->latMax>=zone->latMin)?1.0:-1.0);
813 for(int i=0;i<zone->nbLon;i++){
814 double lon = zone->lonLeft + i * zone->lonStep;
815 if(!zone->anteMeridian) lon = lonCanonize(lon);
816 for(size_t t=0;t<zone->nTimeStamp;t++){
817 long k = idxTij((int)t, i, j, zone);
818 tGribData[iFlow][k].lat = (float)lat;
819 tGribData[iFlow][k].lon = (float)lon;
820 }
821 }
822 }
823
824 /* Read entire file */
825 struct stat st;
826 if(stat(fileName,&st)!=0){ fprintf(stderr,"readGribAll: stat fail\n"); return false; }
827 FILE *f = fopen(fileName,"rb");
828 if(!f){ fprintf(stderr,"readGribAll: open fail\n"); return false; }
829
830 uint8_t *buf = (uint8_t*)malloc((size_t)st.st_size);
831 if(!buf){ fclose(f); fprintf(stderr,"readGribAll: malloc fail\n"); return false; }
832 if(fread(buf,1,(size_t)st.st_size,f) != (size_t)st.st_size){
833 fclose(f); free(buf); fprintf(stderr,"readGribAll: short read\n"); return false;
834 }
835 fclose(f);
836
837 const uint8_t *p = buf, *end = buf + st.st_size;
838 zone->nMessage = 0;
839 zone->allTimeStepOK = true;
840
841 while((p = findNextGrib(p, end)) != NULL){
842 uint64_t totalLen = rdU64BE(p+8);
843 if(totalLen < 16 || p + totalLen > end) break;
844 int discipline = (int)rdU8(p+6);
845
846
847
848 MsgParse mp; float *vals = NULL;
849 if(parseGrib2Message(p, (size_t)totalLen, /*wantData=*/1, &mp, &vals) == 0){
850 const char *sn = shortNameFor(rdU8(p+6), mp.s4.category, mp.s4.parameter);
851
852 bool writeU = (strcmp(sn,"10u")==0 || strcmp(sn,"ucurr")==0);
853 bool writeV = (strcmp(sn,"10v")==0 || strcmp(sn,"vcurr")==0);
854 bool writeG = (strcmp(sn,"gust")==0);
855 bool writeW = (strcmp(sn,"swh")==0);
856
857 // Si sn=="unknown" mais triplet “courant”, force U/V
858 bool isU=false;
859 bool isCur = isCurrentTriplet(discipline, mp.s4.category, mp.s4.parameter, &isU);
860
861 int h = pdtLeadHours(mp.s4.pdtn, mp.s4.pdt, mp.s4.pdtLen);
862 int iT = (h>=0) ? findTimeIndex(h, zone) : -1;
863
864 if(vals && iT>=0 && (writeU||writeV||writeG||writeW)){
865 /* Respect Section 3 scanning mode for the S7 walk (order only) */
866 const int Ni = mp.s3.Ni, Nj = mp.s3.Nj;
867 const double lat1 = mp.s3.lat1, lon1 = mp.s3.lon1, di = mp.s3.di, dj = mp.s3.dj;
868 const int scan = mp.s3.scanFlags;
869
870 /* Use S3 geometry for directions; keep scan flags only for adjacency & boustrophedon */
871 const bool iPlus = (di > 0);
872 const bool jPlus = (dj > 0);
873 const bool adjI = scanAdjI(scan); // 0 => adjacent along i (row-major)
874 const bool boust = scanBoustro(scan); // 1 => boustrophedon
875
876
877 size_t lin = 0;
878
879 if(adjI){
880 int jStart = jPlus ? 0 : (Nj-1);
881 int jStep = jPlus ? 1 : -1;
882
883 for(int jj=0; jj<Nj; jj++){
884 int j = jStart + jj*jStep;
885
886 bool thisIPlus = iPlus ^ (boust && ((jj & 1) != 0));
887 int iStart = thisIPlus ? 0 : (Ni-1);
888 int iStep = thisIPlus ? 1 : -1;
889
890 for(int ii=0; ii<Ni; ii++){
891 int i = iStart + ii*iStep;
892 if(lin >= (size_t)Ni*(size_t)Nj) break;
893
894 double lat = lat1 + j * dj;
895 double lon = lon1 + i * di;
896 if(!zone->anteMeridian) lon = lonCanonize(lon);
897
898 long iLat = indLat(lat, zone);
899 long iLon = indLonWrap(lon, zone);
900 if(iLat < 0 || iLat >= zone->nbLat || iLon < 0 || iLon >= zone->nbLon){
901 lin++; continue;
902 }
903
904 float v = vals[lin++];
905
906 long k = idxTij(iT, (int)iLon, (int)iLat, zone);
907 if (isnan (v)) v = 0.0f; // prefer 0 to nan
908 if (isCur && strcmp(sn,"unknown")==0){
909 if (isU) tGribData[iFlow][k].u = v; else tGribData[iFlow][k].v = v;
910 } else {
911 if (isnan (v)) v = 0.0f; // prefer 0 to nan
912 if (writeU) tGribData[iFlow][k].u = v;
913 if (writeV) tGribData[iFlow][k].v = v;
914 if (writeG) tGribData[iFlow][k].g = v;
915 if (writeW) tGribData[iFlow][k].w = v;
916 }
917
918 }
919 }
920 } else {
921 bool baseIPlus = iPlus;
922 int iStartBase = baseIPlus ? 0 : (Ni-1);
923 int iStepBase = baseIPlus ? 1 : -1;
924
925 for(int ii=0; ii<Ni; ii++){
926 int i = iStartBase + ii*iStepBase;
927
928 bool thisJPlus = jPlus ^ (boust && ((ii & 1) != 0));
929 int jStart = thisJPlus ? 0 : (Nj-1);
930 int jStep = thisJPlus ? 1 : -1;
931
932 for(int jj=0; jj<Nj; jj++){
933 int j = jStart + jj*jStep;
934 if(lin >= (size_t)Ni*(size_t)Nj) break;
935
936 double lat = lat1 + j * dj;
937 double lon = lon1 + i * di;
938 if(!zone->anteMeridian) lon = lonCanonize(lon);
939
940 long iLat = indLat(lat, zone);
941 long iLon = indLonWrap(lon, zone);
942 if(iLat < 0 || iLat >= zone->nbLat || iLon < 0 || iLon >= zone->nbLon){
943 lin++; continue;
944 }
945
946 float v = vals[lin++];
947
948 long k = idxTij(iT, (int)iLon, (int)iLat, zone);
949 if(writeU) tGribData[iFlow][k].u = v;
950 if(writeV) tGribData[iFlow][k].v = v;
951 if(writeG) tGribData[iFlow][k].g = v;
952 if(writeW) tGribData[iFlow][k].w = isnan(v) ? 0.0f : v;
953 }
954 }
955 }
956 }
957
958 free(vals);
959 }
960
961 zone->nMessage += 1;
962 p += totalLen;
963 }
964
965 free((void*)buf);
966 zone->wellDefined = true;
967 return true;
968}
969
static double lonCanonize(double lon)
return lon on ]-180, 180 ] interval
Definition inline.h:29
#define MAX_N_TIME_STAMPS
Definition r3types.h:54
#define MAX_N_DATA_DATE
Definition r3types.h:55
#define MAX_N_DATA_TIME
Definition r3types.h:56
#define MAX_SIZE_SHORT_NAME
Definition r3types.h:60
#define MAX_N_SHORT_NAME
Definition r3types.h:57
Par par
parameter
Definition r3util.c:58
Zone zone
geographic zone covered by grib file
Definition r3util.c:64
static uint32_t rdU32BE(const uint8_t *p)
static void bitInit(BitReader *br, const uint8_t *buf, size_t len)
static uint8_t rdU8(const uint8_t *p)
static void strCopySafe(char *dst, size_t cap, const char *src)
static bool scanJPlus(int scan)
static uint32_t bitGet(BitReader *br, int nbits)
static int32_t rdI32BE(const uint8_t *p)
char * gribReaderVersion(char *str, size_t maxLen)
return type of reader
static bool isCurrentTriplet(int disc, int cat, int par, bool *isU)
static uint64_t rdU64BE(const uint8_t *p)
static bool parseSec3LatLon(const uint8_t *s, uint32_t bodyLen, Sec3Grid *out)
static bool scanIPlus(int scan)
FlowP * tGribData[2]
grib data description
static long indLat(double lat, const Zone *zone)
static bool parseSec1(const uint8_t *s, uint32_t bodyLen, Sec1Info *out)
static bool parseSec4(const uint8_t *s, uint32_t bodyLen, int discipline, Sec4Info *out)
static int pdtLeadHours(int pdtn, const uint8_t *pdt, uint32_t pdtLen)
static int bmBitAt(const uint8_t *bm, size_t bmBytes, size_t k)
static bool parseSec5(const uint8_t *s, uint32_t bodyLen, Sec5Info *out)
static bool scanAdjI(int scan)
static uint16_t rdU16BE(const uint8_t *p)
static int unitToHours(uint8_t unit, int32_t val)
static int parseGrib2Message(const uint8_t *buf, size_t len, int wantData, MsgParse *out, float **outValues)
static int scoreHours(int h)
static const char * shortNameFor(int disc, int cat, int par)
static int findTimeIndex(long timeStep, const Zone *zone)
static int16_t readS16beFlexible(const uint8_t *p)
static const uint8_t * findNextGrib(const uint8_t *p, const uint8_t *end)
bool readGribLists(const char *fileName, Zone *zone)
Read lists zone.timeStamp, shortName, zone.dataDate, dataTime before full grib reading.
bool readGribAll(const char *fileName, Zone *zone, int iFlow)
read grib file using eccodes C API return true if OK
static bool scanBoustro(int scan)
bool readGribParameters(const char *fileName, Zone *zone)
Read grib parameters in zone before full grib reading.
static long idxTij(int iT, int iLon, int iLat, const Zone *zone)
static void sortLongAsc(long *a, size_t n)
static size_t updateLongUnique(long value, size_t n, size_t maxN, long arr[])
static long indLonWrap(double lon, const Zone *zone)
static float rdIEEE32BE(const uint8_t *p)
const uint8_t * buf
Wind point.
Definition r3types.h:128
float u
Definition r3types.h:131
float g
Definition r3types.h:134
float lat
Definition r3types.h:129
float w
Definition r3types.h:133
float v
Definition r3types.h:132
float lon
Definition r3types.h:130
const uint8_t * sec7
const uint8_t * pdt
const uint8_t * bitmap
zone description
Definition r3types.h:140
double latMin
Definition r3types.h:149
double lonRight
Definition r3types.h:152
bool anteMeridian
Definition r3types.h:141
long dataTime[MAX_N_DATA_TIME]
Definition r3types.h:165
long intervalEnd
Definition r3types.h:167
bool wellDefined
Definition r3types.h:143
long centreId
Definition r3types.h:144
long intervalBegin
Definition r3types.h:166
long numberOfValues
Definition r3types.h:147
size_t nTimeStamp
Definition r3types.h:157
char shortName[MAX_N_SHORT_NAME][MAX_SIZE_SHORT_NAME]
Definition r3types.h:162
long nbLon
Definition r3types.h:156
long stepUnits
Definition r3types.h:148
long editionNumber
Definition r3types.h:146
size_t nDataDate
Definition r3types.h:158
double lonStep
Definition r3types.h:154
long nbLat
Definition r3types.h:155
double latMax
Definition r3types.h:150
double lonLeft
Definition r3types.h:151
int nMessage
Definition r3types.h:145
size_t intervalLimit
Definition r3types.h:168
double latStep
Definition r3types.h:153
size_t nShortName
Definition r3types.h:161
bool allTimeStepOK
Definition r3types.h:142
long timeStamp[MAX_N_TIME_STAMPS]
Definition r3types.h:163
long dataDate[MAX_N_DATA_DATE]
Definition r3types.h:164
size_t nDataTime
Definition r3types.h:159