gqgmc.go 33 KB
Newer Older
1
2
3
4
5
6
7
8
9
//
// gqgmc.go
// Copyright (C) 2017 kevin <kevin@phrye.com>
//
// Distributed under terms of the GPL license.
//

package geiger

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
import (
	"time"

	"github.com/tarm/serial"
)

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 (
	key1 = iota
	key2
	key3
	key4
)

const (
	historyDataMaxSize = 0x1000
	historyAddrMaxSize = 0x10000
	forFirmware = 2.23
	kNVMSize = 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"
    )

// CONFIGURATION DATA
//
// Setting the configuration data of the GQ GMC is not a
// straight forward procedure. The following statement is the
// principle reason why: the GQ GMC does not use or keep a RAM copy
// of its non-volatile memory (NVM) (configuration data is in NVM).
// That condition coupled with the fact that the EEPROM
// used by the GQ GMC can only be reprogrammed
// all 256 bytes at a shot means that if you write a byte of
// configuration data and then read back the configuration data,
// you will not see your data changed as expected.
//
// All this means that in order to change the configuration
// parameters and have the GQ GMC change its operations accordingly,
// 1) the host computer must keep its own copy of NVM,
// 2) update its own copy of NVM,
// 3) issue the erase configuration command,
// 4) write all 256 bytes of configuration data at one shot,
// 5) follow immediately with an update configuration command.
//
// When the GQ GMC receives the update configuration command,
// then and only then does it re-configure its operation in
// accordance with the NVM configuration data. Keeping the host
// computer's copy of the NVM accurate and up to date can be
// problematic since behind the back of the host computer,
// the GQ GMC can be changed manually.
//
// The GQGMC software makes a valiant attempt to hide all this
// from the user. First, immediately following the opening of
// the USB port, the software silently reads the configuration
// data from the GQ GMC to obtain its own copy of NVM.
// From that point on, it is assumed that no manual changes
// to the GQ GMC will occur. The GQGMC software then reads/writes
// it own local copy of NVM. When the user issues the Update CFG
// command, the GQGMC software silently
// 1) issues the erase configuraton command,
// 2) writes all 256 bytes of NVM,
// 3) issues the update configuration command.

// The user software may at any time cause a Get configuration
// command in which case, the user must be aware that the GQGMC's
// local copy of NVM will be overwritten.


// getConfigurationData public method reads configuration data. You
// don't request pieces of the configuration data, all 256 bytes
// are returned, although, there are currently only about 60
// bytes used (corresponding to about 50 parameters). The command is
// get_cfg_cmd (see GQ GMC COMMANDS above).
void
func (gc *GQGMCCounter) getConfigurationData()
{
  // Issue command to get configuration and read returned data.
  communicate(get_cfg_cmd, reinterpret_cast<char *>(&mCFG_Data),
                           sizeof(mCFG_Data));

  // If read of returned data failed, set error code.
  if (mRead_status == false)
  {
    mError_code = eGet_CFG;
  }

  //  debugging code
  /*
  uint8_t * inp = (uint8_t *)&mCFG_Data;
  for(uint32_t i=0; i<64; i++)
  {
    cout << Hex(inp[i]) << "-";
    if (i > 0)
      if (((i+1)%16) == 0) cout << endl;
    if (i > 62)break;
  }
  cout << endl;
  */
  // end debug code

  return;
} // end getConfigurationData()

// The following get methods are provided in order to access the
// configuration data, not all configuration data, but only those
// parameters which are needed to retrieve and parse the history data.

// getSaveDataType is a get method to retrieve the saved data type
// parameter in the configuration data. Note that SaveDataType is
// retrieved from the host computer's local copy of NVM
// configuration data. The return type is an enumeration
// which matches the following possibilities:
// 0 = logging is off,
// 1 = counts per second,
// 2 = counts per minute,
// 3 = CPM averaged per hour.
enum saveDataType_t
func (gc *GQGMCCounter) getSaveDataType()
{
  return ((enum saveDataType_t)(mCFG_Data.saveDataType));
} // end getSaveDataType()


// setSaveDataType is a set method to reconfigure the data type
// logged in the history buffer. This method is provided as a
// convenience instead of using the writeConfigurationData method
// since changing the saved data type is considered to be
// more commonly used configuration change. The passed argument
// is an enumeration (see definition in gqgmc.hh) whose value
// is to be the new value of the saveDataType configuration
// parameter. Note that only the host computer's local copy
// of NVM is updated.  The user must issue a update configuration
// command to cause the GQGMC to implement the NVM changes.
void
func (gc *GQGMCCounter) setSaveDataType(enum saveDataType_t newSaveDataType)
{
  uint8_t  saveData = uint8_t(newSaveDataType);

  // Use writeConfigurationData method
  writeConfigurationData(eSaveDataType, eSaveDataType_bytecnt, &saveData);
  // error condition will be handled by writeConfigurationData()

  return;
} // end setSaveDatatype method


// getDataSaveAddress is get method to retrieve the address
// in the history buffer where the logged data begins. See
// getHistoryData method for an explanation of the
// dataSaveAddress in the configuration data structure.
// The returned value is a 32 bit address, although the
// dataSaveAddress maximum value cannot exceed 24 bits worth.
// Note that the DataSaveAddress is retrieved from the host
// computer's local copy of the GQ GMC's NVM configuration data.
uint32_t
func (gc *GQGMCCounter) getDataSaveAddress()
{
  uint32_t      address(0);

  address |= (uint32_t(mCFG_Data.dataSaveAddress2) << 16);
  address |= (uint32_t(mCFG_Data.dataSaveAddress1) <<  8);
  address |= (uint32_t(mCFG_Data.dataSaveAddress0) <<  0);

  return address;
}  // end getDataSaveAddress()

// The resetDataSaveAddress sets the dataSaveAddress configuration
// parameter to the value of 0x10.  This is provided as a
// convenience instead of using writeConfigurationData() directly
// because setting the start of the history buffer back to zero
// would be a common thing to do. Note that the DataSaveAddress
// is being updated in the host computer's local copy of the
// GQ GMC's NVM configuration data. The user must issue the
// update configuration command to force the GQ GMC to implement
// the NVM configuration data changes.
void
func (gc *GQGMCCounter) resetDataSaveAddress()
{
  uint32_t     address(0x10); // 0x10 provides room for date/timestamp

  // Use writeConfigurationData() method
  writeConfigurationData(eDataSaveAddress, eDataSaveAddress_bytecnt,
                         (uint8_t *)(&address));
  // error condition handled by writeConfigurationData()

  return;
} // end resetDataSaveAddress()


// writeConfiguratonData is the public method to write configuration
// data. It takes the following parameters.
//
// cfgParameter is actually the offset from the beginning of the
// configuration data of the desired parameter. It was explicitly
// created this way and assigned the offset as its enumeration value so
// that cfgParameter serves both as an enumeration and as the actual
// address of the configuration parameter.
//
// cfgData is a pointer to an array of raw binary for the parameter.
//
// cfgDataCount is the number of bytes of cfgData. Since cfgData is
// passed as pointer, we need to supply the array length separately
// as the cfgDataCount parameter.
//
// Note that this method updates the local copy of the GQ GMC's
// NVM configuration data. As noted previously in the documentation,
// the GQ GMC does not support a direct method of updating its
// NVM configuration data and having it take effect immediately.
//
// This method is not an exact reflection of the native GQ GMC write
// configuration command. The native GQ GMC write configuration
// only writes one byte at a time. So writing multibyte configuration
// parameters would take multiple writes, one per data byte. Instead,
// we abstract the write configuration command so the user does not
// have to know this much detail. We can do this because we
// know a priori the base address of each parameter and how many
// bytes each parameter needs. So this method is intended to
// handle parameters with multibyte data by requiring the user
// to only pass the parameter enumeration, its byte count, and
// value.
//
// Note that changes to the configuration data do not have
// immediate effect since we writing to the local copy of the
// GQ GMC's NVM configuration data. To take effect, the user must
// call the updateConfigurationData() method. Before doing so, all
// changes to the configuration data should be completed so that
// interdependent configuration parameters are updated
// simultaneously.
void
func (gc *GQGMCCounter) writeConfigurationData(enum cfg_param_t       cfgParameter,
                              enum cfg_bytecnt_t     cfgDataCount,
                              uint8_t const * const  cfgData)
{
  uint8_t * pCfg_Data = (uint8_t *)&mCFG_Data + uint8_t(cfgParameter);
  for(int i=0; i<cfgDataCount; i++)
  {
    // Convert little endian to big endian which GQ GMC wants.
    if (mBig_endian)
      pCfg_Data[i] = cfgData[i];
    else
      pCfg_Data[i] = cfgData[cfgDataCount-1-i];
  } // end for loop

  return;
} // end writeConfigurationData()

// loadConfigurationData private method writes all 256 bytes
// of the configuration data to the GQ GMC. This will take
// over a minute to complete. This is a practice in patience.
// The GQ GMC's write_cfg_cmd only transmits a single byte
// at a time. So it takes 256 transmits and each transmit
// has to wait for a 0xAA return.  The user obviously should
// not update the NVM configuration too often.
void
func (gc *GQGMCCounter) loadConfigurationData()
{
  const
  uint32_t     retsize = 1;
  char         ret_char[retsize+1];

  // Need a pointer to the local host computer's copy
  // of the NVM configuration data.
  uint8_t *    pCfg_Data = (uint8_t *)&mCFG_Data;

  // Begin formulating the write configuration data command.
  // "AD" is just a place holder for the address byte and data byte
  // that will be dynamically derived and inserted.
  string write_cfg_cmd = "<WCFGAD>>";

  // The parameter and its data have to be dynamically derived.
  // write_cfg_cmd[5] is parameter enumeration (aka address offset)
  // write_cfg_cmd[6] is parameter data

  // Pack address and data into write_cfg_data_cmd with big
  // endian byte order.

  // Address (ie, parameter) is less than 256 since configuration
  // data has a fixed size of 256 bytes, so address is one byte.

  // pack address and data into command
  for(uint16_t i=0; i<kNVMSize; i++)
  {
    // Increment the address for each additional data byte.
    write_cfg_cmd[5] = uint8_t(i);

    // Load data one byte at a time
    write_cfg_cmd[6] = pCfg_Data[i];

/*    // debug code
    if (i < 64)
    {
      for(int i=0; i<5; i++)
        cout << write_cfg_cmd[i];
      cout << Hex(write_cfg_cmd[5]);
      cout << Hex(write_cfg_cmd[6]);
      cout << write_cfg_cmd[7];
      cout << write_cfg_cmd[8];
      cout << endl;
    }
*/    // end debug code

    // Issue command to write configuration data, one byte
    // at a time because that is the native write configuration
    // command of the GQ GMC.
    communicate(write_cfg_cmd, ret_char, retsize);

    // if read of returned data succeeded, convert raw data to float
    if (mRead_status == true)
    {
      // We really don't care about the return value of 0xAA. If the
      // GQ GMC does not recognize the command, it returns nothing and
      // we get a read_status error.
    }
    else  // else for failure, set error code
    {
      mError_code = eWrite_CFG;
      break;                    // break out of for loop
    } // end (read_status == true)
  } // end for loop

  return;
} // loadConfigurationData()

// eraseConfigurationData public method erases the configuration data
// in its entirety. The configuration returns to its factory default
// setting. It might have been better to call this the reset
// configuration data command. The command is erase_cfg_cmd
// (see GQ GMC COMMANDS above).
void
func (gc *GQGMCCounter) eraseConfigurationData()
{
  const
  uint32_t     retsize = 1;
  char         ret_char[retsize+1];

  // Issue command to erase NVM configuration.
  communicate(erase_cfg_cmd, ret_char, retsize);

  // If read of returned data succeeded, convert raw data to float,
  if (mRead_status == true)
  {
    // We really don't care about the return value of 0xAA. If the
    // GQ GMC does not recognize the command, it returns nothing and
    // we get a read_status error.
  }
  else  // else for failure, set the error code.
  {
    mError_code = eErase_CFG;
  }

  return;
} // end eraseConfigurationData()


// The updateConfigurationdata public method is called to make changes
// to configuration data take effect. All other methods to modify
// the configuration data do not cause the GQ GMC to immediately
// change operation. This would not be desireable since various
// changes to the configuration may be interdependent and so
// we would want the changes to take effect simultaneously to
// insure proper operation. There are no arguments to call.
// The command is update_cfg_cmd (see GQ GMC COMMANDS above).
// The user who calls this method may want to pop-up a window
// stating that this will take about one minute. That is about
// how long it will take to write all 256 bytes to the GQ GMC.
// This method calls eraseConfigurationData() and
// loadConfigurationData() as part of the procedure needed to
// force the GQ GMC to implement operational changes per the
// new NVM configuration data.
void
func (gc *GQGMCCounter) updateConfigurationData()
{
  const
  uint32_t     retsize = 1;
  char         ret_char[retsize+1];

    // 1st, we have to erase configuration data
  // cout << erase_cfg_cmd << endl; // debug
  eraseConfigurationData();
  // 2nd, write all 256 bytes of NVM to GQ GMC
  // cout << "load cfg" << endl; // debug
  loadConfigurationData();

  // cout << update_cfg_cmd << endl; // debug
  // Issue command to update NVM and force GQ GMC to change
  // operation in accordance to new configuration data.
  communicate(update_cfg_cmd, ret_char, retsize);

  // If read of returned data succeeded, convert raw data to float,
  if (mRead_status == true)
  {
    // We really don't care about the return value of 0xAA. If the
    // GQ GMC does not recognize the command, it returns nothing and
    // we get a read_status error.
  }
  else  // else for failure, set the error code.
  {
    mError_code = eUpdate_CFG;
  }

  return;
} // end updateConfigurationData()


// sendKey is the public method to emulate any one of the 4 keys on
// the front panel of the GQ GMC. The front panel has a 'left arrow',
// 'up arrow, 'down arrow', and 'enter' keys. These are used to
// navigate through the GQ GMC's menu system. In principle, the
// menu system can be used to set virtually any and all of the
// configuration data (although this is not recommended for
// such configuration data as the calibration values).
// So instead of the writeConfigurationData method,
// the proper sequence of sending the keys would do the
// same thing. The command is derived dynamically, but the actual
// command is "<KEY0>>" or "<KEY1>>" or "<KEY2>>" or "<KEY3>>".
// So for the purpose that the user need not know the actual
// command string, the softkey_t enumeration is created and used
// as the passed argument to the method. See the enum softkey_t
// declaration in gqgmc.hh for more discussion.
//
// For successive sendKey calls there is a trick to know when
// using the sendKey method. The trick is you can't
// transmit sendKey too fast and you can't do it too slow.
// Another thing is that the Save Data option menu starts with
// the current data type and then cycles through the other options.
// So you have to know what is the current data type and
// then send the Enter key the proper number of times to cycle
// to the desired option. When moving through the menu we use
// 0.5 seconds, but when moving through a pop up options menu
// we have to move faster and so should use 0.25 seconds
// between sendKey in that context.
void
func (gc *GQGMCCounter) sendKey(enum softkey_t key)
{
  char   inp[1]; // This will not be used, just needed as dummy arg

  // Begin formulating the send key command.
  string keycmd = "<KEY";

  // Append key number which is an enumeration equal to the
  // ASCII value of the key, ie, '0', '1', '2', or '3'.
  keycmd += uint8_t(key);
  // Append ">>"
  keycmd += ">>";

  communicate(keycmd, inp, 0);  // no return data
  // Since the sendkey command returns no data there is no way to
  // test success of communication.

  // Debug code
/*
  for(int i=0; i<7; i++)
    cout << Hex(keycmd[i]);
  cout << endl;
*/
  return;
} // end sendKey()


// setDate is the public method to set the date. The date is passed as
// an ASCII string with the format of <month><day><year>, for example,
// 112312 is November (11th month) 12, 2012.  The year is specified
// as the last two digits of the century since presumably the date is
// is being set to the current date. In reality, the GQ GMC has a separate
// command for setting each of the month, day, and year.
void
func (gc *GQGMCCounter) setDate(string date)
{
  const
  uint32_t     retsize = 1;
  char         ret_char[retsize+1];

  // The date is broken up into three separate commands one each for
  // month, day, and year as supported by the GQ GMC.

  // Set the month, <SETDATEMMXX>> where XX = month byte.
  {
    // uint16_t is necessary since ss >> uint8_t does not work.
    uint16_t      month=0;
    string        setMonthCmd;
    stringstream  ss;
    ss << date[0] << date[1];
    ss >> month;
    //cout << "month = " << Hex(uint8_t(month)) << endl;
    setMonthCmd  = "<SETDATEMM";
    setMonthCmd += uint8_t(month);
    setMonthCmd += ">>";
    communicate(setMonthCmd, ret_char, retsize);
  }

  // Set the day, <SETDATEDDXX>> where XX = day byte.
  {
    uint16_t      day=0;
    string        setDayCmd;
    stringstream  ss;
    ss << date[2] << date[3];
    ss >> day;
    //cout << "day = " << Hex(uint8_t(day)) << endl;
    setDayCmd  = "<SETDATEDD";
    setDayCmd += uint8_t(day);
    setDayCmd += ">>";
    communicate(setDayCmd, ret_char, retsize);
  }

  // Set the year, <SETDATEYYXX>> where XX = year byte.
  {
    uint16_t      year=0;
    string        setYearCmd;
    stringstream  ss;
    ss << date[4] << date[5];
    ss >> year;
    //cout << "year = " << Hex(uint8_t(year)) << endl;
    setYearCmd  = "<SETDATEYY";
    setYearCmd += uint8_t(year);
    setYearCmd += ">>";
    communicate(setYearCmd, ret_char, retsize);
  }

  return;
} // end set Date()

// setTime is the public method to set the time of day. The time is
// passed as an ASCII string with the format of <hour><minutes><seconds>,
// for example, 142256 is the 14th hour, 22 minutes after the hour,
// 56 seconds after the minute.  The hour is given in 24 hour format
// counting from 0 to 23. In reality, the GQ GMC provides a separate
// command for setting each of the hour, minutes and seconds.
void
func (gc *GQGMCCounter) setTime(string time)
{
    const
    uint32_t     retsize = 1;
    char         ret_char[retsize+1];

    // The time is broken up into three separate commands one each for
    // hour, minute, and second as supported by the GQ GMC.

    // Set the hour, <SETTIMEHHXX>> where XX = hour byte.
    {
      uint16_t      hour=0;       // stringstream does not convert to uint8_t
      string        setHourCmd;
      stringstream  ss;
      ss << time[0] << time[1];
      ss >> hour;
      //cout << "hours = " << Hex(uint8_t(hour)) << endl;
      setHourCmd  = "<SETTIMEHH";
      setHourCmd += uint8_t(hour);
      setHourCmd += ">>";
      communicate(setHourCmd, ret_char, retsize);
    }

    // Set the minute, <SETTIMEMMXX>> where XX = minute byte.
    {
      uint16_t      minute=0;
      string        setMinuteCmd;
      stringstream  ss;
      ss << time[2] << time[3];
      ss >> minute;
      //cout << "minute = " << Hex(uint8_t(minute)) << endl;
      setMinuteCmd  = "<SETTIMEMM";
      setMinuteCmd += uint8_t(minute);
      setMinuteCmd += ">>";
      communicate(setMinuteCmd, ret_char, retsize);
    }

    // Set the seconds, <SETTIMESSXX>> where XX = second byte.
    {
      uint16_t      second=0;
      string        setSecondCmd;
      stringstream  ss;
      ss << time[4] << time[5];
      ss >> second;
      //cout << "second = " << Hex(uint8_t(second)) << endl;
      setSecondCmd  = "<SETTIMESS";
      setSecondCmd += uint8_t(second);
      setSecondCmd += ">>";
      communicate(setSecondCmd, ret_char, retsize);
    }

  return;
} // end setTime()


// PRIVATE METHODS

// communicate private method is used to write/read data to/from
// the GMC-300. This method is expressedly designed to be called
// by methods which send an ASCII string and expect to receive
// returned data. However for flexibility, if the command string
// is null, no command is transmitted and if the expected number
// of return bytes is zero, no read is performed.
// cmd is the ASCII string command.
// retdata is the repository for the returned data.
// retbytes is the number of bytes of returned data.
void
func (gc *GQGMCCounter) communicate(const string cmd, char * retdata, uint32_t retbytes)
{
  // Clear the USB port of any left over data from last exchange. Even
  // though we know how many bytes the GQ GMC transmits for each
  // command, experience has shown this is the safe thing to do since
  // there is no protocol for the returned data.
  clearUSB();

  //cout << cmd << endl;
  // 1st, issue the command to the GMC-300, this is always an ASCII
  // string.
  // For flexibility, only transmit if cmdbytes is not 'null'.
  if (cmd.size() > 0) sendCmd(cmd);
  // 2nd, read the return data, for all commands except get version
  // this is always raw binary data.
  // For flexibility, only read if return is not 'null'.
  if (retbytes > 0) readCmdReturn(retdata, retbytes);

  return;
} // end communicate()

// sendCmd is the private method (the basic method) to transmit
// the command to the GMC-300.
// cmd is the ASCII string to send as the command.
void
func (gc *GQGMCCounter) sendCmd(const string cmd)
{
  // This is a common place to reset the error code since it is always
  // called for any command.
  mError_code = eNoProblem;

  // This is a common place to reset the read status since every read
  // is always preceeded by a write command (except for turn_on_cps!).
  mRead_status = true;

  // Call low level C stdio routine to write to USB port.
  write(mUSB_serial, cmd.c_str(), cmd.size());

  return;
} // end sendCmd()

// readCmdReturn is the private method (the basic method) to read
// the return bytes from the command.
// retdata is the repository for the returned data.
// retbytes is the number of bytes of the returned data.
void
func (gc *GQGMCCounter) readCmdReturn(char * retdata, uint32_t retbytes)
{
  uint32_t rcvd = 0;           // the number of received bytes
  char * inp    = &retdata[0]; // pointer to returned data char array
                               // start pointer off at beginning of
                               // repository.

  // Assume read will succeed, replicated here only because of the
  // nature of the turn_on_cps command which automatically returns
  // data without a preceeding write.
  mRead_status = true;

  // Now read the returned raw byte string. Do this by reading one byte
  // at a time until the requested number of bytes are attained. However,
  // the serial port has been setup to timeout each read attempt. So if
  // after N calls to read, we haven't yet read all N bytes, declare
  // a failure. The read is done this way to avoid an indefinite blocking
  // situation when 0 bytes are returned by the GQ GMC. The only good thing
  // about this methodology is that the largest possible read is only 4K
  // for the history data. So the read never really takes that much time.
  for(uint32_t i=0; i<retbytes; i++)
  {
    rcvd += read(mUSB_serial, inp, 1);
    inp   = &retdata[rcvd];
    if (rcvd >= retbytes) break;
  } // end for loop

  //  debugging code
  /*
  inp = &retdata[0];
  for(uint32_t i=0; i<retbytes; i++)
  {
    cout << Hex(inp[i]) << "-";
    if (i > 0)
      if (((i+1)%16) == 0) cout << endl;
    if (i > 62)break;
  }
  cout << endl;
  cout << "rcvd = " << rcvd << endl;
  */
  // end debug code

  // Communication is considered a failure if less than the expected
  // number of bytes is returned by the GMC-300.
  if (rcvd < retbytes)
    mRead_status = false;

  return;
} // readCmdReturn()

810
811
812
// GQGMCCounter is a GQ GMC Counter
type GQGMCCounter struct {
	fh string // TODO: make this a file handle.
813
	config serial.Config
814
815
816
817
}

// NewGQGMC creates a new GQGMC Counter instance
func NewGQGMC(c Config) (*GQGMCCounter, error) {
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
	var gc GQGMCCounter

	portCfg := serial.Config {
		Name: c.Device,
		Baud: 57600,
		ReadTimeout: 500 * time.Millisecond,
	}
	gc.port = OpenPort(portCfg)
	//vers := getVersion()
        //getConfigurationData()
	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.
	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
}

// GetCPM returns CPM
func (gc *GQGMCCounter) GetCPM() (uint16, error) {
	//uint32_t cpmsize = 2;          // 2 bytes of returned data
	//communicate(get_cpm_cmd, cpm_char, cpmsize);
	// 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.
	//cpm_int |= ((uint16_t(cpm_char[0]) << 8) & 0x3f00);
	//cpm_int |=  (uint16_t(cpm_char[1]) & 0x00ff);
	return 0, nil
}

// GetCPS returns CPS
func (gc *GQGMCCounter) GetCPS() (uint16, error) {
	//uint32_t cpssize = 2;          // 2 bytes of returned data
	//communicate(get_cps_cmd, 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
}


const (
	VoltageIdeal = 98
	VoltageTooLow = 75
)

// 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.
}

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
}

func (gc *GQGMCCounter) TurnOffCPS() error {
//sendCmd(turn_off_cps_cmd);
//call Clear()
}

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
947
948
}

949
950
951
func (gc *GQGMCCounter) TurnOffPower() {
  sendCmd(turn_off_pwr_cmd);
  // Note that power off cannot fail because the GQ GMC returns nothing.
952
}