grib

package module
v0.0.1-rc1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 14 Imported by: 0

README

simple-grib

[!NOTE] 🚀 Initial release, canary testing in progress. If you hit any problem, please open an issue!

English | 简体中文

A pure-Go library to read and write GRIB edition 1 + 2 (the WMO meteorological data format). Zero C dependencies: CGO_ENABLED=0 go build ./... passes clean. A sibling project of simple-netcdf.

GRIB1 support (landed 2026-10-15): grib.Open dispatches automatically by edition — GRIB1 messages are parsed by the grib1/ package (five sections IS/PDS/GDS/BMS/BDS, IBM-float reference value, sign-magnitude scale factors, bitmap expansion, simple-packed bit stream), reusing the grid-coordinate generators already verified on the GRIB2 side (regular lat/lon, Gaussian, rotated lat/lon, polar stereographic, Lambert conformal, quasi-regular); GRIB1 and GRIB2 messages may be mixed within one file. GRIB1 values keep native float64 precision (Message.Values64()); parameter names resolve once a table 2 is registered per (centre, table version) via grib1.RegisterTable2.

  • Spec-as-data: the spec data (429+ code tables, 254 template descriptors) is generated directly from the official WMO machine-readable CSVs (specdata/, provenance-stamped) — correctness comes from the data, not from hand-copied constants
  • Bit-level verification on real corpora: decoded values are compared point by point with eccodes on real CMA/ECMWF/GFS/JMA/ICON files (see Correctness)
  • fail-loud contract: unknown templates, local-use values, damaged or truncated files, non-GRIB (GRIB3/BUFR) → contextual errors; never panics, never stays silent

Features

Capability Status
Multi-message file index + section 0–7 structural parsing and validation (GRIB2) ✅
GRIB1 decoding: five-section parse + values (native float64) + coordinates; mixable with GRIB2 ✅
Metadata: Identification / Product (with WMO code-table names), Walk generic template walker (one parser for all 254 templates) ✅
Grid coordinates: 3.0/3.1/3.2/3.3 (+rotated), 3.40/3.41 Gaussian, 3.20 polar stereographic / 3.30 Lambert conformal point-by-point ✅
Data decoding: 5.0 simple (any bit width), 5.2 complex, 5.3 complex+spatial differencing, 5.4 IEEE binary32, 5.40 JPEG 2000 (pure-Go JPC decoder), 5.42 CCSDS AEC (own codec) ✅
Bitmap (section 6, indicator 0/255) + declared point count cross-check ✅
Writers: gribw templates 5.0 / 5.4 with round-trip tests; local table registration ✅
Writer gribw1: GRIB1 encoding (simple packing, grid types 0/4 + bitmap + constant fields) ✅
CLI gribdump: metadata / values / field-by-field Walk / JSON ✅
WASM wasm/: decode GRIB1 + GRIB2 directly in the browser / Node; metadata + values + coordinates as TypedArrays, drag-and-drop viewer included ✅

Install

go get github.com/knoweather/simple-grib

Quick start

CGO_ENABLED=0 go build ./...
CGO_ENABLED=0 go test -count=1 ./...

go run ./cmd/gribdump sample.grib2 # per-message metadata (with code-table names)
go run ./cmd/gribdump -values 16 sample.grib2 # first 16 decoded values
go run ./cmd/gribdump -section 3 -section 5 sample.grib2 # field-by-field Walk
go run ./cmd/gribdump -json sample.grib2 # machine readable

Examples

Reading: metadata + values + coordinates
package main

import (
	"fmt"

	grib "github.com/knoweather/simple-grib"
)

func main() {
	fl, err := grib.Open("sample.grib2")
	if err != nil { panic(err) }
	defer fl.Close()

	n, _ := fl.NumMessages()
	for i := 0; i < n; i++ {
		m, err := fl.Message(i)
		if err != nil { panic(err) }
		p, err := m.Product()
		if err != nil { panic(err) }

		level := "n/a"
		if p.Level1 != nil {
			level = fmt.Sprintf("type=%d", p.Level1.Type)
			if p.Level1.HasValue {
				level = fmt.Sprintf("type=%d value=%g", p.Level1.Type, p.Level1.Scale)
			}
		}
		forecast := "n/a"
		if p.ForecastTime != nil && !p.ForecastTime.Overflow {
			forecast = p.ForecastTime.Value.String()
		}
		fmt.Printf("msg%d: %s level=%s forecast=%s\n",
			i, p.ParameterName, level, forecast)

		vals, err := m.Values() // data values (5.0/5.2/5.3/5.4/5.40/5.42)
		if err != nil { fmt.Println(" values:", err); continue }
		fmt.Printf(" %d values, first=%.4g\n", len(vals), vals[0])

		g, _ := m.Grid()
		if lat, lon, err := g.Point(0); err == nil {
			fmt.Printf(" point(0) = %.4f, %.4f\n", lat, lon)
		}
	}
}
Writing: simple packing round trip
import (
	grib "github.com/knoweather/simple-grib"
	"github.com/knoweather/simple-grib/gribw"
)

