Repository navigation
Releases
v1.3.0
v1.3.0
Fixed and tested: an upstream test audit
We compared our tests with the eccodes, libaec and openjpeg test suites. That found 80 behaviours we never tested; 21 of them were live bugs. All are fixed and covered by tests.
Round-trips
WriteMessageis now byte-exact on every fixture. It used to drop three things:- the Section 4 pv array (eccodes rejected the rewritten files)
- Section 1 octets 22 onward
- the reduced lat/lon point-count list
- Multi-field messages that repeat from Section 2, 3 or 4 decode and rewrite byte-exact. New fields:
Field.SharesSection3andField.Section2.
Decoding
- Negative
forecastTimeis read as sign-magnitude. - A missing level scale factor means the level is used unscaled (it gave 10^127).
- Constant fields (0 bits per value) decode to the reference value R, without the decimal scale factor, as eccodes does.
- PNG at 25–32 bits per value no longer loses data to alpha premultiplication.
- Legacy template numbers 5.40000 and 5.41000 are decoded.
- Bit-map indicator 254 reuses the previous bitmap.
- Inconsistent counts between sections are errors.
Coordinates and lookups
- One shared grid geometry for
GridCoordinates,NearestValue,BilinearValueandSampler:- every scanning mode
- every shape of the Earth, including oblate Lambert
- basic angle and subdivisions
- reduced Gaussian sub-areas and reduced lat/lon grids
- rotated grids' angle of rotation
- the 0/360 seam and Gaussian rows
- Longitudes are in [0, 360). Inconsistent grid descriptions return errors.
- For projected grids scanned in a mode other than 0x40, the first grid point is the first point in data order, as WMO defines it. eccodes treats it as the south-west corner; the two agree for 0x40, the common case.
Encoding
- The reference value is never above the minimum. It used to give errors of up to 32.
- Encoders reject NaN, infinities, values outside float32 range, and 0 bits per value on varying data. Complex packing stores NaN as missing.
- Constant input encodes with 0 bits per value.
- All-NaN input to complex packing stays missing, and the complex encoder no longer drops an unstored bias.
- AEC/CCSDS: the encoder output now matches libaec byte for byte; it miscoded a run of exactly 5 zero blocks.
- JPEG2000:
- the decoder is ported from OpenJPEG (tiles, layers, progression orders, all code-block styles);
- 9/7 and multi-component streams return explicit errors;
- the encoder writes valid tile-part lengths, so Jasper reads its output;
- encoding is limited to 30 bits per value.
Reading streams
Decoderskips junk and GRIB1 messages between messages; a GRIB1 message gives an error wrappingErrNotEdition2.- It checks each message's total length.
- A truncated message gives
ErrTruncated; a clean end givesio.EOF. - No more panics on malformed templates.
New API
Field.SharesSection3,Field.Section2Section1.Reserved,Section1.ValidateReferenceTimeSection6.PreviousBitmapMissingScaleFactorSection4.Describe(discipline)- Code-table names now cover every code in WMO table version 35.
Compatibility notes
Section1holds a[]byte, so it can no longer be compared with==.- Inputs that were silently accepted now return errors. That covers invalid encoder input, inconsistent grids, mismatched counts, and a message length that doesn't match the declared total.
Tests
Every test runs from committed data, so anyone can run the full suite:
- an eccodes-derived reference for 317 fields (exact value fingerprints, sampled coordinates, metadata, pv arrays, nearest-point answers)
- golden encoder output that eccodes decoded
- libaec's CCSDS test vectors
- JPEG2000 streams checked with OpenJPEG and Jasper
With eccodes built locally:
GRIB2_WRITE_ECCODES_REFERENCE=1 go test -run TestEccodesReference_Write .regenerates the data.TestEccodesReference_UpToDatechecks the committed data still matches eccodes.
v1.2.0
v1.2.0
New: Sampler for reusable bilinear weights
BilinearValue unpacks the whole field on every call (~408 µs per point on an RDPS rotated grid), which rules it out for rendering, regridding, or reading the same points from many fields on one grid.
NewSampler(f)builds once per grid;Weights(lat, lon)returns the four corner indices and fractions, andInterpolateapplies them to theValues()of any field on that grid.- Supports templates 3.0, 3.1 and regular 3.40.
BilinearWeights.Outsideflags points beyond the grid edge (clamped to the edge cell, as before).NearestCorner()returns the heaviest corner for categorical fields such as precipitation type.Sampler.Matches(f)checks that a field shares the grid.BilinearValueis now built on the same weights/interpolate steps with the same arithmetic order, so its results are unchanged.
Verification
Sampler results are bit-identical to BilinearValue, errors included, across 69 decodable fixture grids (57 on 3.0, 4 on 3.1, 8 on 3.40). The existing suite passes unchanged.
| lookup | cost per point |
|---|---|
BilinearValue |
408 µs |
Sampler.Weights + Interpolate |
0.21 µs |
v1.1.1
v1.1.1
Bug fix
- Scale factor sign-magnitude encoding: Fixed the root cause of all RDPS isobaric level bugs. The WMO spec encodes scale factors as sign-magnitude (byte 0x84 = -4), not two s complement (byte 0x84 = -124). The standard formula now works for all producers without hacks.
Testing overhaul
Added eccodes cross-validation and integration test suite (372 tests, up from 294).
Real-world fixtures from GFS and RDPS (previously 0% CMC data). Oracle tests comparing every parsed field against live eccodes output. Integration tests simulating real user workflows including RDPS NearestValue queries, isobaric level verification, 40-field GFS parsing, and write/re-read round-trips.
v1.1.0
v1.1.0
New features
- NearestValue haversine distance — all grid types now use great-circle distance to pick the closest point, matching the eccodes approach. Correct at grid edges where projection-space rounding fails.
- O(1) NearestValue for Template 3.1 (rotated lat/lon) — converts target to rotated coordinates, finds 4 surrounding points, picks nearest by haversine. Turns seconds into microseconds on 1.2M point RDPS grids.
- BilinearValue for Template 3.1 — bilinear interpolation in rotated coordinate space.
- GridCoordinates canonical ordering — now returns coordinates in the same order as Values(), regardless of scanning mode.
- Product templates 4.2, 4.11, 4.12 — derived ensemble forecast, ensemble + time interval, derived ensemble + time interval. Covers GEFS/ECMWF ensemble statistics.
- Grid template 3.90 — Space View / Satellite Perspective (GOES-R, Himawari).
- SurfaceLevel/SurfaceLevelHPa helpers — handle both GFS-style (raw Pa) and RDPS-style (compact hPa with non-standard scale factors).
- CMC parameter codes — SkyTransparencyIndex (0,19,38) and SeeingIndex (0,19,39).
- Fuzz tests — 9 fuzz targets covering all parsers (j2k, aec, complex, run-length, message, JPEG2000, CCSDS, simple, PNG).
- Hardened against malformed input — bounds-checked extractBits, 100M allocation caps, iteration limits on state machines, 256MB section size limit.
Bug fixes
- NearestValue returned wrong index for rotated grids with non-zero scanning mode (row inversion)
- GridCoordinates returned raw scanning order instead of canonical order
- SurfaceLevelHPa double-division for RDPS isobaric encoding
- pixelToUint negative shift panic when bits > 16
- Template330 longitude fields now correctly use sign-magnitude encoding
- gridScanParams now handles Template390
- Section0 reserved bytes preserved on round-trip
- WriteMessage patches TotalLength after writing
- Scale factor fields for surfaces changed to signed int8
Testing
- 294 tests, 0 failures
- Exhaustive brute-force agreement tests for NearestValue (59 points on rotated grids, 10 Canadian cities on real RDPS data)
- Cross-validated against eccodes 2.47.0
v1.0.4
v1.0.4
Bug fixes
- NearestValue index for rotated lat/lon grids (Template 3.1): Fixed canonical grid origin computation to use scanning mode flags instead of min/max heuristics. The O(1) path now matches brute-force search exactly on real RDPS data (1.2M point grid).
- GridCoordinates ordering: Now returns coordinates in canonical order (matching Values()), fixing the root cause where GridCoordinates[i] did not correspond to Values()[i] for non-zero scanning modes.
- SurfaceLevelHPa for RDPS isobaric encoding: Fixed double-division bug where CMC's compact hPa values were divided by 100 twice.
New features
- O(1) NearestValue and BilinearValue for Template 3.1 (rotated lat/lon)
- CMC local parameter codes: SkyTransparencyIndex (0,19,38), SeeingIndex (0,19,39)
- SurfaceLevel(), SurfaceLevelHPa(), SurfaceType() helpers with fallback for non-standard scale factors
Testing
- Regression test comparing O(1) vs brute-force search on Template31 grids
- Verified on real RDPS 1.2M point grid: NearestValue(32.715, -117.161) returns 291.59K (18.44C) matching GFS and Open-Meteo
v1.0.0
v1.0.0 — Initial Release
Pure Go GRIB2 reader/writer. No CGo, no external dependencies.
Compression formats (read + write)
- Simple packing (5.0)
- Complex packing with spatial differencing (5.2/5.3)
- IEEE floating point (5.4)
- JPEG2000 (5.40)
- PNG (5.41)
- CCSDS/AEC (5.42)
- Run-length (5.200, decode only)
Grid templates
Lat/lon, rotated lat/lon, Mercator, polar stereographic, Lambert conformal, Gaussian (regular + reduced), space view/satellite
High-level API
Field.Values()with automatic bitmap application and scanning mode reorderingField.GridCoordinates()for all projection typesField.NearestValue()andField.BilinearValue()for point queries- WMO code table lookups for disciplines, parameters, centres, surface types
- Streaming multi-message decoder
Quality
- 274 tests, cross-validated against eccodes 2.47.0
- Fuzz tested (9 targets)
- Hardened against malformed input