Jump to content

LUA Export NMEA AHRS & GPS over TCP/IP


zbmtwo

Recommended Posts

I'm interested in writing a LUA script to send NMEA data over TCP/IP or as UDP broadcast. I understand that the DCS coordinate system doesn't necessarily match the real world 100% of the time (it's a flat plane instead of a spheroid?), but I'm still interested in trying as at least AHRS data would be accurate.

 

I would like to receive the data with a Glass Cockpit app on my iOS devices. In particular, I'm interested in Air Navigation Pro as it already has FSX and X Plane integration. It should be a matter of taking ownship information from DCS, converting it into the relevant formats for the appropriate NMEA sentences, then open a socket and send the data as NMEA sentences to the client. I've emailed the company that makes Air Navigation Pro asking for more info about the protocol they use, but in the meantime I want to get started writing the export script (I'll post their response when I receive it).

 

I understand that I would have to do something like this from the example in the default export.lua:

 

function LuaExportAfterNextFrame()

-- get the variables
local t = LoGetModelTime()
local name = LoGetPilotName()
local altBar = LoGetAltitudeAboveSeaLevel()
local altRad = LoGetAltitudeAboveGroundLevel()
local pitch, bank, yaw = LoGetADIPitchBankYaw()
local engine = LoGetEngineInfo()
local HSI    = LoGetControlPanel_HSI()

-- format the data and send it over the network
socket.try(c:send(string.format("t = %.2f, name = %s, altBar = %.2f, alrRad = %.2f, pitch = %.2f, bank = %.2f, yaw = %.2f\n", t, name, altRad, altBar, pitch, bank, yaw)))

end

 

Outside of having a basic understanding of what is required, I'm stuck as to execution.

 

I want to send the following NMEA sentences to start with:

 

$PAHR - AHRS info; pitch, roll, heading

$GPGLL - Geographic position; latitude / longitude

 

So I'd need to convert:

  • angles from Radians to Degrees in the format of DDD.DD (radian * 57.296; +ve values for roll to port/pitch up, -ve values for roll to starboard/pitch down)
  • LAT/LON to the format DDMM.MM,N/S / DDDMM.MM,E/W
  • time to the format HHMMSS.SS
  • speed from m/s to knots in the format KKK.KK (speed * 1.943844)
  • height from meters to feet (alt * 3.048, as an integer)

 

Is someone able to point me in the direction of some resources or otherwise provide me an example of how to format NMEA strings and send them via TCP/IP using LUA? I don't want to reinvent the wheel if someone has already made something for this purpose.

 

I had a look at the TacView export script, a script for exporting UH-1 telemetry to Android, and at LotATC but the info in this post is as far as I've got on my own for now. I'm going to have a go at writing a LUA script based on that UH-1 script (I see that it uses the ID of specific instruments, but I'm thinking I can get the values I need using LoGet functions?)

 

Thanks for your help!

 

 

Here's some greater detail on what I want to accomplish...

 

 

Background

  • It is possible to export ownship data from DCS via LUA
  • There are a number of Glass Cockpit apps for iOS and Android that accept NMEA data over TCP/IP

 

Goal

Create code for export.lua that extracts relevant ownship data, formats it as NMEA sentences, and sends it over TCP/IP to a Glass Cockpit app on a mobile device

 

Requirements

  • Exctract ownship data from DCS
  • Format ownship data as NMEA sentences for AHRS and GPS
  • Transmit data over TCP/IP to a mobile device running client software
  • Alternatively/additionally: transmit data via UDP

 

Resources

 

1. AHRS Devices

 

Levil have AHRS devices that sends data to iOS and Android devices via TCP/IP over WLAN:

 

Wireless Transmission

 

Some mobile devices that do not have a USB or serial port, require wireless communication to

the AHRS (i.e iPad/iPhones). The iLevil and AHRS-G mini series have an embedded wireless

transmitter that works as the router at home and creates a WiFi access point to which your tablet will link to. Once connected, navigation programs can then telnet to the transmitter using either

TCP or UDP transmission:

 

- TCP/IP connection is very robust and requires the remote device to confirm receipt of data

every time the AHRS sends a package. Although TCP connection guarantees no data is lost on

the way, it may cause some latency on the transmission if either the sender or the receiver is not

properly responding in a timely manner. For example, iPad devices have shown latency when

you move the device around because it is trying to figure out the screen rotation. You can also

expect some latency if you are inside a building with multiple Wi-Fi access points or if you are

accidentally blocking the iPad transmitter with your hand. The best way to test the AHRS

transmission is to fly with it. TCP can only be used by one application at a time.

 

- UDP transmission is not as robust as TCP, but allows multiple devices to access the same data

simultaneously. If you’d like to use two iPads, for example, you might want to use the device in

UDP mode. UDP is the default mode and is recommended for in-flight use.

 

InertialLabs make and AHRS that does the same, and we can see via their AHRS documentation (pp. 21-22) that NMEA has a proprietry sentence for AHRS data ($PAHR):

 

At the “NMEA Output” the AHRS output data are transmitted in the form of sentences with printable ASCII characters like the NMEA 0183 format. Each sentence starts with a "$" sign and ends with <CR><LF> (carriage return 0xD and line feed 0xA symbols). All data fields are separated by commas.

 

The general form of the “NMEA Output” sentence is the text

PAHR,RRRR.rr,PPP.pp,HHH.hh,TTT.t,V.vv,SSSS*CC<CR><LF>

where PAHR is identifier and other fields are listed in the Table 6.7.

 

Тable 6.7.The AHRS message in NMEA format

(at NMEAcont or NMEAreq command)

Field RRRR.rr PPP.pp HHH.hh TTT.t V.vv SSSS CC

Parameter Roll Pitch Heading Temperature Vdd USW Checksum

Note deg deg deg ºC VDC Hex*

 

*Hex written in ASCII; AHRS state information (Ready/Sleeping, Errors, BIT, etc)

 

Notes:

1. USW is unit status word (see section 6.5 pp. 43-44 for details).

2. Temperature is averaged value for 3 accelerometers.

3. Vdd is input voltage of the AHRS.

4. Check sum consists of a "*" and two hex digits representing XOR of all characters

between, but not including "$" and "*".

Important note: The AHRS maximum data rate is limited to 50 Hz in the NMEA output

format

 

2. NMEA Sentences

 

Although the NMEA standard costs $$$ to obtain, there are a number of resources that explain some of the sentences

 

For example: http://aprs.gids.nl/nmea/

 

   $GPBOD - Bearing, origin to destination
  $GPBWC - Bearing and distance to waypoint, great circle
  $GPGGA - Global Positioning System Fix Data
  $GPGLL - Geographic position, latitude / longitude
  $GPGSA - GPS DOP and active satellites 
  $GPGSV - GPS Satellites in view
  $GPHDT - Heading, True
  $GPR00 - List of waypoints in currently active route
  $GPRMA - Recommended minimum specific Loran-C data
  $GPRMB - Recommended minimum navigation info
  $GPRMC - Recommended minimum specific GPS/Transit data
  $GPRTE - Routes
  $GPTRF - Transit Fix Data
  $GPSTN - Multiple Data ID
  $GPVBW - Dual Ground / Water Speed
  $GPVTG - Track made good and ground speed
  $GPWPL - Waypoint location
  $GPXTE - Cross-track error, Measured
  $GPZDA - Date & Time

 

3. Client Applications

 

Air Navigation Pro

 

Air Navigation is a flight planning and real-time aircraft navigation application with 2D moving map and 3D synthetic vision for iPhone, iPad and Android devices. A Desktop version for Mac OS X is also available. Air Navigation Apps support free charts of almost the entire world as well as commercial aviation VFR charts for many countries. The application costs a fraction of the price of a dedicated portable aviation GPS and will help you to plan your flight, saving you time and money.

 

Air Navigation can be connected to the X-Plane or FSX flight simulators by installing a plugin on your favorite simulator. This is a great feature for training at home when bad weather prevents real flying! Also, simulator enthusiasts can add a full moving map system to their Sim setup. Currently we have an X-Plane 9/10 plugin for Windows and Mac OS X and and FSX plugin for Windows.

 

List of Other Client Apps: http://aviation.levil.com/compatible-apps.html

 

4. DCS export.lua Variables

 

Get the relevant paramaters:

LoGetAltitudeAboveSeaLevel()
LoGetADIPitchBankYaw()
LoGetVerticalVelocity()
LoGetTrueAirSpeed()
MyPlane.LatLongAlt.Lat
MyPlane.LatLongAlt.Long

Format them for NMEA:

local t = LoGetModelTime()
local altBar = LoGetAltitudeAboveSeaLevel()
local pitch, bank, yaw = LoGetADIPitchBankYaw()
local vertvel = LoGetVerticalVelocity()
local trueairspeed = LoGetTrueAirSpeed()
local LatPos = MyPlane.LatLongAlt.Lat
local LongPos = MyPlane.LatLongAlt.Long
local av = LoGetAngularVelocity()

Send as NMEA sentences:

$PAHR - AHRS info
$GPGLL - Geographic position, latitude / longitude

 

I have also found this script that is used to export UH-1 telemetry to an Android app. It looks like I could use this code as the bones of an NMEA export script over UDP:

 

---------------------------------------------------------------------------------------------------
-- Export UH-1H instruments
---------------------------------------------------------------------------------------------------

f_uh1h =
{
Start=function(self) 
package.path = package.path..";.\\LuaSocket\\?.lua"
package.cpath = package.cpath..";.\\LuaSocket\\?.dll"
socket = require("socket")

my_init = socket.protect(function()	
	-- export telemetry to instrumeny panel on android
	host2, port2 = "10.0.0.3", 6000 -- replace IP with android device ip 
	udp = socket.try(socket.udp())	
end)
my_init()	
end,

AfterNextFrame=function(self)
   --local player = LoGetObjectById(LoGetPlayerPlaneId())
--print(player.Name)
--if (player and player.Name == "UH-1H") then
	-- read from UH-1H main panel instruments
	local MainPanel = GetDevice(0)
	local AirspeedNeedle = MainPanel:get_argument_value(117)*150
	local Altimeter_10000_footPtr = MainPanel:get_argument_value(178)*100000
	local Altimeter_1000_footPtr = MainPanel:get_argument_value(179)*10000
	local Altimeter_100_footPtr = MainPanel:get_argument_value(180)*1000
	local Variometer = MainPanel:get_argument_value(134)*4000
	local TurnNeedle = MainPanel:get_argument_value(132)*math.rad(1.5)
	local Slipball = MainPanel:get_argument_value(133)
	local CoursePointer1 = MainPanel:get_argument_value(159) * 2.0 * math.pi
	local CoursePointer2 = MainPanel:get_argument_value(160) * 2.0 * math.pi
	local CompassHeading = MainPanel:get_argument_value(165) * 2.0 * math.pi
	local Torque = MainPanel:get_argument_value(124) * 103 + -3
	local Engine_RPM = MainPanel:get_argument_value(122)*7200
	local AHorizon_Pitch = MainPanel:get_argument_value(143) * math.pi / 2.0
	local AHorizon_Bank = MainPanel:get_argument_value(142) * math.pi
	local AHorizon_PitchShift = 0
	local GyroHeading = MainPanel:get_argument_value(165) * 2.0 * math.pi
	local Oil_Temperature = MainPanel:get_argument_value(114) * 150
	local Oil_Pressure = MainPanel:get_argument_value(113) * 100
	local Fuel_Pressure = MainPanel:get_argument_value(126) * 50
	local Fuel_Tank = MainPanel:get_argument_value(239) * 1580.0 / 100.0
	local VerticalBar = MainPanel:get_argument_value(151) * -2.0
	local HorisontalBar = MainPanel:get_argument_value(152) * -2.0
	local ToMarker = MainPanel:get_argument_value(153)
	local FromMarker = MainPanel:get_argument_value(154)		
	local RotCourseCard = MainPanel:get_argument_value(156) * 2.0 * math.pi
	
	my_send = socket.protect(function()
		local json = string.format("{ 'AirspeedNeedle':%.2f, 'Altimeter_10000_footPtr':%.2f, 'Altimeter_1000_footPtr':%.2f, 'Altimeter_100_footPtr':%.2f, 'Variometer':%.2f, 'TurnNeedle':%.2f, 'Slipball':%.2f, 'CoursePointer1':%.2f, 'CoursePointer2':%.2f, 'CompassHeading':%.2f, 'Torque':%.2f, 'Engine_RPM':%.2f, 'AHorizon_Pitch':%.2f, 'AHorizon_Bank':%.2f, 'GyroHeading':%.2f, 'Oil_Temperature':%.2f, 'Oil_Pressure':%.2f, 'Fuel_Pressure':%.2f, 'Fuel_Tank':%.2f,'VerticalBar':%.2f,'HorisontalBar':%.2f,'ToMarker':%.2f,'FromMarker':%.2f,'RotCourseCard':%.2f}\n", 
			AirspeedNeedle, 
			Altimeter_10000_footPtr, 
			Altimeter_1000_footPtr, 
			Altimeter_100_footPtr, 
			Variometer, 
			TurnNeedle, 
			Slipball, 
			CoursePointer1, 
			CoursePointer2, 
			CompassHeading, 
			Torque, 
			Engine_RPM, 
			AHorizon_Pitch, 
			AHorizon_Bank, 
			GyroHeading, 
			Oil_Temperature, 
			Oil_Pressure, 
			Fuel_Pressure, 
			Fuel_Tank,
			VerticalBar,
			HorisontalBar,
			ToMarker,
			FromMarker,
			RotCourseCard
		)
		--print(json)	
		socket.try(udp:sendto(json, host2, port2))
	end) -- my_send
	my_send()
--end -- if UH-1H	
end,


Stop=function(self)
my_close = socket.protect(function()
	socket.try(udp:close())
end)
my_close()
end
}

-- =============
-- Overload
-- =============
do
local PrevLuaExportStart=LuaExportStart
LuaExportStart=function()
	f_uh1h:Start()
	if PrevLuaExportStart then
		PrevLuaExportStart()
	end
end
end

do
local PrevLuaExportAfterNextFrame=LuaExportAfterNextFrame
LuaExportAfterNextFrame=function()
	f_uh1h:AfterNextFrame()
	if PrevLuaExportAfterNextFrame then
		PrevLuaExportAfterNextFrame()
	end
end
end

do
local PrevLuaExportStop=LuaExportStop
LuaExportStop=function()
	f_uh1h:Stop()
	if PrevLuaExportStop then
		PrevLuaExportStop()
	end
end
end

 

 

Link to comment
Share on other sites

Link to comment
Share on other sites

@zbmtwo

I've done what you are trying to do, with the same "template", the TacView export script. But I used the GPGGA and GPRMC sentence.

But I'm using true airspeed instead of groundspeed so far, haven't bother to do the calculations yet.

You will be off a few 100m depending on where you are flying.

I'm not a regular programmer my self so my code is ugly. I might share the code if you like, but no support guarantee.

 

@BR=55=Sevas

What is that for and application you have there? Homemade?

 

 

Edit:

Sorry, I didn't use the TacView export script, but the exporting UH-1 telemetry to Android one.


Edited by Holton181

Helicopters and Viggen

DCS 1.5.7 and OpenBeta

Win7 Pro 64bit

i7-3820 3.60GHz

P9X79 Pro

32GB

GTX 670 2GB

VG278H + a Dell

PFT Lynx

TrackIR 5

Link to comment
Share on other sites

  • 2 years later...
HEre is my app with MFD and GPS map

https://forums.eagle.ru/showthread.php?t=80496

 

 

Better late than never :smilewink:

 

 

Seriously, thanks :thumbup:, always interesting to see how others solve problems. But both OP an I want to send NMEA sentences to whatever application/device that can handle them. You sends the coordinates straight to a self made app. And you also get the same location error as me, since we both uses the in game coordinates without any adjustments for the real world. All is working good for me except I cant get the ground speed and track direction (not heading or LoGetMagneticYaw you are using).

Helicopters and Viggen

DCS 1.5.7 and OpenBeta

Win7 Pro 64bit

i7-3820 3.60GHz

P9X79 Pro

32GB

GTX 670 2GB

VG278H + a Dell

PFT Lynx

TrackIR 5

Link to comment
Share on other sites

You can send NMEA sentence in your app, all you need is correct string format according to NMEA standart

$GPRMC...and so on

 

The reason for localtion error on map is DCS itself. DCS uses flat earth projection, but gps map uses spherical.

 

To get ground speed and track direction use Speed vector

 

LoGetVectorVelocity = {x,y,z} -- vector of self velocity (world axis)

 

Just calculate derivative between frames and you`ll get real world speed, using arctan you can calculate true direction.


Edited by BR=55=Sevas
Link to comment
Share on other sites

You can send NMEA sentence in your app, all you need is correct string format according to NMEA standart

$GPRMC...and so on

 

The reason for localtion error on map is DCS itself. DCS uses flat earth projection, but gps map uses spherical.

 

To get ground speed and track direction use Speed vector

 

LoGetVectorVelocity = {x,y,z} -- vector of self velocity (world axis)

 

Just calculate derivative between frames and you`ll get real world speed, using arctan you can calculate true direction.

 

Thanks, I do use RMC and GGA sentences and I know about the flat earth in DCS. But I didn't think of using LoGetVectorVelocity, will try it out. It's in meters right? I figure it out.

Thanks again!

Helicopters and Viggen

DCS 1.5.7 and OpenBeta

Win7 Pro 64bit

i7-3820 3.60GHz

P9X79 Pro

32GB

GTX 670 2GB

VG278H + a Dell

PFT Lynx

TrackIR 5

Link to comment
Share on other sites

Also z axis directed up, but you know about I think.

Wasn't sure though, though I read something a while ago they had some strange configuration with Y as up, but most likely my memory fail on me. Thanks again.

Helicopters and Viggen

DCS 1.5.7 and OpenBeta

Win7 Pro 64bit

i7-3820 3.60GHz

P9X79 Pro

32GB

GTX 670 2GB

VG278H + a Dell

PFT Lynx

TrackIR 5

Link to comment
Share on other sites

To get ground speed and track direction use Speed vector

LoGetVectorVelocity = {x,y,z} -- vector of self velocity (world axis)

 

Just calculate derivative between frames and you`ll get real world speed, using arctan you can calculate true direction.

This works like a charm! :thumbup:

But no need to calculate derivative between frames, it's a velocity vector so all information needed is there for each point. Just to use some Pythagoras and arctan to get the resultant lateral vector.

 

Just gonna clean up the code and then I might share it. One can connect with either UDP or Bluetooth virtual serial port. I have a small annoying issue with the Bluetooth port though. When there is no one listening on it DCS freeze until something start to. Would like to find a way to handle that situation.

 

Thanks again!


Edited by Holton181

Helicopters and Viggen

DCS 1.5.7 and OpenBeta

Win7 Pro 64bit

i7-3820 3.60GHz

P9X79 Pro

32GB

GTX 670 2GB

VG278H + a Dell

PFT Lynx

TrackIR 5

Link to comment
Share on other sites

Thats weird.

As I remember DCS freeze only if you try to send data from your app to DCS, and DCS waits data, if there are no data its freeze.

Functions like socket.receive or similar.

 

Try this advise, maybe it fix freeze

А почему нельзя к этому:

c:setoption("tcp-nodelay",true) -- set immediate transmission mode

добавить это:

c:settimeout(.001) -- set the timeout for reading the socket

Link to comment
Share on other sites

It's only when using the Bluetooth virtual port, not UDP. It freeze the second I hit "Fly" on the briefing window at mission start, and it unfreeze the second any application start listening to the port. If I stop listening the app DCS freeze again, start and it unfreeze.

These are the three blocks used for it:

at start

--Bluetooth
---[[
sPort = "COM3"
com = io.open(sPort,"w+b")
--]]

sending

--Bluetooth
---[[
com:write(string.format("$%s*%x\r\n$%s*%x\r\n", GGA,csGGA,RMC,csRMC)) -- fills the serial buffer
com:flush() -- send the serial buffer
--]]

stop

--Bluetooth
---[[
com:close(sPort)
--]]

COM3 is the Bluetooth VP configured as "incoming" in Windows 7 Pro.

 

I basically know nothing about these things but have used examples around the web to get it working. I assume I should do some sort of test on the port before sending. Searched the web how to but could not find anything. Checking the io.open in the beginning for errors should not help, since that is only done at start and the issue is strictly related to if anything is listening on the port: If it is, all good. If not, freeze.

 

 

Also, it can be a Bluetooth issue, and since DCS and the script isn't really aware of that, only seeing the serial port, it might not be possible to do anything about it?


Edited by Holton181

Helicopters and Viggen

DCS 1.5.7 and OpenBeta

Win7 Pro 64bit

i7-3820 3.60GHz

P9X79 Pro

32GB

GTX 670 2GB

VG278H + a Dell

PFT Lynx

TrackIR 5

Link to comment
Share on other sites

  • 3 weeks later...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...