fl, _ := grib.Open("sample.grib2")
m, _ := fl.Message(0)
g, _ := m.Grid()
vals, _ := m.Values()

msg := gribw.New()
_ = msg.SetDiscipline(0)
_ = msg.SetIdentification(id) // *grib.Identification
_ = msg.SetGrid(g) // reuse the reader's grid definition
_ = msg.SetProduct(prod) // *grib.Product

sec5, payload, _ := gribw.EncodeSimple(vals, gribw.SimpleParams{R: 0, E: 4, D: 1, Bits: 10})
_ = msg.SetDataSection(sec5, payload)
data, _ := gribw.File(msg) // complete GRIB2 byte stream (variadic: multiple messages)

WASM (browser / Node)

Pure Go + zero CGO means it builds to WebAssembly as-is: decode GRIB2 from a byte stream in the frontend — data never leaves the browser, no backend needed. Entry point in wasm/.

GOOS=js GOARCH=wasm CGO_ENABLED=0 go build -o wasm/grib.wasm ./wasm
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" wasm/ # glue must match the Go version
cd wasm && python3 -m http.server 8000 # open http://localhost:8000/viewer.html

To skip the server step, build a single-file bundle (inlined glue + gzipped wasm ≈ 1.9 MB, just double-click it):

go run ./wasm/tools/mkstandalone -html wasm/viewer.html -wasm wasm/grib.wasm \
 -glue "$(go env GOROOT)/lib/wasm/wasm_exec.js" -out wasm/viewer.standalone.html
const res = simpleGrib.decode(new Uint8Array(await file.arrayBuffer()),
 { values: true, points: true, perPoint: true });
res.messages[0].product.parameterName; // "Temperature"
res.messages[0].values; // Float32Array
res.messages[0].grid.lats; // Float64Array (same order as values with perPoint)

wasm/viewer.html is a ready-made single-file viewer: drop a .grib2 and see results — message list, identification/product/grid metadata (code-table names resolved on the Go side), value-field rendering (regular grids draw a raster, projected grids draw a scatter with hover readouts), first 200 rows as a value table, plus CSV / PNG / metadata JSON export.

Measured compatibility: GOOS=js GOARCH=wasm go build ./... passes clean on 11/11 packages; tests of spec / grid / gribw / internal/octetio / probe / cmd/gribdump / wasm all pass under Node. The few failures are unrelated to library logic: js/wasm under a Windows host cannot read directories (Linux hosts can), tests that spawn go build subprocesses, and cross-architecture bit-level float differences (amd64 GOAMD64=v3 FMA fusion + math.Exp/Pow assembly vs wasm, 1–4 ulp apart) — bit-level sha256 anchors therefore cannot be reused across architectures; cross-architecture checks must use tolerances. Item-by-item evidence in wasm/README.md.

Performance comparison

Measured on an AMD 4C Debian 13 box (go1.24.4 vs system eccodes 2.41.0), corpus = 47 real files from 10 families (CMA LARP TRAMS/GRAPES, ECMWF ICON/agl/atmosphere, GFS pgrb2, JMA GPV, CCSDS, pangu, …). Protocol: one warmup pass + 10 timed rounds averaged per engine; one round = open the file + call Values() on every message. Driver and protocol in bench/p7_decode_bench/.

The 42 files both engines can decode (350.13 MB / 485,311,281 values / 1030 messages):

Family Files Size simple-grib (Go) eccodes (C) Notes
JPEG 2000 (5.40) CMA LARP 4 153.3 MB 3.5 MB/s 9.3 MB/s pure-Go JPC decoder vs Jasper C — currently the main gap
Simple packing, large files ICON/pangu 5 139.1 MB 681 MB/s 678 MB/s on par (≈1.00×)
GFS pgrb2 multi-field 3 34.4 MB 453 MB/s 229 MB/s Go ≈2.0× faster
JMA GPV multi-field 9 19.5 MB 246 MB/s 428 MB/s eccodes ≈1.7× faster (incl. a multi-field-slice protocol difference)
ECMWF small files (agl/atmosphere/CCSDS…) 21 3.8 MB 326 MB/s 74 MB/s Go ≈4.4× faster (per-message overhead path)
Total 42 350.1 MB 8.0 MB/s · 11.1M val/s 20.7 MB/s · 28.7M val/s overall throughput: eccodes ≈2.6× (dragged down by the JPC files)

fail-loud evidence on the mixed corpus (before GRIB1 landed): all 5 non-GRIB2 files were rejected with context — GRIB edition 1 ×2, BUFR ×2 (magic BUFR), zero-byte truncation ×1; failed files are never silently dropped, they are reported along with the results. (GRIB1 is now supported; edition 3 / BUFR are still rejected this way.)

To reproduce (from the repo root):

CGO_ENABLED=0 go run ./bench/p7_decode_bench --rounds 10 <files...> > go_results.jsonl
python3 -u bench/p7_decode_bench/eccodes_bench.py --rounds 10 <files...> > py_results.jsonl

