Example #1
0
void ComPort::ProcessChar(char c) {
    if (ComCheck_ActivePort>=0 && GetPortIndex()==(unsigned)ComCheck_ActivePort) {
        ComCheck_AddChar(c);
    }

#ifdef RADIO_ACTIVE
    if(RadioPara.Enabled && devParseStream(devIdx, &c, 1, &GPS_INFO)) {
        // if this port is used for stream device, leave immediately.
        return;
    }
#endif // RADIO_ACTIVE

    // last char need to be reserved for '\0' for avoid buffer overflow
    // in theory this should never happen because NMEA sentence can't have more than 82 char and _NmeaString size is 160.
    if (pLastNmea >= std::begin(_NmeaString) && (pLastNmea+1) < std::end(_NmeaString)) {

        if (c == '\n' || c == '\r') {
            // abcd\n , now closing the line also with \r
            *(pLastNmea++) = _T('\n');
            *(pLastNmea) = _T('\0'); // terminate string.
            // process only meaningful sentences, avoid processing a single \n \r etc.
            if (std::distance(std::begin(_NmeaString), pLastNmea) > 5) {
                LockFlightData();
                devParseNMEA(devIdx, _NmeaString, &GPS_INFO);
                UnlockFlightData();
            }
        } else {
            *(pLastNmea++) = c;
            return;
        }
    }
    // overflow, so reset buffer
    pLastNmea = std::begin(_NmeaString);
}
Example #2
0
void DebugStore(const char *Str, ...)
{
#if defined(DEBUG)
  char buf[MAX_PATH];
  va_list ap;
  int len;

  va_start(ap, Str);
  len = vsprintf(buf, Str, ap);
  va_end(ap);

  LockFlightData();
  FILE *stream;
  TCHAR szFileName[] = TEXT(LKF_DEBUG);
  static bool initialised = false;
  if (!initialised) {
    initialised = true;
    stream = _wfopen(szFileName,TEXT("w"));
  } else {
    stream = _wfopen(szFileName,TEXT("a+"));
  }

  fwrite(buf,len,1,stream);

  fclose(stream);
  UnlockFlightData();
#endif
}
Example #3
0
void SwitchZoomClimb(NMEA_INFO *Basic, DERIVED_INFO *Calculated, bool isclimb, bool left) {

  if ( (AutoWindMode == D_AUTOWIND_CIRCLING) || (AutoWindMode==D_AUTOWIND_BOTHCIRCZAG) ) {
    LockFlightData();
    windanalyser->slot_newFlightMode(Basic, Calculated, left, 0);
    UnlockFlightData();
  }
}
Example #4
0
void MapWindow::UpdateInfo(NMEA_INFO *nmea_info,
                           DERIVED_INFO *derived_info) {
  LockFlightData();
  memcpy(&DrawInfo,nmea_info,sizeof(NMEA_INFO));
  memcpy(&DerivedDrawInfo,derived_info,sizeof(DERIVED_INFO));
  zoom.UpdateMapScale(); // done here to avoid double latency due to locks 
  UnlockFlightData();
}
Example #5
0
static void DoOptimise(void) {
  double myrange= Range;
  double RangeLast= Range;
  double deltaTlast = 0;
  int steps = 0;
  if (!AATEnabled) return;

  LockFlightData();
  LockTaskData();
  TargetDialogOpen = true;
  do {
    myrange = Range;
    AdjustAATTargets(Range);
    RefreshTask();
    RefreshTaskStatistics();
    double deltaT = CALCULATED_INFO.TaskTimeToGo;
    if ((CALCULATED_INFO.TaskStartTime>0.0)&&(CALCULATED_INFO.Flying)) {
      deltaT += GPS_INFO.Time-CALCULATED_INFO.TaskStartTime;
    }
    deltaT= min(24.0*60.0,deltaT/60.0)-AATTaskLength-5;

    double dRdT = 0.001;
    if (steps>0) {
      if (fabs(deltaT-deltaTlast)>0.01) {
        dRdT = min(0.5,(Range-RangeLast)/(deltaT-deltaTlast));
        if (dRdT<=0.0) {
          // error, time decreases with increasing range!
          // or, no effect on time possible
          break;
        }
      } else {
        // minimal change for whatever reason
        // or, no effect on time possible, e.g. targets locked
        break;
      }
    }
    RangeLast = Range;
    deltaTlast = deltaT;

    if (fabs(deltaT)>0.25) {
      // more than 15 seconds error
      Range -= dRdT*deltaT;
      Range = max(-1.0, min(Range,1.0));
    } else {
      break;
    }

  } while (steps++<25);

  Range = myrange;
  AdjustAATTargets(Range);
  RefreshCalculator();

  TargetDialogOpen = false;
  UnlockTaskData();
  UnlockFlightData();
}
Example #6
0
void ComPort::ProcessChar(char c) {
  if (bi<(MAX_NMEA_LEN-1)) {

	BuildingString[bi++] = c;

#if 100430
	if(c=='\n' || c=='\r') {
		// abcd\n , now closing the line also with \r
		BuildingString[bi] = '\0';

		if (c == '\r') BuildingString[bi-1]='\n';

		// process only meaningful sentences, avoid processing a single \n \r etc.
		if (bi>5) { // 100430
			LockFlightData();
			devParseNMEA(devIdx, BuildingString, &GPS_INFO);
			UnlockFlightData();
		}
	} else {
		return;
	}
#else
	if(c=='\n') {
		// abcd\n 
		BuildingString[bi] = '\0';

		// do not consider CR in ProcessChar inside Port
		// bi-2==\r bi-1==\n bi=\0
		if (bi>1 && (BuildingString[bi-2]==0x0d) ) {
			BuildingString[bi-2] = '\n';
			BuildingString[bi-1] = '\0';
		}
		LockFlightData();
		devParseNMEA(devIdx, BuildingString, &GPS_INFO);
		UnlockFlightData();
	} else {
		return;
	}
#endif
  }
  // overflow, so reset buffer
  bi = 0;
}
Example #7
0
void ReplayLogger::Stop(void) {
  ReadLine(NULL); // close the file
  if (Enabled) {
    LockFlightData();
    GPS_INFO.Speed = 0;
    //    GPS_INFO.Time = 0;
    NumLoggerBuffered = 0;
    UnlockFlightData();
  }
  Enabled = false;
}
Example #8
0
bool ReplayLogger::UpdateInternal(void) {
  static bool init=true;

  if (!Enabled) {
    init = true;
    ReadLine(NULL); // close file
    Enabled = true;
  }

  static CatmullRomInterpolator cli;

  SYSTEMTIME st;
  GetLocalTime(&st);
  static double time_lstart = 0;

  if (init) {
    time_lstart = 0;
  }
  static double time=0;
  double deltatimereal;

  bool finished = false;

  double timelast = time;

  time = (st.wHour*3600+st.wMinute*60+st.wSecond-time_lstart);
  deltatimereal = time-timelast;

  if (init) {
    time_lstart = time;
    time = 0;
    deltatimereal = 0;
    ReplayTime = 0;
    cli.Reset();
  }

  ReplayTime += TimeScale*deltatimereal;

#if DEBUG_REPLAY
  TCHAR tutc[20];
  Units::TimeToText(tutc, (int)ReplayTime);
  StartupStore(_T("........ REPLAY: UTC h%s\n"),tutc);
#endif

  double mintime = cli.GetMinTime(); // li_lat.GetMinTime();
  if (ReplayTime<mintime) {
	ReplayTime = mintime;
  }

  // if need a new point
  while (cli.NeedData(ReplayTime)&&(!finished)) {

    double t1, Lat1, Lon1, Alt1;
    finished = !ReadPoint(&t1,&Lat1,&Lon1,&Alt1);

    if (!finished && (t1>0)) {
	if (!cli.Update(t1,Lon1,Lat1,Alt1)) { 
		break;
	}
    }
  }

  if (!finished) {

    double LatX, LonX, AltX, SpeedX, BearingX;
    double LatX1, LonX1, AltX1;

    cli.Interpolate(ReplayTime, &LonX, &LatX, &AltX);
    cli.Interpolate(ReplayTime+0.1, &LonX1, &LatX1, &AltX1);

    SpeedX = cli.GetSpeed(ReplayTime);
    DistanceBearing(LatX, LonX, LatX1, LonX1, NULL, &BearingX);

    if ((SpeedX>0) && (LatX != LatX1) && (LonX != LonX1)) {

      LockFlightData();
      if (init) {
        flightstats.Reset();
      }
      GPS_INFO.Latitude = LatX;
      GPS_INFO.Longitude = LonX;
      GPS_INFO.Speed = SpeedX;
      GPS_INFO.TrackBearing = BearingX;
      GPS_INFO.Altitude = AltX;
      GPS_INFO.BaroAltitude = AltitudeToQNHAltitude(AltX);
      GPS_INFO.Time = ReplayTime;
      UnlockFlightData();
    } else {
      // This is required in case the integrator fails,
      // which can occur due to parsing faults
      ReplayTime = cli.GetMaxTime();
    }
  }

  // quit if finished.
  if (finished) {
    #if DEBUG_REPLAY
    StartupStore(_T("......... REPLAY: FINISHED\n"));
    #endif
    Stop();
  }
  init = false;

  return !finished;
}
Example #9
0
DWORD CalculationThread (LPVOID lpvoid) {
	(void)lpvoid;
  bool needcalculationsslow;

  NMEA_INFO     tmpGPS;
  DERIVED_INFO  tmpCALCULATED;
  FILETIME CreationTime, ExitTime, StartKernelTime, EndKernelTime, StartUserTime, EndUserTime ;
  needcalculationsslow = false;

  // let's not create a deadlock here, setting the go after another race condition
  goCalculationThread=true; // 091119 CHECK
  // wait for proper startup signal
  while (!MapWindow::IsDisplayRunning()) {
    Sleep(100);
  }

  // while (!goCalculating) Sleep(100);
  Sleep(1000); // 091213  BUGFIX need to syncronize !!! TOFIX02 TODO

  while (!MapWindow::CLOSETHREAD) {

    WaitForSingleObject(dataTriggerEvent, 5000);
    ResetEvent(dataTriggerEvent);
    if (MapWindow::CLOSETHREAD) break; // drop out on exit

    GetThreadTimes( hCalculationThread, &CreationTime, &ExitTime,&StartKernelTime,&StartUserTime);

    // make local copy before editing...
    LockFlightData();
      FLARM_RefreshSlots(&GPS_INFO);
      memcpy(&tmpGPS,&GPS_INFO,sizeof(NMEA_INFO));
      memcpy(&tmpCALCULATED,&CALCULATED_INFO,sizeof(DERIVED_INFO));
    UnlockFlightData();

    DoCalculationsVario(&tmpGPS,&tmpCALCULATED);
    if (!tmpGPS.VarioAvailable) {
    	TriggerVarioUpdate(); // emulate vario update
    } 
    
    if(DoCalculations(&tmpGPS,&tmpCALCULATED)){
	#if (WINDOWSPC>0) && !TESTBENCH
	#else
        if (!INPAN)
	#endif
	{
           MapWindow::MapDirty = true;
	}
        needcalculationsslow = true;

        if (tmpCALCULATED.Circling)
          MapWindow::mode.Fly(MapWindow::Mode::MODE_FLY_CIRCLING);
        else if (tmpCALCULATED.FinalGlide)
          MapWindow::mode.Fly(MapWindow::Mode::MODE_FLY_FINAL_GLIDE);
        else
          MapWindow::mode.Fly(MapWindow::Mode::MODE_FLY_CRUISE);
    }
        
    if (MapWindow::CLOSETHREAD) break; // drop out on exit

    // This is activating another run for Thread Draw
    TriggerRedraws(&tmpGPS, &tmpCALCULATED);

    if (MapWindow::CLOSETHREAD) break; // drop out on exit

    if (SIMMODE) {
	if (needcalculationsslow || ( ReplayLogger::IsEnabled() ) ) { 
		DoCalculationsSlow(&tmpGPS,&tmpCALCULATED);
		needcalculationsslow = false;
	}
    } else {
	if (needcalculationsslow) {
		DoCalculationsSlow(&tmpGPS,&tmpCALCULATED);
		needcalculationsslow = false;
	}
    }

    if (MapWindow::CLOSETHREAD) break; // drop out on exit

    // values changed, so copy them back now: ONLY CALCULATED INFO
    // should be changed in DoCalculations, so we only need to write
    // that one back (otherwise we may write over new data)
    LockFlightData();
    memcpy(&CALCULATED_INFO,&tmpCALCULATED,sizeof(DERIVED_INFO));
    UnlockFlightData();

    // update live tracker with new values
    // this is a nonblocking call, live tracker runs on different thread
     LiveTrackerUpdate(&tmpGPS, &tmpCALCULATED);

    if (FlightDataRecorderActive) UpdateFlightDataRecorder(&tmpGPS,&tmpCALCULATED);
   
    
    if ( (GetThreadTimes( hCalculationThread, &CreationTime, &ExitTime,&EndKernelTime,&EndUserTime)) == 0) {
               Cpu_Calc=9999;
    } else {
               Cpustats(&Cpu_Calc,&StartKernelTime, &EndKernelTime, &StartUserTime, &EndUserTime);
    }
  }
  return 0;
}
Example #10
0
static void OnTargetClicked(WindowControl * Sender) {
  (void)Sender;

  if (SelectedTraffic<0 || SelectedTraffic>MAXTRAFFIC) {
	StartupStore(_T("--- Invalid traffic selected to Target, out of range%s"),NEWLINE);
	DoStatusMessage(_T("ERR-126 invalid TARGET traffic"));
	return;
  }
  if ( GPS_INFO.FLARM_Traffic[SelectedTraffic].ID <1 ) {
	DoStatusMessage(gettext(TEXT("_@M879_"))); // SORRY TARGET JUST DISAPPEARED
	return;
  }

  if ( LKTraffic[SelectedTraffic].Locked ) {
#if 0
	if (MessageBoxX(hWndMapWindow,
		gettext(TEXT("_@M880_")), // UNLOCK current target?
		gettext(TEXT("_@M881_")), // Target selection
	MB_YESNO|MB_ICONQUESTION) == IDYES) {

#endif
	LockFlightData();
	GPS_INFO.FLARM_Traffic[SelectedTraffic].Locked=false;
	UnlockFlightData();
	LKTargetIndex=-1;
	LKTargetType=LKT_TYPE_NONE;
	WayPointList[RESWP_FLARMTARGET].Latitude   = RESWP_INVALIDNUMBER;
	WayPointList[RESWP_FLARMTARGET].Longitude  = RESWP_INVALIDNUMBER;
	WayPointList[RESWP_FLARMTARGET].Altitude   = RESWP_INVALIDNUMBER;
	_tcscpy(WayPointList[RESWP_FLARMTARGET].Name,_T(RESWP_FLARMTARGET_NAME) );
	DoStatusMessage(gettext(TEXT("_@M882_"))); // TARGET RELEASED
	wf->SetModalResult(mrOK);
	return;
  }
#if 0
  if (MessageBoxX(hWndMapWindow, 
	gettext(TEXT("_@M884_")), // LOCK this target?
	gettext(TEXT("_@M881_")), // Target selection
  MB_YESNO|MB_ICONQUESTION) == IDYES) {
#endif
	// one more check for existance
	if ( GPS_INFO.FLARM_Traffic[SelectedTraffic].ID <1 ) {
		DoStatusMessage(gettext(TEXT("_@M883_"))); // TARGET DISAPPEARED!
		return;
	}

	LockFlightData();
	// unlock previous target, if any
	if (LKTargetIndex>=0 && LKTargetIndex<MAXTRAFFIC) {
		GPS_INFO.FLARM_Traffic[LKTargetIndex].Locked=false;
	}
	GPS_INFO.FLARM_Traffic[SelectedTraffic].Locked=true;
	UnlockFlightData();
	// LKTOKEN  _@M675_ = "TARGET LOCKED" 
	DoStatusMessage(gettext(TEXT("_@M675_")));
	LKTargetIndex=SelectedTraffic;
	LKTargetType=LKT_TYPE_MASTER;

	// Remember that we need to update the virtual waypoint constantly when we receive FLARM data of target
	// Probably name is not updated if changed from Flarm menu...
	OvertargetMode=OVT_FLARM;
	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;
	if (_tcslen(GPS_INFO.FLARM_Traffic[LKTargetIndex].Name) == 1) {
		_stprintf(WayPointList[RESWP_FLARMTARGET].Name,_T("%0x"),GPS_INFO.FLARM_Traffic[LKTargetIndex].ID);
	} else {
		_stprintf(WayPointList[RESWP_FLARMTARGET].Name,_T("%s"),GPS_INFO.FLARM_Traffic[LKTargetIndex].Name);
	}
	SetModeType(LKMODE_TRF, IM_TARGET);
  wf->SetModalResult(mrOK);

}

static void OnRenameClicked(WindowControl * Sender){
  (void)Sender;

  if (SelectedTraffic<0 || SelectedTraffic>MAXTRAFFIC) {
	StartupStore(_T("--- Invalid traffic selected to rename, out of range%s"),NEWLINE);
	DoStatusMessage(_T("ERR-126 invalid traffic"));
	return;
  }
  if ( LKTraffic[SelectedTraffic].ID <1 ) {
	StartupStore(_T("--- Invalid traffic selected to rename, invalid ID%s"),NEWLINE);
	DoStatusMessage(_T("ERR-127 invalid traffic"));
	return;
  }

  TCHAR newName[MAXFLARMNAME+1];
  newName[0] = 0;
  dlgTextEntryShowModal(newName, 7); // 100322 raised from 3 to 6 (+1)
  newName[MAXFLARMNAME] = 0;

  #ifdef DEBUG_LKT
  StartupStore(_T("************  dlgLK RenameClicked for slot=%d id=%lx status=%d oldname=<%s> newName=<%s>\n"),
	SelectedTraffic, 
	GPS_INFO.FLARM_Traffic[SelectedTraffic].ID,
	GPS_INFO.FLARM_Traffic[SelectedTraffic].Status,
	GPS_INFO.FLARM_Traffic[SelectedTraffic].Name,
	newName);
  #endif

  int newnamelen=_tcslen(newName);

  if (newnamelen>0) {

	LockFlightData();

	// Since we don-t know if a new PFLAA with this ID will arrive, and the UpdateNameFlag will be used,
	// we must change the name here now.
	_tcscpy(GPS_INFO.FLARM_Traffic[SelectedTraffic].Name,newName);
	// ... and here also, because SetValues is using this copy 
	_tcscpy(LKTraffic[SelectedTraffic].Name,newName);

	// we assume that no Cn is available, or in any case cannot be kept
	// If newName is smaller or equal to possible Cn, we use it entirely
	if (newnamelen<=MAXFLARMCN) {
		_tcscpy( GPS_INFO.FLARM_Traffic[SelectedTraffic].Cn, newName);
	} else {
		// else we create a fake Cn
		GPS_INFO.FLARM_Traffic[SelectedTraffic].Cn[0]=newName[0];
		GPS_INFO.FLARM_Traffic[SelectedTraffic].Cn[1]=newName[newnamelen-2];
		GPS_INFO.FLARM_Traffic[SelectedTraffic].Cn[2]=newName[newnamelen-1];
		GPS_INFO.FLARM_Traffic[SelectedTraffic].Cn[3]=_T('\0');
	}
	// update it temporarily so that it appears updated leaving the edit page
	_tcscpy(LKTraffic[SelectedTraffic].Cn,GPS_INFO.FLARM_Traffic[SelectedTraffic].Cn);

	// It will be useless, because we already updated the name.. but never mind.
	GPS_INFO.FLARM_Traffic[SelectedTraffic].UpdateNameFlag=true;
	// This will create the local flarmid entry, but won't update the structure in GPS_INFO
	// until a new PFLAA arrives from this ID. 
	AddFlarmLookupItem(GPS_INFO.FLARM_Traffic[SelectedTraffic].ID, newName, true);
	UnlockFlightData();

	#ifdef DEBUG_LKT
	StartupStore(_T("...... dlgLK RENAMED slot=%d id=%lx status=%d name=<%s>\n"),
	SelectedTraffic, 
	GPS_INFO.FLARM_Traffic[SelectedTraffic].ID,
	GPS_INFO.FLARM_Traffic[SelectedTraffic].Status,
	GPS_INFO.FLARM_Traffic[SelectedTraffic].Name);
	#endif
	// reload the name...
	SetValues(SelectedTraffic);

  }

}


static void OnCloseClicked(WindowControl * Sender){
	(void)Sender;
  wf->SetModalResult(mrOK);
}
Example #11
0
void SettingsLeave() {
  if (!GlobalRunning) return; 

  SwitchToMapWindow();

  // Locking everything here prevents the calculation thread from running,
  // while shared data is potentially reloaded.
 
  LockFlightData();
  LockTaskData();

  MenuActive = false;

  // 101020 LKmaps contain only topology , so no need to force total reload!
  if(MAPFILECHANGED) {
	#if TESTBENCH
	StartupStore(_T(".... MAPFILECHANGED from configuration\n"));
	#endif
	if (LKTopo==0) {
		AIRSPACEFILECHANGED = TRUE;
		AIRFIELDFILECHANGED = TRUE;
		WAYPOINTFILECHANGED = TRUE;
		TERRAINFILECHANGED  = TRUE;
	}
	TOPOLOGYFILECHANGED = TRUE;
  } 

  if (TERRAINFILECHANGED) {
	#if TESTBENCH
	StartupStore(_T(".... TERRAINFILECHANGED from configuration\n"));
	#endif
	RasterTerrain::CloseTerrain();
	RasterTerrain::OpenTerrain();
	// NO! We dont reload waypoints on terrain change.
	// SetHome(WAYPOINTFILECHANGED==TRUE);
	MapWindow::ForceVisibilityScan = true;
  }

  if((WAYPOINTFILECHANGED) || (AIRFIELDFILECHANGED)) {
	#if TESTBENCH
	StartupStore(_T(".... WAYPOINT OR AIRFIELD CHANGED from configuration\n"));
	#endif
	SaveDefaultTask(); //@ 101020 BUGFIX
	ClearTask();
	ReadWayPoints();
	StartupStore(_T(". RELOADED %d WAYPOINTS + %d virtuals%s"),WayPointList.size()-NUMRESWP,NUMRESWP,NEWLINE);
	SetHome(true); // force home reload

	if (WAYPOINTFILECHANGED) {
		#if TESTBENCH
		StartupStore(_T(".... WAYPOINTFILECHANGED from configuration\n"));
		#endif
		SaveRecentList();
		LoadRecentList();
		RangeLandableNumber=0;
		RangeAirportNumber=0;
		RangeTurnpointNumber=0;
		CommonNumber=0;
		SortedNumber=0;
		// SortedTurnpointNumber=0; 101222
		LastDoRangeWaypointListTime=0;
		LKForceDoCommon=true;
		LKForceDoNearest=true;
		LKForceDoRecent=true;
		// LKForceDoNearestTurnpoint=true; 101222
	}
	InputEvents::eventTaskLoad(_T(LKF_DEFAULTASK)); //@ BUGFIX 101020
  } 

  if (TOPOLOGYFILECHANGED) {
	#if TESTBENCH
	StartupStore(_T(".... TOPOLOGYFILECHANGED from configuration\n"));
	#endif
	CloseTopology();
	OpenTopology();
	MapWindow::ForceVisibilityScan = true;
  }
  
  if(AIRSPACEFILECHANGED) {
	#if TESTBENCH
	StartupStore(_T(".... AIRSPACEFILECHANGED from configuration\n"));
	#endif
	CAirspaceManager::Instance().CloseAirspaces();
	CAirspaceManager::Instance().ReadAirspaces();
	CAirspaceManager::Instance().SortAirspaces();
	MapWindow::ForceVisibilityScan = true;
  }  
  
  if (POLARFILECHANGED) {
	#if TESTBENCH
	StartupStore(_T(".... POLARFILECHANGED from configuration\n"));
	#endif
	CalculateNewPolarCoef();
	GlidePolar::SetBallast();
  }
  
  if (AIRFIELDFILECHANGED
      || AIRSPACEFILECHANGED
      || WAYPOINTFILECHANGED
      || TERRAINFILECHANGED
      || TOPOLOGYFILECHANGED
      ) {
	CloseProgressDialog();
	MainWindow.SetFocus();
  }

  extern void ReinitScreen(void);
  if (FONTSCHANGED) {
      ReinitScreen();
  }
  
  UnlockTaskData();
  UnlockFlightData();

  if(!SIMMODE && COMPORTCHANGED) {
      #if TESTBENCH
      StartupStore(_T(".... COMPORTCHANGED from configuration.  ForceComPortReset @%s\n"),WhatTimeIsIt());
      #endif
      LKForceComPortReset=true;
      // RestartCommPorts(); 110605
  }

  MapWindow::ResumeDrawingThread();
  // allow map and calculations threads to continue on their merry way
}
Example #12
0
bool DoTarget(NMEA_INFO *Basic, DERIVED_INFO *Calculated)
{
   double x0, y0, etas, ttgo=0;
   double bearing, distance;

   if (LKTargetIndex<0 || LKTargetIndex>=MAXTRAFFIC) return false;

   // DoTarget is called from MapWindow, in real time. We have enough CPU power there now

   #if 0
   if (  LastDoTarget > Basic->Time ) LastDoTarget=Basic->Time;
   // We calculate in real time, because PFLAA sentences are calculated too in real time
   if ( Basic->Time < (LastDoTarget+3.0) ) { 
	return false;
   }
   LastDoTarget=Basic->Time;
   #endif

   #ifdef DEBUG_LKT
   StartupStore(_T("... DoTarget Copy LKTraffic\n"));
   #endif
   LockFlightData();
   memcpy(LKTraffic, Basic->FLARM_Traffic, sizeof(LKTraffic));
   UnlockFlightData();

   if (LKTARG.ID <=0) return false;

   DistanceBearing(Basic->Latitude, Basic->Longitude, 
		LKTARG.Latitude, LKTARG.Longitude,
		&distance, &bearing);
   LKTARG.Distance=distance;
   LKTARG.Bearing=bearing;

   if (LKTARG.Speed>1) {
	x0 = fastsine(LKTARG.TrackBearing)*LKTARG.Speed;
	y0 = fastcosine(LKTARG.TrackBearing)*LKTARG.Speed;
	x0 += fastsine(Calculated->WindBearing)*Calculated->WindSpeed;
	y0 += fastcosine(Calculated->WindBearing)*Calculated->WindSpeed;
	// LKTARG.Heading = AngleLimit360(atan2(x0,y0)*RAD_TLK_DEG); // 101210 check
	etas = isqrt4((unsigned long)(x0*x0*100+y0*y0*100))/10.0;
	LKTARG.EIAS = etas/AirDensityRatio(LKTARG.Altitude);
   } else {
	LKTARG.EIAS=0;
   }


   //double height_above_target = Calculated->NavAltitude - LKTARG.Altitude;

   // We DONT use EnergyHeight here because we are not considering the Target's TE either
   LKTARG.AltArriv = Calculated->NavAltitude - GlidePolar::MacCreadyAltitude(MACCREADY,
	distance,
	bearing,
	Calculated->WindSpeed, Calculated->WindBearing,
	0, 0,
	// final glide, use wind
	true, 
	&ttgo) - LKTARG.Altitude;
	
   // We CANNOT use RelativeAltitude because when ghost or zombie, it wont be updated in real time in respect
   // to OUR real position!! Lets use the last target altitude known.
   double relalt=Calculated->NavAltitude - LKTARG.Altitude;
   if (relalt==0)
	LKTARG.GR=999;
   else {
	// we need thus to invert the relative altitude
	LKTARG.GR=distance/(relalt);
   }


   return true;
}
Example #13
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.52, GPS_INFO.Longitude-0.52, 873, 1478,1.5);
	InsertThermalHistory(GPS_INFO.Time-987, GPS_INFO.Latitude-0.41, GPS_INFO.Longitude-0.41, 762, 1367,1.8);
	InsertThermalHistory(GPS_INFO.Time-100, GPS_INFO.Latitude-0.02, GPS_INFO.Longitude-0.02, 650, 1542,2.2);
	WayPointList[RESWP_LASTTHERMAL].Latitude  = GPS_INFO.Latitude-0.022;
	WayPointList[RESWP_LASTTHERMAL].Longitude = GPS_INFO.Longitude-0.022;
	WayPointList[RESWP_LASTTHERMAL].Altitude  = 650;
	ThLatitude=GPS_INFO.Latitude-0.022;
	ThLongitude=GPS_INFO.Longitude-0.022;

	if (EnableFLARMMap) {
		srand( GetTickCount());
		SimFlarmTraffic(0xdd8951,22.0+(double)(rand()/1000.0));
		SimFlarmTraffic(0xdd8944,31.0+(double)(rand()/1000.0));
		SimFlarmTraffic(0xdd8a43,16.0+(double)(rand()/1000.0));
		SimFlarmTraffic(0xdd8a42,41.0+(double)(rand()/1000.0));
	}
	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( GetTickCount());
		// Add a poker of traffic for the boys
		SimFlarmTraffic(0xdd8951,22.0+(double)(rand()/1000.0));
		SimFlarmTraffic(0xdd8944,31.0+(double)(rand()/1000.0));
		SimFlarmTraffic(0xdd8a43,16.0+(double)(rand()/1000.0));
		SimFlarmTraffic(0xdd8a42,41.0+(double)(rand()/1000.0));
		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);
	}
  }

  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*(GlidePolar::sinkratecache[(int)IASMS]);
	if (sinkias>10) sinkias=10; // set a limiter for sink rate
	// StartupStore(_T(".... ias=%.0f 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 {
		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+0.1); // rounding errors require a correction
	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;
	}
  }


  FindLatitudeLongitude(GPS_INFO.Latitude, GPS_INFO.Longitude, 
                          GPS_INFO.TrackBearing, GPS_INFO.Speed*1.0,
                          &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();
  }
Example #14
0
void Turning(NMEA_INFO *Basic, DERIVED_INFO *Calculated)
{
  static double LastTrack = 0;
  static double StartTime  = 0;
  static double StartLong = 0;
  static double StartLat = 0;
  static double StartAlt = 0;
  static double StartEnergyHeight = 0;
  static double LastTime = 0;
  static int MODE = CRUISE;
  static bool LEFT = FALSE;
  double Rate;
  static double LastRate=0;
  double dRate;
  double dT;

  if (!Calculated->Flying) {
	if (MODE!=CRUISE) {
		#if TESTBENCH
		StartupStore(_T(".... Not flying, still circling -> Cruise forced!\n"));
		#endif
		goto _forcereset;
	}
	return;
  }

  // Back in time in IGC replay mode?
  if(Basic->Time <= LastTime) {
    #if TESTBENCH
    StartupStore(_T("...... Turning check is back in time. Now=%f Last=%f: reset %s\n"),Basic->Time, LastTime,WhatTimeIsIt());
    #endif
_forcereset:
    LastTime = Basic->Time; // 101216 PV not sure of this.. 
    LastTrack = 0;
    StartTime  = 0;
    StartLong = 0;
    StartLat = 0;
    StartAlt = 0;
    StartEnergyHeight = 0;
    LastTime = 0;
    LEFT = FALSE;
    if (MODE!=CRUISE) {
	MODE = CRUISE;
        // Finally do the transition to cruise
        Calculated->Circling = false;
        SwitchZoomClimb(Basic, Calculated, false, LEFT);
        InputEvents::processGlideComputer(GCE_FLIGHTMODE_CRUISE);
    }
    return;
  }
  dT = Basic->Time - LastTime;
  LastTime = Basic->Time;

  #if BUGSTOP
  LKASSERT(dT!=0);
  #else
  if (dT==0) dT=1;
  #endif

  Rate = AngleLimit180(Basic->TrackBearing-LastTrack)/dT;

  #if DEBUGTURN
  StartupStore(_T("... Rate=%f  in time=%f\n"),Rate,dT);
  #endif

  if (dT<2.0 && dT!=0) {
    // time step ok

    // calculate acceleration
    dRate = (Rate-LastRate)/dT;

    double dtlead=0.3;
    // integrate assuming constant acceleration, for one second
    Calculated->NextTrackBearing = Basic->TrackBearing
      + dtlead*(Rate+0.5*dtlead*dRate);
    // s = u.t+ 0.5*a*t*t

    Calculated->NextTrackBearing = 
      AngleLimit360(Calculated->NextTrackBearing);
    
  } else {
    // time step too big, so just take it at last measurement
    Calculated->NextTrackBearing = Basic->TrackBearing;
  }

  Calculated->TurnRate = Rate;

  // JMW limit rate to 50 deg per second otherwise a big spike
  // will cause spurious lock on circling for a long time
  if (Rate>50) {
    Rate = 50;
  } 
  if (Rate<-50) {
    Rate = -50;
  }

  // average rate, to detect essing
  static double rate_history[60];
  double rate_ave=0;
  for (int i=59; i>0; i--) {
    rate_history[i] = rate_history[i-1];
    rate_ave += rate_history[i];
  }
  rate_history[0] = Rate;
  rate_ave /= 60;
 
  // THIS IS UNUSED in 4.0 
  Calculated->Essing = fabs(rate_ave)*100/MinTurnRate;

  if (MODE==CLIMB||MODE==WAITCRUISE)
	Rate = LowPassFilter(LastRate,Rate,0.9);
  else
	Rate = LowPassFilter(LastRate,Rate,0.3);

  LastRate = Rate;

  if(Rate <0)
    {
      if (LEFT) {
        // OK, already going left
      } else {
        LEFT = true;
      }
      Rate *= -1;
    } else {
    if (!LEFT) {
      // OK, already going right
    } else {
      LEFT = false;
    }
  }

  PercentCircling(Basic, Calculated, Rate);

  LastTrack = Basic->TrackBearing;

  bool forcecruise = false;
  bool forcecircling = false;

  #if 1 // UNUSED, EnableExternalTriggerCruise not configurable, set to false since ever
  if (EnableExternalTriggerCruise ) {
    if (ExternalTriggerCruise && ExternalTriggerCircling) {
      // this should never happen
      ExternalTriggerCircling = false;
    }
    forcecruise = ExternalTriggerCruise;
    forcecircling = ExternalTriggerCircling;
  }
  #endif

  switch(MODE) {
  case CRUISE:

    double cruise_turnthreshold;
    if (ISPARAGLIDER)
	cruise_turnthreshold=5;
    else
	cruise_turnthreshold=4;

    if((Rate >= cruise_turnthreshold)||(forcecircling)) {
      // This is initialising the potential thermalling start
      // We still dont know if we are really circling for thermal
      StartTime = Basic->Time;
      StartLong = Basic->Longitude;
      StartLat  = Basic->Latitude;
      StartAlt  = Calculated->NavAltitude;
      StartEnergyHeight  = Calculated->EnergyHeight;
      #if DEBUGTURN
      StartupStore(_T("... CRUISE -> WAITCLIMB\n"));
      #endif
      MODE = WAITCLIMB;
    }
    if (forcecircling) {
      MODE = WAITCLIMB;
    } else {
      break;
    }
  case WAITCLIMB:
    if (forcecruise) {
      MODE = CRUISE;
      break;
    }

    double waitclimb_turnthreshold;
    double cruiseclimbswitch;
    if (ISPARAGLIDER) {
	waitclimb_turnthreshold=5;
        cruiseclimbswitch=15; // this should be finetuned for PGs
    } else {
	waitclimb_turnthreshold=4;
        cruiseclimbswitch=15; // this is ok for gliders
    }

    if((Rate >= waitclimb_turnthreshold)||(forcecircling)) {
      // WE CANNOT do this, because we also may need Circling mode to detect FF!!
      // if( (Calculated->FreeFlying && ((Basic->Time  - StartTime) > cruiseclimbswitch))|| forcecircling) {
       if( (!ISCAR && !ISGAAIRCRAFT && ((Basic->Time  - StartTime) > cruiseclimbswitch))|| forcecircling) { 

         #ifdef TOW_CRUISE
         // If free flight (FF) hasn�t yet been detected, then we may
         // still be on tow.  The following prevents climb mode from
         // engaging due to normal on-aerotow turns.

         if (!Calculated->FreeFlying && (fabs(Calculated->TurnRate) < 12))
           break;
         #endif

         #if DEBUGTURN
         StartupStore(_T("... WAITCLIMB -> CLIMB\n"));
         #endif
       Calculated->Circling = true;
        // JMW Transition to climb
        MODE = CLIMB;
	// Probably a replay flight, with fast forward with no cruise init
	if (StartTime==0) {
	      StartTime = Basic->Time;
	      StartLong = Basic->Longitude;
	      StartLat  = Basic->Latitude;
	      StartAlt  = Calculated->NavAltitude;
	      StartEnergyHeight  = Calculated->EnergyHeight;
	}
        Calculated->ClimbStartLat = StartLat;
        Calculated->ClimbStartLong = StartLong;
        Calculated->ClimbStartAlt = StartAlt+StartEnergyHeight;
        Calculated->ClimbStartTime = StartTime;
        
        if (flightstats.Altitude_Ceiling.sum_n>0) {
          // only update base if have already climbed, otherwise
          // we will catch the takeoff height as the base.

          flightstats.Altitude_Base.
            least_squares_update(max(0.0, Calculated->ClimbStartTime
                                     - Calculated->TakeOffTime)/3600.0,
                                 StartAlt);
        }
        
        SwitchZoomClimb(Basic, Calculated, true, LEFT);
        InputEvents::processGlideComputer(GCE_FLIGHTMODE_CLIMB);
      }
    } else {
      // nope, not turning, so go back to cruise
      #if DEBUGTURN
      StartupStore(_T("... WAITCLIMB -> CRUISE\n"));
      #endif
      MODE = CRUISE;
    }
    break;
  case CLIMB:
    if ( (AutoWindMode == D_AUTOWIND_CIRCLING) || (AutoWindMode==D_AUTOWIND_BOTHCIRCZAG) ) {
      LockFlightData();
      windanalyser->slot_newSample(Basic, Calculated);
      UnlockFlightData();
    }

    double climb_turnthreshold;
    if (ISPARAGLIDER)
	climb_turnthreshold=10;
    else
	climb_turnthreshold=4;
    
    if((Rate < climb_turnthreshold)||(forcecruise)) {
      StartTime = Basic->Time;
      StartLong = Basic->Longitude;
      StartLat  = Basic->Latitude;
      StartAlt  = Calculated->NavAltitude;
      StartEnergyHeight  = Calculated->EnergyHeight;
      // JMW Transition to cruise, due to not properly turning
      MODE = WAITCRUISE;
      #if DEBUGTURN
      StartupStore(_T("... CLIMB -> WAITCRUISE\n"));
      #endif
    }
    if (forcecruise) {
      MODE = WAITCRUISE;
    } else {
      break;
    }
  case WAITCRUISE:

    if (forcecircling) {
      MODE = CLIMB;
      break;
    }

    double waitcruise_turnthreshold;
    double climbcruiseswitch;
    if (ISPARAGLIDER) {
	waitcruise_turnthreshold=10;
        climbcruiseswitch=15;
    } else {
	waitcruise_turnthreshold=4;
        climbcruiseswitch=9; // ok for gliders
    }
    //
    // Exiting climb mode?
    //
    if((Rate < waitcruise_turnthreshold) || forcecruise) {

      if( ((Basic->Time  - StartTime) > climbcruiseswitch) || forcecruise) {

	// We are no more in climb mode

        if (StartTime==0) {
          StartTime = Basic->Time;
          StartLong = Basic->Longitude;
          StartLat  = Basic->Latitude;
          StartAlt  = Calculated->NavAltitude;
          StartEnergyHeight  = Calculated->EnergyHeight;
        }
        Calculated->CruiseStartLat  = StartLat;
        Calculated->CruiseStartLong = StartLong;
        Calculated->CruiseStartAlt  = StartAlt;
        Calculated->CruiseStartTime = StartTime;

	// Here we assign automatically this last thermal to the L> multitarget
	if (Calculated->ThermalGain >100) {

		// Force immediate calculation of average thermal, it would be made
		// during next cycle, but we need it here immediately
		AverageThermal(Basic,Calculated);

		if (EnableThermalLocator) {
			InsertThermalHistory(Calculated->ClimbStartTime, 
				Calculated->ThermalEstimate_Latitude, Calculated->ThermalEstimate_Longitude,
				Calculated->ClimbStartAlt, Calculated->NavAltitude, Calculated->AverageThermal);
		} else {
			InsertThermalHistory(Calculated->ClimbStartTime, 
				Calculated->ClimbStartLat, Calculated->ClimbStartLong, 
				Calculated->ClimbStartAlt, Calculated->NavAltitude, Calculated->AverageThermal);
		}

	}

	InitLDRotary(&rotaryLD);
	InitWindRotary(&rotaryWind);
        
        flightstats.Altitude_Ceiling.
          least_squares_update(max(0.0, Calculated->CruiseStartTime
                                   - Calculated->TakeOffTime)/3600.0,
                               Calculated->CruiseStartAlt);
        
        // Finally do the transition to cruise
        Calculated->Circling = false;
        MODE = CRUISE;
        #if DEBUGTURN
        StartupStore(_T("... WAITCRUISE -> CRUISE\n"));
        #endif
        SwitchZoomClimb(Basic, Calculated, false, LEFT);
        InputEvents::processGlideComputer(GCE_FLIGHTMODE_CRUISE);

      } // climbcruiseswitch time in range

    } else { // Rate>Minturnrate, back to climb, turning again
      #if DEBUGTURN
      StartupStore(_T("... WAITCRUISE -> CLIMB\n"));
      #endif
      MODE = CLIMB;
    }
    break;
  default:
    // error, go to cruise
    MODE = CRUISE;
  }
  // generate new wind vector if altitude changes or a new
  // estimate is available
  if (AutoWindMode>D_AUTOWIND_MANUAL && AutoWindMode <D_AUTOWIND_EXTERNAL) {
    LockFlightData();
    windanalyser->slot_Altitude(Basic, Calculated);
    UnlockFlightData();
  }

  if (EnableThermalLocator) {
    if (Calculated->Circling) {
      thermallocator.AddPoint(Basic->Time, Basic->Longitude, Basic->Latitude,
			      Calculated->NettoVario);
      thermallocator.Update(Basic->Time, Basic->Longitude, Basic->Latitude,
			    Calculated->WindSpeed, Calculated->WindBearing,
			    Basic->TrackBearing,
			    &Calculated->ThermalEstimate_Longitude,
			    &Calculated->ThermalEstimate_Latitude,
			    &Calculated->ThermalEstimate_W,
			    &Calculated->ThermalEstimate_R);
    } else {
      Calculated->ThermalEstimate_W = 0;
      Calculated->ThermalEstimate_R = -1;
      thermallocator.Reset();
    }
  }

  // update atmospheric model
  CuSonde::updateMeasurements(Basic, Calculated);

}
Example #15
0
void GlidePolar::SetBallast() {
  LockFlightData();
  double BallastWeight;
  BallastLitres = WEIGHTS[2] * BALLAST;
  BallastWeight = GetAUW();
  if (WingArea>0.1) {
    WingLoading = BallastWeight/WingArea; 
  } else {
    WingLoading = 0;
  }
  BallastWeight = (double)sqrt(BallastWeight);
  LKASSERT(BUGS!=0);
  if (BUGS==0) BUGS=1;
  double bugfactor = 1.0/BUGS;
  LKASSERT(BallastWeight!=0);
  if (BallastWeight==0) BallastWeight=1;
  polar_a = POLAR[0] / BallastWeight*bugfactor;
  polar_b = POLAR[1] * bugfactor;
  polar_c = POLAR[2] * BallastWeight*bugfactor;

  // do preliminary scan to find min sink and best LD
  // this speeds up mcready calculations because we have a reduced range
  // to search across.
  // this also limits speed to fly to logical values (will never try
  // to fly slower than min sink speed)

  minsink = 10000.0;
  bestld = 0.0;
  int i;

  // Rounding errors could make SAFTEYSPEED 0.00xx and not 0
  // Now below 3kmh we consider the speed wrong
  // MAXSAFETYSPEED WAS 200, = 720kmh!!
  if ((SAFTEYSPEED<1)||(SAFTEYSPEED>=MAXSAFETYSPEED)) {
    SAFTEYSPEED=MAXSAFETYSPEED-1;
  }
  iSAFETYSPEED=(int)SAFTEYSPEED;

  // sinkratecache is an array for m/s values!! i is the speed in m/s
  for(i=4;i<=MAXSPEED;i++)
    {
      double vtrack = (double)i; // TAS along bearing in cruise
      double thesinkrate 
        =  -SinkRate(polar_a,polar_b,polar_c,0,0,vtrack);

      LKASSERT(thesinkrate!=0);
      if (thesinkrate==0) thesinkrate=0.001;
      double ld = vtrack/thesinkrate;
      if (ld>=bestld) {
        bestld = ld;
        Vbestld = i;
      }
      if (thesinkrate<= minsink) {
        minsink = thesinkrate;
        Vminsink = i;
      }
      sinkratecache[i] = -thesinkrate;

    }
  UnlockFlightData();

}
Example #16
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;
  static short counter=0;

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

  if (doinit||!CALCULATED_INFO.TerrainValid) {
	if (counter++<3) {
		UnlockFlightData();
		return;
	}
	if (ALTITUDE==0)
		if (CALCULATED_INFO.TerrainValid) ALTITUDE= CALCULATED_INFO.TerrainAlt;
	doinit=false;
  }


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

  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*(GlidePolar::sinkratecache[(int)IASMS]);
	if (sinkias>10) sinkias=10; // set a limiter for sink rate
	// StartupStore(_T(".... ias=%.0f 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 {
		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+0.1); // rounding errors require a correction
	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;
	}
  }


  FindLatitudeLongitude(GPS_INFO.Latitude, GPS_INFO.Longitude, 
                          GPS_INFO.TrackBearing, GPS_INFO.Speed*1.0,
                          &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();
  }
Example #17
0
//
// Running every n seconds ONLY when the thermal history page is active and we are not drawing map.
// Returns true if did calculations, false if ok to use old values.
// Warning, this function is run by Draw thread.
bool DoThermalHistory(NMEA_INFO *Basic, DERIVED_INFO *Calculated)
{
   int i,k,l;
   double bearing, distance, sortvalue;
   double sortedValue[MAX_THERMAL_HISTORY+1];

   if (DoInit[MDI_DOTHERMALHISTORY]) {
	#ifdef DEBUG_THISTORY
	StartupStore(_T("... DoTHistory Init memset CopyTHistory\n"));
	#endif
	memset(CopyThermalHistory, 0, sizeof(CopyThermalHistory));
	LKNumThermals=0;
	#ifdef DEBUG_THISTORY
	StartupStore(_T("... DoTHistory Init memset LKSortedThermals\n"));
	#endif
	memset(LKSortedThermals, -1, sizeof(LKSortedThermals));
	DoInit[MDI_DOTHERMALHISTORY]=false;
	return true;
   }

   // Wait for n seconds before updating again, to avoid data change too often
   // distracting the pilot.
   static double lastRunTime=0;
   if (  lastRunTime > Basic->Time ) lastRunTime=Basic->Time;
   if (  (Basic->Time < (lastRunTime+NEARESTUPDATETIME)) && (LastDoThermalH>0)) {
        return false;
   }

   // Consider replay mode...
   if (  LastDoThermalH > Basic->Time ) LastDoThermalH=Basic->Time;
   // Dont recalculate while user is using the virtual keys
   if ( Basic->Time < (LastDoThermalH+PAGINGTIMEOUT) ) { 
	return false;
   }

   LastDoThermalH=Basic->Time;
   lastRunTime=Basic->Time;

   // Do not copy the struct while circling, we are awaiting for new thermal being inserted.
   // We should copy only once every new thermal has been updated.
   // However, since we should also consider igc replay, flight resets, etc.etc.
   // lets copy and make it short.
   if (!Calculated->Circling) {
     #ifdef DEBUG_THISTORY
     StartupStore(_T("... DoTHistory Copy CopyThermalHistory and reset LKSortedThermals\n"));
     #endif
     LockFlightData(); // No need to lock really
     memcpy(CopyThermalHistory, ThermalHistory, sizeof(ThermalHistory));
     UnlockFlightData();
   }

   memset(LKSortedThermals, -1, sizeof(LKSortedThermals));
   memset(sortedValue, -1, sizeof(sortedValue));
   LKNumThermals=0;

   for (i=0; i<MAX_THERMAL_HISTORY; i++) {
	if ( CopyThermalHistory[i].Valid != true ) continue;
	LKNumThermals++;
	DistanceBearing(Basic->Latitude, Basic->Longitude, 
		CopyThermalHistory[i].Latitude, CopyThermalHistory[i].Longitude,
		&distance, &bearing);

	double altReqd = GlidePolar::MacCreadyAltitude (MACCREADY,
                distance, bearing,
                Calculated->WindSpeed, Calculated->WindBearing,
                0, 0, true, NULL);

	// These values are available only in the mapwindow thread, i.e. in the Copy.
	CopyThermalHistory[i].Arrival= Calculated->NavAltitude + Calculated->EnergyHeight - altReqd - CopyThermalHistory[i].HBase;
	CopyThermalHistory[i].Distance=distance;
	CopyThermalHistory[i].Bearing=bearing;
   }
   if (LKNumThermals<1) return true;

   // We know there is at least one thermal
   for (i=0; i<MAX_THERMAL_HISTORY; i++) {

	if ( CopyThermalHistory[i].Valid != true ) continue;

	switch (SortedMode[MSM_THERMALS]) {
		case 0:	
			sortvalue=CopyThermalHistory[i].Time;
			// sort by highest: the highest the closer to now
			sortvalue*=-1;
			break;
		case 1:
			sortvalue=CopyThermalHistory[i].Distance;
			break;
		case 2:
			if (MapWindow::mode.Is(MapWindow::Mode::MODE_CIRCLING)) {
				sortvalue=CopyThermalHistory[i].Bearing;
				break;
			}
			sortvalue=CopyThermalHistory[i].Bearing - Basic->TrackBearing;
			if (sortvalue < -180.0) sortvalue += 360.0;
			else
				if (sortvalue > 180.0) sortvalue -= 360.0;
			if (sortvalue<0) sortvalue*=-1;
			break;
		case 3:
			sortvalue=CopyThermalHistory[i].Lift;
			// sort by highest
			sortvalue*=-1;
			break;
		case 4:
			sortvalue=CopyThermalHistory[i].Arrival;
			// sort by highest
			sortvalue*=-1;
			break;
		default:
			sortvalue=CopyThermalHistory[i].Distance;
			break;
	}

	for (k=0; k< MAXTHISTORY; k++)  {

		// if new value is lower or index position is empty,  AND index position is not itself
		if ( ((sortvalue < sortedValue[k]) || (LKSortedThermals[k]== -1))
		&& (LKSortedThermals[k] != i) )
		{
			// ok, got new lower value, put it into slot
			for (l=MAXTHISTORY-1; l>k; l--) {
				if (l>0) {
					sortedValue[l] = sortedValue[l-1];
					LKSortedThermals[l] = LKSortedThermals[l-1];
				}
			}
			sortedValue[k] = sortvalue;
			LKSortedThermals[k] = i;
			//inserted++;
			break;
		}
	} // for k
	continue;

   } // for i
   #ifdef DEBUG_THISTORY
   StartupStore(_T("... DoTHistory Sorted, LKNumThermals=%d :\n"),LKNumThermals);
   for (i=0; i<MAXTHISTORY; i++) {
	if (LKSortedThermals[i]>=0)
		StartupStore(_T("... DoTHistory LKSortedThermals[%d]=CopyThermalHistory[%d] Valid=%d Name=<%s> Lift=%.1f\n"), i, 
			LKSortedThermals[i],
			CopyThermalHistory[LKSortedThermals[i]].Valid,
			CopyThermalHistory[LKSortedThermals[i]].Name,
			CopyThermalHistory[LKSortedThermals[i]].Lift);
   }
   #endif

   return true;
}
Example #18
0
bool MapWindow::Event_NearestWaypointDetails(double lon, double lat) {

    double Dist;
    unsigned int i;
    const double range = zoom.RealScale()*500;  
    double dyn_range = std::max(5000.0, range*3.5);

    //StartupStore(_T("RANGE=%f\n"),dyn_range);
    LKSound(TEXT("LK_BELL.WAV"));

start_search:

#ifdef BUTTONS_MS
    LockFlightData();
    DistanceBearing(lat,lon, GPS_INFO.Latitude,GPS_INFO.Longitude, &Dist, NULL);
    UnlockFlightData();

    if(Dist < dyn_range) {
	#ifdef OWN_POS_MS
  	dlgAddMultiSelectListItem(NULL,0, IM_OWN_POS, Dist);
	#endif
	#ifdef ORACLE_MS
  	dlgAddMultiSelectListItem(NULL,0, IM_ORACLE, Dist);
	#endif
	#ifdef TEAM_CODE_MS
  	dlgAddMultiSelectListItem(NULL,0, IM_TEAM, Dist);
	#endif
    }
#endif	// BUTTONS_MS


    for(size_t i=NUMRESWP;i<WayPointList.size();++i) {    // Consider only valid markers
        if ((WayPointCalc[i].WpType==WPT_AIRPORT)|| (WayPointCalc[i].WpType==WPT_OUTLANDING)) {
            DistanceBearing(lat,lon, WayPointList[i].Latitude, WayPointList[i].Longitude, &Dist, NULL);
            if(Dist < dyn_range) {
    	        dlgAddMultiSelectListItem(NULL,i, IM_WAYPOINT, Dist);
            }
        }
    }

#ifdef FLARM_MS
    if((MapWindow::mode.Is(MapWindow::Mode::MODE_PAN) || MapWindow::mode.Is(MapWindow::Mode::MODE_TARGET_PAN))) {
        LastDoTraffic=0;
        DoTraffic(&DrawInfo,&DerivedDrawInfo);
        for (unsigned i=0; i<FLARM_MAX_TRAFFIC; ++i) {
	    if (LKTraffic[i].Status != LKT_EMPTY) {
                DistanceBearing(lat,lon, LKTraffic[i].Latitude, LKTraffic[i].Longitude, &Dist, NULL);
                if(Dist < range) {
    	            dlgAddMultiSelectListItem((long*)&LKTraffic[i],i, IM_FLARM, Dist);
                }
            }
        }
    }
#endif

    int  HorDist=0, Bearing=0, VertDist=0; 
    CAirspaceList reslist = CAirspaceManager::Instance().GetNearAirspacesAtPoint(lon, lat, (int)(dyn_range/2));

    for (CAirspaceList::const_iterator it = reslist.begin(); it != reslist.end(); ++it) {
        LKASSERT((*it));
        (*it)->CalculateDistance(&HorDist, &Bearing, &VertDist,lon, lat);
        dlgAddMultiSelectListItem((long*) (*it),0, IM_AIRSPACE, HorDist);
    }


    for(i=1;i<WayPointList.size();i++) {    // Consider only valid markers
        if ((WayPointCalc[i].WpType != WPT_AIRPORT)|| (WayPointCalc[i].WpType != WPT_OUTLANDING)) {
            DistanceBearing(lat,lon, WayPointList[i].Latitude, WayPointList[i].Longitude, &Dist, NULL);
            if(Dist < (dyn_range)) {
    	        dlgAddMultiSelectListItem(NULL,i, IM_WAYPOINT, Dist);
            }
        }
    }


    // TASK MULTISELECT
    int SecType= DAe;
    double SecRadius =0;
    double Bear=0;
    bool Angleinside = false;

    LockTaskData();
    for(i=0; ValidTaskPoint(i); i++) {
        LKASSERT(Task[i].Index <=(int)WayPointList.size());
        DistanceBearing(lat,lon,
            WayPointList[Task[i].Index].Latitude,
            WayPointList[Task[i].Index].Longitude, &Dist, &Bear);

        GetTaskSectorParameter(i, &SecType, &SecRadius);

        Angleinside = true;
        if( SecRadius < (dyn_range)) SecRadius =dyn_range;

        if((Dist < SecRadius) && Angleinside) {
  	    dlgAddMultiSelectListItem(NULL,i, IM_TASK_PT, Dist);
        }
    }
    UnlockTaskData();

    #if (WINDOWSPC>0)
    if (EnableSoundModes) Poco::Thread::sleep(1000); // let the sound be heard in sequence
    #endif
    if(dlgGetNoElements() ==0) { 
        if(dyn_range < 120000) {
            dyn_range *=2;
            goto start_search;
        } else {
            DoStatusMessage(gettext(TEXT("_@M2248_")));  // _@M2248_  "No Near Object found!"
        }
    } else {
        LKSound(TEXT("LK_GREEN.WAV"));
        dlgMultiSelectListShowModal();
        if(ValidTaskPoint(PanTaskEdit)) {
            MapWindow::Event_Pan(1);
        }
        return true;
    }

    return false;
}
Example #19
0
void CDevCProbe::Update() {
	TCHAR Temp[50] = {0};

	LockFlightData();
	NMEA_INFO _INFO = GPS_INFO;
	UnlockFlightData();

	LockDeviceData();
	_stprintf(Temp, TEXT("C-Probe - Version: %s"), m_szVersion);
	UnlockDeviceData();

	m_wf->SetCaption(Temp);

	WndProperty* wp;
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpPitch"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f%s"), _INFO.Pitch, gettext(_T("_@M2179_")));
		wp->SetText(Temp);
	}
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpHeading"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f%s"), _INFO.MagneticHeading, gettext(_T("_@M2179_")));
		wp->SetText(Temp);
	}
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpRoll"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f%s"), _INFO.Roll, gettext(_T("_@M2179_")));
		wp->SetText(Temp);
	}

	wp = (WndProperty*)m_wf->FindByName(TEXT("prpGx"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f"), _INFO.AccelX);
		wp->SetText(Temp);
	}
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpGy"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f"), _INFO.AccelY);
		wp->SetText(Temp);
	}
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpGz"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f"), _INFO.AccelZ);
		wp->SetText(Temp);
	}

	wp = (WndProperty*)m_wf->FindByName(TEXT("prpTemp"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f %sC"), _INFO.OutsideAirTemperature, gettext(_T("_@M2179_")));
		wp->SetText(Temp);
	}
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpRh"));
	if(wp){
		_stprintf(Temp, TEXT("%.2f %%"), _INFO.RelativeHumidity);
		wp->SetText(Temp);
	}
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpDeltaPress"));
	if(wp){
		LockDeviceData();
		_stprintf(Temp, TEXT("%.2f Pa"), m_delta_press);
		UnlockDeviceData();

		wp->SetText(Temp);
	}
	wp = (WndProperty*)m_wf->FindByName(TEXT("prpAbsPress"));
	if(wp){
		LockDeviceData();
		_stprintf(Temp, TEXT("%.2f hPa"), m_abs_press);
		UnlockDeviceData();

		wp->SetText(Temp);
	}
}
Example #20
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();
}