runlength.go 2.13 KB
Newer Older
Jonny Schäfer's avatar
Jonny Schäfer committed
1
2
3
4
5
6
7
8
package radolan

import (
	"bufio"
	"math"
)

// parseRunlength parses the runlength encoded composite and writes into the
9
// previously created PlainData field of the composite.
Jonny Schäfer's avatar
Jonny Schäfer committed
10
func (c *Composite) parseRunlength(reader *bufio.Reader) error {
11
	for i := range c.PlainData {
Jonny Schäfer's avatar
Jonny Schäfer committed
12
13
14
15
16
		line, err := c.readLineRunlength(reader)
		if err != nil {
			return err
		}

17
		err = c.decodeRunlength(c.PlainData[i], line)
Jonny Schäfer's avatar
Jonny Schäfer committed
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
		if err != nil {
			return err
		}
	}

	return nil
}

// readLineRunlength reads a line until newline (non inclusive) from the given reader.
// This method is used to get a line of runlenth encoded data.
func (c *Composite) readLineRunlength(rd *bufio.Reader) (line []byte, err error) {
	line, err = rd.ReadBytes('\x0A')
	if err != nil {
		err = newError("readLineRunlength", err.Error())
	}
	length := len(line)
	if length > 0 {
		line = line[:length-1]
	}
	return
}

// decodeRunlength decodes the source line and writes to the given destination.
func (c *Composite) decodeRunlength(dst []RVP6, line []byte) error {
	// fill destination as runlength encoding will induce gaps
	nan := RVP6(math.NaN())
	for i := range dst {
		dst[i] = nan
	}

	dstpos := 0
	offset := true
	for i, value := range line {
		switch true {
		case i == 0: // skip useless line number
		case offset: // calculate offset
			if value < 16 {
				return newError("decodeRunlength", "invalid offset value")
			}

			dstpos += int(value) - 16 // update offset position
			offset = value == 255     // see if next byte will be also offset
		default:
			// value [XXXX|YYYY] decodes to YYYY repeated XXXX times.
			runlength := int(value >> 4)
			value &= 0x0F

			for j := 0; j < runlength; j++ {
				if dstpos >= len(dst) {
					return newError("decodeRunlength", "destination size exceeded")
				}

				dst[dstpos] = c.rvp6Runlength(value)
				dstpos++
			}
		}
	}

	return nil
}

79
80
// rvp6Runlength sets the value of level based composite products to radar
// video processor values (rvp-6).
Jonny Schäfer's avatar
Jonny Schäfer committed
81
func (c *Composite) rvp6Runlength(value byte) RVP6 {
82
	if value == 0 {
Jonny Schäfer's avatar
Jonny Schäfer committed
83
84
		return RVP6(math.NaN())
	}
85
86
87
88
89
90
	value--

	if int(value) >= len(c.level) { // border markings
		return RVP6(math.NaN())
	}
	return c.level[value]
Jonny Schäfer's avatar
Jonny Schäfer committed
91
}