Pl/Drzewo właściwości/Gniazda sieciowe
Użycie FG do odtwarzania prawdziwego lotu powinno być całkiem proste. Możesz podłączyć wiele instancji FG do jednej głównej instancji FG w czasie rzeczywistym. Podporządkowanie FG do nagranego strumienia danych powinno być również bardzo podobne.
Mam kilka instancji FG komunikujących się z innymi instancjami FG, jak również zewnętrzny model dynamiki lotów (FDM) komunikujący się z FG przy użyciu tej samej struktury. Mogą występować różnice w pakowaniu między kompilatorami, ale byliśmy dość ostrożni z obecną strukturą (wszystkie wartości mają 4 lub 8 bajtów) i nie znamy żadnych kompilatorów, które pakują strukturę inaczej.
Możesz nadpisać większość właściwości używając niestandardowego protokołu, jednak musisz upewnić się, że dane, które próbujesz nadpisać, nie są zapisywane przez żadne inne podsystemy lub części FlightGeara.
Może to być na przykład FDM, który wykorzystuje tak zwane „właściwości wiązane” – zasadniczo właściwości wiązane przez FDM nie są właściwością drzewa właściwości, ale są właściwością FDM, a drzewo właściwości zawiera jedynie odniesienie/wskaźniki do właściwości FDM.
W pewnym sensie musisz rozróżnić właściwości wejściowe i wyjściowe.
Najprostszym sposobem na uniknięcie takich problemów jest całkowite wyłączenie odpowiedniego podsystemu. W przypadku FDM można to zrobić opcją wiersza poleceń --fdm=null
. Dzięki temu nie jest powoływany do życia żaden FDM, który mógłby powodować konflikty.
Z drugiej strony istnieje wiele podsystemów, które nie mogą być obecnie bezpośrednio wyłączone, musiałyby one zostać załatane w kodzie źródłowym.
Ponadto, każda właściwość, która jest aktualizowana na sztywno, mogłaby prawdopodobnie konfliktować z twoimi pakietami UDP. Na przykład, niektóre właściwości są aktualizowane w każdej ramce, i podczas gdy nadpisywanie tych właściwości nie spowoduje błędu lub ostrzeżenia, twoje aktualizacje będą nieważne z powodu tych twardo zakodowanych aktualizacji w pętli głównej.
Następujące opcje wiersza poleceń utworzą gniazda sieciowe do przesyłania danych o powierzchniach sterowych samolotu: --native-ctrls=socket,in,10,127.0.0.1,5600,udp --native-ctrls=socket,out,10,127.0.0.1,5700,udp Prawdopodobnie będziesz potrzebować również danych z modelu dynamiki lotu: --native-fdm=socket,out,10,127.0.0.1,5500,udp
Rozpoczęcie
Jedno wielkie pytanie brzmi: z jakim językiem/platformą się łączysz?
Najłatwiej byłoby napisać drugą aplikację w C/C++ i po prostu użyć dokładnie tej samej struktury. Jeśli spojrzysz na wywołania gniazda, zobaczysz, że podajesz bufor i długość, a on wypełni go danymi sieciowymi.
Być może będziesz musiał wziąć pod uwagę kolejność bajtów (endianess), jeśli komunikujesz się między dwoma różnymi architekturami, ale w tym przypadku kod konwertuje sieciową kolejności bajtów, więc nie powinno to być problemem.
Jeśli chcesz znać rozmiar różnych struktur, takich jak bool
lub int
lub double
, istnieje funkcja w C/C++ o nazwie sizeof()
. Więc mógłbyś zrobić coś w stylu cout << sizeof(bool) << endl;
FlightGear vs. WideFS
FlightGear może już to wszystko zrobić co WideFS. Możesz ustawić jedną instancję FG jako główną i dowolną liczbę dodatkowych instancji jako podrzędną. Dla każdego kanału wyświetlania można określić kierunek przesunięcia widoku i pola widzenia. Możesz skonfigurować wszystko, od prostego 3-monitorowego systemu wyświetlania po ogromny, zawiły bałagan, jeśli chcesz połączyć 20 maszyn.
Sprzęt w pętli symulacyjnej
Naszym rozwiązaniem było użycie interfejsu FGNetCtrls dla FG i napisanie aplikacji bramy, która łączyłaby nasz komputer kontroli lotu dla UAV[1] (przez port szeregowy) z FG (przez TCP). To pozwoliło nam na testowanie systemu autopilota metodą hardware-in-the-loop. Jeśli wolisz, możesz również zaimplementować prawa kontroli lotu w aplikacji na PC i "latać" modelem FG poprzez połączenie TCP.
Właściwie FlightGear okazał się być uniwersalnym narzędziem dla naszego projektu. Sprawdza się również jako system wizualizacji (zarówno na żywo podczas lotu, jak i do odtwarzania po misji). Gdy już się to rozgryzie, komunikacja z FG jest dość prosta. Po prostu stworzyłem klasę w C# do obsługi wszystkich I/O dla FG i mogę ją wrzucić tam, gdzie jest potrzebna.
Wizualizacja UAV
Ten przypadek użycia jest wykorzystywany w autopilocie Paparazzi.
A jeśli używasz FlightGear jako systemu wizualizacji dla swojego UAV (na żywo lub odtwarzając dane) możesz włączyć tryb multiplayer i wstrzyknąć się tam. Ja właśnie teraz to robię. Jeśli chcesz zobaczyć UAV mojego uniwersytetu w akcji, wejdź na stronę mpmap01.flightgear.org i kliknij pole wyboru obok znaku wywoławczego Rascal-1. Zapętlam dane i prawdopodobnie będę je zapętlał przez resztę dnia. Nie mamy bezprzewodowego połączenia z Internetem, które jest dostępne na naszym lotnisku, ale kiedyś, jeśli będziemy w stanie podłączyć coś, będziemy mogli wstrzyknąć nasze dane o locie na żywo do systemu multiplayer, wtedy inni piloci będą mogą przyjść latać [wirtualnie] razem z nami. To byłaby fajna sztuczka.
Używanie zastrzeżonych/zewnętrznych FDM
Spójrz na net_fdm.* i native_ctrls.* w katalogu FlightGear/src/Network. To są pliki, których chcesz użyć.
Zajrzyj również do Docs/README.IO
Możesz wyłączyć wbudowany model dynamiki lotu opcją --fdm=null
. Następnie określ opcje --native-fdm=
i --native-ctrls=
z odpowiednimi parametrami. To ustawi FlightGear na otrzymywanie danych z zewnętrznego źródła. Następnie wszystko, co musisz zrobić, to przyjrzeć się strukturze pakietów UDP i wysłać pakiety danych z twojego zewnętrznego oprogramowania. Być może jest tu kilka trudniejszych szczegółów, jeśli nigdy wcześniej nie robiłeś komunikacji przez gniazdo sieciowe, ale było to robione wiele razy i jest wiele przykładów do obejrzenia, w tym sam kod FlightGeara. Możesz skonfigurować jedną "główną" kopię FlightGeara do kontrolowania jednej (lub więcej) innych kopii FlightGeara w ten sam sposób - jest to jeden ze sposobów na uzyskanie wieloekranowego wyświetlania kokpitu, jeśli nie chcesz podłączać wielu monitorów do jednego komputera.
Szukaj w net_fdm.hxx typów zmiennych i jednostek, jak radiany czy metry.
Celem jest również umieszczenie w tym podkatalogu kilku samodzielnych aplikacji FDM, które łączą się z FlightGear przez interfejs ExternalNet. Będą one służyć jako przykłady dla osób zainteresowanych integracją innych FDM w ten sposób.
Usunąłem także external FDM, ponieważ jest on zastąpiony przez lepiej nazwany null FDM. Opcja --fdm=external
jest nadal obsługiwana dla wstecznej kompatybilności i odpowiada --fdm=null
(co robi to samo).
Więc jedyną zmianą w wierszu poleceń byłoby przejście z
--native=socket,in,30,,5500,udp --fdm=external
na
--native=socket,in,30,,5500,udp --fdm=null
--fdm=external
wciąż działa, mimo że --fdm=null
jest teraz oficjalnym sposobem na wyłączenie wewnętrznego modelu dynamiki lotu.
Niektóre korzyści z uruchomienia modułu FDM jako samodzielnej aplikacji oddzielonej od FlightGear to:
- możesz ubić i zrestartować moduł FDM bez restartowania FG.
- możesz zmienić/skompilować/uruchomić moduł FDM bez ponownego uruchamiania FG.
- podczas rozwijania modułu FDM, nie musisz spędzać czasu na kompilacji/łączeniu z FG i uruchamianiu FG od nowa.
- możesz połączyć własnościowy kod dynamiki lotu poprzez ten mechanizm.
- możesz uruchomić FDM na osobnej maszynie/systemie operacyjnym, osobnym CPU, możesz nawet rozwijać go w innym języku programowania.
Prawie wszystkie, jeśli nie wszystkie wartości FGNetFDM są wypełniane przez FDM. Ale jest prawdopodobne, że nie wszystkie FDM-y będą generować całkowicie te same pola wyjściowe i nie wszystkie będą dbać o całkowicie te same wejścia.
Z konieczności struktura ta jest rozszerzana w miarę odkrywania nowych potrzeb. W strukturze zawarliśmy numer wersji, więc oprogramowanie po obu stronach może sprawdzić, że przekazuje tę samą wersję zawartości.
Dołączony kod źródłowy, który może wziąć instancję NetFDM i przetłumaczyć ją na właściwości FG-a, jak również kod, który może zbudować instancję NetFDM z aktualnego stanu drzewa właściwości, może opcjonalnie wykonać swoją pracę w kolejności bajtów sieci.
Kiedy struktura się zmieni, aktualizacja drugiej strony jest dosłownie trywialna. Skopiuj nową strukturą, sprawdź, czy zależy Ci na którymś z nowych pól (zwykle nie) i przekompiluj swoje oprogramowanie.
Jeśli chcesz najwyższej elastyczności, powinieneś użyć "generycznego" obiektu wyjściowego Erica, ponieważ tam możesz zdefiniować dokładnie to, co chcesz za pomocą pliku konfiguracyjnego XML, który zmienia się tylko wtedy, gdy chcesz, aby się zmienił.
FlightGear jako generator scenerii
FlightGear ma dokładnie to, czego szukasz.
Poszukaj w src/Network/net_fdm.hxx to definiuje strukturę, którą możesz przekazać do FG. Jeśli uruchomisz FG z opcją --fdm=null
wyłączysz wewnętrzną dynamikę lotu i dostaniesz wszystko co przekażesz w strukturze net_fdm.hxx.
Dodatkowo mamy strukturę net_ctrls.hxx, którą również możesz przekazać do FG. To pozwoli na animację powierzchni sterowyach (lotek, klap, podwozia itp.) w zewnętrznych widokach.
FlightGear był używany jako generator obrazu w symulatorze z certyfikatem FAA Level 3 FTD. Widziałem pytania ludzi, którzy również pracują nad wykorzystaniem FG jako generatora obrazu w taki czy inny sposób albo podłączając go do istniejącego symulatora, albo próbując zaimportować scenerię FG do istniejącego oprogramowania do generowania obrazu, albo próbując zaimportować istniejącą scenerię do FG.
Jest kilka rzeczy, o których należy pamiętać, a które napotkasz wkrótce.
- Musisz dopasować pas startowy lotniska i bazę danych pomocy nawigacyjnych pomiędzy FlightGear i PS1[2]. Jeśli tak się nie stanie, będziesz idealnie dopasowany do swojego podejścia w PS1 i możesz wyskoczyć z chmur, aby znaleźć się w poważnej niewspółosiowości z pasem startowym.
- Potrzebujesz PS1 i FG, aby uzgodnić wysokość gruntu. FG można skonfigurować tak, aby wysyłał na eksport wysokość terenu w świecie FG w bieżącym miejscu, ale musisz znaleźć sposób na zaimportowanie tego z powrotem do PS1. Jeśli uda Ci się to zrobić, będziesz mógł poprawnie kołować na wyboistych pasach startowych we FG, nie będziesz mógł latać pod ziemią, nie rozbijesz się o tajemniczy teren, który jest w PS1, ale nie we FG, itd. itp.
- Będziesz potrzebował przekazać parametry pogodowe do FG, tak aby wiatr wiał z właściwego kierunku, warstwy chmur były we właściwym miejscu, itd. itp. A jeśli używasz wielu wyświetlaczy, potrzebujesz je wszystkie skonfigurowane w ten sam sposób i zsynchronizować z czasem, tak aby wszystkie wyświetlały słońce/księżyc/gwiazdy w tym samym miejscu, miały to samo cieniowanie, kolorowanie i oświetlenie sceny.
- Na dobrym sprzęcie, FG może działać z prędkością 60+ fps. Jeśli pamiętam, PS1 aktualizuje się na przerwaniu DOS, które wynosi 18,2 Hz, jak sądzę. Jeśli nie zsynchronizujesz FG dokładnie z zegarem PS1 lub jakąś jego wielokrotnością, uzyskasz "szarpaną" animację. Nie jestem pewien, czy jest możliwe uruchomienie odświeżania monitora z dokładną wielokrotnością 18,2, więc być może będziesz musiał użyć jittera wideo, co prawdopodobnie nie przeszkadza, jeśli używasz MSFS jako punktu odniesienia :-)
Więc w większości jest to bardzo wykonalne i powinieneś być w stanie bardzo szybko coś uruchomić, zwłaszcza jeśli masz jakieś doświadczenie w gniazdach sieciowych, ale jest kilka rzeczy, które musisz wziąć pod uwagę i się nimi zająć aby wszystko naprawdę dobrze działało.
FGNetFDM & FGNetCtrl
To co zrobiliśmy ze strukturami FGNetFDM i FGNetCtrls to napisanie osobnych plików nagłówkowych poza zakresem projektu FlightGear i udostępnienie ich w domenie publicznej.
Nie ma problemu z włączeniem tych plików do programu na licencji GPL, takiego jak FlightGear, jak również nie ma problemu z włączeniem tych plików do innego oprogramowania własnościowego.
Nie należy tego postrzegać jako próby obejścia GPL. To tylko wygoda, aby ułatwić komunikację i uczynić FG użytecznym do większego zestawu zadań.
Dobrym pomysłem jest myślenie w kategoriach kanału "danych" i kanału "poleceń". Kanał "danych" byłby strukturą NetFDM, która byłaby wysyłana przez UDP w szybkim tempie (np. 60 Hz). Jeśli zgubisz pakiet, nie spędzaj czasu próbując ponownie wysłać stare dane, po prostu wyślij nowe dane i kontynuuj.
Kanał "poleceń" (props/telnet) jest kanałem o niższej przepustowości i wysokiej niezawodności. Masz gwarancję, że każda wiadomość dotrze do odbiorcy raz (i tylko raz). Jest to świetne rozwiązanie do ustawiania warunków pogodowych, pory dnia, parametrów kamery i wszystkiego innego, co może mieć wpływ na wizualizację. Mamy wygodny interfejs "telnet" do wszystkich wewnętrznych właściwości i wbudowanych poleceń. Wszystko, co można ustawić za pomocą klawiatury, myszy lub GUI, można zrobić za pomocą zdalnego programu lub skryptu. Jest to znacznie mniejsza przepustowość, ale bardzo wygodna.
Możesz użyć struktur net-ctrls i native-ctrl w katalogu Network, aby zmodyfikować właściwości, aby ustawić częstotliwości radiowe/nav, ale to wymaga trochę pracy, aby zhakować kod. Niektóre z nich mogą nie mieć zastosowania do ogólnego użytku. Podejściem byłoby dodanie zmiennych częstotliwości do pakietu sieciowego i zmodyfikowanie źródła, aby załadować wartości z odebranego pakietu do odpowiednich właściwości we FG. Następnie trzeba będzie stworzyć program sieciowy na kliencie "symulowanego stosu radiowego", aby zbudować pakiet, utworzyć połączenie za pomocą gniazda i nawiązać komunikację sieciową. Przynajmniej takie podejście zastosowałem w moim projekcie 747. Jeśli jesteś średnio biegły w C++, istniejący kod służy jako dobry przykład i baza do rozbudowy w celu spełnienia Twoich potrzeb.
Jeden duży problem ;-) Pakiet sieciowy wczytywany jest z dowolną szybkością wybraną przez opcje gniazda i nadpisuje wszystkie wartości właściwości wywołane w strukturze danych net-ctrls, w tym wszystkie elementy sterujące do latania. Jest to w porządku, jeśli planujesz również kontrolować FG-a za pomocą zewnętrznej maszyny. Przypuszczam, że mógłbyś "wyłączyć" aktualizację tych właściwości, których nie chcesz aktualizować w FGNetCtrls2Props, ale to niechlujny hak.
Zakładam, że nie potrzebujesz dużej przepustowości. Istnieją inne opcje, mniej wymagające i o ile nie planujesz rozbudowy sprzętu w przyszłości, mogą one spełnić twoje potrzeby.
Te dwa rodzime protokoły uzupełniają się wzajemnie. Pierwotnie zostały zaprojektowane do komunikacji z zewnętrznym FDM. FlightGear wysyłał native-ctrls do zdalnego modułu FDM, a ten odpowiadał danymi native-fdm.
Możesz zsynchronizować wizualizacje używając tylko --native-fdm=
, ponieważ zawiera on wszystkie informacje o pozycji i orientacji.
Jeśli chcesz również zsynchronizować wejścia sterujące (np. do animacji modelu zewnętrznego), możesz to dodać. Jest to bardzo przydatne, jeśli uruchamiasz Flyer braci Wright na systemie wizualnym złożonym z 5 projektorów. Jeśli chcesz zobaczyć, jak ten wielki ster wysokości macha przed tobą, gdy walczysz o pozostanie przy życiu jeszcze przez kilka sekund.
Instancje podrzędne FG powinny używać --fdm=null
, żeby nie próbowały obliczać swoich własnych informacji o pozycji.
To naprawdę zależy od tego, co próbujesz zrobić.
Myślę, że większość informacji, których potrzebujesz, znajdziesz w.
- src/Network/net_fdm.hxx
i (jeśli wysyłasz również ruchy sterujące)
- src/Network/net_ctrls.hxx
ale niektóre przetwarzanie (jak zamiana kolejności bajtów) najprawdopodobniej znajduje się w
- src/Network/native_fdm.{hxx,cxx}
i
- src/Network/native_ctrls.{hxx,cxx}
Domyślnie wszystko, co jest przesyłane, jest wykonywane w formacie sieciowym (big-endian). Wysyłanie danych w jakimkolwiek innym formacie to proszenie się o kłopoty (np. jak mogę wysłać dane z mojego PowerPC do mojego x86, jeśli nie są one w formacie sieciowym). Spójrz na source/src/Network/native_ctrls.cxx, aby zobaczyć, jak dane są przetwarzane. Wielkości to:
- Boolean = 8-bits
- Integer = 32-bits
- Float = 32-bits
- Double = 64-bits
Nie zapomnij o wyrównaniu danych. Kompilator C/C++ wypełni dane tak, aby były wyrównane do architektury procesora. Oznacza to, że tablica składająca się z trzech bajtów, po której następuje liczba całkowita, najprawdopodobniej będzie miała ukryty czwarty bajt po tablicy i przed liczbą całkowitą. Kompilator wyrówna dane na ich natywnej granicy (tj. Liczby 32-bitowe będą zaczynać się na granicy 32-bitowej).
Większość kompilatorów ma zestaw przełączników, których można użyć do sterowania dopełnieniem struktury i wyrównaniem. Ale rzeczywiście, w specyfikacji nie ma nic, co mówiłoby, jakie powinno być prawidłowe wyrównanie.
FWIW, każdy kompilator, który znam, przechowuje każdy element w kolejności i wyścieła go do jego "naturalnego" wyrównania. Tak więc ta struktura zajmuje 8 bajtów w pamięci na wszystkich platformach, które znam:
struct { int8_t a; int8_t b; int32_t c; }
Ale ten zajmuje co najmniej 12 (dopełnienie wielkości słowa jest dodawane na końcu w większości lub we wszystkich kompilatorach):
struct { int8_t a; int32_t b; int8_t c; }
Tak długo, jak struktura honoruje te zasady, powinieneś być mniej lub bardziej przenośny. Inne opcje to oczywiście przechowywanie wszystkiego jako int32_t
i samodzielne pakowanie bitów. Będziesz musiał to zrobić w każdym przypadku, jeśli chcesz uzyskać kompatybilność kolejności bajtów (endian). Jest to jeden z wielu powodów, dla których wyrzucanie struktur z pamięci na I/O jest uważane za problematyczne.
Ogólnie rzecz biorąc, nigdy nie wolno wysyłać binarnej struktury przez sieć. Nawet jeśli uzyskasz poprawne rozmiary, będziesz miał problemy z kolejnością bajtów podczas pracy na różnych architektur (PC vs Mac). Musisz ręcznie wysłać bajty danych w określonej kolejności i zrekonstruować je na drugim końcu. W tym momencie nie ma znaczenia, jakie jest dopełnienie dla różnych kompilatorów (lub kolejność), ponieważ nie będziesz wysyłać dopełenień - tylko dane.
Nigdy nie blokuj przekazywania struktury poprzez dostarczenie wskaźnika i rozmiaru, po prostu nie ma sposobu, aby to działało międzyplatformowo.
Jeśli zdecydujesz się na użycie interfejsu binarnego, polecam używanie XDR (RFC 1014) przez cały czas (już używanego przez system multiplayer).
Nie jest zbyt daleki od tego, co FG ma obecnie (przynajmniej dla bardziej popularnych platform), ale ma tę zaletę, że jest dobrze zdefiniowany.
<stdint.h> jest rzeczywiście standardowym nagłówkiem C, ale *nie* w C++ (chociaż może być włączony w przyszłości pod nazwą <cstdint>). O ile mi wiadomo, nawet jeśli <stdint.h> definiuje liczbę bitów dla poszczególnych typów, to *nie* implikuje niczego o tym, gdzie poszczególne bity mieszkają w pamięci. Inni podnieśli kwestie takie jak wyrównanie itp.
Pliki net_fdm.hxx i net_ctrls.hxx pochodzą z FlightGeara i definiują dwie struktury danych, które mogą być wysyłane i odbierane przez FlightGeara. W swoim programie możesz odebrać strukturę FGNetCtrls, zmienić parametry, które chcesz kontrolować i odesłać ją z powrotem.
Natywne gniazdo sieciowe
Zanim połączymy się z FlightGear na gnieździe, rozważ ten przykład używając natywnego protokołu. DO ZROBIENIA: wyjaśnij natywny protokół
--native=socket,kierunek,hz,host,port,typ kierunek = in, out lub bi hz = ilość razy na sekundę host = nazwa hosta lub adres IP w przypadku klienta (pozostaw puste w przypadku serwera) port = port, pozostawić puste aby umożliwić systemowi wybór typ = tcp lub udp np. --native=socket,out,10,,5444,udp ^ nie ma hosta więc wiąże się z localhost --native=socket,in,1,192.5.22.33 ,6789,tcp //**Ważne**, spacja = błąd ^
Przykład połączenia dwóch instancji FlightGeara na oddzielnych maszynach
fgfs1: --native=socket,out,30,seattle.com,5500,udp fgfs2: --native=socket,in,30,tolouse.net,5500,udp --fdm=external To nakazuje pierwszej kopii fgfs wysłanie pakietów UDP w natywnym formacie do maszyny o nazwie seattle.com na porcie 5500. Druga kopia fgfs zaakceptuje pakiety UDP (od kogokolwiek) na porcie 5500. Zwróć uwagę na dodatkową opcję --fdm=external. To mówi drugiej kopii fgfs, aby nie uruchamiała normalnego modelu lotu, ale zamiast tego ustawiała wartości FDM na podstawie zewnętrznego źródła, w tym przypadku sieci.