From 380c69c8a0f2cd5d11ec488e6f5774cadf1cf3de Mon Sep 17 00:00:00 2001 From: daveluff Date: Mon, 6 Oct 2003 15:20:47 +0000 Subject: [PATCH] Progress towards AI/ATC communication without speaking all at once --- src/ATC/AILocalTraffic.cxx | 15 +++- src/ATC/AIPlane.cxx | 150 +++++++++++++++++++++++++++++++++---- src/ATC/AIPlane.hxx | 42 ++++++++++- src/ATC/ATC.cxx | 45 ++++++----- src/ATC/ATC.hxx | 83 +++++++++++--------- src/ATC/tower.cxx | 4 + src/ATC/tower.hxx | 12 +-- 7 files changed, 262 insertions(+), 89 deletions(-) diff --git a/src/ATC/AILocalTraffic.cxx b/src/ATC/AILocalTraffic.cxx index b62f4b4c0..f1a866e26 100644 --- a/src/ATC/AILocalTraffic.cxx +++ b/src/ATC/AILocalTraffic.cxx @@ -319,7 +319,8 @@ void FGAILocalTraffic::Update(double dt) { trns += buf; trns += " "; trns += plane.callsign; - Transmit(trns); + pending_transmission = trns; + Transmit(30.0); responseCounter = 0.0; contactTower = false; changeFreq = true; @@ -414,7 +415,8 @@ void FGAILocalTraffic::Update(double dt) { holdingShort = false; string trns = "Cleared for take-off "; trns += plane.callsign; - Transmit(trns); + pending_transmission = trns; + Transmit(); StartTaxi(); } //cout << "^" << flush; @@ -476,7 +478,8 @@ void FGAILocalTraffic::Update(double dt) { trns += plane.callsign; trns += " on apron parking request taxi for traffic pattern"; //cout << "trns = " << trns << endl; - Transmit(trns); + pending_transmission = trns; + Transmit(); taxiRequestCleared = false; taxiRequestPending = true; } @@ -504,6 +507,9 @@ void FGAILocalTraffic::Update(double dt) { fgSetDouble("/AI/Local1/ortho-x", (ortho.ConvertToLocal(pos)).x()); fgSetDouble("/AI/Local1/ortho-y", (ortho.ConvertToLocal(pos)).y()); fgSetDouble("/AI/Local1/elev", pos.elev() * SG_METER_TO_FEET); + + // And finally, call parent for transmission rendering + FGAIPlane::Update(dt); } void FGAILocalTraffic::RegisterTransmission(int code) { @@ -947,7 +953,8 @@ void FGAILocalTraffic::TransmitPatternPositionReport(void) { // And add the airport name again trns += tower->get_name(); - Transmit(trns); + pending_transmission = trns; // FIXME - make up pending_transmission natively + Transmit(90.0); // Assume a report of this leg will be invalid if we can't transmit within a minute and a half. } void FGAILocalTraffic::ExitRunway(Point3D orthopos) { diff --git a/src/ATC/AIPlane.cxx b/src/ATC/AIPlane.cxx index 7d224f65f..5ce556b47 100644 --- a/src/ATC/AIPlane.cxx +++ b/src/ATC/AIPlane.cxx @@ -22,6 +22,7 @@ #include
#include #include +#include #include #include SG_USING_STD(string); @@ -32,12 +33,73 @@ SG_USING_STD(string); FGAIPlane::FGAIPlane() { leg = LEG_UNKNOWN; + tuned_station = NULL; + pending_transmission = ""; + _timeout = 0; + _pending = false; + _callback = NULL; + _transmit = false; + _transmitting = false; + voice = false; + playing = false; + voiceOK = false; + vPtr = NULL; } FGAIPlane::~FGAIPlane() { } void FGAIPlane::Update(double dt) { + if(_pending) { + if(tuned_station) { + if(tuned_station->FreqClear()) { + _pending = false; + _transmit = true; + _transmitting = false; + } else { + if(_timeout > 0.0) { // allows count down to be avoided by initially setting it to zero + _timeout -= dt; + if(_timeout <= 0.0) { + _timeout = 0.0; + _pending = false; + // timed out - don't render. + } + } + } + } else { + // Not tuned to ATC - Just go ahead and transmit + _pending = false; + _transmit = true; + _transmitting = false; + } + } + + // This turns on rendering if on the same freq as the user + // TODO - turn it off if user switches to another freq - keep track of where in message we are etc. + if(_transmit) { + double user_freq0 = fgGetDouble("/radios/comm[0]/frequencies/selected-mhz"); + //comm1 is not used yet. + _counter = 0.0; + _max_count = 5.0; // FIXME - hardwired length of message - need to calculate it! + + if(freq == user_freq0) { + //cout << "Transmitting..." << endl; + // we are on the same frequency, so check distance to the user plane + if(1) { + // For now assume in range !!! + // TODO - implement range checking + Render(plane.callsign, false); + _transmit = false; + _transmitting = true; + } + } + } else if(_transmitting) { + if(_counter >= _max_count) { + NoRender(plane.callsign); + _transmitting = false; + } + _counter += dt; + } } void FGAIPlane::Bank(double angle) { @@ -55,23 +117,85 @@ void FGAIPlane::LevelWings(void) { } } -void FGAIPlane::Transmit(string msg) { - SG_LOG(SG_ATC, SG_INFO, "Transmit called, msg = " << msg); - double user_freq0 = fgGetDouble("/radios/comm[0]/frequencies/selected-mhz"); - //double user_freq0 = ("/radios/comm[0]/frequencies/selected-mhz"); - //comm1 is not used yet. - - if(freq == user_freq0) { - //cout << "Transmitting..." << endl; - // we are on the same frequency, so check distance to the user plane - if(1) { - // For now (testing) assume in range !!! - // TODO - implement range checking - globals->get_ATC_display()->RegisterSingleMessage(msg, 0); +void FGAIPlane::Transmit(ai_plane_callback_t callback) { + SG_LOG(SG_ATC, SG_INFO, "Transmit called for plane " << plane.callsign << ", msg = " << pending_transmission); + _pending = true; + _callback = callback; + _timeout = 0.0; +} + +void FGAIPlane::Transmit(double timeout, ai_plane_callback_t callback) { + SG_LOG(SG_ATC, SG_INFO, "Timed transmit called for plane " << plane.callsign << ", msg = " << pending_transmission); + _pending = true; + _callback = callback; + _timeout = timeout; +} + +void FGAIPlane::ImmediateTransmit(ai_plane_callback_t callback) { + Render(plane.callsign, false); + if(_callback) { + (*_callback)(); + } +} + +// Render a transmission +// Outputs the transmission either on screen or as audio depending on user preference +// The refname is a string to identify this sample to the sound manager +// The repeating flag indicates whether the message should be repeated continuously or played once. +void FGAIPlane::Render(string refname, bool repeating) { +#ifdef ENABLE_AUDIO_SUPPORT + voice = (voiceOK && fgGetBool("/sim/sound/audible") + && fgGetBool("/sim/sound/voice")); + if(voice) { + int len; + unsigned char* buf = vPtr->WriteMessage((char*)pending_transmission.c_str(), len, voice); + if(voice) { + SGSimpleSound* simple = new SGSimpleSound(buf, len); + // TODO - at the moment the volume is always set off comm1 + // and can't be changed after the transmission has started. + simple->set_volume(5.0 * fgGetDouble("/radios/comm[0]/volume")); + globals->get_soundmgr()->add(simple, refname); + if(repeating) { + globals->get_soundmgr()->play_looped(refname); + } else { + globals->get_soundmgr()->play_once(refname); + } + } + delete[] buf; + } +#endif // ENABLE_AUDIO_SUPPORT + if(!voice) { + // first rip the underscores and the pause hints out of the string - these are for the convienience of the voice parser + for(unsigned int i = 0; i < pending_transmission.length(); ++i) { + if((pending_transmission.substr(i,1) == "_") || (pending_transmission.substr(i,1) == "/")) { + pending_transmission[i] = ' '; + } } + globals->get_ATC_display()->RegisterSingleMessage(pending_transmission, 0.0); } + playing = true; } + +// Cease rendering a transmission. +void FGAIPlane::NoRender(string refname) { + if(playing) { + if(voice) { +#ifdef ENABLE_AUDIO_SUPPORT + globals->get_soundmgr()->stop(refname); + globals->get_soundmgr()->remove(refname); +#endif + } else { + globals->get_ATC_display()->CancelRepeatingMessage(); + } + playing = false; + } +} + +/* + +*/ + void FGAIPlane::RegisterTransmission(int code) { } diff --git a/src/ATC/AIPlane.hxx b/src/ATC/AIPlane.hxx index a757bf26a..544212ee0 100644 --- a/src/ATC/AIPlane.hxx +++ b/src/ATC/AIPlane.hxx @@ -72,7 +72,7 @@ public: virtual ~FGAIPlane(); // Run the internal calculations - virtual void Update(double dt); + void Update(double dt); // Send a transmission *TO* the AIPlane. // FIXME int code is a hack - eventually this will receive Alexander's coded messages. @@ -85,6 +85,9 @@ public: LandingType GetLandingOption(); protected: + // callback type for derived classes to use + typedef void (*ai_plane_callback_t) (void); + PlaneRec plane; double mag_hdg; // degrees - the heading that the physical aircraft is *pointing* @@ -106,12 +109,47 @@ protected: // Make radio transmission - this simply sends the transmission for physical rendering if the users // aircraft is on the same frequency and in range. It is up to the derived classes to let ATC know // what is going on. - void Transmit(string msg); + string pending_transmission; // derived classes set this string before calling Transmit(...) + FGATC* tuned_station; // and this if they are tuned to ATC + + // Transmit a message when channel becomes free of other dialog + void Transmit(ai_plane_callback_t callback = NULL); + + // Transmit a message if channel becomes free within timeout (seconds). timeout of zero implies no limit + void Transmit(double timeout, ai_plane_callback_t callback = NULL); + + // Transmit regardless of other dialog on the channel eg emergency + void ImmediateTransmit(ai_plane_callback_t callback = NULL); void Bank(double angle); void LevelWings(void); PatternLeg leg; + +private: + bool _pending; + double _timeout; + ai_plane_callback_t _callback; + bool _transmit; // we are to transmit + bool _transmitting; // we are transmitting + double _counter; + double _max_count; + + // Render a transmission (in string pending_transmission) + // Outputs the transmission either on screen or as audio depending on user preference + // The refname is a string to identify this sample to the sound manager + // The repeating flag indicates whether the message should be repeated continuously or played once. + void Render(string refname, bool repeating); + + // Cease rendering a transmission. + // Requires the sound manager refname if audio, else "". + void NoRender(string refname); + + // Rendering related stuff + bool voice; // Flag - true if we are using voice + bool playing; // Indicates a message in progress + bool voiceOK; // Flag - true if at least one voice has loaded OK + FGATCVoice* vPtr; }; #endif // _FG_AI_PLANE_HXX diff --git a/src/ATC/ATC.cxx b/src/ATC/ATC.cxx index d8fc4b737..120c00e98 100644 --- a/src/ATC/ATC.cxx +++ b/src/ATC/ATC.cxx @@ -29,6 +29,10 @@ #include "ATC.hxx" #include "ATCdisplay.hxx" +FGATC::FGATC() { + freqClear = true; +} + FGATC::~FGATC() { } @@ -39,7 +43,7 @@ void FGATC::AddPlane(string pid) { } int FGATC::RemovePlane() { - return 0; + return 0; } void FGATC::SetDisplay() { @@ -49,7 +53,7 @@ void FGATC::SetNoDisplay() { } atc_type FGATC::GetType() { - return INVALID; + return INVALID; } void FGATC::SetData(ATCData* d) { @@ -70,9 +74,9 @@ void FGATC::SetData(ATCData* d) { // The refname is a string to identify this sample to the sound manager // The repeating flag indicates whether the message should be repeated continuously or played once. void FGATC::Render(string msg, string refname, bool repeating) { -#ifdef ENABLE_AUDIO_SUPPORT + #ifdef ENABLE_AUDIO_SUPPORT voice = (voiceOK && fgGetBool("/sim/sound/audible") - && fgGetBool("/sim/sound/voice")); + && fgGetBool("/sim/sound/voice")); if(voice) { int len; unsigned char* buf = vPtr->WriteMessage((char*)msg.c_str(), len, voice); @@ -90,7 +94,7 @@ void FGATC::Render(string msg, string refname, bool repeating) { } delete[] buf; } -#endif // ENABLE_AUDIO_SUPPORT + #endif // ENABLE_AUDIO_SUPPORT if(!voice) { // first rip the underscores and the pause hints out of the string - these are for the convienience of the voice parser for(unsigned int i = 0; i < msg.length(); ++i) { @@ -108,10 +112,10 @@ void FGATC::Render(string msg, string refname, bool repeating) { void FGATC::NoRender(string refname) { if(playing) { if(voice) { -#ifdef ENABLE_AUDIO_SUPPORT + #ifdef ENABLE_AUDIO_SUPPORT globals->get_soundmgr()->stop(refname); globals->get_soundmgr()->remove(refname); -#endif + #endif } else { globals->get_ATC_display()->CancelRepeatingMessage(); } @@ -120,21 +124,14 @@ void FGATC::NoRender(string refname) { } ostream& operator << (ostream& os, atc_type atc) { - switch(atc) { - case(INVALID): - return(os << "INVALID"); - case(ATIS): - return(os << "ATIS"); - case(GROUND): - return(os << "GROUND"); - case(TOWER): - return(os << "TOWER"); - case(APPROACH): - return(os << "APPROACH"); - case(DEPARTURE): - return(os << "DEPARTURE"); - case(ENROUTE): - return(os << "ENROUTE"); - } - return(os << "ERROR - Unknown switch in atc_type operator << "); + switch(atc) { + case(INVALID): return(os << "INVALID"); + case(ATIS): return(os << "ATIS"); + case(GROUND): return(os << "GROUND"); + case(TOWER): return(os << "TOWER"); + case(APPROACH): return(os << "APPROACH"); + case(DEPARTURE): return(os << "DEPARTURE"); + case(ENROUTE): return(os << "ENROUTE"); + } + return(os << "ERROR - Unknown switch in atc_type operator << "); } diff --git a/src/ATC/ATC.hxx b/src/ATC/ATC.hxx index 38750898a..1249e6e1f 100644 --- a/src/ATC/ATC.hxx +++ b/src/ATC/ATC.hxx @@ -58,13 +58,13 @@ struct PlaneRec { // Possible types of ATC type that the radios may be tuned to. // INVALID implies not tuned in to anything. enum atc_type { - INVALID, - ATIS, - GROUND, - TOWER, - APPROACH, - DEPARTURE, - ENROUTE + INVALID, + ATIS, + GROUND, + TOWER, + APPROACH, + DEPARTURE, + ENROUTE }; // DCL - new experimental ATC data store @@ -98,28 +98,39 @@ struct RunwayDetails { ostream& operator << (ostream& os, atc_type atc); class FGATC { - -public: - - virtual ~FGATC(); - - // Run the internal calculations - virtual void Update(double dt); - - // Add plane to a stack - virtual void AddPlane(string pid); - - // Remove plane from stack - virtual int RemovePlane(); - - // Indicate that this instance should output to the display if appropriate - virtual void SetDisplay(); - - // Indicate that this instance should not output to the display - virtual void SetNoDisplay(); - - // Return the type of ATC station that the class represents - virtual atc_type GetType(); + + public: + + FGATC(); + virtual ~FGATC(); + + // Run the internal calculations + virtual void Update(double dt); + + // Add plane to a stack + virtual void AddPlane(string pid); + + // Remove plane from stack + virtual int RemovePlane(); + + // Indicate that this instance should output to the display if appropriate + virtual void SetDisplay(); + + // Indicate that this instance should not output to the display + virtual void SetNoDisplay(); + + // Returns true if OK to transmit on this frequency + inline bool FreqClear() { return freqClear; } + // Indicate that the frequency is in use + inline void FreqInUse() { freqClear = false; } + // Under development!! + // The idea is that AI traffic or the user ATC dialog box calls FreqInUse() when they begin transmitting, + // and that the tower control sets freqClear back to true following a reply. + // AI traffic should check FreqClear() is true prior to transmitting. + // The user will just have to wait for a gap in dialog as in real life. + + // Return the type of ATC station that the class represents + virtual atc_type GetType(); // Set the core ATC data void SetData(ATCData* d); @@ -145,18 +156,18 @@ public: inline const char* get_name() {return name.c_str();} inline void set_name(const string nm) {name = nm;} -protected: - + protected: + // Render a transmission // Outputs the transmission either on screen or as audio depending on user preference // The refname is a string to identify this sample to the sound manager // The repeating flag indicates whether the message should be repeated continuously or played once. void Render(string msg, string refname, bool repeating); - + // Cease rendering a transmission. // Requires the sound manager refname if audio, else "". void NoRender(string refname); - + double lon, lat, elev; double x, y, z; int freq; @@ -169,6 +180,8 @@ protected: bool playing; // Indicates a message in progress bool voiceOK; // Flag - true if at least one voice has loaded OK FGATCVoice* vPtr; + + bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog }; inline istream& @@ -177,7 +190,7 @@ operator >> ( istream& fin, ATCData& a ) double f; char ch; char tp; - + fin >> tp; switch(tp) { @@ -201,7 +214,7 @@ operator >> ( istream& fin, ATCData& a ) a.type = INVALID; return fin >> skipeol; } - + fin >> a.lat >> a.lon >> a.elev >> f >> a.range >> a.ident; diff --git a/src/ATC/tower.cxx b/src/ATC/tower.cxx index 1fd7dab04..dee428491 100644 --- a/src/ATC/tower.cxx +++ b/src/ATC/tower.cxx @@ -1248,6 +1248,10 @@ void FGTower::ReportRunwayVacated(string ID) { //cout << "Report Runway Vacated Called...\n"; } +void FGTower::ReportDownwind(string ID) { + // Tell the plane reporting what number she is in the circuit +} + ostream& operator << (ostream& os, tower_traffic_type ttt) { switch(ttt) { case(CIRCUIT): return(os << "CIRCUIT"); diff --git a/src/ATC/tower.hxx b/src/ATC/tower.hxx index c8eef6a43..022591964 100644 --- a/src/ATC/tower.hxx +++ b/src/ATC/tower.hxx @@ -124,6 +124,7 @@ public: void ReportGoingAround(string ID); void ReportRunwayVacated(string ID); void ReportReadyForDeparture(string ID); + void ReportDownwind(string ID); // Contact tower when at a hold short for departure - for now we'll assume plane - maybe vehicles might want to cross runway eventually? void ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_traffic_type operation); @@ -146,15 +147,6 @@ public: bool GetCrosswindConstraint(double& cpos); bool GetDownwindConstraint(double& dpos); bool GetBaseConstraint(double& bpos); - - // Returns true if OK to transmit on this frequency - inline bool FreqClear() { return freqClear; } - // Indicate that the frequency is in use - inline void FreqInUse() { freqClear = false; } - // The idea is that AI traffic or the user ATC dialog box calls FreqInUse() when they begin transmitting, - // and that the tower control sets freqClear back to true following a reply. - // AI traffic should check FreqClear() is true prior to transmitting. - // The user will just have to wait for a gap in dialog as in real life. private: FGATCMgr* ATCmgr; @@ -198,8 +190,6 @@ private: bool display; // Flag to indicate whether we should be outputting to the ATC display. bool displaying; // Flag to indicate whether we are outputting to the ATC display. - bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog - double timeSinceLastDeparture; // Time in seconds since last departure from active rwy. bool departed; // set true when the above needs incrementing with time, false when it doesn't. -- 2.39.5