├── Cessna 414AW
├── C414_Mixture_Controller.wasm
├── panel.GTN750
│ └── PANEL.CFG
├── panel.GTN750XI
│ └── PANEL.CFG
├── panel
│ └── PANEL.CFG
└── sound.xml
├── LICENSE.txt
├── README.md
├── Seneca V
└── SenecaV_Mixture_Controller.wasm
├── Turbo Arrow
├── TurboArrow_Mixture_Controller.wasm
└── panel.cfg
├── Turbo Bonanza
├── Bonanza_Mixture_Controller.wasm
└── panel.cfg
├── instructions.pdf
└── src
├── C414AW
├── C414_Mixture_Controller.cpp
├── FdController.h
├── FdGauge.h
├── Mixture_Controller.sln
├── Mixture_Controller.vcxproj
├── Mixture_Controller.vcxproj.user
├── PidController.h
├── SimConnectDefs.h
├── common.h
└── turbocharger.h
├── SenecaV
├── FdController.h
├── FdGauge.h
├── Mixture_Controller.sln
├── Mixture_Controller.vcxproj
├── Mixture_Controller.vcxproj.user
├── PidController.h
├── SenecaV_Mixture_Controller.cpp
├── SimConnectDefs.h
├── common.h
└── turbocharger.h
├── TurboArrow
├── FdController.h
├── FdGauge.h
├── Mixture_Controller.sln
├── Mixture_Controller.vcxproj
├── Mixture_Controller.vcxproj.user
├── PidController.h
├── SimConnectDefs.h
├── TurboArrow_Mixture_Controller.cpp
├── common.h
└── turbocharger.h
└── TurboBonanza
├── Bonanza_Mixture_Controller.cpp
├── FdController.h
├── FdGauge.h
├── Mixture_Controller.sln
├── Mixture_Controller.vcxproj
├── Mixture_Controller.vcxproj.user
├── PidController.h
├── SimConnectDefs.h
├── common.h
└── turbocharger.h
/Cessna 414AW/C414_Mixture_Controller.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WxMarc/TurboEngineMixtureController/12a9b40bd4ee75bda1be05fdef31debdbec8cf7f/Cessna 414AW/C414_Mixture_Controller.wasm
--------------------------------------------------------------------------------
/Cessna 414AW/panel.GTN750/PANEL.CFG:
--------------------------------------------------------------------------------
1 | //--------------------------Designed by FLYSIMWARE! 2022 ------------------------------
2 |
3 |
4 | [Vcockpit01]
5 | size_mm = 650,768
6 | pixel_size = 650,768
7 | texture = SCREEN_PMSGTN750U1
8 | htmlgauge00=NavSystems/flysimware_Cessna_414A/pms50_gtn750_int/gtn750_int.html, 0, 0, 650,768
9 |
10 | [Vcockpit02]
11 | size_mm = 650,768
12 | pixel_size = 650,768
13 | texture = SCREEN_PMSGTN750U2
14 | htmlgauge00=NavSystems/flysimware_Cessna_414A/pms50_gtn750_int/gtn750_int.html?index=2, 0, 0, 650,768
15 |
16 |
17 | [Vcockpit03]
18 | size_mm = 512,512
19 | pixel_size = 512,512
20 | texture = RNAVNAV2
21 | htmlgauge00=Generic/Radios/RNAV_NAV2/RNAVNAV.html, 0,0,512,512
22 |
23 |
24 | [Vcockpit04]
25 | size_mm = 512,512
26 | pixel_size = 512,512
27 | texture = CFS1000A
28 | htmlgauge00=Generic/Radios/CFS1000A/CFS1000A.html, 0,0,512,512
29 |
30 |
31 |
32 | [Vcockpit05]
33 | size_mm = 512,512
34 | pixel_size = 512,512
35 | texture = DMEC1077B
36 | htmlgauge00=Generic/Radios/DME_C1077B/DMEC1077B.html, 0,0,512,512
37 |
38 |
39 | [Vcockpit06]
40 | size_mm = 512,512
41 | pixel_size = 512,512
42 | texture = COMM2
43 | htmlgauge00=Generic/Radios/COMM2_C1038A/C1038A.html, 0,0,512,512
44 |
45 |
46 | [Vcockpit07]
47 | size_mm = 512,512
48 | pixel_size = 512,512
49 | texture = ADF1
50 | htmlgauge00=Generic/Radios/ADF1_C1046A/C1046A.html, 0,0,512,512
51 |
52 |
53 | [Vcockpit08]
54 | size_mm = 512,512
55 | pixel_size = 512,512
56 | texture = M811B
57 | htmlgauge00=Generic/Radios/DAVTRON_M811B/M811B.html, 0,0,512,512
58 |
59 |
60 | [Vcockpit09]
61 | size_mm = 2048,2048
62 | pixel_size = 2048,2048
63 | texture = EFB_TABLET_CLICKABLE
64 | htmlgauge00=Generic/Radios/EFB_TABLET/EFBTABLET.html, 0,0,2048,2048
65 |
66 |
67 | [VCockpit10]
68 | size_mm = 440,100
69 | pixel_size = 440,100
70 | texture = GTX345_LCD_SCREEN
71 | htmlgauge00=Generic/Radios/AS330/AS330.html, 0,0,440, 100
72 |
73 |
74 | [Vcockpit11]
75 | size_mm = 2000,2000
76 | pixel_size = 2000,2000
77 | texture = GARMIN_AP_LCD_SCREEN
78 | gauge00=FLYSIMWARE_GARMIN_605_AP!GARMIN_605_AutoPilot, 0,0, 2000, 526
79 |
80 |
81 | [Vcockpit12]
82 | size_mm = 512,512
83 | pixel_size = 512,512
84 | texture = ALTSET
85 | htmlgauge00=Generic/Radios/ALT_AL300/AL300.html, 0,0,512,512
86 |
87 |
88 | [Vcockpit13]
89 | size_mm=0,0
90 | pixel_size=0,0
91 | texture=$PFD
92 | background_color=0,0,0
93 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=C414_Mixture_Controller.wasm&wasm_gauge=FdGauge,0,0,1,1
94 |
95 |
96 | [VPainting01]
97 | size_mm = 2048,512
98 | texture = $RegistrationNumber
99 | location = exterior
100 |
101 | painting00=Registration/Registration.html?font_color=black, 0, 0, 2048, 512
102 |
103 | [VPainting02]
104 | size_mm = 512,128
105 | texture = $RegistrationNumber
106 | location = interior
107 |
108 | painting00=Registration/Registration.html?font_color=white, 0, 0, 512, 128
--------------------------------------------------------------------------------
/Cessna 414AW/panel.GTN750XI/PANEL.CFG:
--------------------------------------------------------------------------------
1 | //--------------------------Designed by FLYSIMWARE! 2022 ------------------------------
2 |
3 |
4 | [Vcockpit01]
5 | size_mm = 650,768
6 | pixel_size = 650,768
7 | texture = SCREEN_TDSGTNXI750U1
8 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=Gauge/TDSGTNXiGaugeModule.wasm&wasm_gauge=GTNXI750U1, 0,0,650,768
9 |
10 |
11 | [VCockpit02]
12 | size_mm = 650,768
13 | pixel_size = 650,768
14 | texture = SCREEN_TDSGTNXI750U2
15 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=Gauge/TDSGTNXiGaugeModule.wasm&wasm_gauge=GTNXI750U2, 0,0,650,768
16 |
17 |
18 | [Vcockpit03]
19 | size_mm = 512,512
20 | pixel_size = 512,512
21 | texture = RNAVNAV2
22 | htmlgauge00=Generic/Radios/RNAV_NAV2/RNAVNAV.html, 0,0,512,512
23 |
24 |
25 | [Vcockpit04]
26 | size_mm = 512,512
27 | pixel_size = 512,512
28 | texture = CFS1000A
29 | htmlgauge00=Generic/Radios/CFS1000A/CFS1000A.html, 0,0,512,512
30 |
31 |
32 |
33 | [Vcockpit05]
34 | size_mm = 512,512
35 | pixel_size = 512,512
36 | texture = DMEC1077B
37 | htmlgauge00=Generic/Radios/DME_C1077B/DMEC1077B.html, 0,0,512,512
38 |
39 |
40 | [Vcockpit06]
41 | size_mm = 512,512
42 | pixel_size = 512,512
43 | texture = COMM2
44 | htmlgauge00=Generic/Radios/COMM2_C1038A/C1038A.html, 0,0,512,512
45 |
46 |
47 | [Vcockpit07]
48 | size_mm = 512,512
49 | pixel_size = 512,512
50 | texture = ADF1
51 | htmlgauge00=Generic/Radios/ADF1_C1046A/C1046A.html, 0,0,512,512
52 |
53 |
54 | [Vcockpit08]
55 | size_mm = 512,512
56 | pixel_size = 512,512
57 | texture = M811B
58 | htmlgauge00=Generic/Radios/DAVTRON_M811B/M811B.html, 0,0,512,512
59 |
60 |
61 | [Vcockpit09]
62 | size_mm = 2048,2048
63 | pixel_size = 2048,2048
64 | texture = EFB_TABLET_CLICKABLE
65 | htmlgauge00=Generic/Radios/EFB_TABLET/EFBTABLET.html, 0,0,2048,2048
66 |
67 |
68 | [VCockpit10]
69 | size_mm = 440,100
70 | pixel_size = 440,100
71 | texture = GTX345_LCD_SCREEN
72 | htmlgauge00=Generic/Radios/AS330/AS330.html, 0,0,440, 100
73 |
74 |
75 | [Vcockpit11]
76 | size_mm = 2000,2000
77 | pixel_size = 2000,2000
78 | texture = GARMIN_AP_LCD_SCREEN
79 | gauge00=FLYSIMWARE_GARMIN_605_AP!GARMIN_605_AutoPilot, 0,0, 2000, 526
80 |
81 |
82 | [Vcockpit12]
83 | size_mm = 512,512
84 | pixel_size = 512,512
85 | texture = ALTSET
86 | htmlgauge00=Generic/Radios/ALT_AL300/AL300.html, 0,0,512,512
87 |
88 |
89 | [Vcockpit13]
90 | size_mm=0,0
91 | pixel_size=0,0
92 | texture=$PFD
93 | background_color=0,0,0
94 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=C414_Mixture_Controller.wasm&wasm_gauge=FdGauge,0,0,1,1
95 |
96 |
97 |
98 |
99 | [VPainting01]
100 | size_mm = 2048,512
101 | texture = $RegistrationNumber
102 | location = exterior
103 |
104 | painting00=Registration/Registration.html?font_color=black, 0, 0, 2048, 512
105 |
106 | [VPainting02]
107 | size_mm = 512,128
108 | texture = $RegistrationNumber
109 | location = interior
110 |
111 | painting00=Registration/Registration.html?font_color=white, 0, 0, 512, 128
--------------------------------------------------------------------------------
/Cessna 414AW/panel/PANEL.CFG:
--------------------------------------------------------------------------------
1 | //--------------------------Designed by FLYSIMWARE! 2021 ------------------------------
2 |
3 |
4 |
5 |
6 | [VCockpit01]
7 | size_mm = 320,234
8 | pixel_size = 320,234
9 | texture = AS530_Screen
10 | htmlgauge00=NavSystems/GPS/AS530/AS530.html, 0, 0, 320,234
11 |
12 |
13 | [Vcockpit02]
14 | size_mm = 512,512
15 | pixel_size = 512,512
16 | texture = RNAVNAV2
17 | htmlgauge00=Generic/Radios/RNAV_NAV2/RNAVNAV.html, 0,0,512,512
18 |
19 |
20 | [Vcockpit03]
21 | size_mm = 512,512
22 | pixel_size = 512,512
23 | texture = CFS1000A
24 | htmlgauge00=Generic/Radios/CFS1000A/CFS1000A.html, 0,0,512,512
25 |
26 |
27 |
28 | [Vcockpit04]
29 | size_mm = 512,512
30 | pixel_size = 512,512
31 | texture = DMEC1077B
32 | htmlgauge00=Generic/Radios/DME_C1077B/DMEC1077B.html, 0,0,512,512
33 |
34 |
35 | [Vcockpit05]
36 | size_mm = 512,512
37 | pixel_size = 512,512
38 | texture = COMM2
39 | htmlgauge00=Generic/Radios/COMM2_C1038A/C1038A.html, 0,0,512,512
40 |
41 |
42 | [Vcockpit06]
43 | size_mm = 512,512
44 | pixel_size = 512,512
45 | texture = ADF1
46 | htmlgauge00=Generic/Radios/ADF1_C1046A/C1046A.html, 0,0,512,512
47 |
48 |
49 | [Vcockpit07]
50 | size_mm = 512,512
51 | pixel_size = 512,512
52 | texture = M811B
53 | htmlgauge00=Generic/Radios/DAVTRON_M811B/M811B.html, 0,0,512,512
54 |
55 |
56 | [Vcockpit08]
57 | size_mm = 2048,2048
58 | pixel_size = 2048,2048
59 | texture = EFB_TABLET_CLICKABLE
60 | htmlgauge00=Generic/Radios/EFB_TABLET/EFBTABLET.html, 0,0,2048,2048
61 |
62 | [Vcockpit09]
63 | size_mm=0,0
64 | pixel_size=0,0
65 | texture=$PFD
66 | background_color=0,0,0
67 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=C414_Mixture_Controller.wasm&wasm_gauge=FdGauge,0,0,1,1
68 |
69 |
70 |
71 | [VPainting01]
72 | size_mm = 2048,512
73 | texture = $RegistrationNumber
74 | location = exterior
75 |
76 | painting00=Registration/Registration.html?font_color=black, 0, 0, 2048, 512
77 |
78 | [VPainting02]
79 | size_mm = 512,128
80 | texture = $RegistrationNumber
81 | location = interior
82 |
83 | painting00=Registration/Registration.html?font_color=white, 0, 0, 512, 128
--------------------------------------------------------------------------------
/Cessna 414AW/sound.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TurboEngineMixtureController
2 | Mixture Controller for Turbocharged Piston Engines in MSFS 2020
3 |
4 | NOTE: If using the Working Title Technology add-on for the PMS50 GTN750 in the Carenado Seneca V, the mixture controller
5 | WASM file and the panel.cfg must be edited in the "pms50-gtn750wtt-aircraft-carenado-seneca" folder in your community
6 | folder, rather than following the procedure in the included PDF instructions. The PMS50 WTT add-on replaces the default
7 | panel in the Seneca V, and it will override the mixture controller if it is located in the official Carenado installation
8 | directory.
9 |
10 | Microsoft Flight Simulator's fuel mixture logic for turbocharged piston engines produces excessively rich fuel mixtures
11 | and loss of power at high altitudes. The default logic appears to calculate fuel/air mixture based on ambient density.
12 | In a real-world turbocharged engine, the turbocharger compresses air before it enters the cylinders. This increases the
13 | density of air in the engine significantly above ambient density. As such, the default logic in MSFS does not properly
14 | represent the behavior of a turbocharged engine. A more detailed description of the problem, with data extracted from
15 | MSFS, is available here:
16 |
17 | https://forums.flightsimulator.com/t/inaccurate-mixture-behavior-on-turbocharged-piston-engines/411095
18 |
19 | This package includes mixture controllers for four turbocharged piston aircraft that are currently available for
20 | MSFS:
21 |
22 | - Robert Young's Turbonormalized Bonanza
23 | - Just Flight Turbo Arrow
24 | - Carenado Seneca V
25 | - Flysimware 414AW
26 |
27 | The controllers calculate a target fuel/air mixture based on the user's hardware mixture lever setting and the air density
28 | in the intake manifold (rather than the ambient density). This allows the mixture to be managed according to the POH,
29 | setting mixture at full-rich throughout the climb instead of leaning the mixture to maintain proper power.
30 |
31 | Please see the included instructions.pdf for detailed instructions on how to install the controllers for any of the
32 | aircraft mentioned above.
33 |
34 | DEPENDENCIES:
35 | Successful installtion of the mixture controller requires updating the layout.json file for the airplane to include a
36 | reference to the .wasm file which is added to the airplane's panel folder. I recommend using the MSFS Layout Generator for
37 | this. It is a quick drag-and-drop utility that updates layout files painlessly. See the link below for more information.
38 |
39 | https://github.com/HughesMDflyer4/MSFSLayoutGenerator
40 |
41 | CAUTION:
42 | Successfully installing these controllers will require copying and pasting the files to the proper folders and updating
43 | each airplane's panel.cfg and layout.json files. If you are not comfortable taking these steps, do not use this code.
44 |
45 | These controllers are designed to work with a hardware axis (throttle quadrant, HOTAS with mixture axis, etc). If you do
46 | not have a hardware mixture axis, or if you have automixture enabled in MSFS settings, this controller will not add much
47 | value to your experience.
48 |
49 | KNOWN ISSUES:
50 | The mixture lever displayed in the virtual cockpit will not reflect the mixture setting on your hardware axis. The virtual
51 | cockpit lever displays the mixture setting in the sim, which the controller code calculates based on your hardware axis
52 | position and the density in the intake manifold. At high altitudes, it is quite possible to see the virtual cockpit lever
53 | set to 25% mixture while the hardware axis is set to full-rich. This is an artifact of the incorrect mixture logic in the
54 | core sim. It is possible to map the virtual cockpit mixture lever to the hardware axis, but this controller currently does
55 | not implement that feature.
56 |
--------------------------------------------------------------------------------
/Seneca V/SenecaV_Mixture_Controller.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WxMarc/TurboEngineMixtureController/12a9b40bd4ee75bda1be05fdef31debdbec8cf7f/Seneca V/SenecaV_Mixture_Controller.wasm
--------------------------------------------------------------------------------
/Turbo Arrow/TurboArrow_Mixture_Controller.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WxMarc/TurboEngineMixtureController/12a9b40bd4ee75bda1be05fdef31debdbec8cf7f/Turbo Arrow/TurboArrow_Mixture_Controller.wasm
--------------------------------------------------------------------------------
/Turbo Arrow/panel.cfg:
--------------------------------------------------------------------------------
1 | // Panel Configuration file
2 | // JF_PA28_TurboArrow
3 |
4 | [VCockpit01]
5 | size_mm=1024,768
6 | pixel_size=1024,768
7 | texture=$TEST
8 | background_color=0,0,255
9 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=JF_PA28_TurboArrow_WASM.wasm&wasm_gauge=SystemWatch, 0,0,1024,768
10 |
11 | [VCockpit02]
12 | size_mm=347,95
13 | pixel_size=347,95
14 | texture=SCREEN_GPS
15 | background_color=0,0,255
16 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=JF_PA28_TurboArrow_WASM.wasm&wasm_gauge=GPS100, 0,0,347,95
17 |
18 | [VCockpit03]
19 | size_mm=421,88
20 | pixel_size=421,88
21 | texture=SCREEN_KN62
22 | background_color=0,0,255
23 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=JF_PA28_TurboArrow_WASM.wasm&wasm_gauge=KN62, 0,0,421,88
24 |
25 | [VCockpit04]
26 | size_mm=256,90
27 | pixel_size=256,90
28 | texture=SCREEN_TIMER
29 | background_color=0,0,255
30 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=JF_PA28_TurboArrow_WASM.wasm&wasm_gauge=AstroTechTimer, 0,0,256,90
31 |
32 | [VCockpit05]
33 | size_mm = 350,190
34 | pixel_size = 350,190
35 | texture = SCREEN_GNS430
36 | htmlgauge00=NavSystems/GPS/AS430/AS430.html, 4, 3, 342, 183
37 |
38 | [VCockpit06]
39 | size_mm = 320,234
40 | pixel_size = 320,234
41 | texture = SCREEN_GNS530
42 | htmlgauge00=NavSystems/GPS/AS530/AS530.html, 4, 3, 316, 231
43 |
44 | [VCockpit07]
45 | size_mm=1024,1024
46 | pixel_size=1024,1024
47 | texture=SCREEN_EFB
48 | background_color=0,0,255
49 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=JF_PA28_TurboArrow_WASM.wasm&wasm_gauge=EFB, 0,0,1024,1024
50 |
51 | [VCockpit08]
52 | Background_color = 0,0,0
53 | size_mm = 650,768
54 | visible = 0
55 | pixel_size = 650,768
56 | texture=SCREEN_GTN
57 |
58 | htmlgauge00= NavSystems/justflight-aircraft-pa28-turboarrow/pms50_gtn750_int/gtn750_int.html, 0,0,650,768
59 |
60 | [VCockpit09]
61 | Background_color = 0,0,0
62 | size_mm = 650,290
63 | visible = 0
64 | pixel_size = 650,290
65 | texture = SCREEN_GTN650
66 |
67 | htmlgauge00= NavSystems/justflight-aircraft-pa28-turboarrow/pms50_gtn750_int/gtn650_int.html?index=2, 0, 0, 650,290
68 |
69 | [Vcockpit10]
70 | size_mm = 650,768
71 | pixel_size = 650,768
72 | texture = SCREEN_TDSGTNXI750
73 | background_color=0,0,0
74 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=Gauge/TDSGTNXiGaugeModule.wasm&wasm_gauge=GTNXI750U1, 0,0,650,768
75 |
76 | [Vcockpit11]
77 | size_mm = 650,287
78 | pixel_size = 650,287
79 | texture = SCREEN_TDSGTNXI650
80 | background_color=0,0,0
81 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=Gauge/TDSGTNXiGaugeModule.wasm&wasm_gauge=GTNXI650U2, 0,0,650,287
82 |
83 | [Vcockpit12]
84 | size_mm=0,0
85 | pixel_size=0,0
86 | texture=$PFD
87 | background_color=0,0,0
88 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=TurboArrow_Mixture_Controller.wasm&wasm_gauge=FdGauge,0,0,1,1
89 |
90 | [VIEWS]
91 | VIEW_FORWARD_DIR=2.000, 0.000, 0.000
92 |
93 | [Color]
94 | Day=255,255,255
95 | Night=255,255,255
96 | Luminous=201,64,64
97 |
98 |
99 | [VPainting01]
100 | size_mm = 512,128
101 | texture = $RegistrationNumberInt
102 | location = interior
103 |
104 | painting00=Registration/Registration.html?font_color=white, 0, 0, 512, 128
105 |
106 | [VPainting02]
107 | size_mm = 2048,512
108 | texture = $RegistrationNumbersExt
109 | location = interior
110 |
111 | painting00=Registration/Registration.html?font_color=black&font_style=bold-italic, 0, 0, 2048, 512
112 |
113 | [VPainting03]
114 | size_mm = 2048,512
115 | texture = $RegistrationNumbersExt
116 | location = exterior
117 |
118 | painting00=Registration/Registration.html?font_color=black&font_style=bold-italic, 0, 0, 2048, 512
--------------------------------------------------------------------------------
/Turbo Bonanza/Bonanza_Mixture_Controller.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WxMarc/TurboEngineMixtureController/12a9b40bd4ee75bda1be05fdef31debdbec8cf7f/Turbo Bonanza/Bonanza_Mixture_Controller.wasm
--------------------------------------------------------------------------------
/Turbo Bonanza/panel.cfg:
--------------------------------------------------------------------------------
1 | // Panel Configuration file
2 | // Asobo BONANZA_G36
3 |
4 | [VCockpit01]
5 | size_mm=1024,768
6 | pixel_size=1024,768
7 | texture=$AS1000_PFD_1
8 | background_color=42,42,40
9 | htmlgauge00=NavSystems/AS1000/PFD/AS1000_PFD.html, 0,0,1024,768
10 |
11 | [VCockpit02]
12 | size_mm=1024,768
13 | pixel_size=1024,768
14 | texture=$AS1000_PFD_2
15 | background_color=42,42,40
16 | htmlgauge00=NavSystems/AS1000/MFD/AS1000_MFD.html, 0,0,1024,768
17 |
18 | [VCockpit03]
19 | size_mm=317,109
20 | pixel_size=317,109
21 | texture=$OAT_Screen
22 | htmlgauge00=Generic/Misc/DigitalOAT/DigitalOAT.html, 0,0,317,109
23 |
24 | [Vcockpit04]
25 | size_mm=0,0
26 | pixel_size=0,0
27 | texture=$PFD
28 | background_color=0,0,0
29 | htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=Bonanza_Mixture_Controller.wasm&wasm_gauge=FdGauge,0,0,1,1
30 |
31 | [VPainting01]
32 | size_mm = 256, 64
33 | texture = $RegistrationNumber
34 | location = interior
35 | painting00=Registration/Registration.html?font_color=white, 0, 0, 256, 64
36 |
37 | [VPainting02]
38 | size_mm = 1024, 256
39 | texture = $RegistrationNumber
40 | location = exterior
41 | painting00=Registration/Registration.html?font_color=white, 0, 0, 1024, 256
--------------------------------------------------------------------------------
/instructions.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WxMarc/TurboEngineMixtureController/12a9b40bd4ee75bda1be05fdef31debdbec8cf7f/instructions.pdf
--------------------------------------------------------------------------------
/src/C414AW/C414_Mixture_Controller.cpp:
--------------------------------------------------------------------------------
1 |
2 | #ifdef _MSC_VER
3 | #define snprintf _snprintf_s
4 | #elif !defined(__MINGW32__)
5 | #include
6 | #endif
7 |
8 | #include "FdGauge.h"
9 |
10 | FdGauge FD_GAUGE;
11 |
12 |
13 | // ------------------------
14 | // Callbacks
15 | extern "C" {
16 |
17 | MSFS_CALLBACK bool FdGauge_gauge_callback(FsContext ctx, int service_id, void* pData)
18 | {
19 | switch (service_id)
20 | {
21 | case PANEL_SERVICE_PRE_INSTALL:
22 | {
23 | return true;
24 | }
25 | break;
26 | case PANEL_SERVICE_POST_INSTALL:
27 | {
28 | return FD_GAUGE.InitializeFD();
29 | }
30 | break;
31 | case PANEL_SERVICE_PRE_DRAW:
32 | {
33 | sGaugeDrawData* drawData = static_cast(pData);
34 | return FD_GAUGE.OnUpdate(drawData->dt);
35 | }
36 | break;
37 | case PANEL_SERVICE_PRE_KILL:
38 | {
39 | FD_GAUGE.KillFD();
40 | return true;
41 | }
42 | break;
43 | }
44 | return false;
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/C414AW/FdController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef FDCTRL
4 | #define FDCTRL
5 |
6 | #include "common.h"
7 | #include "PidController.h"
8 |
9 | class FdController
10 | {
11 | private:
12 | SimVars* simVars;
13 |
14 | ///
15 | /// An instance of the mixture PID controller.
16 | ///
17 | PidController* mixtureController[2];
18 |
19 | ///
20 | /// The current mixture control axis, from -16384 to 16384.
21 | ///
22 | int mixtureAxis[2] = { 16384, 16384 };
23 |
24 | bool enabled = true;
25 |
26 | ///
27 | /// Calculates and updates mixture according to target and PIDs.
28 | ///
29 | ///
30 | void updateMixture(double deltaTime) {
31 | EngineControlData controls;
32 | controls.mixtureLeft = this->getDesiredMixture(0, deltaTime);
33 | controls.mixtureRight = this->getDesiredMixture(1, deltaTime);
34 | SimConnect_SetDataOnSimObject(hSimConnect, DataTypes::EngineControls, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(EngineControlData), &controls);
35 | }
36 |
37 | ///
38 | /// Gets the mixture lever position to achieve the desired fuel/air mixture ratio
39 | ///
40 | /// Desired mixture setting
41 | double getDesiredMixture(int idx, double deltaTime) {
42 | double mixtureLeverPerc = 100.0 * (this->mixtureAxis[idx] + 16384) / 32768.0;
43 |
44 | if (!enabled) {
45 | return mixtureLeverPerc;
46 | }
47 |
48 | double targetFuelAirMixture = Turbocharger::getTargetFuelAirRatio(mixtureLeverPerc);
49 | double simFuelAirMixture = this->simVars->getFuelAirRatio(idx + 1);
50 | //printf("Target Mixture: %1.3f\nSim Mixture: %1.3f\n", targetFuelAirMixture, simFuelAirMixture);
51 |
52 | double error = targetFuelAirMixture - simFuelAirMixture;
53 | double pidOut = 0.0;
54 | pidOut = this->mixtureController[idx]->GetOutput(error, deltaTime);
55 | //printf("Output from PID Controller: %2.3f\n", error);
56 | return max(0, min(100, this->simVars->getMixtureLeverPosition(idx + 1) + pidOut));
57 | }
58 |
59 | void updateVisibleMixture(int idx) {
60 | int targetMixture = this->mixtureAxis[idx];
61 | double mixtureLeverPerc = 100.0 * (targetMixture + 16384) / 32768.0;
62 | if (idx == 0) {
63 | this->simVars->setMixture1Pos(mixtureLeverPerc);
64 | }
65 | else {
66 | this->simVars->setMixture2Pos(mixtureLeverPerc);
67 | }
68 | }
69 |
70 | public:
71 | void init()
72 | {
73 | printf("FdController init");
74 |
75 | this->simVars = new SimVars();
76 |
77 | float p = 150.0;
78 | float i = 580.0;
79 | float d = 0.0;
80 | this->mixtureController[0] = new PidController(p, i, d, -5, 5);
81 | this->mixtureController[1] = new PidController(p, i, d, -5, 5);
82 | }
83 |
84 | void update(int mixtureAxis[], double deltaTime)
85 | {
86 | this->mixtureAxis[0] = mixtureAxis[0];
87 | this->mixtureAxis[1] = mixtureAxis[1];
88 | this->updateMixture(deltaTime);
89 | this->updateVisibleMixture(0);
90 | this->updateVisibleMixture(1);
91 | this->simVars->setFadecActiveFlag();
92 | }
93 | };
94 |
95 | FdController FdCtrlInstance;
96 |
97 | #endif // !FDCTRL
98 |
--------------------------------------------------------------------------------
/src/C414AW/FdGauge.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | #ifndef FDGAUGE
5 | #define FDGAUGE
6 |
7 | #ifndef __INTELLISENSE__
8 | # define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
9 | # define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
10 | #else
11 | # define MODULE_EXPORT
12 | # define MODULE_WASM_MODNAME(mod)
13 | # define __attribute__(x)
14 | # define __restrict__
15 | #endif
16 |
17 | #include "common.h"
18 | #include "FdController.h"
19 |
20 | const int MIN_MIX = -16384;
21 | const int MAX_MIX = 16384;
22 | const int MIX_STEP = 256;
23 |
24 | int globalMixtureAxis[2] = { MAX_MIX, MAX_MIX };
25 |
26 | class FdGauge
27 | {
28 | private:
29 |
30 | bool isConnected = false;
31 |
32 | ///
33 | /// Registers all the mixture SimConnect client events.
34 | ///
35 | void RegisterMixtureClientEvents()
36 | {
37 | printf("Registering mixture events...\r\n");
38 |
39 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixtureSet, "AXIS_MIXTURE_SET");
40 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixture1Set, "AXIS_MIXTURE1_SET");
41 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixture2Set, "AXIS_MIXTURE2_SET");
42 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSetBest, "MIXTURE_SET_BEST");
43 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSet, "MIXTURE_SET");
44 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureRich, "MIXTURE_RICH");
45 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncr, "MIXTURE_INCR");
46 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncrSmall, "MIXTURE_INCR_SMALL");
47 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecrSmall, "MIXTURE_DECR_SMALL");
48 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecr, "MIXTURE_DECR");
49 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureLean, "MIXTURE_LEAN");
50 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Set, "MIXTURE1_SET");
51 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Rich, "MIXTURE1_RICH");
52 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Incr, "MIXTURE1_INCR");
53 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1IncrSmall, "MIXTURE1_INCR_SMALL");
54 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1DecrSmall, "MIXTURE1_DECR_SMALL");
55 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Decr, "MIXTURE1_DECR");
56 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Lean, "MIXTURE1_LEAN");
57 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Set, "MIXTURE2_SET");
58 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Rich, "MIXTURE2_RICH");
59 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Incr, "MIXTURE2_INCR");
60 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2IncrSmall, "MIXTURE2_INCR_SMALL");
61 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2DecrSmall, "MIXTURE2_DECR_SMALL");
62 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Decr, "MIXTURE2_DECR");
63 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Lean, "MIXTURE2_LEAN");
64 | }
65 |
66 | ///
67 | /// Registers the SimConnect mixture event group for capture.
68 | ///
69 | void RegisterMixtureEventGroup()
70 | {
71 | printf("Registering mixture event group...\r\n");
72 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixtureSet, TRUE);
73 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixture1Set, TRUE);
74 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixture2Set, TRUE);
75 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSetBest, TRUE);
76 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSet, TRUE);
77 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureRich, TRUE);
78 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncr, TRUE);
79 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncrSmall, TRUE);
80 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecrSmall, TRUE);
81 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecr, TRUE);
82 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureLean, TRUE);
83 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Set, TRUE);
84 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Rich, TRUE);
85 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Incr, TRUE);
86 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1IncrSmall, TRUE);
87 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1DecrSmall, TRUE);
88 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Decr, TRUE);
89 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Lean, TRUE);
90 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Set, TRUE);
91 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Rich, TRUE);
92 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Incr, TRUE);
93 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2IncrSmall, TRUE);
94 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2DecrSmall, TRUE);
95 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Decr, TRUE);
96 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Lean, TRUE);
97 |
98 | SimConnect_SetNotificationGroupPriority(hSimConnect, EventGroups::Mixture, SIMCONNECT_GROUP_PRIORITY_HIGHEST_MASKABLE);
99 | }
100 |
101 | ///
102 | /// Initializes the connection to SimConnect.
103 | ///
104 | /// True if successful, false otherwise.
105 | bool InitializeSimConnect()
106 | {
107 | printf("Connecting to SimConnect...\r\n");
108 | if (SUCCEEDED(SimConnect_Open(&hSimConnect, "FdGauge", nullptr, 0, 0, 0)))
109 | {
110 | printf("SimConnect connected.\r\n");
111 |
112 | this->RegisterMixtureClientEvents();
113 | this->RegisterMixtureEventGroup();
114 |
115 | SimConnect_AddToDataDefinition(hSimConnect, DataTypes::EngineControls, "GENERAL ENG MIXTURE LEVER POSITION:1", "Percent");
116 | SimConnect_AddToDataDefinition(hSimConnect, DataTypes::EngineControls, "GENERAL ENG MIXTURE LEVER POSITION:2", "Percent");
117 |
118 | printf("SimConnect registrations complete.\r\n");
119 | return true;
120 | }
121 |
122 | printf("SimConnect failed.\r\n");
123 |
124 | return false;
125 | }
126 |
127 | ///
128 | /// A callback used for handling SimConnect updates.
129 | ///
130 | /// The update data sent by SimConnect.
131 | /// The size of the SimConnect data structure.
132 | /// A pointer specified by the client.
133 | static void CALLBACK HandleAxisEvent(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
134 | {
135 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EVENT)
136 | {
137 | SIMCONNECT_RECV_EVENT* evt = static_cast(pData);
138 | if (evt->uGroupID == EventGroups::Mixture)
139 | {
140 | FdGauge* fd = static_cast(pContext);
141 | if (fd == 0)
142 | {
143 | printf("FD pointer was null processing SimConnect event.\r\n");
144 | }
145 | else
146 | {
147 | HandleMixtureAxis(evt);
148 | }
149 | }
150 | }
151 |
152 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EXCEPTION)
153 | {
154 | SIMCONNECT_RECV_EXCEPTION* ex = static_cast(pData);
155 | printf("SimConnect Exception: %d \r\n", ex->dwException);
156 | }
157 | }
158 |
159 | ///
160 | /// Handles throttle axis updates received from SimConnect.
161 | ///
162 | /// A pointer to the SimConnect event structure.
163 | static void HandleMixtureAxis(SIMCONNECT_RECV_EVENT* evt)
164 | {
165 | switch (evt->uEventID)
166 | {
167 | case MixtureEventIDs::AxisMixtureSet:
168 | globalMixtureAxis[0] = static_cast(evt->dwData);
169 | globalMixtureAxis[1] = static_cast(evt->dwData);
170 | break;
171 | case MixtureEventIDs::AxisMixture1Set:
172 | globalMixtureAxis[0] = static_cast(evt->dwData);
173 | break;
174 | case MixtureEventIDs::AxisMixture2Set:
175 | globalMixtureAxis[1] = static_cast(evt->dwData);
176 | break;
177 | case MixtureEventIDs::MixtureSetBest:
178 | case MixtureEventIDs::MixtureRich:
179 | globalMixtureAxis[0] = MAX_MIX;
180 | globalMixtureAxis[1] = MAX_MIX;
181 | break;
182 | case MixtureEventIDs::Mixture1Rich:
183 | globalMixtureAxis[0] = MAX_MIX;
184 | break;
185 | case MixtureEventIDs::Mixture2Rich:
186 | globalMixtureAxis[1] = MAX_MIX;
187 | break;
188 | case MixtureEventIDs::MixtureSet:
189 | globalMixtureAxis[0] = (static_cast(evt->dwData) * 2) - MAX_MIX;
190 | globalMixtureAxis[1] = (static_cast(evt->dwData) * 2) - MAX_MIX;
191 | break;
192 | case MixtureEventIDs::Mixture1Set:
193 | globalMixtureAxis[0] = (static_cast(evt->dwData) * 2) - MAX_MIX;
194 | break;
195 | case MixtureEventIDs::Mixture2Set:
196 | globalMixtureAxis[1] = (static_cast(evt->dwData) * 2) - MAX_MIX;
197 | break;
198 | case MixtureEventIDs::MixtureLean:
199 | globalMixtureAxis[0] = MIN_MIX;
200 | globalMixtureAxis[1] = MIN_MIX;
201 | break;
202 | case MixtureEventIDs::Mixture1Lean:
203 | globalMixtureAxis[0] = MIN_MIX;
204 | break;
205 | case MixtureEventIDs::Mixture2Lean:
206 | globalMixtureAxis[1] = MIN_MIX;
207 | break;
208 | case MixtureEventIDs::MixtureIncr:
209 | case MixtureEventIDs::MixtureIncrSmall:
210 | globalMixtureAxis[0] += MIX_STEP; // TODO: CLAMP ALL INCR/DECR EVENTS
211 | globalMixtureAxis[1] += MIX_STEP;
212 | break;
213 | case MixtureEventIDs::Mixture1Incr:
214 | case MixtureEventIDs::Mixture1IncrSmall:
215 | globalMixtureAxis[0] += MIX_STEP;
216 | break;
217 | case MixtureEventIDs::Mixture2Incr:
218 | case MixtureEventIDs::Mixture2IncrSmall:
219 | globalMixtureAxis[1] += MIX_STEP;
220 | break;
221 | case MixtureEventIDs::MixtureDecr:
222 | case MixtureEventIDs::MixtureDecrSmall:
223 | globalMixtureAxis[0] -= MIX_STEP;
224 | globalMixtureAxis[1] -= MIX_STEP;
225 | break;
226 | case MixtureEventIDs::Mixture1Decr:
227 | case MixtureEventIDs::Mixture1DecrSmall:
228 | globalMixtureAxis[0] -= MIX_STEP;
229 | break;
230 | case MixtureEventIDs::Mixture2Decr:
231 | case MixtureEventIDs::Mixture2DecrSmall:
232 | globalMixtureAxis[1] -= MIX_STEP;
233 | break;
234 | }
235 |
236 | globalMixtureAxis[0] = clamp(globalMixtureAxis[0], MIN_MIX, MAX_MIX);
237 | globalMixtureAxis[1] = clamp(globalMixtureAxis[1], MIN_MIX, MAX_MIX);
238 | }
239 |
240 | public:
241 |
242 | ///
243 | /// Initializes the FD.
244 | ///
245 | /// True if successful, false otherwise.
246 | bool InitializeFD()
247 | {
248 | if (!this->InitializeSimConnect()) {
249 | printf("Init SimConnect failed");
250 | return false;
251 | }
252 |
253 | FdCtrlInstance.init();
254 | isConnected = true;
255 | SimConnect_CallDispatch(hSimConnect, HandleAxisEvent, this);
256 |
257 | return true;
258 | }
259 |
260 | ///
261 | /// A callback used to update the FD at each tick.
262 | ///
263 | /// The time since the previous update.
264 | /// True if successful, false otherwise.
265 | bool OnUpdate(double deltaTime)
266 | {
267 | if (isConnected == true) {
268 | FdCtrlInstance.update(globalMixtureAxis, deltaTime);
269 | }
270 |
271 | return true;
272 | }
273 |
274 | ///
275 | /// Kill.
276 | ///
277 | /// True if succesful, false otherwise.
278 | bool KillFD()
279 | {
280 | isConnected = false;
281 | unregister_all_named_vars();
282 | return SUCCEEDED(SimConnect_Close(hSimConnect));
283 | }
284 | };
285 |
286 | #endif // !FDGAUGE
287 |
--------------------------------------------------------------------------------
/src/C414AW/Mixture_Controller.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31410.357
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mixture_Controller", "Mixture_Controller.vcxproj", "{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|MSFS = Debug|MSFS
11 | Release|MSFS = Release|MSFS
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.ActiveCfg = Debug|MSFS
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.Build.0 = Debug|MSFS
16 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.ActiveCfg = Release|MSFS
17 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.Build.0 = Release|MSFS
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {9129C4E4-B566-46E3-81AE-74CCDE9CA599}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/C414AW/Mixture_Controller.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | MSFS
7 |
8 |
9 | Release
10 | MSFS
11 |
12 |
13 |
14 | 16.0
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}
16 | Module
17 | 10.0
18 | C414_Mixture_Controller
19 |
20 |
21 |
22 | Application
23 | true
24 | MSFS
25 | MultiByte
26 |
27 |
28 | Application
29 | false
30 | MSFS
31 | true
32 | MultiByte
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .wasm
48 |
49 |
50 | $(MSFS_IncludePath)
51 |
52 |
53 | .wasm
54 | $(MSFS_IncludePath)
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | false
67 |
68 |
69 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
70 | false
71 |
72 |
73 |
74 |
75 |
76 |
77 | ProgramDatabase
78 |
79 |
80 | stdcpp14
81 |
82 |
83 |
84 |
85 | %(AdditionalDependencies)
86 |
87 |
88 | true
89 | $(OutDir)$(TargetName)$(TargetExt)
90 |
91 |
92 |
93 |
94 | true
95 |
96 |
97 |
98 |
99 |
100 |
101 | $(OutDir)
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | Level3
111 | MaxSpeed
112 | true
113 | true
114 | true
115 | true
116 |
117 |
118 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
119 | false
120 | false
121 | false
122 |
123 |
124 |
125 |
126 |
127 |
128 | %(AdditionalDependencies)
129 |
130 |
131 | true
132 | $(OutDir)$(TargetName)$(TargetExt)
133 |
134 |
135 |
136 |
137 | false
138 |
139 |
140 |
141 |
142 |
143 |
144 | $(OutDir)
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/src/C414AW/Mixture_Controller.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/C414AW/PidController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef PIDC
4 | #define PIDC
5 |
6 | ///
7 | /// A class for controlling values via a PID.
8 | ///
9 | class PidController
10 | {
11 | private:
12 | ///
13 | /// The gain of the proportional term.
14 | ///
15 | double gainProportion;
16 |
17 | ///
18 | /// The gain of the integral term.
19 | ///
20 | double gainIntegral;
21 |
22 | ///
23 | /// The gain of the derivative term.
24 | ///
25 | double gainDerivative;
26 |
27 | ///
28 | /// The minimum output allowed.
29 | ///
30 | double minOutput;
31 |
32 | ///
33 | /// The maximum output allowed.
34 | ///
35 | double maxOutput;
36 |
37 | ///
38 | /// The previous error amount.
39 | ///
40 | double prevError;
41 |
42 | ///
43 | /// The previous PID output.
44 | ///
45 | double prevOutput;
46 |
47 | ///
48 | /// The current integral term.
49 | ///
50 | double integral;
51 |
52 | ///
53 | /// Clamps a value to the PID min and max outputs.
54 | ///
55 | /// The value to clamp.
56 | /// The clamped value.
57 | double Clamp(double value, double max, double min)
58 | {
59 | if (value > max)
60 | {
61 | return max;
62 | }
63 |
64 | if (value < min)
65 | {
66 | return min;
67 | }
68 |
69 | return value;
70 | }
71 |
72 | public:
73 | ///
74 | /// Creates an instance of a PidController.
75 | ///
76 | /// The gain of the proportional term.
77 | /// The gain of the integral term.
78 | /// The gain of the derivative term.
79 | /// The maximum output.
80 | /// The minimum output.
81 | PidController(double gainProportion, double gainIntegral, double gainDerivative, double minOutput, double maxOutput)
82 | : gainProportion(gainProportion), gainIntegral(gainIntegral), gainDerivative(gainDerivative),
83 | minOutput(minOutput), maxOutput(maxOutput), prevError(0), prevOutput(0), integral(0) { }
84 |
85 | template int sgn(T val) {
86 | return (T(0) < val) - (val < T(0));
87 | }
88 |
89 | ///
90 | /// Gets the output of the PID for a given error and timespan.
91 | ///
92 | /// The error vs the target value.
93 | /// The delta time vs the previous observation.
94 | /// The PID output.
95 | double GetOutput(double error, double deltaTime)
96 | {
97 | auto proportion = gainProportion * error;
98 | //if ((gainIntegral * integral) >= maxOutput) {
99 | //integral -= (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
100 | //}
101 | //else if ((gainIntegral * integral) <= minOutput) {
102 | integral += (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
103 | //}
104 |
105 |
106 | if (sgn(error) != sgn(prevError)) {
107 | integral = 0;
108 | }
109 |
110 | auto derivative = gainDerivative * ((error - prevError) / deltaTime);
111 | derivative = this->Clamp(derivative, 20.0, -20.0);
112 |
113 | auto output = this->Clamp(proportion + (gainIntegral * integral) + (derivative), this->maxOutput, this->minOutput);
114 |
115 | //printf("P: %.2f I: %.2f D %.2f \r\n", proportion, (gainIntegral * integral), (derivative));
116 |
117 | prevError = error;
118 | prevOutput = output;
119 |
120 | return output;
121 | }
122 | };
123 |
124 | #endif
125 |
--------------------------------------------------------------------------------
/src/C414AW/SimConnectDefs.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef SCDEFS
4 | #define SCDEFS
5 |
6 | #include "common.h"
7 |
8 | ///
9 | /// SimConnect client event IDs for the mixture group.
10 | ///
11 | enum MixtureEventIDs
12 | {
13 | AxisMixtureSet,
14 | AxisMixture1Set,
15 | AxisMixture2Set,
16 | MixtureSetBest,
17 | MixtureSet,
18 | MixtureRich,
19 | MixtureIncr,
20 | MixtureIncrSmall,
21 | MixtureDecrSmall,
22 | MixtureDecr,
23 | MixtureLean,
24 | Mixture1Set,
25 | Mixture1Rich,
26 | Mixture1Incr,
27 | Mixture1IncrSmall,
28 | Mixture1DecrSmall,
29 | Mixture1Decr,
30 | Mixture1Lean,
31 | Mixture2Set,
32 | Mixture2Rich,
33 | Mixture2Incr,
34 | Mixture2IncrSmall,
35 | Mixture2DecrSmall,
36 | Mixture2Decr,
37 | Mixture2Lean
38 | };
39 |
40 | ///
41 | /// SimConnect event groups.
42 | ///
43 | enum EventGroups
44 | {
45 | ///
46 | /// The client event group ID to use when any events from the mixture axis group
47 | /// are received.
48 | ///
49 | Mixture = 0,
50 | };
51 |
52 | ///
53 | /// SimConnect data types for sending the sim updates.
54 | ///
55 | enum DataTypes
56 | {
57 | ///
58 | /// The data type ID to use when sending engine controls data.
59 | ///
60 | EngineControls = 0
61 | };
62 | ///
63 | /// Engine controls.
64 | ///
65 | struct EngineControlData
66 | {
67 | ///
68 | /// The mixture of the engine, expressed in a 100s base percent.
69 | ///
70 | double mixtureLeft;
71 | double mixtureRight;
72 | };
73 |
74 | ///
75 | /// A collection of SimVar unit enums.
76 | ///
77 | class Units
78 | {
79 | public:
80 | ///
81 | /// The Percent SimVar unit.
82 | ///
83 | ENUM Percent = get_units_enum("Percent");
84 |
85 | ///
86 | /// The Ratio SimVar unit.
87 | ///
88 | ENUM Ratio = get_units_enum("Ratio");
89 |
90 | ENUM Celsius = get_units_enum("Celsius");
91 |
92 | ENUM Number = get_units_enum("Number");
93 |
94 | ENUM inHg = get_units_enum("inches of mercury");
95 | };
96 |
97 |
98 | ///
99 | /// A collection of SimVar enums.
100 | ///
101 | class SimVars
102 | {
103 | public:
104 | Units* m_Units;
105 |
106 | ///
107 | /// The GENERAL ENGINE MIXTURE LEVER POSITION SimVar.
108 | ///
109 | ENUM Mixture = get_aircraft_var_enum("GENERAL ENG MIXTURE LEVER POSITION");
110 |
111 | ENUM FuelAirRatio = get_aircraft_var_enum("RECIP MIXTURE RATIO");
112 |
113 | ENUM AmbientTemperature = get_aircraft_var_enum("AMBIENT TEMPERATURE");
114 |
115 | ENUM AmbientPressure = get_aircraft_var_enum("AMBIENT PRESSURE");
116 |
117 | ID FadecActive;
118 |
119 | ///
120 | /// The local variable for the visible mixture position
121 | ///
122 | ID MixturePos1;
123 | ID MixturePos2;
124 |
125 | SimVars()
126 | {
127 | this->initializeVars();
128 | }
129 |
130 | void initializeVars() {
131 | FadecActive = register_named_variable("FADEC_ACTIVE");
132 | MixturePos1 = register_named_variable("Mixture1_Pos");
133 | MixturePos2 = register_named_variable("Mixture2_Pos");
134 | m_Units = new Units();
135 | }
136 |
137 | void setFadecActiveFlag() {
138 | set_named_variable_value(FadecActive, 1);
139 | }
140 |
141 | void setMixture1Pos(double value) {
142 | set_named_variable_value(MixturePos1, value);
143 | }
144 |
145 | void setMixture2Pos(double value) {
146 | set_named_variable_value(MixturePos2, value);
147 | }
148 |
149 | FLOAT64 getMixtureLeverPosition(int index) {
150 | return aircraft_varget(Mixture, m_Units->Percent, index);
151 | }
152 |
153 | FLOAT64 getFuelAirRatio(int index) {
154 | return aircraft_varget(FuelAirRatio, m_Units->Ratio, index);
155 | }
156 |
157 | FLOAT64 getAmbientTemperature() {
158 | return aircraft_varget(AmbientTemperature, m_Units->Celsius, 0);
159 | }
160 |
161 | FLOAT64 getAmbientPressure() {
162 | return aircraft_varget(AmbientPressure, m_Units->inHg, 0);
163 | }
164 | };
165 |
166 | #endif
167 |
--------------------------------------------------------------------------------
/src/C414AW/common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef _COMMON_H_
4 | #define _COMMON_H_
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "SimConnectDefs.h"
16 |
17 | #include "turbocharger.h"
18 | #include
19 | #include
20 |
21 |
22 | ///
23 | /// The handle to the SimConnect instance.
24 | ///
25 | HANDLE hSimConnect;
26 |
27 | double clamp(double v, double lo, double hi)
28 | {
29 | assert(!(hi < lo));
30 | return (v < lo) ? lo : (hi < v) ? hi : v;
31 | }
32 |
33 |
34 | #endif
--------------------------------------------------------------------------------
/src/C414AW/turbocharger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef TURBOCHARGER
4 | #define TURBOCHARGER
5 |
6 | #include "common.h"
7 |
8 | class Turbocharger
9 | {
10 | public:
11 | static double getTargetFuelAirRatio(double mixturePercentage) {
12 | // The proportionality constant between Fuel/Air mixture and ambient specific volume measured in the sim:
13 | double fullRichSpecificVolumeConstant = 0.110236;
14 | double seaLevelSpecificVolume = 1.0 / 1.225;
15 | // Slope of the relationship between mixture lever position (in percent) and the ratio of fuel/air mixture to ambient specific volume -- measured from sim data.
16 | double slope_90_100 = 0.000487531;
17 | double slope_75_90 = 0.000243697;
18 | double slope_60_75 = 0.000409167;
19 | double slope_20_60 = 0.000954534;
20 |
21 | if (mixturePercentage == 100.0) {
22 | double targetFuelAirRatio = seaLevelSpecificVolume * fullRichSpecificVolumeConstant;
23 | return targetFuelAirRatio;
24 | }
25 | else if (mixturePercentage >= 90.0) {
26 | double targetFuelAirRatio = seaLevelSpecificVolume * (fullRichSpecificVolumeConstant + (mixturePercentage - 100.0) * slope_90_100);
27 | return targetFuelAirRatio;
28 | }
29 | else if (mixturePercentage >= 75.0) {
30 | double targetFuelAirRatio = seaLevelSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (mixturePercentage - 90.0) * slope_75_90);
31 | return targetFuelAirRatio;
32 | }
33 | else if (mixturePercentage >= 60.0) {
34 | double targetFuelAirRatio = seaLevelSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (mixturePercentage - 75.0) * slope_60_75);
35 | return targetFuelAirRatio;
36 | }
37 | else if (mixturePercentage >= 20.0) {
38 | double targetFuelAirRatio = seaLevelSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (mixturePercentage - 60.0) * slope_20_60);
39 | return targetFuelAirRatio;
40 | }
41 | else {
42 | double slope_0_20 = (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60) / 20.0;
43 | double targetFuelAirRatio = seaLevelSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60 + (mixturePercentage - 20.0) * slope_0_20);
44 | return targetFuelAirRatio;
45 | }
46 | }
47 | };
48 |
49 | #endif // !TURBOCHARGER
50 |
--------------------------------------------------------------------------------
/src/SenecaV/FdController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef FDCTRL
4 | #define FDCTRL
5 |
6 | #include "common.h"
7 | #include "PidController.h"
8 |
9 | class FdController
10 | {
11 | private:
12 | SimVars* simVars;
13 |
14 | ///
15 | /// An instance of the mixture PID controller.
16 | ///
17 | PidController* mixtureController[2];
18 |
19 | ///
20 | /// The current mixture control axis, from -16384 to 16384.
21 | ///
22 | int mixtureAxis[2] = { 16384, 16384 };
23 |
24 | bool enabled = true;
25 |
26 | ///
27 | /// Calculates and updates mixture according to target and PIDs.
28 | ///
29 | ///
30 | void updateMixture(double deltaTime) {
31 | EngineControlData controls;
32 | controls.mixtureLeft = this->getDesiredMixture(0, deltaTime);
33 | controls.mixtureRight = this->getDesiredMixture(1, deltaTime);
34 | SimConnect_SetDataOnSimObject(hSimConnect, DataTypes::EngineControls, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(EngineControlData), &controls);
35 | }
36 |
37 | ///
38 | /// Gets the mixture lever position to achieve the desired fuel/air mixture ratio
39 | ///
40 | /// Desired mixture setting
41 | double getDesiredMixture(int idx, double deltaTime) {
42 | double mixtureLeverPerc = 100.0 * (this->mixtureAxis[idx] + 16384) / 32768.0;
43 |
44 | if (!enabled) {
45 | return mixtureLeverPerc;
46 | }
47 |
48 | double targetFuelAirMixture = Turbocharger::getTargetFuelAirRatio(this->simVars->getAmbientTemperature(), this->simVars->getAmbientPressure(), mixtureLeverPerc);
49 | double simFuelAirMixture = this->simVars->getFuelAirRatio(idx + 1);
50 | //printf("Target Mixture: %1.3f\nSim Mixture: %1.3f\n", targetFuelAirMixture, simFuelAirMixture);
51 |
52 | double error = targetFuelAirMixture - simFuelAirMixture;
53 | double pidOut = 0.0;
54 | pidOut = this->mixtureController[idx]->GetOutput(error, deltaTime);
55 | //printf("Output from PID Controller: %2.3f\n", error);
56 | return max(0, min(100, this->simVars->getMixtureLeverPosition(idx + 1) + pidOut));
57 | }
58 |
59 | void updateVisibleMixture(int idx) {
60 | int targetMixture = this->mixtureAxis[idx];
61 | double mixtureLeverPerc = 100.0 * (targetMixture + 16384) / 32768.0;
62 | if (idx == 0) {
63 | this->simVars->setMixture1Pos(mixtureLeverPerc);
64 | }
65 | else {
66 | this->simVars->setMixture2Pos(mixtureLeverPerc);
67 | }
68 | }
69 |
70 | public:
71 | void init()
72 | {
73 | printf("FdController init");
74 |
75 | this->simVars = new SimVars();
76 |
77 | float p = 350.0;
78 | float i = 380.0;
79 | float d = 0.0;
80 | this->mixtureController[0] = new PidController(p, i, d, -5, 5);
81 | this->mixtureController[1] = new PidController(p, i, d, -5, 5);
82 | }
83 |
84 | void update(int mixtureAxis[], double deltaTime)
85 | {
86 | this->mixtureAxis[0] = mixtureAxis[0];
87 | this->mixtureAxis[1] = mixtureAxis[1];
88 | this->updateMixture(deltaTime);
89 | this->updateVisibleMixture(0);
90 | this->updateVisibleMixture(1);
91 | this->simVars->setFadecActiveFlag();
92 | }
93 | };
94 |
95 | FdController FdCtrlInstance;
96 |
97 | #endif // !FDCTRL
98 |
--------------------------------------------------------------------------------
/src/SenecaV/FdGauge.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | #ifndef FDGAUGE
5 | #define FDGAUGE
6 |
7 | #ifndef __INTELLISENSE__
8 | # define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
9 | # define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
10 | #else
11 | # define MODULE_EXPORT
12 | # define MODULE_WASM_MODNAME(mod)
13 | # define __attribute__(x)
14 | # define __restrict__
15 | #endif
16 |
17 | #include "common.h"
18 | #include "FdController.h"
19 |
20 | const int MIN_MIX = -16384;
21 | const int MAX_MIX = 16384;
22 | const int MIX_STEP = 256;
23 |
24 | int globalMixtureAxis[2] = { MAX_MIX, MAX_MIX };
25 |
26 | class FdGauge
27 | {
28 | private:
29 |
30 | bool isConnected = false;
31 |
32 | ///
33 | /// Registers all the mixture SimConnect client events.
34 | ///
35 | void RegisterMixtureClientEvents()
36 | {
37 | printf("Registering mixture events...\r\n");
38 |
39 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixtureSet, "AXIS_MIXTURE_SET");
40 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixture1Set, "AXIS_MIXTURE1_SET");
41 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixture2Set, "AXIS_MIXTURE2_SET");
42 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSetBest, "MIXTURE_SET_BEST");
43 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSet, "MIXTURE_SET");
44 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureRich, "MIXTURE_RICH");
45 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncr, "MIXTURE_INCR");
46 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncrSmall, "MIXTURE_INCR_SMALL");
47 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecrSmall, "MIXTURE_DECR_SMALL");
48 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecr, "MIXTURE_DECR");
49 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureLean, "MIXTURE_LEAN");
50 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Set, "MIXTURE1_SET");
51 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Rich, "MIXTURE1_RICH");
52 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Incr, "MIXTURE1_INCR");
53 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1IncrSmall, "MIXTURE1_INCR_SMALL");
54 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1DecrSmall, "MIXTURE1_DECR_SMALL");
55 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Decr, "MIXTURE1_DECR");
56 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Lean, "MIXTURE1_LEAN");
57 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Set, "MIXTURE2_SET");
58 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Rich, "MIXTURE2_RICH");
59 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Incr, "MIXTURE2_INCR");
60 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2IncrSmall, "MIXTURE2_INCR_SMALL");
61 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2DecrSmall, "MIXTURE2_DECR_SMALL");
62 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Decr, "MIXTURE2_DECR");
63 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture2Lean, "MIXTURE2_LEAN");
64 | }
65 |
66 | ///
67 | /// Registers the SimConnect mixture event group for capture.
68 | ///
69 | void RegisterMixtureEventGroup()
70 | {
71 | printf("Registering mixture event group...\r\n");
72 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixtureSet, TRUE);
73 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixture1Set, TRUE);
74 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixture2Set, TRUE);
75 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSetBest, TRUE);
76 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSet, TRUE);
77 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureRich, TRUE);
78 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncr, TRUE);
79 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncrSmall, TRUE);
80 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecrSmall, TRUE);
81 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecr, TRUE);
82 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureLean, TRUE);
83 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Set, TRUE);
84 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Rich, TRUE);
85 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Incr, TRUE);
86 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1IncrSmall, TRUE);
87 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1DecrSmall, TRUE);
88 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Decr, TRUE);
89 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Lean, TRUE);
90 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Set, TRUE);
91 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Rich, TRUE);
92 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Incr, TRUE);
93 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2IncrSmall, TRUE);
94 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2DecrSmall, TRUE);
95 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Decr, TRUE);
96 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture2Lean, TRUE);
97 |
98 | SimConnect_SetNotificationGroupPriority(hSimConnect, EventGroups::Mixture, SIMCONNECT_GROUP_PRIORITY_HIGHEST_MASKABLE);
99 | }
100 |
101 | ///
102 | /// Initializes the connection to SimConnect.
103 | ///
104 | /// True if successful, false otherwise.
105 | bool InitializeSimConnect()
106 | {
107 | printf("Connecting to SimConnect...\r\n");
108 | if (SUCCEEDED(SimConnect_Open(&hSimConnect, "FdGauge", nullptr, 0, 0, 0)))
109 | {
110 | printf("SimConnect connected.\r\n");
111 |
112 | this->RegisterMixtureClientEvents();
113 | this->RegisterMixtureEventGroup();
114 |
115 | SimConnect_AddToDataDefinition(hSimConnect, DataTypes::EngineControls, "GENERAL ENG MIXTURE LEVER POSITION:1", "Percent");
116 | SimConnect_AddToDataDefinition(hSimConnect, DataTypes::EngineControls, "GENERAL ENG MIXTURE LEVER POSITION:2", "Percent");
117 |
118 | printf("SimConnect registrations complete.\r\n");
119 | return true;
120 | }
121 |
122 | printf("SimConnect failed.\r\n");
123 |
124 | return false;
125 | }
126 |
127 | ///
128 | /// A callback used for handling SimConnect updates.
129 | ///
130 | /// The update data sent by SimConnect.
131 | /// The size of the SimConnect data structure.
132 | /// A pointer specified by the client.
133 | static void CALLBACK HandleAxisEvent(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
134 | {
135 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EVENT)
136 | {
137 | SIMCONNECT_RECV_EVENT* evt = static_cast(pData);
138 | if (evt->uGroupID == EventGroups::Mixture)
139 | {
140 | FdGauge* fd = static_cast(pContext);
141 | if (fd == 0)
142 | {
143 | printf("FD pointer was null processing SimConnect event.\r\n");
144 | }
145 | else
146 | {
147 | HandleMixtureAxis(evt);
148 | }
149 | }
150 | }
151 |
152 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EXCEPTION)
153 | {
154 | SIMCONNECT_RECV_EXCEPTION* ex = static_cast(pData);
155 | printf("SimConnect Exception: %d \r\n", ex->dwException);
156 | }
157 | }
158 |
159 | ///
160 | /// Handles throttle axis updates received from SimConnect.
161 | ///
162 | /// A pointer to the SimConnect event structure.
163 | static void HandleMixtureAxis(SIMCONNECT_RECV_EVENT* evt)
164 | {
165 | switch (evt->uEventID)
166 | {
167 | case MixtureEventIDs::AxisMixtureSet:
168 | globalMixtureAxis[0] = static_cast(evt->dwData);
169 | globalMixtureAxis[1] = static_cast(evt->dwData);
170 | break;
171 | case MixtureEventIDs::AxisMixture1Set:
172 | globalMixtureAxis[0] = static_cast(evt->dwData);
173 | break;
174 | case MixtureEventIDs::AxisMixture2Set:
175 | globalMixtureAxis[1] = static_cast(evt->dwData);
176 | break;
177 | case MixtureEventIDs::MixtureSetBest:
178 | case MixtureEventIDs::MixtureRich:
179 | globalMixtureAxis[0] = MAX_MIX;
180 | globalMixtureAxis[1] = MAX_MIX;
181 | break;
182 | case MixtureEventIDs::Mixture1Rich:
183 | globalMixtureAxis[0] = MAX_MIX;
184 | break;
185 | case MixtureEventIDs::Mixture2Rich:
186 | globalMixtureAxis[1] = MAX_MIX;
187 | break;
188 | case MixtureEventIDs::MixtureSet:
189 | globalMixtureAxis[0] = (static_cast(evt->dwData) * 2) - MAX_MIX;
190 | globalMixtureAxis[1] = (static_cast(evt->dwData) * 2) - MAX_MIX;
191 | break;
192 | case MixtureEventIDs::Mixture1Set:
193 | globalMixtureAxis[0] = (static_cast(evt->dwData) * 2) - MAX_MIX;
194 | break;
195 | case MixtureEventIDs::Mixture2Set:
196 | globalMixtureAxis[1] = (static_cast(evt->dwData) * 2) - MAX_MIX;
197 | break;
198 | case MixtureEventIDs::MixtureLean:
199 | globalMixtureAxis[0] = MIN_MIX;
200 | globalMixtureAxis[1] = MIN_MIX;
201 | break;
202 | case MixtureEventIDs::Mixture1Lean:
203 | globalMixtureAxis[0] = MIN_MIX;
204 | break;
205 | case MixtureEventIDs::Mixture2Lean:
206 | globalMixtureAxis[1] = MIN_MIX;
207 | break;
208 | case MixtureEventIDs::MixtureIncr:
209 | case MixtureEventIDs::MixtureIncrSmall:
210 | globalMixtureAxis[0] += MIX_STEP; // TODO: CLAMP ALL INCR/DECR EVENTS
211 | globalMixtureAxis[1] += MIX_STEP;
212 | break;
213 | case MixtureEventIDs::Mixture1Incr:
214 | case MixtureEventIDs::Mixture1IncrSmall:
215 | globalMixtureAxis[0] += MIX_STEP;
216 | break;
217 | case MixtureEventIDs::Mixture2Incr:
218 | case MixtureEventIDs::Mixture2IncrSmall:
219 | globalMixtureAxis[1] += MIX_STEP;
220 | break;
221 | case MixtureEventIDs::MixtureDecr:
222 | case MixtureEventIDs::MixtureDecrSmall:
223 | globalMixtureAxis[0] -= MIX_STEP;
224 | globalMixtureAxis[1] -= MIX_STEP;
225 | break;
226 | case MixtureEventIDs::Mixture1Decr:
227 | case MixtureEventIDs::Mixture1DecrSmall:
228 | globalMixtureAxis[0] -= MIX_STEP;
229 | break;
230 | case MixtureEventIDs::Mixture2Decr:
231 | case MixtureEventIDs::Mixture2DecrSmall:
232 | globalMixtureAxis[1] -= MIX_STEP;
233 | break;
234 | }
235 |
236 | globalMixtureAxis[0] = clamp(globalMixtureAxis[0], MIN_MIX, MAX_MIX);
237 | globalMixtureAxis[1] = clamp(globalMixtureAxis[1], MIN_MIX, MAX_MIX);
238 | }
239 |
240 | public:
241 |
242 | ///
243 | /// Initializes the FD.
244 | ///
245 | /// True if successful, false otherwise.
246 | bool InitializeFD()
247 | {
248 | if (!this->InitializeSimConnect()) {
249 | printf("Init SimConnect failed");
250 | return false;
251 | }
252 |
253 | FdCtrlInstance.init();
254 | isConnected = true;
255 | SimConnect_CallDispatch(hSimConnect, HandleAxisEvent, this);
256 |
257 | return true;
258 | }
259 |
260 | ///
261 | /// A callback used to update the FD at each tick.
262 | ///
263 | /// The time since the previous update.
264 | /// True if successful, false otherwise.
265 | bool OnUpdate(double deltaTime)
266 | {
267 | if (isConnected == true) {
268 | FdCtrlInstance.update(globalMixtureAxis, deltaTime);
269 | }
270 |
271 | return true;
272 | }
273 |
274 | ///
275 | /// Kill.
276 | ///
277 | /// True if succesful, false otherwise.
278 | bool KillFD()
279 | {
280 | isConnected = false;
281 | unregister_all_named_vars();
282 | return SUCCEEDED(SimConnect_Close(hSimConnect));
283 | }
284 | };
285 |
286 | #endif // !FDGAUGE
287 |
--------------------------------------------------------------------------------
/src/SenecaV/Mixture_Controller.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31410.357
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mixture_Controller", "Mixture_Controller.vcxproj", "{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|MSFS = Debug|MSFS
11 | Release|MSFS = Release|MSFS
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.ActiveCfg = Debug|MSFS
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.Build.0 = Debug|MSFS
16 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.ActiveCfg = Release|MSFS
17 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.Build.0 = Release|MSFS
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {9129C4E4-B566-46E3-81AE-74CCDE9CA599}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/SenecaV/Mixture_Controller.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | MSFS
7 |
8 |
9 | Release
10 | MSFS
11 |
12 |
13 |
14 | 16.0
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}
16 | Module
17 | 10.0
18 | SenecaV_Mixture_Controller
19 |
20 |
21 |
22 | Application
23 | true
24 | MSFS
25 | MultiByte
26 |
27 |
28 | Application
29 | false
30 | MSFS
31 | true
32 | MultiByte
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .wasm
48 |
49 |
50 | $(MSFS_IncludePath)
51 |
52 |
53 | .wasm
54 | $(MSFS_IncludePath)
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | false
67 |
68 |
69 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
70 | false
71 |
72 |
73 |
74 |
75 |
76 |
77 | ProgramDatabase
78 |
79 |
80 | stdcpp14
81 |
82 |
83 |
84 |
85 | %(AdditionalDependencies)
86 |
87 |
88 | true
89 | $(OutDir)$(TargetName)$(TargetExt)
90 |
91 |
92 |
93 |
94 | true
95 |
96 |
97 |
98 |
99 |
100 |
101 | $(OutDir)
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | Level3
111 | MaxSpeed
112 | true
113 | true
114 | true
115 | true
116 |
117 |
118 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
119 | false
120 | false
121 | false
122 |
123 |
124 |
125 |
126 |
127 |
128 | %(AdditionalDependencies)
129 |
130 |
131 | true
132 | $(OutDir)$(TargetName)$(TargetExt)
133 |
134 |
135 |
136 |
137 | false
138 |
139 |
140 |
141 |
142 |
143 |
144 | $(OutDir)
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/src/SenecaV/Mixture_Controller.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/SenecaV/PidController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef PIDC
4 | #define PIDC
5 |
6 | ///
7 | /// A class for controlling values via a PID.
8 | ///
9 | class PidController
10 | {
11 | private:
12 | ///
13 | /// The gain of the proportional term.
14 | ///
15 | double gainProportion;
16 |
17 | ///
18 | /// The gain of the integral term.
19 | ///
20 | double gainIntegral;
21 |
22 | ///
23 | /// The gain of the derivative term.
24 | ///
25 | double gainDerivative;
26 |
27 | ///
28 | /// The minimum output allowed.
29 | ///
30 | double minOutput;
31 |
32 | ///
33 | /// The maximum output allowed.
34 | ///
35 | double maxOutput;
36 |
37 | ///
38 | /// The previous error amount.
39 | ///
40 | double prevError;
41 |
42 | ///
43 | /// The previous PID output.
44 | ///
45 | double prevOutput;
46 |
47 | ///
48 | /// The current integral term.
49 | ///
50 | double integral;
51 |
52 | ///
53 | /// Clamps a value to the PID min and max outputs.
54 | ///
55 | /// The value to clamp.
56 | /// The clamped value.
57 | double Clamp(double value, double max, double min)
58 | {
59 | if (value > max)
60 | {
61 | return max;
62 | }
63 |
64 | if (value < min)
65 | {
66 | return min;
67 | }
68 |
69 | return value;
70 | }
71 |
72 | public:
73 | ///
74 | /// Creates an instance of a PidController.
75 | ///
76 | /// The gain of the proportional term.
77 | /// The gain of the integral term.
78 | /// The gain of the derivative term.
79 | /// The maximum output.
80 | /// The minimum output.
81 | PidController(double gainProportion, double gainIntegral, double gainDerivative, double minOutput, double maxOutput)
82 | : gainProportion(gainProportion), gainIntegral(gainIntegral), gainDerivative(gainDerivative),
83 | minOutput(minOutput), maxOutput(maxOutput), prevError(0), prevOutput(0), integral(0) { }
84 |
85 | template int sgn(T val) {
86 | return (T(0) < val) - (val < T(0));
87 | }
88 |
89 | ///
90 | /// Gets the output of the PID for a given error and timespan.
91 | ///
92 | /// The error vs the target value.
93 | /// The delta time vs the previous observation.
94 | /// The PID output.
95 | double GetOutput(double error, double deltaTime)
96 | {
97 | auto proportion = gainProportion * error;
98 | //if ((gainIntegral * integral) >= maxOutput) {
99 | //integral -= (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
100 | //}
101 | //else if ((gainIntegral * integral) <= minOutput) {
102 | integral += (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
103 | //}
104 |
105 |
106 | if (sgn(error) != sgn(prevError)) {
107 | integral = 0;
108 | }
109 |
110 | auto derivative = gainDerivative * ((error - prevError) / deltaTime);
111 | derivative = this->Clamp(derivative, 20.0, -20.0);
112 |
113 | auto output = this->Clamp(proportion + (gainIntegral * integral) + (derivative), this->maxOutput, this->minOutput);
114 |
115 | //printf("P: %.2f I: %.2f D %.2f \r\n", proportion, (gainIntegral * integral), (derivative));
116 |
117 | prevError = error;
118 | prevOutput = output;
119 |
120 | return output;
121 | }
122 | };
123 |
124 | #endif
125 |
--------------------------------------------------------------------------------
/src/SenecaV/SenecaV_Mixture_Controller.cpp:
--------------------------------------------------------------------------------
1 |
2 | #ifdef _MSC_VER
3 | #define snprintf _snprintf_s
4 | #elif !defined(__MINGW32__)
5 | #include
6 | #endif
7 |
8 | #include "FdGauge.h"
9 |
10 | FdGauge FD_GAUGE;
11 |
12 |
13 | // ------------------------
14 | // Callbacks
15 | extern "C" {
16 |
17 | MSFS_CALLBACK bool FdGauge_gauge_callback(FsContext ctx, int service_id, void* pData)
18 | {
19 | switch (service_id)
20 | {
21 | case PANEL_SERVICE_PRE_INSTALL:
22 | {
23 | return true;
24 | }
25 | break;
26 | case PANEL_SERVICE_POST_INSTALL:
27 | {
28 | return FD_GAUGE.InitializeFD();
29 | }
30 | break;
31 | case PANEL_SERVICE_PRE_DRAW:
32 | {
33 | sGaugeDrawData* drawData = static_cast(pData);
34 | return FD_GAUGE.OnUpdate(drawData->dt);
35 | }
36 | break;
37 | case PANEL_SERVICE_PRE_KILL:
38 | {
39 | FD_GAUGE.KillFD();
40 | return true;
41 | }
42 | break;
43 | }
44 | return false;
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/SenecaV/SimConnectDefs.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef SCDEFS
4 | #define SCDEFS
5 |
6 | #include "common.h"
7 |
8 | ///
9 | /// SimConnect client event IDs for the mixture group.
10 | ///
11 | enum MixtureEventIDs
12 | {
13 | AxisMixtureSet,
14 | AxisMixture1Set,
15 | AxisMixture2Set,
16 | MixtureSetBest,
17 | MixtureSet,
18 | MixtureRich,
19 | MixtureIncr,
20 | MixtureIncrSmall,
21 | MixtureDecrSmall,
22 | MixtureDecr,
23 | MixtureLean,
24 | Mixture1Set,
25 | Mixture1Rich,
26 | Mixture1Incr,
27 | Mixture1IncrSmall,
28 | Mixture1DecrSmall,
29 | Mixture1Decr,
30 | Mixture1Lean,
31 | Mixture2Set,
32 | Mixture2Rich,
33 | Mixture2Incr,
34 | Mixture2IncrSmall,
35 | Mixture2DecrSmall,
36 | Mixture2Decr,
37 | Mixture2Lean
38 | };
39 |
40 | ///
41 | /// SimConnect event groups.
42 | ///
43 | enum EventGroups
44 | {
45 | ///
46 | /// The client event group ID to use when any events from the mixture axis group
47 | /// are received.
48 | ///
49 | Mixture = 0,
50 | };
51 |
52 | ///
53 | /// SimConnect data types for sending the sim updates.
54 | ///
55 | enum DataTypes
56 | {
57 | ///
58 | /// The data type ID to use when sending engine controls data.
59 | ///
60 | EngineControls = 0
61 | };
62 | ///
63 | /// Engine controls.
64 | ///
65 | struct EngineControlData
66 | {
67 | ///
68 | /// The mixture of the engine, expressed in a 100s base percent.
69 | ///
70 | double mixtureLeft;
71 | double mixtureRight;
72 | };
73 |
74 | ///
75 | /// A collection of SimVar unit enums.
76 | ///
77 | class Units
78 | {
79 | public:
80 | ///
81 | /// The Percent SimVar unit.
82 | ///
83 | ENUM Percent = get_units_enum("Percent");
84 |
85 | ///
86 | /// The Ratio SimVar unit.
87 | ///
88 | ENUM Ratio = get_units_enum("Ratio");
89 |
90 | ENUM Celsius = get_units_enum("Celsius");
91 |
92 | ENUM Number = get_units_enum("Number");
93 |
94 | ENUM inHg = get_units_enum("inches of mercury");
95 | };
96 |
97 |
98 | ///
99 | /// A collection of SimVar enums.
100 | ///
101 | class SimVars
102 | {
103 | public:
104 | Units* m_Units;
105 |
106 | ///
107 | /// The GENERAL ENGINE MIXTURE LEVER POSITION SimVar.
108 | ///
109 | ENUM Mixture = get_aircraft_var_enum("GENERAL ENG MIXTURE LEVER POSITION");
110 |
111 | ENUM FuelAirRatio = get_aircraft_var_enum("RECIP MIXTURE RATIO");
112 |
113 | ENUM AmbientTemperature = get_aircraft_var_enum("AMBIENT TEMPERATURE");
114 |
115 | ENUM AmbientPressure = get_aircraft_var_enum("AMBIENT PRESSURE");
116 |
117 | ID FadecActive;
118 |
119 | ///
120 | /// The local variable for the visible mixture position
121 | ///
122 | ID MixturePos1;
123 | ID MixturePos2;
124 |
125 | SimVars()
126 | {
127 | this->initializeVars();
128 | }
129 |
130 | void initializeVars() {
131 | FadecActive = register_named_variable("FADEC_ACTIVE");
132 | MixturePos1 = register_named_variable("Mixture1_Pos");
133 | MixturePos2 = register_named_variable("Mixture2_Pos");
134 | m_Units = new Units();
135 | }
136 |
137 | void setFadecActiveFlag() {
138 | set_named_variable_value(FadecActive, 1);
139 | }
140 |
141 | void setMixture1Pos(double value) {
142 | set_named_variable_value(MixturePos1, value);
143 | }
144 |
145 | void setMixture2Pos(double value) {
146 | set_named_variable_value(MixturePos2, value);
147 | }
148 |
149 | FLOAT64 getMixtureLeverPosition(int index) {
150 | return aircraft_varget(Mixture, m_Units->Percent, index);
151 | }
152 |
153 | FLOAT64 getFuelAirRatio(int index) {
154 | return aircraft_varget(FuelAirRatio, m_Units->Ratio, index);
155 | }
156 |
157 | FLOAT64 getAmbientTemperature() {
158 | return aircraft_varget(AmbientTemperature, m_Units->Celsius, 0);
159 | }
160 |
161 | FLOAT64 getAmbientPressure() {
162 | return aircraft_varget(AmbientPressure, m_Units->inHg, 0);
163 | }
164 | };
165 |
166 | #endif
167 |
--------------------------------------------------------------------------------
/src/SenecaV/common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef _COMMON_H_
4 | #define _COMMON_H_
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "SimConnectDefs.h"
16 |
17 | #include "turbocharger.h"
18 | #include
19 | #include
20 |
21 |
22 | ///
23 | /// The handle to the SimConnect instance.
24 | ///
25 | HANDLE hSimConnect;
26 |
27 | double clamp(double v, double lo, double hi)
28 | {
29 | assert(!(hi < lo));
30 | return (v < lo) ? lo : (hi < v) ? hi : v;
31 | }
32 |
33 |
34 | #endif
--------------------------------------------------------------------------------
/src/SenecaV/turbocharger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef TURBOCHARGER
4 | #define TURBOCHARGER
5 |
6 | #include "common.h"
7 |
8 | class Turbocharger
9 | {
10 | public:
11 | static double getTargetFuelAirRatio(FLOAT64 ambientTemperature, FLOAT64 ambientPressure, double mixturePercentage) {
12 | double maxPowerManifoldPressure = 38.0;
13 | double criticalAltitude = 19000.0;
14 | double icaoStandardPressureAtCriticalAltitude = 14.3532959;
15 | double maxTurboBoostPressure = maxPowerManifoldPressure - icaoStandardPressureAtCriticalAltitude;
16 | // The proportionality constant between Fuel/Air mixture and ambient specific volume measured in the sim:
17 | double fullRichSpecificVolumeConstant = 0.110236;
18 | // The ratio between ICAO standard atmosphere specific volume at sea-level and the specific volume of air in the intake manifold (at maximum safe power setting -- calculated using the adiabatic process equation)
19 | double turboBoostCorrectionFactor = 1.18614538;
20 | double turboSpecificVolumeConstant = fullRichSpecificVolumeConstant * turboBoostCorrectionFactor;
21 | // Slope of the relationship between mixture lever position (in percent) and the ratio of fuel/air mixture to ambient specific volume -- measured from sim data.
22 | double slope_90_100 = 0.000487531;
23 | double slope_75_90 = 0.000243697;
24 | double slope_60_75 = 0.000409167;
25 | double slope_20_60 = 0.000954534;
26 | double ambientPressurePa = ambientPressure * 3386.39;
27 | double maxManifoldPressurePa = maxPowerManifoldPressure * 3386.39;
28 | double ambientTemperatureK = ambientTemperature + 273.15;
29 | double maxManifoldDensity = 0.0;
30 | if (ambientPressure >= icaoStandardPressureAtCriticalAltitude) {
31 | maxManifoldDensity = maxManifoldPressurePa / (287.058 * pow(pow(ambientPressurePa, (1.0 - 1.4)) * pow(ambientTemperatureK, 1.4) / pow(maxManifoldPressurePa, (1.0 - 1.4)), (1.0 / 1.40)));
32 | }
33 | else {
34 | maxManifoldDensity = ((ambientPressure + maxTurboBoostPressure) * 3386.39) / (287.058 * pow(pow(ambientPressurePa, (1.0 - 1.4)) * pow(ambientTemperatureK, 1.4) / pow(((ambientPressure + maxTurboBoostPressure) * 3386.39), (1.0 - 1.4)), (1.0 / 1.40)));
35 | }
36 | double fullRichSpecificVolume = 1.0 / maxManifoldDensity;
37 | if (mixturePercentage == 100.0) {
38 | double targetFuelAirRatio = fullRichSpecificVolume * turboSpecificVolumeConstant;
39 | return targetFuelAirRatio;
40 | }
41 | else if (mixturePercentage >= 90.0) {
42 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (mixturePercentage - 100.0) * slope_90_100);
43 | return targetFuelAirRatio;
44 | }
45 | else if (mixturePercentage >= 75.0) {
46 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (mixturePercentage - 90.0) * slope_75_90);
47 | return targetFuelAirRatio;
48 | }
49 | else if (mixturePercentage >= 60.0) {
50 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (mixturePercentage - 75.0) * slope_60_75);
51 | return targetFuelAirRatio;
52 | }
53 | else if (mixturePercentage >= 20.0) {
54 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (mixturePercentage - 60.0) * slope_20_60);
55 | return targetFuelAirRatio;
56 | }
57 | else {
58 | double slope_0_20 = (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60) / 20.0;
59 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60 + (mixturePercentage - 20.0) * slope_0_20);
60 | return targetFuelAirRatio;
61 | }
62 | }
63 | };
64 |
65 | #endif // !TURBOCHARGER
66 |
--------------------------------------------------------------------------------
/src/TurboArrow/FdController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef FDCTRL
4 | #define FDCTRL
5 |
6 | #include "common.h"
7 | #include "PidController.h"
8 |
9 | class FdController
10 | {
11 | private:
12 | SimVars* simVars;
13 |
14 | ///
15 | /// An instance of the mixture PID controller.
16 | ///
17 | PidController* mixtureController;
18 |
19 | ///
20 | /// The current mixture control axis, from -16384 to 16384.
21 | ///
22 | int mixtureAxis = 16384;
23 |
24 | bool enabled = true;
25 |
26 | ///
27 | /// Calculates and updates mixture according to target and PIDs.
28 | ///
29 | ///
30 | void updateMixture(double deltaTime) {
31 | EngineControlData controls;
32 | controls.mixtureone = this->getDesiredMixture(deltaTime);
33 | SimConnect_SetDataOnSimObject(hSimConnect, DataTypes::EngineControls, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(EngineControlData), &controls);
34 | }
35 |
36 | ///
37 | /// Gets the mixture lever position to achieve the desired fuel/air mixture ratio
38 | ///
39 | /// Desired mixture setting
40 | double getDesiredMixture(double deltaTime) {
41 | double mixtureLeverPerc = 100.0 * (this->mixtureAxis + 16384) / 32768.0;
42 |
43 | if (!enabled) {
44 | return mixtureLeverPerc;
45 | }
46 |
47 | double targetFuelAirMixture = Turbocharger::getTargetFuelAirRatio(this->simVars->getAmbientTemperature(), this->simVars->getAmbientPressure(), mixtureLeverPerc);
48 | double simFuelAirMixture = this->simVars->getFuelAirRatio(1);
49 | printf("Target Mixture: %1.3f\nSim Mixture: %1.3f\n", targetFuelAirMixture, simFuelAirMixture);
50 |
51 | double error = targetFuelAirMixture - simFuelAirMixture;
52 | double pidOut = 0.0;
53 | pidOut = this->mixtureController->GetOutput(error, deltaTime);
54 | printf("Output from PID Controller: %2.3f\n", error);
55 | return max(0, min(100, this->simVars->getMixtureLeverPosition(1) + pidOut));
56 | }
57 |
58 | void updateVisibleMixture() {
59 | int targetMixture = this->mixtureAxis;
60 | double mixtureLeverPerc = 100.0 * (targetMixture + 16384) / 32768.0;
61 | this->simVars->setMixturePos(mixtureLeverPerc);
62 | }
63 |
64 | public:
65 | void init()
66 | {
67 | printf("FdController init");
68 |
69 | this->simVars = new SimVars();
70 |
71 | float p = 350.0;
72 | float i = 380.0;
73 | float d = 0.0;
74 | this->mixtureController = new PidController(p, i, d, -5, 5);
75 | }
76 |
77 | void update(int mixtureAxis, double deltaTime)
78 | {
79 | this->mixtureAxis = mixtureAxis;
80 | this->updateMixture(deltaTime);
81 | this->updateVisibleMixture();
82 | this->simVars->setFadecActiveFlag();
83 | }
84 | };
85 |
86 | FdController FdCtrlInstance;
87 |
88 | #endif // !FDCTRL
89 |
--------------------------------------------------------------------------------
/src/TurboArrow/FdGauge.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | #ifndef FDGAUGE
5 | #define FDGAUGE
6 |
7 | #ifndef __INTELLISENSE__
8 | # define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
9 | # define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
10 | #else
11 | # define MODULE_EXPORT
12 | # define MODULE_WASM_MODNAME(mod)
13 | # define __attribute__(x)
14 | # define __restrict__
15 | #endif
16 |
17 | #include "common.h"
18 | #include "FdController.h"
19 |
20 | const int MIN_MIX = -16384;
21 | const int MAX_MIX = 16384;
22 | const int MIX_STEP = 256;
23 |
24 | int globalMixtureAxis = MAX_MIX;
25 |
26 | class FdGauge
27 | {
28 | private:
29 |
30 | bool isConnected = false;
31 |
32 | ///
33 | /// Registers all the mixture SimConnect client events.
34 | ///
35 | void RegisterMixtureClientEvents()
36 | {
37 | printf("Registering mixture events...\r\n");
38 |
39 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixtureSet, "AXIS_MIXTURE_SET");
40 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixture1Set, "AXIS_MIXTURE1_SET");
41 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSetBest, "MIXTURE_SET_BEST");
42 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSet, "MIXTURE_SET");
43 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureRich, "MIXTURE_RICH");
44 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncr, "MIXTURE_INCR");
45 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncrSmall, "MIXTURE_INCR_SMALL");
46 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecrSmall, "MIXTURE_DECR_SMALL");
47 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecr, "MIXTURE_DECR");
48 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureLean, "MIXTURE_LEAN");
49 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Set, "MIXTURE1_SET");
50 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Rich, "MIXTURE1_RICH");
51 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Incr, "MIXTURE1_INCR");
52 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1IncrSmall, "MIXTURE1_INCR_SMALL");
53 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1DecrSmall, "MIXTURE1_DECR_SMALL");
54 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Decr, "MIXTURE1_DECR");
55 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Lean, "MIXTURE1_LEAN");
56 | }
57 |
58 | ///
59 | /// Registers the SimConnect mixture event group for capture.
60 | ///
61 | void RegisterMixtureEventGroup()
62 | {
63 | printf("Registering mixture event group...\r\n");
64 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixtureSet, TRUE);
65 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixture1Set, TRUE);
66 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSetBest, TRUE);
67 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSet, TRUE);
68 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureRich, TRUE);
69 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncr, TRUE);
70 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncrSmall, TRUE);
71 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecrSmall, TRUE);
72 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecr, TRUE);
73 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureLean, TRUE);
74 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Set, TRUE);
75 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Rich, TRUE);
76 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Incr, TRUE);
77 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1IncrSmall, TRUE);
78 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1DecrSmall, TRUE);
79 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Decr, TRUE);
80 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Lean, TRUE);
81 |
82 | SimConnect_SetNotificationGroupPriority(hSimConnect, EventGroups::Mixture, SIMCONNECT_GROUP_PRIORITY_HIGHEST_MASKABLE);
83 | }
84 |
85 | ///
86 | /// Initializes the connection to SimConnect.
87 | ///
88 | /// True if successful, false otherwise.
89 | bool InitializeSimConnect()
90 | {
91 | printf("Connecting to SimConnect...\r\n");
92 | if (SUCCEEDED(SimConnect_Open(&hSimConnect, "FdGauge", nullptr, 0, 0, 0)))
93 | {
94 | printf("SimConnect connected.\r\n");
95 |
96 | this->RegisterMixtureClientEvents();
97 | this->RegisterMixtureEventGroup();
98 |
99 | SimConnect_AddToDataDefinition(hSimConnect, DataTypes::EngineControls, "GENERAL ENG MIXTURE LEVER POSITION:1", "Percent");
100 |
101 | printf("SimConnect registrations complete.\r\n");
102 | return true;
103 | }
104 |
105 | printf("SimConnect failed.\r\n");
106 |
107 | return false;
108 | }
109 |
110 | ///
111 | /// A callback used for handling SimConnect updates.
112 | ///
113 | /// The update data sent by SimConnect.
114 | /// The size of the SimConnect data structure.
115 | /// A pointer specified by the client.
116 | static void CALLBACK HandleAxisEvent(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
117 | {
118 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EVENT)
119 | {
120 | SIMCONNECT_RECV_EVENT* evt = static_cast(pData);
121 | if (evt->uGroupID == EventGroups::Mixture)
122 | {
123 | FdGauge* fd = static_cast(pContext);
124 | if (fd == 0)
125 | {
126 | printf("FD pointer was null processing SimConnect event.\r\n");
127 | }
128 | else
129 | {
130 | HandleMixtureAxis(evt);
131 | }
132 | }
133 | }
134 |
135 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EXCEPTION)
136 | {
137 | SIMCONNECT_RECV_EXCEPTION* ex = static_cast(pData);
138 | printf("SimConnect Exception: %d \r\n", ex->dwException);
139 | }
140 | }
141 |
142 | ///
143 | /// Handles throttle axis updates received from SimConnect.
144 | ///
145 | /// A pointer to the SimConnect event structure.
146 | static void HandleMixtureAxis(SIMCONNECT_RECV_EVENT* evt)
147 | {
148 | switch (evt->uEventID)
149 | {
150 | case MixtureEventIDs::AxisMixtureSet:
151 | case MixtureEventIDs::AxisMixture1Set:
152 | globalMixtureAxis = static_cast(evt->dwData);
153 | break;
154 | case MixtureEventIDs::MixtureSetBest:
155 | case MixtureEventIDs::MixtureRich:
156 | case MixtureEventIDs::Mixture1Rich:
157 | globalMixtureAxis = MAX_MIX;
158 | break;
159 | case MixtureEventIDs::MixtureSet:
160 | globalMixtureAxis = (static_cast(evt->dwData) * 2) - MAX_MIX;
161 | break;
162 | case MixtureEventIDs::Mixture1Set:
163 | globalMixtureAxis = (static_cast(evt->dwData) * 2) - MAX_MIX;
164 | break;
165 | case MixtureEventIDs::MixtureLean:
166 | case MixtureEventIDs::Mixture1Lean:
167 | globalMixtureAxis = MIN_MIX;
168 | break;
169 | case MixtureEventIDs::MixtureIncr:
170 | case MixtureEventIDs::MixtureIncrSmall:
171 | case MixtureEventIDs::Mixture1Incr:
172 | case MixtureEventIDs::Mixture1IncrSmall:
173 | globalMixtureAxis += MIX_STEP; // TODO: CLAMP ALL INCR/DECR EVENTS
174 | break;
175 | case MixtureEventIDs::MixtureDecr:
176 | case MixtureEventIDs::MixtureDecrSmall:
177 | case MixtureEventIDs::Mixture1Decr:
178 | case MixtureEventIDs::Mixture1DecrSmall:
179 | globalMixtureAxis -= MIX_STEP; // TODO: CLAMP ALL INCR/DECR EVENTS
180 | break;
181 | }
182 |
183 | globalMixtureAxis = clamp(globalMixtureAxis, MIN_MIX, MAX_MIX);
184 | }
185 |
186 | public:
187 |
188 | ///
189 | /// Initializes the FD.
190 | ///
191 | /// True if successful, false otherwise.
192 | bool InitializeFD()
193 | {
194 | if (!this->InitializeSimConnect()) {
195 | printf("Init SimConnect failed");
196 | return false;
197 | }
198 |
199 | FdCtrlInstance.init();
200 | isConnected = true;
201 | SimConnect_CallDispatch(hSimConnect, HandleAxisEvent, this);
202 |
203 | return true;
204 | }
205 |
206 | ///
207 | /// A callback used to update the FD at each tick.
208 | ///
209 | /// The time since the previous update.
210 | /// True if successful, false otherwise.
211 | bool OnUpdate(double deltaTime)
212 | {
213 | if (isConnected == true) {
214 | FdCtrlInstance.update(globalMixtureAxis, deltaTime);
215 | }
216 |
217 | return true;
218 | }
219 |
220 | ///
221 | /// Kill.
222 | ///
223 | /// True if succesful, false otherwise.
224 | bool KillFD()
225 | {
226 | isConnected = false;
227 | unregister_all_named_vars();
228 | return SUCCEEDED(SimConnect_Close(hSimConnect));
229 | }
230 | };
231 |
232 | #endif // !FDGAUGE
233 |
--------------------------------------------------------------------------------
/src/TurboArrow/Mixture_Controller.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31410.357
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mixture_Controller", "Mixture_Controller.vcxproj", "{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|MSFS = Debug|MSFS
11 | Release|MSFS = Release|MSFS
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.ActiveCfg = Debug|MSFS
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.Build.0 = Debug|MSFS
16 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.ActiveCfg = Release|MSFS
17 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.Build.0 = Release|MSFS
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {9129C4E4-B566-46E3-81AE-74CCDE9CA599}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/TurboArrow/Mixture_Controller.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | MSFS
7 |
8 |
9 | Release
10 | MSFS
11 |
12 |
13 |
14 | 16.0
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}
16 | Module
17 | 10.0
18 | TurboArrow_Mixture_Controller
19 |
20 |
21 |
22 | Application
23 | true
24 | MSFS
25 | MultiByte
26 |
27 |
28 | Application
29 | false
30 | MSFS
31 | true
32 | MultiByte
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .wasm
48 |
49 |
50 | $(MSFS_IncludePath)
51 |
52 |
53 | .wasm
54 | $(MSFS_IncludePath)
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | false
67 |
68 |
69 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
70 | false
71 |
72 |
73 |
74 |
75 |
76 |
77 | ProgramDatabase
78 |
79 |
80 | stdcpp14
81 |
82 |
83 |
84 |
85 | %(AdditionalDependencies)
86 |
87 |
88 | true
89 | $(OutDir)$(TargetName)$(TargetExt)
90 |
91 |
92 |
93 |
94 | true
95 |
96 |
97 |
98 |
99 |
100 |
101 | $(OutDir)
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | Level3
111 | MaxSpeed
112 | true
113 | true
114 | true
115 | true
116 |
117 |
118 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
119 | false
120 | false
121 | false
122 |
123 |
124 |
125 |
126 |
127 |
128 | %(AdditionalDependencies)
129 |
130 |
131 | true
132 | $(OutDir)$(TargetName)$(TargetExt)
133 |
134 |
135 |
136 |
137 | false
138 |
139 |
140 |
141 |
142 |
143 |
144 | $(OutDir)
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/src/TurboArrow/Mixture_Controller.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/TurboArrow/PidController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef PIDC
4 | #define PIDC
5 |
6 | ///
7 | /// A class for controlling values via a PID.
8 | ///
9 | class PidController
10 | {
11 | private:
12 | ///
13 | /// The gain of the proportional term.
14 | ///
15 | double gainProportion;
16 |
17 | ///
18 | /// The gain of the integral term.
19 | ///
20 | double gainIntegral;
21 |
22 | ///
23 | /// The gain of the derivative term.
24 | ///
25 | double gainDerivative;
26 |
27 | ///
28 | /// The minimum output allowed.
29 | ///
30 | double minOutput;
31 |
32 | ///
33 | /// The maximum output allowed.
34 | ///
35 | double maxOutput;
36 |
37 | ///
38 | /// The previous error amount.
39 | ///
40 | double prevError;
41 |
42 | ///
43 | /// The previous PID output.
44 | ///
45 | double prevOutput;
46 |
47 | ///
48 | /// The current integral term.
49 | ///
50 | double integral;
51 |
52 | ///
53 | /// Clamps a value to the PID min and max outputs.
54 | ///
55 | /// The value to clamp.
56 | /// The clamped value.
57 | double Clamp(double value, double max, double min)
58 | {
59 | if (value > max)
60 | {
61 | return max;
62 | }
63 |
64 | if (value < min)
65 | {
66 | return min;
67 | }
68 |
69 | return value;
70 | }
71 |
72 | public:
73 | ///
74 | /// Creates an instance of a PidController.
75 | ///
76 | /// The gain of the proportional term.
77 | /// The gain of the integral term.
78 | /// The gain of the derivative term.
79 | /// The maximum output.
80 | /// The minimum output.
81 | PidController(double gainProportion, double gainIntegral, double gainDerivative, double minOutput, double maxOutput)
82 | : gainProportion(gainProportion), gainIntegral(gainIntegral), gainDerivative(gainDerivative),
83 | minOutput(minOutput), maxOutput(maxOutput), prevError(0), prevOutput(0), integral(0) { }
84 |
85 | template int sgn(T val) {
86 | return (T(0) < val) - (val < T(0));
87 | }
88 |
89 | ///
90 | /// Gets the output of the PID for a given error and timespan.
91 | ///
92 | /// The error vs the target value.
93 | /// The delta time vs the previous observation.
94 | /// The PID output.
95 | double GetOutput(double error, double deltaTime)
96 | {
97 | auto proportion = gainProportion * error;
98 | //if ((gainIntegral * integral) >= maxOutput) {
99 | //integral -= (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
100 | //}
101 | //else if ((gainIntegral * integral) <= minOutput) {
102 | integral += (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
103 | //}
104 |
105 |
106 | if (sgn(error) != sgn(prevError)) {
107 | integral = 0;
108 | }
109 |
110 | auto derivative = gainDerivative * ((error - prevError) / deltaTime);
111 | derivative = this->Clamp(derivative, 20.0, -20.0);
112 |
113 | auto output = this->Clamp(proportion + (gainIntegral * integral) + (derivative), this->maxOutput, this->minOutput);
114 |
115 | //printf("P: %.2f I: %.2f D %.2f \r\n", proportion, (gainIntegral * integral), (derivative));
116 |
117 | prevError = error;
118 | prevOutput = output;
119 |
120 | return output;
121 | }
122 | };
123 |
124 | #endif
125 |
--------------------------------------------------------------------------------
/src/TurboArrow/SimConnectDefs.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef SCDEFS
4 | #define SCDEFS
5 |
6 | #include "common.h"
7 |
8 | ///
9 | /// SimConnect client event IDs for the mixture group.
10 | ///
11 | enum MixtureEventIDs
12 | {
13 | AxisMixtureSet,
14 | AxisMixture1Set,
15 | MixtureSetBest,
16 | MixtureSet,
17 | MixtureRich,
18 | MixtureIncr,
19 | MixtureIncrSmall,
20 | MixtureDecrSmall,
21 | MixtureDecr,
22 | MixtureLean,
23 | Mixture1Set,
24 | Mixture1Rich,
25 | Mixture1Incr,
26 | Mixture1IncrSmall,
27 | Mixture1DecrSmall,
28 | Mixture1Decr,
29 | Mixture1Lean
30 | };
31 |
32 | ///
33 | /// SimConnect event groups.
34 | ///
35 | enum EventGroups
36 | {
37 | ///
38 | /// The client event group ID to use when any events from the mixture axis group
39 | /// are received.
40 | ///
41 | Mixture = 0,
42 | };
43 |
44 | ///
45 | /// SimConnect data types for sending the sim updates.
46 | ///
47 | enum DataTypes
48 | {
49 | ///
50 | /// The data type ID to use when sending engine controls data.
51 | ///
52 | EngineControls = 0
53 | };
54 | ///
55 | /// Engine controls.
56 | ///
57 | struct EngineControlData
58 | {
59 | ///
60 | /// The mixture of the engine, expressed in a 100s base percent.
61 | ///
62 | double mixtureone;
63 | };
64 |
65 | ///
66 | /// A collection of SimVar unit enums.
67 | ///
68 | class Units
69 | {
70 | public:
71 | ///
72 | /// The Percent SimVar unit.
73 | ///
74 | ENUM Percent = get_units_enum("Percent");
75 |
76 | ///
77 | /// The Ratio SimVar unit.
78 | ///
79 | ENUM Ratio = get_units_enum("Ratio");
80 |
81 | ENUM Celsius = get_units_enum("Celsius");
82 |
83 | ENUM Number = get_units_enum("Number");
84 |
85 | ENUM inHg = get_units_enum("inches of mercury");
86 | };
87 |
88 |
89 | ///
90 | /// A collection of SimVar enums.
91 | ///
92 | class SimVars
93 | {
94 | public:
95 | Units* m_Units;
96 |
97 | ///
98 | /// The GENERAL ENGINE MIXTURE LEVER POSITION SimVar.
99 | ///
100 | ENUM Mixture = get_aircraft_var_enum("GENERAL ENG MIXTURE LEVER POSITION");
101 |
102 | ENUM FuelAirRatio = get_aircraft_var_enum("RECIP MIXTURE RATIO");
103 |
104 | ENUM AmbientTemperature = get_aircraft_var_enum("AMBIENT TEMPERATURE");
105 |
106 | ENUM AmbientPressure = get_aircraft_var_enum("AMBIENT PRESSURE");
107 |
108 | ID FadecActive;
109 |
110 | ///
111 | /// The local variable for the visible mixture position
112 | ///
113 | ID MixturePos;
114 |
115 | SimVars()
116 | {
117 | this->initializeVars();
118 | }
119 |
120 | void initializeVars() {
121 | FadecActive = register_named_variable("FADEC_ACTIVE");
122 | MixturePos = register_named_variable("Mixture_Pos");
123 | m_Units = new Units();
124 | }
125 |
126 | void setFadecActiveFlag() {
127 | set_named_variable_value(FadecActive, 1);
128 | }
129 |
130 | void setMixturePos(double value) {
131 | set_named_variable_value(MixturePos, value);
132 | }
133 |
134 | FLOAT64 getMixtureLeverPosition(int index) {
135 | return aircraft_varget(Mixture, m_Units->Percent, index);
136 | }
137 |
138 | FLOAT64 getFuelAirRatio(int index) {
139 | return aircraft_varget(FuelAirRatio, m_Units->Ratio, index);
140 | }
141 |
142 | FLOAT64 getAmbientTemperature() {
143 | return aircraft_varget(AmbientTemperature, m_Units->Celsius, 0);
144 | }
145 |
146 | FLOAT64 getAmbientPressure() {
147 | return aircraft_varget(AmbientPressure, m_Units->inHg, 0);
148 | }
149 | };
150 |
151 | #endif
152 |
--------------------------------------------------------------------------------
/src/TurboArrow/TurboArrow_Mixture_Controller.cpp:
--------------------------------------------------------------------------------
1 |
2 | #ifdef _MSC_VER
3 | #define snprintf _snprintf_s
4 | #elif !defined(__MINGW32__)
5 | #include
6 | #endif
7 |
8 | #include "FdGauge.h"
9 |
10 | FdGauge FD_GAUGE;
11 |
12 |
13 | // ------------------------
14 | // Callbacks
15 | extern "C" {
16 |
17 | MSFS_CALLBACK bool FdGauge_gauge_callback(FsContext ctx, int service_id, void* pData)
18 | {
19 | switch (service_id)
20 | {
21 | case PANEL_SERVICE_PRE_INSTALL:
22 | {
23 | return true;
24 | }
25 | break;
26 | case PANEL_SERVICE_POST_INSTALL:
27 | {
28 | return FD_GAUGE.InitializeFD();
29 | }
30 | break;
31 | case PANEL_SERVICE_PRE_DRAW:
32 | {
33 | sGaugeDrawData* drawData = static_cast(pData);
34 | return FD_GAUGE.OnUpdate(drawData->dt);
35 | }
36 | break;
37 | case PANEL_SERVICE_PRE_KILL:
38 | {
39 | FD_GAUGE.KillFD();
40 | return true;
41 | }
42 | break;
43 | }
44 | return false;
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/TurboArrow/common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef _COMMON_H_
4 | #define _COMMON_H_
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "SimConnectDefs.h"
16 |
17 | #include "turbocharger.h"
18 | #include
19 | #include
20 |
21 |
22 | ///
23 | /// The handle to the SimConnect instance.
24 | ///
25 | HANDLE hSimConnect;
26 |
27 | double clamp(double v, double lo, double hi)
28 | {
29 | assert(!(hi < lo));
30 | return (v < lo) ? lo : (hi < v) ? hi : v;
31 | }
32 |
33 |
34 | #endif
--------------------------------------------------------------------------------
/src/TurboArrow/turbocharger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef TURBOCHARGER
4 | #define TURBOCHARGER
5 |
6 | #include "common.h"
7 |
8 | class Turbocharger
9 | {
10 | public:
11 | static double getTargetFuelAirRatio(FLOAT64 ambientTemperature, FLOAT64 ambientPressure, double mixturePercentage) {
12 | double maxPowerManifoldPressure = 41.0;
13 | double criticalAltitude = 12000.0;
14 | double icaoStandardPressureAtCriticalAltitude = 19.03455;
15 | double maxTurboBoostPressure = maxPowerManifoldPressure - icaoStandardPressureAtCriticalAltitude;
16 | double fullRichSpecificVolumeConstant = 0.110236;
17 | double turboBoostCorrectionFactor = 1.252303;
18 | double turboSpecificVolumeConstant = fullRichSpecificVolumeConstant * turboBoostCorrectionFactor;
19 | double slope_90_100 = 0.000487531;
20 | double slope_75_90 = 0.000243697;
21 | double slope_60_75 = 0.000409167;
22 | double slope_20_60 = 0.000954534;
23 | double ambientPressurePa = ambientPressure * 3386.39;
24 | double maxManifoldPressurePa = maxPowerManifoldPressure * 3386.39;
25 | double ambientTemperatureK = ambientTemperature + 273.15;
26 | double maxManifoldDensity = 0.0;
27 | if (ambientPressure >= icaoStandardPressureAtCriticalAltitude) {
28 | maxManifoldDensity = maxManifoldPressurePa / (287.058 * pow(pow(ambientPressurePa, (1.0 - 1.4)) * pow(ambientTemperatureK, 1.4) / pow(maxManifoldPressurePa, (1.0 - 1.4)), (1.0 / 1.40)));
29 | }
30 | else {
31 | maxManifoldDensity = ((ambientPressure + maxTurboBoostPressure) * 3386.39) / (287.058 * pow(pow(ambientPressurePa, (1.0 - 1.4)) * pow(ambientTemperatureK, 1.4) / pow(((ambientPressure + maxTurboBoostPressure) * 3386.39), (1.0 - 1.4)), (1.0 / 1.40)));
32 | }
33 | double fullRichSpecificVolume = 1.0 / maxManifoldDensity;
34 | if (mixturePercentage == 100.0) {
35 | double targetFuelAirRatio = fullRichSpecificVolume * turboSpecificVolumeConstant;
36 | return targetFuelAirRatio;
37 | }
38 | else if (mixturePercentage >= 90.0) {
39 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (mixturePercentage - 100.0) * slope_90_100);
40 | return targetFuelAirRatio;
41 | }
42 | else if (mixturePercentage >= 75.0) {
43 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (mixturePercentage - 90.0) * slope_75_90);
44 | return targetFuelAirRatio;
45 | }
46 | else if (mixturePercentage >= 60.0) {
47 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (mixturePercentage - 75.0) * slope_60_75);
48 | return targetFuelAirRatio;
49 | }
50 | else if (mixturePercentage >= 20.0) {
51 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (mixturePercentage - 60.0) * slope_20_60);
52 | return targetFuelAirRatio;
53 | }
54 | else {
55 | double slope_0_20 = (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60) / 20.0;
56 | double targetFuelAirRatio = fullRichSpecificVolume * (turboSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60 + (mixturePercentage - 20.0) * slope_0_20);
57 | return targetFuelAirRatio;
58 | }
59 | }
60 | };
61 |
62 | #endif // !TURBOCHARGER
63 |
--------------------------------------------------------------------------------
/src/TurboBonanza/Bonanza_Mixture_Controller.cpp:
--------------------------------------------------------------------------------
1 |
2 | #ifdef _MSC_VER
3 | #define snprintf _snprintf_s
4 | #elif !defined(__MINGW32__)
5 | #include
6 | #endif
7 |
8 | #include "FdGauge.h"
9 |
10 | FdGauge FD_GAUGE;
11 |
12 |
13 | // ------------------------
14 | // Callbacks
15 | extern "C" {
16 |
17 | MSFS_CALLBACK bool FdGauge_gauge_callback(FsContext ctx, int service_id, void* pData)
18 | {
19 | switch (service_id)
20 | {
21 | case PANEL_SERVICE_PRE_INSTALL:
22 | {
23 | return true;
24 | }
25 | break;
26 | case PANEL_SERVICE_POST_INSTALL:
27 | {
28 | return FD_GAUGE.InitializeFD();
29 | }
30 | break;
31 | case PANEL_SERVICE_PRE_DRAW:
32 | {
33 | sGaugeDrawData* drawData = static_cast(pData);
34 | return FD_GAUGE.OnUpdate(drawData->dt);
35 | }
36 | break;
37 | case PANEL_SERVICE_PRE_KILL:
38 | {
39 | FD_GAUGE.KillFD();
40 | return true;
41 | }
42 | break;
43 | }
44 | return false;
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/TurboBonanza/FdController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef FDCTRL
4 | #define FDCTRL
5 |
6 | #include "common.h"
7 | #include "PidController.h"
8 |
9 | class FdController
10 | {
11 | private:
12 | SimVars* simVars;
13 |
14 | ///
15 | /// An instance of the mixture PID controller.
16 | ///
17 | PidController* mixtureController;
18 |
19 | ///
20 | /// The current mixture control axis, from -16384 to 16384.
21 | ///
22 | int mixtureAxis = 16384;
23 |
24 | bool enabled = true;
25 |
26 | ///
27 | /// Calculates and updates mixture according to target and PIDs.
28 | ///
29 | ///
30 | void updateMixture(double deltaTime) {
31 | EngineControlData controls;
32 | controls.mixtureone = this->getDesiredMixture(deltaTime);
33 | SimConnect_SetDataOnSimObject(hSimConnect, DataTypes::EngineControls, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(EngineControlData), &controls);
34 | }
35 |
36 | ///
37 | /// Gets the mixture lever position to achieve the desired fuel/air mixture ratio
38 | ///
39 | /// Desired mixture setting
40 | double getDesiredMixture(double deltaTime) {
41 | double mixtureLeverPerc = 100.0 * (this->mixtureAxis + 16384) / 32768.0;
42 |
43 | if (!enabled) {
44 | return mixtureLeverPerc;
45 | }
46 |
47 | double targetFuelAirMixture = Turbocharger::getTargetFuelAirRatio(this->simVars->getAmbientTemperature(), this->simVars->getAmbientPressure(), mixtureLeverPerc);
48 | double simFuelAirMixture = this->simVars->getFuelAirRatio(1);
49 | printf("Target Mixture: %1.3f\nSim Mixture: %1.3f\n", targetFuelAirMixture, simFuelAirMixture);
50 |
51 | double error = targetFuelAirMixture - simFuelAirMixture;
52 | double pidOut = 0.0;
53 | pidOut = this->mixtureController->GetOutput(error, deltaTime);
54 | printf("Output from PID Controller: %2.3f\n", error);
55 | return max(0, min(100, this->simVars->getMixtureLeverPosition(1) + pidOut));
56 | }
57 |
58 | void updateVisibleMixture() {
59 | int targetMixture = this->mixtureAxis;
60 | double mixtureLeverPerc = 100.0 * (targetMixture + 16384) / 32768.0;
61 | this->simVars->setMixturePos(mixtureLeverPerc);
62 | }
63 |
64 | public:
65 | void init()
66 | {
67 | printf("FdController init");
68 |
69 | this->simVars = new SimVars();
70 |
71 | float p = 350.0;
72 | float i = 380.0;
73 | float d = 0.0;
74 | this->mixtureController = new PidController(p, i, d, -5, 5);
75 | }
76 |
77 | void update(int mixtureAxis, double deltaTime)
78 | {
79 | this->mixtureAxis = mixtureAxis;
80 | this->updateMixture(deltaTime);
81 | this->updateVisibleMixture();
82 | this->simVars->setFadecActiveFlag();
83 | }
84 | };
85 |
86 | FdController FdCtrlInstance;
87 |
88 | #endif // !FDCTRL
89 |
--------------------------------------------------------------------------------
/src/TurboBonanza/FdGauge.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | #ifndef FDGAUGE
5 | #define FDGAUGE
6 |
7 | #ifndef __INTELLISENSE__
8 | # define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
9 | # define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
10 | #else
11 | # define MODULE_EXPORT
12 | # define MODULE_WASM_MODNAME(mod)
13 | # define __attribute__(x)
14 | # define __restrict__
15 | #endif
16 |
17 | #include "common.h"
18 | #include "FdController.h"
19 |
20 | const int MIN_MIX = -16384;
21 | const int MAX_MIX = 16384;
22 | const int MIX_STEP = 256;
23 |
24 | int globalMixtureAxis = MAX_MIX;
25 |
26 | class FdGauge
27 | {
28 | private:
29 |
30 | bool isConnected = false;
31 |
32 | ///
33 | /// Registers all the mixture SimConnect client events.
34 | ///
35 | void RegisterMixtureClientEvents()
36 | {
37 | printf("Registering mixture events...\r\n");
38 |
39 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixtureSet, "AXIS_MIXTURE_SET");
40 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::AxisMixture1Set, "AXIS_MIXTURE1_SET");
41 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSetBest, "MIXTURE_SET_BEST");
42 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureSet, "MIXTURE_SET");
43 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureRich, "MIXTURE_RICH");
44 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncr, "MIXTURE_INCR");
45 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureIncrSmall, "MIXTURE_INCR_SMALL");
46 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecrSmall, "MIXTURE_DECR_SMALL");
47 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureDecr, "MIXTURE_DECR");
48 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::MixtureLean, "MIXTURE_LEAN");
49 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Set, "MIXTURE1_SET");
50 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Rich, "MIXTURE1_RICH");
51 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Incr, "MIXTURE1_INCR");
52 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1IncrSmall, "MIXTURE1_INCR_SMALL");
53 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1DecrSmall, "MIXTURE1_DECR_SMALL");
54 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Decr, "MIXTURE1_DECR");
55 | SimConnect_MapClientEventToSimEvent(hSimConnect, MixtureEventIDs::Mixture1Lean, "MIXTURE1_LEAN");
56 | }
57 |
58 | ///
59 | /// Registers the SimConnect mixture event group for capture.
60 | ///
61 | void RegisterMixtureEventGroup()
62 | {
63 | printf("Registering mixture event group...\r\n");
64 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixtureSet, TRUE);
65 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::AxisMixture1Set, TRUE);
66 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSetBest, TRUE);
67 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureSet, TRUE);
68 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureRich, TRUE);
69 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncr, TRUE);
70 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureIncrSmall, TRUE);
71 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecrSmall, TRUE);
72 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureDecr, TRUE);
73 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::MixtureLean, TRUE);
74 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Set, TRUE);
75 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Rich, TRUE);
76 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Incr, TRUE);
77 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1IncrSmall, TRUE);
78 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1DecrSmall, TRUE);
79 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Decr, TRUE);
80 | SimConnect_AddClientEventToNotificationGroup(hSimConnect, EventGroups::Mixture, MixtureEventIDs::Mixture1Lean, TRUE);
81 |
82 | SimConnect_SetNotificationGroupPriority(hSimConnect, EventGroups::Mixture, SIMCONNECT_GROUP_PRIORITY_HIGHEST_MASKABLE);
83 | }
84 |
85 | ///
86 | /// Initializes the connection to SimConnect.
87 | ///
88 | /// True if successful, false otherwise.
89 | bool InitializeSimConnect()
90 | {
91 | printf("Connecting to SimConnect...\r\n");
92 | if (SUCCEEDED(SimConnect_Open(&hSimConnect, "FdGauge", nullptr, 0, 0, 0)))
93 | {
94 | printf("SimConnect connected.\r\n");
95 |
96 | this->RegisterMixtureClientEvents();
97 | this->RegisterMixtureEventGroup();
98 |
99 | SimConnect_AddToDataDefinition(hSimConnect, DataTypes::EngineControls, "GENERAL ENG MIXTURE LEVER POSITION:1", "Percent");
100 |
101 | printf("SimConnect registrations complete.\r\n");
102 | return true;
103 | }
104 |
105 | printf("SimConnect failed.\r\n");
106 |
107 | return false;
108 | }
109 |
110 | ///
111 | /// A callback used for handling SimConnect updates.
112 | ///
113 | /// The update data sent by SimConnect.
114 | /// The size of the SimConnect data structure.
115 | /// A pointer specified by the client.
116 | static void CALLBACK HandleAxisEvent(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
117 | {
118 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EVENT)
119 | {
120 | SIMCONNECT_RECV_EVENT* evt = static_cast(pData);
121 | if (evt->uGroupID == EventGroups::Mixture)
122 | {
123 | FdGauge* fd = static_cast(pContext);
124 | if (fd == 0)
125 | {
126 | printf("FD pointer was null processing SimConnect event.\r\n");
127 | }
128 | else
129 | {
130 | HandleMixtureAxis(evt);
131 | }
132 | }
133 | }
134 |
135 | if (pData->dwID == SIMCONNECT_RECV_ID::SIMCONNECT_RECV_ID_EXCEPTION)
136 | {
137 | SIMCONNECT_RECV_EXCEPTION* ex = static_cast(pData);
138 | printf("SimConnect Exception: %d \r\n", ex->dwException);
139 | }
140 | }
141 |
142 | ///
143 | /// Handles throttle axis updates received from SimConnect.
144 | ///
145 | /// A pointer to the SimConnect event structure.
146 | static void HandleMixtureAxis(SIMCONNECT_RECV_EVENT* evt)
147 | {
148 | switch (evt->uEventID)
149 | {
150 | case MixtureEventIDs::AxisMixtureSet:
151 | case MixtureEventIDs::AxisMixture1Set:
152 | globalMixtureAxis = static_cast(evt->dwData);
153 | break;
154 | case MixtureEventIDs::MixtureSetBest:
155 | case MixtureEventIDs::MixtureRich:
156 | case MixtureEventIDs::Mixture1Rich:
157 | globalMixtureAxis = MAX_MIX;
158 | break;
159 | case MixtureEventIDs::MixtureSet:
160 | globalMixtureAxis = (static_cast(evt->dwData) * 2) - MAX_MIX;
161 | break;
162 | case MixtureEventIDs::Mixture1Set:
163 | globalMixtureAxis = (static_cast(evt->dwData) * 2) - MAX_MIX;
164 | break;
165 | case MixtureEventIDs::MixtureLean:
166 | case MixtureEventIDs::Mixture1Lean:
167 | globalMixtureAxis = MIN_MIX;
168 | break;
169 | case MixtureEventIDs::MixtureIncr:
170 | case MixtureEventIDs::MixtureIncrSmall:
171 | case MixtureEventIDs::Mixture1Incr:
172 | case MixtureEventIDs::Mixture1IncrSmall:
173 | globalMixtureAxis += MIX_STEP; // TODO: CLAMP ALL INCR/DECR EVENTS
174 | break;
175 | case MixtureEventIDs::MixtureDecr:
176 | case MixtureEventIDs::MixtureDecrSmall:
177 | case MixtureEventIDs::Mixture1Decr:
178 | case MixtureEventIDs::Mixture1DecrSmall:
179 | globalMixtureAxis -= MIX_STEP; // TODO: CLAMP ALL INCR/DECR EVENTS
180 | break;
181 | }
182 |
183 | globalMixtureAxis = clamp(globalMixtureAxis, MIN_MIX, MAX_MIX);
184 | }
185 |
186 | public:
187 |
188 | ///
189 | /// Initializes the FD.
190 | ///
191 | /// True if successful, false otherwise.
192 | bool InitializeFD()
193 | {
194 | if (!this->InitializeSimConnect()) {
195 | printf("Init SimConnect failed");
196 | return false;
197 | }
198 |
199 | FdCtrlInstance.init();
200 | isConnected = true;
201 | SimConnect_CallDispatch(hSimConnect, HandleAxisEvent, this);
202 |
203 | return true;
204 | }
205 |
206 | ///
207 | /// A callback used to update the FD at each tick.
208 | ///
209 | /// The time since the previous update.
210 | /// True if successful, false otherwise.
211 | bool OnUpdate(double deltaTime)
212 | {
213 | if (isConnected == true) {
214 | FdCtrlInstance.update(globalMixtureAxis, deltaTime);
215 | }
216 |
217 | return true;
218 | }
219 |
220 | ///
221 | /// Kill.
222 | ///
223 | /// True if succesful, false otherwise.
224 | bool KillFD()
225 | {
226 | isConnected = false;
227 | unregister_all_named_vars();
228 | return SUCCEEDED(SimConnect_Close(hSimConnect));
229 | }
230 | };
231 |
232 | #endif // !FDGAUGE
233 |
--------------------------------------------------------------------------------
/src/TurboBonanza/Mixture_Controller.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31410.357
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mixture_Controller", "Mixture_Controller.vcxproj", "{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|MSFS = Debug|MSFS
11 | Release|MSFS = Release|MSFS
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.ActiveCfg = Debug|MSFS
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.Build.0 = Debug|MSFS
16 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.ActiveCfg = Release|MSFS
17 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.Build.0 = Release|MSFS
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {9129C4E4-B566-46E3-81AE-74CCDE9CA599}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/TurboBonanza/Mixture_Controller.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | MSFS
7 |
8 |
9 | Release
10 | MSFS
11 |
12 |
13 |
14 | 16.0
15 | {A5468B35-BBBD-4C55-97ED-81BFE343B0E4}
16 | Module
17 | 10.0
18 | Bonanza_Mixture_Controller
19 |
20 |
21 |
22 | Application
23 | true
24 | MSFS
25 | MultiByte
26 |
27 |
28 | Application
29 | false
30 | MSFS
31 | true
32 | MultiByte
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .wasm
48 |
49 |
50 | $(MSFS_IncludePath)
51 |
52 |
53 | .wasm
54 | $(MSFS_IncludePath)
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | false
67 |
68 |
69 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
70 | false
71 |
72 |
73 |
74 |
75 |
76 |
77 | ProgramDatabase
78 |
79 |
80 | stdcpp14
81 |
82 |
83 |
84 |
85 | %(AdditionalDependencies)
86 |
87 |
88 | true
89 | $(OutDir)$(TargetName)$(TargetExt)
90 |
91 |
92 |
93 |
94 | true
95 |
96 |
97 |
98 |
99 |
100 |
101 | $(OutDir)
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | Level3
111 | MaxSpeed
112 | true
113 | true
114 | true
115 | true
116 |
117 |
118 | __wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)
119 | false
120 | false
121 | false
122 |
123 |
124 |
125 |
126 |
127 |
128 | %(AdditionalDependencies)
129 |
130 |
131 | true
132 | $(OutDir)$(TargetName)$(TargetExt)
133 |
134 |
135 |
136 |
137 | false
138 |
139 |
140 |
141 |
142 |
143 |
144 | $(OutDir)
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/src/TurboBonanza/Mixture_Controller.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/TurboBonanza/PidController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef PIDC
4 | #define PIDC
5 |
6 | ///
7 | /// A class for controlling values via a PID.
8 | ///
9 | class PidController
10 | {
11 | private:
12 | ///
13 | /// The gain of the proportional term.
14 | ///
15 | double gainProportion;
16 |
17 | ///
18 | /// The gain of the integral term.
19 | ///
20 | double gainIntegral;
21 |
22 | ///
23 | /// The gain of the derivative term.
24 | ///
25 | double gainDerivative;
26 |
27 | ///
28 | /// The minimum output allowed.
29 | ///
30 | double minOutput;
31 |
32 | ///
33 | /// The maximum output allowed.
34 | ///
35 | double maxOutput;
36 |
37 | ///
38 | /// The previous error amount.
39 | ///
40 | double prevError;
41 |
42 | ///
43 | /// The previous PID output.
44 | ///
45 | double prevOutput;
46 |
47 | ///
48 | /// The current integral term.
49 | ///
50 | double integral;
51 |
52 | ///
53 | /// Clamps a value to the PID min and max outputs.
54 | ///
55 | /// The value to clamp.
56 | /// The clamped value.
57 | double Clamp(double value, double max, double min)
58 | {
59 | if (value > max)
60 | {
61 | return max;
62 | }
63 |
64 | if (value < min)
65 | {
66 | return min;
67 | }
68 |
69 | return value;
70 | }
71 |
72 | public:
73 | ///
74 | /// Creates an instance of a PidController.
75 | ///
76 | /// The gain of the proportional term.
77 | /// The gain of the integral term.
78 | /// The gain of the derivative term.
79 | /// The maximum output.
80 | /// The minimum output.
81 | PidController(double gainProportion, double gainIntegral, double gainDerivative, double minOutput, double maxOutput)
82 | : gainProportion(gainProportion), gainIntegral(gainIntegral), gainDerivative(gainDerivative),
83 | minOutput(minOutput), maxOutput(maxOutput), prevError(0), prevOutput(0), integral(0) { }
84 |
85 | template int sgn(T val) {
86 | return (T(0) < val) - (val < T(0));
87 | }
88 |
89 | ///
90 | /// Gets the output of the PID for a given error and timespan.
91 | ///
92 | /// The error vs the target value.
93 | /// The delta time vs the previous observation.
94 | /// The PID output.
95 | double GetOutput(double error, double deltaTime)
96 | {
97 | auto proportion = gainProportion * error;
98 | //if ((gainIntegral * integral) >= maxOutput) {
99 | //integral -= (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
100 | //}
101 | //else if ((gainIntegral * integral) <= minOutput) {
102 | integral += (error * deltaTime) + ((deltaTime * (error - prevError)) / 2);
103 | //}
104 |
105 |
106 | if (sgn(error) != sgn(prevError)) {
107 | integral = 0;
108 | }
109 |
110 | auto derivative = gainDerivative * ((error - prevError) / deltaTime);
111 | derivative = this->Clamp(derivative, 20.0, -20.0);
112 |
113 | auto output = this->Clamp(proportion + (gainIntegral * integral) + (derivative), this->maxOutput, this->minOutput);
114 |
115 | //printf("P: %.2f I: %.2f D %.2f \r\n", proportion, (gainIntegral * integral), (derivative));
116 |
117 | prevError = error;
118 | prevOutput = output;
119 |
120 | return output;
121 | }
122 | };
123 |
124 | #endif
125 |
--------------------------------------------------------------------------------
/src/TurboBonanza/SimConnectDefs.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef SCDEFS
4 | #define SCDEFS
5 |
6 | #include "common.h"
7 |
8 | ///
9 | /// SimConnect client event IDs for the mixture group.
10 | ///
11 | enum MixtureEventIDs
12 | {
13 | AxisMixtureSet,
14 | AxisMixture1Set,
15 | MixtureSetBest,
16 | MixtureSet,
17 | MixtureRich,
18 | MixtureIncr,
19 | MixtureIncrSmall,
20 | MixtureDecrSmall,
21 | MixtureDecr,
22 | MixtureLean,
23 | Mixture1Set,
24 | Mixture1Rich,
25 | Mixture1Incr,
26 | Mixture1IncrSmall,
27 | Mixture1DecrSmall,
28 | Mixture1Decr,
29 | Mixture1Lean
30 | };
31 |
32 | ///
33 | /// SimConnect event groups.
34 | ///
35 | enum EventGroups
36 | {
37 | ///
38 | /// The client event group ID to use when any events from the mixture axis group
39 | /// are received.
40 | ///
41 | Mixture = 0,
42 | };
43 |
44 | ///
45 | /// SimConnect data types for sending the sim updates.
46 | ///
47 | enum DataTypes
48 | {
49 | ///
50 | /// The data type ID to use when sending engine controls data.
51 | ///
52 | EngineControls = 0
53 | };
54 | ///
55 | /// Engine controls.
56 | ///
57 | struct EngineControlData
58 | {
59 | ///
60 | /// The mixture of the engine, expressed in a 100s base percent.
61 | ///
62 | double mixtureone;
63 | };
64 |
65 | ///
66 | /// A collection of SimVar unit enums.
67 | ///
68 | class Units
69 | {
70 | public:
71 | ///
72 | /// The Percent SimVar unit.
73 | ///
74 | ENUM Percent = get_units_enum("Percent");
75 |
76 | ///
77 | /// The Ratio SimVar unit.
78 | ///
79 | ENUM Ratio = get_units_enum("Ratio");
80 |
81 | ENUM Celsius = get_units_enum("Celsius");
82 |
83 | ENUM Number = get_units_enum("Number");
84 |
85 | ENUM inHg = get_units_enum("inches of mercury");
86 | };
87 |
88 |
89 | ///
90 | /// A collection of SimVar enums.
91 | ///
92 | class SimVars
93 | {
94 | public:
95 | Units* m_Units;
96 |
97 | ///
98 | /// The GENERAL ENGINE MIXTURE LEVER POSITION SimVar.
99 | ///
100 | ENUM Mixture = get_aircraft_var_enum("GENERAL ENG MIXTURE LEVER POSITION");
101 |
102 | ENUM FuelAirRatio = get_aircraft_var_enum("RECIP MIXTURE RATIO");
103 |
104 | ENUM AmbientTemperature = get_aircraft_var_enum("AMBIENT TEMPERATURE");
105 |
106 | ENUM AmbientPressure = get_aircraft_var_enum("AMBIENT PRESSURE");
107 |
108 | ID FadecActive;
109 |
110 | ///
111 | /// The local variable for the visible mixture position
112 | ///
113 | ID MixturePos;
114 |
115 | SimVars()
116 | {
117 | this->initializeVars();
118 | }
119 |
120 | void initializeVars() {
121 | FadecActive = register_named_variable("FADEC_ACTIVE");
122 | MixturePos = register_named_variable("Mixture_Pos");
123 | m_Units = new Units();
124 | }
125 |
126 | void setFadecActiveFlag() {
127 | set_named_variable_value(FadecActive, 1);
128 | }
129 |
130 | void setMixturePos(double value) {
131 | set_named_variable_value(MixturePos, value);
132 | }
133 |
134 | FLOAT64 getMixtureLeverPosition(int index) {
135 | return aircraft_varget(Mixture, m_Units->Percent, index);
136 | }
137 |
138 | FLOAT64 getFuelAirRatio(int index) {
139 | return aircraft_varget(FuelAirRatio, m_Units->Ratio, index);
140 | }
141 |
142 | FLOAT64 getAmbientTemperature() {
143 | return aircraft_varget(AmbientTemperature, m_Units->Celsius, 0);
144 | }
145 |
146 | FLOAT64 getAmbientPressure() {
147 | return aircraft_varget(AmbientPressure, m_Units->inHg, 0);
148 | }
149 | };
150 |
151 | #endif
152 |
--------------------------------------------------------------------------------
/src/TurboBonanza/common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef _COMMON_H_
4 | #define _COMMON_H_
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "SimConnectDefs.h"
16 |
17 | #include "turbocharger.h"
18 | #include
19 | #include
20 |
21 |
22 | ///
23 | /// The handle to the SimConnect instance.
24 | ///
25 | HANDLE hSimConnect;
26 |
27 | double clamp(double v, double lo, double hi)
28 | {
29 | assert(!(hi < lo));
30 | return (v < lo) ? lo : (hi < v) ? hi : v;
31 | }
32 |
33 |
34 | #endif
--------------------------------------------------------------------------------
/src/TurboBonanza/turbocharger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef TURBOCHARGER
4 | #define TURBOCHARGER
5 |
6 | #include "common.h"
7 |
8 | class Turbocharger
9 | {
10 | public:
11 | static double getTargetFuelAirRatio(FLOAT64 ambientTemperature, FLOAT64 ambientPressure, double mixturePercentage) {
12 | double maxPowerManifoldPressure = 29.6;
13 | double criticalAltitude = 18000.0;
14 | double icaoStandardPressureAtCriticalAltitude = 14.95168;
15 | double maxTurboBoostPressure = maxPowerManifoldPressure - icaoStandardPressureAtCriticalAltitude;
16 | double fullRichSpecificVolumeConstant = 0.110236;
17 | double slope_90_100 = 0.000487531;
18 | double slope_75_90 = 0.000243697;
19 | double slope_60_75 = 0.000409167;
20 | double slope_20_60 = 0.000954534;
21 | double ambientPressurePa = ambientPressure * 3386.39;
22 | double maxManifoldPressurePa = maxPowerManifoldPressure * 3386.39;
23 | double ambientTemperatureK = ambientTemperature + 273.15;
24 | double maxManifoldDensity = 0.0;
25 | if (ambientPressure >= icaoStandardPressureAtCriticalAltitude) {
26 | maxManifoldDensity = maxManifoldPressurePa / (287.058 * pow(pow(ambientPressurePa, (1.0 - 1.4)) * pow(ambientTemperatureK, 1.4) / pow(maxManifoldPressurePa, (1.0 - 1.4)), (1.0 / 1.40)));
27 | }
28 | else {
29 | maxManifoldDensity = ((ambientPressure + maxTurboBoostPressure) * 3386.39) / (287.058 * pow(pow(ambientPressurePa, (1.0 - 1.4)) * pow(ambientTemperatureK, 1.4) / pow(((ambientPressure + maxTurboBoostPressure) * 3386.39), (1.0 - 1.4)), (1.0 / 1.40)));
30 | }
31 | double fullRichSpecificVolume = 1.0 / maxManifoldDensity;
32 | if (mixturePercentage == 100.0) {
33 | double targetFuelAirRatio = fullRichSpecificVolume * fullRichSpecificVolumeConstant;
34 | return targetFuelAirRatio;
35 | }
36 | else if (mixturePercentage >= 90.0) {
37 | double targetFuelAirRatio = fullRichSpecificVolume * (fullRichSpecificVolumeConstant + (mixturePercentage - 100.0) * slope_90_100);
38 | return targetFuelAirRatio;
39 | }
40 | else if (mixturePercentage >= 75.0) {
41 | double targetFuelAirRatio = fullRichSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (mixturePercentage - 90.0) * slope_75_90);
42 | return targetFuelAirRatio;
43 | }
44 | else if (mixturePercentage >= 60.0) {
45 | double targetFuelAirRatio = fullRichSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (mixturePercentage - 75.0) * slope_60_75);
46 | return targetFuelAirRatio;
47 | }
48 | else if (mixturePercentage >= 20.0) {
49 | double targetFuelAirRatio = fullRichSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (mixturePercentage - 60.0) * slope_20_60);
50 | return targetFuelAirRatio;
51 | }
52 | else {
53 | double slope_0_20 = (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60) / 20.0;
54 | double targetFuelAirRatio = fullRichSpecificVolume * (fullRichSpecificVolumeConstant + (90.0 - 100.0) * slope_90_100 + (75.0 - 90.0) * slope_75_90 + (60.0 - 75.0) * slope_60_75 + (20.0 - 60.0) * slope_20_60 + (mixturePercentage - 20.0) * slope_0_20);
55 | return targetFuelAirRatio;
56 | }
57 | }
58 | };
59 |
60 | #endif // !TURBOCHARGER
61 |
--------------------------------------------------------------------------------