Commit 6d2cdacc authored by Jonny Schäfer's avatar Jonny Schäfer
Browse files

Correctly process level fields in other runlength based formats

parent d4c16e8a
......@@ -3,7 +3,6 @@ package radolan
import (
"bufio"
"fmt"
"strings"
"time"
"unicode"
)
......@@ -108,15 +107,25 @@ func (c *Composite) parseHeader(reader *bufio.Reader) error {
}
// Parse Level - Example "LV 6 1.0 19.0 28.0 37.0 46.0 55.0"
// or "LV12-31.5-24.5-17.5-10.5 -5.5 -1.0 1.0 5.5 10.5 17.5 24.5 31.5"
if lv, ok := section["LV"]; ok {
level := strings.Fields(lv)
if len(level) < 2 {
return newError("parseHeader", "invalid level count")
if len(lv) < 2 {
return newError("parseHeader", "level field too short")
}
c.level = make([]DBZ, len(level)-1)
for i, f := range level[1:] {
if _, err = fmt.Sscanf(f, "%f", &c.level[i]); err != nil {
var cnt int
if _, err = fmt.Sscanf(lv[:2], "%d", &cnt); err != nil {
return newError("parseHeader", "could not parse level count: "+err.Error())
}
if len(lv) != cnt*5+2 { // fortran format I2 + F5.1
return newError("parseHeader", "invalid level format: "+lv)
}
c.level = make([]RVP6, cnt)
for i := range c.level {
n := i * 5
if _, err = fmt.Sscanf(lv[n+2:n+7], "%f", &c.level[i]); err != nil {
return newError("parseHeader", "invalid level value: "+err.Error())
}
}
......
......@@ -21,7 +21,7 @@ type headerTestcase struct {
expDy int
expDataLength int
expPrecision int
expLevel []DBZ
expLevel []RVP6
}
func TestParseHeaderPG(t *testing.T) {
......@@ -42,7 +42,7 @@ func TestParseHeaderPG(t *testing.T) {
ht.expDy = 460
ht.expDataLength = 22205 - 159 // BY - header_etx_length
ht.expPrecision = 0
ht.expLevel = []DBZ{1.0, 19.0, 28.0, 37.0, 46.0, 55.0}
ht.expLevel = []RVP6{1.0, 19.0, 28.0, 37.0, 46.0, 55.0}
if err1 != nil || err2 != nil {
t.Errorf("%s.parseHeader(): wrong testcase time.Parse", ht.expProduct)
......@@ -71,7 +71,7 @@ func TestParseHeaderFZ(t *testing.T) {
ht.expDy = 450
ht.expDataLength = 405160 - 154 // BY - header_etx_length
ht.expPrecision = -1
ht.expLevel = []DBZ(nil)
ht.expLevel = []RVP6(nil)
if err1 != nil || err2 != nil {
t.Errorf("%s.parseHeader(): wrong testcase time.Parse", ht.expProduct)
......
......@@ -37,16 +37,17 @@ import (
// raw rvp-6 value (NaN if the no-data flag is set). This rvp-6 value is used differently
// depending on the product type:
//
// Product label || raw value "live" cloud reflectivity "live" rainfall rate
// -----------------|| +-------+ +-----+ +------+
// (PG), FZ, ... || | rvp-6 |---ToDBZ()--->| dBZ |---PrecipitationRate()--->| mm/h |
// RX, EX || +---+---+ +-----+ +------+
// -----------------|| - - - | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// RW, SF, ... || | +------+
// || +----- >| mm/h |
// || | mm/d |
// -----------------|| +------+
// aggregated precipitation
// The rvp-6 value is used differently depending on the product type:
//
// Product label | rvp-6 value represents | unit
// -------------------------+--------------------------+------------------------
// PG, PC, PX*, ... | cloud reflectivity | dBZ
// RX, WX, EX, FZ, FX, ... | raw value | convert to dBZ with ToDBZ()
// RW, SF, ... | aggregated precipitation | mm/h or mm/d
// PR*, ... | doppler radial velocity | m/s
//
// The cloud reflectivity (in dBZ) can be converted to rainfall rate (in mm/h)
// via PrecipitationRate().
//
// The cloud reflectivity factor Z is stored in its logarithmic representation dBZ:
// dBZ = 10 * log(Z)
......@@ -81,7 +82,7 @@ type Composite struct {
dataLength int // length of binary section in bytes
precision int // multiplicator 10^precision for each raw value
level []DBZ // maps data value to corresponding dBZ value in runlength based formats
level []RVP6 // maps data value to corresponding rvp-6 value in runlength based formats
offx float64 // horizontal projection offset
offy float64 // vertical projection offset
......
......@@ -76,11 +76,16 @@ func (c *Composite) decodeRunlength(dst []RVP6, line []byte) error {
return nil
}
// rvp6Runlength converts the raw value of level based composite products to radar video
// processor values (rvp-6). NaN may be returned when the given value has no internal mapping.
// rvp6Runlength sets the value of level based composite products to radar
// video processor values (rvp-6).
func (c *Composite) rvp6Runlength(value byte) RVP6 {
if c.level == nil || int(value) >= len(c.level) || value < 0 {
if value == 0 {
return RVP6(math.NaN())
}
return DBZ(c.level[value]).ToRVP6()
value--
if int(value) >= len(c.level) { // border markings
return RVP6(math.NaN())
}
return c.level[value]
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment