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); }
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 }
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(); } }
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(); }
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(); }
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; }
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; }
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; }
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; }
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); }
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 }
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; }
// // 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(); }
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); }
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(); }
// // 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(); }
// // 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; }
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; }
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); } }
// // 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(); }