16 Commits

Author SHA1 Message Date
David Högborg
f799efcf32 Merge pull request #14 from dhogborg/j2ghz/palette
J2ghz/palette
2020-07-18 20:15:09 +02:00
David Högborg
c30e2324b6 Configurable palette 2020-07-18 19:50:55 +02:00
J2ghz
1ef3625c99 use heatmap.py's palette 2020-07-18 18:47:08 +02:00
David Högborg
81c84080d9 Including generated resources 2020-07-18 17:54:19 +02:00
David Högborg
7d8fcee96f Switched from Godeps to go mod 2020-07-18 17:49:04 +02:00
David Högborg
f73444f5b8 More generic makefile now builds current platform by default 2016-07-20 12:29:30 +02:00
David Högborg
ce16889d39 Version bump 2016-05-13 07:49:26 +02:00
David Högborg
fcd62b3760 Vendorized libs 2016-05-12 17:35:15 +02:00
David Högborg
bdc9dac911 building 32 bit windows platform 2016-05-12 17:26:15 +02:00
David Högborg
a07b34988f Switched freetype source vendor from google code to github 2016-01-26 09:21:27 +01:00
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
12 changed files with 499 additions and 103 deletions

1
.gitignore vendored
View File

@@ -25,4 +25,3 @@ _testmain.go
/gopow /gopow
/build /build
resources.go

View File

@@ -4,7 +4,7 @@
VERSION = $(shell git describe --always --dirty) VERSION = $(shell git describe --always --dirty)
TIMESTAMP = $(shell git show -s --format=%ct) TIMESTAMP = $(shell git show -s --format=%ct)
default: build_darwin default: build
setup: setup:
go get -u github.com/jteeuwen/go-bindata/... go get -u github.com/jteeuwen/go-bindata/...
@@ -12,34 +12,38 @@ setup:
resources: resources:
go-bindata -pkg resources -o internal/resources/resources.go resources/... go-bindata -pkg resources -o internal/resources/resources.go resources/...
build_darwin: resources build:
GOOS=darwin GOARCH=amd64 go build -a -o ./build/gopow *.go go build -o ./build/gopow *.go
zip ./build/gopow_darwin64.zip ./build/gopow
build_linux: resources all: build_darwin build_linux build_arm5 build_arm7 build_win64 build_win32
GOOS=linux GOARCH=amd64 go build -a -o ./build/gopow *.go
zip ./build/gopow_linux64.zip ./build/gopow
build_arm5: resources
GOOS=linux GOARM=5 GOARCH=arm go build -a -o ./build/gopow *.go
zip ./build/gopow_linux_arm5.zip ./build/gopow
build_arm7: resources
GOOS=linux GOARM=7 GOARCH=arm go build -a -o ./build/gopow *.go
zip ./build/gopow_linux_arm7.zip ./build/gopow
build_win: resources
GOOS=windows GOARCH=amd64 go build -a -o ./build/gopow.exe *.go
zip ./build/gopow_win64.zip ./build/gopow.exe
all: build_darwin build_linux build_arm5 build_arm7 build_win
rm ./build/gopow rm ./build/gopow
rm ./build/gopow.exe rm ./build/gopow.exe
lint: build_darwin:
golint . GOOS=darwin GOARCH=amd64 go build -a -o ./build/gopow *.go
zip ./build/gopow_darwin64.zip ./build/gopow
build_linux:
GOOS=linux GOARCH=amd64 go build -a -o ./build/gopow *.go
zip ./build/gopow_linux64.zip ./build/gopow
build_arm5:
GOOS=linux GOARM=5 GOARCH=arm go build -a -o ./build/gopow *.go
zip ./build/gopow_linux_arm5.zip ./build/gopow
build_arm7:
GOOS=linux GOARM=7 GOARCH=arm go build -a -o ./build/gopow *.go
zip ./build/gopow_linux_arm7.zip ./build/gopow
build_win64:
GOOS=windows GOARCH=amd64 go build -a -o ./build/gopow.exe *.go
zip ./build/gopow_win64.zip ./build/gopow.exe
build_win32:
GOOS=windows GOARCH=386 go build -a -o ./build/gopow.exe *.go
zip ./build/gopow_win32.zip ./build/gopow.exe
clean: clean:
rm -f build - rm -r build
rm -rf internal/resources - rm -rf internal/resources

