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