Protocol notes: the simple-grib column is the pure data-values path (lazy coordinate generation is not counted); eccodes calls the C API per message through its Python binding (wrapper overhead inside the timed path); the JMA GPV multi-field wrap decodes against prebuilt per-field slices on the eccodes side (that binding build has no next-message API).

Design

Key principles (spec-as-data / parse–interpret split / three-layer separation / graceful degradation / symmetric write):

  1. Spec-as-data — the WMO CSVs are the single source of truth; cmd/specgen generates spec/tables_gen.go + templates_gen.go (provenance-stamped), -check detects drift; every WMO revision = re-sync + regenerate, with an auditable workflow.
  2. Parse–interpret split — one generic template engine (Walk) parses all 254 templates from the generated descriptors; semantic interpretation (grid coordinates, packing decode) is implemented per template number only where needed; unimplemented parts expose raw fields instead of failing.
  3. Three-layer separation — L1 wire format (sections/bytes/bits) → L2 structure (templates/code tables, pure data) → L3 semantics (grid/pack/Product); each layer testable in isolation.
  4. Graceful degradation is a first-class citizen — unknown template numbers, local-use values → typed accessors return contextual errors; raw fields are always reachable; never panics.
  5. Symmetric writing — the same TemplateDef descriptors drive both reading and writing (gribw), so two structural definitions cannot drift.
L3 semantics grid/ (grid → coords) pack/ (data representation → values, registry + built-in decoders) (root) Product/Identification
L2 structure spec: WMO CSV ──specgen──► Go tables/template descriptors; Table.Lookup / Template / Walk
L1 wire format File (io.ReaderAt + message index) → Message (whole-message buffer) → Section (zero-copy slices); big-endian reads / BitReader

Correctness

  • Real-corpus bit-level comparison: CMA LARP four-anchor 442 messages (358M values) + data_snd 11 files 1109 messages (788.8M values, including CCSDS 156/156 and simple packing 69/69) match eccodes point-by-point on SHA-256; JPEG 2000 additionally cross-checked against Jasper's own decoder.
  • eccodes binary comparison vectors (testdata/ecc-xcheck/): simple packing 576/576, IEEE 108/108 (incl. ±Inf/NaN/subnormals), regular/Gaussian/rotated grids point-by-point max diff = 0 / 4.26e-14° / 9.9e-7°.
  • CCSDS 5.42 oracle: 36/36 PASS (scripts/xcheck/ccsds-oracle.sh, digest anchors in the script header comments).

Known limitations

  • Data templates 5.1 / 5.41 PNG / 5.5x spectral / 5.200 run-length → pack.ErrUnsupported (fail-loud)
  • IEEE 5.4 only precision=1 (binary32); 64/128-bit → fail-loud
  • Bitmap indicator 1–254 (centre-defined) → fail-loud
  • Grids: remaining projection templates (3.10/3.12/3.31…) and stretched Gaussian (3.42/3.43) are metadata-only; Point/Lats/Lons → grid.ErrNotImplemented
  • Writers currently support 5.0 / 5.4(precision=1) (GRIB2) and gribw1: GRIB1 simple packing (grid types 0/4, bitmap, constant fields; round-trip verified against real files); BUFR is only recognised and rejected

Documentation

Doc Contents
wasm/README.md WASM entry: build, JS API, browser viewer, measured cross-arch compatibility
bench/p7_decode_bench/README.md benchmark driver and protocol

AI powered

Model credits:

Documentation

Overview

Package grib 提供纯 Go 实现的 WMO GRIB 第 2 版(GRIB2)消息读取器。

它可以读取包含多条首尾相接消息的文件,校验节结构(第 0-7 节,以及 以 "7777" 结尾的第 8 节),并以类型化方式访问标识段、网格定义段、产品 定义段、数据表示段与解码后的要素值。模板布局来自 spec 包(由 WMO 官方 CSV 文件生成);网格坐标计算在 grid 包,数值解包在 pack 包。

该读取器遵循"失败即报错"(fail-loud)原则:任何结构性违规必定返回错误, 绝不猜测绕过。未知模板或本地使用模板会降级为仅元数据访问 (Section.Data / Raw 视图),不会阻断消息其余部分的读取。

Package grib is a pure-Go reader for WMO GRIB edition 2 (GRIB2) messages.

It reads files containing multiple concatenated messages, validates the section structure (sections 0-7 plus the "7777" end section, section 8), and provides typed access to the identification, grid definition, product definition, data representation, and decoded values. Template layouts come from the spec package (generated from the official WMO CSV files); grid coordinate math lives in grid, value unpacking in pack.