View File

@@ -1,9 +1,8 @@
# rtl-gopow # rtl-gopow
Render tables from rtl_power to a nice heat map 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.
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).
# Availability ## 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: 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) * OS X (x64)
* Linux (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) * Linux (arm7)
* Windows (x64) * 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 ## 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. 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 ## Options
``` ```
GLOBAL OPTIONS: GLOBAL OPTIONS:
@@ -25,4 +52,7 @@ GLOBAL OPTIONS:
--help, -h show help --help, -h show help
--version, -v print the version --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).

14
go.mod Normal file
View File

@@ -0,0 +1,14 @@
module github.com/dhogborg/rtl-gopow
require (
github.com/codegangsta/cli v1.4.2-0.20150128051617-e1712f381785
github.com/dustin/go-humanize v0.0.0-20151125214831-8929fe90cee4
github.com/golang/freetype v0.0.0-20160410050536-c67e4d98d212
github.com/jteeuwen/go-bindata v3.0.7+incompatible // indirect
github.com/lucasb-eyer/go-colorful v0.0.0-20150907065137-e524a63fc3d3
github.com/sirupsen/logrus v1.6.0
github.com/stretchr/testify v1.6.1 // indirect
golang.org/x/image v0.0.0-20160423080830-f551d3a6b7fc
)
go 1.13

32
go.sum Normal file
View File

@@ -0,0 +1,32 @@
github.com/codegangsta/cli v1.4.2-0.20150128051617-e1712f381785 h1:dd7vSznOk2xIT8gJyFqbcrshRrTIWtPG7f41LLfTtMY=
github.com/codegangsta/cli v1.4.2-0.20150128051617-e1712f381785/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v0.0.0-20151125214831-8929fe90cee4 h1:WX/DKY159S5AHCpmUWGsVKoCXqLSpKd0R1150CWscw8=
github.com/dustin/go-humanize v0.0.0-20151125214831-8929fe90cee4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/golang/freetype v0.0.0-20160410050536-c67e4d98d212 h1:erqso4t6p/N1EjjNntiZUXlhmdTEIDSmM8lzICeaY/c=
github.com/golang/freetype v0.0.0-20160410050536-c67e4d98d212/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/jteeuwen/go-bindata v3.0.7+incompatible h1:91Uy4d9SYVr1kyTJ15wJsog+esAZZl7JmEfTkwmhJts=
github.com/jteeuwen/go-bindata v3.0.7+incompatible/go.mod h1:JVvhzYOiGBnFSYRyV00iY8q7/0PThjIYav1p9h5dmKs=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/lucasb-eyer/go-colorful v0.0.0-20150907065137-e524a63fc3d3 h1:HKvdgi34B5ivKjlR94L4WMyiC37Kzvmti867/EdbGRk=
github.com/lucasb-eyer/go-colorful v0.0.0-20150907065137-e524a63fc3d3/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/image v0.0.0-20160423080830-f551d3a6b7fc h1:2xx8haaLdeT9SyZMv+NzYjaPJbm5ZU4CzTiF9icqXQg=
golang.org/x/image v0.0.0-20160423080830-f551d3a6b7fc/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -3,23 +3,21 @@ package main
import ( import (
"os" "os"
log "github.com/Sirupsen/logrus"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
log "github.com/sirupsen/logrus"
"github.com/dhogborg/rtl-gopow/internal/gopow" "github.com/dhogborg/rtl-gopow/internal/gopow"
) )
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "RTL GoPow" app.Name = "RTL GoPow"
app.Usage = "Render a rtl_power CSV output as waterfall image" app.Usage = "Render a rtl_power CSV output as waterfall image"
app.Version = "0.0.1" app.Version = "0.0.4"
app.Author = "github.com/dhogborg" app.Author = "github.com/dhogborg"
app.Email = "d@hogborg.se" app.Email = "d@hogborg.se"
app.Action = func(c *cli.Context) { app.Action = func(c *cli.Context) {
if c.Bool("verbose") == true { if c.Bool("verbose") == true {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
} else { } else {
@@ -49,7 +47,6 @@ func main() {
}).Fatal("write failed") }).Fatal("write failed")
return return
} }
} }
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
@@ -68,6 +65,16 @@ func main() {
Value: "png", Value: "png",
Usage: "Output file format, default png [png,jpeg]", 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{ cli.BoolFlag{
Name: "verbose", Name: "verbose",
Usage: "Enable more verbose output", Usage: "Enable more verbose output",
@@ -76,6 +83,11 @@ func main() {
Name: "no-annotations", Name: "no-annotations",
Usage: "Disabled annotations such as time and frequency scales", Usage: "Disabled annotations such as time and frequency scales",
}, },
cli.StringFlag{
Name: "palette",
Usage: "Select the palette for output image. [spectrum,yellow]",
Value: "spectrum",
},
} }
app.Run(os.Args) app.Run(os.Args)

