6 Commits

Author SHA1 Message Date
David Högborg
1c915a237d Optional manually defined power scale 2016-01-24 15:17:27 +01:00
David Högborg
480ea14a72 version bump 2015-07-21 12:15:40 +02:00
David Högborg
a1df57bb39 Instructions for building 2015-07-21 12:12:15 +02:00
David Högborg
f08490122c fixed bug where an infinity sample messed up the render 2015-07-19 09:58:31 +02:00
David Högborg
0c18fc145d switched from splitting the string block to splitting bytes and converting lines 2015-07-17 23:47:39 +02:00
David Högborg
a4ce307d34 Update to readme file 2015-07-17 22:30:43 +02:00
6 changed files with 126 additions and 39 deletions

View File

@@ -40,6 +40,6 @@ lint:
golint .
clean:
rm -f build
rm -rf internal/resources
- rm -r build
- rm -rf internal/resources

View File

@@ -1,9 +1,8 @@
# rtl-gopow
Render tables from rtl_power to a nice heat map
Here is an render of rtl_power tool scanning 80-90 MHz during 2.5 hours moving in a car. ![80-90 MHz](http://i.imgur.com/knkzLXO.jpg).
Render tables from rtl_power to a nice heat map. Faster and easier to use than other tools, gopow does not require a scripting enviroment, dependencies or development enviroment to run. Just download the binary and execute. At the same time gopow offers 2-3.6 times the performance compared to script based tools, depending on input file.
# Availability
Since Go is easy to cross compile, this tool can be easily distributed as a binary without any dependencies. You'll find it under Releases here on github. The following platforms are avalible as a ready to run binary file:
## Availability
Since Go is easy to cross compile, this tool can be easily distributed as a binary without any dependencies. You'll find it under [Releases](https://github.com/dhogborg/rtl-gopow/releases) here on github. The following platforms are avalible as a ready to run binary file:
* OS X (x64)
* Linux (x64)
@@ -11,9 +10,37 @@ Since Go is easy to cross compile, this tool can be easily distributed as a bina
* Linux (arm7)
* Windows (x64)
https://github.com/dhogborg/rtl-gopow/releases
## Building
(only needed if making changes to the source, otherwise you can download a [release binary](https://github.com/dhogborg/rtl-gopow/releases))
### Install tooling
`make setup` will install [go-bindata](https://github.com/jteeuwen/go-bindata) which is needed to build the resources. Make sure $GOPATH/bin is in your $PATH so your shell can find the tool.
### Make resources
`make resources` will compile rtl-gopow/resources/ to rtl-gopow/internal/resources/resources.go. This file is disposable and will re-generate on every build.
### Make build
Every platform has it's own make command.
* `make build_darwin`
* `make build_linux`
* `make build_arm5`
* `make build_arm7`
* `make build_win`
All commands will compile a binary to rtl-gopow/build/gopow[.exe] and create a distributable zip file. For more info on cross compilation see [this document](http://dave.cheney.net/2013/07/09/an-introduction-to-cross-compilation-with-go-1-1).
`make all` will build all platforms and create zip files in rtl-gopow/build/ and then remove the executable files.
### go build/run
Before runnning `go build *.go` or `go run *.go` make sure the resources has been generated by `make resources`.
## Performance
A render of a 600 MB csv file takes about 2 minutes on a 2,4 GHz Intel Core i5. There is still lots of room for improvement on that though. Memory usage is quite horrid.
Compared to script based tools gopow will run more than 2x faster for smaller files, and more than 3x faster for bigger files.
## Options
```
GLOBAL OPTIONS:
@@ -26,3 +53,6 @@ GLOBAL OPTIONS:
--version, -v print the version
```
## Demo
Here is an render of rtl_power tool scanning 80-90 MHz during 2.5 hours moving in a car. ![80-90 MHz](http://i.imgur.com/knkzLXO.jpg).

View File

@@ -14,7 +14,7 @@ func main() {
app := cli.NewApp()
app.Name = "RTL GoPow"
app.Usage = "Render a rtl_power CSV output as waterfall image"
app.Version = "0.0.1"
app.Version = "0.0.3"
app.Author = "github.com/dhogborg"
app.Email = "d@hogborg.se"
@@ -68,6 +68,16 @@ func main() {
Value: "png",
Usage: "Output file format, default png [png,jpeg]",
},
cli.Float64Flag{
Name: "max-power",
Value: 0,
Usage: "Define a manual maximum power (format nn.n)",
},
cli.Float64Flag{
Name: "min-power",
Value: 0,
Usage: "Define a manual minimum power (format nn.n)",
},
cli.BoolFlag{
Name: "verbose",
Usage: "Enable more verbose output",

View File

@@ -13,11 +13,17 @@ import (
"github.com/dustin/go-humanize"
)
const (
PowerConfigAuto = -9813
)
type RunConfig struct {
InputFile string
OutputFile string
Format string
Annotations bool
MaxPower float64
MinPower float64
}
type GoPow struct {
@@ -33,6 +39,15 @@ func NewGoPow(c *cli.Context) (*GoPow, error) {
OutputFile: c.String("output"),
Format: c.String("format"),
Annotations: !c.Bool("no-annotations"),
MaxPower: c.Float64("max-power"),
MinPower: c.Float64("min-power"),
}
if !c.IsSet("max-power") {
config.MaxPower = PowerConfigAuto
}
if !c.IsSet("min-power") {
config.MinPower = PowerConfigAuto
}
if config.InputFile == "" {
@@ -66,10 +81,20 @@ func NewGoPow(c *cli.Context) (*GoPow, error) {
func (g *GoPow) Render() error {
conf := &RenderConfig{}
if g.config.MaxPower != PowerConfigAuto {
conf.MaxPower = &g.config.MaxPower
}
if g.config.MinPower != PowerConfigAuto {
conf.MinPower = &g.config.MinPower
}
log.Debug("staring render")
g.timestamp = time.Now()
table, err := NewTable(g.config.InputFile)
table, err := NewTable(g.config.InputFile, conf)
if err != nil {
return err
}
@@ -77,7 +102,7 @@ func (g *GoPow) Render() error {
g.image = table.Image()
for y, row := range table.Rows {
for x, _ := range row.Samples {
for x := range row.Samples {
g.image.Set(x, y, table.ColorAt(x, y))
}
}

View File

@@ -1,6 +1,7 @@
package gopow
import (
"math"
"strconv"
"strings"
"time"
@@ -108,9 +109,9 @@ func (l *LineComplex) AddSamples(line *LineComplex) {
func (l *LineComplex) HighSample() float64 {
high := float64(-99999)
high := float64(math.MaxFloat64 * -1)
for _, sample := range l.Samples {
if sample > high {
if sample > high && !math.IsInf(sample, 0) {
high = sample
}
}
@@ -119,9 +120,10 @@ func (l *LineComplex) HighSample() float64 {
}
func (l *LineComplex) LowSample() float64 {
low := float64(99999)
low := float64(math.MaxFloat64)
for _, sample := range l.Samples {
if sample < low {
if sample < low && !math.IsInf(sample, 0) {
low = sample
}
}

View File

@@ -1,6 +1,7 @@
package gopow
import (
"bytes"
"image"
"image/color"
"io/ioutil"
@@ -16,12 +17,10 @@ import (
type TableComplex struct {
File string // our input file
Config *RenderConfig
Rows []*LineComplex
Min float64 // minimum power value, used for color rendering
Max float64 // maximum dito
Bins int // horizontal slots, columns, bandwidth
Integrations int // vertical slots, rows
@@ -32,11 +31,19 @@ type TableComplex struct {
TimeEnd *time.Time
}
func NewTable(file string) (*TableComplex, error) {
// RenderConfig overrides automaticly calculated defaults
type RenderConfig struct {
MinPower *float64 // minimum power value, used for color rendering
MaxPower *float64 // maximum dito
}
func NewTable(file string, conf *RenderConfig) (*TableComplex, error) {
log.Debug("creating table")
t := &TableComplex{}
t := &TableComplex{
Config: conf,
}
err := t.Load(file)
if err != nil {
@@ -69,17 +76,16 @@ func (t *TableComplex) Load(file string) error {
func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex {
t.Max = float64(math.MaxFloat64 * -1)
t.Min = float64(math.MaxFloat64)
var max = float64(math.MaxFloat64 * -1)
var min = float64(math.MaxFloat64)
block := string(filebuffer)
lines := strings.Split(block, "\n")
lines := bytes.Split(filebuffer, []byte("\n"))
table := map[string][]*LineComplex{}
for _, l := range lines {
cells := strings.Split(l, ",")
cells := strings.Split(string(l), ",")
line := NewLineComplex(cells)
if table[line.Hash] == nil {
@@ -100,11 +106,11 @@ func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex {
rows = append(rows, row)
if t.Min > row.LowSample() {
t.Min = row.LowSample()
if min > row.LowSample() {
min = row.LowSample()
}
if t.Max < row.HighSample() {
t.Max = row.HighSample()
if max < row.HighSample() {
max = row.HighSample()
}
t.HzLow = row.HzLow
@@ -134,9 +140,17 @@ func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex {
sort.Sort(LineSort(rows))
if t.Config.MaxPower == nil {
t.Config.MaxPower = &max
}
if t.Config.MinPower == nil {
t.Config.MinPower = &min
}
log.WithFields(log.Fields{
"pMax": t.Max,
"pMin": t.Min,
"pMax": *t.Config.MaxPower,
"pMin": *t.Config.MinPower,
}).Debug("integrated lines")
t.Integrations = len(rows)
@@ -176,7 +190,6 @@ func (t *TableComplex) IntegrateLines(lines []*LineComplex) *LineComplex {
if i > 0 {
masterline.AddSamples(l)
}
}
return masterline
@@ -186,14 +199,21 @@ func (t *TableComplex) ColorAt(x, y int) color.Color {
cell := t.Rows[y].Sample(x)
hue_start := 236.0
hue_end := 0.0
hueStart := 236.0
hueEnd := 0.0
span := (t.Min - t.Max) * -1
h_per_deg := (hue_start - hue_end) / span
pow_normalized := cell - t.Min
pow_degrees := pow_normalized * h_per_deg
hue := hue_start - pow_degrees
span := (*t.Config.MinPower - *t.Config.MaxPower) * -1
hPerDeg := (hueStart - hueEnd) / span
powNormalized := cell - *t.Config.MinPower
powDegrees := powNormalized * hPerDeg
hue := hueStart - powDegrees
if hue > hueStart {
hue = hueStart
}
if hue < hueEnd {
hue = hueEnd
}
return colorful.Hsv(hue, 1, 0.90)