public void run()
        {
            while (!_abort) {
                //Start stopwatch
                lastCycle = DateTime.UtcNow.Ticks;
                Console.Write("Updating..." + counter++ + "\n");
                info.timestamp = Global.unixTime(DateTime.Now);

                /////////////////////////////////////////////////
                ///
                ///Update bus info
                ///
                /////////////////////////////////////////////////

                //Send request to StreetEagle
                HttpWebRequest req = (HttpWebRequest) WebRequest.Create(new Uri(GPS_URL));
                req.Method = "POST";
                req.ContentType = "text/xml; encoding=UTF-8";
                req.Accept = "*/*";
                req.Headers.Add(SOAP_PROPERTY,SOAP_VALUE);

                using(StreamWriter sw = new StreamWriter(req.GetRequestStream())) {
                    sw.Write(String.Format(REQUEST_XML, idNumbers));
                    sw.Close();
                }

                //Get response
                HttpWebResponse resp;
                try {
                    resp = (HttpWebResponse) req.GetResponse();
                } catch(WebException w) {
                    Console.WriteLine ("Error getting StreetEagle data!");
                    Console.WriteLine(w.ToString());
                    resp = (HttpWebResponse) w.Response;
                    //continue;

                }

                //Load XML
                XmlDocument doc = new XmlDocument();
                string xmlFromOutside = (new StreamReader(resp.GetResponseStream())).ReadToEnd();
                doc.LoadXml(xmlFromOutside);
                XPathNavigator nav = doc.CreateNavigator();

                //Find all buses
                XPathExpression busXpr = nav.Compile("//*[local-name()='List_Bus']");
                XPathNodeIterator busItr = (XPathNodeIterator)nav.Evaluate(busXpr);

                //For each bus
                foreach (XPathNavigator n in busItr) {
                    //Get id number
                    int id = int.Parse(Global.getIntSegment(n.SelectSingleNode("./*[local-name()='VehicleID']").Value));
                    Bus bus = info.busInfo.Buses[id - Bus.MIN_BUS_ID];

                    //Get last update time
                    long time = Global.unixTime(DateTime.Parse(n.SelectSingleNode("./*[local-name()='DateTimeLoc']").Value));
                    //Skip this bus if it hasn't changed (this will happen a LOT)
                    if (bus.lastUpdated == time) continue;
                    else bus.lastUpdated = time;

                    //Check if running - can ignore the rest if not
                    bus.running = !n.SelectSingleNode("./*[local-name()='Status']").Value.Equals("Parked");
                    if (!bus.running) {
                        bus.lastLatitude = 0;
                        bus.lastLongitude = 0;
                        bus.lastStop = 0;
                        bus.nextStop = 0;
                        continue;
                    }

                    //Update location
                    bus.lastLatitude = bus.latitude;
                    bus.lastLongitude = bus.longitude;
                    bus.latitude = double.Parse(n.SelectSingleNode("./*[local-name()='Latitude']").Value);
                    bus.longitude = double.Parse(n.SelectSingleNode("./*[local-name()='Longitude']").Value);
                    bus.heading = int.Parse(n.SelectSingleNode("./*[local-name()='Heading']").Value);
                    bus.address = n.SelectSingleNode("./*[local-name()='Address']").Value;
                    bus.name = n.SelectSingleNode("./*[local-name()='Name']").Value;
                    if (bus.lastStop != 0)
                        bus.timeSinceLastStop = time - bus.stopCheckInTimes[bus.lastStop];

                    //Update stop
                    if (bus.lastLatitude != 0) {
                        //If this is not the first update for this bus, look for a stop
                        int stopAt = info.stopInfo.getStop(bus);
                        if (stopAt != 0 && stopAt != bus.lastStop) {
                            //Got to a stop
                            bus.lastStop = stopAt;

                            //Notify travel times
                            if (bus.route == 1) {
                                foreach (int stopId in Stop.BLUE_ROUTE) {
                                    if (bus.stopCheckInTimes[stopId] != 0) //0 means haven't been there yet
                                        info.stopInfo.stops[bus.lastStop].logTime(
                                            stopId, time - bus.stopCheckInTimes[stopId]);

                                }
                            }
                            else if (bus.route == 2) {
                                foreach (int stopId in Stop.RED_ROUTE) {
                                    if (bus.stopCheckInTimes[stopId] != 0) //0 means haven't been there yet
                                        info.stopInfo.stops[bus.lastStop].logTime(
                                            stopId, time - bus.stopCheckInTimes[stopId]);
                                }

                            }

                            //Here we are now, entertain us
                            bus.stopCheckInTimes[stopAt] = time;
                            bus.timeSinceLastStop = 0; //Time since last being at this stop is now 0

                        }//at stop
                    }

                    //Update route if this stop allows differentiation
                    bus.setRoute (Global.getRoute (bus.lastStop));

                    //Predict next stop
                    if (bus.lastStop != 0) {
                        int cachedNextStop = bus.nextStop;
                        bus.nextStop = Stop.getNextStop(bus.lastStop,bus.route);
                        if (bus.nextStop != cachedNextStop) {

                            if (cachedNextStop != 0 && bus.nextStop != 0) {
                                //Update what segments they're in
            //								info.stopInfo.stops[bus.nextStop].busesComing += 1;
            //								info.stopInfo.stops[cachedNextStop].busesComing -= 1;

                            }

                        }//next stop changed
                    }//predictions
                }//for each bus in XML

                //Find out how long until a bus arrives at each stop
                //Time to get there is the time from the last stop to this destination
                //	(as determined by averages)
                //	minus how long it's been since the bus left the last stop
                foreach (int stopId in info.stopInfo.stops.Keys) {
                    long bestTime = long.MaxValue;
                    foreach (Bus bus in info.busInfo.Buses) {
                        //Check that the stop and bus are on the same route
                        if (bus.lastStop == 0 || bus.route == 0) continue; //new bus
                        if (bus.route == 1 && Array.IndexOf(Stop.BLUE_ROUTE,stopId) == -1) continue; //wrong route
                        if (bus.route == 2 && Array.IndexOf(Stop.RED_ROUTE,stopId) == -1) continue; //wrong route
                        if (info.stopInfo.stops[stopId].travelTimes[bus.lastStop].Count == 0) continue; //no estimate

                        long timeToStop = (long)Global.average(info.stopInfo.stops[stopId].travelTimes[bus.lastStop])
                                            - bus.timeSinceLastStop;

                        if (timeToStop <= bestTime)
                            bestTime = timeToStop;
                    }

                    info.stopInfo.stops[stopId].timeToNextBus = bestTime;
                }

                //Write info to string - which is then shared, so that this only happens once per update
                using (StringWriter writer = new StringWriter()) {
                    serializer.Serialize(writer, info);
                    Global.xmlLock.EnterWriteLock();
                    try {
                        //Only one quick pointer-switch while the lock is held... oh yeah
                        sw.setStr(writer.ToString());
            //						sw.setStr(xmlFromOutside);
            //						sw.setStr(writer.ToString() + xmlFromOutside);
                    }finally { Global.xmlLock.ExitWriteLock(); }
                }

                //Wait for next update phase
                if (UPDATE_INTERVAL > 0)
                    while ((DateTime.UtcNow.Ticks - lastCycle) / 10000 < UPDATE_INTERVAL)
                        Thread.Sleep(500);
            }
        }