pax_global_header 0000666 0000000 0000000 00000000064 13050164363 0014513 g ustar 00root root 0000000 0000000 52 comment=bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/ 0000775 0000000 0000000 00000000000 13050164363 0020276 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/.gitignore 0000664 0000000 0000000 00000000017 13050164363 0022264 0 ustar 00root root 0000000 0000000 /gqgmc
/gqgmcd
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/.gitlab-ci.yml 0000664 0000000 0000000 00000000516 13050164363 0022734 0 ustar 00root root 0000000 0000000 image: golang:1.7
test:
type: test
script:
- mkdir -p /go/src/gitlab.com/lyda
- ln -s $PWD /go/src/gitlab.com/lyda/gqgmc
- cd /go/src/gitlab.com/lyda/gqgmc
- b=master; test -z "$CI_BUILD_TAG" && b=$CI_BUILD_REF_NAME; git branch -f $b $CI_BUILD_REF; git checkout $b
- go get -u -f ./cmd/*
- ./run_tests.sh
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/README.md 0000664 0000000 0000000 00000005763 13050164363 0021570 0 ustar 00root root 0000000 0000000 # GQ GMC
This is a web service interface for GQ GMC geiger counters.
## Installation
Note this is not yet complete.
```bash
go get -u gitlab.com/lyda/gqgmc/cmd/gqgmcd
```
## Configuring
Command line:
```
Usage of gqgmcd:
--config string Config file (default "gqgmc.conf")
--device string Device for Geiger Counter (default "/dev/gqgmc")
--listen-address string Address for HTTP requests (default ":8080")
--model string Model of Geiger Counter (default "gqgmc")
--sleep-cycle int Seconds to sleep per cycle. (default 5)
--static-dir string Static files directory (default "static")
--template-dir string Template directory (default "templates")
```
The config file uses the same variables (not `config` obviously)
but with any dashes replaced with underscores. So
`static_dir = /var/lib/gqgmcd/static` for instance.
## Prometheus variables
There is a sample `prometheus.yml` configuration in the `docs/prometheus/`
directory. See the Prometheus
[getting started](https://prometheus.io/docs/introduction/getting_started/)
docs for more information.
* `gqgmc_geiger_cpm` (histogram) CPM readings.
* `gqgmc_geiger_cps` (histogram) CPS readings.
* `gqgmc_power_volts` (histogram) Voltage readings.
* `gqgmc_sys_errors` (gauge) Error counts. Records problems
communicating with the device.
There are also sample consoles in the `docs/prometheus/` directory. Add this
to the left hand side menu in `menu.lib` your prometheus install:
```
{{ if query "up{job='gqgmc'}" }}
{{ template "_menuItem" (args . "gqgmc.html" "GQGMC") }}
{{ if match "^gqgmc" .Path }}
{{ if .Params.instance }}
{{ end }}
{{ end }}
{{ end }}
```
![Prometheus Console](/docs/gqgmc.png)
## Configuring udev
Copy the `linux/51-gqgmc.rules` file to the `/etc/udev/rules.d`
directory and force the reload of udev rules:
```bash
sudo cp ./51-gqgmc.rules /etc/udev/rules.d/51-gqgmc.rules sudo
udevadm control --reload-rules
```
Disconnect the GQ GMC-300 from the computer and then reconnect.
Verify that there exists a `/dev/gqgmc` in the `/dev` directory
with read/write permission for all users.
```bash
ls -la /dev/gqgmc
```
## Development Resources
There's a GQ Electronics [forum](https://www.gqelectronicsllc.com/forum/) which
isn't hugely active, but some really good info and helpful people
there. If you follow the git log on this project you'll see this
is based off a C++ implementation which is
[over on sourceforge](https://sourceforge.net/projects/gqgmc/)
## TODO
There's still a fair bit to do. This works good enough for my purposes so
I'm not likely to do much more. I've seeded the
[issue tracker](https://gitlab.com/lyda/gqgmc/issues) with the obvious
issues.
[Merge Requests](https://docs.gitlab.com/ee/gitlab-basics/add-merge-request.html)
welcome!
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/cmd/ 0000775 0000000 0000000 00000000000 13050164363 0021041 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/cmd/gqgmc/ 0000775 0000000 0000000 00000000000 13050164363 0022137 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/cmd/gqgmc/main.go 0000664 0000000 0000000 00000002660 13050164363 0023416 0 ustar 00root root 0000000 0000000 //
// main.go
// Copyright (C) 2017 kevin
//
// Distributed under terms of the GPL license.
//
package main
import (
"fmt"
"time"
"gitlab.com/lyda/gqgmc/devices/geiger"
)
func main() {
var (
gc geiger.Counter
cfg *geiger.DevConfig
cps, cpm uint16
volts int16
temp float64
err error
t time.Time
)
gc, err = geiger.New(geiger.Config{
Model: "gqgmc",
Device: "/dev/gqgmc",
})
if err != nil {
fmt.Printf("Failed to connect to geiger counter: '%s'\n", err)
return
}
t, err = gc.GetTime()
if err != nil {
fmt.Printf("Failed: '%s'\n", err)
} else {
fmt.Printf("Time: %s\n", t)
}
cpm, err = gc.GetCPM()
if err != nil {
fmt.Printf("CPM failed: '%s'\n", err)
} else {
fmt.Printf("CPM: %d\n", cpm)
}
cps, err = gc.GetCPS()
if err != nil {
fmt.Printf("CPS failed: '%s'\n", err)
} else {
fmt.Printf("CPS: %d\n", cps)
}
fmt.Printf("Version: %s\n", gc.Version())
fmt.Printf("Model: %s\n", gc.Model())
fmt.Printf("Serial: %s\n", gc.Serial())
volts, err = gc.Volts()
if err != nil {
fmt.Printf("Volts failed: '%s'\n", err)
} else {
fmt.Printf("Volts: %d\n", volts)
}
temp, err = gc.GetTemp()
if err != nil {
fmt.Printf("Temp failed: '%s'\n", err)
} else {
fmt.Printf("Temp: %g\n", temp)
}
cfg, err = gc.GetConfiguration()
if err != nil {
fmt.Printf("Failed to get config: '%s'\n", err)
} else {
fmt.Printf("Cfg: %+v\n", cfg)
}
}
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/cmd/gqgmcd/ 0000775 0000000 0000000 00000000000 13050164363 0022303 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/cmd/gqgmcd/main.go 0000664 0000000 0000000 00000002367 13050164363 0023566 0 ustar 00root root 0000000 0000000 //
// main.go
// Copyright (C) 2017 kevin
//
// Distributed under terms of the MIT license.
//
package main
import (
flag "github.com/spf13/pflag"
"log"
"net/http"
"gitlab.com/lyda/gqgmc/config"
"gitlab.com/lyda/gqgmc/devices/geiger"
"gitlab.com/lyda/gqgmc/server/metrics"
"gitlab.com/lyda/gqgmc/server/pages"
)
var (
addr = flag.String("listen-address", ":8080", "Address for HTTP requests")
device = flag.String("device", "/dev/gqgmc", "Device for Geiger Counter")
model = flag.String("model", "gqgmc", "Model of Geiger Counter")
templateDir = flag.String("template-dir", "templates", "Template directory")
staticDir = flag.String("static-dir", "static", "Static files directory")
sleepCycle = flag.Int64("sleep-cycle", 5, "Seconds to sleep per cycle.")
cfg = flag.String("config", "gqgmc.conf", "Config file")
)
func main() {
flag.Parse()
c, err := config.ReadConfig(*cfg)
if err != nil {
log.Printf("Couldn't read config: %s\n", err)
return
}
gc, _ := geiger.New(geiger.Config{Model: c.Model, Device: c.Device})
p := pages.New(gc, c.StaticDir, c.TemplateDir)
p.Register()
m := metrics.Register(gc)
go m.Gather(c.SleepCycle)
log.Fatal(http.ListenAndServe(c.ListenAddress, nil))
}
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/config/ 0000775 0000000 0000000 00000000000 13050164363 0021543 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/config/config.go 0000664 0000000 0000000 00000002411 13050164363 0023335 0 ustar 00root root 0000000 0000000 //
// config.go
// Copyright (C) 2017 kevin
//
// Distributed under terms of the GPL license.
//
package config
import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
// Config for storing configuration.
type Config struct {
ListenAddress string `mapstructure:"listen_address"`
Device string `mapstructure:"device"`
Model string `mapstructure:"model"`
TemplateDir string `mapstructure:"template_dir"`
StaticDir string `mapstructure:"static_dir"`
SleepCycle int64 `mapstructure:"sleep_cycle"`
}
func setDefaults() {
viper.BindPFlag("listen_address", pflag.Lookup("listen-address"))
viper.BindPFlag("device", pflag.Lookup("device"))
viper.BindPFlag("model", pflag.Lookup("model"))
viper.BindPFlag("template_dir", pflag.Lookup("template-dir"))
viper.BindPFlag("static_dir", pflag.Lookup("static-dir"))
viper.BindPFlag("sleep_cycle", pflag.Lookup("sleep-cycle"))
}
// ReadConfig reads the client configuration from a file into a Config struct.
func ReadConfig(cfg string) (*Config, error) {
setDefaults()
viper.SetConfigFile(cfg)
viper.SetConfigType("hcl")
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
c := &Config{}
if err := viper.Unmarshal(c); err != nil {
return nil, err
}
return c, nil
}
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/devices/ 0000775 0000000 0000000 00000000000 13050164363 0021720 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/devices/geiger/ 0000775 0000000 0000000 00000000000 13050164363 0023162 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/devices/geiger/geiger.go 0000664 0000000 0000000 00000002026 13050164363 0024753 0 ustar 00root root 0000000 0000000 //
// geiger.go
// Copyright (C) 2017 kevin
//
// Distributed under terms of the GPL license.
//
package geiger
import "time"
// New creates a new Counter instance
func New(c Config) (Counter, error) {
switch c.Model {
case "gqgmc":
return NewGQGMC(c)
}
return nil, nil
}
// Counter is an interface for Geiger Counters
type Counter interface {
Clear() error
Model() string
Version() string
Serial() string
GetCPM() (uint16, error)
GetCPS() (uint16, error)
Volts() (int16, error)
GetHistory()
TurnOnCPS() error
TurnOffCPS() error
GetAutoCPS() (uint16, error)
TurnOffPower()
GetConfiguration() (*DevConfig, error)
SetConfiguration()
SetTime(time.Time)
GetTime() (time.Time, error)
GetTemp() (float64, error)
GetGyro() (int16, int16, int16, error)
FactoryReset()
Reboot()
}
// Config contain the configuration for a Geiger Counter
type Config struct {
Model string
Device string
Options map[string]string
}
// Reading contains a single geiger reading
type Reading struct {
CPM float64
}
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/devices/geiger/gqgmc.go 0000664 0000000 0000000 00000032145 13050164363 0024614 0 ustar 00root root 0000000 0000000 //
// gqgmc.go
// Copyright (C) 2017 kevin
//
// Distributed under terms of the GPL license.
//
package geiger
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"strconv"
"sync"
"time"
"github.com/go-restruct/restruct"
"github.com/tarm/serial"
)
// The Ideal and Low voltage threshholds
const (
VoltageIdeal = 98
VoltageTooLow = 75
)
const (
cmdGetSerial = ">" // GMC-280, GMC-300 Re.2.11
cmdGetVersion = ">" // GMC-280, GMC-300 Re.2.0x, Re.2.10
cmdGetVoltage = ">" // GMC-280, GMC-300 Re.2.0x, Re.2.10
cmdGetCPM = ">" // GMC-280, GMC-300 Re.2.0x, Re.2.10
cmdGetCPS = ">" // GMC-280, GMC-300 Re.2.0x, Re.2.10
cmdGetCfg = ">" // GMC-280, GMC-300 Re.2.10
cmdEraseCfg = ">" // GMC-280, GMC-300 Re.2.10
cmdUpdateCfg = ">" // GMC-280, GMC-300 Re.2.20
cmdTurnOnCPS = ">" // GMC-280, GMC-300 Re.2.10
cmdTurnOffCPS = ">" // Re.2.10
cmdTurnOffPwr = ">" // GMC-280, GMC-300 Re.2.11
cmdTurnOnPwr = ">" // GMC-280, GMC-300 Re.3.10
cmdFactoryReset = ">" // GMC-280, GMC-300 Re.3.00
cmdReboot = ">" // GMC-280, GMC-300 Re.3.00
cmdGetTime = ">" // GMC-280, GMC-300 Re.3.00
cmdGetTemp = ">" // GMC-320 Re.3.01
cmdGetGyro = ">" // GMC-320 Re.3.01
)
var (
mod280n300 = []string{"GMC-280", "GMC-300"}
mod320 = []string{"GMC-320"}
)
// GQGMCCounter is a GQ GMC Counter
type GQGMCCounter struct {
port *serial.Port
config *serial.Config
version,
model,
serial string
mutex *sync.Mutex
}
// DevConfig is the gcgmc config block.
type DevConfig struct {
PowerOnOff int8 `struct:"int8"`
AlarmOnOff int8 `struct:"int8"`
SpeakerOnOff int8 `struct:"int8"`
GraphicModeOnOff int8 `struct:"int8"`
BackLightTimeoutSeconds int8 `struct:"int8"`
IdleTitleDisplayMode int8 `struct:"int8"`
AlarmCPMValue int16 `struct:"int16,big"`
CalibrationCPM0 int16 `struct:"int16,big"`
CalibrationSvUcByte3p0 byte `struct:"byte"`
CalibrationSvUcByte2p0 byte `struct:"byte"`
CalibrationSvUcByte1p0 byte `struct:"byte"`
CalibrationSvUcByte0p0 byte `struct:"byte"`
CalibrationCPM1 int16 `struct:"int16,big"`
CalibrationSvUcByte3p1 byte `struct:"byte"`
CalibrationSvUcByte2p1 byte `struct:"byte"`
CalibrationSvUcByte1p1 byte `struct:"byte"`
CalibrationSvUcByte0p1 byte `struct:"byte"`
CalibrationCPM2 int16 `struct:"int16,big"`
CalibrationSvUcByte3p2 byte `struct:"byte"`
CalibrationSvUcByte2p2 byte `struct:"byte"`
CalibrationSvUcByte1p2 byte `struct:"byte"`
CalibrationSvUcByte0p2 byte `struct:"byte"`
IdleDisplayMode byte `struct:"byte"`
AlarmValueuSvByte3 byte `struct:"byte"`
AlarmValueuSvByte2 byte `struct:"byte"`
AlarmValueuSvByte1 byte `struct:"byte"`
AlarmValueuSvByte0 byte `struct:"byte"`
AlarmType byte `struct:"byte"`
SaveDataType byte `struct:"byte"`
SwivelDisplay byte `struct:"byte"`
ZoomByte3 byte `struct:"byte"`
ZoomByte2 byte `struct:"byte"`
ZoomByte1 byte `struct:"byte"`
ZoomByte0 byte `struct:"byte"`
SPIDataSaveAddress2 byte `struct:"byte"`
SPIDataSaveAddress1 byte `struct:"byte"`
SPIDataSaveAddress0 byte `struct:"byte"`
SPIDataReadAddress2 byte `struct:"byte"`
SPIDataReadAddress1 byte `struct:"byte"`
SPIDataReadAddress0 byte `struct:"byte"`
PowerSavingMode int8 `struct:"int8"`
SensitivityMode int8 `struct:"int8"`
CounterDelay int16 `struct:"int16,big"`
VoltageOffset int8 `struct:"int8"`
MaxCPM uint16 `struct:"uint16,big"`
SensitivityAutoModeThreshold int8 `struct:"int8"`
SaveDateTimeStamp6 byte `struct:"byte"`
SaveDateTimeStamp5 byte `struct:"byte"`
SaveDateTimeStamp4 byte `struct:"byte"`
SaveDateTimeStamp3 byte `struct:"byte"`
SaveDateTimeStamp2 byte `struct:"byte"`
SaveDateTimeStamp1 byte `struct:"byte"`
MaximumBytes byte `struct:"byte"`
}
// NewGQGMC creates a new GQGMC Counter instance
func NewGQGMC(c Config) (*GQGMCCounter, error) {
var gc GQGMCCounter
var buf []byte
gc.config = &serial.Config{
Name: c.Device,
Baud: 57600,
ReadTimeout: 500 * time.Millisecond,
}
p, err := serial.OpenPort(gc.config)
if err != nil {
return nil, err
}
gc.port = p
gc.mutex = &sync.Mutex{}
buf, err = gc.communicate(cmdGetVersion, 14)
if err == nil {
gc.model = string(buf[:7])
gc.version = string(buf[7:])
}
if gc.supportedModels(mod280n300) && !gc.versionLT("Re 2.11") {
buf, err := gc.communicate(cmdGetSerial, 7)
if err == nil {
gc.serial = hex.EncodeToString(buf)
}
}
//getConfiguration()
return &gc, nil
}
// Clear clears out any remaining data
func (gc *GQGMCCounter) Clear() error {
// Read up to 10 chars until nothing comes back.
// error otherwise.
for i := 0; i < 10; i++ {
if _, err := gc.recv(1); err != nil {
break
}
}
return nil
}
// Version gets the version of the device
func (gc *GQGMCCounter) Version() string {
return gc.version
}
// Model gets the model of the device
func (gc *GQGMCCounter) Model() string {
return gc.model
}
// Serial gets the serial number of the device
func (gc *GQGMCCounter) Serial() string {
return gc.serial
}
func (gc *GQGMCCounter) getReading(what string) (uint16, error) {
buf, err := gc.communicate(what, 2)
if err != nil {
return 0, err
}
reading := ((uint16(buf[0]) << 8) & 0x3f00)
reading |= (uint16(buf[1]) & 0x00ff)
return reading, nil
}
// GetCPM returns CPM
func (gc *GQGMCCounter) GetCPM() (uint16, error) {
return gc.getReading(cmdGetCPM)
}
// GetCPS returns CPS
func (gc *GQGMCCounter) GetCPS() (uint16, error) {
return gc.getReading(cmdGetCPS)
}
// Volts returns current battery voltage (times 10)
func (gc *GQGMCCounter) Volts() (int16, error) {
if !gc.supportedModels(mod280n300) {
return 0, errors.New("Unsupported model")
}
if gc.versionLT("Re 2.00") {
return 0, errors.New("Unsupported version")
}
volts, err := gc.communicate(cmdGetVoltage, 1)
if err != nil {
return 0, err
}
return int16(volts[0]), err
}
// GetHistory Should return history data but is unimplemented for now
func (gc *GQGMCCounter) GetHistory() {
// It's not recommended to use this so blank for now.
return
}
// TurnOnCPS turns on CPS collection
func (gc *GQGMCCounter) TurnOnCPS() error {
if !gc.supportedModels(mod280n300) {
return errors.New("Unsupported model")
}
if gc.versionLT("Re 2.10") {
return errors.New("Unsupported version")
}
gc.mutex.Lock()
gc.send(cmdTurnOnCPS)
gc.mutex.Unlock()
return nil
}
// TurnOffCPS turns off CPS collection
func (gc *GQGMCCounter) TurnOffCPS() error {
if !gc.supportedModels(mod280n300) {
return errors.New("Unsupported model")
}
if gc.versionLT("Re 2.10") {
return errors.New("Unsupported version")
}
gc.mutex.Lock()
gc.send(cmdTurnOffCPS)
gc.Clear()
gc.mutex.Unlock()
return nil
}
// GetAutoCPS gets a reading once auto CPS is turned on
func (gc *GQGMCCounter) GetAutoCPS() (uint16, error) {
gc.mutex.Lock()
buf, err := gc.recv(2)
gc.mutex.Unlock()
if err != nil {
return 0, err
}
cps := ((uint16(buf[0]) << 8) & 0x3f00)
cps |= (uint16(buf[1]) & 0x00ff)
return cps, nil
}
// TurnOnPower turns the device on
func (gc *GQGMCCounter) TurnOnPower() {
if !gc.supportedModels(mod280n300) {
return
}
if gc.versionLT("Re 3.10") {
return
}
gc.mutex.Lock()
gc.send(cmdTurnOnPwr)
gc.mutex.Unlock()
return
}
// TurnOffPower turns the device off
func (gc *GQGMCCounter) TurnOffPower() {
if !gc.supportedModels(mod280n300) {
return
}
if gc.versionLT("Re 2.11") {
return
}
gc.mutex.Lock()
gc.send(cmdTurnOffPwr)
gc.mutex.Unlock()
return
}
// GetConfiguration reads configuration data
func (gc *GQGMCCounter) GetConfiguration() (*DevConfig, error) {
if !gc.supportedModels(mod280n300) {
return nil, errors.New("Unsupported Model")
}
if gc.versionLT("Re 2.10") {
return nil, errors.New("Unsupported version")
}
data, err := gc.communicate(cmdGetCfg, 256)
if err != nil {
return nil, err
}
var cfg DevConfig
restruct.Unpack(data[:59], binary.BigEndian, &cfg)
fmt.Printf("Configuration: %+v\n", data)
return &cfg, nil
}
// SetConfiguration writes configuration data
func (gc *GQGMCCounter) SetConfiguration() {
if !gc.supportedModels(mod280n300) {
return
}
if gc.versionLT("Re 2.10") {
return
}
// See the ConfigurationData functions in gqgmc.cc
}
// SetTime sets the time
func (gc *GQGMCCounter) SetTime(t time.Time) {
if !gc.supportedModels(mod280n300) {
return
}
if gc.versionLT("Re 2.23") {
return
}
gc.mutex.Lock()
defer gc.mutex.Unlock()
if gc.versionLT("Re 3.30") {
gc.setTimeParts(t)
}
gc.setTimeAll(t)
}
func (gc *GQGMCCounter) setTimeParts(t time.Time) {
cmd := make([]byte, 13)
var timeCmds = []struct {
cmd string
unit int
}{
{">")
// Mutex acquired in setTime()
gc.port.Write(cmd)
gc.recv(1)
}
}
func (gc *GQGMCCounter) setTimeAll(t time.Time) {
cmd := make([]byte, 20)
copy(cmd[:], ">")
// Mutex acquired in setTime()
gc.port.Write(cmd)
gc.recv(1)
}
// GetTime gets the time
func (gc *GQGMCCounter) GetTime() (time.Time, error) {
if !gc.supportedModels(mod280n300) {
return time.Unix(0, 0), errors.New("Unsupported model")
}
if gc.versionLT("Re 3.00") {
return time.Unix(0, 0), errors.New("Unsupported version")
}
b, err := gc.communicate(cmdGetTime, 7)
if err != nil {
return time.Unix(0, 0), err
}
t := time.Date(int(b[0])+2000, time.Month(b[1]), int(b[2]),
int(b[3]), int(b[4]), int(b[5]), 0, time.Local)
return t, nil
}
// GetTemp gets the temp
func (gc *GQGMCCounter) GetTemp() (float64, error) {
if !gc.supportedModels(mod320) {
return 0, errors.New("Unsupported model")
}
if gc.versionLT("Re 3.01") {
return 0, errors.New("Unsupported version")
}
t, err := gc.communicate(cmdGetTemp, 4)
if err != nil {
return 0, err
}
var temp float64
temp, err = strconv.ParseFloat(fmt.Sprintf("%d.%d", uint8(t[0]), uint8(t[1])), 64)
if err != nil {
return 0, err
}
if t[2] != 0 {
temp = -temp
}
return temp, nil
}
// GetGyro gets the position in space
func (gc *GQGMCCounter) GetGyro() (int16, int16, int16, error) {
if !gc.supportedModels(mod320) {
return 0, 0, 0, errors.New("Unsupported model")
}
if gc.versionLT("Re 3.01") {
return 0, 0, 0, errors.New("Unsupported version")
}
buf, err := gc.communicate(cmdGetGyro, 7)
if err != nil {
return 0, 0, 0, err
}
x := (int16(buf[0]) << 8)
x |= (int16(buf[1]) & 0x00ff)
y := (int16(buf[0]) << 8)
y |= (int16(buf[1]) & 0x00ff)
z := (int16(buf[0]) << 8)
z |= (int16(buf[1]) & 0x00ff)
return x, y, z, nil
}
// FactoryReset does a factory reset
func (gc *GQGMCCounter) FactoryReset() {
if !gc.supportedModels(mod280n300) {
return
}
if gc.versionLT("Re 3.00") {
return
}
gc.mutex.Lock()
gc.send(cmdFactoryReset)
gc.mutex.Unlock()
return
}
// Reboot reboots the device
func (gc *GQGMCCounter) Reboot() {
if !gc.supportedModels(mod280n300) {
return
}
if gc.versionLT("Re 3.00") {
return
}
gc.mutex.Lock()
gc.send(cmdReboot)
gc.mutex.Unlock()
return
}
func (gc *GQGMCCounter) supportedModels(models []string) bool {
for _, model := range models {
if model == gc.model {
return true
}
}
return false
}
func (gc *GQGMCCounter) versionLT(version string) bool {
return gc.version < version
}
func (gc *GQGMCCounter) communicate(cmd string, length int) ([]byte, error) {
gc.mutex.Lock()
defer gc.mutex.Unlock()
gc.Clear()
if len(cmd) > 0 {
gc.send(cmd)
}
if length != 0 {
return gc.recv(length)
}
return nil, nil
}
func (gc *GQGMCCounter) send(cmd string) {
gc.port.Write([]byte(cmd))
return
}
func (gc *GQGMCCounter) recv(length int) ([]byte, error) {
buf := make([]byte, length)
n, err := gc.port.Read(buf)
if err != nil {
return nil, err
}
read := n
if n != length {
// Handle the case where we couldn't read it all.
// Really only happens for length > 32.
for i := 0; i < 20; i++ {
n, err = gc.port.Read(buf[read:])
if err != nil {
return nil, err
}
read += n
if read == length {
break
}
}
}
if read != length {
return nil, fmt.Errorf("Short read (got: %d, wanted: %d)", n, length)
}
return buf, nil
}
gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/docs/ 0000775 0000000 0000000 00000000000 13050164363 0021226 5 ustar 00root root 0000000 0000000 gqgmc-bcf22ac56717e6fbc996b1e4c8f2adeb9fea299e/docs/GQ-GMC-ICD.odt 0000664 0000000 0000000 00000061631 13050164363 0023255 0 ustar 00root root 0000000 0000000 PK
5C^2' ' mimetypeapplication/vnd.oasis.opendocument.textPK
5Cݍí meta.xml
2013-09-20T19:16:02Phil LibreOffice/3.6$Linux_X86_64 LibreOffice_project/360m1$Build-101PT8H14M52S71PK
5C settings.xmlZMs8H@ *aʐa( ڹ odKmf2?V~>B~R(ˋsC?{y=u>, uFLo;Md*PMBPM51}f,xwZGJe\^,(O>UG=`~qȼ.&qVV*wlw9mom灆ls,!ͷ 9y+M0rw:;ij]e0ӹƫ} |_uV<7e_6nŬOsPSDL8--c('
чCg͟,:+e;>.=?Te`mV(m;z^/z*r(.Y;OLB֠z6jaj_lenRTl CwYۈ{?.]ik\}OJP`9WC7+b5KdTQ6[(dq+}
z ⑉6W[J9`܇l1-ͳgPn&}+.P.,ŗaKI~`M C&ʑhӡvQ胐8aqah儼:N$BIP,]
%P$r#1-}lS?@
WLc}TNA Hɢ -_H-qƳmή@ӿ(T[zx*#0 2ǪpJ7j{$mDp gRP!AGCb