Skip to content
Snippets Groups Projects
Select Git revision
22 results Searching

compat

Blame
  • gqgmc.go 11.09 KiB
    //
    // gqgmc.go
    // Copyright (C) 2017 kevin <kevin@phrye.com>
    //
    // Distributed under terms of the GPL license.
    //
    
    package geiger
    
    import (
    	"errors"
    	"fmt"
    	"time"
    
    	"github.com/tarm/serial"
    )
    
    // The Ideal and Low voltage threshholds
    const (
    	VoltageIdeal  = 98
    	VoltageTooLow = 75
    )
    
    const (
    	powerOnOff                    = 0
    	alarmOnOff                    = 1
    	speakerOnOff                  = 2
    	graphicModeOnOff              = 3
    	backlightTimeoutSeconds       = 4
    	idleTitleDisplayMode          = 5
    	alarmCPMValue                 = 6
    	calibrationCPM0               = 8
    	calibrationSvUc0              = 10
    	calibrationCPM1               = 14
    	calibrationSvUc1              = 16
    	calibrationCPM2               = 20
    	calibrationSvUc2              = 22
    	idleDisplayMode               = 26
    	alarmValueuSvUc               = 27
    	alarmType                     = 31
    	saveDataType                  = 32
    	swivelDisplay                 = 33
    	zoom                          = 34
    	dataSaveAddress               = 38
    	dataReadAddress               = 41
    	nPowerSavingMode              = 44
    	nSensitivityMode              = 45
    	nCounterDelay                 = 46
    	nVoltageOffset                = 48
    	maxCPM                        = 49
    	nSensitivityAutoModeThreshold = 51
    	saveDate                      = 52
    	saveTime                      = 55
    	maxBytes                      = 58
    )
    
    var configBytes = map[int]int{
    	powerOnOff:              1,
    	alarmOnOff:              1,
    	speakerOnOff:            1,
    	graphicModeOnOff:        1,
    	backlightTimeoutSeconds: 1,
    	idleTitleDisplayMode:    1,
    	alarmCPMValue:           2,
    	calibrationCPM0:         2,
    	calibrationSvUc0:        4,
    	calibrationCPM1:         2,
    	calibrationSvUc1:        4,
    	calibrationCPM2:         2,
    	calibrationSvUc2:        4,
    	idleDisplayMode:         1,
    	alarmValueuSvUc:         4,
    	alarmType:               1,
    	saveDataType:            1,
    	swivelDisplay:           1,
    	zoom:                    4,
    	dataSaveAddress:         3,
    	dataReadAddress:         3,
    	nPowerSavingMode:        1,
    	nSensitivityMode:        1,
    	nCounterDelay:           2,
    	nVoltageOffset:          1,
    	maxCPM:                  2,
    	nSensitivityAutoModeThreshold: 1,
    	saveDate:                      3,
    	saveTime:                      3,
    	maxBytes:                      1,
    }
    
    const (
    	saveOff = iota
    	saveCPS
    	saveCPM
    	saveCPH
    	saveMax
    )
    
    const (
    	historyDataMaxSize = 0x1000
    	historyAddrMaxSize = 0x10000
    	forFirmware        = 2.23
    	nvmSize            = 256
    )
    
    const (
    	cmdGetSerial  = "<GETSERIAL>>"
    	cmdGetVersion = "<GETVER>>"
    	cmdGetVoltage = "<GETVOLT>>"
    	cmdGetCPM     = "<GETCPM>>"
    	cmdGetCPS     = "<GETCPS>>"
    	cmdGetCfg     = "<GETCFG>>"
    	cmdEraseCfg   = "<ECFG>>"
    	cmdUpdateCfg  = "<CFGUPDATE>>"
    	cmdTurnOnCPS  = "<HEARTBEAT1>>"
    	cmdTurnOffCPS = "<HEARTBEAT0>>"
    	cmdTurnOffPwr = "<POWEROFF>>"
    )
    
    const (
    	errOpen                  = "Failed to open USB port"
    	errFirmware              = "GQ GMC has older firmware.  Some commands may not work."
    	errVersion               = "Failed to read the version number of the firmware"
    	errSerialNumber          = "Failed to read the serial number "
    	errCPM                   = "Failed to read the counts per minute "
    	errCPS                   = "Failed to read the counts per second "
    	errAutoCPS               = "Failed to read auto counts per second "
    	errCFG                   = "Failed to get configuration data "
    	errEraseCFG              = "Failed to erase configuration data "
    	errUpdateCFG             = "Failed to update configuration data "
    	errClear                 = "Failed to clear USB input buffer. You should power cycle GQ GMC."
    	errBatteryVoltage        = "Failed to read the battery voltage "
    	errHistoryData           = "Failed to read the history data "
    	errHistoryDataLengthFmt  = "Requested data length of the history command cannot exceed %d bytes"
    	errHistoryDataAddressFmt = "Address of the history command cannot exceed %d bytes"
    	errHistoryDataOverrunFmt = "History data length added to the address cannot exceed %d bytes"
    	errYear                  = "Failed to set year"
    	errMonth                 = "Failed to set month"
    	errDay                   = "Failed to set day"
    	errHour                  = "Failed to set hour"
    	errMinute                = "Failed to set minute"
    	errSecond                = "Failed to set second"
    )
    
    // GQGMCCounter is a GQ GMC Counter
    type GQGMCCounter struct {
    	port   *serial.Port
    	config *serial.Config
    }
    
    // NewGQGMC creates a new GQGMC Counter instance
    func NewGQGMC(c Config) (*GQGMCCounter, error) {
    	cfg := serial.Config{
    		Name:        c.Device,
    		Baud:        57600,
    		ReadTimeout: 500 * time.Millisecond,
    	}
    	p, err := serial.OpenPort(&cfg)
    	if err != nil {
    		return nil, err
    	}
    	//vers := getVersion()
    	//getConfigurationData()
    	return &GQGMCCounter{port: p, config: &cfg}, nil
    }
    
    // Clear clears out any remaining data
    func (gc *GQGMCCounter) Clear() error {
    	// Read up to 10 chars until nothing comes back.
    	// error otherwise.
    	return nil
    }
    
    // Version gets the version of the device
    func (gc *GQGMCCounter) Version() (string, error) {
    	//communicate(get_version_cmd, version, versize);
    	// use cmdGetVersion. Returns 14 byte string.
    	return "", nil
    }
    
    // SerialNum gets the serial number of the device
    func (gc *GQGMCCounter) SerialNum() (string, error) {
    	//communicate(get_serial_cmd, serial_number, sernumsize);
    	// use cmdGetSerial. Returns 7 bytes.
    	// Turn each 4 bits into corresponging hex char.
    	bs := []byte{0, 0x30, 0, 0xE3, 0x4A, 0x35, 0x1A}
    	for _, b := range bs {
    		fmt.Printf("%02X", b)
    	}
    	fmt.Println("")
    	return "", nil
    }
    
    func (gc *GQGMCCounter) getReading(what string) (uint16, error) {
    	buf, err := gc.communicate([]byte(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)
    }
    
    // GetVoltage returns current battery voltage
    func (gc *GQGMCCounter) GetVoltage() (int16, error) {
    	// Do this differently - for 9.6 return 96. And if not supported,
    	// Return -1.
    	// Public method to read voltage value of battery. The GQ GMC returns
    	// a single byte whose integer value converted to a float divided by 10
    	// equals the battery voltage. For example, 0x60 = 96 converts to 9.6 Volts.
    	// The ideal value is 9.8 volts. So practically speaking, we should not
    	// expect to see a value higher than 100. The command is get_voltage_cmd
    	// (see GQ GMC COMMANDS above). If the voltage falls below 7.5V, GQ LLC
    	// says the geiger counter cannot be expected to operate properly.
    	//func (gc *GQGMCCounter) getBatteryVoltage()
    	//uint32_t     voltsize = 1;             // one byte of returned data
    	// Issue command to get battery voltage and read returned data.
    	//communicate(get_voltage_cmd, voltage_char, voltsize);
    	// If read of returned data succeeded, convert raw data to float,
    	//int32_t  voltage_int = int16_t(voltage_char[0]);
    	//voltage = float(voltage_int) / 10.0;
    
    	return 0, nil
    }
    
    // GetHistoryData Should return history data but is unimplemented for now
    func (gc *GQGMCCounter) GetHistoryData() {
    	// It's not recommended to use this so blank for now.
    	return
    }
    
    // TurnOnCPS turns on CPS collection
    func (gc *GQGMCCounter) TurnOnCPS() error {
    	// turnOnCPS is public method to enable automatic reporting of the
    	// count per second (CPS) value. First, I would say don't use this
    	// command. Since the returned data has no protocol, that is, there
    	// is no start/stop marker, no identification, no nothing but a
    	// sequence of bytes, any other command issued while CPS is turned on
    	// will take extraordinary effort not to confuse its returned data
    	// with the CPS data. To handle it correctly, you would have to
    	// wait for the CPS data to be returned, and then issue the command.
    	// This would be most safely done by creating a new thread to
    	// read the CPS and using a mutex to signal opportunity to issue a
    	// separate command. The command is turn_on_cps_cmd
    	// (see GQ GMC COMMANDS above). The returned data is always two
    	// bytes so that samples > 255 can be reported (even though unlikely).
    	//sendCmd(turn_on_cps_cmd);
    	// There is no pass/fail return from GQ GMC
    	return nil
    }
    
    // TurnOffCPS turns off CPS collection
    func (gc *GQGMCCounter) TurnOffCPS() error {
    	//sendCmd(turn_off_cps_cmd);
    	//call Clear()
    	return nil
    }
    
    // GetAutoCPS gets a reading once auto CPS is turned on
    func (gc *GQGMCCounter) GetAutoCPS() (uint16, error) {
    	//uint32_t cpssize = 2;          // 2 bytes of returned data
    	//read-from-port(cps_char, cpssize);
    	// 1st byte is MSB, but note that upper two bits are reserved bits.
    	// Note that shifting and bitmasking performed in uP register, so
    	// endianess is irrevelant.
    	//cps_int |= ((uint16_t(cps_char[0]) << 8) & 0x3f00);
    	//cps_int |=  (uint16_t(cps_char[1]) & 0x00ff);
    	return 0, nil
    }
    
    // TurnOffPower turns the device off
    func (gc *GQGMCCounter) TurnOffPower() {
    	//sendCmd(turn_off_pwr_cmd)
    	// Note that power off cannot fail because the GQ GMC returns nothing.
    	return
    }
    
    // GetConfiguration reads configuration data
    func (gc *GQGMCCounter) GetConfiguration() {
    	// Issue command to get configuration and read returned data.
    	// communicate(get_cfg_cmd, reinterpret_cast<char *>(&mCFG_Data),
    }
    
    // SetConfiguration writes configuration data
    func (gc *GQGMCCounter) SetConfiguration() {
    	// See the ConfigurationData functions in gqgmc.cc
    }
    
    // ResetConfiguration resets to factory default
    func (gc *GQGMCCounter) ResetConfiguration() {
    	//uint32_t     retsize = 1;
    	//char         ret_char[retsize+1];
    	//communicate(erase_cfg_cmd, ret_char, retsize);
    }
    
    // SetDate sets the date - format of YYYYMMDD
    func (gc *GQGMCCounter) SetDate(date string) {
    	//setMonthCmd  = "<SETDATEMM";
    	//setMonthCmd += uint8_t(month);
    	//setMonthCmd += ">>";
    	//communicate(setMonthCmd, ret_char, retsize);
    
    	//setDayCmd  = "<SETDATEDD";
    	//setDayCmd += uint8_t(day);
    	//setDayCmd += ">>";
    	//communicate(setDayCmd, ret_char, retsize);
    
    	// year - last two digits
    	//setYearCmd  = "<SETDATEYY";
    	//setYearCmd += uint8_t(year);
    	//setYearCmd += ">>";
    	//communicate(setYearCmd, ret_char, retsize);
    }
    
    // SetTime sets the time (HH:MM:SS)
    func (gc *GQGMCCounter) SetTime(time string) {
    	//setHourCmd  = "<SETTIMEHH";
    	//setHourCmd += uint8_t(hour);
    	//setHourCmd += ">>";
    	//communicate(setHourCmd, ret_char, retsize);
    
    	//setMinuteCmd  = "<SETTIMEMM";
    	//setMinuteCmd += uint8_t(minute);
    	//setMinuteCmd += ">>";
    	//communicate(setMinuteCmd, ret_char, retsize);
    
    	//setSecondCmd  = "<SETTIMESS";
    	//setSecondCmd += uint8_t(second);
    	//setSecondCmd += ">>";
    	//communicate(setSecondCmd, ret_char, retsize);
    }
    
    func (gc *GQGMCCounter) communicate(cmd []byte, length uint32) ([]byte, error) {
    	gc.Clear()
    	if len(cmd) > 0 {
    		gc.sendCmd(cmd)
    	}
    	if length != 0 {
    		return gc.readCmd(length)
    	}
    	return nil, nil
    }
    
    func (gc *GQGMCCounter) sendCmd(cmd []byte) {
    	gc.port.Write(cmd)
    	return
    }
    
    func (gc *GQGMCCounter) readCmd(length uint32) ([]byte, error) {
    	buf := make([]byte, length)
    	n, err := gc.port.Read(buf)
    	if err != nil {
    		return nil, err
    	}
    	if uint32(n) != length {
    		return nil, errors.New("Short read")
    	}
    	return buf, nil
    }