From ec3cd3cb162683f65c15ed4400ab2c491954e2ac Mon Sep 17 00:00:00 2001 From: kinglevel Date: Mon, 4 Nov 2024 17:55:21 +0100 Subject: [PATCH 1/2] fixed missing R15, 1nm and 5nm missing datapoints, flags added --- README.md | 26 ++++- cmd/skread/main.go | 244 +++++++++++++++++++++++++++++++++++++++------ device.go | 2 + measurement.go | 22 +++- 4 files changed, 258 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 85fcd5d..21d8e24 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Based on original C# SDK for Windows from SEKONIC. - Sekonic C-700 - Sekonic C-800 +- Sekonic C-800-U (US Version. Basically the same as C-800) - Sekonic C-7000 (supports extended measurement configuration: FOV and Exposure Time) ## Supported (tested) platforms @@ -20,6 +21,10 @@ Based on original C# SDK for Windows from SEKONIC. - Windows - Linux +## Known limitations + +TM-30 and TCLI only works on C-7000 firmware accordingly to SEKONIC SDK manual page 11? + ## Dependencies Default implementation uses [gousb](https://github.com/google/gousb) wrapper for the libusb library. @@ -35,10 +40,29 @@ _Alternatively_ you can provide custom USB implementation with [simple interface See the [skread](cmd/skread/main.go) command implementation. +## MacOS install example + + +Use homebrew to install go. +go on MacOS will take care of dependencies when running this script for the first time. + +``` +homebrew install go +``` + +Connect your device. +Then simply run example below. + + + ## Run example ``` -go run ./cmd/skread +go run ./cmd/skread --run --all +``` + +``` +go run ./cmd/skread --help ``` ## License diff --git a/cmd/skread/main.go b/cmd/skread/main.go index e28ec40..e75cc75 100644 --- a/cmd/skread/main.go +++ b/cmd/skread/main.go @@ -3,47 +3,227 @@ package main import ( "fmt" "strconv" - + "flag" + "os" "github.com/akares/skreader" ) func main() { - // Connect to SEKONIC device. - sk, err := skreader.NewDeviceWithAdapter(&skreader.GousbAdapter{}) - if err != nil { - panic(err) - } - defer sk.Close() - // Get some basic info of the device. - model, _ := sk.ModelName() - fw, _ := sk.FirmwareVersion() + // Help + help := flag.Bool("help", false, "Display help information") + + // Mode + run := flag.Bool("run", false, "Runs a normal measurement and outputs the selected data") + + // Device info + info := flag.Bool("info", false, "Shows info about the connected device") + + // Data to show and later save + all := flag.Bool("all", false, "Shows all data from the connected device") + illuminance := flag.Bool("illuminance", false, "Shows illuminance data") + ColorTemperature := flag.Bool("ColorTemperature", false, "Shows ColorTemperature data") + Tristimulus := flag.Bool("Tristimulus", false, "Shows Tristimulus data") + CIE1931 := flag.Bool("CIE1931", false, "Shows CIE1931 data") + CIE1976 := flag.Bool("CIE1976", false, "Shows CIE1976 data") + DWL := flag.Bool("DWL", false, "Shows DWL data") + CRI := flag.Bool("CRI", false, "Shows CRI data") + Spectra1nm := flag.Bool("Spectra1nm", false, "Shows Spectra1nm data") + Spectra5nm := flag.Bool("Spectra5nm", false, "Shows Spectra5nm data") + + // Special + LDi := flag.Bool("LDi", false, "Shows the most interesting data for LDs") + + - fmt.Println(strconv.Quote(sk.String())) - fmt.Println("MN:", strconv.Quote(model)) - fmt.Println("FW:", fw) + // Parse all flags + flag.Parse() - // Get the current operational mode, knobs and buttons states of the device. - st, err := sk.State() - if err != nil { - panic(err) + + + // Help + if *help { + fmt.Println("Usage: go run main.go [options]") + fmt.Println("Example: go run main.go --run --all") + fmt.Println("\nOptions:") + flag.PrintDefaults() + os.Exit(0) } - fmt.Printf("ST: %+v\n", st) - // Run one measurement. - meas, err := sk.Measure() - if err != nil { - panic(err) + // Set the rest to true if all is set. + if *all { + *info = true + *illuminance = true + *ColorTemperature = true + *Tristimulus = true + *CIE1931 = true + *CIE1976 = true + *DWL = true + *CRI = true + *Spectra1nm = true + *Spectra5nm = true } - // Print the measurement result in various vays. - fmt.Printf("Meas: %s\n", meas.Repr()) - fmt.Printf("Meas: %s\n", meas.String()) - fmt.Printf( - "Lux=%s x=%s y=%s CCT=%s\n", - meas.Illuminance.Lux.Str, - meas.CIE1931.X.Str, - meas.CIE1931.Y.Str, - meas.ColorTemperature.Tcp.Str, - ) + + + if *run { + + // Connect to SEKONIC device. + sk, err := skreader.NewDeviceWithAdapter(&skreader.GousbAdapter{}) + if err != nil { + panic(err) + } + defer sk.Close() + + // Get some basic info of the device. + model, _ := sk.ModelName() + fw, _ := sk.FirmwareVersion() + + + // Get the current operational mode, knobs and buttons states of the device. + st, err := sk.State() + if err != nil { + panic(err) + } + + + // print the device info + if *info { + fmt.Println(strconv.Quote(sk.String())) + fmt.Println("Model:", strconv.Quote(model)) + fmt.Println("Firmware:", fw) + + fmt.Printf("State: %+v\n", st) + } + + + + + + // Run one measurement. + meas, err := sk.Measure() + if err != nil { + panic(err) + } + + + + + if *illuminance { + + fmt.Printf("------------\n") + fmt.Printf("Illuminance:\n") + fmt.Printf("LUX: %s\n", meas.Illuminance.Lux.Str) + fmt.Printf("Fc: %s\n", meas.Illuminance.FootCandle) + + } + + + + if *ColorTemperature { + + fmt.Printf("------------\n") + fmt.Printf("ColorTemperature:\n") + fmt.Printf("CCT: %s\n", meas.ColorTemperature.Tcp) + fmt.Printf("CCT DeltaUv: %s\n", meas.ColorTemperature.DeltaUv) + + } + + + + if *Tristimulus { + fmt.Printf("------------\n") + fmt.Printf("Tristimulus:\n") + fmt.Printf("X: %s\n", meas.Tristimulus.X) + fmt.Printf("Y: %s\n", meas.Tristimulus.Y) + fmt.Printf("Z: %s\n", meas.Tristimulus.Z) + } + + + + + if *CIE1931 { + fmt.Printf("------------\n") + fmt.Printf("CIE1931:\n") + fmt.Printf("X: %s\n", meas.CIE1931.X) + fmt.Printf("Y: %s\n", meas.CIE1931.Y) + } + + + + if *CIE1976 { + fmt.Printf("------------\n") + fmt.Printf("CIE1976:\n") + fmt.Printf("Ud: %s\n", meas.CIE1976.Ud) + fmt.Printf("Vd: %s\n", meas.CIE1976.Vd) + } + + + if *DWL{ + + fmt.Printf("------------\n") + fmt.Printf("DominantWavelength:\n") + fmt.Printf("Wavelength: %s\n", meas.DWL.Wavelength) + fmt.Printf("ExcitationPurity: %s\n", meas.DWL.ExcitationPurity) + + } + + + + if *CRI { + fmt.Printf("------------\n") + fmt.Printf("CRI:\n") + fmt.Printf("RA: %s\n", meas.ColorRenditionIndexes.Ra) + + for i := range meas.ColorRenditionIndexes.Ri { + fmt.Printf("R%d: %s\n", i+1, meas.ColorRenditionIndexes.Ri[i]) + } + } + + + + + if *Spectra1nm { + fmt.Printf("------------\n") + fmt.Printf("SpectralData 1nm:\n") + for i := range meas.SpectralData1nm { + //Missing one datapoint? + var wavelength int = 380+i + + fmt.Printf("%d,%f\n", wavelength, meas.SpectralData1nm[i].Val) + + } + } + + + + if *Spectra5nm { + fmt.Printf("------------\n") + fmt.Printf("SpectralData 5nm:\n") + for i := range meas.SpectralData5nm { + //Missing one datapoint? + var wavelength int = 380+(i*5) + + fmt.Printf("%d,%f\n", wavelength, meas.SpectralData5nm[i].Val) + + } + } + + + if *LDi { + + fmt.Printf("LUX: %s\n", meas.Illuminance.Lux.Str) + fmt.Printf("CCT: %s\n", meas.ColorTemperature.Tcp) + fmt.Printf("CCT DeltaUv: %s\n", meas.ColorTemperature.DeltaUv) + fmt.Printf("RA: %s\n", meas.ColorRenditionIndexes.Ra) + fmt.Printf("R9: %s\n", meas.ColorRenditionIndexes.Ri[8]) + + } + + + + } + + + } diff --git a/device.go b/device.go index 03f87cc..abea32a 100644 --- a/device.go +++ b/device.go @@ -144,6 +144,8 @@ func (d *Device) Measure() (*Measurement, error) { return d.MeasurementResult() } + + // WaitReady waits for device to be ready for next measurement. // It polls device state every step duration until idle status is reached or timeout duration is reached. // If device state is not valid for measurement, error is returned. diff --git a/measurement.go b/measurement.go index 5517459..5aa55bf 100644 --- a/measurement.go +++ b/measurement.go @@ -19,8 +19,8 @@ type Measurement struct { ColorRenditionIndexes ColorRenditionIndexesValue // Color Rendition Indexes - SpectralData5nm [80]DecimalValue // Spectral Data (5nm) - SpectralData1nm [400]DecimalValue // Spectral Data (1nm) + SpectralData5nm [81]DecimalValue // Spectral Data (5nm) + SpectralData1nm [401]DecimalValue // Spectral Data (1nm) PeakWavelength int // Peak Wavelength (380...780nm) } @@ -109,7 +109,7 @@ type DominantWavelengthValue struct { // ColorRenditionIndexesValue represents a color rendition indexes Ra and Ri. type ColorRenditionIndexesValue struct { Ra DecimalValue - Ri [14]DecimalValue + Ri [15]DecimalValue } // Not implemented here but available for C-7000 FW > 25 extended measurement data: @@ -130,16 +130,24 @@ func NewMeasurementFromBytes(data []byte) (*Measurement, error) { // Parse binary data to struct + + //ColorTemperature m.ColorTemperature.Tcp = toDecimalValue(parseFloat32(data, 50), 1563, 100000, 0) m.ColorTemperature.DeltaUv = toDecimalValue(parseFloat32(data, 55), -0.1, 0.1, 4) if m.ColorTemperature.DeltaUv.Range != RangeOk { // limit the CCT value (C-800 returns Tcp=50000 value instead of "Over" as C-7000 does) m.ColorTemperature.Tcp.Range = m.ColorTemperature.DeltaUv.Range } + + //Illuminance m.Illuminance.Lux = parseLuxToDecimalValue(data, 271, 100, 200000) m.Illuminance.FootCandle = parseLuxToDecimalValue(data, 276, 0.093000002205371857, 18580.607421875) + + //Tristimulus m.Tristimulus.X = toDecimalValue(parseFloat64(data, 281), 0, 1000000, 4) m.Tristimulus.Y = toDecimalValue(parseFloat64(data, 290), 0, 1000000, 4) m.Tristimulus.Z = toDecimalValue(parseFloat64(data, 299), 0, 1000000, 4) + + //CIE1931 m.CIE1931.X = toDecimalValue(parseFloat32(data, 308), 0, 1, 4) m.CIE1931.Y = toDecimalValue(parseFloat32(data, 313), 0, 1, 4) if m.CIE1931.X.Range != RangeOk { @@ -149,14 +157,22 @@ func NewMeasurementFromBytes(data []byte) (*Measurement, error) { } else { m.CIE1931.Z = toDecimalValue(1.0-m.CIE1931.X.Val-m.CIE1931.Y.Val, 0, 1, 4) } + + //CIE1976 m.CIE1976.Ud = toDecimalValue(parseFloat32(data, 328), 0, 1, 4) m.CIE1976.Vd = toDecimalValue(parseFloat32(data, 333), 0, 1, 4) + + //Dominant wavelength m.DWL.Wavelength = toDecimalValue(parseFloat32(data, 338), -780, 780, 0) m.DWL.ExcitationPurity = toDecimalValue(parseFloat32(data, 343), 0, 100, 1) + + //CRI m.ColorRenditionIndexes.Ra = toDecimalValue(parseFloat32(data, 348), -100, 100, 1) for i := range m.ColorRenditionIndexes.Ri { m.ColorRenditionIndexes.Ri[i] = toDecimalValue(parseFloat32(data, 353+i*5), -100, 100, 1) } + + //Boundaries check if m.Illuminance.Lux.Range == RangeUnder { for i := range m.SpectralData5nm { m.SpectralData5nm[i].Range = RangeUnder From b40954ff3df2b84b89644fbaba75e474732ee5e5 Mon Sep 17 00:00:00 2001 From: Aleksandr Reshetniak Date: Tue, 5 Nov 2024 10:46:06 +0100 Subject: [PATCH 2/2] Apply fmt; Refactor a bit; Improve docs --- .gitignore | 1 + README.md | 100 ++++++++++++-- cmd/skread/main.go | 321 +++++++++++++++++++-------------------------- device.go | 2 - measurement.go | 32 +++-- 5 files changed, 239 insertions(+), 217 deletions(-) diff --git a/.gitignore b/.gitignore index ce2a4ad..86c0f0f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ coverage.out coverage.html /tmp +skread diff --git a/README.md b/README.md index 21d8e24..43732b8 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,13 @@ Based on original C# SDK for Windows from SEKONIC. ## Known limitations -TM-30 and TCLI only works on C-7000 firmware accordingly to SEKONIC SDK manual page 11? +Currently **only ambient** measuring mode is supported. -## Dependencies +TM-30, SSI and TLCI measurements are available for Sekonic C-7000 with FW version > 25 but parsing of these fields is **not implemented yet**. + +## Usage + +### Install USB driver Default implementation uses [gousb](https://github.com/google/gousb) wrapper for the libusb library. @@ -34,37 +38,107 @@ You must have [libusb-1.0](https://github.com/libusb/libusb/wiki) installed on y Installation for different platforms is covered in [gousb documentation](https://github.com/google/gousb/blob/master/README.md#dependencies). -_Alternatively_ you can provide custom USB implementation with [simple interface](usbadapter.go) close to io.Reader. See the default [gousb based implementation](gousb_adapter.go) for reference. +_If you use Linux, you probably already have it._ +_If you use Mac ot Windows and was using USB devices that needed custom driver, you also probably have it._ -## SDK usage +_Alternatively_ you can provide custom USB implementation with [simple interface](usbadapter.go) close to io.Reader. See the default [gousb based implementation](gousb_adapter.go) for reference. -See the [skread](cmd/skread/main.go) command implementation. +### Install Go -## MacOS install example +You need `Go` programming language to be installed first. +See the Go's [Download and install](https://go.dev/doc/install) page for installation instructions for different OS. -Use homebrew to install go. -go on MacOS will take care of dependencies when running this script for the first time. +On MacOS just use [Homebrew](https://brew.sh/): ``` homebrew install go ``` -Connect your device. -Then simply run example below. +### Run example measurement program usung Go + +1. Connect your device. + +2. Run commands below: + +``` +go run ./cmd/skread -run -all +``` + +``` +go run ./cmd/skread -help +``` + +_Go will take care of dependencies when running this script for the first time._ + +### Build standalone program executable (if needed) +``` +go build ./cmd/skread +``` +_This will create binary executable file named `skread` in the same directory._ -## Run example +Run the executable: ``` -go run ./cmd/skread --run --all +./skread -help ``` +_Now you can run this file on any other machine with the same OS and architecture as the one you built it with. But remeber about USB driver dependency mentioned earlier, it is needed for program to run._ + +### Create your own measurement program (if needed) + +Note: the [cmd/skread/main.go](cmd/skread/main.go) is just an example of the library usage. If more specific funtionality is needed, you always can create your own Go program and import the library in the same way as example does. Go will fetch it automatically. + +Example: + +1. Create your project dir: + ``` -go run ./cmd/skread --help +mkdir my_skreader && cd my_skreader ``` +2. Initialise Go module named `my_skreader` and add `skreader` library as an external dependency: + +``` +go mod init my_skreader +go get github.com/akares/skreader +``` + +3. Download `main.go` example file there (or create manually): + +``` +curl -OL https://raw.githubusercontent.com/akares/skreader/main/cmd/skread/main.go +``` + +4. Run: + +``` +go run . -help +``` + +5. _Modify the program according to your needs._ + +6. Build standalone executable (if needed): + +``` +go build -o my_skreader ./main.go +./my_skreader -help +``` + +_You will still need to take care of the USB driver dependency mentioned earlier._ + +## Library API + +See the [skread](cmd/skread/main.go) command implementation for details. + +## Contribution + +1. Use `gofmt` +2. Ensure unit tests are okay: `go test ./...` +3. Ensure Go linter finds no errors: `golangci-lint run .` + ## License This project is licensed under the terms of the MIT license. diff --git a/cmd/skread/main.go b/cmd/skread/main.go index e75cc75..8799e0f 100644 --- a/cmd/skread/main.go +++ b/cmd/skread/main.go @@ -1,229 +1,174 @@ package main import ( - "fmt" - "strconv" "flag" + "fmt" "os" + "strconv" + "github.com/akares/skreader" ) +//nolint:funlen,gocyclo func main() { - - // Help - help := flag.Bool("help", false, "Display help information") - - // Mode + // Available commands + help := flag.Bool("help", false, "Shows usage information") run := flag.Bool("run", false, "Runs a normal measurement and outputs the selected data") - // Device info - info := flag.Bool("info", false, "Shows info about the connected device") - - // Data to show and later save - all := flag.Bool("all", false, "Shows all data from the connected device") - illuminance := flag.Bool("illuminance", false, "Shows illuminance data") - ColorTemperature := flag.Bool("ColorTemperature", false, "Shows ColorTemperature data") - Tristimulus := flag.Bool("Tristimulus", false, "Shows Tristimulus data") - CIE1931 := flag.Bool("CIE1931", false, "Shows CIE1931 data") - CIE1976 := flag.Bool("CIE1976", false, "Shows CIE1976 data") - DWL := flag.Bool("DWL", false, "Shows DWL data") - CRI := flag.Bool("CRI", false, "Shows CRI data") - Spectra1nm := flag.Bool("Spectra1nm", false, "Shows Spectra1nm data") - Spectra5nm := flag.Bool("Spectra5nm", false, "Shows Spectra5nm data") - - // Special - LDi := flag.Bool("LDi", false, "Shows the most interesting data for LDs") - + // Data to show + showInfo := flag.Bool("info", false, "Shows info about the connected device") + showAll := flag.Bool("all", false, "Shows all data from the connected device") + showIlluminance := flag.Bool("Illuminance", false, "Shows illuminance data") + showColorTemperature := flag.Bool("ColorTemperature", false, "Shows ColorTemperature data") + showTristimulus := flag.Bool("Tristimulus", false, "Shows Tristimulus data") + showCIE1931 := flag.Bool("CIE1931", false, "Shows CIE1931 data") + showCIE1976 := flag.Bool("CIE1976", false, "Shows CIE1976 data") + showDWL := flag.Bool("DWL", false, "Shows DWL data") + show := flag.Bool("CRI", false, "Shows CRI data") + showSpectra1nm := flag.Bool("Spectra1nm", false, "Shows Spectra1nm data") + showSpectra5nm := flag.Bool("Spectra5nm", false, "Shows Spectra5nm data") + + // Shown by default if no other flag is set + showLDi := flag.Bool("LDi", false, "Shows the most interesting data for LDs") - - // Parse all flags flag.Parse() - - - // Help - if *help { - fmt.Println("Usage: go run main.go [options]") - fmt.Println("Example: go run main.go --run --all") + if *help || len(os.Args) == 1 { + fmt.Println("Usage: skreader [options]") + fmt.Println("Example: skreader -run -all") fmt.Println("\nOptions:") flag.PrintDefaults() + os.Exit(0) } - // Set the rest to true if all is set. - if *all { - *info = true - *illuminance = true - *ColorTemperature = true - *Tristimulus = true - *CIE1931 = true - *CIE1976 = true - *DWL = true - *CRI = true - *Spectra1nm = true - *Spectra5nm = true + if *run && len(os.Args) == 2 { + *showLDi = true } + if *showAll { + *showInfo = true + *showIlluminance = true + *showColorTemperature = true + *showTristimulus = true + *showCIE1931 = true + *showCIE1976 = true + *showDWL = true + *show = true + *showSpectra1nm = true + *showSpectra5nm = true + } + if *run { + // Connect to SEKONIC device. + sk, err := skreader.NewDeviceWithAdapter(&skreader.GousbAdapter{}) + if err != nil { + panic(err) + } + defer sk.Close() - if *run { - - // Connect to SEKONIC device. - sk, err := skreader.NewDeviceWithAdapter(&skreader.GousbAdapter{}) - if err != nil { - panic(err) - } - defer sk.Close() - - // Get some basic info of the device. - model, _ := sk.ModelName() - fw, _ := sk.FirmwareVersion() - - - // Get the current operational mode, knobs and buttons states of the device. - st, err := sk.State() - if err != nil { - panic(err) - } - - - // print the device info - if *info { - fmt.Println(strconv.Quote(sk.String())) - fmt.Println("Model:", strconv.Quote(model)) - fmt.Println("Firmware:", fw) - - fmt.Printf("State: %+v\n", st) - } - - - - - - // Run one measurement. - meas, err := sk.Measure() - if err != nil { - panic(err) - } - - - - - if *illuminance { - - fmt.Printf("------------\n") - fmt.Printf("Illuminance:\n") - fmt.Printf("LUX: %s\n", meas.Illuminance.Lux.Str) - fmt.Printf("Fc: %s\n", meas.Illuminance.FootCandle) - - } - - - - if *ColorTemperature { - - fmt.Printf("------------\n") - fmt.Printf("ColorTemperature:\n") - fmt.Printf("CCT: %s\n", meas.ColorTemperature.Tcp) - fmt.Printf("CCT DeltaUv: %s\n", meas.ColorTemperature.DeltaUv) - - } - - - - if *Tristimulus { - fmt.Printf("------------\n") - fmt.Printf("Tristimulus:\n") - fmt.Printf("X: %s\n", meas.Tristimulus.X) - fmt.Printf("Y: %s\n", meas.Tristimulus.Y) - fmt.Printf("Z: %s\n", meas.Tristimulus.Z) - } - - - - - if *CIE1931 { - fmt.Printf("------------\n") - fmt.Printf("CIE1931:\n") - fmt.Printf("X: %s\n", meas.CIE1931.X) - fmt.Printf("Y: %s\n", meas.CIE1931.Y) - } - - - - if *CIE1976 { - fmt.Printf("------------\n") - fmt.Printf("CIE1976:\n") - fmt.Printf("Ud: %s\n", meas.CIE1976.Ud) - fmt.Printf("Vd: %s\n", meas.CIE1976.Vd) - } + // Get some basic info of the device. + model, _ := sk.ModelName() + fw, _ := sk.FirmwareVersion() + // Get the current operational mode, knobs and buttons states of the device. + st, err := sk.State() + if err != nil { + panic(err) + } - if *DWL{ + // Print the device info + if *showInfo { + fmt.Println(strconv.Quote(sk.String())) + fmt.Println("Model:", strconv.Quote(model)) + fmt.Println("Firmware:", fw) + fmt.Printf("State: %+v\n", st) + } - fmt.Printf("------------\n") - fmt.Printf("DominantWavelength:\n") - fmt.Printf("Wavelength: %s\n", meas.DWL.Wavelength) - fmt.Printf("ExcitationPurity: %s\n", meas.DWL.ExcitationPurity) + // Run one measurement. + meas, err := sk.Measure() + if err != nil { + panic(err) + } - } + if *showIlluminance { + fmt.Printf("------------\n") + fmt.Printf("Illuminance:\n") + fmt.Printf("LUX: %s\n", meas.Illuminance.Lux.Str) + fmt.Printf("Fc: %s\n", meas.Illuminance.FootCandle) + } - + if *showColorTemperature { + fmt.Printf("------------\n") + fmt.Printf("ColorTemperature:\n") + fmt.Printf("CCT: %s\n", meas.ColorTemperature.Tcp) + fmt.Printf("CCT DeltaUv: %s\n", meas.ColorTemperature.DeltaUv) + } - if *CRI { - fmt.Printf("------------\n") - fmt.Printf("CRI:\n") - fmt.Printf("RA: %s\n", meas.ColorRenditionIndexes.Ra) + if *showTristimulus { + fmt.Printf("------------\n") + fmt.Printf("Tristimulus:\n") + fmt.Printf("X: %s\n", meas.Tristimulus.X) + fmt.Printf("Y: %s\n", meas.Tristimulus.Y) + fmt.Printf("Z: %s\n", meas.Tristimulus.Z) + } - for i := range meas.ColorRenditionIndexes.Ri { - fmt.Printf("R%d: %s\n", i+1, meas.ColorRenditionIndexes.Ri[i]) - } - } + if *showCIE1931 { + fmt.Printf("------------\n") + fmt.Printf("CIE1931:\n") + fmt.Printf("X: %s\n", meas.CIE1931.X) + fmt.Printf("Y: %s\n", meas.CIE1931.Y) + } - + if *showCIE1976 { + fmt.Printf("------------\n") + fmt.Printf("CIE1976:\n") + fmt.Printf("Ud: %s\n", meas.CIE1976.Ud) + fmt.Printf("Vd: %s\n", meas.CIE1976.Vd) + } + if *showDWL { + fmt.Printf("------------\n") + fmt.Printf("DominantWavelength:\n") + fmt.Printf("Wavelength: %s\n", meas.DWL.Wavelength) + fmt.Printf("ExcitationPurity: %s\n", meas.DWL.ExcitationPurity) + } - if *Spectra1nm { - fmt.Printf("------------\n") - fmt.Printf("SpectralData 1nm:\n") - for i := range meas.SpectralData1nm { - //Missing one datapoint? - var wavelength int = 380+i - - fmt.Printf("%d,%f\n", wavelength, meas.SpectralData1nm[i].Val) - - } + if *show { + fmt.Printf("------------\n") + fmt.Printf("CRI:\n") + fmt.Printf("RA: %s\n", meas.ColorRenditionIndexes.Ra) + for i := range meas.ColorRenditionIndexes.Ri { + fmt.Printf("R%d: %s\n", i+1, meas.ColorRenditionIndexes.Ri[i]) } + } - - - if *Spectra5nm { - fmt.Printf("------------\n") - fmt.Printf("SpectralData 5nm:\n") - for i := range meas.SpectralData5nm { - //Missing one datapoint? - var wavelength int = 380+(i*5) - - fmt.Printf("%d,%f\n", wavelength, meas.SpectralData5nm[i].Val) - - } + if *showSpectra1nm { + fmt.Printf("------------\n") + fmt.Printf("SpectralData 1nm:\n") + for i := range meas.SpectralData1nm { + // TODO: Missing one datapoint? + wavelength := 380 + i + fmt.Printf("%d,%f\n", wavelength, meas.SpectralData1nm[i].Val) } - - - if *LDi { - - fmt.Printf("LUX: %s\n", meas.Illuminance.Lux.Str) - fmt.Printf("CCT: %s\n", meas.ColorTemperature.Tcp) - fmt.Printf("CCT DeltaUv: %s\n", meas.ColorTemperature.DeltaUv) - fmt.Printf("RA: %s\n", meas.ColorRenditionIndexes.Ra) - fmt.Printf("R9: %s\n", meas.ColorRenditionIndexes.Ri[8]) + } + if *showSpectra5nm { + fmt.Printf("------------\n") + fmt.Printf("SpectralData 5nm:\n") + for i := range meas.SpectralData5nm { + // TODO: Missing one datapoint? + wavelength := 380 + (i * 5) + fmt.Printf("%d,%f\n", wavelength, meas.SpectralData5nm[i].Val) } - - - } - - + if *showLDi { + fmt.Printf("LUX: %s\n", meas.Illuminance.Lux.Str) + fmt.Printf("CCT: %s\n", meas.ColorTemperature.Tcp) + fmt.Printf("CCT DeltaUv: %s\n", meas.ColorTemperature.DeltaUv) + fmt.Printf("RA: %s\n", meas.ColorRenditionIndexes.Ra) + fmt.Printf("R9: %s\n", meas.ColorRenditionIndexes.Ri[8]) + } + } } diff --git a/device.go b/device.go index abea32a..03f87cc 100644 --- a/device.go +++ b/device.go @@ -144,8 +144,6 @@ func (d *Device) Measure() (*Measurement, error) { return d.MeasurementResult() } - - // WaitReady waits for device to be ready for next measurement. // It polls device state every step duration until idle status is reached or timeout duration is reached. // If device state is not valid for measurement, error is returned. diff --git a/measurement.go b/measurement.go index 5aa55bf..ad21573 100644 --- a/measurement.go +++ b/measurement.go @@ -112,42 +112,45 @@ type ColorRenditionIndexesValue struct { Ri [15]DecimalValue } -// Not implemented here but available for C-7000 FW > 25 extended measurement data: +// TODO: Not implemented here but available for C-7000 FW > 25 extended measurement data: // TM30, SSI, TLCI // NewMeasurementFromBytes creates a new Measurement instance from the given raw // binary response from SEKONIC device. // Note: currently only ambient measuring mode results are supported. -// Magic numbers for limits and precisions are based on original C-7000 SDK from SEKONIC. // -//nolint:exhaustruct,gomnd,gocyclo +//nolint:exhaustruct,funlen,gomnd,gocyclo func NewMeasurementFromBytes(data []byte) (*Measurement, error) { if len(data) < MeasurementDataValidSize { return nil, fmt.Errorf("invalid measurement data size: %d < %d bytes", len(data), MeasurementDataValidSize) } - m := &Measurement{} - - // Parse binary data to struct + // Parse binary data to struct. + // + // Data offsets and sizes are based on SEKONIC USB data packet layout which + // seems to be stable between various devices. + // + // Magic numbers for limits and precisions are based on original C-7000 SDK. + m := &Measurement{} - //ColorTemperature + // Color temperature and deviation from the Planckian locus m.ColorTemperature.Tcp = toDecimalValue(parseFloat32(data, 50), 1563, 100000, 0) m.ColorTemperature.DeltaUv = toDecimalValue(parseFloat32(data, 55), -0.1, 0.1, 4) if m.ColorTemperature.DeltaUv.Range != RangeOk { // limit the CCT value (C-800 returns Tcp=50000 value instead of "Over" as C-7000 does) m.ColorTemperature.Tcp.Range = m.ColorTemperature.DeltaUv.Range } - //Illuminance + // Illuminance values in Lux and foot-candle units m.Illuminance.Lux = parseLuxToDecimalValue(data, 271, 100, 200000) m.Illuminance.FootCandle = parseLuxToDecimalValue(data, 276, 0.093000002205371857, 18580.607421875) - //Tristimulus + // Tristimulus values in XYZ color space m.Tristimulus.X = toDecimalValue(parseFloat64(data, 281), 0, 1000000, 4) m.Tristimulus.Y = toDecimalValue(parseFloat64(data, 290), 0, 1000000, 4) m.Tristimulus.Z = toDecimalValue(parseFloat64(data, 299), 0, 1000000, 4) - //CIE1931 + // CIE1931 (x, y, z) chromaticity coordinates m.CIE1931.X = toDecimalValue(parseFloat32(data, 308), 0, 1, 4) m.CIE1931.Y = toDecimalValue(parseFloat32(data, 313), 0, 1, 4) if m.CIE1931.X.Range != RangeOk { @@ -158,21 +161,22 @@ func NewMeasurementFromBytes(data []byte) (*Measurement, error) { m.CIE1931.Z = toDecimalValue(1.0-m.CIE1931.X.Val-m.CIE1931.Y.Val, 0, 1, 4) } - //CIE1976 + // CIE1976 (u', v') chromaticity coordinates m.CIE1976.Ud = toDecimalValue(parseFloat32(data, 328), 0, 1, 4) m.CIE1976.Vd = toDecimalValue(parseFloat32(data, 333), 0, 1, 4) - //Dominant wavelength + // Dominant Wavelength m.DWL.Wavelength = toDecimalValue(parseFloat32(data, 338), -780, 780, 0) m.DWL.ExcitationPurity = toDecimalValue(parseFloat32(data, 343), 0, 100, 1) - //CRI + // CRI (Ra, Ri) m.ColorRenditionIndexes.Ra = toDecimalValue(parseFloat32(data, 348), -100, 100, 1) for i := range m.ColorRenditionIndexes.Ri { m.ColorRenditionIndexes.Ri[i] = toDecimalValue(parseFloat32(data, 353+i*5), -100, 100, 1) } - //Boundaries check + // Boundaries check + if m.Illuminance.Lux.Range == RangeUnder { for i := range m.SpectralData5nm { m.SpectralData5nm[i].Range = RangeUnder