1
0
Fork 0

Progress towards AI/ATC communication without speaking all at once

This commit is contained in:
daveluff 2003-10-06 15:20:47 +00:00
parent ea0751eaa2
commit 380c69c8a0
7 changed files with 263 additions and 90 deletions

View file

@ -319,7 +319,8 @@ void FGAILocalTraffic::Update(double dt) {
trns += buf; trns += buf;
trns += " "; trns += " ";
trns += plane.callsign; trns += plane.callsign;
Transmit(trns); pending_transmission = trns;
Transmit(30.0);
responseCounter = 0.0; responseCounter = 0.0;
contactTower = false; contactTower = false;
changeFreq = true; changeFreq = true;
@ -414,7 +415,8 @@ void FGAILocalTraffic::Update(double dt) {
holdingShort = false; holdingShort = false;
string trns = "Cleared for take-off "; string trns = "Cleared for take-off ";
trns += plane.callsign; trns += plane.callsign;
Transmit(trns); pending_transmission = trns;
Transmit();
StartTaxi(); StartTaxi();
} }
//cout << "^" << flush; //cout << "^" << flush;
@ -476,7 +478,8 @@ void FGAILocalTraffic::Update(double dt) {
trns += plane.callsign; trns += plane.callsign;
trns += " on apron parking request taxi for traffic pattern"; trns += " on apron parking request taxi for traffic pattern";
//cout << "trns = " << trns << endl; //cout << "trns = " << trns << endl;
Transmit(trns); pending_transmission = trns;
Transmit();
taxiRequestCleared = false; taxiRequestCleared = false;
taxiRequestPending = true; taxiRequestPending = true;
} }
@ -504,6 +507,9 @@ void FGAILocalTraffic::Update(double dt) {
fgSetDouble("/AI/Local1/ortho-x", (ortho.ConvertToLocal(pos)).x()); fgSetDouble("/AI/Local1/ortho-x", (ortho.ConvertToLocal(pos)).x());
fgSetDouble("/AI/Local1/ortho-y", (ortho.ConvertToLocal(pos)).y()); fgSetDouble("/AI/Local1/ortho-y", (ortho.ConvertToLocal(pos)).y());
fgSetDouble("/AI/Local1/elev", pos.elev() * SG_METER_TO_FEET); 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) { void FGAILocalTraffic::RegisterTransmission(int code) {
@ -947,7 +953,8 @@ void FGAILocalTraffic::TransmitPatternPositionReport(void) {
// And add the airport name again // And add the airport name again
trns += tower->get_name(); 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) { void FGAILocalTraffic::ExitRunway(Point3D orthopos) {

View file

@ -22,6 +22,7 @@
#include <Main/fg_props.hxx> #include <Main/fg_props.hxx>
#include <simgear/math/point3d.hxx> #include <simgear/math/point3d.hxx>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/sound/soundmgr.hxx>
#include <math.h> #include <math.h>
#include <string> #include <string>
SG_USING_STD(string); SG_USING_STD(string);
@ -32,12 +33,73 @@ SG_USING_STD(string);
FGAIPlane::FGAIPlane() { FGAIPlane::FGAIPlane() {
leg = LEG_UNKNOWN; 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() { FGAIPlane::~FGAIPlane() {
} }
void FGAIPlane::Update(double dt) { 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::Bank(double angle) {
@ -55,23 +117,85 @@ void FGAIPlane::LevelWings(void) {
} }
} }
void FGAIPlane::Transmit(string msg) { void FGAIPlane::Transmit(ai_plane_callback_t callback) {
SG_LOG(SG_ATC, SG_INFO, "Transmit called, msg = " << msg); SG_LOG(SG_ATC, SG_INFO, "Transmit called for plane " << plane.callsign << ", msg = " << pending_transmission);
double user_freq0 = fgGetDouble("/radios/comm[0]/frequencies/selected-mhz"); _pending = true;
//double user_freq0 = ("/radios/comm[0]/frequencies/selected-mhz"); _callback = callback;
//comm1 is not used yet. _timeout = 0.0;
}
if(freq == user_freq0) {
//cout << "Transmitting..." << endl; void FGAIPlane::Transmit(double timeout, ai_plane_callback_t callback) {
// we are on the same frequency, so check distance to the user plane SG_LOG(SG_ATC, SG_INFO, "Timed transmit called for plane " << plane.callsign << ", msg = " << pending_transmission);
if(1) { _pending = true;
// For now (testing) assume in range !!! _callback = callback;
// TODO - implement range checking _timeout = timeout;
globals->get_ATC_display()->RegisterSingleMessage(msg, 0); }
}
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) { void FGAIPlane::RegisterTransmission(int code) {
} }

View file

@ -72,7 +72,7 @@ public:
virtual ~FGAIPlane(); virtual ~FGAIPlane();
// Run the internal calculations // Run the internal calculations
virtual void Update(double dt); void Update(double dt);
// Send a transmission *TO* the AIPlane. // Send a transmission *TO* the AIPlane.
// FIXME int code is a hack - eventually this will receive Alexander's coded messages. // FIXME int code is a hack - eventually this will receive Alexander's coded messages.
@ -85,6 +85,9 @@ public:
LandingType GetLandingOption(); LandingType GetLandingOption();
protected: protected:
// callback type for derived classes to use
typedef void (*ai_plane_callback_t) (void);
PlaneRec plane; PlaneRec plane;
double mag_hdg; // degrees - the heading that the physical aircraft is *pointing* 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 // 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 // aircraft is on the same frequency and in range. It is up to the derived classes to let ATC know
// what is going on. // 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 Bank(double angle);
void LevelWings(void); void LevelWings(void);
PatternLeg leg; 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 #endif // _FG_AI_PLANE_HXX

View file

@ -29,6 +29,10 @@
#include "ATC.hxx" #include "ATC.hxx"
#include "ATCdisplay.hxx" #include "ATCdisplay.hxx"
FGATC::FGATC() {
freqClear = true;
}
FGATC::~FGATC() { FGATC::~FGATC() {
} }
@ -39,7 +43,7 @@ void FGATC::AddPlane(string pid) {
} }
int FGATC::RemovePlane() { int FGATC::RemovePlane() {
return 0; return 0;
} }
void FGATC::SetDisplay() { void FGATC::SetDisplay() {
@ -49,7 +53,7 @@ void FGATC::SetNoDisplay() {
} }
atc_type FGATC::GetType() { atc_type FGATC::GetType() {
return INVALID; return INVALID;
} }
void FGATC::SetData(ATCData* d) { 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 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. // The repeating flag indicates whether the message should be repeated continuously or played once.
void FGATC::Render(string msg, string refname, bool repeating) { void FGATC::Render(string msg, string refname, bool repeating) {
#ifdef ENABLE_AUDIO_SUPPORT #ifdef ENABLE_AUDIO_SUPPORT
voice = (voiceOK && fgGetBool("/sim/sound/audible") voice = (voiceOK && fgGetBool("/sim/sound/audible")
&& fgGetBool("/sim/sound/voice")); && fgGetBool("/sim/sound/voice"));
if(voice) { if(voice) {
int len; int len;
unsigned char* buf = vPtr->WriteMessage((char*)msg.c_str(), len, voice); 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; delete[] buf;
} }
#endif // ENABLE_AUDIO_SUPPORT #endif // ENABLE_AUDIO_SUPPORT
if(!voice) { if(!voice) {
// first rip the underscores and the pause hints out of the string - these are for the convienience of the voice parser // 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) { 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) { void FGATC::NoRender(string refname) {
if(playing) { if(playing) {
if(voice) { if(voice) {
#ifdef ENABLE_AUDIO_SUPPORT #ifdef ENABLE_AUDIO_SUPPORT
globals->get_soundmgr()->stop(refname); globals->get_soundmgr()->stop(refname);
globals->get_soundmgr()->remove(refname); globals->get_soundmgr()->remove(refname);
#endif #endif
} else { } else {
globals->get_ATC_display()->CancelRepeatingMessage(); globals->get_ATC_display()->CancelRepeatingMessage();
} }
@ -120,21 +124,14 @@ void FGATC::NoRender(string refname) {
} }
ostream& operator << (ostream& os, atc_type atc) { ostream& operator << (ostream& os, atc_type atc) {
switch(atc) { switch(atc) {
case(INVALID): case(INVALID): return(os << "INVALID");
return(os << "INVALID"); case(ATIS): return(os << "ATIS");
case(ATIS): case(GROUND): return(os << "GROUND");
return(os << "ATIS"); case(TOWER): return(os << "TOWER");
case(GROUND): case(APPROACH): return(os << "APPROACH");
return(os << "GROUND"); case(DEPARTURE): return(os << "DEPARTURE");
case(TOWER): case(ENROUTE): return(os << "ENROUTE");
return(os << "TOWER"); }
case(APPROACH): return(os << "ERROR - Unknown switch in atc_type operator << ");
return(os << "APPROACH");
case(DEPARTURE):
return(os << "DEPARTURE");
case(ENROUTE):
return(os << "ENROUTE");
}
return(os << "ERROR - Unknown switch in atc_type operator << ");
} }

View file

@ -58,13 +58,13 @@ struct PlaneRec {
// Possible types of ATC type that the radios may be tuned to. // Possible types of ATC type that the radios may be tuned to.
// INVALID implies not tuned in to anything. // INVALID implies not tuned in to anything.
enum atc_type { enum atc_type {
INVALID, INVALID,
ATIS, ATIS,
GROUND, GROUND,
TOWER, TOWER,
APPROACH, APPROACH,
DEPARTURE, DEPARTURE,
ENROUTE ENROUTE
}; };
// DCL - new experimental ATC data store // DCL - new experimental ATC data store
@ -98,28 +98,39 @@ struct RunwayDetails {
ostream& operator << (ostream& os, atc_type atc); ostream& operator << (ostream& os, atc_type atc);
class FGATC { class FGATC {
public: public:
virtual ~FGATC(); FGATC();
virtual ~FGATC();
// Run the internal calculations
virtual void Update(double dt); // Run the internal calculations
virtual void Update(double dt);
// Add plane to a stack
virtual void AddPlane(string pid); // Add plane to a stack
virtual void AddPlane(string pid);
// Remove plane from stack
virtual int RemovePlane(); // 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 output to the display if appropriate
virtual void SetDisplay();
// Indicate that this instance should not output to the display
virtual void SetNoDisplay(); // 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(); // 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 // Set the core ATC data
void SetData(ATCData* d); void SetData(ATCData* d);
@ -145,18 +156,18 @@ public:
inline const char* get_name() {return name.c_str();} inline const char* get_name() {return name.c_str();}
inline void set_name(const string nm) {name = nm;} inline void set_name(const string nm) {name = nm;}
protected: protected:
// Render a transmission // Render a transmission
// Outputs the transmission either on screen or as audio depending on user preference // 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 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. // The repeating flag indicates whether the message should be repeated continuously or played once.
void Render(string msg, string refname, bool repeating); void Render(string msg, string refname, bool repeating);
// Cease rendering a transmission. // Cease rendering a transmission.
// Requires the sound manager refname if audio, else "". // Requires the sound manager refname if audio, else "".
void NoRender(string refname); void NoRender(string refname);
double lon, lat, elev; double lon, lat, elev;
double x, y, z; double x, y, z;
int freq; int freq;
@ -169,6 +180,8 @@ protected:
bool playing; // Indicates a message in progress bool playing; // Indicates a message in progress
bool voiceOK; // Flag - true if at least one voice has loaded OK bool voiceOK; // Flag - true if at least one voice has loaded OK
FGATCVoice* vPtr; FGATCVoice* vPtr;
bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog
}; };
inline istream& inline istream&
@ -177,7 +190,7 @@ operator >> ( istream& fin, ATCData& a )
double f; double f;
char ch; char ch;
char tp; char tp;
fin >> tp; fin >> tp;
switch(tp) { switch(tp) {
@ -201,7 +214,7 @@ operator >> ( istream& fin, ATCData& a )
a.type = INVALID; a.type = INVALID;
return fin >> skipeol; return fin >> skipeol;
} }
fin >> a.lat >> a.lon >> a.elev >> f >> a.range fin >> a.lat >> a.lon >> a.elev >> f >> a.range
>> a.ident; >> a.ident;

View file

@ -1248,6 +1248,10 @@ void FGTower::ReportRunwayVacated(string ID) {
//cout << "Report Runway Vacated Called...\n"; //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) { ostream& operator << (ostream& os, tower_traffic_type ttt) {
switch(ttt) { switch(ttt) {
case(CIRCUIT): return(os << "CIRCUIT"); case(CIRCUIT): return(os << "CIRCUIT");

View file

@ -124,6 +124,7 @@ public:
void ReportGoingAround(string ID); void ReportGoingAround(string ID);
void ReportRunwayVacated(string ID); void ReportRunwayVacated(string ID);
void ReportReadyForDeparture(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? // 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); void ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_traffic_type operation);
@ -146,15 +147,6 @@ public:
bool GetCrosswindConstraint(double& cpos); bool GetCrosswindConstraint(double& cpos);
bool GetDownwindConstraint(double& dpos); bool GetDownwindConstraint(double& dpos);
bool GetBaseConstraint(double& bpos); 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: private:
FGATCMgr* ATCmgr; FGATCMgr* ATCmgr;
@ -198,8 +190,6 @@ private:
bool display; // Flag to indicate whether we should be outputting to the ATC display. 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 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. 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. bool departed; // set true when the above needs incrementing with time, false when it doesn't.