// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace Spludlow.MetSat { //0: Indicator Section //1: Identification Section //2: Local Use Section(optional) //3: Grid Definition Section //4: Product Definition Section //5: Data Representation Section //6: Bit-Map Section //7: Data Section //8: End Section public class IndicatorSection { public String Grib; // 1-4 “GRIB” (coded according to the International Alphabet No. 5) public UInt16 Reserved; // 5-6 Reserved public Byte Discipline; // 7 Discipline – GRIB Master Table Number(see Code Table 0.0) public Byte EditionNumber; // 8 GRIB Edition Number(currently 2) public UInt64 Totallength; // 9-16 Total length of GRIB message in octets(including Section 0) } public class IdentificationSection { public UInt32 SectionLength; //1-4 Length of section in octets(21 or nn) public Byte SectionNumber; //5 Number of Section(“1”) public UInt16 GeneratingCentre; //6-7 Identification of originating/generating centre(see Common Code Table C-1) public UInt16 GeneratingSubCentre; //8-9 Identification of originating/generating sub-centre(allocated by originating/ generating centre) public Byte MasterTablesVersion; //10 GRIB Master Tables Version Number(see Code Table 1.0) public Byte LocalTablesVersion; //11 GRIB Local Tables Version Number(see Code Table 1.1) public Byte SignificanceOfReference; //12 Significance of Reference Time(see Code Table 1.2) public UInt16 Year; //13-14 Year(4 digits) public Byte Month; //15 Month public Byte Day; //16 Day public Byte Hour; //17 Hour public Byte Minute; //18 Minute public Byte Second; //19 Second public Byte ProductionStatus; //20 Production status of processed data in this GRIB message (see Code Table 1.3) public Byte DataType; //21 Type of processed data in this GRIB message (see Code Table 1.4) //22-nn Reserved: need not be present } //1-4 Length of section in octets(nn) //5 Number of Section(“3”) //6 Source of grid definition(see Code Table 3.0 and Note 1) //7-10 Number of data points //11 Number of octets for optional list of numbers defining number of points(see Note 2) //12 Interpretation of list of numbers defining number of points(see Code Table 3.11) //13-14 Grid Definition Template Number(N) (see Code Table 3.1) //15-xx Grid Definition Template(see Template 3.N, where N is the Grid Definition Template Number given in octets 13-14) //[xx+1]-nn Optional list of numbers defining number of points(see Notes 2, 3, and 4) /// /// GRIB Handling /// public class GRIB { public IndicatorSection IndicatorSection; public IdentificationSection IdentificationSection; public UInt32 NumberOfDataPoints; public UInt16 NumberOfCoordinateValues; public UInt16 ProductDefinitionTemplateNumber; public UInt16 DataRepresentationTemplateNumber; public byte[] Data; private int[] IndicatorSection_StringLengths = new int[] { 4 }; private bool Endian = true; public GRIB(byte[] data) { using (MemoryStream stream = new MemoryStream(data)) { this.Load(stream); } } public GRIB(string filename) { using (FileStream stream = new FileStream(filename, FileMode.Open)) { this.Load(stream); } } public GRIB(FileStream stream) { this.Load(stream); } public void Load(Stream stream) { long read; byte[] data; using (BinaryReader reader = new BinaryReader(stream)) { //0: Indicator Section this.IndicatorSection = new IndicatorSection(); read = Spludlow.Io.BinaryIO.ReadBinary(reader, this.IndicatorSection, this.Endian, this.IndicatorSection_StringLengths); if (this.IndicatorSection.Grib != "GRIB") throw new ApplicationException("Not a GRIB"); if ((long)this.IndicatorSection.Totallength != stream.Length) throw new ApplicationException("GRIB Bad File length, header:" + this.IndicatorSection.Totallength + ", stream:" + stream.Length); //1: Identification Section this.IdentificationSection = new IdentificationSection(); read = Spludlow.Io.BinaryIO.ReadBinary(reader, this.IdentificationSection, this.Endian); //, this.IdentificationSection_StringLengths); if (this.IdentificationSection.SectionNumber != 1) throw new ApplicationException("Not GRIB Identification Section"); reader.ReadBytes((int)this.IdentificationSection.SectionLength - 21); //2: Local Use Section(optional) UInt32 nextLength = Spludlow.Io.BinaryIO.ReadUInt32(reader, this.Endian); Byte nextSectionNumber = reader.ReadByte(); if (nextSectionNumber == 2) { reader.ReadBytes((int)nextLength - 5); nextLength = Spludlow.Io.BinaryIO.ReadUInt32(reader, this.Endian); nextSectionNumber = reader.ReadByte(); } //3: Grid Definition Section UInt32 gridDefinitionLength = nextLength; Byte gridDefinitionNumber = nextSectionNumber; if (gridDefinitionNumber != 3) throw new ApplicationException("Not GRIB Grid Definition Section"); reader.ReadBytes((int)gridDefinitionLength - 5); //4: Product Definition Section data = this.ReadSection(reader, 4, "Product Definition"); // 6 - 7 Number of coordinate values after Template(see Note 1) this.NumberOfCoordinateValues = Spludlow.Io.BinaryIO.ReadUInt16(data, 5, this.Endian); // 8 - 9 Product Definition Template Number(see Code Table 4.0) this.ProductDefinitionTemplateNumber = Spludlow.Io.BinaryIO.ReadUInt16(data, 7, this.Endian); // 10 - xx Product Definition Template(see Template 4.X, where X is the Product Definition Template Number given in octets 8 - 9) // [xx + 1] - nn Optional list of coordinate values(see Notes 2 and 3) //throw new ApplicationException(this.NumberOfCoordinateValues + " " + this.ProductDefinitionTemplateNumber); 0 30 30 Satellite product //5: Data Representation Section data = this.ReadSection(reader, 5, "Data Representation"); // 6 - 9 Number of data points where one or more values are specified in Section 7 when a bit map is present, total number of data points when a bit map is absent this.NumberOfDataPoints = Spludlow.Io.BinaryIO.ReadUInt32(data, 5, this.Endian); // 10 - 11 Data Representation Template Number(see code Table 5.0) this.DataRepresentationTemplateNumber = Spludlow.Io.BinaryIO.ReadUInt16(data, 9, this.Endian); // 12 - nn Data Representation Template(see Template 5.X, where X is the Data Representation Template Number given in octets 10 - 11) //throw new ApplicationException(this.NumberOfDataPoints.ToString()); //6: Bit-Map Section this.ReadSection(reader, 6, "Bit-Map"); //7: Data Section UInt32 dataLength = Spludlow.Io.BinaryIO.ReadUInt32(reader, this.Endian); Byte dataNumber = reader.ReadByte(); if (dataNumber != 7) throw new ApplicationException("Not GRIB Data Section"); this.Data = reader.ReadBytes((int)dataLength - 5); //8: End Section if (Encoding.ASCII.GetString(reader.ReadBytes(4)) != "7777") throw new ApplicationException("GRIB Bad End"); if ((long)this.IndicatorSection.Totallength != stream.Position) throw new ApplicationException("GRIB Unexpected data on End"); } } private byte[] ReadSection(BinaryReader reader, byte sectionNumber, string sectionName) { UInt32 length = Spludlow.Io.BinaryIO.ReadUInt32(reader, this.Endian); Byte number = reader.ReadByte(); if (number != sectionNumber) throw new ApplicationException("Not GRIB " + sectionName + " Section"); byte[] data = new byte[length]; reader.Read(data, 5, (int)length - 5); return data; } } }