Esempio n. 1
0
BOOL NMEAParser::PFLAA(TCHAR *String, TCHAR **params, size_t nparams, NMEA_INFO *pGPS)
{
  int flarm_slot = 0;

  isFlarm = true;

  // 5 id, 6 digit hex
  long ID;
  swscanf(params[5],TEXT("%lx"), &ID);
//  unsigned long uID = ID;

  flarm_slot = FLARM_FindSlot(pGPS, ID);
  if (flarm_slot<0) {
    // no more slots available,
	#ifdef DEBUG_LKT
	StartupStore(_T("... NO SLOTS for Flarm traffic, too many ids!%s"),NEWLINE);
	#endif
	return FALSE;
  }

  // before changing timefix, see if it was an old target back locked in!
  CheckBackTarget(pGPS, flarm_slot);
  // and then set time of fix to current time
  pGPS->FLARM_Traffic[flarm_slot].Time_Fix = pGPS->Time;

  TCHAR nString[MAX_NMEA_LEN+1];
  unsigned int i, j;
  for (i=0, j=0; i<_tcslen(String); i++) {
	// if not a comma, copy and proceed
	if (String[i] != _T(',')) {
		nString[j++]=String[i];
		continue;
	}
	// there was a comma, but the next one is not a comma, so ok..
	if (String[i+1] != _T(',') ) {
		nString[j++]=String[i];
		continue;
	}
	// We have a bad ,, case that scanf cannot bear with, so we add a 0
	nString[j++] = String[i];
	nString[j++] = _T('0');
  }
  nString[j]=_T('\0');

  //#ifdef DEBUG_LKT
  //StartupStore(_T("PFLAA: %s%s"),nString,NEWLINE);
  //#endif

  _stscanf(nString,
	  TEXT("%hu,%lf,%lf,%lf,%hu,%lx,%lf,%lf,%lf,%lf,%hu"),
	  &pGPS->FLARM_Traffic[flarm_slot].AlarmLevel, // unsigned short 0
	  &pGPS->FLARM_Traffic[flarm_slot].RelativeNorth, //  1	
	  &pGPS->FLARM_Traffic[flarm_slot].RelativeEast, //   2
	  &pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude, //  3
	  &pGPS->FLARM_Traffic[flarm_slot].IDType, // unsigned short     4
	  &pGPS->FLARM_Traffic[flarm_slot].ID, // 6 char hex
	  &pGPS->FLARM_Traffic[flarm_slot].TrackBearing, // double       6
	  &pGPS->FLARM_Traffic[flarm_slot].TurnRate, // double           7
	  &pGPS->FLARM_Traffic[flarm_slot].Speed, // double              8 m/s
	  &pGPS->FLARM_Traffic[flarm_slot].ClimbRate, // double          9 m/s
	  &pGPS->FLARM_Traffic[flarm_slot].Type); // unsigned short     10
  // 1 relativenorth, meters  
  pGPS->FLARM_Traffic[flarm_slot].Latitude = 
    pGPS->FLARM_Traffic[flarm_slot].RelativeNorth *FLARM_NorthingToLatitude + pGPS->Latitude;
  // 2 relativeeast, meters
  pGPS->FLARM_Traffic[flarm_slot].Longitude = 
    pGPS->FLARM_Traffic[flarm_slot].RelativeEast *FLARM_EastingToLongitude + pGPS->Longitude;

  // we need to compare with BARO altitude FLARM relative Alt difference!
  if (pGPS->BaroAltitude>0) // just to be sure
	pGPS->FLARM_Traffic[flarm_slot].Altitude = pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude + pGPS->BaroAltitude;
  else
	pGPS->FLARM_Traffic[flarm_slot].Altitude = pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude + pGPS->Altitude;



  pGPS->FLARM_Traffic[flarm_slot].Average30s = flarmCalculations.Average30s(
	  pGPS->FLARM_Traffic[flarm_slot].ID,
	  pGPS->Time,
	  pGPS->FLARM_Traffic[flarm_slot].Altitude);

  TCHAR *name = pGPS->FLARM_Traffic[flarm_slot].Name;
  //TCHAR *cn = pGPS->FLARM_Traffic[flarm_slot].Cn;
  // If there is no name yet, or if we have a pending update event..
  if (!_tcslen(name) || pGPS->FLARM_Traffic[flarm_slot].UpdateNameFlag ) {

	#ifdef DEBUG_LKT
	if (pGPS->FLARM_Traffic[flarm_slot].UpdateNameFlag ) {
		StartupStore(_T("... UpdateNameFlag for slot %d\n"),flarm_slot);
	} else {
		StartupStore(_T("... First lookup name for slot %d\n"),flarm_slot);
	}
	#endif

	pGPS->FLARM_Traffic[flarm_slot].UpdateNameFlag=false; // clear flag first
	TCHAR *fname = LookupFLARMDetails(pGPS->FLARM_Traffic[flarm_slot].ID);
	if (fname) {
		LK_tcsncpy(name,fname,MAXFLARMNAME);

		//  Now we have the name, so lookup also for the Cn
		// This will return either real Cn or Name, again
		TCHAR *cname = LookupFLARMCn(pGPS->FLARM_Traffic[flarm_slot].ID);
		if (cname) {
			int cnamelen=_tcslen(cname);
			if (cnamelen<=MAXFLARMCN) {
				_tcscpy( pGPS->FLARM_Traffic[flarm_slot].Cn, cname);
			} else {
				// else probably it is the Name again, and we create a fake Cn
				pGPS->FLARM_Traffic[flarm_slot].Cn[0]=cname[0];
				pGPS->FLARM_Traffic[flarm_slot].Cn[1]=cname[cnamelen-2];
				pGPS->FLARM_Traffic[flarm_slot].Cn[2]=cname[cnamelen-1];
				pGPS->FLARM_Traffic[flarm_slot].Cn[3]=_T('\0');
			}
		} else {
			_tcscpy( pGPS->FLARM_Traffic[flarm_slot].Cn, _T("Err"));
		}

		#ifdef DEBUG_LKT
		StartupStore(_T("... PFLAA Name to FlarmSlot=%d ID=%lx Name=<%s> Cn=<%s>\n"),
			flarm_slot,
	  		pGPS->FLARM_Traffic[flarm_slot].ID,
			pGPS->FLARM_Traffic[flarm_slot].Name,
			pGPS->FLARM_Traffic[flarm_slot].Cn);
		#endif
	} else {
		// Else we NEED to set a name, otherwise it will constantly search for it over and over..
		name[0]=_T('?');
		name[1]=_T('\0');
		pGPS->FLARM_Traffic[flarm_slot].Cn[0]=_T('?');
		pGPS->FLARM_Traffic[flarm_slot].Cn[1]=_T('\0');
		
		#ifdef DEBUG_LKT
		StartupStore(_T("... New FlarmSlot=%d ID=%lx with no name, assigned a \"?\"\n"),
			flarm_slot,
	  		pGPS->FLARM_Traffic[flarm_slot].ID);
		#endif
	}
  }

  #ifdef DEBUG_LKT
  StartupStore(_T("... PFLAA pGPS slot=%d ID=%lx name=<%s> cn=<%s> rAlt=%.0f Track=%.0f Speed=%.0f Climb=%.1f Baro=%f FlAlt=%f\n"),
	flarm_slot,
	pGPS->FLARM_Traffic[flarm_slot].ID,
	pGPS->FLARM_Traffic[flarm_slot].Name,
	pGPS->FLARM_Traffic[flarm_slot].Cn,
	pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude,
	pGPS->FLARM_Traffic[flarm_slot].TrackBearing,
	pGPS->FLARM_Traffic[flarm_slot].Speed,
	pGPS->FLARM_Traffic[flarm_slot].ClimbRate,
	pGPS->BaroAltitude,
	pGPS->FLARM_Traffic[flarm_slot].Altitude);
  #endif

  //  update Virtual Waypoint for target FLARM
  if (flarm_slot == LKTargetIndex) {
	WayPointList[RESWP_FLARMTARGET].Latitude   = pGPS->FLARM_Traffic[LKTargetIndex].Latitude;
	WayPointList[RESWP_FLARMTARGET].Longitude  = pGPS->FLARM_Traffic[LKTargetIndex].Longitude;
	WayPointList[RESWP_FLARMTARGET].Altitude   = pGPS->FLARM_Traffic[LKTargetIndex].Altitude;
  }


  return FALSE;
}
Esempio n. 2
0
//
// The purpose of simulating flarm traffic is NOT to play a videogame against flarm objects:
// we only need some traffic to display for testing on ground during simulations.
//
// >>>>> This is accessing directly the GPS_INFO main struct, writing inside it. <<<<<
// Called by LKSimulator, already locking FlightData . No worry.
//
void SimFlarmTraffic(long ID, double offset)
{
  int flarm_slot = 0;
  bool newtraffic=false;

  GPS_INFO.FLARM_Available=true;
  LastFlarmCommandTime=GPS_INFO.Time; // useless really, we dont call UpdateMonitor from SIM
  

  flarm_slot = FLARM_FindSlot(&GPS_INFO, ID);

  if (flarm_slot<0) return;
  if ( GPS_INFO.FLARM_Traffic[flarm_slot].Status == LKT_EMPTY) {
	newtraffic=true;
  }

  // before changing timefix, see if it was an old target back locked in!
  CheckBackTarget(&GPS_INFO, flarm_slot);
  // and then set time of fix to current time
  GPS_INFO.FLARM_Traffic[flarm_slot].Time_Fix = GPS_INFO.Time;

/*
	  TEXT("%hu,%lf,%lf,%lf,%hu,%lx,%lf,%lf,%lf,%lf,%hu"),
	  &GPS_INFO.FLARM_Traffic[flarm_slot].AlarmLevel, // unsigned short 0
	  &GPS_INFO.FLARM_Traffic[flarm_slot].RelativeNorth, //  1	
	  &GPS_INFO.FLARM_Traffic[flarm_slot].RelativeEast, //   2
	  &GPS_INFO.FLARM_Traffic[flarm_slot].RelativeAltitude, //  3
	  &GPS_INFO.FLARM_Traffic[flarm_slot].IDType, // unsigned short     4
	  &GPS_INFO.FLARM_Traffic[flarm_slot].ID, // 6 char hex
	  &GPS_INFO.FLARM_Traffic[flarm_slot].TrackBearing, // double       6
	  &GPS_INFO.FLARM_Traffic[flarm_slot].TurnRate, // double           7
	  &GPS_INFO.FLARM_Traffic[flarm_slot].Speed, // double              8 m/s
	  &GPS_INFO.FLARM_Traffic[flarm_slot].ClimbRate, // double          9 m/s
	  &GPS_INFO.FLARM_Traffic[flarm_slot].Type); // unsigned short     10
*/

  // If first time seen this traffic, place it nearby
  if ( newtraffic ) {
	GPS_INFO.FLARM_Traffic[flarm_slot].RelativeNorth=2;
	GPS_INFO.FLARM_Traffic[flarm_slot].RelativeEast=2;
	GPS_INFO.FLARM_Traffic[flarm_slot].Latitude  = SimNewCoordinate(GPS_INFO.Latitude, offset);
	GPS_INFO.FLARM_Traffic[flarm_slot].Longitude = SimNewCoordinate(GPS_INFO.Longitude,offset);
	GPS_INFO.FLARM_Traffic[flarm_slot].Altitude = SimNewAltitude(GPS_INFO.Altitude);

	GPS_INFO.FLARM_Traffic[flarm_slot].TrackBearing= (double) rand()/91.276;
	GPS_INFO.FLARM_Traffic[flarm_slot].AlarmLevel=0;
	GPS_INFO.FLARM_Traffic[flarm_slot].ID=ID;
	GPS_INFO.FLARM_Traffic[flarm_slot].TurnRate=0;
	GPS_INFO.FLARM_Traffic[flarm_slot].Speed= SimNewSpeed(GPS_INFO.Speed);

	GPS_INFO.FLARM_Traffic[flarm_slot].Status = LKT_REAL;
  } else {
	GPS_INFO.FLARM_Traffic[flarm_slot].Latitude  += (double)(rand()/20000000.0)*(rand()>15000?1:-1);
	GPS_INFO.FLARM_Traffic[flarm_slot].Longitude += (double)(rand()/20000000.0)*(rand()>15000?1:-1);
	GPS_INFO.FLARM_Traffic[flarm_slot].Altitude += (double)(rand()/2200.0)*(rand()>15000?1:-1);
  }
  GPS_INFO.FLARM_Traffic[flarm_slot].RelativeAltitude = GPS_INFO.FLARM_Traffic[flarm_slot].Altitude - GPS_INFO.Altitude;

  //
  GPS_INFO.FLARM_Traffic[flarm_slot].Average30s = flarmCalculations.Average30s(
	  GPS_INFO.FLARM_Traffic[flarm_slot].ID,
	  GPS_INFO.Time,
	  GPS_INFO.FLARM_Traffic[flarm_slot].Altitude);

  TCHAR *name = GPS_INFO.FLARM_Traffic[flarm_slot].Name;
  //TCHAR *cn = GPS_INFO.FLARM_Traffic[flarm_slot].Cn;
  // If there is no name yet, or if we have a pending update event..
  if (!_tcslen(name) || GPS_INFO.FLARM_Traffic[flarm_slot].UpdateNameFlag ) {

	#ifdef DEBUG_SIMLKT
	if (GPS_INFO.FLARM_Traffic[flarm_slot].UpdateNameFlag ) {
		StartupStore(_T("... UpdateNameFlag for slot %d\n"),flarm_slot);
	} else {
		StartupStore(_T("... First lookup name for slot %d\n"),flarm_slot);
	}
	#endif

	GPS_INFO.FLARM_Traffic[flarm_slot].UpdateNameFlag=false; // clear flag first
	TCHAR *fname = LookupFLARMDetails(GPS_INFO.FLARM_Traffic[flarm_slot].ID);
	if (fname) {
		LK_tcsncpy(name,fname,MAXFLARMNAME);

		//  Now we have the name, so lookup also for the Cn
		// This will return either real Cn or Name, again
		TCHAR *cname = LookupFLARMCn(GPS_INFO.FLARM_Traffic[flarm_slot].ID);
		if (cname) {
			int cnamelen=_tcslen(cname);
			if (cnamelen<=MAXFLARMCN) {
				_tcscpy( GPS_INFO.FLARM_Traffic[flarm_slot].Cn, cname);
			} else {
				// else probably it is the Name again, and we create a fake Cn
				GPS_INFO.FLARM_Traffic[flarm_slot].Cn[0]=cname[0];
				GPS_INFO.FLARM_Traffic[flarm_slot].Cn[1]=cname[cnamelen-2];
				GPS_INFO.FLARM_Traffic[flarm_slot].Cn[2]=cname[cnamelen-1];
				GPS_INFO.FLARM_Traffic[flarm_slot].Cn[3]=_T('\0');
			}
		} else {
			_tcscpy( GPS_INFO.FLARM_Traffic[flarm_slot].Cn, _T("Err"));
		}

		#ifdef DEBUG_SIMLKT
		StartupStore(_T("... PFLAA Name to FlarmSlot=%d ID=%lx Name=<%s> Cn=<%s>\n"),
			flarm_slot,
	  		GPS_INFO.FLARM_Traffic[flarm_slot].ID,
			GPS_INFO.FLARM_Traffic[flarm_slot].Name,
			GPS_INFO.FLARM_Traffic[flarm_slot].Cn);
		#endif
	} else {
		// Else we NEED to set a name, otherwise it will constantly search for it over and over..
		name[0]=_T('?');
		name[1]=_T('\0');
		GPS_INFO.FLARM_Traffic[flarm_slot].Cn[0]=_T('?');
		GPS_INFO.FLARM_Traffic[flarm_slot].Cn[1]=_T('\0');
		
		#ifdef DEBUG_SIMLKT
		StartupStore(_T("... New FlarmSlot=%d ID=%lx with no name, assigned a \"?\"\n"),
			flarm_slot,
	  		GPS_INFO.FLARM_Traffic[flarm_slot].ID);
		#endif
	}
  }

  //  update Virtual Waypoint for target FLARM
  if (flarm_slot == LKTargetIndex) {
	WayPointList[RESWP_FLARMTARGET].Latitude   = GPS_INFO.FLARM_Traffic[LKTargetIndex].Latitude;
	WayPointList[RESWP_FLARMTARGET].Longitude  = GPS_INFO.FLARM_Traffic[LKTargetIndex].Longitude;
	WayPointList[RESWP_FLARMTARGET].Altitude   = GPS_INFO.FLARM_Traffic[LKTargetIndex].Altitude;
  }

}
Esempio n. 3
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) {
		srand( Poco::Timestamp().epochTime());
		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) {
	if (!flarmwasinit) {
		srand(Poco::Timestamp().epochTime());
		// 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(gettext(TEXT("_@M279_"))); // 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();
}