1 /* 2 * CRRCsim - the Charles River Radio Control Club Flight Simulator Project 3 * Copyright (C) 2004, 2005, 2007, 2008, 2010 - Jens Wilhelm Wulf (original author) 4 * Copyright (C) 2005, 2007, 2008, 2010 - Jan Reucker 5 * Copyright (C) 2007 - Martin Herrmann 6 * Copyright (C) 2006 - Todd Templeton 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 10 * as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, 20 * Boston, MA 02111-1307, USA. 21 * 22 */ 23 #ifndef TX_INTERFACE_H 24 #define TX_INTERFACE_H 25 26 #include "../mod_misc/SimpleXMLTransfer.h" 27 #include "../mod_fdm/fdm_inputs.h" 28 #include "../mod_math/ratelim.h" 29 30 31 #include <stdio.h> 32 #include <string> 33 34 // 1: show constructor/init/destructor and a chunk of serial input data 35 // for all T_TX_InterfaceSerial-based interfaces 36 // 2: plus getInputData, and continous serial input stream for 37 // all T_TX_InterfaceSerial-based interfaces 38 #define DEBUG_TX_INTERFACE (1) 39 40 41 42 // maximum number of axis supported by CRRCSim 43 #define TX_MAXAXIS (8) 44 45 /** 46 * Strings ordered like radio type enums in T_TX_Interface 47 */ 48 extern const char* RadioTypeStrings[]; 49 50 // forward declarations 51 class T_TX_Interface; 52 class T_TX_Mixer; 53 class T_AxisMapper; 54 class T_Calibration; 55 56 57 /** \brief A simple axis-number-to-function-mapper. 58 * 59 * This class maps a control function to each input device 60 * axis. The actual mapping is done through the public array 61 * <code>axis[]</code>. The integer value of <code>axis[n]</code> 62 * is the symbolic constant value of the control function assigned 63 * to axis <code>n</code>. Possible values are AILERON, ELEVATOR, 64 * THROTTLE, RUDDER and NOTHING. 65 * 66 * Besides the array the class contains the usual functions for 67 * getting the initial values from the XML config file and putting 68 * them back if they were modified by the application. 69 */ 70 class T_AxisMapper 71 { 72 public: 73 /** 74 * Initialize an empty T_AxisMapper 75 */ 76 T_AxisMapper(T_TX_Interface *parent); 77 78 /** 79 * Create a T_AxisMapper object from the given config file. 80 * It's the same as using the empty constructor and calling 81 * <code>T_AxisMapper::init(cfg, child)</code>. 82 */ 83 T_AxisMapper(T_TX_Interface *parent, SimpleXMLTransfer* cfg, std::string child); 84 85 /** 86 * Total number of functions for the axis (including NOTHING). 87 */ 88 enum { NUM_AXISFUNCS=9 }; 89 90 /** 91 * 92 */ 93 enum { NOTHING=0, AILERON=1, ELEVATOR=2, RUDDER=3, THROTTLE=4, 94 FLAP=5, SPOILER=6, RETRACT=7, PITCH=8 }; 95 96 int func[T_AxisMapper::NUM_AXISFUNCS]; ///< joystick functions 97 float inv[T_AxisMapper::NUM_AXISFUNCS]; ///< -1.0: invert, 1.0: don't invert 98 99 /** 100 * Available radio types. Ordered like RadioTypeStrings[] 101 */ 102 enum { FUTABA = 0, AIRTRONICS = 1, HITEC = 2, 103 JR = 3, COCKPIT = 4, WALKERA=5, CUSTOM = 6 }; 104 105 /** 106 * Number of available radio types 107 */ 108 enum { NR_OF_RADIO_TYPES = 7 }; 109 110 /** 111 * Loads configuration from <code>cfgfile</code>. 112 * \param cfgfile Pointer to the XML config tree 113 * \param childname Full name of the "bindings" child of the selected interface. 114 */ 115 void init(SimpleXMLTransfer* cfgfile, std::string childname); 116 117 /** 118 * Writes current configuration back into <code>cfgfile</code> 119 * \param cfgfile Pointer to the XML config tree 120 */ 121 void putBackIntoCfg(SimpleXMLTransfer* cfgfile); 122 123 /** 124 * Set the mapper to a standard configuration. 125 * \param radio_type Symbolic name of a predefined radio layout 126 */ 127 void setRadioType(int rtype); 128 129 /** 130 * selected radio type 131 */ radioType()132 int radioType() {return radio_type;}; 133 134 /** 135 * Save the current mapping to the CUSTOM entries. 136 * To make the mapping persistent you have to call 137 * <code>T_AxisMapper::putBackIntoCfg()</code> 138 * after that. 139 */ 140 void saveToCustom(); 141 142 private: 143 int getValAxis (std::string asString, int nDefault); 144 std::string child_in_cfg; 145 int radio_type; ///< selected radio type 146 int c_func[T_AxisMapper::NUM_AXISFUNCS]; ///< joystick axes (for CUSTOM mapping) 147 float c_inv[T_AxisMapper::NUM_AXISFUNCS]; ///< polarity (for CUSTOM mapping) 148 T_TX_Interface *iface; ///< pointer to the 'parent' interface 149 150 }; 151 152 153 /** \brief A simple software mixer. 154 * 155 * This class yields the settings of a software mixer 156 * and offers methods to transfer it from and to a 157 * config file. 158 * 159 * Note: Public data members are often considered as bad design, 160 * but as this class is more or less something like an extended 161 * struct I think it's o.k. in this case... 162 */ 163 class T_TX_Mixer 164 { 165 public: 166 /** 167 * Number of mixers. 168 */ 169 enum { NUM_MIXERS=4 }; 170 171 T_TX_Mixer(T_TX_Interface *parent); 172 T_TX_Mixer(T_TX_Interface *parent, 173 SimpleXMLTransfer* cfg, 174 std::string child); 175 ~T_TX_Mixer(); 176 177 int init(SimpleXMLTransfer* cfg, std::string child); 178 int reinit(SimpleXMLTransfer* cfg, bool fIsChild = true); 179 void putBackIntoCfg(SimpleXMLTransfer* config); 180 std::string getErrMsg(); 181 182 float mix_unsigned(float in, int function) const; 183 float mix_signed(float in, int function) const; 184 float mix_mixer(float *in, int function) const; 185 186 float trim_val[T_AxisMapper::NUM_AXISFUNCS]; 187 float nrate_val[T_AxisMapper::NUM_AXISFUNCS]; // normal rate 188 float srate_val[T_AxisMapper::NUM_AXISFUNCS]; // slow rate 189 float exp_val[T_AxisMapper::NUM_AXISFUNCS]; 190 191 float mtravel_val[T_AxisMapper::NUM_AXISFUNCS]; // -travel 0..-0.5 192 float ptravel_val[T_AxisMapper::NUM_AXISFUNCS]; // +travel 0..+0.5 193 194 int enabled; 195 int dr_enabled; // if dr_enabled (dual rate) use slow rate, else normal rate 196 197 int mixer_enabled[T_TX_Mixer::NUM_MIXERS]; // mixer enabled 198 int mixer_src[T_TX_Mixer::NUM_MIXERS]; // mixer source channel 199 int mixer_dst[T_TX_Mixer::NUM_MIXERS]; // mixer destination channel 200 float mixer_val[T_TX_Mixer::NUM_MIXERS]; // mixing rate 201 202 private: 203 void baseInit(); 204 float mix_exp(const float x, const float p) const; 205 std::string child_in_cfg; ///< name of the child in the config file 206 std::string errMsg; ///< error message 207 T_TX_Interface *iface; ///< pointer to 'parent' interface 208 }; 209 210 211 /** \brief Calibration data class 212 * 213 * This class holds calibration data for an interface, 214 * exchanges this data with a config file and offers 215 * a routine to apply the calibration values to a 216 * raw input value. 217 */ 218 class T_Calibration 219 { 220 public: 221 /** 222 * Create an empty T_Calibration object 223 */ 224 T_Calibration(T_TX_Interface *parent); 225 226 /** 227 * Create a T_Calibration object from the given config file. 228 * It's the same as using the empty constructor and calling 229 * <code>T_Calibration::init(cfg, child)</code>. 230 */ 231 T_Calibration(T_TX_Interface *parent, 232 SimpleXMLTransfer* cfg, 233 std::string child); 234 235 /** 236 * Loads configuration from <code>cfgfile</code>. 237 * \param cfgfile Pointer to the XML config tree 238 * \param childname Full name of the selected interface's child 239 */ 240 void init(SimpleXMLTransfer* cfgfile, std::string childname); 241 242 /** 243 * Writes current configuration back into <code>cfgfile</code> 244 * \param cfgfile Pointer to the XML config tree 245 */ 246 void putBackIntoCfg(SimpleXMLTransfer* cfgfile); 247 248 /** 249 * Apply the calibration values to a raw value 250 * \param axis The number of the axis to be calibrated. 251 * \param raw The raw, uncalibrated value. 252 * \return The calibrated axis value. 253 */ 254 float calibrate(int axis, float raw); 255 256 /** 257 * Set value for one axis. 258 */ setValMinMax(int axis,float valmin,float valmax)259 void setValMinMax(int axis, float valmin, float valmax) {val_min[axis] = valmin; val_max[axis] = valmax; }; 260 261 /** 262 * Set value for one axis. 263 */ setValMid(int axis,float val)264 void setValMid(int axis, float val) {val_mid[axis] = val;}; 265 266 /** 267 * 268 */ 269 void PrintSettings(int axis); 270 271 private: 272 std::string child_in_cfg; 273 float val_min[TX_MAXAXIS]; 274 float val_mid[TX_MAXAXIS]; 275 float val_max[TX_MAXAXIS]; 276 T_TX_Interface *iface; ///< pointer to 'parent' interface 277 }; 278 279 280 /** 281 * There already are some TX-interfaces implemented into crrcsim and therefore 282 * lots of 'if' and things in the code. I wanted to add my own interface, but 283 * I don't like 'if' that much, so I implemented a new scheme. 284 * All those 'if' could be removed if the interfaces were ported to this scheme. 285 * See tx_interface.h and the things in interface_serial2 for an example. 286 * 287 * Now I ported RCTRAN, the audio interface and PARALLEL1 to PARALLEL3 to use 288 * this scheme, too. 289 * 290 * There is a pointer to a class T_TX_Interface in crrc_main, which is 291 * initialized on startup. Depending on the configuration, it points to one of 292 * the interface-classes. That's it, no more 'if'. 293 * 294 * Jens Wilhelm Wulf, 06.01.2005 295 */ 296 class T_TX_Interface 297 { 298 public: 299 T_TX_Interface(); 300 virtual ~T_TX_Interface(); 301 302 /** 303 * Input methods. Ordered like InputMethodStrings[] 304 */ 305 enum { NUM_INPUTMETHODS=12 }; 306 enum { eIM_keyboard = 0, eIM_mouse = 1, eIM_joystick = 2, 307 eIM_rctran = 3, eIM_audio = 4, eIM_parallel = 5, 308 eIM_serial2 = 6, eIM_rctran2 = 7, eIM_serpic = 8, 309 eIM_mnav = 9, eIM_zhenhua = 10, eIM_CT6A = 11 }; 310 /** 311 * Get input method 312 */ inputMethod()313 virtual int inputMethod() { return(-1); }; 314 315 /** 316 * Initialize interface. Read further config data from a file, if necessary. 317 */ 318 virtual int init(SimpleXMLTransfer* config); 319 320 /** 321 * Write configuration back 322 */ 323 virtual void putBackIntoCfg(SimpleXMLTransfer* config); 324 325 /** 326 * Set current input data. If some value is not available, the value 327 * is not overwritten. 328 * From crrc_main.c it seems like the values should be in the range: 329 * aileron, elevator, rudder: -0.5 .. 0.5 330 * throttle: 0.0 .. 1.0 331 * others? 332 */ 333 virtual void getInputData(TSimInputs* inputs); 334 335 /** 336 * Get raw interface data. Needed for calibration. 337 * \param target Location where the data will be written to. 338 * Make sure there's enough memory reserved for 339 * getNumAxes() values! 340 */ getRawData(float * target)341 virtual void getRawData(float* target) {}; 342 343 344 /** 345 * Get the number of axis of the current interface. 346 */ getNumAxes()347 virtual int getNumAxes() {return TX_MAXAXIS;}; 348 349 350 /** 351 * Get text of error message (if any). 352 */ 353 std::string getErrMsg(); 354 355 /** 356 * Some generic interface hooks for the "software" 357 * interfaces which receive their motion commands 358 * from the event loop. 359 */ 360 virtual void move_aileron(const float x); 361 virtual void move_rudder(const float x); 362 virtual void move_elevator(const float x); 363 virtual void move_flap(const float x); 364 virtual void move_spoiler(const float x); 365 virtual void move_retract(const float x); 366 virtual void move_pitch(const float x); 367 virtual void increase_throttle(); 368 virtual void decrease_throttle(); 369 virtual void centerControls(); 370 setAxis(int axis,const float x)371 virtual void setAxis(int axis, const float x) {}; 372 373 /** 374 * Query if the interface uses the mixer. 375 */ usesMixer()376 virtual bool usesMixer() {return (mixer != NULL);}; 377 378 /** 379 * Query if the interface uses the mapper. 380 */ usesMapper()381 virtual bool usesMapper() {return (map != NULL);}; 382 383 /** 384 * Query if the interface uses the calibration class. 385 */ usesCalibration()386 virtual bool usesCalibration() {return (calib != NULL);}; 387 388 /** 389 * The parameters for scaling are public to make configuration easier. 390 */ 391 T_TX_Mixer *mixer; 392 T_Calibration *calib; 393 T_AxisMapper *map; 394 395 /** 396 * Limit values to the "mechanical" range of a servo. 397 * \param in Input value 398 * \return The input value, but clamped to -0.5 ... 0.5 399 */ 400 virtual float limit(float in); 401 402 /** 403 * Limit values to the "mechanical" range of a servo which 404 * operates only in one direction (e.g. throttle, spoiler). 405 * In addition, very small values are set to 0. 406 * 407 * \param in Input value 408 * \return The input value, but clamped to 0.0 ... 1.0 409 */ 410 virtual float limit_unsigned(float in); 411 412 /** 413 * Return the device name of the interface. The meaning 414 * of this name is interface-dependend, if it is used 415 * at all. 416 */ getDeviceName()417 virtual std::string getDeviceName() {return std::string("");}; 418 419 /** 420 * Return the speed of the interface. The meaning 421 * of this number is interface-dependend, if it is used 422 * at all. 423 */ getDeviceSpeed()424 virtual int getDeviceSpeed() {return 0;}; 425 426 /** 427 * Set the device name of the interface. The meaning 428 * of this name is interface-dependend, if it is used 429 * at all. 430 */ setDeviceName(std::string devname)431 virtual void setDeviceName(std::string devname) {}; 432 433 /** 434 * Set the speed of the interface. The meaning 435 * of this number is interface-dependend, if it is used 436 * at all. 437 */ setDeviceSpeed(int speed)438 virtual void setDeviceSpeed(int speed) {}; 439 440 /** 441 * Using this method might not be perfect, but code like this 442 * happened to be used in nearly every T_TX_Interface, so I felt 443 * like providing/using a single implementation... 444 */ 445 void CalibMixMapValues(TSimInputs* inputs, float* myArrayOfValues); 446 447 /** 448 * Pre-initialize the axis values with the values from the 449 * keyboard controller emulation. 450 */ preInitFromKeyboard(TSimInputs * inputs)451 inline void preInitFromKeyboard(TSimInputs* inputs) 452 { 453 inputs->elevator = keyb_elevator_input; 454 inputs->aileron = keyb_aileron_input; 455 inputs->rudder = keyb_rudder_input; 456 inputs->throttle = keyb_throttle_input; 457 inputs->flap = keyb_flap_input; 458 inputs->spoiler = keyb_spoiler_limited.val; 459 inputs->retract = keyb_retract_limited.val; 460 inputs->pitch = keyb_pitch_input; 461 } 462 463 /** 464 * Toggle the emulated retract axis. 465 * Each call to this function will toggle the value of the emulated retract 466 * axis. This value will be used if no "real" controller axis is 467 * configured for the retract function. 468 */ 469 virtual void toggleRetract(); 470 471 /** 472 * Toggle the emulated spoiler axis. 473 * Each call to this function will toggle the value of the emulated spoiler 474 * axis. This value will be used if no "real" controller axis is 475 * configured for the spoiler function. 476 */ 477 virtual void toggleSpoiler(); 478 479 /** 480 * Perform cyclic updates for the interface driver. 481 * \param dt elapsed time since last call 482 */ 483 void update(double dt); 484 485 /** 486 * Reset the interface. 487 */ 488 void reset(); 489 490 protected: 491 /** 492 * What kind of input device is this? 493 */ 494 int input_device; 495 496 /** 497 * error message 498 */ 499 std::string errMsg; 500 501 float keyb_aileron_input; 502 float keyb_rudder_input; 503 float keyb_elevator_input; 504 float keyb_throttle_input; 505 float keyb_flap_input; 506 float keyb_spoiler_input; 507 float keyb_retract_input; 508 float keyb_pitch_input; 509 510 CRRCMath::RateLimiter<float> keyb_retract_limited; 511 CRRCMath::RateLimiter<float> keyb_spoiler_limited; 512 }; 513 514 515 #endif 516 517