Skip to content

Releases

v1.3.0

Choose a tag to compare

@dmarkham dmarkham released this 02 Nov 03:55
146d573

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

  • WriteMessage is 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.SharesSection3 and Field.Section2.

Decoding

  • Negative forecastTime is 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, BilinearValue and Sampler:
    • 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

  • Decoder skips junk and GRIB1 messages between messages; a GRIB1 message gives an error wrapping ErrNotEdition2.
  • It checks each message's total length.
  • A truncated message gives ErrTruncated; a clean end gives io.EOF.
  • No more panics on malformed templates.

New API

  • Field.SharesSection3, Field.Section2
  • Section1.Reserved, Section1.ValidateReferenceTime
  • Section6.PreviousBitmap
  • MissingScaleFactor
  • Section4.Describe(discipline)
  • Code-table names now cover every code in WMO table version 35.

Compatibility notes

  • Section1 holds 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_UpToDate checks the committed data still matches eccodes.

v1.2.0

Choose a tag to compare

@dmarkham dmarkham released this 12 Oct 20:37
fab24c0

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, and Interpolate applies them to the Values() of any field on that grid.
  • Supports templates 3.0, 3.1 and regular 3.40.
  • BilinearWeights.Outside flags 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.
  • BilinearValue is 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

Choose a tag to compare

@dmarkham dmarkham released this 09 Apr 05:44

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

Choose a tag to compare

@dmarkham dmarkham released this 09 Apr 04:59

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

Choose a tag to compare

@dmarkham dmarkham released this 09 Apr 03:32

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

Choose a tag to compare

@dmarkham dmarkham released this 08 Apr 23:17

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 reordering
  • Field.GridCoordinates() for all projection types
  • Field.NearestValue() and Field.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