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 ...@@ -3,7 +3,6 @@ package radolan
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"strings"
"time" "time"
"unicode" "unicode"
) )
...@@ -108,15 +107,25 @@ func (c *Composite) parseHeader(reader *bufio.Reader) error { ...@@ -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" // 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 { if lv, ok := section["LV"]; ok {
level := strings.Fields(lv) if len(lv) < 2 {
if len(level) < 2 { return newError("parseHeader", "level field too short")
return newError("parseHeader", "invalid level count")
} }
c.level = make([]DBZ, len(level)-1) var cnt int
for i, f := range level[1:] { if _, err = fmt.Sscanf(lv[:2], "%d", &cnt); err != nil {
if _, err = fmt.Sscanf(f, "%f", &c.level[i]); 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()) return newError("parseHeader", "invalid level value: "+err.Error())
} }
} }
......
...@@ -21,7 +21,7 @@ type headerTestcase struct { ...@@ -21,7 +21,7 @@ type headerTestcase struct {
expDy int expDy int
expDataLength int expDataLength int
expPrecision int expPrecision int
expLevel []DBZ expLevel []RVP6
} }
func TestParseHeaderPG(t *testing.T) { func TestParseHeaderPG(t *testing.T) {
...@@ -42,7 +42,7 @@ func TestParseHeaderPG(t *testing.T) { ...@@ -42,7 +42,7 @@ func TestParseHeaderPG(t *testing.T) {
ht.expDy = 460 ht.expDy = 460
ht.expDataLength = 22205 - 159 // BY - header_etx_length ht.expDataLength = 22205 - 159 // BY - header_etx_length
ht.expPrecision = 0 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 { if err1 != nil || err2 != nil {
t.Errorf("%s.parseHeader(): wrong testcase time.Parse", ht.expProduct) t.Errorf("%s.parseHeader(): wrong testcase time.Parse", ht.expProduct)
...@@ -71,7 +71,7 @@ func TestParseHeaderFZ(t *testing.T) { ...@@ -71,7 +71,7 @@ func TestParseHeaderFZ(t *testing.T) {
ht.expDy = 450 ht.expDy = 450
ht.expDataLength = 405160 - 154 // BY - header_etx_length ht.expDataLength = 405160 - 154 // BY - header_etx_length
ht.expPrecision = -1 ht.expPrecision = -1
ht.expLevel = []DBZ(nil) ht.expLevel = []RVP6(nil)
if err1 != nil || err2 != nil { if err1 != nil || err2 != nil {
t.Errorf("%s.parseHeader(): wrong testcase time.Parse", ht.expProduct) t.Errorf("%s.parseHeader(): wrong testcase time.Parse", ht.expProduct)
......
...@@ -37,16 +37,17 @@ import ( ...@@ -37,16 +37,17 @@ import (
// raw rvp-6 value (NaN if the no-data flag is set). This rvp-6 value is used differently // raw rvp-6 value (NaN if the no-data flag is set). This rvp-6 value is used differently
// depending on the product type: // depending on the product type:
// //
// Product label || raw value "live" cloud reflectivity "live" rainfall rate // The rvp-6 value is used differently depending on the product type:
// -----------------|| +-------+ +-----+ +------+ //
// (PG), FZ, ... || | rvp-6 |---ToDBZ()--->| dBZ |---PrecipitationRate()--->| mm/h | // Product label | rvp-6 value represents | unit
// RX, EX || +---+---+ +-----+ +------+ // -------------------------+--------------------------+------------------------
// -----------------|| - - - | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // PG, PC, PX*, ... | cloud reflectivity | dBZ
// RW, SF, ... || | +------+ // RX, WX, EX, FZ, FX, ... | raw value | convert to dBZ with ToDBZ()
// || +----- >| mm/h | // RW, SF, ... | aggregated precipitation | mm/h or mm/d
// || | mm/d | // PR*, ... | doppler radial velocity | m/s
// -----------------|| +------+ //
// aggregated precipitation // 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: // The cloud reflectivity factor Z is stored in its logarithmic representation dBZ:
// dBZ = 10 * log(Z) // dBZ = 10 * log(Z)
...@@ -81,7 +82,7 @@ type Composite struct { ...@@ -81,7 +82,7 @@ type Composite struct {
dataLength int // length of binary section in bytes dataLength int // length of binary section in bytes
precision int // multiplicator 10^precision for each raw value 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 offx float64 // horizontal projection offset
offy float64 // vertical projection offset offy float64 // vertical projection offset
......
...@@ -76,11 +76,16 @@ func (c *Composite) decodeRunlength(dst []RVP6, line []byte) error { ...@@ -76,11 +76,16 @@ func (c *Composite) decodeRunlength(dst []RVP6, line []byte) error {
return nil return nil
} }
// rvp6Runlength converts the raw value of level based composite products to radar video // rvp6Runlength sets the value of level based composite products to radar
// processor values (rvp-6). NaN may be returned when the given value has no internal mapping. // video processor values (rvp-6).
func (c *Composite) rvp6Runlength(value byte) RVP6 { 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 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