1 // multiplay.cxx -- protocol object for multiplay in Flightgear
3 // Written by Diarmuid Tyson, started February 2003.
4 // diarmuid.tyson@airservicesaustralia.com
6 // With addtions by Vivian Meazza, January 2006
8 // Copyright (C) 2003 Airservices Australia
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 #include <simgear/compiler.h>
37 #include <simgear/debug/logstream.hxx>
38 #include <simgear/math/SGMath.hxx>
40 #include <FDM/flightProperties.hxx>
41 #include <MultiPlayer/mpmessages.hxx>
43 #include "multiplay.hxx"
48 // These constants are provided so that the ident command can list file versions.
49 const char sFG_MULTIPLAY_BID[] = "$Id$";
50 const char sFG_MULTIPLAY_HID[] = FG_MULTIPLAY_HID;
52 typedef std::set<std::string> string_set;
54 class MPPropertyListener : public SGPropertyChangeListener
57 MPPropertyListener(FGMultiplay* mp) :
62 virtual void childAdded(SGPropertyNode*, SGPropertyNode*)
64 _multiplay->setPropertiesChanged();
68 FGMultiplay* _multiplay;
71 /******************************************************************
73 * Description: Constructor. Initialises the protocol and stores
74 * host and port information.
75 ******************************************************************/
76 FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, const int port) {
82 if (get_direction() == SG_IO_IN) {
84 fgSetInt("/sim/multiplay/rxport", port);
85 fgSetString("/sim/multiplay/rxhost", host.c_str());
87 } else if (get_direction() == SG_IO_OUT) {
89 fgSetInt("/sim/multiplay/txport", port);
90 fgSetString("/sim/multiplay/txhost", host.c_str());
94 mPropertiesChanged = true;
98 /******************************************************************
100 * Description: Destructor.
101 ******************************************************************/
102 FGMultiplay::~FGMultiplay () {
106 /******************************************************************
108 * Description: Enables the protocol.
109 ******************************************************************/
110 bool FGMultiplay::open() {
112 if ( is_enabled() ) {
113 SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
114 << "is already in use, ignoring" );
120 mPropertiesChanged = true;
122 MPPropertyListener* pl = new MPPropertyListener(this);
123 globals->get_props()->addChangeListener(pl, false);
127 void FGMultiplay::findProperties()
129 if (!mPropertiesChanged) {
133 mPropertiesChanged = false;
135 for (unsigned i = 0; i < FGMultiplayMgr::numProperties; ++i) {
136 const char* name = FGMultiplayMgr::sIdPropertyList[i].name;
137 SGPropertyNode* pNode = globals->get_props()->getNode(name);
142 int id = FGMultiplayMgr::sIdPropertyList[i].id;
143 if (mPropertyMap.find(id) != mPropertyMap.end()) {
144 continue; // already activated
147 mPropertyMap[id] = pNode;
148 SG_LOG(SG_NETWORK, SG_INFO, "activating MP property:" << pNode->getPath());
154 /******************************************************************
156 * Description: Prompts the multiplayer mgr to either send
157 * or receive data over the network
158 ******************************************************************/
159 bool FGMultiplay::process() {
160 using namespace simgear;
161 if (get_direction() == SG_IO_OUT) {
164 // check if we have left initialization phase. That will not provide
165 // interresting data, also the freeze in simulation time hurts the
166 // multiplayer clients
167 double sim_time = globals->get_sim_time_sec();
168 // if (sim_time < 20)
171 FlightProperties ifce;
173 // put together a motion info struct, you will get that later
174 // from FGInterface directly ...
175 FGExternalMotionData motionInfo;
177 // The current simulation time we need to update for,
178 // note that the simulation time is updated before calling all the
179 // update methods. Thus it contains the time intervals *end* time.
180 // The FDM is already run, so the states belong to that time.
181 motionInfo.time = sim_time;
183 // The typical lag will be the reciprocal of the output frequency
184 double hz = get_hz();
185 if (hz != 0) // I guess we can test a double for exact zero in this case
186 motionInfo.lag = 1/get_hz();
188 motionInfo.lag = 0.1; //??
190 // These are for now converted from lat/lon/alt and euler angles.
191 // But this should change in FGInterface ...
192 double lon = ifce.get_Longitude();
193 double lat = ifce.get_Latitude();
194 // first the aprioriate structure for the geodetic one
195 SGGeod geod = SGGeod::fromRadFt(lon, lat, ifce.get_Altitude());
196 // Convert to cartesion coordinate
197 motionInfo.position = SGVec3d::fromGeod(geod);
199 // The quaternion rotating from the earth centered frame to the
200 // horizontal local frame
201 SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)lon, (float)lat);
202 // The orientation wrt the horizontal local frame
203 float heading = ifce.get_Psi();
204 float pitch = ifce.get_Theta();
205 float roll = ifce.get_Phi();
206 SGQuatf hlOr = SGQuatf::fromYawPitchRoll(heading, pitch, roll);
207 // The orientation of the vehicle wrt the earth centered frame
208 motionInfo.orientation = qEc2Hl*hlOr;
210 if (!globals->get_subsystem("flight")->is_suspended()) {
212 motionInfo.linearVel = SG_FEET_TO_METER*SGVec3f(ifce.get_uBody(),
215 motionInfo.angularVel = SGVec3f(ifce.get_P_body(),
219 // accels, set that to zero for now.
220 // Angular accelerations are missing from the interface anyway,
221 // linear accelerations are screwed up at least for JSBSim.
222 // motionInfo.linearAccel = SG_FEET_TO_METER*SGVec3f(ifce.get_U_dot_body(),
223 // ifce.get_V_dot_body(),
224 // ifce.get_W_dot_body());
225 motionInfo.linearAccel = SGVec3f::zeros();
226 motionInfo.angularAccel = SGVec3f::zeros();
228 // if the interface is suspendend, prevent the client from
229 // wild extrapolations
230 motionInfo.linearVel = SGVec3f::zeros();
231 motionInfo.angularVel = SGVec3f::zeros();
232 motionInfo.linearAccel = SGVec3f::zeros();
233 motionInfo.angularAccel = SGVec3f::zeros();
236 // now send the properties
237 PropertyMap::iterator it;
238 for (it = mPropertyMap.begin(); it != mPropertyMap.end(); ++it) {
239 FGPropertyData* pData = new FGPropertyData;
240 pData->id = it->first;
241 pData->type = it->second->getType();
243 switch (pData->type) {
247 pData->int_value = it->second->getIntValue();
251 pData->float_value = it->second->getFloatValue();
254 case props::UNSPECIFIED:
256 // FIXME: We assume unspecified are strings for the moment.
258 const char* cstr = it->second->getStringValue();
259 int len = strlen(cstr);
263 pData->string_value = new char[len + 1];
264 strcpy(pData->string_value, cstr);
269 pData->string_value = 0;
272 //cout << " Sending property " << pData->id << " " << pData->type << " " << pData->string_value << "\n";
276 // FIXME Currently default to a float.
277 //cout << "Unknown type when iterating through props: " << pData->type << "\n";
278 pData->float_value = it->second->getFloatValue();
282 motionInfo.properties.push_back(pData);
285 FGMultiplayMgr* mpmgr = (FGMultiplayMgr*) globals->get_subsystem("mp");
286 mpmgr->SendMyPosition(motionInfo);
288 // Now remove the data
289 std::vector<FGPropertyData*>::const_iterator propIt;
290 std::vector<FGPropertyData*>::const_iterator propItEnd;
291 propIt = motionInfo.properties.begin();
292 propItEnd = motionInfo.properties.end();
294 //cout << "Deleting data\n";
296 while (propIt != propItEnd)
307 /******************************************************************
309 * Description: Closes the multiplayer mgrs to stop any further
311 ******************************************************************/
312 bool FGMultiplay::close()
314 mPropertyMap.clear();
316 FGMultiplayMgr* mgr = (FGMultiplayMgr*) globals->get_subsystem("mp");
322 if (get_direction() == SG_IO_IN) {
326 } else if (get_direction() == SG_IO_OUT) {