The reader is fail-loud: structural violations always return an error and are never guessed around. Unknown or local-use templates degrade to metadata-only access (Section.Data / Raw views) without blocking the read of the rest of the message.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotGRIB2 reports a bad 'GRIB' magic — the file is not GRIB at all.
	// (The name is kept for backward compatibility; it is now used only for
	// magic mismatches.)
	//
	// ErrNotGRIB2 报告 'GRIB' 魔数不匹配——该文件根本不是 GRIB。
	// (名称保留以兼容旧代码;现在仅用于魔数校验失败。)
	ErrNotGRIB2 = errors.New("grib: not a GRIB message")
	// ErrUnsupportedEdition reports the right magic but an edition that is
	// neither 1 nor 2. This package supports GRIB edition 1 (FM 92-IX) and
	// edition 2 (FM 92-XIII); editions 0 and 3+ are rejected here.
	//
	// ErrUnsupportedEdition 报告魔数正确、但版本号既不是 1 也不是 2。本包
	// 支持 GRIB 第 1 版(FM 92-IX)与第 2 版(FM 92-XIII);版本 0 及 3 以上
	// 在此被拒绝。
	ErrUnsupportedEdition = errors.New("grib: unsupported GRIB edition")
	// ErrTruncated reports a file that ends inside a message or a section.
	//
	// ErrTruncated 报告文件在某条消息或某个节的中途结束。
	ErrTruncated = errors.New("grib: truncated")
	// ErrBadSectionLength reports a section length field that disagrees with
	// the actual bytes (too small, or past the end of the message).
	//
	// ErrBadSectionLength 报告节长度字段与实际字节数不符(过小,或越过消息
	// 末尾)。
	ErrBadSectionLength = errors.New("grib: bad section length")
)

Sentinel errors for file-level and section-level problems. Classify with errors.Is; retrieve the offending number with errors.As via *ErrSectionAbsent or *ErrUnknownTemplate.

文件级与节级问题的哨兵错误。用 errors.Is 分类;用 errors.As 配合 *ErrSectionAbsent 或 *ErrUnknownTemplate 取出出错的编号。

Functions

This section is empty.

Types

type Calendar

type Calendar struct {
	TemplateNumber int // 模板号 octet 22-23,表 1.5(0=日历定义,2=日历+古气候偏移) / template number octets 22-23, table 1.5 (0 = calendar definition, 2 = calendar + paleoclimate offset)
	TypeOfCalendar int // 模板内容 octet 24,表 1.6(0=Gregorian,1=360 天,…),缺失时为 0 / template content octet 24, table 1.6 (0 = Gregorian, 1 = 360-day, ...); 0 when absent
}

Calendar 是第 1 节(octet 22 起)携带的可选日历定义。

Calendar is the optional calendar definition carried in section 1 (octets 22+).

type DataRep

type DataRep struct {
	TemplateNumber int // octets 10-11
	NumValues      int // octets 6-9: expected output length after the bitmap
	Fields         *spec.Walk
}

DataRep is the parsed section 5: the expected value count and the data representation template number, plus (for WMO-defined templates) a Walk over the template fields. Fields is nil for local/unknown templates; in that case Values() fails in the pack layer with a clear error.

type ErrSectionAbsent

type ErrSectionAbsent struct{ Number int }

ErrSectionAbsent reports a required section number missing from a message. ErrSectionAbsent 报告消息中缺少某个必需的节号。

func (*ErrSectionAbsent) Error

func (e *ErrSectionAbsent) Error() string

type ErrUnknownTemplate

type ErrUnknownTemplate struct {
	Section int
	Number  int
}

ErrUnknownTemplate reports a template number with neither a WMO definition nor a local registration (local-use range, or unassigned). Metadata-only access still works; registering a definition with spec.RegisterLocalTable enables Walk-based parsing and name display.

ErrUnknownTemplate 报告某个模板号既无 WMO 定义也无本地注册(处于本地使用 区间或尚未分配)。此时仍可做仅元数据访问;用 spec.RegisterLocalTable 注册 定义后即可启用基于 Walk 的解析与名称显示。

func (*ErrUnknownTemplate) Error

func (e *ErrUnknownTemplate) Error() string

type ErrUnsupportedBitmap

type ErrUnsupportedBitmap struct{ Indicator int }

ErrUnsupportedBitmap reports a section 6 bitmap indicator the reader cannot handle: indicators 1-253 (a bitmap predetermined by the centre, not present in the section) and 254 (bitmap defined earlier in the same message). Only indicator 0 (bitmap right here) and 255 (no bitmap) are supported.

func (*ErrUnsupportedBitmap) Error

func (e *ErrUnsupportedBitmap) Error() string

type File

type File struct {
	// contains filtered or unexported fields
}

File 表示一个已打开的 GRIB 文件(版本 1 或 2,可能混合),其消息索引在 打开时建立。索引只读取第 0 节头部,因此打开多消息大文件开销很小;消息 正文按需延迟读取。

File 支持混合文件:每条消息按其自身版本解析,返回的 Message 用 Message.Edition() 区分。数据服务常把 GRIB1 与 GRIB2 产品放在同一目录。

File is an open GRIB file (edition 1 or 2, possibly mixed) whose message index was built at open time. The index reads only the section 0 headers, so opening a large multi-message file is cheap; message bodies are read lazily on demand.

File supports mixed files: each message is parsed by its own edition and the returned Messages are told apart with Message.Edition(). Data services routinely keep GRIB1 and GRIB2 products in the same directory.

func Open

func Open(path string, opts ...Option) (*File, error)

