diff --git a/README.md b/README.md index e151972..d7b06d7 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ So now what? Use the tools... eporetrieve [ [ ] ] Example: -# eporetrieve MTK14.EPO /tmp/MTK14.EPO +# eporetrieve EPO.DAT /tmp/EPO.DAT ``` This script is just a wrapper around wget. It's generally best to do this in the morning UTC @@ -59,34 +59,36 @@ optional arguments: You can use '@filename' to read arguments from a file. ``` -To examine the file /tmp/MTK14.EPO... +To examine the file /tmp/EPO.DAT... ``` -# epoinfo /tmp/MTK14.EPO -Opening EPO Type I file -Set: 1. GPS Wk: 1832 Hr: 48 Sec: 172800 2015-02-16 23:59:46 to 2015-02-17 05:59:46 UTC -Set: 2. GPS Wk: 1832 Hr: 54 Sec: 194400 2015-02-17 05:59:46 to 2015-02-17 11:59:46 UTC -Set: 3. GPS Wk: 1832 Hr: 60 Sec: 216000 2015-02-17 11:59:46 to 2015-02-17 17:59:46 UTC -Set: 4. GPS Wk: 1832 Hr: 66 Sec: 237600 2015-02-17 17:59:46 to 2015-02-17 23:59:46 UTC -Set: 5. GPS Wk: 1832 Hr: 72 Sec: 259200 2015-02-17 23:59:46 to 2015-02-18 05:59:46 UTC +# epoinfo /tmp/EPO.DAT +Opening EPO Type II file +Set: 1. GPS Wk: 2194 Hr: 24 Sec: 86400 2022-01-23 23:59:46 to 2022-01-24 05:59:46 UTC +Set: 2. GPS Wk: 2194 Hr: 30 Sec: 108000 2022-01-24 05:59:46 to 2022-01-24 11:59:46 UTC +Set: 3. GPS Wk: 2194 Hr: 36 Sec: 129600 2022-01-24 11:59:46 to 2022-01-24 17:59:46 UTC +Set: 4. GPS Wk: 2194 Hr: 42 Sec: 151200 2022-01-24 17:59:46 to 2022-01-24 23:59:46 UTC +Set: 5. GPS Wk: 2194 Hr: 48 Sec: 172800 2022-01-24 23:59:46 to 2022-01-25 05:59:46 UTC -Set: 53. GPS Wk: 1834 Hr: 24 Sec: 86400 2015-03-01 23:59:46 to 2015-03-02 05:59:46 UTC -Set: 54. GPS Wk: 1834 Hr: 30 Sec: 108000 2015-03-02 05:59:46 to 2015-03-02 11:59:46 UTC -Set: 55. GPS Wk: 1834 Hr: 36 Sec: 129600 2015-03-02 11:59:46 to 2015-03-02 17:59:46 UTC -Set: 56. GPS Wk: 1834 Hr: 42 Sec: 151200 2015-03-02 17:59:46 to 2015-03-02 23:59:46 UTC - 56 EPO sets. Valid from 2015-02-16 23:59:46 to 2015-03-02 23:59:46 UTC +Set: 115. GPS Wk: 2198 Hr: 36 Sec: 129600 2022-02-21 11:59:46 to 2022-02-21 17:59:46 UTC +Set: 116. GPS Wk: 2198 Hr: 42 Sec: 151200 2022-02-21 17:59:46 to 2022-02-21 23:59:46 UTC +Set: 117. GPS Wk: 2198 Hr: 48 Sec: 172800 2022-02-21 23:59:46 to 2022-02-22 05:59:46 UTC +Set: 118. GPS Wk: 2198 Hr: 54 Sec: 194400 2022-02-22 05:59:46 to 2022-02-22 11:59:46 UTC +Set: 119. GPS Wk: 2198 Hr: 60 Sec: 216000 2022-02-22 11:59:46 to 2022-02-22 17:59:46 UTC +Set: 120. GPS Wk: 2198 Hr: 66 Sec: 237600 2022-02-22 17:59:46 to 2022-02-22 23:59:46 UTC + 120 EPO sets. Valid from 2022-01-23 23:59:46 to 2022-02-22 23:59:46 UTC ``` -Each set covers a 6 hour period so for a 14 day file, there should be 56 sets in the file. -For the PA6H, make sure the file is a Type I file and that the "Valid from" span includes -the current time. +The current EPO.DAT provides by MediaTek is a type II file covering 30 days. Since each +set covers 6 hours, the file will contain 120 sets. However, the MT3999 can only keep +26 sets of Type II data, or about 6.5 days worth, in it's NVRAM. See below. + ### Load the EPO file to the GPS: #### epoloader: Loads an EPO file to the GPS ``` -usage: epoloader [-h] [-t yyyy,mm,dd,hh,mm,ss | -] - [-l lat.dddddd,lon.dddddd,alt] - [-s {4800,9600,19200,38400,57600,115200}] [-k] [-c] +usage: epoloader [-h] [-t yyyy,mm,dd,hh,mm,ss | -] [-l lat.dddddd,lon.dddddd,alt] + [-s {4800,9600,19200,38400,57600,115200}] [-k] [-c] [-n] [-m MAX_SETS] Loads EPO data sets to MT3339 GPS @@ -95,20 +97,21 @@ positional arguments: EPO File or '-' to just set the known parameters GPS serial device such as '/dev/ttyUSB0' -optional arguments: +options: -h, --help show this help message and exit -s {4800,9600,19200,38400,57600,115200}, --speed {4800,9600,19200,38400,57600,115200} Interface speed -k, --keep-new-speed Don't restore the old speed on exit -c, --clear Clears the existing EPO data from the unit - -n, --no-init The unit is already in the correct state + -n, --no-init Skips the initialization + -m MAX_SETS, --max-sets MAX_SETS + Send only n sets optional known time and location parameters: -t yyyy,mm,dd,hh,mm,ss | - , --time yyyy,mm,dd,hh,mm,ss | - Current UTC or UTC from host -l lat.dddddd,lon.dddddd,alt, --location lat.dddddd,lon.dddddd,alt - Current location specified in decimal degrees and - meters + Current location specified in decimal degrees and meters You can use '@filename' to read arguments from a file. ``` @@ -153,35 +156,56 @@ setting both the unit and the port to the correct speed, then resetting both to original speed on completion. Use 115200 (the default) unless you have a flaky setup. You can also specify the '-k' option to leave the unit and port at the new speed. -NOTE: It's pretty much impossible to mess up the GPS unit with this process as it doesn't touch +It's pretty much impossible to mess up the GPS unit with this process as it doesn't touch the unit's firmware. If you get into a bad communications state, just try again, use gpsinit, try holding the EN signal low for a second, or remove the -power and battery for a few seconds to reset back to factory state. +power and battery for a few seconds to reset back to factory state. -Now you're ready. Assuming your location and time is in epoloader.conf, the EPO file is -/tmp/MTK14.EPO, your device is connected to /dev/ttyUSB1 and you want to use the default 115200 speed, then... +Almost there... Since the MT3339 can only hold 26 EPO sets, the default maximum number +of sets to send is 26. You can change this with the `-m / --max-sets` option but +attempting to send more than 26 will cause the validation to fail + +NOW you're ready. Assuming your location and time is in epoloader.conf, the EPO file is +/tmp/EPO.DAT, your device is connected to /dev/ttyUSB1 and you want to use the default 115200 speed, then... ``` -# epoloader @epoloader.conf /tmp/MTK14.EPO /dev/ttyUSB1 -Opening EPO Type I file -Current port speed: 9600 -Set port speed: 115200 -Setting known values: 38.889468,-77.352420,5 2015,02,17,23,15,35 +$ ./epoloader -s 115200 -c -l 39.754598,-105.236353,1780 -t- /tmp/EPO.DAT /dev/ttyUSB1 +Opening EPO Type II file with 120 sets +Setting NMEA at 115200 baud. + Unit is currently in NMEA mode at 115200 baud. +GPS and port are now synchronized at 115200 +Setting known values: 39.754598,-105.236353,1780 2022,01,24,14,23,04 +Time set +Location set Clearing existing EPO data Setting binary mode, speed: 115200 -Sending 56 EPO sets -Sending set 1. Valid from 2015-02-16 23:59:46 UTC -Sending set 2. Valid from 2015-02-17 05:59:46 UTC -Sending set 3. Valid from 2015-02-17 11:59:46 UTC -Sending set 4. Valid from 2015-02-17 17:59:46 UTC -Sending set 5. Valid from 2015-02-17 23:59:46 UTC - -Sending set 52. Valid from 2015-03-01 17:59:46 UTC -Sending set 53. Valid from 2015-03-01 23:59:46 UTC -Sending set 54. Valid from 2015-03-02 05:59:46 UTC -Sending set 55. Valid from 2015-03-02 11:59:46 UTC -Sending set 56. Valid from 2015-03-02 17:59:46 UTC - 56 sets sent. Valid from 2015-02-16 23:59:46 to 2015-03-02 23:59:46 UTC -Resetting NMEA Mode +Sending 26 EPO sets of 120 +Sending set 1. Valid from 2022-01-23 23:59:46 UTC +Sending set 2. Valid from 2022-01-24 05:59:46 UTC +Sending set 3. Valid from 2022-01-24 11:59:46 UTC +Sending set 4. Valid from 2022-01-24 17:59:46 UTC +Sending set 5. Valid from 2022-01-24 23:59:46 UTC +Sending set 6. Valid from 2022-01-25 05:59:46 UTC +Sending set 7. Valid from 2022-01-25 11:59:46 UTC +Sending set 8. Valid from 2022-01-25 17:59:46 UTC +Sending set 9. Valid from 2022-01-25 23:59:46 UTC +Sending set 10. Valid from 2022-01-26 05:59:46 UTC +Sending set 11. Valid from 2022-01-26 11:59:46 UTC +Sending set 12. Valid from 2022-01-26 17:59:46 UTC +Sending set 13. Valid from 2022-01-26 23:59:46 UTC +Sending set 14. Valid from 2022-01-27 05:59:46 UTC +Sending set 15. Valid from 2022-01-27 11:59:46 UTC +Sending set 16. Valid from 2022-01-27 17:59:46 UTC +Sending set 17. Valid from 2022-01-27 23:59:46 UTC +Sending set 18. Valid from 2022-01-28 05:59:46 UTC +Sending set 19. Valid from 2022-01-28 11:59:46 UTC +Sending set 20. Valid from 2022-01-28 17:59:46 UTC +Sending set 21. Valid from 2022-01-28 23:59:46 UTC +Sending set 22. Valid from 2022-01-29 05:59:46 UTC +Sending set 23. Valid from 2022-01-29 11:59:46 UTC +Sending set 24. Valid from 2022-01-29 17:59:46 UTC +Sending set 25. Valid from 2022-01-29 23:59:46 UTC +Sending set 26. Valid from 2022-01-30 05:59:46 UTC + 26 sets sent. Valid from 2022-01-23 23:59:46 to 2022-01-30 11:59:46 UTC Verified EPO in NVRAM matches file ``` @@ -271,23 +295,25 @@ sets NMEA sentences and frequency and finally does an epo load. # # The following commands are recognized... # -# sleep Sleep for n.nnn seconds -# set_system_clock Gets the date/time from the GPS unit and sets the system clock -# setspeed Set new port and unit speed to -# epoloader Run epoloader -# factory_reset Clear system/user configurations then cold start -# The unit and port speed will be reset to 9600 +# sleep Sleep for n.nnn seconds +# set_system_clock Gets the date/time from the GPS unit and sets the system clock +# setspeed Set new port and unit speed to +# clear_epo Clear the existing EPO from the device +# eporetrieve Run eporetrieve +# epoloader Run epoloader +# factory_reset Clear system/user configurations then cold start +# The unit and port speed will be reset to 9600 # The following commands can be prefixed with '-' to not wait for an ACK. -# hot_start Restart using all available data -# warm_start Don't use Ephemeris at restart -# cold_start Don't use Time, Position, Almanacs and Ephemeris -# data at re‐start -# PMTKnnn, Send the specified PMTK command +# hot_start Restart using all available data +# warm_start Don't use Ephemeris at restart +# cold_start Don't use Time, Position, Almanacs and Ephemeris +# data at re‐start +# PMTKnnn, Send the specified PMTK command # # Clear the EPO NVRAM -PMTK127 +clear_epo # Factory reset factory_reset # Now set to 115200 diff --git a/epoloader b/epoloader index 599bb79..71f5992 100755 --- a/epoloader +++ b/epoloader @@ -34,6 +34,8 @@ baudrate = { termios.B115200: 115200, } +baudrates = [4800, 9600, 19200, 38400, 57600, 115200 ] + def Convert2UTC(GPSHour): GPSHour *= SECONDS_PER_HOUR GPSHour += GPS_OFFSET_SECONDS @@ -48,6 +50,8 @@ def crc8(d): crc = 0 if isinstance(d, (bytearray)): ba = d + elif isinstance(d, (bytes)): + ba = bytearray(d) else: ba = bytearray(d.encode("ASCII")) @@ -55,112 +59,70 @@ def crc8(d): crc ^= (b & 0xff) return crc -def getResponse(fg, command): - limit = 0 - while True: - b = fg.read(1) - if len(b) == 0: - break - - if b != '\x04': - continue - - b = fg.read(1) - if b != '\x24': - continue - length, cmd = struct.unpack(" 10: - break +def read_response(fg, timeout, r="PMTK001"): + fg.flushInput() + while True: + try: + for line in fg: + if len(line) < 3: + continue + spl = line.decode("ASCII").strip()[1:-3].split(',') + if spl[0] == r: + return spl + except: + pass + return None + +def send_string(fg, string, flush=True): + if isinstance(string, (bytearray)): + ba = string + elif isinstance(string, (bytes)): + ba = bytearray(string) + else: + ba = bytearray(string.encode("ASCII")) - return (-1, -1) + buf = b"$%s*%02x\r\n" % (ba, crc8(ba)) + fg.write(buf) + if flush: + fg.flush() -class rx_loop_thread(Thread): - def __init__(self, fg, r): - Thread.__init__(self) - self.fg = fg - self.result = None; - self.killme = False - self.r = r - - def getResult(self): - return self.result - - def kill(self): - self.killme = True - - def run(self): - self.fg.flushInput() - while True: - try: - for line in self.fg: - if self.killme: - return - if len(line) < 3: - continue - spl = line.decode("ASCII").strip()[1:-3].split(',') - if spl[0] == self.r: - self.result = spl - return - except: - pass -""" -class SerialReceiver(serial.threaded.LineReader): - TERMINATOR = b'\r\n' - def __init__(self): - super(SerialReceiver, self).__init__() - - def handle_line(self, line): - - - -class gpsproto(SerialReceiver): - def __init__(self): - super(gpsproto, self).__init__() - -def send_and_wait0(fg, string, timeout, r="PMTK001", retries=0): - with serial.threaded.ReaderThread(fg, gpsproto) as protocol: - send_string(fg, string) -""" - -def send_and_wait(fg, string, timeout, r="PMTK001", retries=0): - - resp = None +def send_and_wait(fg, string, timeout, r="PMTK001,0,3", retries=0, debug=False): + spl = None count = 0 + temp_timeout = fg.timeout + fg.timeout = timeout + while spl == None and count <= retries: + resp = None + if debug: + print(">> %s" % string) + send_string(fg, string, False); + fg.reset_input_buffer() + fg.flush() + resp = fg.read_until() + try: + sresp = resp.decode("ASCII") + if debug: + print("<< %s" % sresp) + ix = sresp.find(r) + if ix >= 0: + spl = sresp.strip()[ix:-3].split(',') + break + except: + pass + count = count + 1 + time.sleep(0.500) - while resp == None and count <= retries: - thread = rx_loop_thread(fg, r) - thread.setDaemon(True) - thread.start() - send_string(fg, string); - thread.join(timeout) - if thread.is_alive(): - thread.kill() - count += 1 - continue - return thread.getResult() - return resp + fg.timeout = temp_timeout + return spl -def send_string(fg, string): - buf = "$%s*%02x\r\n" % (string, crc8(string)) - fg.write(buf.encode("ASCII")) - fg.flush() -def send_speed(fg, speed, count, delay): - speed_string = "PMTK251,%d" % speed - send_string = "$%s*%02x\r\n" % (speed_string, crc8(speed_string)) - i = iter(range(count, -1, -1)) - while next(i): - fg.write(send_string.encode("ASCII")) - fg.flush() - time.sleep(delay) +def ping_unit(fg): + resp = send_and_wait(fg, "PMTK000", 0.250, retries=1) + if resp: + return True + return False def convert_arg_line_to_args(arg_line): for arg in arg_line.split(): @@ -168,74 +130,126 @@ def convert_arg_line_to_args(arg_line): continue yield arg -def set_nmea(fg, rate): +def set_nmea(fg, rate, quiet=False): + if not quiet: print("Setting NMEA at %d baud." % rate) + current_rate = fg.baudrate + fg.baudrate = rate + if ping_unit(fg): + print(" Unit is currently in NMEA mode at %d baud." % rate); + return True + if not quiet: print(" No response. Seeing if it's in binary mode.") buf = bytearray(b'\0' * 14) struct.pack_into(" 10: + break + return (-1, -1) def main(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@', @@ -246,11 +260,11 @@ def main(): group.add_argument("-t", "--time", dest="time_string", help="Current UTC or UTC from host", metavar="yyyy,mm,dd,hh,mm,ss | - ") group.add_argument("-l", "--location", dest="location_string", help="Current location specified in decimal degrees and meters", metavar="lat.dddddd,lon.dddddd,alt") - parser.add_argument("-s", "--speed", type=int, default=115200, dest="speed", help="Interface speed", choices=[4800, 9600, 19200, 38400, 57600, 115200]) + parser.add_argument("-s", "--speed", type=int, default=-1, dest="speed", help="Interface speed", choices=[4800, 9600, 19200, 38400, 57600, 115200]) parser.add_argument("-k", "--keep-new-speed", dest="keep_new_speed", default=False, action="store_true", help="Don't restore the old speed on exit") parser.add_argument("-c", "--clear", dest="clear_epo", default=False, action="store_true", help="Clears the existing EPO data from the unit") parser.add_argument("-n", "--no-init", dest="no_init", default=False, action="store_true", help="Skips the initialization") - parser.add_argument("-m", "--max-sets", type=int, default=-1, dest="max_sets", help="Send only n sets") + parser.add_argument("-m", "--max-sets", type=int, default=26, dest="max_sets", help="Send only n sets") parser.add_argument("input_file", metavar="", help="EPO File or '-' to just set the known parameters") parser.add_argument("output_device", metavar="", help="GPS serial device such as '/dev/ttyUSB0'") @@ -288,31 +302,25 @@ def main(): fi = io.open(args.input_file, mode="rb", buffering=0) - p = subprocess.Popen("lsof -FpcL %s 2>/dev/null" % args.output_device, - stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True, shell=True) - o = p.communicate()[0] - if len(o) > 0: - lines = o.split('\n') - print("ERROR: Device %s is open by PID: %s PROG: %s USER: %s" % (args.output_device, lines[0][1:], lines[1][1:], lines[2][1:])) - return 1; try: tfg = os.open(args.output_device, os.O_RDWR) + params = termios.tcgetattr(tfg); + port_baudrate = baudrate[params[5]]; + tty.setraw(tfg, tty.TCSANOW) except: print(sys.exc_info()[1]) return 1 + finally: + os.close(tfg) - params = termios.tcgetattr(tfg); - previous_baudrate = baudrate[params[5]]; - tty.setraw(tfg, tty.TCSANOW) - os.close(tfg) - - print("Current port speed: %d" % previous_baudrate) + if args.speed < 0: + args.speed = port_baudrate if args.no_init: fg = serial.Serial(args.output_device, timeout=5, baudrate=args.speed) else: - fg = get_known_state(args.output_device, args.speed, previous_baudrate, args.keep_new_speed) + fg = get_known_state(args.output_device, args.speed, port_baudrate, args.keep_new_speed) if fg is None: return 1 @@ -327,28 +335,29 @@ def main(): time_string = args.time_string print("Setting known values: %s %s" % (args.location_string, time_string)) - resp = send_and_wait(fg, "PMTK740,%s" % time_string, 2, retries=1) + resp = send_and_wait(fg, "PMTK740,%s" % time_string, 2, r="PMTK001,740,3", retries=2) if resp is None or resp[2] != '3': - print("ERROR: Unable to set time.", file=sys.stderr) - cleanup(fi, fg, args.output_device, previous_baudrate, args.keep_new_speed) + print("ERROR: Unable to set time.", resp, file=sys.stderr) + cleanup(fi, fg, args.output_device, port_baudrate, args.keep_new_speed) return 1 - - resp = send_and_wait(fg, "PMTK741,%s,%s" % (args.location_string, time_string), 2, retries=1) + print("Time set") + resp = send_and_wait(fg, "PMTK741,%s,%s" % (args.location_string, time_string), 2, r="PMTK001,741,3", retries=2) if resp is None or resp[2] != '3': print("ERROR: Unable to set location.", file=sys.stderr) - cleanup(fi, fg, args.output_device, previous_baudrate, args.keep_new_speed) + cleanup(fi, fg, args.output_device, port_baudrate, args.keep_new_speed) return 1 + print("Location set") if args.clear_epo: print("Clearing existing EPO data") - resp = send_and_wait(fg, "PMTK127", 2, retries=1) - if resp is None or resp[2] != '3': + resp = send_and_wait(fg, "PMTK127", 2, r="$CLR,EPO", retries=2) + if resp is None or resp[1] != 'EPO': print("ERROR: Unable to clear existing EPO data.", file=sys.stderr) - cleanup(fi, fg, args.output_device, previous_baudrate, args.keep_new_speed) + cleanup(fi, fg, args.output_device, port_baudrate, args.keep_new_speed) return 1 if fi is None: - cleanup(fi, fg, args.output_device, previous_baudrate, args.keep_new_speed) + cleanup(fi, fg, args.output_device, port_baudrate, args.keep_new_speed) return 0 print("Setting binary mode, speed: %d" % args.speed) @@ -382,7 +391,7 @@ def main(): if lseq == 0: epo_sets += 1 - set_start_time = struct.unpack("> %s" % string) + send_string(fg, string, False); + fg.reset_input_buffer() + fg.flush() + resp = fg.read_until() + try: + sresp = resp.decode("ASCII") + if debug: + print("<< %s" % sresp) + ix = sresp.find(r) + if ix >= 0: + spl = sresp.strip()[ix:-3].split(',') + break + except: + pass + count = count + 1 + time.sleep(0.500) + + + fg.timeout = temp_timeout + return spl + + +def ping_unit(fg): + resp = send_and_wait(fg, "PMTK000", 0.250, retries=1) + if resp: + return True + return False - b = fg.read(1) - if b != '\x24': +def convert_arg_line_to_args(arg_line): + for arg in arg_line.split(): + if not arg.strip(): continue + yield arg - length, cmd = struct.unpack(" 10: - break + if fg and not keep: + set_speed(fg, fg.baudrate, port_baudrate, 3, 0.057) + fg.baudrate = port_baudrate - return (-1, -1, -1, -1) + if fg: + fg.close() + if fi: + fi.close() +def get_known_state(device, speed, port_baudrate, keep): + fg = serial.Serial(port=device, timeout=1) +# tty.setraw(fg, tty.TCSANOW) -class rx_loop_thread(Thread): - def __init__(self, fg, r): - Thread.__init__(self) - self.fg = fg - self.result = None; - self.killme = False - self.r = r + if set_nmea(fg, speed): + print("GPS and port are now synchronized at %d" % speed) + return fg - def getResult(self): - return self.result + print("GPS isn't at the desired %d baud rate. Trying the current port rate of %d baud." % (speed, port_baudrate)) + if set_nmea(fg, port_baudrate): + print("Attempting to set it to %d baud" % speed) + if set_speed(fg, port_baudrate, speed, 3, 0.250): + print("GPS and port are now synchronized at %d" % speed) + return fg + else: + print("Couldn't set the new speed for some reason.") + return None + + print("GPS isn't at the current port rate either.") + foundrate = 0 + for rate in baudrates: + isnmea = set_nmea(fg, rate) + if isnmea: + foundrate = rate + break - def kill(self): - self.killme = True + if foundrate == 0: + print("Unable to locate unit at port %s" % device) + return None - def run(self): - self.fg.flushInput() - while True: - try: - for line in self.fg: - if self.killme: return - if len(line) < 3: - continue - spl = line.strip()[1:-3].split(',') - if spl[0] == self.r: - self.result = spl - return - except: - pass - -def send_and_wait(fg, string, timeout, r="PMTK001", retries=0): - resp = None - count = 0 + print("Found unit at %d baud. Attempting to set it to %d baud" % (foundrate, speed)) - while resp == None and count <= retries: - thread = rx_loop_thread(fg, r) - thread.setDaemon(True) - thread.start() - fg.flushOutput() - send_string(fg, string); - thread.join(timeout) - if thread.is_alive(): - thread.kill() - count += 1 - continue - return thread.getResult() - return resp + if not set_speed(fg, foundrate, speed, 1, 0.100): + print("Failed to set baudrate to %d" % speed) + return None -def send_string(fg, string): - fg.write("$%s*%02x\r\n".encode('utf8') % (string.encode('utf8'), crc8(string.encode('utf8')))) - fg.flush() + print("GPS and port are now synchronized at %d" % speed) + return fg -def send_speed(fg, speed, count, delay): - speed_string = "PMTK251,%d" % speed - send_string = "$%s*%02x\r\n".encode('utf8') % (speed_string.encode('utf8'), crc8(speed_string.encode('utf8'))) - i = iter(range(count, -1, -1)) - while next(i): - fg.write(send_string) - fg.flush() - time.sleep(delay) def setclock(fg): uid = os.geteuid() @@ -162,53 +228,52 @@ def setclock(fg): print("Set system time to %s UTC" % tts) -def convert_arg_line_to_args(arg_line): - for arg in arg_line.split(): - if not arg.strip(): - continue - yield arg - -def set_nmea(fg, rate): - buf = bytearray("\0\0\0\0\0\0\0\0\0\0\0\0\0\0".encode('utf8')) - struct.pack_into("= 1: - process_command(fg, line.replace("hot_start", "PMTK101")) + rc = process_command(fg, line.replace("hot_start", "PMTK101"), expected_response="$PGACK") elif line.count("warm_start") >= 1: - process_command(fg, line.replace("warm_start", "PMTK102")) + rc = process_command(fg, line.replace("warm_start", "PMTK102"), expected_response="$PGACK") elif line.count("cold_start") >= 1: - process_command(fg, line.replace("cold_start", "PMTK103")) + rc = process_command(fg, line.replace("cold_start", "PMTK103"), expected_response="$PGACK") + elif line.count("clear_epo") >= 1: + rc = process_command(fg, "PMTK127", expected_response="$CLR,EPO") elif line == "factory_reset": - process_command(fg, "-PMTK104") + rc = process_command(fg, "-PMTK104") time.sleep(0.500) - set_speed(fg, 9600) + fg.close() + fg = get_known_state(output_device, speed, port_baudrate, keep_new_speed) + if fg is None: + rc = 1 elif line == "set_system_clock": rc = setclock(fg) elif line[0:len("eporetrieve ")] == "eporetrieve ": @@ -217,97 +282,72 @@ def process_line(fg, device, speed, line): p = subprocess.Popen(d + line, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True, shell=True) while True: - pline = p.stdout.readline() + pline = p.stdout.readline().decode("utf-8") if not pline: break print(">> " + pline.strip()) elif line[0:len("epoloader ")] == "epoloader ": + port_baudrate = fg.baudrate fg.close() a0 = sys.argv[0] - d = dirname(a0) + 'python2 /' + d = dirname(a0) + '/' p = subprocess.Popen(d + line, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True, shell=True) while True: - pline = p.stdout.readline() + pline = p.stdout.readline().decode("utf-8") if not pline: break print(">> " + pline.strip()) - fg = serial.Serial(port=device, timeout=5, baudrate=speed) + fg = get_known_state(output_device, speed, port_baudrate, keep_new_speed) + if fg is None: + rc = 1 else: - process_command(fg, line) + rc = process_command(fg, line) return (rc, fg) -def get_known_state(device, speed, previous_baudrate, keep): - fg = serial.Serial(port=device, timeout=5, baudrate=speed) - tty.setraw(fg, tty.TCSANOW) - fg.flushInput() - resp = send_and_wait(fg, "PMTK000", 0.5, retries=1) - if resp and resp[2] == '3': - print("GPS and port are now synchronized at %d" % speed) - return fg - -# Make sure the unit is in NMEA mode - print("Making sure the unit is in NMEA mode") - for t in baudrate: - rate = baudrate[t] - fg.baudrate = rate - set_nmea(fg, rate) - set_nmea(fg, rate) - fg.flushInput() - fg.flushOutput() - time.sleep(0.500) - tries = 0 - while True: - print("Making sure the speeds match") - - for t in baudrate: - fg.baudrate = baudrate[t] - send_speed(fg, speed, 1, 0.100) - fg.flush() - - fg.baudrate = speed - time.sleep(0.250) - fg.flushInput() - # Now try to send a NoOp command - resp = send_and_wait(fg, "PMTK000", 0.50, retries=1) - if resp is None or resp[2] != '3': - print("The GPS speed and the port speed couldn't be synchronized.") - tries += 1 - if tries < 2: - print("Retrying") - else: - return None - else: - print("GPS and port are now synchronized at %d" % speed) - return fg def main(): + global output_device + global keep_new_speed + global speed + parser = argparse.ArgumentParser(fromfile_prefix_chars='@', description="Initialized GPS to known state", epilog="You can use '@filename' to read arguments from a file.") group = parser.add_mutually_exclusive_group() parser.convert_arg_line_to_args = convert_arg_line_to_args - parser.add_argument("-s", "--speed", type=int, default=115200, dest="speed", help="Interface speed", choices=[4800, 9600, 19200, 38400, 57600, 115200]) + parser.add_argument("-s", "--speed", type=int, default=-1, dest="speed", help="Interface speed", choices=[4800, 9600, 19200, 38400, 57600, 115200]) group.add_argument("-i", "--init_command", metavar="", dest="init_command", help="A single initialization command such as 'PMTK101'") group.add_argument("-f", "--command_file", metavar="", dest="command_file", help="A file containing commands used to initialize the GPS") group.add_argument("-n", "--no-init", dest="no_init", default=False, action="store_true", help="Skip the initialization, just run the commands") + parser.add_argument("-k", "--keep-new-speed", dest="keep_new_speed", default=True, action="store_true", help="Don't restore the old speed on exit") parser.add_argument("output_device", metavar="", help="GPS serial device such as '/dev/ttyUSB0'") args = parser.parse_args() - p = subprocess.Popen("lsof -FpcL %s 2>/dev/null" % args.output_device, - stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True, shell=True) - o = p.communicate()[0] - if len(o) > 0: - lines = o.split('\n') - print("ERROR: Device %s is open by PID: %s PROG: %s USER: %s" % (args.output_device, lines[0][1:], lines[1][1:], lines[2][1:])) - return 1; + try: + tfg = os.open(args.output_device, os.O_RDWR) + params = termios.tcgetattr(tfg); + port_baudrate = baudrate[params[5]]; + tty.setraw(tfg, tty.TCSANOW) + except: + print(sys.exc_info()[1]) + return 1 + finally: + os.close(tfg) + + output_device = args.output_device + keep_new_speed = args.keep_new_speed + if args.speed < 0: + args.speed = port_baudrate + + speed = args.speed if args.no_init: fg = serial.Serial(args.output_device, timeout=5, baudrate=args.speed) else: - fg = get_known_state(args.output_device, args.speed, None, False) + fg = get_known_state(args.output_device, args.speed, port_baudrate, args.keep_new_speed) if fg is None: return 1 @@ -324,19 +364,21 @@ def main(): continue if line[0] == "#": continue - line = line.replace("${DEVICE}", args.output_device).replace("${SPEED}", str(args.speed)) + line = line.replace("${DEVICE}", output_device).replace("${SPEED}", str(speed)) print(line) - (rc, fg) = process_line(fg, args.output_device, args.speed, line) + (rc, fg) = process_line(fg, line) + if rc != 0: + break fi.close() print("Done") elif args.init_command: - (rc, fg) = process_line(fg, args.output_device, args.speed, args.init_command) + (rc, fg) = process_line(fg, args.init_command) else: - (rc, fg) = process_line(fg, args.output_device, args.speed, DEFAULT_COMMAND) + (rc, fg) = process_line(fg, DEFAULT_COMMAND) + + cleanup(fi, fg, args.output_device, port_baudrate, args.keep_new_speed) - tty.setraw(fg, tty.TCSANOW) - fg.close() return rc if __name__ == "__main__": diff --git a/gpsinit_epoload.conf b/gpsinit_epoload.conf index 1647cd2..65dd5dd 100644 --- a/gpsinit_epoload.conf +++ b/gpsinit_epoload.conf @@ -7,6 +7,7 @@ # sleep Sleep for n.nnn seconds # set_system_clock Gets the date/time from the GPS unit and sets the system clock # setspeed Set new port and unit speed to +# clear_epo Clear the existing EPO from the device # eporetrieve Run eporetrieve # epoloader Run epoloader # factory_reset Clear system/user configurations then cold start @@ -20,7 +21,7 @@ # # Clear the EPO NVRAM -PMTK127 +clear_epo eporetrieve EPO.DAT /tmp/EPO.DAT # Load the EPO. epoloader -n -s ${SPEED} @/etc/gpsinit_loc.conf /tmp/EPO.DAT ${DEVICE} diff --git a/gpsinit_reset.conf b/gpsinit_reset.conf index 12f1d3b..4eb14c0 100644 --- a/gpsinit_reset.conf +++ b/gpsinit_reset.conf @@ -7,6 +7,7 @@ # sleep Sleep for n.nnn seconds # set_system_clock Gets the date/time from the GPS unit and sets the system clock # setspeed Set new port and unit speed to +# clear_epo Clear the existing EPO from the device # eporetrieve Run eporetrieve # epoloader Run epoloader # factory_reset Clear system/user configurations then cold start @@ -20,7 +21,7 @@ # # Clear the EPO NVRAM -PMTK127 +clear_epo # Factory reset factory_reset # Now set to 115200 @@ -44,9 +45,9 @@ PMTK869,1,1 # Set output rate to 1 second PMTK220,1000 # Retrieve the EPO -eporetrieve MTK14.EPO /tmp/MTK14.EPO +eporetrieve EPO.DAT /tmp/EPO.DAT # Load the EPO. -epoloader -n -c -s ${SPEED} @/etc/gpsinit_loc.conf /tmp/MTK14.EPO ${DEVICE} +epoloader -n -c -s ${SPEED} @/etc/gpsinit_loc.conf /tmp/EPO.DAT ${DEVICE} # # You shouldn't put anything after the epoloader. # It may disturb the unit's ability to get a first fix. diff --git a/gpsinit_time.conf b/gpsinit_time.conf index 52a2e8b..99a2b29 100644 --- a/gpsinit_time.conf +++ b/gpsinit_time.conf @@ -4,12 +4,14 @@ # # The following commands are recognized... # -# sleep Sleep for n.nnn seconds +# sleep Sleep for n.nnn seconds # set_system_clock Gets the date/time from the GPS unit and sets the system clock -# setspeed Set new port and unit speed to -# epoloader Run epoloader -# factory_reset Clear system/user configurations then cold start -# The unit and port speed will be reset to 9600 +# setspeed Set new port and unit speed to +# clear_epo Clear the existing EPO from the device +# eporetrieve Run eporetrieve +# epoloader Run epoloader +# factory_reset Clear system/user configurations then cold start +# The unit and port speed will be reset to 9600 # The following commands can be prefixed with '-' to not wait for an ACK. # hot_start Restart using all available data diff --git a/gpsstatus b/gpsstatus index a3668eb..7ec4288 100755 --- a/gpsstatus +++ b/gpsstatus @@ -22,6 +22,10 @@ import serial from serial import threaded import curses.panel as panel +PREAMBLE = 0x2404 +EPO_CMD = 0x02d2 +EOW = 0x0a0d +UART_CMD = 253 SECONDS_PER_HOUR = 3600 GPS_OFFSET_SECONDS = 315964786 @@ -63,6 +67,8 @@ baudrate = { termios.B115200: 115200, } +baudrates = [4800, 9600, 19200, 38400, 57600, 115200 ] + def Convert2UTC(GPSHour): GPSHour *= SECONDS_PER_HOUR GPSHour += GPS_OFFSET_SECONDS @@ -90,10 +96,148 @@ def convert_arg_line_to_args(arg_line): continue yield arg -def send_string(fg, string): - buf = "$%s*%02x\r\n" % (string, crc8(string)) - fg.write(buf.encode("ASCII")) - fg.flush() +def send_string(fg, string, flush=True): + if isinstance(string, (bytearray)): + ba = string + elif isinstance(string, (bytes)): + ba = bytearray(string) + else: + ba = bytearray(string.encode("ASCII")) + + buf = b"$%s*%02x\r\n" % (ba, crc8(ba)) + fg.write(buf) + if flush: + fg.flush() + +def send_and_wait(fg, string, timeout, r="PMTK001,0,3", retries=0, debug=False): + spl = None + count = 0 + temp_timeout = fg.timeout + fg.timeout = timeout + while spl == None and count <= retries: + resp = None + if debug: + print(">> %s" % string) + send_string(fg, string, False); + fg.reset_input_buffer() + fg.flush() + resp = fg.read_until() + try: + sresp = resp.decode("ASCII") + if debug: + print("<< %s" % sresp) + ix = sresp.find(r) + if ix >= 0: + spl = sresp.strip()[ix:-3].split(',') + break + except: + pass + count = count + 1 + time.sleep(0.500) + + + fg.timeout = temp_timeout + return spl + + +def ping_unit(fg): + resp = send_and_wait(fg, "PMTK000", 0.250, retries=1) + if resp: + return True + return False + +def set_nmea(fg, rate, quiet=False): + if not quiet: print("Setting NMEA at %d baud." % rate) + current_rate = fg.baudrate + fg.baudrate = rate + if ping_unit(fg): + print(" Unit is currently in NMEA mode at %d baud." % rate); + return True + if not quiet: print(" No response. Seeing if it's in binary mode.") + buf = bytearray(b'\0' * 14) + struct.pack_into("