trns += buf;
trns += " ";
trns += plane.callsign;
- Transmit(trns);
+ pending_transmission = trns;
+ Transmit(30.0);
responseCounter = 0.0;
contactTower = false;
changeFreq = true;
holdingShort = false;
string trns = "Cleared for take-off ";
trns += plane.callsign;
- Transmit(trns);
+ pending_transmission = trns;
+ Transmit();
StartTaxi();
}
//cout << "^" << flush;
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;
}
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) {
// 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) {
#include <Main/fg_props.hxx>
#include <simgear/math/point3d.hxx>
#include <simgear/debug/logstream.hxx>
+#include <simgear/sound/soundmgr.hxx>
#include <math.h>
#include <string>
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) {
}
}
-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) {
}
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.
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*
// 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
#include "ATC.hxx"
#include "ATCdisplay.hxx"
+FGATC::FGATC() {
+ freqClear = true;
+}
+
FGATC::~FGATC() {
}
}
int FGATC::RemovePlane() {
- return 0;
+ return 0;
}
void FGATC::SetDisplay() {
}
atc_type FGATC::GetType() {
- return INVALID;
+ return INVALID;
}
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);
}
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) {
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();
}
}
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 << ");
}
// 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
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);
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;
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&
double f;
char ch;
char tp;
-
+
fin >> tp;
switch(tp) {
a.type = INVALID;
return fin >> skipeol;
}
-
+
fin >> a.lat >> a.lon >> a.elev >> f >> a.range
>> a.ident;
//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");
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);
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;
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.