84# pragma warning( disable: 4355 )
97#include <simgear/constants.h>
98#include <simgear/sg_inlines.h>
99#include <simgear/debug/logstream.hxx>
100#include <simgear/math/sg_geodesy.hxx>
101#include <simgear/sound/soundmgr.hxx>
102#include <simgear/sound/sample_group.hxx>
103#include <simgear/structure/exception.hxx>
129const TCAS::SensitivityLevel
130TCAS::ThreatDetector::sensitivityLevels[] = {
131 { 1000, 2, {20, 0.30, 850}, {0, 0, 0 }},
132 { 2350, 3, {25, 0.33, 850}, {15, 0.20, 300}},
133 { 5000, 4, {30, 0.48, 850}, {20, 0.35, 300}},
134 {10000, 5, {40, 0.75, 850}, {25, 0.55, 350}},
135 {20000, 6, {45, 1.00, 850}, {30, 0.80, 400}},
136 {42000, 7, {48, 1.30, 850}, {35, 1.10, 600}},
137 {0, 8, {48, 1.30, 1200}, {35, 1.10, 700}}
144#define ADD_VOICE(Var, Sample, SayTwice) \
145 { make_voice(&Var); \
146 append(Var, Sample); \
147 if (SayTwice) append(Var, Sample); }
149#define AVAILABLE_RA(Options, Advisory) (Advisory == (Advisory & Options))
151#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
154relAngle(
float Heading1,
float Heading2)
156 Heading1 -= Heading2;
158 while (Heading1 >= 360.0)
161 while (Heading1 < 0.0)
171 double &rangeNm,
double &bearing)
174 double az2, distanceM;
175 geo_inverse_wgs_84(lat1, lon1, lat2, lon2, &bearing, &az2, &distanceM);
176 rangeNm = distanceM * SG_METER_TO_NM;
184TCAS::VoicePlayer::init(
void)
188 ADD_VOICE(Voices.pTrafficTraffic,
"traffic",
true);
189 ADD_VOICE(Voices.pClear,
"clear",
false);
191 ADD_VOICE(Voices.pClimbNow,
"climb_now",
true);
192 ADD_VOICE(Voices.pClimbCrossing,
"climb_crossing",
true);
193 ADD_VOICE(Voices.pClimbIncrease,
"increase_climb",
false);
194 ADD_VOICE(Voices.pDescend,
"descend",
true);
195 ADD_VOICE(Voices.pDescendNow,
"descend_now",
true);
196 ADD_VOICE(Voices.pDescendCrossing,
"descend_crossing",
true);
197 ADD_VOICE(Voices.pDescendIncrease,
"increase_descent",
false);
198 ADD_VOICE(Voices.pAdjustVSpeed,
"adjust_vertical_speed",
false);
199 ADD_VOICE(Voices.pMaintVSpeed,
"maintain_vertical_speed",
false);
200 ADD_VOICE(Voices.pMonitorVSpeed,
"monitor_vertical_speed",
false);
201 ADD_VOICE(Voices.pLevelOff,
"level_off",
false);
202 ADD_VOICE(Voices.pTestOk,
"test_ok",
false);
203 ADD_VOICE(Voices.pTestFail,
"test_fail",
false);
205 speaker.update_configuration();
212TCAS::Annunciator::Annunciator(
TCAS* _tcas) :
220void TCAS::Annunciator::clear(
void)
222 previous.threatLevel = ThreatNone;
223 previous.RA = AdvisoryClear;
224 previous.RAOption = OptionNone;
229TCAS::Annunciator::bind(SGPropertyNode* node)
231 voicePlayer.bind(node,
"Sounds/tcas/female/");
235TCAS::Annunciator::init(
void)
238 nodeGpwsAlertOn =
fgGetNode(
"/instrumentation/mk-viii/outputs/discretes/audio-on",
true);
243TCAS::Annunciator::update(
void)
245 voicePlayer.update();
250 if (nodeGpwsAlertOn->getBoolValue())
253 voicePlayer.resume();
258TCAS::Annunciator::trigger(
const ResolutionAdvisory& current,
bool revertedRA)
261 int RAOption = current.RAOption;
263 if (RA == AdvisoryClear)
265 if (previous.RA != AdvisoryClear)
274 if ((previous.RA == AdvisoryClear)||
275 (tcas->tracker.newTraffic()))
286 pVoice = voicePlayer.Voices.pClimbNow;
289 pVoice = voicePlayer.Voices.pClimbIncrease;
292 pVoice = voicePlayer.Voices.pClimbCrossing;
294 pVoice = voicePlayer.Voices.pClimb;
297 case AdvisoryDescend:
299 pVoice = voicePlayer.Voices.pDescendNow;
302 pVoice = voicePlayer.Voices.pDescendIncrease;
305 pVoice = voicePlayer.Voices.pDescendCrossing;
307 pVoice = voicePlayer.Voices.pDescend;
310 case AdvisoryAdjustVSpeed:
311 pVoice = voicePlayer.Voices.pAdjustVSpeed;
314 case AdvisoryMaintVSpeed:
315 pVoice = voicePlayer.Voices.pMaintVSpeed;
318 case AdvisoryMonitorVSpeed:
319 pVoice = voicePlayer.Voices.pMonitorVSpeed;
322 case AdvisoryLevelOff:
323 pVoice = voicePlayer.Voices.pLevelOff;
326 case AdvisoryIntrusion:
330 RA = AdvisoryIntrusion;
336 if ((pLastVoice == pVoice)&&
337 (!tcas->tracker.newTraffic()))
344 voicePlayer.
play(pVoice);
346#ifdef FEATURE_TCAS_DEBUG_ANNUNCIATOR
347 cout <<
"Annunciating TCAS RA " << RA << endl;
352TCAS::Annunciator::test(
bool testOk)
355 voicePlayer.play(voicePlayer.Voices.pTestOk);
357 voicePlayer.play(voicePlayer.Voices.pTestFail);
364TCAS::AdvisoryCoordinator::AdvisoryCoordinator(
TCAS* _tcas) :
372TCAS::AdvisoryCoordinator::init(
void)
378TCAS::AdvisoryCoordinator::reinit(
void)
385TCAS::AdvisoryCoordinator::bind(SGPropertyNode* node)
387 nodeTAWarning = node->getNode(
"outputs/traffic-alert",
true);
388 nodeTAWarning->setBoolValue(
false);
392TCAS::AdvisoryCoordinator::clear(
void)
394 current.threatLevel = ThreatNone;
395 current.RA = AdvisoryClear;
396 current.RAOption = OptionNone;
401TCAS::AdvisoryCoordinator::add(
const ResolutionAdvisory& newAdvisory)
403 if ((newAdvisory.RA == AdvisoryClear)||
404 (newAdvisory.threatLevel < current.threatLevel))
407 if (current.threatLevel == newAdvisory.threatLevel)
410 current.RA &= newAdvisory.RA;
412 current.RAOption |= newAdvisory.RAOption;
416 current = newAdvisory;
422TCAS::AdvisoryCoordinator::update(
int mode)
424 bool revertedRA =
false;
427 if (current.RA == AdvisoryClear)
430 if ((previous.RA != AdvisoryClear)&&
431 (currentTime - lastTATime < 5.0))
438#ifdef FEATURE_TCAS_DEBUG_COORDINATOR
439 cout <<
"TCAS::Annunciator::update: previous: " << previous.RA <<
", new: " << current.RA << endl;
442 lastTATime = currentTime;
443 if ((previous.RA == AdvisoryClear)||
444 (previous.RA == AdvisoryIntrusion)||
445 ((current.RA & previous.RA) != previous.RA))
452 current.RA = AdvisoryMonitorVSpeed;
458 current.RA = AdvisoryMaintVSpeed;
464 current.RA = AdvisoryAdjustVSpeed;
470 current.RA = AdvisoryLevelOff;
476 current.RA = AdvisoryClimb;
482 current.RA = AdvisoryDescend;
487 current.RA = AdvisoryIntrusion;
491 revertedRA = ((previous.RA != current.RA)&&
493 (previous.RA != AdvisoryIntrusion));
498 current.RA = previous.RA;
503 if ((tcas->threatDetector.getRadarAlt() > tcas->_TAInhbAlt) &&
504 (mode >= SwitchTaOnly))
505 tcas->annunciator.trigger(current, revertedRA);
507 if (current.RA == AdvisoryClear)
510 tcas->annunciator.clear();
518 nodeTAWarning->setBoolValue(current.RA != AdvisoryClear);
525TCAS::ThreatDetector::ThreatDetector(
TCAS* _tcas) :
527 pAlarmThresholds(&sensitivityLevels[0])
529#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
532 self.radarAltFt = 0.0;
537TCAS::ThreatDetector::init(
void)
539 nodeLat =
fgGetNode(
"/position/latitude-deg",
true);
540 nodeLon =
fgGetNode(
"/position/longitude-deg",
true);
541 nodePressureAlt =
fgGetNode(
"/position/altitude-ft",
true);
542 nodeRadarAlt =
fgGetNode(
"/position/altitude-agl-ft",
true);
543 nodeHeading =
fgGetNode(
"/orientation/heading-deg",
true);
544 nodeVelocity =
fgGetNode(
"/velocities/airspeed-kt",
true);
545 nodeVerticalFps =
fgGetNode(
"/velocities/vertical-speed-fps",
true);
547 tcas->advisoryGenerator.init(&self,¤tThreat);
552TCAS::ThreatDetector::update(
void)
555 self.lat = nodeLat->getDoubleValue();
556 self.lon = nodeLon->getDoubleValue();
557 self.pressureAltFt = nodePressureAlt->getDoubleValue();
558 self.heading = nodeHeading->getDoubleValue();
559 self.velocityKt = nodeVelocity->getDoubleValue();
560 self.verticalFps = nodeVerticalFps->getDoubleValue();
567 const double SmoothingFactor = 0.3;
568 self.radarAltFt = nodeRadarAlt->getDoubleValue()*SmoothingFactor +
569 (1-SmoothingFactor)*self.radarAltFt;
571#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
572 printf(
"TCAS::ThreatDetector::update: radarAlt = %f\n",self.radarAltFt);
578 for (sl=0;((self.radarAltFt > sensitivityLevels[sl].maxAltitude)&&
579 (sensitivityLevels[sl].maxAltitude));sl++);
580 pAlarmThresholds = &sensitivityLevels[sl];
581 tcas->advisoryGenerator.setAlarmThresholds(pAlarmThresholds);
589 if (pModel->getBoolValue(
"controls/invisible",
false ))
594 if (pModel->getNameString() ==
"swift")
598 bool xpdr_on = pModel->getBoolValue(
"swift/transponder/c-mode",
false);
599 if (!xpdr_on)
return false;
600 o_altFt = pModel->getDoubleValue(
"position/altitude-ft");
603 else if (pModel->getNameString() ==
"aircraft")
607 if (velocityKt < 40.0)
return false;
608 o_altFt = pModel->getDoubleValue(
"position/altitude-ft");
611 o_altFt = pModel->getIntValue(
"instrumentation/transponder/altitude", -9999);
614 return (o_altFt != -9999);
619TCAS::ThreatDetector::checkTransponder(
const SGPropertyNode* pModel,
float velocityKt)
627TCAS::ThreatDetector::checkThreat(
int mode,
const SGPropertyNode* pModel)
629#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
632 if (!pModel->getBoolValue(
"valid",
true))
633 return ThreatInvisible;
635 float velocityKt = pModel->getDoubleValue(
"velocities/true-airspeed-kt");
639 return ThreatInvisible;
641 int threatLevel = ThreatNone;
642 currentThreat.relativeAltitudeFt = altFt - self.pressureAltFt;
645 if (fabs(currentThreat.relativeAltitudeFt) > tcas->_verticalRange)
649 double lat = pModel->getDoubleValue(
"position/latitude-deg");
650 double lon = pModel->getDoubleValue(
"position/longitude-deg");
651 float heading = pModel->getDoubleValue(
"orientation/true-heading-deg");
653 double distanceNm, bearing;
657 if ((distanceNm > tcas->_lateralRange) || (distanceNm < 0))
660 currentThreat.verticalFps = pModel->getDoubleValue(
"velocities/vertical-speed-fps");
666 (fabs(currentThreat.relativeAltitudeFt) < tcas->_proxVertRange))
669 threatLevel = ThreatProximity;
673 if ((mode <= SwitchStandby)||
674 ((self.radarAltFt < 360)&&(self.velocityKt < 40)))
679 if (tcas->tracker.active())
681 currentThreat.callsign = pModel->getStringValue(
"callsign");
682 currentThreat.isTracked = tcas->tracker.isTracked(currentThreat.callsign);
685 currentThreat.isTracked =
false;
688 checkVerticalThreat();
691 if ((!currentThreat.verticalTA)&&
692 (!currentThreat.isTracked))
696 horizontalThreat(bearing, distanceNm, heading, velocityKt);
698 if (!currentThreat.isTracked)
701 if (!currentThreat.horizontalTA)
704 if ((currentThreat.horizontalTau < 0)||
705 (currentThreat.verticalTau < 0))
708 int previousThreatLevel = pModel->getIntValue(
"tcas/threat-level", 0);
709 if (previousThreatLevel == 0)
714#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
715 cout <<
"#" << checkCount <<
": " << pModel->getStringValue(
"callsign") << endl;
723 if (currentThreat.horizontalTA && currentThreat.verticalTA)
724 threatLevel = ThreatTA;
725 if (currentThreat.horizontalRA && currentThreat.verticalRA)
726 threatLevel = ThreatRA;
728 if (!tcas->tracker.active())
729 currentThreat.callsign = pModel->getStringValue(
"callsign");
731 tcas->tracker.add(currentThreat.callsign, threatLevel);
734 if (currentThreat.isTracked)
736 int oldLevel = tcas->tracker.getThreatLevel(currentThreat.callsign);
737 if (oldLevel > threatLevel)
738 threatLevel = oldLevel;
742 threatLevel = tcas->advisoryGenerator.resolution(mode, threatLevel, distanceNm, altFt, heading, velocityKt);
745#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
746 printf(
" threat: distance: %4.1f, bearing: %4.1f, alt: %5.1f, velocity: %4.1f, heading: %4.1f, vspeed: %4.1f, "
747 "own alt: %5.1f, own heading: %4.1f, own velocity: %4.1f, vertical tau: %3.2f"
750 distanceNm, relAngle(bearing, self.heading), altFt, velocityKt, heading, currentThreat.verticalFps,
751 self.altFt, self.heading, self.velocityKt
753 ,currentThreat.verticalTau
762TCAS::ThreatDetector::checkVerticalThreat(
void)
765 float dV = self.verticalFps - currentThreat.verticalFps;
766 float dA = currentThreat.relativeAltitudeFt;
768 currentThreat.verticalTA =
false;
769 currentThreat.verticalRA =
false;
770 currentThreat.verticalTau = 0;
782 if ((fabs(dV) < 3.0)||
783 ((tau < 0) && (tau > -5)))
787 float abs_dA = fabs(dA);
788 if (abs_dA < pAlarmThresholds->RA.ALIM)
791 currentThreat.verticalTA =
true;
792 currentThreat.verticalRA =
true;
795 if (abs_dA < pAlarmThresholds->TA.ALIM)
798 currentThreat.verticalTA =
true;
804 if ((tau < pAlarmThresholds->TA.Tau)&&
807 currentThreat.verticalTA =
true;
808 currentThreat.verticalRA = (tau < pAlarmThresholds->RA.Tau);
811 currentThreat.verticalTau = tau;
813#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
814 if (currentThreat.verticalTA)
815 printf(
" vertical dV=%f (%f-%f), dA=%f\n", dV, self.verticalFps, currentThreat.verticalFps, dA);
821TCAS::ThreatDetector::horizontalThreat(
float bearing,
float distanceNm,
float heading,
float velocityKt)
824 float vxKt = sin(heading*SGD_DEGREES_TO_RADIANS)*velocityKt - sin(self.heading*SGD_DEGREES_TO_RADIANS)*self.velocityKt;
825 float vyKt = cos(heading*SGD_DEGREES_TO_RADIANS)*velocityKt - cos(self.heading*SGD_DEGREES_TO_RADIANS)*self.velocityKt;
828 float closingSpeedKt2 = vxKt*vxKt+vyKt*vyKt;
829 float closingSpeedKt = sqrt(closingSpeedKt2);
834 float TA_rangeNm = (pAlarmThresholds->TA.Tau*closingSpeedKt)/3600;
835 float RA_rangeNm = (pAlarmThresholds->RA.Tau*closingSpeedKt)/3600;
837 if (closingSpeedKt < 100)
845 TA_rangeNm += (100.0-closingSpeedKt)*(pAlarmThresholds->TA.DMOD/100.0);
846 RA_rangeNm += (100.0-closingSpeedKt)*(pAlarmThresholds->RA.DMOD/100.0);
848 if (TA_rangeNm < pAlarmThresholds->TA.DMOD)
849 TA_rangeNm = pAlarmThresholds->TA.DMOD;
850 if (RA_rangeNm < pAlarmThresholds->RA.DMOD)
851 RA_rangeNm = pAlarmThresholds->RA.DMOD;
853 currentThreat.horizontalTA = (distanceNm < TA_rangeNm);
854 currentThreat.horizontalRA = (distanceNm < RA_rangeNm);
855 currentThreat.horizontalTau = -1;
857 if ((currentThreat.horizontalRA)&&
858 (currentThreat.verticalRA))
880 float sx = sin(bearing*SGD_DEGREES_TO_RADIANS)*distanceNm;
881 float sy = cos(bearing*SGD_DEGREES_TO_RADIANS)*distanceNm;
882 float vx = vxKt * (SG_KT_TO_MPS*SG_METER_TO_NM);
883 float vy = vyKt * (SG_KT_TO_MPS*SG_METER_TO_NM);
884 float a = vx*vx + vy*vy;
885 float b = 2*(sx*vx + sy*vy);
889#ifdef FEATURE_TCAS_DEBUG_THREAT_DETECTOR
890 printf(
" Time to horizontal CPA: %4.2f\n",tau);
892 if (tau > pAlarmThresholds->RA.Tau)
893 tau = pAlarmThresholds->RA.Tau;
896 currentThreat.horizontalTau = tau;
902TCAS::ThreatDetector::unitTest(
void)
904 pAlarmThresholds = &sensitivityLevels[1];
907 self.verticalFps = 0;
909 cout <<
"identical altitude and vspeed " << endl;
910 checkVerticalThreat(self.altFt, self.verticalFps);
911 cout <<
"1000ft alt offset, dV=100 " << endl;
912 checkVerticalThreat(self.altFt+1000, 100);
913 cout <<
"-1000ft alt offset, dV=100 " << endl;
914 checkVerticalThreat(self.altFt-1000, 100);
915 cout <<
"3000ft alt offset, dV=10 " << endl;
916 checkVerticalThreat(self.altFt+3000, 10);
917 cout <<
"500ft alt offset, dV=100 " << endl;
918 checkVerticalThreat(self.altFt+500, 100);
919 cout <<
"500ft alt offset, dV=-100 " << endl;
920 checkVerticalThreat(self.altFt+500, -100);
925 cout <<
"10nm behind, overtaking with 1Nm/s" << endl;
926 horizontalThreat(-180, 10, 0, 1/(SG_KT_TO_MPS*SG_METER_TO_NM));
928 cout <<
"10nm ahead, departing with 1Nm/s" << endl;
929 horizontalThreat(0, 20, 0, 1/(SG_KT_TO_MPS*SG_METER_TO_NM));
932 self.velocityKt = 1/(SG_KT_TO_MPS*SG_METER_TO_NM);
933 cout <<
"10nm behind, overtaking with 1Nm/s at 90 degrees" << endl;
934 horizontalThreat(-90, 20, 90, 2/(SG_KT_TO_MPS*SG_METER_TO_NM));
937 self.velocityKt = 1/(SG_KT_TO_MPS*SG_METER_TO_NM);
938 cout <<
"10nm behind, overtaking with 1Nm/s at 20 degrees" << endl;
939 horizontalThreat(200, 20, 20, 2/(SG_KT_TO_MPS*SG_METER_TO_NM));
947TCAS::AdvisoryGenerator::AdvisoryGenerator(
TCAS* _tcas) :
950 pCurrentThreat(NULL),
951 pAlarmThresholds(NULL)
956TCAS::AdvisoryGenerator::init(
const LocalInfo* _pSelf, ThreatInfo* _pCurrentThreat)
958 pCurrentThreat = _pCurrentThreat;
963TCAS::AdvisoryGenerator::setAlarmThresholds(
const SensitivityLevel* _pAlarmThresholds)
965 pAlarmThresholds = _pAlarmThresholds;
970TCAS::AdvisoryGenerator::verticalSeparation(
float newVerticalFps)
973 float dV = pCurrentThreat->verticalFps - newVerticalFps;
974 float tau = pCurrentThreat->horizontalTau;
978 return pCurrentThreat->relativeAltitudeFt + tau * dV;
983TCAS::AdvisoryGenerator::determineRAsense(
int& RASense,
bool& isCrossing)
994 float upSenseRelAltFt = verticalSeparation(+2000/60.0);
995 float downSenseRelAltFt = verticalSeparation(-2000/60.0);
996 if (fabs(upSenseRelAltFt) >= fabs(downSenseRelAltFt))
1007 (pCurrentThreat->relativeAltitudeFt > 200))
1010 if (fabs(downSenseRelAltFt) > pAlarmThresholds->TA.ALIM)
1023 (pCurrentThreat->relativeAltitudeFt < -200))
1026 if (fabs(upSenseRelAltFt) > pAlarmThresholds->TA.ALIM)
1039 pCurrentThreat->RASense = RASense;
1041#ifdef FEATURE_TCAS_DEBUG_ADV_GENERATOR
1042 printf(
" RASense: %i, crossing: %u, relAlt: %4.1f, upward separation: %4.1f, downward separation: %4.1f\n",
1044 pCurrentThreat->relativeAltitudeFt,
1045 upSenseRelAltFt,downSenseRelAltFt);
1051TCAS::AdvisoryGenerator::resolution(
int mode,
int threatLevel,
float rangeNm,
float altFt,
1052 float heading,
float velocityKt)
1054 int RAOption = OptionNone;
1055 int RA = AdvisoryIntrusion;
1058 if (threatLevel == ThreatRA)
1063 if ((altFt < tcas->
_intruderInhbAlt) && (!tcas->_intruderInhbSelfAltToggle || pSelf->radarAltFt < tcas->_intruderInhbSelfAlt))
1064 threatLevel = ThreatTA;
1068 if (pSelf->radarAltFt < tcas->_RAInhbAlt)
1069 threatLevel = ThreatTA;
1072 if (mode != SwitchAuto)
1073 threatLevel = ThreatTA;
1076 bool isCrossing =
false;
1079 if (threatLevel == ThreatRA)
1083 determineRAsense(RASense, isCrossing);
1090 if ((pSelf->verticalFps < -1000/60.0)&&
1094 float relAltFt = verticalSeparation(-500/60.0);
1095 if (relAltFt > pAlarmThresholds->TA.ALIM)
1096 RA |= AdvisoryAdjustVSpeed;
1098 RA |= AdvisoryClimb;
1100 RAOption |= OptionCrossingClimb;
1107 if ((pSelf->verticalFps > 1000/60.0)&&
1111 float relAltFt = verticalSeparation(500/60.0);
1112 if (relAltFt < -pAlarmThresholds->TA.ALIM)
1113 RA |= AdvisoryAdjustVSpeed;
1115 RA |= AdvisoryDescend;
1117 RAOption |= OptionCrossingDescent;
1136 if (pSelf->radarAltFt < tcas->_DesInhbAlt)
1138 RA &= ~AdvisoryDescend;
1140 RA |= AdvisoryIntrusion;
1144#ifdef FEATURE_TCAS_DEBUG_ADV_GENERATOR
1145 cout <<
" resolution advisory: " << RA << endl;
1148 ResolutionAdvisory newAdvisory;
1149 newAdvisory.RAOption = RAOption;
1150 newAdvisory.RA = RA;
1151 newAdvisory.threatLevel = threatLevel;
1152 tcas->advisoryCoordinator.add(newAdvisory);
1166 properties_handler(this),
1167 threatDetector(this),
1169 advisoryCoordinator(this),
1170 advisoryGenerator(this),
1184 for (
int i = 0;
i < pNode->nChildren(); ++
i)
1186 SGPropertyNode* pChild = pNode->getChild(
i);
1187 string cname = pChild->getNameString();
1188 string cval = pChild->getStringValue();
1189 int cintval = pChild->getIntValue();
1192 SG_LOG(SG_INSTR, SG_WARN,
"Error in TCAS config logic: value less than zero. Skipping!");
1195 if (cname ==
"name")
1197 else if (cname ==
"number")
1199 else if (cname ==
"vertical-range-ft" && cintval > 0)
1201 else if (cname ==
"lateral-range-nm" && cintval > 0)
1203 else if (cname ==
"proximate-vertical-range-ft" && cintval > 0)
1205 else if (cname ==
"proximate-lateral-range-nm" && cintval > 0)
1207 else if (cname ==
"increase-descent-inhibit-alt-ft" && cintval > 0)
1209 else if (cname ==
"descend-inhibit-alt-ft" && cintval > 0)
1211 else if (cname ==
"ra-inhibit-alt-ft" && cintval > 0)
1213 else if (cname ==
"ta-inhibit-alt-ft" && cintval > 0)
1215 else if (cname ==
"intruder-inhibit-alt-ft" && cintval > 0)
1217 else if (cname ==
"intruder-use-own-alt")
1219 else if (cname ==
"intruder-minimum-own-alt-ft" && cintval > 0)
1223 SG_LOG(SG_INSTR, SG_WARN,
"Error in TCAS config logic");
1225 SG_LOG(SG_INSTR, SG_WARN,
"Section = " << name);
1234 advisoryCoordinator.init();
1235 threatDetector.init();
1242 advisoryCoordinator.reinit();
1248 SGPropertyNode* node =
fgGetNode((
"/instrumentation/" + name), num,
true);
1250 nodeServiceable = node->getNode(
"serviceable",
true);
1253 nodeModeSwitch = node->getNode(
"inputs/mode",
true);
1255 nodeSelfTest = node->getNode(
"inputs/self-test",
true);
1257 nodeSelfTest->setBoolValue(
false);
1259#ifdef FEATURE_TCAS_DEBUG_PROPERTIES
1260 SGPropertyNode* nodeDebug = node->getNode(
"debug",
true);
1262 nodeDebugTrigger = nodeDebug->getNode(
"threat-trigger",
true);
1263 nodeDebugRA = nodeDebug->getNode(
"threat-RA",
true);
1264 nodeDebugThreat = nodeDebug->getNode(
"threat-level",
true);
1266 nodeDebugTrigger->setBoolValue(
false);
1267 nodeDebugRA->setIntValue(3);
1268 nodeDebugThreat->setIntValue(1);
1271 annunciator.bind(node);
1272 advisoryCoordinator.bind(node);
1278 properties_handler.unbind();
1285 if (!nodeServiceable->getBoolValue())
1287 int mode = nodeModeSwitch->getIntValue();
1288 if (mode == SwitchOff)
1291 nextUpdateTime -= dt;
1292 if (nextUpdateTime <= 0.0 )
1294 nextUpdateTime = 1.0;
1300 threatDetector.update();
1303 advisoryCoordinator.clear();
1305 if (nodeSelfTest->getBoolValue())
1307 if (threatDetector.getVelocityKt() >= 40)
1310 nodeSelfTest->setBoolValue(
false);
1323#ifdef FEATURE_TCAS_DEBUG_PROPERTIES
1324 if (nodeDebugTrigger->getBoolValue())
1327 ResolutionAdvisory debugAdvisory;
1328 debugAdvisory.RAOption = OptionNone;
1329 debugAdvisory.RA = nodeDebugRA->getIntValue();
1330 debugAdvisory.threatLevel = nodeDebugThreat->getIntValue();
1331 advisoryCoordinator.add(debugAdvisory);
1336 SGPropertyNode* pAi =
fgGetNode(
"/ai/models",
true);
1339 for (
int i = pAi->nChildren() - 1;
i >= -1;
i--)
1341 SGPropertyNode* pModel = pAi->getChild(
i);
1342 if ((pModel)&&(pModel->nChildren()))
1344 int threatLevel = threatDetector.checkThreat(mode, pModel);
1347 if (threatLevel==ThreatRA)
1348 pModel->setIntValue(
"tcas/ra-sense", -threatDetector.getRASense());
1349 pModel->setIntValue(
"tcas/threat-level", threatLevel);
1353 advisoryCoordinator.update(mode);
1355 annunciator.update();
1362 annunciator.update();
1363 if (annunciator.isPlaying())
1368 ResolutionAdvisory newAdvisory;
1369 newAdvisory.threatLevel = ThreatRA;
1370 newAdvisory.RA = AdvisoryClear;
1371 newAdvisory.RAOption = OptionNone;
1373 threatDetector.setRadarAlt(501);
1376 switch(selfTestStep)
1379 newAdvisory.RA = AdvisoryIntrusion;
1380 newAdvisory.threatLevel = ThreatTA;
1383 newAdvisory.RA = AdvisoryClimb;
1386 newAdvisory.RA = AdvisoryClimb;
1387 newAdvisory.RAOption = OptionIncreaseClimb;
1390 newAdvisory.RA = AdvisoryClimb;
1391 newAdvisory.RAOption = OptionCrossingClimb;
1394 newAdvisory.RA = AdvisoryDescend;
1397 newAdvisory.RA = AdvisoryDescend;
1398 newAdvisory.RAOption = OptionIncreaseDescend;
1401 newAdvisory.RA = AdvisoryDescend;
1402 newAdvisory.RAOption = OptionCrossingDescent;
1405 newAdvisory.RA = AdvisoryAdjustVSpeed;
1408 newAdvisory.RA = AdvisoryMaintVSpeed;
1411 newAdvisory.RA = AdvisoryMonitorVSpeed;
1414 newAdvisory.threatLevel = ThreatNone;
1415 newAdvisory.RA = AdvisoryClear;
1418 annunciator.test(
true);
1422 nodeSelfTest->setBoolValue(
false);
1427 advisoryCoordinator.add(newAdvisory);
1428 advisoryCoordinator.update(SwitchAuto);
1437TCAS::Tracker::Tracker(
TCAS*) :
1446TCAS::Tracker::update(
void)
1454 TrackerTargets::iterator it = targets.begin();
1455 while (it != targets.end())
1457 TrackerTarget* pTarget = it->second;
1458 if (currentTime - pTarget->TAtimestamp > 10.0)
1460 TrackerTargets::iterator temp = it;
1462#ifdef FEATURE_TCAS_DEBUG_TRACKER
1463 printf(
"target %s no longer a TA threat.\n",temp->first.c_str());
1465 targets.erase(temp->first);
1471 if ((pTarget->threatLevel == ThreatRA)&&
1472 (currentTime - pTarget->RAtimestamp > 7.0))
1474 pTarget->threatLevel = ThreatTA;
1475#ifdef FEATURE_TCAS_DEBUG_TRACKER
1476 printf(
"target %s no longer an RA threat.\n",it->first.c_str());
1482 haveTargets = !targets.empty();
1487TCAS::Tracker::add(
const string callsign,
int detectedLevel)
1489 TrackerTarget* pTarget = NULL;
1492 TrackerTargets::iterator it = targets.find(callsign);
1493 if (it != targets.end())
1495 pTarget = it->second;
1501 pTarget =
new TrackerTarget();
1502 pTarget->TAtimestamp = 0;
1503 pTarget->RAtimestamp = 0;
1504 pTarget->threatLevel = 0;
1506 targets[callsign] = pTarget;
1507#ifdef FEATURE_TCAS_DEBUG_TRACKER
1508 printf(
"new target: %s, level: %i\n",callsign.c_str(),detectedLevel);
1512 if (detectedLevel > pTarget->threatLevel)
1513 pTarget->threatLevel = detectedLevel;
1515 if (detectedLevel >= ThreatTA)
1516 pTarget->TAtimestamp = currentTime;
1518 if (detectedLevel >= ThreatRA)
1519 pTarget->RAtimestamp = currentTime;
1525TCAS::Tracker::_isTracked(
string callsign)
1527 return targets.find(callsign) != targets.end();
1531TCAS::Tracker::getThreatLevel(
string callsign)
1533 TrackerTargets::iterator it = targets.find(callsign);
1534 if (it != targets.end())
1535 return it->second->threatLevel;
1543SGSubsystemMgr::InstancedRegistrant<TCAS> registrantTCAS(
1544 SGSubsystemMgr::FDM,
1545 {{
"instrumentation", SGSubsystemMgr::Dependency::HARD}},
double get_sim_time_sec() const
void update(double dt) override
Monitor traffic for safety threats.
TCAS(SGPropertyNode *node)
bool _intruderInhbSelfAltToggle
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
static void clear(FGReplayInternal &self)
static bool checkTransponderLocal(const SGPropertyNode *pModel, float velocityKt, float &o_altFt)
static void calcRangeBearing(double lat1, double lon1, double lat2, double lon2, double &rangeNm, double &bearing)
calculate range and bearing of lat2/lon2 relative to lat1/lon1
#define AVAILABLE_RA(Options, Advisory)
#define ADD_VOICE(Var, Sample, SayTwice)