Skip to content

Commit

Permalink
Merge pull request #128 from ffac/update-location-gps
Browse files Browse the repository at this point in the history
ffac-update-location-gps: initial package version
  • Loading branch information
maurerle authored Aug 29, 2024
2 parents 6c8a76f + 03b0bba commit 102e43e
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 0 deletions.
22 changes: 22 additions & 0 deletions ffac-update-location-gps/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2024 Florian Maurer, xelo
# SPDX-License-Identifier: MIT
include $(TOPDIR)/rules.mk

PKG_NAME:=ffac-update-location-gps
PKG_VERSION:=1.0
PKG_RELEASE:=1

PKG_LICENSE:=MIT

include $(TOPDIR)/../package/gluon.mk

define Package/$(PKG_NAME)
TITLE:=Use attached GPS controller to update location from it
DEPENDS:=+kmod-usb-core +kmod-usb-ohci +kmod-usb2 +kmod-usb-acm +kmod-usb-serial-pl2303 +micrond
endef

define Package/$(PKG_NAME)/description
If enabled, this package updates the gluon-node-info location based on the NMEA output of an attached GPS device.
endef

$(eval $(call BuildPackageGluon,$(PKG_NAME)))
26 changes: 26 additions & 0 deletions ffac-update-location-gps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# ffac-update-location-gps

This package configures gluon to update the location based on tty output of an attached GPS device.

When a USB device which provides a tty is attached, it updates the location based on the output of it.
This is done using a lua rewrite of the script from the original forum posting.
It seems to work without coreutils-stty installed, which did fail the installation when selected as package dependency.

The location is only updated in memory - and only if a valid GPS fix is available.
After a reboot, the old location from the config is set.

## behvario and lockfiles

This creates a lockfile `/var/lock/hotplug-update-location-gps_$DEVNAME.lock` per found TTY device and sets up a cron job which runs every 5 minutes to check if coordinates are available from the stream.
If the TTY is not in use, the open stream waits for the first line to be read (never) and is stuck.
A lockfile `/var/lock/update-location-gps_$TTYDEVICE` is used to check if the cron is already running/stuck and does not start another reading terminal in that case.

On creation and removal of the micron.d job, the micron.d service is restarted


## further information

Further information can be found here:

* https://github.com/dmth/gluon-gps-locationupdate
* https://forum.freifunk.net/t/freifunk-location-update-via-gps/1493
2 changes: 2 additions & 0 deletions ffac-update-location-gps/files/etc/config/update-location-gps
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
config settings 'settings'
option enabled '0'
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/sh
lockpath="/var/lock/hotplug-update-location-gps_${DEVNAME}.lock"

if [ "${ACTION}" = "add" ]; then
enabled=$(uci get update-location-gps.settings.enabled)
[ "${enabled}" != "1" ] && exit 0
test -e "${lockpath}" && exit 0
echo "hotplug-update-location-gps: TTY device ${DEVNAME} was plugged in" > /dev/kmsg
echo "${DEVPATH}" > "${lockpath}"
echo "*/5 * * * * /usr/bin/update-location-gps /dev/${DEVNAME} | logger -t update-location-gps" > "/usr/lib/micron.d/update-location-gps_${DEVNAME}"
/etc/init.d/micrond restart
fi

if [ "${ACTION}" = "remove" ]; then
if [ "${DEVPATH}" = "$(cat "${lockpath}")" ]; then
echo "hotplug-update-location-gps: TTY device ${DEVNAME} was removed" > /dev/kmsg
rm -f "/usr/lib/micron.d/update-location-gps_${DEVNAME}"
/etc/init.d/micrond restart
rm "${lockpath}"
fi
fi
110 changes: 110 additions & 0 deletions ffac-update-location-gps/luasrc/usr/bin/update-location-gps
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/lua

if not arg[1] then
print("Error: No /dev/tty* path specified. Please provide a TTY device path as the first argument.")
os.exit(1)
end

local uci = require('simple-uci').cursor()
local bit = require 'bit'
local fcntl = require 'posix.fcntl'
local unistd = require 'posix.unistd'

-- Get GPS device tty
local tty_device = arg[1]

-- Use GPS as Stream
local file = io.open(tty_device, "r")
if not file then
print("Error: Unable to open " .. tty_device)
os.exit(2)
end

local lockfilename = "/var/lock/update-location-gps_" .. string.gsub(tty_device, "/", "_")

local lockfd, err = fcntl.open(lockfilename, bit.bor(fcntl.O_WRONLY, fcntl.O_CREAT), 384) -- mode 0600
if not lockfd then
print('err', err)
local err_verbose = string.format("Unable to get file descriptor for lock file %s .", lockfilename)
print('err', err_verbose)
os.exit(1)
end

local ok, _ = fcntl.fcntl(lockfd, fcntl.F_SETLK, {
l_start = 0,
l_len = 0,
l_type = fcntl.F_WRLCK,
l_whence = unistd.SEEK_SET,
})
if not ok then
-- silent as this is run in cron
os.exit(1)
end

local line_count = 0
local max_lines = 50

while line_count < max_lines do
local this_line = file:read("*line")
if not this_line then break end -- Exit loop if no more lines

line_count = line_count + 1 -- Increment the line counter

local nc = this_line:match("^([^,]+)")

if nc == '$GPRMC' then
local fields = {}
for field in this_line:gmatch("([^,]+)") do
table.insert(fields, field)
end

local valid = fields[3]

if valid == "A" then
-- First: Retrieve coordinate
local lat = fields[4]
local lon = fields[6]

-- Second: Determine if coordinate is oriented North/South or East/West
local latdir = fields[5]
local londir = fields[7]

-- Split DEGREES from coordinate
local latdeg = tonumber(lat:sub(1, 2))
local londeg = tonumber(lon:sub(1, 3))

-- Split MINUTES.SECONDS from coordinate
local latmin = tonumber(lat:sub(3))
local lonmin = tonumber(lon:sub(4))

-- Convert from Degree-Minutes to Decimal-Minutes
local latdec = latmin / 60
local londec = lonmin / 60

-- Use negative notation instead of North/South or East/West
if latdir == 'S' then
latdeg = -latdeg
end
if londir == 'W' then
londeg = -londeg
end
lat = string.format("%f", latdeg + latdec)
lon = string.format("%f", londeg + londec)

print("GPS position is valid Lat/Lon:", lat, lon)
-- set temp location in gluon-node-info
uci:set('gluon-node-info', '@location[0]', 'latitude', lat)
uci:set('gluon-node-info', '@location[0]', 'longitude', lon)
uci:save('gluon-node-info')
-- Link to Phip's comment: https://forum.freifunk.net/t/freifunk-location-update-via-gps/1493/2
-- Committing here would wear out the nvram very fast, so it should not be done.
break
else
print("GPS position is Invalid...", valid)
break
end
end
end

file:close()
os.exit(0)

0 comments on commit 102e43e

Please sign in to comment.