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