Skip to content
Snippets Groups Projects
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
No related branches found
No related tags found
No related merge requests found
......@@ -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]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment