Internals
=========

Debugging
---------

First, install the necessary software to get the GPS tracker working on Windows
(drivers and @trip PC from
http://global.mobileaction.com/download/i-gotU_download.jsp). Restart your PC,
then play around with the GPS tracker, try to record some points, and make sure
you can actually read them into @trip PC. Close @trip PC and disconnect the
tracker.

To make @trip PC output debug information, create the two directories
'C:\GT_DEBUG_LOG' and 'C:\GT_DATA_LOG'. Because we want to know what is sent to
the serial port created by the device driver for the GPS tracker, download
portmon (http://technet.microsoft.com/en-us/sysinternals/bb896644.aspx) and
start it. Set "Edit->Max Output Bytes" to 4096, select "Options->Show Hex", and
choose the correct com port for the tracker in "Capture->Ports".

Attach the GPS tracker to the PC, start @trip PC and download the complete data
on the tracker.

'C:\GT_DATA_LOG\Data_*.csv' now contain decoded data logs of all downloaded
trackpoints, 'C:\GT_DEBUG_LOG\GT.log' contains debug messages of the driver dll
and the portmon log contains all communication with the device. Use
----
    src/serialdecoder/serialdecoder.py portmon.log
---- 
to get an idea what the commands mean.

GPSD support
------------

For gpsd support, try the navman kernel driver to get an USB serial device at
something like /dev/ttyUSB0. Do this before you plug the device in:
----
    sudo modprobe navman
    echo -n "0x0df7 0x0900" | sudo tee /sys/bus/usb-serial/drivers/navman/new_id
----
and then plug your device in. You now have read-only access to the device and
you can use it as an nmea data source for gpsd.

USB transport
-------------

The i-gotU GT-120 has a vendor id of 0x0df7 and a product id of 0x0900.

Data can be read from interrupt endpoint 0x81 with sth. like
----
    usb_interrupt_read(handle, 0x81, buf, size, timeout);
----

Writes are done with control messages:
----
    usb_control_msg(handle, 0x21, 0x09, 0x0200, 0x0000, data, size, timeout);
----

The Windows driver schedules the reads asynchronously, i.e., writes are
independent from reads. If one read completes, the next one is started
immediately. Such a scheme was also implemented with libusb 1.0, but did not
result in successful communication. Instead, reads and writes are scheduled
synchronously, which works perfectly well.

There seems to be a firmware bug that may send responses twice for some
commands. Because the transmission is done synchronously, it is not possible to
detect and discard superfluous bytes. This is especially important on Mac OS X,
as the USB interrupt transfers there ignore the timeout given.

Binary protocol
---------------

Commands are sent in 8 byte chunks. Each chunk is acknowledged by the gps
tracker. Most commands consist of 2 chunks. The last byte of all commands
contains a checksum (SS) so that the sum of all command bytes is zero.

Before each command, the connection is cleared, i.e., all superfluous bytes
that are still in the input buffer are discarded.

----
Response:
  Data: 93 XX XX
  XX: big endian size (>= 0) or error code (< 0):
      fffe: seems to be a recoverable error
----

General commands
~~~~~~~~~~~~~~~~

----
NmeaSwitchCommand:
  Command chunk 1: 93 01 01 XX 00 00 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00
  XX: 
    00 GPS Dongle mode (NMEA)
    01 GPS Tracer mode (normal trackpoint recording)
    03 GPS Configure mode (no NMEA)

IdentificationCommand:
  Command chunk 1: 93 0A 00 00 00 00 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 0A XX XX XX XX YY ZZ MM MM UU UU
  XX: little endian serial number
  YY: major version number
  ZZ: minor version number
  MM: model number (01 for GT100/GT120, 02 for GT200)?
  UU: USB library version

CountCommand (number of data points):
  Command chunk 1: 93 0B 03 00 1D 00 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 03 ?? XX XX
  XX: big endian number of data points

Internal memory read? (used for number of datapoints):
  Command chunk 1: 93 0B XX YY YY 00 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 XX ZZ...
  XX: read size
  YY: big endian read position
  ZZ: read data

ModelCommand:
  Command chunk 1: 93 05 04 00 03 01 9F 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 03 XX XX YY
  XX: always C220?
  YY: 13 for GT-100, 14 for GT-120, 15 for GT-200

Internal memory read? (used for model type):
  Command chunk 1: 93 05 04 XX XX 01 9F 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 XX XX YY...
  XX: read size
  YY: read data
----

Flash read commands
~~~~~~~~~~~~~~~~~~~

----
ReadCommand:
  Command chunk 1: 93 05 07 XX XX 04 03 YY
  Response:        93 00 00
  Command chunk 2: YY YY 00 00 00 00 00 SS
  Response:        93 XX XX ZZ...
  XX: big endian read size (1000 for read, 0010 for purge)
  YY: big endian read position
  ZZ: read data
----

Flash write/erase commands
~~~~~~~~~~~~~~~~~~~~~~~~~~

----
UnknownWriteCommand1:
  Command chunk 1: 93 06 04 00 XX 01 06 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00
  XX: 00 for purge, 01? for write

WriteCommand:
  Command chunk 1: 93 06 07 XX XX 04 WW YY
  Response:        93 00 00
  Command chunk 2: YY YY 00 00 00 00 00 SS
  Response:        93 00 00
  Data chunk n:    ZZ ZZ ZZ ZZ ZZ ZZ ZZ SS
  Response:        93 00 00
  WW: 20 for purge, 02 for write
  XX: big endian write size (0000 for purge, 0100 for write)
  YY: big endian write position
  ZZ: write data
  needs ceil(size / 7) data chunks

UnknownWriteCommand2:
  Command chunk 1: 93 05 04 XX XX 01 05 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 XX XX ZZ
  XX: big endian read size (0001 for purge and write)
  ZZ: purge first time 03, purge second time and write 00

UnknownWriteCommand3:
  Command chunk 1: 93 0d 07 00 00 00 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00

UnknownPurgeCommand1:
  Command chunk 1: 93 0C 00 XX 00 00 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00
  XX: first time called 1E, second time called 1F

UnknownPurgeCommand2:
  Command chunk 1: 93 08 02 00 00 00 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00

TimeCommand:
  Command chunk 1: 93 09 03 XX YY ZZ 00 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00
  XX:YY:ZZ: UTC time
----

Other commands
~~~~~~~~~~~~~~

----
Command used after write (not tested):
  Command chunk 1: 93 06 04 00 01 01 06 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00
----

----
BC4 off mode:
  Command chunk 1: 93 01 XX 04 HH MM LL 00
  Response:        93 00 00
  Command chunk 2: 00 00 00 00 00 00 00 SS
  Response:        93 00 00
  XX: 
    01 log time = 0
    04 log time > 0
  HH, MM, LL: logtime (seconds)?

Purge
-----

@trip PC reads the first 0x10 bytes of each block starting from the end of the
flash (0x0FF000 or 0x1FF000) and stops at the first block where these bytes are
not 0xFF. Each block but the first one is then purged according to the
following pseudo code:

GT-100, Firmware 1.39, @trip PC 2.0 908.446
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

----
  purge = false
  blocks = 0x080
  for i = blocks - 1; i > 0; --i:
    if purge:
      while UnknownWriteCommand2(0x0001) != 0x00:
        pass
    else:
      if ReadCommand(i * 0x1000, 0x10) != rep(0xff, 0x10):
        purge = true
      else:
        continue
    UnknownWriteCommand1(0x00)
    WriteCommand(0x20, i * 0x1000, '')
  if purge:
    UnknownPurgeCommand2()
    while UnknownWriteCommand2(0x0001) != 0x00:
      pass
  UnknownPurgeCommand2()
----

GT-200, Firmware 2.11, @trip PC 1.0 804.0976
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

----
  purge = false
  blocks = 0x100
  for i = blocks - 1; i > 0; --i:
    if purge:
      while UnknownWriteCommand2(0x0001) != 0x00:
        pass
    else:
      if ReadCommand(i * 0x1000, 0x10) != rep(0xff, 0x10):
        purge = true
      else:
        continue
    UnknownWriteCommand1(0x00)
    WriteCommand(0x20, i * 0x1000, '')
  if purge:
    UnknownPurgeCommand2()
    while UnknownWriteCommand2(0x0001) != 0x00:
      pass
----

GT-200, Firmware 2.11, @trip PC 2.0 811.2582
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

----
  purge = false
  blocks = 0x100
  for i = blocks - 1; i > 0; --i:
    if purge:
      while UnknownWriteCommand2(0x0001) != 0x00:
        pass
    else:
      if ReadCommand(i * 0x1000, 0x10) != rep(0xff, 0x10):
        purge = true
      else:
        continue
    UnknownWriteCommand1(0x00)
    WriteCommand(0x20, i * 0x1000, '')
  if purge:
    UnknownPurgeCommand2()
    while UnknownWriteCommand2(0x0001) != 0x00:
      pass
  UnknownPurgeCommand2()
----

GT-100, Firmware 2.24, @trip PC 2.0 908.446
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

----
  purge = false
  blocks = 0x080
  for i = blocks - 1; i > 0; --i:
    if purge:
      while UnknownWriteCommand2(0x0001) != 0x00:
        pass
    else:
      if ReadCommand(i * 0x1000, 0x10) != rep(0xff, 0x10):
        purge = true
      else:
        continue
    UnknownWriteCommand1(0x00)
    WriteCommand(0x20, i * 0x1000, '')
  if purge:
    UnknownPurgeCommand1(0x1e)
    UnknownPurgeCommand1(0x1f)
    while UnknownWriteCommand2(0x0001) != 0x00:
      pass
  UnknownPurgeCommand1(0x1e)
  UnknownPurgeCommand1(0x1f)
----

GT-120, Firmware 3.03, @trip PC 2.0 811.2582
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

----
  purge = false
  blocks = 0x200
  for i = blocks - 1; i > 0; --i:
    if purge:
      while UnknownWriteCommand2(0x0001) != 0x00:
        pass
    else:
      if ReadCommand(i * 0x1000, 0x10) != rep(0xff, 0x10):
        purge = true
      else:
        continue
    UnknownWriteCommand1(0x00)
    WriteCommand(0x20, i * 0x1000, '')
  if purge:
    UnknownPurgeCommand1(0x1e)
    UnknownPurgeCommand1(0x1f)
    while UnknownWriteCommand2(0x0001) != 0x00:
      pass
  UnknownPurgeCommand1(0x1e)
  UnknownPurgeCommand1(0x1f)
----

Configuration
-------------

GT-120, Firmware 3.03, @trip PC 2.0 811.2582
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

----
  UnknownWriteCommand1(0x00)
  WriteCommand(0x20, 0x0000, '')
  while UnknownWriteCommand2(0x0001) != 0x00:
    pass

  blocks = 0x10
  for i = 0; i < blocks; ++i:
    UnknownWriteCommand1(0x00)
    WriteCommand(0x02, i * 0x0100, config(i * 0x0100:(i + 1) * 0x0100))
    while UnknownWriteCommand2(0x0001) != 0x00:
        pass

  TimeCommand(now())
  ReadCommand(0, 0x1000)
  UnknownWriteCommand3()

----
Flash layout
------------

The first data block of 0x1000 bytes contains configuration data, all other
data blocks contain the gps trackpoints.

Configuration data:
----
  0x0000: uint8 number of schedule table plans (1 if no schedule used)
  0x0001: uint16 first schedule day (days since 01.01.2000 + 1)
  0x0003: uint8 flags
          0x01: debug mode (save extra records for debugging, fw 2.15)
          0x02: recycle record if memory full (fw 2.15)
          0x04: schedule enabled
          0x08: GT200e dual mode
          0x10: obsolete
          0x20: schedule is repeat?
          0x40: button disabled (gated by password command?)
          0x80: LED flashing when valid data is recorded is disabled
  0x0004: uint8 max allowed time when button is pushed and start tracer mode?
  0x0005: uint8 initial timeout when button is pushed and start tracer mode?
  0x0004: uint8 max allowed time when RTC power of GPS is lost (data/time is
          not a valid value)
  0x0005: uint8 initial timeout when RTC power of GPS is lost (data/time is 
          not a valid value)
  0x0008: uint8 schedule date offset: start date = utc date + offset
  0x0009..0x00ff: uint4 schedule table plans (1 if no schedule used,
          one-based), if terminated with 0 recording is stopped, 0xff means
          loop from beginning

  0x0100: schedule table entries for plan 1 (normally Sunday, no schedule)
  0x0200: schedule table entries for plan 2 (normally Monday)
  0x0300: schedule table entries for plan 3 (normally Tuesday)
  0x0400: schedule table entries for plan 4 (normally Wednesday)
  0x0500: schedule table entries for plan 5 (normally Thursday)
  0x0600: schedule table entries for plan 6 (normally Friday)
  0x0700: schedule table entries for plan 7 (normally Saturday)

  0x0800: uint8 security version; 0x00
  0x0801: uint8 password enabled: 0x80
  0x0802: uint8 password length in bytes (maximum 0x80)
  0x0803..0x0882: utf16le password
----

Schedule table entries are stored in records of 0x40 bytes:
----
  0x00: uint8 start hour (0xff if invalid, 0x00 if no schedule)
  0x01: uint8 start minute (0xff if invalid, 0x00 if no schedule)
  0x02: uint8 end hour (0xff if invalid, 0x00 if no schedule)
  0x03: uint8 end minute (0xff if invalid, 0x00 if no schedule)
  ...
  0x07: uint8 logging interval (number of seconds - 1)
  0x08: uint16b logging interval change speed * 1e2 (in m/s)
  0x0a: uint8 changed logging interval (number of seconds - 1)
  ...
----

Trackpoints are stored in records of 0x20 bytes:
----
  0x00: uint8 flags
        0x01: RTC calibration log (only in debug mode)
        0x02: time out log
        0x04: normal log (0) or waypoint log (1)   (type = -1)
        0x08: timeout > ok criteria?
        0x10: navigation data invalid
        0x20: track stop                           (type = -4)
        0x40: track start                          (type = -2)
        0x80: debug mode (64 byte trackpoints)
  0x01: uint8 YYYYMMMM
        YY year - 2000 (modulo 16)
        MM month (starting with 1)
  0x02: uint16b DDDDDHHHHHMMMMMM
        DD day of month (starting with 1)
        HH hour
        MM minute
  0x04: uint16b seconds * 1000 + milliseconds
  0x06: uint16b AAAAEEEEEEEEEEEE
        AA unknown, found values 0 (common) and 1 (for flags = 0x12)
        EE EHPE * 1e2 / 16 (in m)
  0x08: uint32b satellite map (one bit per satellite)
  0x0c: int32b latitude * 1e7 (in degrees)
  0x10: int32b longitude * 1e7 (in degrees)
  0x14: int32b elevation * 1e2 (in m)
  0x18: uint16b speed * 1e2 (in m/s)
  0x1a: uint16b course * 1e2 (in degrees)
  0x1c: uint8 timeout in s
  0x1d: uint8 SSSSCCCC
        SS maximum found SV for this record
        CC qualified satellite number with C/N value > criteria
  0x1e: uint8 weight criteria
  0x1f: uint8 qualified satellite number with C/N value > criteria? or sleep
        time (unit 16s, debug mode)
----
