### Parse data packet
```erlang
-{ok, {Index, Groups}} = x_plane_data_raw:of_bin(XPlaneDataPacket),
+{ok, {64=Index, GroupsRaw}=DataRaw} = x_plane_data_raw:of_bin(XPlaneDataPacket),
```
### Access parsed data
+#### Raw
+
+At this stage, only the structure of the packet was parsed. No attempt at
+interpreting the values have been made:
+
```erlang
% Speeds are in group 3
-{3, Speeds} = lists:keyfind(3, 1, Groups),
+{3, Speeds} = lists:keyfind(3, 1, GroupsRaw),
{ VindKias
, VindKeas
, VtrueKtas
} = Speeds,
% Pitch roll and headings values are in group 17
-{17, PitchRollHeadings} = lists:keyfind(17, 1, Groups),
+{17, PitchRollHeadings} = lists:keyfind(17, 1, GroupsRaw),
{ PitchDeg
, RollDeg
, HdingTrue
} = PitchRollHeadings,
```
+#### Named
+
+Here we identify what each of the numbered groups mean in a given X-Plane
+version. Right now only X-Plane 10 is supported and I only identified 3 groups
+so far:
+
+- index: `3` , name: `speeds`
+- index: `17`, name: `pitch_roll_heading`
+- index: `20`, name: `lat_lon_alt`
+
+Unidentified groups (with index other than what is listed above) will be
+absent from the list of named groups (think of `x_plane_data_named:of_raw/1` as
+a filter), so you'll have to access their raw version, if needed.
+
+##### Identify
+```erlang
+{ok, {x_plane_data_v10, GroupsNamed}} = x_plane_data_named:of_raw(DataRaw),
+```
+
+##### Access
+```erlang
+-include_lib("x_plane_data_group_lat_lon_alt.hrl").
+-include_lib("x_plane_data_group_pitch_roll_heading.hrl").
+-include_lib("x_plane_data_group_speeds.hrl").
+
+...
+
+{speeds, #x_plane_data_group_speeds
+ { vind_kias = VindKias
+ , vind_keas = VindKeas
+ , vtrue_ktas = VtrueKtas
+ , vtrue_ktgs = VtrueKtgs
+ , vind_mph = VindMph
+ , vtrue_mphas = VtrueMphas
+ , vtrue_mphgs = VtrueMphgs
+ }
+} = lists:keyfind(speeds, 1, GroupsNamed),
+
+{pitch_roll_heading, #x_plane_data_group_pitch_roll_heading
+ { pitch_deg = PitchDeg
+ , roll_deg = RollDeg
+ , hding_true = HdingTrue
+ , hding_mag = HdingMag
+ }
+} = lists:keyfind(pitch_roll_heading, 1, GroupsNamed),
+```
+
Data format references
----------------------
--- /dev/null
+-record(x_plane_data_group_lat_lon_alt,
+ { lat_deg :: float() % 1
+ , lon_deg :: float() % 2
+ , alt_ftmsl :: float() % 3
+ , alt_ftagl :: float() % 4
+ , on_runwy :: float() % 5
+ , alt_ind :: float() % 6
+ , lat_south :: float() % 7
+ , lon_west :: float() % 8
+ }).
--- /dev/null
+-record(x_plane_data_group_pitch_roll_heading,
+ { pitch_deg :: float() % 1
+ , roll_deg :: float() % 2
+ , hding_true :: float() % 3
+ , hding_mag :: float() % 4
+ % 5
+ % 6
+ % 7
+ % 8
+ }).
--- /dev/null
+-record(x_plane_data_group_speeds,
+ { vind_kias :: float() % 1
+ , vind_keas :: float() % 2
+ , vtrue_ktas :: float() % 3
+ , vtrue_ktgs :: float() % 4
+ % 5
+ , vind_mph :: float() % 6
+ , vtrue_mphas :: float() % 7
+ , vtrue_mphgs :: float() % 8
+ }).
%%% vim: set filetype=erlang:
{ deps
-, [
+, [ {hope, ".*", {git, "https://github.com/ibnfirnas/hope.git", {tag, "3.8.1"}}}
]
}.
{application, x_plane_data,
[
{description, "X-Plane UDP data packet parser."},
- {vsn, "0.1.0"},
+ {vsn, "0.2.0"},
{registered, []},
{applications, [
kernel,
--- /dev/null
+-module(x_plane_data_group_lat_lon_alt).
+
+-include("include/x_plane_data_group_lat_lon_alt.hrl").
+
+-export_type(
+ [ t/0
+ ]).
+
+-export(
+ [ of_raw_values_v10/1
+ ]).
+
+-define(T, #?MODULE).
+
+-type t() ::
+ ?T{}.
+
+-spec of_raw_values_v10(x_plane_data_raw:group_values()) ->
+ t().
+of_raw_values_v10({V1, V2, V3, V4, V5, V6, V7, V8}) ->
+ ?T
+ { lat_deg = V1
+ , lon_deg = V2
+ , alt_ftmsl = V3
+ , alt_ftagl = V4
+ , on_runwy = V5
+ , alt_ind = V6
+ , lat_south = V7
+ , lon_west = V8
+ }.
--- /dev/null
+-module(x_plane_data_group_pitch_roll_heading).
+
+-include("include/x_plane_data_group_pitch_roll_heading.hrl").
+
+-export_type(
+ [ t/0
+ ]).
+
+-export(
+ [ of_raw_values_v10/1
+ ]).
+
+-define(T, #?MODULE).
+
+-type t() ::
+ ?T{}.
+
+-spec of_raw_values_v10(x_plane_data_raw:group_values()) ->
+ t().
+of_raw_values_v10({V1, V2, V3, V4, _, _, _, _}) ->
+ ?T
+ { pitch_deg = V1
+ , roll_deg = V2
+ , hding_true = V3
+ , hding_mag = V4
+ }.
--- /dev/null
+-module(x_plane_data_group_speeds).
+
+-include("include/x_plane_data_group_speeds.hrl").
+
+-export_type(
+ [ t/0
+ ]).
+
+-export(
+ [ of_raw_values_v10/1
+ ]).
+
+-define(T, #?MODULE).
+
+-type t() ::
+ ?T{}.
+
+-spec of_raw_values_v10(x_plane_data_raw:group_values()) ->
+ t().
+of_raw_values_v10({V1, V2, V3, V4, _, V6, V7, V8}) ->
+ ?T
+ { vind_kias = V1
+ , vind_keas = V2
+ , vtrue_ktas = V3
+ , vtrue_ktgs = V4
+
+ , vind_mph = V6
+ , vtrue_mphas = V7
+ , vtrue_mphgs = V8
+ }.
--- /dev/null
+-module(x_plane_data_named).
+
+-export_type(
+ [ t/0
+ , version/0
+ , group/0
+ ]).
+
+-export(
+ [ of_raw/1
+ ]).
+
+-type version() ::
+ x_plane_data_v10.
+
+-type group() ::
+ {speeds , x_plane_data_group_speeds:t()}
+ | {pitch_roll_heading, x_plane_data_group_pitch_roll_heading:t()}
+ | {lat_lon_alt , x_plane_data_group_lat_lon_alt:t()}
+ .
+
+-type t() ::
+ {version(), [group()]}.
+
+-define(DATA_INDEX_V10, 64).
+
+-spec of_raw(x_plane_data_raw:t()) ->
+ hope_result:t(t(), unknown_x_plane_version).
+of_raw({?DATA_INDEX_V10, GroupsRaw}) ->
+ ConsKnownDropUnknown =
+ fun (GroupRaw, Groups1) ->
+ GroupOpt = v10_group_identify(GroupRaw),
+ Groups2Opt = hope_option:map(GroupOpt, fun (G) -> [G | Groups1] end),
+ hope_option:get(Groups2Opt, Groups1)
+ end,
+ GroupsNamed = lists:foldl(ConsKnownDropUnknown, [], GroupsRaw),
+ T = {x_plane_data_v10, GroupsNamed},
+ {ok, T};
+of_raw({_, _}) ->
+ {error, unknown_x_plane_version}.
+
+-spec v10_group_identify(x_plane_data_raw:group()) ->
+ hope_option:t(group()).
+v10_group_identify({Index, Values}) ->
+ LabAndConsOpt = v10_index_to_label_and_constructor(Index),
+ hope_option:map(LabAndConsOpt, fun ({L, C}) -> {L, C(Values)} end).
+
+v10_index_to_label_and_constructor(Index) ->
+ F = of_raw_values_v10,
+ case Index
+ of 3 -> {some, {speeds , fun x_plane_data_group_speeds:F/1}}
+ ; 17 -> {some, {pitch_roll_heading, fun x_plane_data_group_pitch_roll_heading:F/1}}
+ ; 20 -> {some, {lat_lon_alt , fun x_plane_data_group_lat_lon_alt:F/1}}
+ ; _ -> none
+ end.
-module(x_plane_data_SUITE).
+-include_lib("x_plane_data_group_lat_lon_alt.hrl").
+-include_lib("x_plane_data_group_pitch_roll_heading.hrl").
+-include_lib("x_plane_data_group_speeds.hrl").
+
%% CT callbacks
-export(
[ all/0
%% Test cases
-export(
- [ t_basic_sanity_check/1
+ [ t_bin_to_raw/1
+ , t_bin_to_raw_to_named/1
]).
-define(GROUP, x_plane_data).
groups() ->
Tests =
- [ t_basic_sanity_check
+ [ t_bin_to_raw
+ , t_bin_to_raw_to_named
],
Properties = [parallel],
[ {?GROUP, Properties, Tests}
%% Test cases
%% =============================================================================
-t_basic_sanity_check(_Cfg) ->
+t_bin_to_raw(_Cfg) ->
Test =
fun (PacketBase64) ->
Packet = base64:decode(PacketBase64),
end,
lists:foreach(Test, sample_packets_base64_encoded()).
+t_bin_to_raw_to_named(_Cfg) ->
+ Test =
+ fun (PacketBase64) ->
+ Packet = base64:decode(PacketBase64),
+ {ok, DataRaw} = x_plane_data_raw:of_bin(Packet),
+ ct:log("DataRaw: ~p", [DataRaw]),
+ {64, _} = DataRaw,
+ {ok, DataNamed} = x_plane_data_named:of_raw(DataRaw),
+ ct:log("DataNamed: ~p", [DataNamed]),
+ {x_plane_data_v10, Groups} = DataNamed,
+ {some, #x_plane_data_group_speeds
+ { vind_kias = 3.106105089187622
+ , vind_keas = 6.640225887298584
+ , vtrue_ktas = 6.793502330780029
+ , vtrue_ktgs = 1.0040892448159866e-5
+ , vind_mph = 3.574441909790039
+ , vtrue_mphas = 7.81782341003418
+ , vtrue_mphgs = 1.1554855518625118e-5
+ }
+ } = kv_list_find(Groups, speeds),
+ {some, #x_plane_data_group_pitch_roll_heading
+ { pitch_deg = 2.3310465812683105
+ , roll_deg = 0.22457626461982727
+ , hding_true = 120.6203384399414
+ , hding_mag = 133.51084899902344
+ }
+ } = kv_list_find(Groups, pitch_roll_heading),
+ {some, #x_plane_data_group_lat_lon_alt
+ { lat_deg = 40.64827346801758
+ , lon_deg = -73.81651306152344
+ , alt_ftmsl = 7.969515800476074
+ , alt_ftagl = 0.226793110370636
+ , on_runwy = 1.0
+ , alt_ind = -70.99662780761719
+ , lat_south = 40.0
+ , lon_west = -75.0
+ }
+ } = kv_list_find(Groups, lat_lon_alt),
+ ok
+ end,
+ lists:foreach(Test, sample_packets_base64_encoded()).
+
%% =============================================================================
%% Sample data
%% =============================================================================