feat: add parser for RV data
This commit is contained in:
parent
e0d64e4cbe
commit
c79c471415
|
@ -0,0 +1,4 @@
|
||||||
|
mod parser;
|
||||||
|
mod rv;
|
||||||
|
|
||||||
|
pub use rv::Rv;
|
|
@ -0,0 +1,92 @@
|
||||||
|
use std::io::{Error as IoError, Read, Seek};
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
use std::slice;
|
||||||
|
use std::str::{self, FromStr, Utf8Error};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ParseError {
|
||||||
|
IoError(IoError),
|
||||||
|
ParseIntError(ParseIntError),
|
||||||
|
Unexpected(String, &'static str),
|
||||||
|
Utf8Error(Utf8Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser<R: Read + Seek> {
|
||||||
|
r: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> Parser<R> {
|
||||||
|
pub fn new(r: R) -> Self {
|
||||||
|
Self { r }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(&mut self, length: usize) -> Result<String, ParseError> {
|
||||||
|
let mut buf = vec![0; length];
|
||||||
|
|
||||||
|
self.r
|
||||||
|
.read_exact(&mut buf)
|
||||||
|
.map_err(|err| ParseError::IoError(err))?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8(buf).map_err(|err| ParseError::Utf8Error(err.utf8_error()))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&mut self, pos: i64, length: usize) -> Result<String, ParseError> {
|
||||||
|
if pos != 0 {
|
||||||
|
self.r
|
||||||
|
.seek_relative(pos)
|
||||||
|
.map_err(|err| ParseError::IoError(err))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let string = self.take(length)?;
|
||||||
|
|
||||||
|
self.r
|
||||||
|
.seek_relative(-(length as i64) - pos)
|
||||||
|
.map_err(|err| ParseError::IoError(err))?;
|
||||||
|
|
||||||
|
Ok(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect(&mut self, expected: &'static str) -> Result<(), ParseError> {
|
||||||
|
let actual = self.take(expected.len())?;
|
||||||
|
|
||||||
|
if actual == expected {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ParseError::Unexpected(actual, expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_int<I, const N: usize>(&mut self) -> Result<I, ParseError>
|
||||||
|
where
|
||||||
|
I: FromStr<Err = ParseIntError>,
|
||||||
|
{
|
||||||
|
let mut buf = [0u8; N];
|
||||||
|
|
||||||
|
self.r
|
||||||
|
.read_exact(&mut buf)
|
||||||
|
.map_err(|err| ParseError::IoError(err))?;
|
||||||
|
|
||||||
|
I::from_str(
|
||||||
|
str::from_utf8(&buf)
|
||||||
|
.map_err(|err| ParseError::Utf8Error(err))?
|
||||||
|
.trim_ascii_start(),
|
||||||
|
)
|
||||||
|
.map_err(|err| ParseError::ParseIntError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume<T, const N: usize>(mut self) -> Result<Vec<T>, ParseError>
|
||||||
|
where
|
||||||
|
T: Clone + Default,
|
||||||
|
{
|
||||||
|
let mut data = vec![T::default(); N];
|
||||||
|
|
||||||
|
let buf =
|
||||||
|
unsafe { slice::from_raw_parts_mut(data.as_mut_ptr() as *mut _, N * size_of::<T>()) };
|
||||||
|
self.r
|
||||||
|
.read_exact(buf)
|
||||||
|
.map_err(|err| ParseError::IoError(err))?;
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
#[cfg(not(target_endian = "little"))]
|
||||||
|
compile_error!("Only little-endian architectures are supported.");
|
||||||
|
|
||||||
|
use crate::data::parser::{ParseError, Parser};
|
||||||
|
|
||||||
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
|
const DATA_LEN: usize = 1100 * 1200;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct VersionMismatch {
|
||||||
|
pub actual: u8,
|
||||||
|
pub expected: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Datetime {
|
||||||
|
pub year: u16,
|
||||||
|
pub month: u8,
|
||||||
|
pub day: u8,
|
||||||
|
pub hour: u8,
|
||||||
|
pub minute: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Rv {
|
||||||
|
timestamp: Datetime,
|
||||||
|
version: u8,
|
||||||
|
precision: i8,
|
||||||
|
forecast: u16,
|
||||||
|
data: Vec<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rv {
|
||||||
|
pub fn parse<R: Read + Seek>(r: R) -> Result<Self, ParseError> {
|
||||||
|
let mut parser = Parser::new(r);
|
||||||
|
|
||||||
|
parser.expect("RV")?;
|
||||||
|
|
||||||
|
let day = parser.parse_int::<_, 2>()?;
|
||||||
|
let hour = parser.parse_int::<_, 2>()?;
|
||||||
|
let minute = parser.parse_int::<_, 2>()?;
|
||||||
|
|
||||||
|
let _wmo = parser.parse_int::<u32, 5>()?;
|
||||||
|
|
||||||
|
let month = parser.parse_int::<_, 2>()?;
|
||||||
|
let year = 2000 + parser.parse_int::<u16, 2>()?;
|
||||||
|
|
||||||
|
parser.expect("BY")?;
|
||||||
|
|
||||||
|
let _len = if parser.peek(7, 2)? == "VS" {
|
||||||
|
parser.parse_int::<usize, 7>()?
|
||||||
|
} else {
|
||||||
|
parser.parse_int::<usize, 10>()?
|
||||||
|
};
|
||||||
|
|
||||||
|
parser.expect("VS")?;
|
||||||
|
|
||||||
|
let version = parser.parse_int::<_, 2>()?;
|
||||||
|
|
||||||
|
parser.expect("SW ")?;
|
||||||
|
|
||||||
|
let _sw_version = parser.take(8)?;
|
||||||
|
|
||||||
|
parser.expect("PR E")?;
|
||||||
|
|
||||||
|
let precision = parser.parse_int::<_, 3>()?;
|
||||||
|
|
||||||
|
parser.expect("INT")?;
|
||||||
|
|
||||||
|
let _interval = parser.parse_int::<u16, 4>()?;
|
||||||
|
|
||||||
|
parser.expect("GP")?;
|
||||||
|
|
||||||
|
let _resolution = parser.take(9)?;
|
||||||
|
|
||||||
|
parser.expect("VV ")?;
|
||||||
|
|
||||||
|
let forecast = parser.parse_int::<_, 3>()?;
|
||||||
|
|
||||||
|
parser.expect("MF ")?;
|
||||||
|
|
||||||
|
let _flags = parser.parse_int::<u32, 8>()?;
|
||||||
|
|
||||||
|
parser.expect("MS")?;
|
||||||
|
|
||||||
|
let text_len = parser.parse_int::<_, 3>()?;
|
||||||
|
|
||||||
|
let _text = parser.take(text_len)?;
|
||||||
|
|
||||||
|
parser.expect("\x03")?;
|
||||||
|
|
||||||
|
let data = parser.consume::<_, DATA_LEN>()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
timestamp: Datetime {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
},
|
||||||
|
version,
|
||||||
|
precision,
|
||||||
|
forecast,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_version(&self, version: u8) -> Result<(), VersionMismatch> {
|
||||||
|
if self.version == version {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(VersionMismatch {
|
||||||
|
actual: self.version,
|
||||||
|
expected: version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale(&self) -> f64 {
|
||||||
|
10_f64.powi(self.precision as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instant(&self) -> (Datetime, u16) {
|
||||||
|
(self.timestamp, self.forecast)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &[u16] {
|
||||||
|
&self.data[..]
|
||||||
|
}
|
||||||
|
}
|
48
src/main.rs
48
src/main.rs
|
@ -1,45 +1,31 @@
|
||||||
use std::fs;
|
mod data;
|
||||||
|
|
||||||
|
use data::Rv;
|
||||||
|
|
||||||
use image::imageops;
|
use image::imageops;
|
||||||
use image::GrayAlphaImage;
|
use image::GrayAlphaImage;
|
||||||
|
|
||||||
use proj::Proj;
|
use proj::Proj;
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
const PROJ_WGS84_DE1200: &'static str = "+proj=stere +lat_0=90 +lat_ts=60 +lon_0=10 +a=6378137 +b=6356752.3142451802 +no_defs +x_0=543196.83521776402 +y_0=3622588.8619310018";
|
const PROJ_WGS84_DE1200: &'static str = "+proj=stere +lat_0=90 +lat_ts=60 +lon_0=10 +a=6378137 +b=6356752.3142451802 +no_defs +x_0=543196.83521776402 +y_0=3622588.8619310018";
|
||||||
|
|
||||||
enum State {
|
|
||||||
Header,
|
|
||||||
Lsb,
|
|
||||||
Msb(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut state = State::Header;
|
let rv = Rv::parse(BufReader::new(File::open("input").unwrap())).unwrap();
|
||||||
|
rv.expect_version(5).unwrap();
|
||||||
|
|
||||||
let mut data = Vec::with_capacity(1100 * 1200 * 2);
|
let mut data = Vec::with_capacity(1100 * 1200 * 2);
|
||||||
|
|
||||||
for b in fs::read("input").unwrap() {
|
for v in rv.data() {
|
||||||
match state {
|
if *v == 0x29c4 {
|
||||||
State::Header if b == 3 => {
|
data.push(0);
|
||||||
state = State::Lsb;
|
data.push(255);
|
||||||
}
|
} else {
|
||||||
State::Header => (),
|
data.push(if *v > 0 { 255 } else { 127 });
|
||||||
State::Lsb => {
|
data.push(255);
|
||||||
state = State::Msb(b);
|
}
|
||||||
}
|
|
||||||
State::Msb(lsb) => {
|
|
||||||
let v = u16::from_le_bytes([lsb, b]);
|
|
||||||
|
|
||||||
if v == 0x29c4 {
|
|
||||||
data.push(0);
|
|
||||||
data.push(255);
|
|
||||||
} else {
|
|
||||||
data.push(if v > 0 { 255 } else { 127 });
|
|
||||||
data.push(255);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = State::Lsb;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut img = GrayAlphaImage::from_raw(1100, 1200, data).unwrap();
|
let mut img = GrayAlphaImage::from_raw(1100, 1200, data).unwrap();
|
||||||
|
|
Loading…
Reference in New Issue