BOOL NMEAParser::PFLAA(TCHAR *String, TCHAR **params, size_t nparams, NMEA_INFO *pGPS) { int flarm_slot = 0; isFlarm = true; // 5 id, 6 digit hex long ID; swscanf(params[5],TEXT("%lx"), &ID); // unsigned long uID = ID; flarm_slot = FLARM_FindSlot(pGPS, ID); if (flarm_slot<0) { // no more slots available, #ifdef DEBUG_LKT StartupStore(_T("... NO SLOTS for Flarm traffic, too many ids!%s"),NEWLINE); #endif return FALSE; } // before changing timefix, see if it was an old target back locked in! CheckBackTarget(pGPS, flarm_slot); // and then set time of fix to current time pGPS->FLARM_Traffic[flarm_slot].Time_Fix = pGPS->Time; TCHAR nString[MAX_NMEA_LEN+1]; unsigned int i, j; for (i=0, j=0; i<_tcslen(String); i++) { // if not a comma, copy and proceed if (String[i] != _T(',')) { nString[j++]=String[i]; continue; } // there was a comma, but the next one is not a comma, so ok.. if (String[i+1] != _T(',') ) { nString[j++]=String[i]; continue; } // We have a bad ,, case that scanf cannot bear with, so we add a 0 nString[j++] = String[i]; nString[j++] = _T('0'); } nString[j]=_T('\0'); //#ifdef DEBUG_LKT //StartupStore(_T("PFLAA: %s%s"),nString,NEWLINE); //#endif _stscanf(nString, TEXT("%hu,%lf,%lf,%lf,%hu,%lx,%lf,%lf,%lf,%lf,%hu"), &pGPS->FLARM_Traffic[flarm_slot].AlarmLevel, // unsigned short 0 &pGPS->FLARM_Traffic[flarm_slot].RelativeNorth, // 1 &pGPS->FLARM_Traffic[flarm_slot].RelativeEast, // 2 &pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude, // 3 &pGPS->FLARM_Traffic[flarm_slot].IDType, // unsigned short 4 &pGPS->FLARM_Traffic[flarm_slot].ID, // 6 char hex &pGPS->FLARM_Traffic[flarm_slot].TrackBearing, // double 6 &pGPS->FLARM_Traffic[flarm_slot].TurnRate, // double 7 &pGPS->FLARM_Traffic[flarm_slot].Speed, // double 8 m/s &pGPS->FLARM_Traffic[flarm_slot].ClimbRate, // double 9 m/s &pGPS->FLARM_Traffic[flarm_slot].Type); // unsigned short 10 // 1 relativenorth, meters pGPS->FLARM_Traffic[flarm_slot].Latitude = pGPS->FLARM_Traffic[flarm_slot].RelativeNorth *FLARM_NorthingToLatitude + pGPS->Latitude; // 2 relativeeast, meters pGPS->FLARM_Traffic[flarm_slot].Longitude = pGPS->FLARM_Traffic[flarm_slot].RelativeEast *FLARM_EastingToLongitude + pGPS->Longitude; // we need to compare with BARO altitude FLARM relative Alt difference! if (pGPS->BaroAltitude>0) // just to be sure pGPS->FLARM_Traffic[flarm_slot].Altitude = pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude + pGPS->BaroAltitude; else pGPS->FLARM_Traffic[flarm_slot].Altitude = pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude + pGPS->Altitude; pGPS->FLARM_Traffic[flarm_slot].Average30s = flarmCalculations.Average30s( pGPS->FLARM_Traffic[flarm_slot].ID, pGPS->Time, pGPS->FLARM_Traffic[flarm_slot].Altitude); TCHAR *name = pGPS->FLARM_Traffic[flarm_slot].Name; //TCHAR *cn = pGPS->FLARM_Traffic[flarm_slot].Cn; // If there is no name yet, or if we have a pending update event.. if (!_tcslen(name) || pGPS->FLARM_Traffic[flarm_slot].UpdateNameFlag ) { #ifdef DEBUG_LKT if (pGPS->FLARM_Traffic[flarm_slot].UpdateNameFlag ) { StartupStore(_T("... UpdateNameFlag for slot %d\n"),flarm_slot); } else { StartupStore(_T("... First lookup name for slot %d\n"),flarm_slot); } #endif pGPS->FLARM_Traffic[flarm_slot].UpdateNameFlag=false; // clear flag first TCHAR *fname = LookupFLARMDetails(pGPS->FLARM_Traffic[flarm_slot].ID); if (fname) { LK_tcsncpy(name,fname,MAXFLARMNAME); // Now we have the name, so lookup also for the Cn // This will return either real Cn or Name, again TCHAR *cname = LookupFLARMCn(pGPS->FLARM_Traffic[flarm_slot].ID); if (cname) { int cnamelen=_tcslen(cname); if (cnamelen<=MAXFLARMCN) { _tcscpy( pGPS->FLARM_Traffic[flarm_slot].Cn, cname); } else { // else probably it is the Name again, and we create a fake Cn pGPS->FLARM_Traffic[flarm_slot].Cn[0]=cname[0]; pGPS->FLARM_Traffic[flarm_slot].Cn[1]=cname[cnamelen-2]; pGPS->FLARM_Traffic[flarm_slot].Cn[2]=cname[cnamelen-1]; pGPS->FLARM_Traffic[flarm_slot].Cn[3]=_T('\0'); } } else { _tcscpy( pGPS->FLARM_Traffic[flarm_slot].Cn, _T("Err")); } #ifdef DEBUG_LKT StartupStore(_T("... PFLAA Name to FlarmSlot=%d ID=%lx Name=<%s> Cn=<%s>\n"), flarm_slot, pGPS->FLARM_Traffic[flarm_slot].ID, pGPS->FLARM_Traffic[flarm_slot].Name, pGPS->FLARM_Traffic[flarm_slot].Cn); #endif } else { // Else we NEED to set a name, otherwise it will constantly search for it over and over.. name[0]=_T('?'); name[1]=_T('\0'); pGPS->FLARM_Traffic[flarm_slot].Cn[0]=_T('?'); pGPS->FLARM_Traffic[flarm_slot].Cn[1]=_T('\0'); #ifdef DEBUG_LKT StartupStore(_T("... New FlarmSlot=%d ID=%lx with no name, assigned a \"?\"\n"), flarm_slot, pGPS->FLARM_Traffic[flarm_slot].ID); #endif } } #ifdef DEBUG_LKT StartupStore(_T("... PFLAA pGPS slot=%d ID=%lx name=<%s> cn=<%s> rAlt=%.0f Track=%.0f Speed=%.0f Climb=%.1f Baro=%f FlAlt=%f\n"), flarm_slot, pGPS->FLARM_Traffic[flarm_slot].ID, pGPS->FLARM_Traffic[flarm_slot].Name, pGPS->FLARM_Traffic[flarm_slot].Cn, pGPS->FLARM_Traffic[flarm_slot].RelativeAltitude, pGPS->FLARM_Traffic[flarm_slot].TrackBearing, pGPS->FLARM_Traffic[flarm_slot].Speed, pGPS->FLARM_Traffic[flarm_slot].ClimbRate, pGPS->BaroAltitude, pGPS->FLARM_Traffic[flarm_slot].Altitude); #endif // update Virtual Waypoint for target FLARM if (flarm_slot == LKTargetIndex) { WayPointList[RESWP_FLARMTARGET].Latitude = pGPS->FLARM_Traffic[LKTargetIndex].Latitude; WayPointList[RESWP_FLARMTARGET].Longitude = pGPS->FLARM_Traffic[LKTargetIndex].Longitude; WayPointList[RESWP_FLARMTARGET].Altitude = pGPS->FLARM_Traffic[LKTargetIndex].Altitude; } return FALSE; }
// // The purpose of simulating flarm traffic is NOT to play a videogame against flarm objects: // we only need some traffic to display for testing on ground during simulations. // // >>>>> This is accessing directly the GPS_INFO main struct, writing inside it. <<<<< // Called by LKSimulator, already locking FlightData . No worry. // void SimFlarmTraffic(long ID, double offset) { int flarm_slot = 0; bool newtraffic=false; GPS_INFO.FLARM_Available=true; LastFlarmCommandTime=GPS_INFO.Time; // useless really, we dont call UpdateMonitor from SIM flarm_slot = FLARM_FindSlot(&GPS_INFO, ID); if (flarm_slot<0) return; if ( GPS_INFO.FLARM_Traffic[flarm_slot].Status == LKT_EMPTY) { newtraffic=true; } // before changing timefix, see if it was an old target back locked in! CheckBackTarget(&GPS_INFO, flarm_slot); // and then set time of fix to current time GPS_INFO.FLARM_Traffic[flarm_slot].Time_Fix = GPS_INFO.Time; /* TEXT("%hu,%lf,%lf,%lf,%hu,%lx,%lf,%lf,%lf,%lf,%hu"), &GPS_INFO.FLARM_Traffic[flarm_slot].AlarmLevel, // unsigned short 0 &GPS_INFO.FLARM_Traffic[flarm_slot].RelativeNorth, // 1 &GPS_INFO.FLARM_Traffic[flarm_slot].RelativeEast, // 2 &GPS_INFO.FLARM_Traffic[flarm_slot].RelativeAltitude, // 3 &GPS_INFO.FLARM_Traffic[flarm_slot].IDType, // unsigned short 4 &GPS_INFO.FLARM_Traffic[flarm_slot].ID, // 6 char hex &GPS_INFO.FLARM_Traffic[flarm_slot].TrackBearing, // double 6 &GPS_INFO.FLARM_Traffic[flarm_slot].TurnRate, // double 7 &GPS_INFO.FLARM_Traffic[flarm_slot].Speed, // double 8 m/s &GPS_INFO.FLARM_Traffic[flarm_slot].ClimbRate, // double 9 m/s &GPS_INFO.FLARM_Traffic[flarm_slot].Type); // unsigned short 10 */ // If first time seen this traffic, place it nearby if ( newtraffic ) { GPS_INFO.FLARM_Traffic[flarm_slot].RelativeNorth=2; GPS_INFO.FLARM_Traffic[flarm_slot].RelativeEast=2; GPS_INFO.FLARM_Traffic[flarm_slot].Latitude = SimNewCoordinate(GPS_INFO.Latitude, offset); GPS_INFO.FLARM_Traffic[flarm_slot].Longitude = SimNewCoordinate(GPS_INFO.Longitude,offset); GPS_INFO.FLARM_Traffic[flarm_slot].Altitude = SimNewAltitude(GPS_INFO.Altitude); GPS_INFO.FLARM_Traffic[flarm_slot].TrackBearing= (double) rand()/91.276; GPS_INFO.FLARM_Traffic[flarm_slot].AlarmLevel=0; GPS_INFO.FLARM_Traffic[flarm_slot].ID=ID; GPS_INFO.FLARM_Traffic[flarm_slot].TurnRate=0; GPS_INFO.FLARM_Traffic[flarm_slot].Speed= SimNewSpeed(GPS_INFO.Speed); GPS_INFO.FLARM_Traffic[flarm_slot].Status = LKT_REAL; } else { GPS_INFO.FLARM_Traffic[flarm_slot].Latitude += (double)(rand()/20000000.0)*(rand()>15000?1:-1); GPS_INFO.FLARM_Traffic[flarm_slot].Longitude += (double)(rand()/20000000.0)*(rand()>15000?1:-1); GPS_INFO.FLARM_Traffic[flarm_slot].Altitude += (double)(rand()/2200.0)*(rand()>15000?1:-1); } GPS_INFO.FLARM_Traffic[flarm_slot].RelativeAltitude = GPS_INFO.FLARM_Traffic[flarm_slot].Altitude - GPS_INFO.Altitude; // GPS_INFO.FLARM_Traffic[flarm_slot].Average30s = flarmCalculations.Average30s( GPS_INFO.FLARM_Traffic[flarm_slot].ID, GPS_INFO.Time, GPS_INFO.FLARM_Traffic[flarm_slot].Altitude); TCHAR *name = GPS_INFO.FLARM_Traffic[flarm_slot].Name; //TCHAR *cn = GPS_INFO.FLARM_Traffic[flarm_slot].Cn; // If there is no name yet, or if we have a pending update event.. if (!_tcslen(name) || GPS_INFO.FLARM_Traffic[flarm_slot].UpdateNameFlag ) { #ifdef DEBUG_SIMLKT if (GPS_INFO.FLARM_Traffic[flarm_slot].UpdateNameFlag ) { StartupStore(_T("... UpdateNameFlag for slot %d\n"),flarm_slot); } else { StartupStore(_T("... First lookup name for slot %d\n"),flarm_slot); } #endif GPS_INFO.FLARM_Traffic[flarm_slot].UpdateNameFlag=false; // clear flag first TCHAR *fname = LookupFLARMDetails(GPS_INFO.FLARM_Traffic[flarm_slot].ID); if (fname) { LK_tcsncpy(name,fname,MAXFLARMNAME); // Now we have the name, so lookup also for the Cn // This will return either real Cn or Name, again TCHAR *cname = LookupFLARMCn(GPS_INFO.FLARM_Traffic[flarm_slot].ID); if (cname) { int cnamelen=_tcslen(cname); if (cnamelen<=MAXFLARMCN) { _tcscpy( GPS_INFO.FLARM_Traffic[flarm_slot].Cn, cname); } else { // else probably it is the Name again, and we create a fake Cn GPS_INFO.FLARM_Traffic[flarm_slot].Cn[0]=cname[0]; GPS_INFO.FLARM_Traffic[flarm_slot].Cn[1]=cname[cnamelen-2]; GPS_INFO.FLARM_Traffic[flarm_slot].Cn[2]=cname[cnamelen-1]; GPS_INFO.FLARM_Traffic[flarm_slot].Cn[3]=_T('\0'); } } else { _tcscpy( GPS_INFO.FLARM_Traffic[flarm_slot].Cn, _T("Err")); } #ifdef DEBUG_SIMLKT StartupStore(_T("... PFLAA Name to FlarmSlot=%d ID=%lx Name=<%s> Cn=<%s>\n"), flarm_slot, GPS_INFO.FLARM_Traffic[flarm_slot].ID, GPS_INFO.FLARM_Traffic[flarm_slot].Name, GPS_INFO.FLARM_Traffic[flarm_slot].Cn); #endif } else { // Else we NEED to set a name, otherwise it will constantly search for it over and over.. name[0]=_T('?'); name[1]=_T('\0'); GPS_INFO.FLARM_Traffic[flarm_slot].Cn[0]=_T('?'); GPS_INFO.FLARM_Traffic[flarm_slot].Cn[1]=_T('\0'); #ifdef DEBUG_SIMLKT StartupStore(_T("... New FlarmSlot=%d ID=%lx with no name, assigned a \"?\"\n"), flarm_slot, GPS_INFO.FLARM_Traffic[flarm_slot].ID); #endif } } // update Virtual Waypoint for target FLARM if (flarm_slot == LKTargetIndex) { WayPointList[RESWP_FLARMTARGET].Latitude = GPS_INFO.FLARM_Traffic[LKTargetIndex].Latitude; WayPointList[RESWP_FLARMTARGET].Longitude = GPS_INFO.FLARM_Traffic[LKTargetIndex].Longitude; WayPointList[RESWP_FLARMTARGET].Altitude = GPS_INFO.FLARM_Traffic[LKTargetIndex].Altitude; } }
// // LK8000 SS1 = Soaring Simulator V1 by Paolo Ventafridda // Still basic, but usable // void LKSimulator(void) { LockFlightData(); // GPS_INFO.NAVWarning = false; GPS_INFO.SatellitesUsed = 6; // Even on ground, we can turn the glider in the hangar BEARING += SimTurn; if (BEARING<0) BEARING+=360; else if (BEARING>359) BEARING-=360; #if SIMLANDING static bool crashed=false, landedwarn=true; #endif static bool doinit=true, landing=false, stallwarn=true, circling=false, flarmwasinit=false; static short counter=0; double tdistance, tbearing; double thermalstrength=0, sinkstrength=0; extern void SimFlarmTraffic(long id, double offset); if (doinit) { if (counter++<4) { UnlockFlightData(); return; } #if TESTBENCH StartupStore(_T(". SIMULATOR: real init%s"),NEWLINE); #endif // Add a couple of thermals for the boys InsertThermalHistory(GPS_INFO.Time-1887, GPS_INFO.Latitude-0.21, GPS_INFO.Longitude+0.13, 873, 1478,1.5); InsertThermalHistory(GPS_INFO.Time-1250, GPS_INFO.Latitude+0.15, GPS_INFO.Longitude-0.19, 991, 1622,0.9); InsertThermalHistory(GPS_INFO.Time-987, GPS_INFO.Latitude-0.11, GPS_INFO.Longitude+0.13, 762, 1367,1.8); InsertThermalHistory(GPS_INFO.Time-100, GPS_INFO.Latitude-0.02, GPS_INFO.Longitude-0.03, 650, 1542,2.2); WayPointList[RESWP_LASTTHERMAL].Latitude = GPS_INFO.Latitude-0.022; WayPointList[RESWP_LASTTHERMAL].Longitude = GPS_INFO.Longitude-0.033; WayPointList[RESWP_LASTTHERMAL].Altitude = 650; ThLatitude=GPS_INFO.Latitude-0.022; ThLongitude=GPS_INFO.Longitude-0.033; if (EnableFLARMMap) { srand( Poco::Timestamp().epochTime()); SimFlarmTraffic(0xdd8951,22.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8944,31.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a43,16.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a42,41.0+(double)(rand() % 32)); /* SimFlarmTraffic(0xdd8a11,11.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a12,21.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a13,31.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a14,41.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a15,51.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a16,61.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a17,71.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a18,81.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a19,91.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a10,01.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a21,21.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a22,22.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a23,23.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a24,24.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a25,25.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a26,26.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a27,27.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a28,28.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a29,29.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a20,31.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a31,32.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a32,33.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a33,34.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a34,35.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a35,36.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a36,37.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a37,41.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a38,42.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a39,43.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a30,44.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a41,45.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a42,46.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a43,47.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a44,48.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a45,49.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a46,01.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a47,02.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a48,03.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a49,04.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a50,81.1+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a51,82.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a52,83.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a53,84.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a54,85.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a55,86.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a56,87.0+(double)(rand() % 32)); */ } doinit=false; } // First Aircraft min altitude is at ground level if (ALTITUDE==0) if (CALCULATED_INFO.TerrainValid) ALTITUDE= CALCULATED_INFO.TerrainAlt; if (ISGAAIRCRAFT) { // todo: fuel consumption, engine efficiency etc. } // We cannot use doinit for flarm, because it could be enabled from configuration AFTER startup, // and it must work all the way the same in order not to confuse users. if (EnableFLARMMap) { if (!flarmwasinit) { srand(Poco::Timestamp().epochTime()); // Add a poker of traffic for the boys SimFlarmTraffic(0xdd8951,22.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8944,31.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a43,16.0+(double)(rand() % 32)); SimFlarmTraffic(0xdd8a42,41.0+(double)(rand() % 32)); DoStatusMessage(gettext(TEXT("_@M279_"))); // FLARM DETECTED (in sim) flarmwasinit=true; } else { // Let one of the objects be a ghost and a zombie, and keep the rest real SimFlarmTraffic(0xdd8951,0); SimFlarmTraffic(0xdd8944,0); SimFlarmTraffic(0xdd8a43,0); // update relative altitude for ghost/zombie traffic extern int FLARM_FindSlot(NMEA_INFO *GPS_INFO, long Id); int flarmslot=FLARM_FindSlot(&GPS_INFO, 0xdd8a42); if (flarmslot>=0) GPS_INFO.FLARM_Traffic[flarmslot].RelativeAltitude = GPS_INFO.FLARM_Traffic[flarmslot].Altitude - GPS_INFO.Altitude; } } if (ISPARAGLIDER || ISGLIDER) { // SetBallast is calculating sinkratecache for values starting from 4 to MAXSPEED, in m/s . // ONLY during flight, we will sink in the air if (FLYING && (IASMS>3) && (IASMS<MAXSPEED) ) { double sinkias=-1*AirDensitySinkRate(IASMS, GPS_INFO.Altitude); if (sinkias>10) sinkias=10; // set a limiter for sink rate // StartupStore(_T(".... ias=%.1f sinkias=%.3f oldAlt=%.3f newAlt=%.3f\n"), // CALCULATED_INFO.IndicatedAirspeedEstimated*TOKPH, sinkias, GPS_INFO.Altitude, GPS_INFO.Altitude-sinkias); double simlift=0; if (THERMALLING == TRUE) { // entering the thermal mode right now if (!circling) { circling=true; DistanceBearing(GPS_INFO.Latitude,GPS_INFO.Longitude,ThLatitude,ThLongitude,&tdistance,&tbearing); if (tdistance>1000) { // a new thermal ThLatitude=GPS_INFO.Latitude; // we mark the new thermal ThLongitude=GPS_INFO.Longitude; ALTITUDE+=simlift; // sink rate adjusted later } else { // start circling near the old thermal } } else { // already thermalling } // ALTITUDE+=simlift+GlidePolar::minsink; } else { sinkias -= SimNettoVario; if (circling) { // we were circling, now leaving the thermal circling=false; } else { // not circling, already cruising } } // Are we near the thermal? DistanceBearing(GPS_INFO.Latitude,GPS_INFO.Longitude,ThLatitude,ThLongitude,&tdistance,&tbearing); thermalstrength=4; // m/s ThermalRadius=200; // we assume a perfect thermal, a circle of this diameter. Stronger in the center. // thermalbase sinkstrength=2; // how intense is the fallout of the thermal SinkRadius=150; // circular ring of the fallout if (tdistance>=ThermalRadius && tdistance<(ThermalRadius+SinkRadius) ) { // we are in the sinking zone of the thermal.. simlift= sinkstrength- ((tdistance-ThermalRadius)/SinkRadius)*sinkstrength; simlift+=0.1; // adjust rounding errors simlift*=-1; //StartupStore(_T(".. sinking zone: dist=%.1f sink=%.1f\n"), tdistance,simlift); } if (tdistance<ThermalRadius) { // we are in the lift zone simlift= thermalstrength- (tdistance/ThermalRadius)*thermalstrength; simlift+=0.1; // adjust rounding errors //StartupStore(_T(".. climbing zone: dist=%.1f climb=%.1f\n"), tdistance,simlift); } // Update altitude with the lift or sink, ALTITUDE+=simlift; // Update the new altitude with the natural sink, but not going lower than 0 ALTITUDE-=sinkias; if (ALTITUDE<=0) ALTITUDE=0; #if SIMLANDING if (CALCULATED_INFO.TerrainValid && (CALCULATED_INFO.AltitudeAGL <=20) ) { if (IAS <= (MINSPEED+3)) landing=true; else { // we dont simulate crashing. LK8000 pilots never crash. crashed=true; } } if (CALCULATED_INFO.TerrainValid && (CALCULATED_INFO.AltitudeAGL >100) ) { landing=false; } if (!landing && CALCULATED_INFO.TerrainValid && (CALCULATED_INFO.AltitudeAGL <=0) ) { GPS_INFO.Speed=0; landing=true; if (landedwarn) { DoStatusMessage(_T("YOU HAVE LANDED")); landedwarn=false; } } else landedwarn=true; #endif } } // Glider/Paragliders if (FLYING) { // simple stall at 1 G if (!landing && (IAS<=STALLSPEED && IASMS>3)) { if (stallwarn) { // DoStatusMessage(_T("STALLING")); // OK, people do not like stalling. stallwarn=false; } #if 0 // DO NOT SIMULATE STALLING NOW // GPS_INFO.Speed= (GlidePolar::Vminsink*0.85)+1; ALTITUDE-=20; if (ALTITUDE<=0) ALTITUDE=0; #endif } else stallwarn=true; #if SIMLANDING if (landing || IASMS<4) { GPS_INFO.Speed-=GPS_INFO.Speed*0.2; } if (crashed) { GPS_INFO.Speed=0; } #endif if (GS<0) { GPS_INFO.Speed=0; } } if (GS>0) { FindLatitudeLongitude(GPS_INFO.Latitude, GPS_INFO.Longitude, GPS_INFO.TrackBearing, GPS_INFO.Speed, &GPS_INFO.Latitude, &GPS_INFO.Longitude); } GPS_INFO.Time+= 1.0; long tsec = (long)GPS_INFO.Time; GPS_INFO.Hour = tsec/3600; GPS_INFO.Minute = (tsec-GPS_INFO.Hour*3600)/60; GPS_INFO.Second = (tsec-GPS_INFO.Hour*3600-GPS_INFO.Minute*60); UnlockFlightData(); }