Open 打开 path 并扫描其 GRIB 消息索引。返回的 File 必须关闭(通常用 defer)。

Open opens path and scans its GRIB message index. The returned File must be closed (typically with defer).

func OpenReader

func OpenReader(r io.ReaderAt, size int64, opts ...Option) (*File, error)

OpenReader 包装一个已知大小的现有 io.ReaderAt(例如内存缓冲区)。 所有权仍归调用方;这类文件的 File.Close 是空操作。

OpenReader wraps an existing io.ReaderAt of known size (e.g. an in-memory buffer). Ownership stays with the caller; File.Close is a no-op for such files.

func (*File) Close

func (f *File) Close() error

Close releases the underlying file handle. Calling it twice is safe, and it does nothing for files opened via OpenReader.

func (*File) Message

func (f *File) Message(i int) (*Message, error)

Message returns message i (0-based), reading its full body from the underlying reader and validating the section structure. For a multi-field envelope, index i selects one data-field group: the returned message is the virtual view [s0][s1][s3][that group's sections] (assembled contiguously), and its Size() reports the envelope's declared totalLength.

func (*File) Messages

func (f *File) Messages() iter.Seq2[*Message, error]

Messages iterates all messages in file order. An error terminates the iteration; when a yielded message is non-nil the error is necessarily nil.

func (*File) NumMessages

func (f *File) NumMessages() (int, error)

NumMessages returns the number of logical messages in the file (known at open time). It never fails for an open File.

func (*File) ValuesAll

func (f *File) ValuesAll(workers ...int) ([][]float32, error)

ValuesAll decodes every message in the file and returns one []float32 per message in file order. The worker count is optional:

  • f.ValuesAll() — sequential decoding (default, workers=1, no goroutines)
  • f.ValuesAll(n), n>1 — decode with at most n concurrent goroutines

Semantics: n <= 1 selects the sequential default; n greater than the message count is clamped to it; more than one argument is a programming error and returns an error (fail-loud). For automatic parallelism pass runtime.GOMAXPROCS(0) explicitly — there is no implicit magic.

Concurrency safety: io.ReaderAt is required to be goroutine-safe; each worker builds its own Message from a freshly read buffer, and a Message is immutable after construction. Do not call pack.Register concurrently while ValuesAll is running (the unpacker registry is shared global state).

Memory: all decoded results are held simultaneously (e.g. a pangu file with 69 messages / 71.6M values is about 287 MB of float32). For a streaming footprint use Messages() + Values() instead.

Errors: the error of the first failing message in file order is returned (the same error a sequential walk would hit at the same position); other messages are not reported individually. Bit-exactness with the sequential path is pinned by TestA_P8A_ParallelValuesBitExact.

type ForecastTime

type ForecastTime struct {
	Unit     int
	Raw      uint32        // original count in units of Unit (template 4.0 octets 19-22)
	Value    time.Duration // Raw × unit; saturates at MaxInt64 on overflow
	Overflow bool          // true when Value saturated
}

ForecastTime is the forecast-time part of a product definition. Unit is a table 4.4 code (0 = minutes, 1 = hours, 2 = days, 3 = months, 4 = years, ...); Raw is the stored count as-is, and Value converts Raw to a duration for units 0-2. For units >= 3 (months/years/...) there is no fixed length, so Value stays 0 and callers must interpret Unit themselves.

Raw counts whose true duration exceeds time.Duration (about 292 years) saturate Value at MaxInt64 and set Overflow — the previous implementation let the multiplication wrap silently into a negative duration (bug.md §2.2).

type Identification

type Identification struct {
	// Edition 是消息的 GRIB 版本(1 或 2)。GRIB1 按 GRIB1 第 1 节布局解析;
	// ProductionStatus / TypeOfProcessedData 仅属 GRIB2,GRIB1 时为 -1。
	//
	// Edition is the message's GRIB edition (1 or 2). GRIB1 follows the
	// GRIB1 section 1 layout; ProductionStatus / TypeOfProcessedData are
	// GRIB2-only concepts and are -1 for GRIB1.
	Edition             int
	Centre              int       // 中心号 octets 6-7,表 CCT-11 / centre octets 6-7, table CCT-11
	SubCentre           int       // 子中心 octets 8-9 / sub-centre octets 8-9
	CentreName          string    // CCT-11 表中 Centre 的名称;代码不在表中时为 "" / CCT-11 name of Centre; "" when the code is not in the table
	MasterTableVersion  int       // 主表版本 octet 10 / master table version octet 10
	LocalTableVersion   int       // 本地表版本 octet 11 / local table version octet 11
	ReferenceTime       time.Time // UTC / UTC
	ProductionStatus    int       // 生产状态 octet 20,表 1.3(0=业务) / production status octet 20, table 1.3 (0 = operational)
	TypeOfProcessedData int       // 处理数据类型 octet 21,表 1.4(0=分析) / type of processed data octet 21, table 1.4 (0 = analysis)
	Calendar            *Calendar // 第 1 节未携带日历定义时为 nil / nil when section 1 carries no calendar definition
}

Identification 是解析后的第 1 节。ReferenceTime 为 UTC 参考时间; 对预报产品而言即分析(起报)时间。

Identification is the parsed section 1. ReferenceTime is the reference time in UTC; for forecast products it is the analysis (initialization) time.

type Level

type Level struct {
	Type     int
	Scale    float64 // decoded level value; meaningful only when HasValue is true
	HasValue bool    // false when the scaled-value octets are missing (all ones) — WMO Note 168 / ecCodes can_be_missing: this type carries no numeric level
}

Level is one fixed surface of a product definition: Type is a table 4.5 code (e.g. 1 = isobaric surface (Pa), 3 = height above ground), Scale the decoded value obtained by applying the WMO scale factor to the raw integer (e.g. type 1 with scale factor 0 and raw 850 → 850 hPa).

type Message

type Message struct {
	// contains filtered or unexported fields
}

Message 表示一条已解析的 GRIB 消息:原始消息体加上校验过的节列表。它同时 承载第 1 版与第 2 版消息——用 Edition() 区分版本,用 GRIB1() 获取 GRIB1 原生视图(GRIB1 独有字段的入口)。

Message 由 File.Message 构造;所有访问器都是只读的,且在构造完成后 消息不可变,可安全地跨 goroutine 共享。

跨版本的语义差异在各访问器处逐一说明。总原则:两个版本共有的概念返回共 有值;某个版本不存在的概念返回显式的"不适用"标记,绝不伪造一个看起来像 真实数据的默认值。

Message is a parsed GRIB message: the raw message body plus a validated section list. It carries both edition 1 and edition 2 — tell them apart with Edition() and get the GRIB1-native view via GRIB1() (the entry point for GRIB1-only fields).

Constructed via File.Message; all accessors are read-only and safe to share across goroutines (the message is immutable after construction).

Cross-edition semantic differences are documented per accessor. The general rule: common concepts get the common value; a concept that does not exist in the edition gets an explicit "not applicable" marker, never a fabricated default that looks like real data.

func (*Message) DataRep

func (m *Message) DataRep() (*DataRep, error)

DataRep parses section 5 (missing → *ErrSectionAbsent). A section too short to carry its fixed header (numberOfValues at octets 6-9, the template number at octets 10-11) is rejected here instead of silently yielding zero values (bug.md §2.4).

func (*Message) Discipline

func (m *Message) Discipline() int

Discipline 返回 WMO 学科代码(GRIB2 第 0 节第 7 八位组,表 0.0),例如 0 表示由大气数据导出的产品。

GRIB1 没有该字段(GRIB1 只承载气象产品);沿用 ecCodes 的 GRIB1→GRIB2 转换约定返回 0——这是有定义的映射,而不是占位值。

Discipline is the WMO discipline code (GRIB2 section 0 octet 7, table 0.0), e.g. 0 = products derived from atmospheric data.

GRIB1 has no such field (GRIB1 carries only meteorological products); following ecCodes' GRIB1→GRIB2 conversion convention it returns 0 — a defined mapping, not a placeholder.

func (*Message) Edition

func (m *Message) Edition() int

Edition 返回 GRIB 版本号:1 或 2。

Edition returns the GRIB edition: 1 or 2.

func (*Message) GRIB1

func (m *Message) GRIB1() (*grib1.Message, bool)

GRIB1 返回 GRIB1 原生视图(表版本号、十进制比例因子、时间范围指示符、 生成过程标识、位图——GRIB2 中不存在的字段都在这里)。对第 2 版消息返回 (nil, false)。

GRIB1 returns the GRIB1-native view (table versions, decimal scale factor, time-range indicator, generating-process id, bitmap — the fields absent from GRIB2 all live there). For an edition-2 message it returns (nil, false).

func (*Message) Grid

func (m *Message) Grid() (*grid.Definition, error)

Grid parses the section 3 grid definition (delegated to the grid package).

func (*Message) Identification

func (m *Message) Identification() (*Identification, error)

Identification parses section 1. The fixed layout follows the WMO template 1.0 definition; the optional calendar part (octets 22+) follows table 1.5. The reference time is validated before use so time.Time normalization can never silently shift an out-of-range date.

func (*Message) Index

func (m *Message) Index() int

Index 返回本消息在其文件中的位置,从 0 开始计数。

Index is this message's 0-based position in its file.

func (*Message) Offset

func (m *Message) Offset() int64

Offset 返回消息起点在其文件中的字节偏移。

Offset is the byte offset of the message start in its file.

func (*Message) Product

func (m *Message) Product() (*Product, error)

Product parses section 4.

func (*Message) Section

func (m *Message) Section(n int) (*Section, error)

Section 返回编号为 n 的节(使用当前版本的编号体系),找不到时返回 *ErrSectionAbsent。返回的 Section 的 Data 切片与消息共用底层数组,应按只读 方式使用。

Section returns the section with the given number (in the current edition's numbering), or *ErrSectionAbsent. The returned Section's Data slice shares the message's backing array; treat it as read-only.

func (*Message) Sections

func (m *Message) Sections() []int

Sections 按消息内顺序列出实际出现的节号。

节号是各版本自己的:GRIB2 使用 0-7(以 "7777" 结尾的结束节不对外暴露), GRIB1 使用 0=IS、1=PDS、2=GDS、3=BMS、4=BDS、5=结束标记。同一节号在两个 版本中含义不同(例如 2 在 GRIB2 中是本地使用段,在 GRIB1 中是网格描述 段),因此跨版本遍历前先检查 Edition()。

Sections lists the section numbers actually present, in message order.

The numbers are each edition's own: GRIB2 uses 0-7 (the "7777" end section is not exposed), GRIB1 uses 0=IS, 1=PDS, 2=GDS, 3=BMS, 4=BDS, 5=end marker. The same number means different things in the two editions (e.g. 2 is local use in GRIB2 but the grid description section in GRIB1), so check Edition() before iterating across editions.

func (*Message) Size

func (m *Message) Size() int

Size 返回消息总长度(字节)。

Size is the message total length in bytes.

func (*Message) Values

func (m *Message) Values() ([]float32, error)

Values decodes all data values of the message in scan order. It ties together the grid point count, the section 5 value count, an optional section 6 bitmap, and the pack registry:

  • the grid point count comes from the section 3 definition;
  • with a bitmap (indicator 0), its popcount over the first GridPoints bits must equal NumValues, otherwise it is an error;
  • without a bitmap (no section 6, or indicator 255), NumValues must equal the grid point count, otherwise it is an error;
  • the section 7 payload (from octet 6) is handed to pack.Unpack.

func (*Message) Values64

func (m *Message) Values64() ([]float64, error)

Values64 returns the data values at float64 precision.

For GRIB1 this is native precision: the reference value is an IBM float (24-bit mantissa) and the formula (R + X·2^E)·10^(−D) is evaluated in float64 — a direct downcast to float32 would lose significant digits. For GRIB2 the values are already IEEE binary32; widening them here neither adds nor removes precision (use Values for native precision).

type Option

type Option func(*fileConfig)

Option customizes File behavior. Options are applied at open time and carried by the File into every message it parses.

Option 定制 File 的行为。选项在打开文件时生效,并随 File 传递到其解析的 每一条消息。

func WithLenientEndSection

func WithLenientEndSection() Option

WithLenientEndSection relaxes the section 8 end-marker check: up to 4 trailing bytes after section 7 are accepted whatever their content, instead of requiring the standard "7777". Use only for producers known to emit non-standard trailers; the default is strict mode.

WithLenientEndSection 放宽第 8 节结束标记的校验:第 7 节之后最多 4 个尾部 字节无论内容如何都被接受,而不再要求标准的 "7777"。只应用于已知会输出 非标准尾部的生成器;默认保持严格模式。

type Product

type Product struct {
	// Edition is the message's GRIB edition (1 or 2). It determines the code
	// space of every code below: GRIB1 parameter codes come from table 2,
	// level type codes from table 3, and the generating-process id is a
	// centre-defined code; the GRIB2 counterparts come from tables 4.2 /
	// 4.5 / 4.3. Equal numbers do not imply equal meaning.
	Edition                 int
	TemplateNumber          int
	ParameterCategory       int // octet 10, table 4.1
	ParameterNumber         int // octet 11, table 4.2 (resolved per the discipline)
	ParameterName           string
	TypeOfGeneratingProcess int           // first TableRef=4.3 field (octet 12 in template 4.0)
	ForecastTime            *ForecastTime // nil = the template has no time-unit field
	Level1, Level2          *Level        // first/second TableRef=4.5 field; nil = absent
	NV                      int           // section 4 octets 6-7: number of coordinate values
}

Product is the parsed section 4. The common core fields are extracted by absolute octet position and table-reference lookup, so they work across product templates without template-specific code. For a local or unknown template only TemplateNumber and NV are filled; the remaining fields stay zero-valued / nil.

type Section

type Section struct {
	Number int    // octet 5 (standard 0-7; 8 = end marker "7777", never exposed here) // 第 8 八位组(标准 0-7;8 = 结束标记 "7777",从不对外暴露)/ octet 5 (standard 0-7; 8 = end marker "7777", never exposed here)
	Length int    // sections 1+: octets 1-4; section 0: message total length (octets 9-16) // 第 1-4 八位组;第 0 节:消息总长度(第 9-16 八位组)/ sections 1+: octets 1-4; section 0: message total length (octets 9-16)
	Data   []byte // full section bytes starting at octet 1; sections 1+: Data[5:] = content from octet 6 // 自第 1 八位组起的完整节字节;第 1 节起:Data[5:] = 自第 6 八位组起的内容 / full section bytes starting at octet 1; sections 1+: Data[5:] = content from octet 6
}

Section 表示一个 GRIB2 节。对第 1-7 节,Data[0:4] 是 4 八位组的节长度, Data[4] 是节号,Data[5:] 是模板内容(自第 6 八位组起)。第 0 节是固定的 16 字节头部,既无长度前缀也无节号字节("GRIB" + 保留位 + 学科代码 + 版本号 + 8 八位组的消息总长度);对它而言 Length 是消息总长度,Data 是这 16 字节 头部。第 8 节是 4 八位组的结束标记 "7777"(ecCodes section.8.def: `ascii[4] '7777'`);它不是本地使用段——GRIB2 中唯一的本地使用段是第 2 节 (ecCodes sections.def)。WMO 模板 CSV 中的字段位置是节内的绝对八位组编号, 因此"第 N 八位组"的字段位于 Data[N-1]。

Section is one GRIB2 section. For sections 1-7, Data[0:4] holds the 4-octet length, Data[4] the section number, and Data[5:] the template content (from octet 6). Section 0 is the fixed 16-byte header with no length prefix and no number byte ("GRIB" + reserved + discipline + edition + 8-octet message total length); for it Length is the message total length and Data is that 16-byte header. Section 8 is the 4-octet end marker "7777" (ecCodes section.8.def: `ascii[4] '7777'`); it is not a local-use section — the only local-use section in GRIB2 is section 2 (ecCodes sections.def). Field positions in the WMO template CSVs are absolute octet numbers within the section, so a field at "octet N" lives at Data[N-1].

type SectionRef

type SectionRef struct {
	Number int   // section number octet // 节号八位组 / section number octet
	Off    int64 // absolute file offset of the section's first octet // 该节第一个八位组的文件绝对偏移 / absolute file offset of the section's first octet
	Length int   // full section length (octets 1-4) // 完整节长度(第 1-4 八位组)/ full section length (octets 1-4)
}

SectionRef 是文件中某个节的位置(仅头部;正文未读取)。供多要素信封索引 使用。

SectionRef is the location of one section in the file (header only; the body is not read). Used by the multi-field envelope index.

Directories

Path Synopsis
bench
p7_decode_bench command
cmd
gribdump command
命令 gribdump 是 GRIB2/GRIB1 文件的检视工具:默认打印消息元数据, 并支持 -values N(前 N 个数据值)、-section n(逐字段 Walk 转储,带表名, 可重复)、-json(机器可读输出)。
命令 gribdump 是 GRIB2/GRIB1 文件的检视工具:默认打印消息元数据, 并支持 -values N(前 N 个数据值)、-section n(逐字段 Walk 转储,带表名, 可重复)、-json(机器可读输出)。
specgen command
命令 specgen 从 specdata/ 里内置的 WMO CSV 文件生成 simple-grib 的 Go 规范数据。
命令 specgen 从 specdata/ 里内置的 WMO CSV 文件生成 simple-grib 的 Go 规范数据。
Package grib1 is a pure-Go reader for WMO GRIB edition 1 (FM 92-IX) messages.
Package grib1 is a pure-Go reader for WMO GRIB edition 1 (FM 92-IX) messages.
Package gribw encodes GRIB2 messages.
Package gribw encodes GRIB2 messages.
gribw1 是 GRIB edition 1 的**写入**器(grib1 包的逆操作、gribw 包的兄弟)。
gribw1 是 GRIB edition 1 的**写入**器(grib1 包的逆操作、gribw 包的兄弟)。
Package grid 实现 GRIB2 第 3 节(网格定义)模板:解码模板字段并计算 每个网格点的地理坐标。
Package grid 实现 GRIB2 第 3 节(网格定义)模板:解码模板字段并计算 每个网格点的地理坐标。
internal
octetio
Package octetio 提供大端、带边界检查的字节切片读取,外加为 GRIB2 位图准备的 MSB-first 比特访问。
Package octetio 提供大端、带边界检查的字节切片读取,外加为 GRIB2 位图准备的 MSB-first 比特访问。
Package pack 将 GRIB2 第 5 段(数据表示模板)解包为 float32 网格点值。
Package pack 将 GRIB2 第 5 段(数据表示模板)解包为 float32 网格点值。
Package spec 提供 WMO GRIB2 规范数据的只读访问:代码表(表 4.1 参数类别、 表 4.2.<discipline>.<category> 参数、CCT-11 中心等)与模板字段描述符由 cmd/specgen 从 specdata/ 源数据生成;包内提供按表 id 查表、按代码区间匹配 以及参数三元组命名的接口。
Package spec 提供 WMO GRIB2 规范数据的只读访问:代码表(表 4.1 参数类别、 表 4.2.<discipline>.<category> 参数、CCT-11 中心等)与模板字段描述符由 cmd/specgen 从 specdata/ 源数据生成;包内提供按表 id 查表、按代码区间匹配 以及参数三元组命名的接口。
这个目录是 simple-grib 的 WebAssembly 入口。
这个目录是 simple-grib 的 WebAssembly 入口。
tools/mkstandalone command
mkstandalone 把 wasm/viewer.html 打成"双击就能用"的单文件版本: 内联 wasm_exec.js,并把 grib.wasm 用 gzip + base64 塞进页面。
mkstandalone 把 wasm/viewer.html 打成"双击就能用"的单文件版本: 内联 wasm_exec.js,并把 grib.wasm 用 gzip + base64 塞进页面。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL