Ejemplo n.º 1
0
JNIEXPORT void JNICALL
Java_org_xcsoar_InternalGPS_setConnected(JNIEnv *env, jobject obj,
                                         jint connected)
{
  jfieldID fid_index = env->GetFieldID(env->GetObjectClass(obj),
                                       "index", "I");
  unsigned index = env->GetIntField(obj, fid_index);

  mutexBlackboard.Lock();
  NMEA_INFO &basic = device_blackboard.SetRealState(index);

  switch (connected) {
  case 0: /* not connected */
    basic.Connected.Clear();
    basic.LocationAvailable.Clear();
    break;

  case 1: /* waiting for fix */
    basic.Connected.Update(fixed(MonotonicClockMS()) / 1000);
    basic.gps.AndroidInternalGPS = true;
    basic.LocationAvailable.Clear();
    break;

  case 2: /* connected */
    basic.Connected.Update(fixed(MonotonicClockMS()) / 1000);
    basic.gps.AndroidInternalGPS = true;
    break;
  }

  device_blackboard.Merge();
  mutexBlackboard.Unlock();
}
Ejemplo n.º 2
0
gcc_visibility_default
JNIEXPORT void JNICALL
Java_org_xcsoar_InternalGPS_setConnected(JNIEnv *env, jobject obj,
                                         jint connected)
{
  unsigned index = getDeviceIndex(env, obj);

  ScopeLock protect(device_blackboard->mutex);
  NMEAInfo &basic = device_blackboard->SetRealState(index);

  switch (connected) {
  case 0: /* not connected */
    basic.alive.Clear();
    basic.location_available.Clear();
    break;

  case 1: /* waiting for fix */
    basic.alive.Update(MonotonicClockMS() / 1000.);
    basic.gps.nonexpiring_internal_gps = true;
    basic.location_available.Clear();
    break;

  case 2: /* connected */
    basic.alive.Update(MonotonicClockMS() / 1000.);
    basic.gps.nonexpiring_internal_gps = true;
    break;
  }

  device_blackboard->ScheduleMerge();
}
Ejemplo n.º 3
0
void StartupStore(const TCHAR *Str, ...)
{
  TCHAR buf[(MAX_PATH*2)+1]; // 260 chars normally  FIX 100205
  va_list ap;

  va_start(ap, Str);
  _vsntprintf(buf, countof(buf), Str, ap);
  va_end(ap);

  LockStartupStore();

  FILE *startupStoreFile = NULL;
  static TCHAR szFileName[MAX_PATH];
  static bool initialised = false;
  if (!initialised) {
	LocalPath(szFileName, TEXT(LKF_RUNLOG));
	initialised = true;
  } 

  startupStoreFile = _tfopen(szFileName, TEXT("ab+"));
  if (startupStoreFile != NULL) {
    char sbuf[(MAX_PATH*2)+1]; // FIX 100205
    
    int i = TCHAR2utf(buf, sbuf, sizeof(sbuf));
    
    if (i > 0) {
      if (sbuf[i - 1] == 0x0a && (i == 1 || (i > 1 && sbuf[i-2] != 0x0d)))
        sprintf(sbuf + i - 1, SNEWLINE);
      fprintf(startupStoreFile, "[%09u] %s", MonotonicClockMS(), sbuf);
    }
    fclose(startupStoreFile);
  }
  UnlockStartupStore();
}
Ejemplo n.º 4
0
/**
 * Sets the location, altitude and other basic parameters
 *
 * Used by the IgcReplay
 * @param loc New location
 * @param speed New speed
 * @param bearing New bearing
 * @param alt New altitude
 * @param baroalt New barometric altitude
 * @param t New time
 * @see IgcReplay::UpdateInternal()
 */
void
DeviceBlackboard::SetLocation(const GeoPoint &loc,
                              const fixed speed, const Angle bearing,
                              const fixed alt, const fixed baroalt,
                              const fixed t)
{
  ScopeLock protect(mutexBlackboard);
  NMEA_INFO &basic = SetReplayState();

  basic.Connected.Update(fixed(MonotonicClockMS()) / 1000);
  basic.gps.SatellitesUsed = 6;
  basic.acceleration.Available = false;
  basic.Location = loc;
  basic.LocationAvailable.Update(t);
  basic.GroundSpeed = speed;
  basic.GroundSpeedAvailable.Update(t);
  basic.AirspeedAvailable.Clear(); // Clear airspeed as it is not given by any value.
  basic.track = bearing;
  basic.track_available.Update(t);
  basic.GPSAltitude = alt;
  basic.GPSAltitudeAvailable.Update(t);
  basic.ProvidePressureAltitude(baroalt);
  basic.ProvideBaroAltitudeTrue(baroalt);
  basic.Time = t;
  basic.TotalEnergyVarioAvailable.Clear();
  basic.NettoVarioAvailable.Clear();
  basic.ExternalWindAvailable.Clear();
  basic.gps.real = false;
  basic.gps.Replay = true;
  basic.gps.Simulator = false;

  Merge();
};
const IMI::TMsg *
IMI::Receive(Port &port, unsigned extraTimeout, unsigned expectedPayloadSize)
{
  if (expectedPayloadSize > COMM_MAX_PAYLOAD_SIZE)
    expectedPayloadSize = COMM_MAX_PAYLOAD_SIZE;

  // set timeout
  unsigned baudrate = port.GetBaudrate();
  if (baudrate == 0)
    return NULL;

  unsigned timeout = extraTimeout + 10000 * (expectedPayloadSize
      + sizeof(IMICOMM_MSG_HEADER_SIZE) + 10) / baudrate;
  if (!port.SetRxTimeout(timeout))
    return NULL;

  // wait for the message
  const TMsg *msg = NULL;
  timeout += MonotonicClockMS();
  while (MonotonicClockMS() < timeout) {
    // read message
    IMIBYTE buffer[64];
    int bytesRead = port.Read(buffer, sizeof(buffer));
    if (bytesRead == 0)
      continue;
    if (bytesRead == -1)
      break;

    // parse message
    const TMsg *lastMsg = MessageParser::Parse(buffer, bytesRead);
    if (lastMsg) {
      // message received
      if (lastMsg->msgID == MSG_ACK_NOTCONFIG)
        Disconnect(port);
      else if (lastMsg->msgID != MSG_CFG_KEEPCONFIG)
        msg = lastMsg;

      break;
    }
  }

  // restore timeout
  if (!port.SetRxTimeout(0))
    return NULL;

  return msg;
}
Ejemplo n.º 6
0
void
SelfTimingKalmanFilter1d::Update(const fixed z_abs, const fixed var_z_abs)
{
  const unsigned int t_ms = MonotonicClockMS();
  const unsigned int dt_ms = t_ms - t_last_update_ms_;
  t_last_update_ms_ = t_ms;

  if (dt_ms > max_dt_ms_)
    filter_.Reset();
  filter_.Update(z_abs, var_z_abs, fixed(dt_ms) / 1000);
}
Ejemplo n.º 7
0
void
NmeaReplayGlue::on_sentence(const char *line)
{
  assert(device != NULL);

  ScopeLock protect(device_blackboard->mutex);
  NMEAInfo &data = device_blackboard->SetReplayState();

  if ((device != NULL && device->ParseNMEA(line, data)) ||
      (parser != NULL && parser->ParseLine(line, data))) {
    data.gps.replay = true;
    data.connected.Update(fixed(MonotonicClockMS()) / 1000);
    device_blackboard->ScheduleMerge();
  }
}
Ejemplo n.º 8
0
bool
DumpPort::CheckEnabled()
{
  if (until_ms == 0)
    return false;

  if (until_ms == unsigned(-1))
    return true;

  if (MonotonicClockMS() >= until_ms) {
    /* duration has just expired; clear to avoid calling
       MonotonicClockMS() again in the next call */
    until_ms = 0;
    return false;
  }

  return true;
}
Ejemplo n.º 9
0
bool
DeviceDescriptor::ParseNMEA(const char *line, NMEAInfo &info)
{
  assert(line != NULL);

  /* restore the driver's ExternalSettings */
  const ExternalSettings old_settings = info.settings;
  info.settings = settings_received;

  if (device != NULL && device->ParseNMEA(line, info)) {
    info.connected.Update(info.clock);

    if (!config.sync_from_device)
      info.settings = old_settings;

    /* clear the settings when the values are the same that we already
       sent to the device */
    const ExternalSettings old_received = settings_received;
    settings_received = info.settings;
    info.settings.EliminateRedundant(settings_sent, old_received);

    return true;
  }

  /* no change - restore the ExternalSettings that we returned last
     time */
  info.settings = old_settings;

  // Additional "if" to find GPS strings
  if (parser.ParseLine(line, info)) {
    info.connected.Update(fixed(MonotonicClockMS()) / 1000);
    return true;
  }

  return false;
}
Ejemplo n.º 10
0
 virtual void DataReceived(const void *data, size_t length) {
   char prefix[16];
   sprintf(prefix, "%12u ", MonotonicClockMS());
   HexDump(prefix, data, length);
 }
Ejemplo n.º 11
0
void
NMEAInfo::UpdateClock()
{
  clock = MonotonicClockMS() / 1000.;
}
Ejemplo n.º 12
0
void
DumpPort::EnableTemporarily(unsigned duration_ms)
{
  until_ms = MonotonicClockMS() + duration_ms;
}
Ejemplo n.º 13
0
 static Stamp GetNow() {
   return MonotonicClockMS();
 }
Ejemplo n.º 14
0
 static Stamp get_now() {
   return MonotonicClockMS();
 }
Ejemplo n.º 15
0
// Leonardo Live Tracker (www.livetrack24.com) data exchange thread
static void LiveTrackerThread()
{
  int tracker_fsm = 0;
  livetracker_point_t sendpoint = {0};
  bool sendpoint_valid = false;
  bool sendpoint_processed = false;
  bool sendpoint_processed_old = false;
  // Session variables
  unsigned int packet_id = 0;
  unsigned int session_id = 0;               
  int userid = -1;
  
  _t_end = false;
  _t_run = true;

  srand(MonotonicClockMS());

  do {
    if (NewDataEvent.tryWait(5000)) NewDataEvent.reset();
    if (!_t_run) break;
    do {
      if (1) {
        sendpoint_valid = false;
        ScopeLock guard(_t_mutex);
        if (!_t_points.empty()) {
          sendpoint = _t_points.front();
          sendpoint_valid = true;
        }
      } //mutex
        if (sendpoint_valid) {
          sendpoint_processed = false;
          do {
            switch (tracker_fsm) {
              default:
              case 0:   // Wait for flying
                if (!sendpoint.flying) {
                  sendpoint_processed = true;
                  break;
                }
                tracker_fsm++;
                break;
                  
              case 1:
                // Get User ID
                userid = GetUserIDFromServer();
                sendpoint_processed = false;
                if (userid>=0) tracker_fsm++;
                break;
                
              case 2:
                //Start of track packet
                sendpoint_processed = SendStartOfTrackPacket(&packet_id, &session_id, userid);
                if (sendpoint_processed) {
                  StartupStore(TEXT(". Livetracker new track started.%s"),NEWLINE);
                  sendpoint_processed_old = true;
                  tracker_fsm++;
                }
                break;

              case 3:
                //Gps point packet
                sendpoint_processed = SendGPSPointPacket(&packet_id, &session_id, &sendpoint);
                
                //Connection lost to server
                if (sendpoint_processed_old && !sendpoint_processed) {
                  StartupStore(TEXT(". Livetracker connection to server lost.%s"), NEWLINE);
                }
                //Connection established to server
                if (!sendpoint_processed_old && sendpoint_processed) {
                  ScopeLock guard(_t_mutex);
                  int queue_size = _t_points.size();
                  StartupStore(TEXT(". Livetracker connection to server established, start sending %d queued packets.%s"), queue_size, NEWLINE);
                }
                sendpoint_processed_old = sendpoint_processed;
                
                if (!sendpoint.flying) {
                  tracker_fsm++;
                }
                break;

              case 4:
                //End of track packet
                sendpoint_processed = SendEndOfTrackPacket(&packet_id, &session_id);
                if (sendpoint_processed) {
                  StartupStore(TEXT(". Livetracker track finished, sent %d points.%s"), packet_id, NEWLINE);
                  tracker_fsm=0;
                }
                break;
            }// sw
            
            if (sendpoint_processed) {
              ScopeLock guard(_t_mutex);
              _t_points.pop_front();
            } else InterruptibleSleep(2500);
            sendpoint_processed_old = sendpoint_processed;
          } while (!sendpoint_processed && _t_run);
      }
    } while (sendpoint_valid && _t_run);
  } while (_t_run);
  
  _t_end = true;
}
Ejemplo n.º 16
0
void
NMEAInfo::UpdateClock()
{
  clock = fixed(MonotonicClockMS()) / 1000;
}
Ejemplo n.º 17
0
JNIEXPORT void JNICALL
Java_org_xcsoar_InternalGPS_setLocation(JNIEnv *env, jobject obj,
                                        jlong time, jint n_satellites,
                                        jdouble longitude, jdouble latitude,
                                        jboolean hasAltitude, jdouble altitude,
                                        jboolean hasBearing, jdouble bearing,
                                        jboolean hasSpeed, jdouble speed,
                                        jboolean hasAccuracy, jdouble accuracy,
                                        jboolean hasAcceleration, jdouble acceleration)
{
  jfieldID fid_index = env->GetFieldID(env->GetObjectClass(obj),
                                       "index", "I");
  unsigned index = env->GetIntField(obj, fid_index);

  mutexBlackboard.Lock();

  NMEA_INFO &basic = device_blackboard.SetRealState(index);
  basic.Connected.Update(fixed(MonotonicClockMS()) / 1000);

  BrokenDateTime date_time = BrokenDateTime::FromUnixTimeUTC(time / 1000);
  fixed second_of_day = fixed(date_time.GetSecondOfDay()) +
    /* add the millisecond fraction of the original timestamp for
       better accuracy */
    fixed((unsigned)(time % 1000)) / 1000u;

  if (second_of_day < basic.Time && date_time > basic.DateTime)
    /* don't wrap around when going past midnight in UTC */
    second_of_day += fixed(24u * 3600u);

  basic.Time = second_of_day;
  basic.DateTime = date_time;

  basic.gps.SatellitesUsed = n_satellites;
  basic.gps.real = true;
  basic.gps.AndroidInternalGPS = true;
  basic.Location = GeoPoint(Angle::degrees(fixed(longitude)),
                            Angle::degrees(fixed(latitude)));
  if (n_satellites > 0)
    basic.LocationAvailable.Update(basic.Time);
  else
    basic.LocationAvailable.Clear();

  if (hasAltitude) {
    fixed GeoidSeparation = LookupGeoidSeparation(basic.Location);
    basic.GPSAltitude = fixed(altitude) - GeoidSeparation;
    basic.GPSAltitudeAvailable.Update(basic.Time);
  } else
    basic.GPSAltitudeAvailable.Clear();

  if (hasBearing) {
    basic.track = Angle::degrees(fixed(bearing));
    basic.track_available.Update(basic.Time);
  } else
    basic.track_available.Clear();

  if (hasSpeed) {
    basic.GroundSpeed = fixed(speed);
    basic.GroundSpeedAvailable.Update(basic.Time);
  }

  if (hasAccuracy)
    basic.gps.HDOP = fixed(accuracy);

  if (hasAcceleration) {
    // TODO: use ACCELERATION_STATE::complement() ?!?
    basic.acceleration.Available = true;
    basic.acceleration.Gload = fixed(acceleration);
  }

  device_blackboard.Merge();
  mutexBlackboard.Unlock();
}
Ejemplo n.º 18
0
//
// LK8000 SS1 = Soaring Simulator V1 by Paolo Ventafridda
// Still basic, but usable
//
void LKSimulator(void) {

  LockFlightData();

  //
  GPS_INFO.NAVWarning = false;
  GPS_INFO.SatellitesUsed = 6;
  // Even on ground, we can turn the glider in the hangar
  BEARING += SimTurn;
  if (BEARING<0) BEARING+=360;
  else if (BEARING>359) BEARING-=360;

  #if SIMLANDING
  static bool crashed=false, landedwarn=true;
  #endif
  static bool doinit=true, landing=false, stallwarn=true, circling=false, flarmwasinit=false;
  static short counter=0;

  double tdistance, tbearing;
  double thermalstrength=0, sinkstrength=0;

  extern void SimFlarmTraffic(long id, double offset);

  if (doinit) {
	if (counter++<4) {
		UnlockFlightData();
		return;
	}
	#if TESTBENCH
	StartupStore(_T(". SIMULATOR: real init%s"),NEWLINE);
	#endif

	// Add a couple of thermals for the boys
	InsertThermalHistory(GPS_INFO.Time-1887, GPS_INFO.Latitude-0.21, GPS_INFO.Longitude+0.13, 873, 1478,1.5);
	InsertThermalHistory(GPS_INFO.Time-1250, GPS_INFO.Latitude+0.15, GPS_INFO.Longitude-0.19, 991, 1622,0.9);
	InsertThermalHistory(GPS_INFO.Time-987, GPS_INFO.Latitude-0.11, GPS_INFO.Longitude+0.13, 762, 1367,1.8);
	InsertThermalHistory(GPS_INFO.Time-100, GPS_INFO.Latitude-0.02, GPS_INFO.Longitude-0.03, 650, 1542,2.2);
	WayPointList[RESWP_LASTTHERMAL].Latitude  = GPS_INFO.Latitude-0.022;
	WayPointList[RESWP_LASTTHERMAL].Longitude = GPS_INFO.Longitude-0.033;
	WayPointList[RESWP_LASTTHERMAL].Altitude  = 650;
	ThLatitude=GPS_INFO.Latitude-0.022;
	ThLongitude=GPS_INFO.Longitude-0.033;

	if (EnableFLARMMap && !LiveTrackerRadar_config ) {
		srand(MonotonicClockMS());
		SimFlarmTraffic(0xdd8951,22.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8944,31.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a43,16.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a42,41.0+(double)(rand() % 32));
/*
		SimFlarmTraffic(0xdd8a11,11.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a12,21.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a13,31.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a14,41.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a15,51.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a16,61.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a17,71.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a18,81.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a19,91.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a10,01.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a21,21.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a22,22.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a23,23.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a24,24.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a25,25.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a26,26.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a27,27.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a28,28.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a29,29.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a20,31.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a31,32.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a32,33.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a33,34.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a34,35.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a35,36.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a36,37.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a37,41.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a38,42.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a39,43.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a30,44.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a41,45.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a42,46.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a43,47.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a44,48.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a45,49.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a46,01.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a47,02.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a48,03.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a49,04.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a50,81.1+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a51,82.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a52,83.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a53,84.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a54,85.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a55,86.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a56,87.0+(double)(rand() % 32));
*/
	}
	doinit=false;
  }

  // First Aircraft min altitude is at ground level
  if (ALTITUDE==0)
	if (CALCULATED_INFO.TerrainValid) ALTITUDE= CALCULATED_INFO.TerrainAlt;


  if (ISGAAIRCRAFT) {
	// todo: fuel consumption, engine efficiency etc.
  }

  // We cannot use doinit for flarm, because it could be enabled from configuration AFTER startup,
  // and it must work all the way the same in order not to confuse users.
  if (EnableFLARMMap && !LiveTrackerRadar_config ) {
	if (!flarmwasinit) {
		srand(MonotonicClockMS());
		// Add a poker of traffic for the boys
		SimFlarmTraffic(0xdd8951,22.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8944,31.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a43,16.0+(double)(rand() % 32));
		SimFlarmTraffic(0xdd8a42,41.0+(double)(rand() % 32));
		DoStatusMessage(MsgToken(279)); // FLARM DETECTED (in sim)
		flarmwasinit=true;
	} else {
		// Let one of the objects be a ghost and a zombie, and keep the rest real
		SimFlarmTraffic(0xdd8951,0);
		SimFlarmTraffic(0xdd8944,0);
		SimFlarmTraffic(0xdd8a43,0);

		// update relative altitude for ghost/zombie traffic
		extern int FLARM_FindSlot(NMEA_INFO *GPS_INFO, long Id);
		int flarmslot=FLARM_FindSlot(&GPS_INFO, 0xdd8a42);
		if (flarmslot>=0)
			GPS_INFO.FLARM_Traffic[flarmslot].RelativeAltitude = GPS_INFO.FLARM_Traffic[flarmslot].Altitude - GPS_INFO.Altitude;

	}
  }

  if (ISPARAGLIDER || ISGLIDER) {

    // SetBallast is calculating sinkratecache for values starting from 4 to MAXSPEED, in m/s .
    // ONLY during flight, we will sink in the air
    if (FLYING && (IASMS>3) && (IASMS<MAXSPEED) ) {

	double sinkias=-1*AirDensitySinkRate(IASMS, GPS_INFO.Altitude);
	if (sinkias>10) sinkias=10; // set a limiter for sink rate
	// StartupStore(_T(".... ias=%.1f sinkias=%.3f oldAlt=%.3f newAlt=%.3f\n"),
	// CALCULATED_INFO.IndicatedAirspeedEstimated*TOKPH, sinkias, GPS_INFO.Altitude, GPS_INFO.Altitude-sinkias);
	double simlift=0;
	if (THERMALLING == TRUE) {
		// entering the thermal mode right now
		if (!circling) {
			circling=true;

			DistanceBearing(GPS_INFO.Latitude,GPS_INFO.Longitude,ThLatitude,ThLongitude,&tdistance,&tbearing);
			if (tdistance>1000) {
				// a new thermal
				ThLatitude=GPS_INFO.Latitude; // we mark the new thermal
				ThLongitude=GPS_INFO.Longitude;
				ALTITUDE+=simlift; // sink rate adjusted later
			} else {
				// start circling near the old thermal
			}
		} else {
			// already thermalling
		}
		// ALTITUDE+=simlift+GlidePolar::minsink;
	} else {
        sinkias -= SimNettoVario;
		if (circling) {
			// we were circling, now leaving the thermal
			circling=false;
		} else {
			// not circling, already cruising
		}
	}

	// Are we near the thermal?
	DistanceBearing(GPS_INFO.Latitude,GPS_INFO.Longitude,ThLatitude,ThLongitude,&tdistance,&tbearing);
	thermalstrength=4; // m/s
	ThermalRadius=200; // we assume a perfect thermal, a circle of this diameter. Stronger in the center.
	// thermalbase
	sinkstrength=2; //  how intense is the fallout of the thermal
	SinkRadius=150; //  circular ring of the fallout

	if (tdistance>=ThermalRadius && tdistance<(ThermalRadius+SinkRadius) ) {
		// we are in the sinking zone of the thermal..
		simlift= sinkstrength- ((tdistance-ThermalRadius)/SinkRadius)*sinkstrength;
		simlift+=0.1; // adjust rounding errors
		simlift*=-1;
		//StartupStore(_T(".. sinking zone:  dist=%.1f  sink=%.1f\n"), tdistance,simlift);
	}
	if (tdistance<ThermalRadius) {
		// we are in the lift zone
		simlift= thermalstrength- (tdistance/ThermalRadius)*thermalstrength;
		simlift+=0.1; // adjust rounding errors
		//StartupStore(_T(".. climbing zone:  dist=%.1f  climb=%.1f\n"), tdistance,simlift);
	}
	// Update altitude with the lift or sink,
	ALTITUDE+=simlift;
	// Update the new altitude with the natural sink, but not going lower than 0
	ALTITUDE-=sinkias;
	if (ALTITUDE<=0) ALTITUDE=0;

	#if SIMLANDING
	if (CALCULATED_INFO.TerrainValid && (CALCULATED_INFO.AltitudeAGL <=20) ) {
		if (IAS <= (MINSPEED+3)) landing=true;
		else {
			// we dont simulate crashing. LK8000 pilots never crash.
			crashed=true;
		}
	}
	if (CALCULATED_INFO.TerrainValid && (CALCULATED_INFO.AltitudeAGL >100) ) {
		landing=false;
	}

	if (!landing && CALCULATED_INFO.TerrainValid && (CALCULATED_INFO.AltitudeAGL <=0) ) {
		GPS_INFO.Speed=0;
		landing=true;
		if (landedwarn) {
			DoStatusMessage(_T("YOU HAVE LANDED"));
			landedwarn=false;
		}
	} else landedwarn=true;
	#endif

    }
  } // Glider/Paragliders

  if (FLYING) {
	// simple stall at 1 G
	if (!landing && (IAS<=STALLSPEED && IASMS>3)) {
		if (stallwarn) {
			// DoStatusMessage(_T("STALLING")); // OK, people do not like stalling.
			stallwarn=false;
		}
		#if 0 // DO NOT SIMULATE STALLING NOW
		// GPS_INFO.Speed= (GlidePolar::Vminsink*0.85)+1;
		ALTITUDE-=20;
		if (ALTITUDE<=0) ALTITUDE=0;
		#endif
	} else stallwarn=true;
	#if SIMLANDING
	if (landing || IASMS<4) {
		GPS_INFO.Speed-=GPS_INFO.Speed*0.2;
	}
	if (crashed) {
		GPS_INFO.Speed=0;
	}
	#endif

	if (GS<0) {
		GPS_INFO.Speed=0;
	}
  }


  if (GS>0) {
      FindLatitudeLongitude(GPS_INFO.Latitude, GPS_INFO.Longitude,
                          GPS_INFO.TrackBearing, GPS_INFO.Speed,
                          &GPS_INFO.Latitude,
                          &GPS_INFO.Longitude);
  }
  GPS_INFO.Time+= 1.0;
  long tsec = (long)GPS_INFO.Time;
  GPS_INFO.Hour = tsec/3600;
  GPS_INFO.Minute = (tsec-GPS_INFO.Hour*3600)/60;
  GPS_INFO.Second = (tsec-GPS_INFO.Hour*3600-GPS_INFO.Minute*60);

  UnlockFlightData();
}