View File

@@ -6,9 +6,10 @@ import (
"math" "math"
"time" "time"
"code.google.com/p/freetype-go/freetype"
log "github.com/Sirupsen/logrus"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/golang/freetype"
log "github.com/sirupsen/logrus"
"golang.org/x/image/font"
"github.com/dhogborg/rtl-gopow/internal/resources" "github.com/dhogborg/rtl-gopow/internal/resources"
) )
@@ -53,7 +54,7 @@ func (a *Annotator) init() error {
return err return err
} }
font, err := freetype.ParseFont(fontBytes) luxisr, err := freetype.ParseFont(fontBytes)
if err != nil { if err != nil {
return err return err
} }
@@ -63,7 +64,7 @@ func (a *Annotator) init() error {
a.context = freetype.NewContext() a.context = freetype.NewContext()
a.context.SetDPI(dpi) a.context.SetDPI(dpi)
a.context.SetFont(font) a.context.SetFont(luxisr)
a.context.SetFontSize(size) a.context.SetFontSize(size)
a.context.SetClip(a.image.Bounds()) a.context.SetClip(a.image.Bounds())
@@ -72,9 +73,9 @@ func (a *Annotator) init() error {
switch hinting { switch hinting {
default: default:
a.context.SetHinting(freetype.NoHinting) a.context.SetHinting(font.HintingNone)
case "full": case "full":
a.context.SetHinting(freetype.FullHinting) a.context.SetHinting(font.HintingFull)
} }
return nil return nil
@@ -203,7 +204,7 @@ func (a *Annotator) DrawInfoBox() error {
pt := freetype.Pt(left, top) pt := freetype.Pt(left, top)
for _, s := range strings { for _, s := range strings {
_, _ = a.context.DrawString(s, pt) _, _ = a.context.DrawString(s, pt)
pt.Y += a.context.PointToFix32(size * spacing) pt.Y += a.context.PointToFixed(size * spacing)
} }
return nil return nil

View File

@@ -8,9 +8,13 @@ import (
"os" "os"
"time" "time"
log "github.com/Sirupsen/logrus"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
log "github.com/sirupsen/logrus"
)
const (
PowerConfigAuto = -9813
) )
type RunConfig struct { type RunConfig struct {
@@ -18,6 +22,9 @@ type RunConfig struct {
OutputFile string OutputFile string
Format string Format string
Annotations bool Annotations bool
MaxPower float64
MinPower float64
Palette string
} }
type GoPow struct { type GoPow struct {
@@ -27,12 +34,21 @@ type GoPow struct {
} }
func NewGoPow(c *cli.Context) (*GoPow, error) { func NewGoPow(c *cli.Context) (*GoPow, error) {
config := &RunConfig{ config := &RunConfig{
InputFile: c.String("input"), InputFile: c.String("input"),
OutputFile: c.String("output"), OutputFile: c.String("output"),
Format: c.String("format"), Format: c.String("format"),
Annotations: !c.Bool("no-annotations"), Annotations: !c.Bool("no-annotations"),
MaxPower: c.Float64("max-power"),
MinPower: c.Float64("min-power"),
Palette: c.String("palette"),
}
if !c.IsSet("max-power") {
config.MaxPower = PowerConfigAuto
}
if !c.IsSet("min-power") {
config.MinPower = PowerConfigAuto
} }
if config.InputFile == "" { if config.InputFile == "" {
@@ -65,11 +81,28 @@ func NewGoPow(c *cli.Context) (*GoPow, error) {
} }
func (g *GoPow) Render() 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
}
var palette Palette
switch g.config.Palette {
case "yellow":
palette = &YellowPalette{}
default:
palette = &SpectrumPalette{}
}
log.Debug("staring render") log.Debug("staring render")
g.timestamp = time.Now() g.timestamp = time.Now()
table, err := NewTable(g.config.InputFile) table, err := NewTable(g.config.InputFile, conf)
if err != nil { if err != nil {
return err return err
} }
@@ -77,13 +110,12 @@ func (g *GoPow) Render() error {
g.image = table.Image() g.image = table.Image()
for y, row := range table.Rows { 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)) g.image.Set(x, y, palette.ColorAt(table, x, y))
} }
} }
if g.config.Annotations { if g.config.Annotations {
annotator, err := NewAnnotator(g.image, table) annotator, err := NewAnnotator(g.image, table)
if err != nil { if err != nil {
return err return err
@@ -99,7 +131,6 @@ func (g *GoPow) Render() error {
} }
func (g *GoPow) Write() error { func (g *GoPow) Write() error {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"file": g.config.OutputFile, "file": g.config.OutputFile,
}).Debug("staring output write") }).Debug("staring output write")

View File

@@ -1,11 +1,12 @@
package gopow package gopow
import ( import (
"math"
"strconv" "strconv"
"strings" "strings"
"time" "time"
log "github.com/Sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
type LineComplex struct { type LineComplex struct {
@@ -108,9 +109,9 @@ func (l *LineComplex) AddSamples(line *LineComplex) {
func (l *LineComplex) HighSample() float64 { func (l *LineComplex) HighSample() float64 {
high := float64(-99999) high := float64(math.MaxFloat64 * -1)
for _, sample := range l.Samples { for _, sample := range l.Samples {
if sample > high { if sample > high && !math.IsInf(sample, 0) {
high = sample high = sample
} }
} }
@@ -119,9 +120,10 @@ func (l *LineComplex) HighSample() float64 {
} }
func (l *LineComplex) LowSample() float64 { func (l *LineComplex) LowSample() float64 {
low := float64(99999)
low := float64(math.MaxFloat64)
for _, sample := range l.Samples { for _, sample := range l.Samples {
if sample < low { if sample < low && !math.IsInf(sample, 0) {
low = sample low = sample
} }
} }

61
internal/gopow/palette.go Normal file
View File

@@ -0,0 +1,61 @@
package gopow
import (
"image/color"
"github.com/lucasb-eyer/go-colorful"
)
type Palette interface {
ColorAt(table *TableComplex, x, y int) color.Color
}
type YellowPalette struct {
}
func (p *YellowPalette) ColorAt(table *TableComplex, x, y int) color.Color {
cell := table.Rows[y].Sample(x)
hueStart := 0.0
hueEnd := 1.0
span := (*table.Config.MinPower - *table.Config.MaxPower) * -1
hPerDeg := (hueStart - hueEnd) / span
powNormalized := cell - *table.Config.MinPower
powDegrees := powNormalized * hPerDeg
hue := hueStart - powDegrees
if hue < hueStart {
hue = hueStart
}
if hue > hueEnd {
hue = hueEnd
}
return colorful.Color{hue, hue, 0}
}
type SpectrumPalette struct {
}
func (p *SpectrumPalette) ColorAt(table *TableComplex, x, y int) color.Color {
cell := table.Rows[y].Sample(x)
hueStart := 236.0
hueEnd := 0.0
span := (*table.Config.MinPower - *table.Config.MaxPower) * -1
hPerDeg := (hueStart - hueEnd) / span
powNormalized := cell - *table.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)
}

View File

@@ -1,27 +1,24 @@
package gopow package gopow
import ( import (
"bytes"
"image" "image"
"image/color"
"io/ioutil" "io/ioutil"
"math" "math"
"sort" "sort"
"strings" "strings"
"time" "time"
log "github.com/Sirupsen/logrus"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/lucasb-eyer/go-colorful" log "github.com/sirupsen/logrus"
) )
type TableComplex struct { type TableComplex struct {
File string // our input file File string // our input file
Config *RenderConfig
Rows []*LineComplex Rows []*LineComplex
Min float64 // minimum power value, used for color rendering
Max float64 // maximum dito
Bins int // horizontal slots, columns, bandwidth Bins int // horizontal slots, columns, bandwidth
Integrations int // vertical slots, rows Integrations int // vertical slots, rows
@@ -32,11 +29,18 @@ type TableComplex struct {
TimeEnd *time.Time 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") log.Debug("creating table")
t := &TableComplex{} t := &TableComplex{
Config: conf,
}
err := t.Load(file) err := t.Load(file)
if err != nil { if err != nil {
@@ -47,7 +51,6 @@ func NewTable(file string) (*TableComplex, error) {
} }
func (t *TableComplex) Load(file string) error { func (t *TableComplex) Load(file string) error {
log.Debug("loading table") log.Debug("loading table")
t.File = file t.File = file
@@ -68,18 +71,15 @@ func (t *TableComplex) Load(file string) error {
} }
func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex { func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex {
var max = float64(math.MaxFloat64 * -1)
var min = float64(math.MaxFloat64)
t.Max = float64(math.MaxFloat64 * -1) lines := bytes.Split(filebuffer, []byte("\n"))
t.Min = float64(math.MaxFloat64)
block := string(filebuffer)
lines := strings.Split(block, "\n")
table := map[string][]*LineComplex{} table := map[string][]*LineComplex{}
for _, l := range lines { for _, l := range lines {
cells := strings.Split(string(l), ",")
cells := strings.Split(l, ",")
line := NewLineComplex(cells) line := NewLineComplex(cells)
if table[line.Hash] == nil { if table[line.Hash] == nil {
@@ -93,25 +93,21 @@ func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex {
// loop over hash keys with lines // loop over hash keys with lines
for _, lines := range table { for _, lines := range table {
row := t.IntegrateLines(lines) row := t.IntegrateLines(lines)
if row != nil { if row != nil {
rows = append(rows, row) rows = append(rows, row)
if t.Min > row.LowSample() { if min > row.LowSample() {
t.Min = row.LowSample() min = row.LowSample()
} }
if t.Max < row.HighSample() { if max < row.HighSample() {
t.Max = row.HighSample() max = row.HighSample()
} }
t.HzLow = row.HzLow t.HzLow = row.HzLow
t.HzHigh = row.HzHigh t.HzHigh = row.HzHigh
if row.Time != nil { if row.Time != nil {
if t.TimeStart == nil { if t.TimeStart == nil {
t.TimeStart = row.Time t.TimeStart = row.Time
} }
@@ -128,15 +124,22 @@ func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex {
t.TimeEnd = row.Time t.TimeEnd = row.Time
} }
} }
} }
} }
sort.Sort(LineSort(rows)) 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{ log.WithFields(log.Fields{
"pMax": t.Max, "pMax": *t.Config.MaxPower,
"pMin": t.Min, "pMin": *t.Config.MinPower,
}).Debug("integrated lines") }).Debug("integrated lines")
t.Integrations = len(rows) t.Integrations = len(rows)
@@ -156,7 +159,6 @@ func (t *TableComplex) parseBuffer(filebuffer []byte) []*LineComplex {
} }
func (t *TableComplex) Image() *image.RGBA { func (t *TableComplex) Image() *image.RGBA {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"width": t.Bins, "width": t.Bins,
"height": t.Integrations, "height": t.Integrations,
@@ -166,7 +168,6 @@ func (t *TableComplex) Image() *image.RGBA {
} }
func (t *TableComplex) IntegrateLines(lines []*LineComplex) *LineComplex { func (t *TableComplex) IntegrateLines(lines []*LineComplex) *LineComplex {
if len(lines) == 0 { if len(lines) == 0 {
return nil return nil
} }
@@ -176,25 +177,7 @@ func (t *TableComplex) IntegrateLines(lines []*LineComplex) *LineComplex {
if i > 0 { if i > 0 {
masterline.AddSamples(l) masterline.AddSamples(l)
} }
} }
return masterline return masterline
} }
func (t *TableComplex) ColorAt(x, y int) color.Color {
cell := t.Rows[y].Sample(x)
hue_start := 236.0
hue_end := 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
return colorful.Hsv(hue, 1, 0.90)
}

File diff suppressed because one or more lines are too long