Added Bulkimport test case

... bulkimport
pull/78/head
Konstantin Gründger 2020-05-15 22:54:52 +02:00
rodzic bb129134a8
commit daf2d81460
15 zmienionych plików z 503 dodań i 51 usunięć

Wyświetl plik

@ -2,6 +2,7 @@ import os
import re
from datetime import datetime, timedelta
from io import StringIO
import gzip
from flask import current_app
from flask.cli import AppGroup
@ -11,8 +12,8 @@ from mgrs import MGRS
from ogn.parser import parse, ParseError
from app.model import AircraftType, Location
from app.gateway.process_tools import open_file, create_tables, drop_tables, update_aircraft_beacons_bigdata
from app.model import AircraftBeacon, ReceiverBeacon, AircraftType, Location
from app.gateway.process_tools import open_file
from app import db
@ -27,12 +28,14 @@ AIRCRAFT_BEACON_TYPES = ["aprs_aircraft", "flarm", "tracker", "fanet", "lt24", "
RECEIVER_BEACON_TYPES = ["aprs_receiver", "receiver"]
# define fields we want to proceed
BEACON_KEY_FIELDS = ["name", "receiver_name", "timestamp"]
AIRCRAFT_BEACON_FIELDS = [
"location",
"altitude",
"name",
"dstcall",
"relay",
"receiver_name",
"timestamp",
"track",
"ground_speed",
"address_type",
@ -57,10 +60,14 @@ AIRCRAFT_BEACON_FIELDS = [
"location_mgrs_short",
"agl",
]
RECEIVER_BEACON_FIELDS = [
"location",
"altitude",
"name",
"dstcall",
"receiver_name",
"timestamp",
]
@ -81,47 +88,23 @@ def initial_file_scan(file):
return row_count, timestamp
class DbFeeder:
def __init__(self, postfix, reference_timestamp, auto_update_timestamp):
self.postfix = postfix
class StringConverter:
def __init__(self, reference_timestamp, auto_update_timestamp):
self.reference_timestamp = reference_timestamp
self.auto_update_timestamp = auto_update_timestamp
self.last_flush = datetime.utcnow()
self.aircraft_buffer = StringIO()
self.receiver_buffer = StringIO()
self.connection = db.engine.raw_connection()
self.cursor = self.connection.cursor()
self.mgrs = MGRS()
create_tables(self.postfix)
def __enter__(self):
return self
def __exit__(self, *args):
self._flush()
update_aircraft_beacons_bigdata(self.postfix)
self.connection.commit()
pass
self.cursor.close()
self.connection.close()
def _convert(self, raw_string):
if raw_string.strip() == '':
return
def _flush(self):
self.aircraft_buffer.seek(0)
self.receiver_buffer.seek(0)
self.cursor.copy_from(self.aircraft_buffer, "aircraft_beacons_{postfix}".format(postfix=self.postfix), sep=",", columns=BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS)
self.cursor.copy_from(self.receiver_buffer, "receiver_beacons_{postfix}".format(postfix=self.postfix), sep=",", columns=BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS)
self.connection.commit()
self.aircraft_buffer = StringIO()
self.receiver_buffer = StringIO()
def add(self, raw_string):
try:
message = parse(raw_string, reference_timestamp=self.reference_timestamp)
except NotImplementedError as e:
@ -148,15 +131,14 @@ class DbFeeder:
latitude = message["latitude"]
longitude = message["longitude"]
location = Location(longitude, latitude)
message["location"] = location.to_wkt()
message["location"] = "SRID=4326;POINT({} {})".format(longitude, latitude)
location_mgrs = self.mgrs.toMGRS(latitude, longitude).decode("utf-8")
message["location_mgrs"] = location_mgrs
message["location_mgrs_short"] = location_mgrs[0:5] + location_mgrs[5:7] + location_mgrs[10:12]
if "aircraft_type" in message:
message["aircraft_type"] = AircraftType(message["aircraft_type"]).name if message["aircraft_type"] in AircraftType.list() else AircraftType.UNKNOWN.name
message["aircraft_type"] = AircraftType(message["aircraft_type"]) if message["aircraft_type"] in AircraftType.list() else AircraftType.UNKNOWN
if "gps_quality" in message:
if message["gps_quality"] is not None and "horizontal" in message["gps_quality"]:
@ -164,26 +146,200 @@ class DbFeeder:
message["gps_quality_vertical"] = message["gps_quality"]["vertical"]
del message["gps_quality"]
if message["beacon_type"] in RECEIVER_BEACON_TYPES:
complete_message = ",".join([str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS])
self.receiver_buffer.write(complete_message)
self.receiver_buffer.write("\n")
elif message["beacon_type"] in AIRCRAFT_BEACON_TYPES:
complete_message = ",".join([str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS])
self.aircraft_buffer.write(complete_message)
self.aircraft_buffer.write("\n")
else:
current_app.logger.error("Ignore beacon_type: {}".format(message["beacon_type"]))
return message
def _get_aircraft_beacon_csv_string(self, message, none_character=''):
csv_string = "{0},{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}\n".format(
message['location'],
int(message['altitude']),
message['name'],
message['dstcall'],
message['relay'] if 'relay' in message and message['relay'] else none_character,
message['receiver_name'],
message['timestamp'],
message['track'] if 'track' in message and message['track'] else none_character,
message['ground_speed'] if 'ground_speed' in message and message['ground_speed'] else none_character,
message['address_type'] if 'address_type' in message and message['address_type'] else none_character,
message['aircraft_type'].name if 'aircraft_type' in message and message['aircraft_type'] else AircraftType.UNKNOWN.name,
message['stealth'] if 'stealth' in message and message['stealth'] else none_character,
message['address'] if 'address' in message and message['address'] else none_character,
message['climb_rate'] if 'climb_rate' in message and message['climb_rate'] else none_character,
message['turn_rate'] if 'turn_rate' in message and message['turn_rate'] else none_character,
message['signal_quality'] if 'signal_quality' in message and message['signal_quality'] else none_character,
message['error_count'] if 'error_count' in message and message['error_count'] else none_character,
message['frequency_offset'] if 'frequency_offset' in message and message['frequency_offset'] else none_character,
message['gps_quality_horizontal'] if 'gps_quality_horizontal' in message and message['gps_quality_horizontal'] else none_character,
message['gps_quality_vertical'] if 'gps_quality_vertical' in message and message['gps_quality_vertical'] else none_character,
message['software_version'] if 'software_version' in message and message['software_version'] else none_character, #20
message['hardware_version'] if 'hardware_version' in message and message['hardware_version'] else none_character,
message['real_address'] if 'real_address' in message and message['real_address'] else none_character,
message['signal_power'] if 'signal_power' in message and message['signal_power'] else none_character,
message['distance'] if 'distance' in message and message['distance'] else none_character,
message['radial'] if 'radial' in message and message['radial'] else none_character,
message['quality'] if 'quality' in message and message['quality'] else none_character,
message['location_mgrs'],
message['location_mgrs_short'],
message['agl'] if 'agl' in message else none_character, #29
)
return csv_string
def _get_receiver_beacon_csv_string(self, message, none_character=''):
csv_string = "{0},{1},{2},{3},{4},{5}\n".format(
message['location'],
int(message['altitude']) if message['altitude'] else none_character,
message['name'],
message['dstcall'],
message['receiver_name'],
message['timestamp'],
)
return csv_string
class FileFeeder(StringConverter):
def __init__(self, postfix, reference_timestamp, reference_timestamp_autoupdate):
self.reference_timestamp = reference_timestamp
self.reference_timestamp_autoupdate = reference_timestamp_autoupdate
self.aircraft_beacons_file = gzip.open('aircraft_beacons_{}.csv.gz'.format(postfix), 'wt')
self.receiver_beacons_file = gzip.open('receiver_beacons_{}.csv.gz'.format(postfix), 'wt')
super().__init__(reference_timestamp, reference_timestamp_autoupdate)
def __enter__(self):
self.aircraft_beacons_file.write(','.join(AIRCRAFT_BEACON_FIELDS))
self.receiver_beacons_file.write(','.join(RECEIVER_BEACON_FIELDS))
return self
def __exit__(self, *args):
self.aircraft_beacons_file.close()
self.receiver_beacons_file.close()
def add(self, raw_string):
message = self._convert(raw_string)
if message['beacon_type'] in AIRCRAFT_BEACON_TYPES:
csv_string = self._get_aircraft_beacon_csv_string(message)
self.aircraft_beacons_file.write(csv_string)
elif message['beacon_type'] in RECEIVER_BEACON_TYPES:
csv_string = self._get_receiver_beacon_csv_string(message)
self.receiver_beacons_file.write(csv_string)
class DbFeeder(StringConverter):
def __init__(self, reference_timestamp, reference_timestamp_autoupdate):
self.reference_timestamp = reference_timestamp
self.reference_timestamp_autoupdate = reference_timestamp_autoupdate
self.aircraft_beacons_buffer = StringIO()
self.receiver_beacons_buffer = StringIO()
self.last_flush = datetime.utcnow()
super().__init__(reference_timestamp, reference_timestamp_autoupdate)
def __exit__(self, *args):
self.flush()
def add(self, raw_string):
message = self._convert(raw_string)
if not message:
return
if datetime.utcnow() - self.last_flush >= timedelta(seconds=5):
self._flush()
if message['beacon_type'] in AIRCRAFT_BEACON_TYPES:
csv_string = self._get_aircraft_beacon_csv_string(message, none_character=r'\N')
self.aircraft_beacons_buffer.write(csv_string)
elif message['beacon_type'] in RECEIVER_BEACON_TYPES:
csv_string = self._get_receiver_beacon_csv_string(message, none_character=r'\N')
self.receiver_beacons_buffer.write(csv_string)
if datetime.utcnow() - self.last_flush >= timedelta(seconds=1):
self.flush()
self.last_flush = datetime.utcnow()
def flush(self):
connection = db.engine.raw_connection()
cursor = connection.cursor()
self.aircraft_beacons_buffer.seek(0)
self.receiver_beacons_buffer.seek(0)
cursor.execute("CREATE TEMPORARY TABLE aircraft_beacons_temp (LIKE aircraft_beacons) ON COMMIT DROP;")
cursor.execute("CREATE TEMPORARY TABLE receiver_beacons_temp (LIKE receiver_beacons) ON COMMIT DROP;")
cursor.copy_from(file=self.aircraft_beacons_buffer, table="aircraft_beacons_temp", sep=",", columns=AIRCRAFT_BEACON_FIELDS)
cursor.copy_from(file=self.receiver_beacons_buffer, table="receiver_beacons_temp", sep=",", columns=RECEIVER_BEACON_FIELDS)
# Update receivers
cursor.execute("""
INSERT INTO receivers AS r (name, location, altitude, firstseen, lastseen, timestamp)
SELECT DISTINCT ON (rbt.name)
rbt.name,
rbt.location,
rbt.altitude,
timezone('utc', NOW()) AS firstseen,
timezone('utc', NOW()) AS lastseen,
rbt.timestamp
FROM receiver_beacons_temp AS rbt,
(
SELECT
rbt.name,
MAX(timestamp) AS timestamp
FROM receiver_beacons_temp AS rbt
GROUP BY rbt.name
) AS sq
WHERE rbt.name = sq.name AND rbt.timestamp = sq.timestamp
ON CONFLICT (name) DO UPDATE
SET
location = EXCLUDED.location,
altitude = EXCLUDED.altitude,
lastseen = timezone('utc', NOW()),
timestamp = EXCLUDED.timestamp
""")
# Update agl
cursor.execute("""
UPDATE aircraft_beacons_temp AS abt
SET
agl = ST_Value(e.rast, abt.location)
FROM elevation AS e
WHERE ST_Intersects(abt.location, e.rast)
""")
# ... update receiver related attributes: distance, radial, quality
cursor.execute("""
UPDATE aircraft_beacons_temp AS abt
SET
distance = CAST(ST_DistanceSphere(r.location, abt.location) AS REAL),
radial = CASE WHEN Degrees(ST_Azimuth(r.location, abt.location)) >= 359.5 THEN 0 ELSE CAST(Degrees(ST_Azimuth(r.location, abt.location)) AS INT) END,
quality = CASE WHEN ST_DistanceSphere(r.location, abt.location) > 0 THEN CAST(abt.signal_quality + 20.0 * LOG(ST_DistanceSphere(r.location, abt.location) / 10000) AS REAL) ELSE NULL END
FROM receivers AS r
WHERE abt.receiver_name = r.name
""")
# Insert all the beacons
cursor.execute("""
INSERT INTO aircraft_beacons
SELECT * FROM aircraft_beacons_temp
ON CONFLICT DO NOTHING;
""")
cursor.execute("""
INSERT INTO receiver_beacons
SELECT * FROM receiver_beacons_temp
ON CONFLICT DO NOTHING;
""")
connection.commit()
cursor.close()
connection.close()
self.aircraft_beacons_buffer = StringIO()
self.receiver_beacons_buffer = StringIO()
def convert(sourcefile):
print("Fast scan of file '{}'...".format(sourcefile), end='')
with open_file(sourcefile) as filehandler:
total_lines, reference_timestamp = initial_file_scan(filehandler)
print("done")
if reference_timestamp is not None:
auto_update_timestamp = True
@ -192,15 +348,85 @@ def convert(sourcefile):
auto_update_timestamp = False
match = re.match(r".*OGN_log\.txt_([0-9]{4}\-[0-9]{2}\-[0-9]{2})\.gz$", sourcefile)
if match:
reference_timestamp = datetime.strptime(match.group(1), "%Y-%m-%d")
reference_timestamp = datetime.strptime(match.group(1), "%Y-%m-%d") + timedelta(hours=12)
postfix = reference_timestamp.strftime("%Y_%m_%d")
else:
current_app.logger.error("No reference time information. Skipping file: {}".format(sourcefile))
return
with open_file(sourcefile) as fin:
with DbFeeder(postfix=postfix, reference_timestamp=reference_timestamp, auto_update_timestamp=auto_update_timestamp) as feeder:
with FileFeeder(postfix=postfix, reference_timestamp=reference_timestamp, auto_update_timestamp=auto_update_timestamp) as feeder:
pbar = tqdm(fin, total=total_lines)
for line in pbar:
pbar.set_description("Importing {}".format(sourcefile))
feeder.add(raw_string=line)
def calculate(ab_filename, rb_filename, target_filename):
sql_string = ("""
DROP TABLE IF EXISTS tmp_ab;
DROP TABLE IF EXISTS tmp_rb;
CREATE TABLE tmp_ab
AS
SELECT *
FROM aircraft_beacons
WITH NO DATA;
CREATE TABLE tmp_rb
AS
SELECT *
FROM receiver_beacons
WITH NO DATA;
COPY tmp_ab FROM PROGRAM 'gunzip -c {ab_filename}' CSV DELIMITER ',' HEADER;
COPY tmp_rb FROM PROGRAM 'gunzip -c {rb_filename}' CSV DELIMITER ',' HEADER;
COPY (
WITH sq AS (
SELECT
'SRID=4326;' || ST_AsText(ab.location) AS location,
ab.altitude,
ab.name,
ab.dstcall,
ab.relay,
ab.receiver_name,
ab.timestamp,
CASE WHEN ab.track = 360 THEN 0 ELSE ab.track END,
ab.ground_speed,
ab.address_type,
ab.aircraft_type,
ab.stealth,
ab.address,
ab.climb_rate,
ab.turn_rate,
ab.signal_quality,
ab.error_count,
ab.frequency_offset,
ab.gps_quality_horizontal,
ab.gps_quality_vertical,
ab.software_version,
ab.hardware_version,
ab.real_address,
ab.signal_power,
CAST(ST_DistanceSphere(rb.location, ab.location) AS REAL) AS distance,
CASE WHEN Degrees(ST_Azimuth(rb.location, ab.location)) >= 359.5 THEN 0 ELSE CAST(Degrees(ST_Azimuth(rb.location, ab.location)) AS INT) END AS radial,
CASE WHEN ST_DistanceSphere(rb.location, ab.location) > 0 THEN CAST(ab.signal_quality + 20.0 * LOG(ST_DistanceSphere(rb.location, ab.location) / 10000) AS REAL) ELSE NULL END quality,
ab.location_mgrs,
ab.location_mgrs_short,
ab.altitude - ST_Value(e.rast, ab.location) AS agl
FROM tmp_ab AS ab, elevation AS e, (SELECT name, MAX(location) AS location FROM tmp_rb GROUP BY name) AS rb
WHERE ab.receiver_name = rb.name AND ST_Intersects(ab.location, e.rast)
)
SELECT DISTINCT ON (name, receiver_name, timestamp) *
FROM sq
) TO PROGRAM 'gzip > {target_filename}' CSV DELIMITER ',' HEADER;
COPY (
SELECT DISTINCT ON (name, receiver_name, timestamp) *
FROM tmp_rb AS rb
) TO PROGRAM 'gzip > {rb_filename}2' CSV DELIMITER ',' HEADER;
""".format(ab_filename=ab_filename, rb_filename=rb_filename, target_filename=target_filename))
db.session.execute(sql_string)

Wyświetl plik

@ -9,12 +9,19 @@ class TestBaseDB(unittest.TestCase):
self.app_context.push()
db.session.execute("CREATE EXTENSION IF NOT EXISTS postgis;")
db.session.commit()
db.create_all()
db.session.commit()
db.session.execute("CREATE TABLE IF NOT EXISTS elevation (rast raster);")
db.session.commit()
def tearDown(self):
db.session.remove()
db.drop_all()
db.session.execute("DROP TABLE IF EXISTS elevaion;")
db.session.commit()
self.app_context.pop()

Wyświetl plik

@ -0,0 +1,44 @@
import os
import unittest
from datetime import datetime
from app.model import AircraftBeacon, ReceiverBeacon
from app.gateway.bulkimport import DbFeeder
from tests.base import TestBaseDB, db
class TestDatabase(TestBaseDB):
def test_valid_messages(self):
"""This test insert all valid beacons. source: https://github.com/glidernet/ogn-aprs-protocol/valid_messages"""
path = os.path.join(os.path.dirname(__file__), 'valid_messages')
with DbFeeder(reference_timestamp=datetime.utcnow(), reference_timestamp_autoupdate=True) as feeder:
with os.scandir(path) as it:
for entry in it:
if entry.name.endswith(".txt") and entry.is_file():
print(f"Parsing {entry.name}")
with open(entry.path) as file:
for line in file:
feeder.add(line)
feeder.flush()
@unittest.skip('currently only positions are considered')
def test_ognsdr_beacons(self):
"""This test tests if status+position is correctly merged."""
aprs_stream = (
"LILH>OGNSDR,TCPIP*,qAC,GLIDERN2:/132201h4457.61NI00900.58E&/A=000423\n"
"LILH>OGNSDR,TCPIP*,qAC,GLIDERN2:>132201h v0.2.7.RPI-GPU CPU:0.7 RAM:770.2/968.2MB NTP:1.8ms/-3.3ppm +55.7C 7/8Acfts[1h] RF:+54-1.1ppm/-0.16dB/+7.1dB@10km[19481]/+16.8dB@10km[7/13]"
)
with DbFeeder(reference_timestamp=datetime.utcnow(), reference_timestamp_autoupdate=True) as feeder:
for line in aprs_stream.split('\n'):
feeder.add(line)
self.assertEqual(len(db.session.query(ReceiverBeacon).all()), 1)
for ab in db.session.query(ReceiverBeacon).all():
print(ab)
if __name__ == "__main__":
unittest.main()

Wyświetl plik

@ -0,0 +1,18 @@
# The following beacons are example for the (deprecated) APRS format for flarms and ogn trackers
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# Until OGN software 0.2.6 all beacons (flarms, ogn trackers and receivers) have the dstcall "APRS"
# These are example beacons for flarms and ogn trackers
#
FLRDDA5BA>APRS,qAS,LFMX:/165829h4415.41N/00600.03E'342/049/A=005524 id0ADDA5BA -454fpm -1.1rot 8.8dB 0e +51.2kHz gps4x5
ICA4B0E3A>APRS,qAS,Letzi:/165319h4711.75N\00802.59E^327/149/A=006498 id154B0E3A -3959fpm +0.5rot 9.0dB 0e -6.3kHz gps1x3
FLRDDB091>APRS,qAS,Letzi:/165831h4740.04N/00806.01EX152/124/A=004881 id06DD8E80 +198fpm +0.0rot 6.5dB 13e +4.0kHz gps3x4
FLRDDDD33>APRS,qAS,LFNF:/165341h4344.27N/00547.41E'/A=000886 id06DDDD33 +020fpm +0.0rot 20.8dB 0e -14.3kHz gps3x4
FLRDDE026>APRS,qAS,LFNF:/165341h4358.58N/00553.89E'204/055/A=005048 id06DDE026 +257fpm +0.1rot 7.2dB 0e -0.8kHz gps4x7
ICA484A9C>APRS,qAS,LFMX:/165341h4403.50N/00559.67E'/A=001460 id05484A9C +000fpm +0.0rot 18.0dB 0e +3.5kHz gps4x7
OGNE95A16>APRS,qAS,Sylwek:/165641h5001.94N/01956.91E'270/004/A=000000 id07E95A16 +000fpm +0.1rot 37.8dB 0e -0.4kHz
ZK-GSC>APRS,qAS,Omarama:/165202h4429.25S/16959.33E'/A=001407 id05C821EA +020fpm +0.0rot 16.8dB 0e -3.1kHz gps1x3 hear1084 hearB597 hearB598
#
# since 0.2.6 a aircraft beacon needs just an ID, climb rate and turn rate or just the ID
ICA3ECE59>APRS,qAS,GLDRTR:/171254h5144.78N/00616.67E'263/000/A=000075 id093D0930 +000fpm +0.0rot
ICA3ECE59>APRS,qAS,GLDRTR:/171254h5144.78N/00616.67E'263/000/A=000075 id053ECE59

Wyświetl plik

@ -0,0 +1,33 @@
# The following beacons are example for the (deprecated) APRS format for ogn receivers
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# Until OGN software 0.2.6 all beacons (flarms, ogn trackers and receivers) have the dstcall "APRS"
# These are example beacons for receivers
#
Lachens>APRS,TCPIP*,qAC,GLIDERN2:/165334h4344.70NI00639.19E&/A=005435 v0.2.1 CPU:0.3 RAM:1764.4/2121.4MB NTP:2.8ms/+4.9ppm +47.0C RF:+0.70dB
LFGU>APRS,TCPIP*,qAC,GLIDERN2:/165556h4907.63NI00706.41E&/A=000833 v0.2.0 CPU:0.9 RAM:281.3/458.9MB NTP:0.5ms/-19.1ppm +53.0C RF:+0.70dB
LSGS>APRS,TCPIP*,qAC,GLIDERN1:/165345h4613.25NI00719.68E&/A=001581 CPU:0.7 RAM:247.9/456.4MB NTP:0.7ms/-11.4ppm +44.4C RF:+53+71.9ppm/+0.4dB
WolvesSW>APRS,TCPIP*,qAC,GLIDERN2:/165343h5232.23NI00210.91W&/A=000377 CPU:1.5 RAM:159.9/458.7MB NTP:6.6ms/-36.7ppm +45.5C RF:+130-0.4ppm/-0.1dB
Oxford>APRS,TCPIP*,qAC,GLIDERN1:/165533h5142.96NI00109.68W&/A=000380 v0.1.3 CPU:0.9 RAM:268.8/458.6MB NTP:0.5ms/-45.9ppm +60.5C RF:+55+2.9ppm/+1.54dB
Salland>APRS,TCPIP*,qAC,GLIDERN2:/165426h5227.93NI00620.03E&/A=000049 v0.2.2 CPU:0.7 RAM:659.3/916.9MB NTP:2.5ms/-75.0ppm RF:+0.41dB
LSGS>APRS,TCPIP*,qAC,GLIDERN1:/165345h4613.25NI00719.68E&/A=001581 CPU:0.7 RAM:247.9/456.4MB NTP:0.7ms/-11.4ppm +44.4C RF:+53+71.9ppm/+0.4dB
Drenstein>APRS,TCPIP*,qAC,GLIDERN1:/165011h5147.51NI00744.45E&/A=000213 v0.2.2 CPU:0.8 RAM:695.7/4025.5MB NTP:16000.0ms/+0.0ppm +63.0C
#
# since 0.2.5 for receiver information not only the "aprs position" format is used but also the "aprs status" format (without lat/lon/alt informations)
Cordoba>APRS,TCPIP*,qAC,GLIDERN3:/194847h3112.85SI06409.56W&/A=001712 v0.2.5.ARM CPU:0.4 RAM:755.4/970.8MB NTP:6.7ms/-0.1ppm +45.5C RF:+48+18.3ppm/+3.45dB
Cordoba>APRS,TCPIP*,qAC,GLIDERN3:>194847h v0.2.5.ARM CPU:0.4 RAM:755.4/970.8MB NTP:6.7ms/-0.1ppm +45.5C 0/0Acfts[1h] RF:+48+18.3ppm/+3.45dB/+0.4dB@10km[71]/+0.4dB@10km[1/1]
VITACURA1>APRS,TCPIP*,qAC,GLIDERN3:/042149h3322.81SI07034.95W&/A=002345 v0.2.5.ARM CPU:0.6 RAM:694.4/970.5MB NTP:0.8ms/-7.5ppm +54.8C RF:+0-0.2ppm/+3.81dB
VITACURA1>APRS,TCPIP*,qAC,GLIDERN3:>042149h v0.2.5.ARM CPU:0.6 RAM:694.4/970.5MB NTP:0.8ms/-7.5ppm +54.8C 0/0Acfts[1h] RF:+0-0.2ppm/+3.81dB/+1.3dB@10km[132205]/+6.6dB@10km[10/20]
Arnsberg>APRS,TCPIP*,qAC,GLIDERN1:/042146h5123.04NI00803.77E&/A=000623 v0.2.5.ARM CPU:0.4 RAM:765.1/970.8MB NTP:0.4ms/-1.7ppm +62.3C RF:+27+1.1ppm/+3.17dB
Arnsberg>APRS,TCPIP*,qAC,GLIDERN1:>042146h v0.2.5.ARM CPU:0.4 RAM:764.9/970.8MB NTP:0.4ms/-1.7ppm +62.3C 0/0Acfts[1h] RF:+27+1.1ppm/+3.17dB/+9.2dB@10km[44487]/+12.1dB@10km[20/40]
CNF3a>APRS,TCPIP*,qAC,GLIDERN3:/042143h4529.25NI07505.65W&/A=000259 v0.2.5.ARM CPU:0.6 RAM:514.6/970.8MB NTP:4.5ms/-1.5ppm +27.2C RF:+0-0.4ppm/+18.69dB
CNF3a>APRS,TCPIP*,qAC,GLIDERN3:>042143h v0.2.5.ARM CPU:0.6 RAM:514.6/970.8MB NTP:4.5ms/-1.5ppm +27.2C 0/0Acfts[1h] RF:+0-0.4ppm/+18.69dB/+13.0dB@10km[104282]/+9.7dB@10km[2/3]
VITACURA2>APRS,TCPIP*,qAC,GLIDERN3:/042136h3322.81SI07034.95W&/A=002345 v0.2.5.ARM CPU:0.3 RAM:695.0/970.5MB NTP:0.6ms/-5.7ppm +51.5C RF:+0-0.0ppm/+1.32dB
VITACURA2>APRS,TCPIP*,qAC,GLIDERN3:>042136h v0.2.5.ARM CPU:0.3 RAM:695.0/970.5MB NTP:0.6ms/-5.7ppm +52.1C 0/0Acfts[1h] RF:+0-0.0ppm/+1.32dB/+2.1dB@10km[193897]/+9.0dB@10km[10/20]
#
# since 0.2.6 the ogn comment of a receiver beacon is just optional, it also can be a user comment
Ulrichamn>APRS,TCPIP*,qAC,GLIDERN1:/085616h5747.30NI01324.77E&/A=001322
ROBLE3>APRS,TCPIP*,qAC,GLIDERN4:/200022h3258.58SI07100.78W&/A=007229 Contact: achanes@manquehue.net, brito.felipe@gmail.com
#
# ... and user comment can include a 'id'
ALFALFAL>APRS,TCPIP*,qAC,GLIDERN4:/221830h3330.40SI07007.88W&/A=008659 Alfalfal Hidroelectric Plant, Club de Planeadores Vitacurs

Wyświetl plik

@ -0,0 +1,12 @@
# The following beacons are example for the Capture APRS format
# source: https://github.com/glidernet/ogn-aprs-protocol
#
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/062744h4845.03N/00230.46E'000/000/
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/064243h4839.64N/00236.78E'000/085/A=000410
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/064548h4838.87N/00234.03E'000/042/A=000377
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/064847h4837.95N/00234.36E'000/000/
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/065144h4837.56N/00233.80E'000/000/
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/065511h4837.63N/00233.79E'000/000/
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/070016h4837.63N/00233.77E'000/001/A=000360
FLRDDEEF1>OGCAPT,qAS,CAPTURS:/070153h4837.62N/00233.77E'000/001/A=000344

Wyświetl plik

@ -0,0 +1,10 @@
# The following beacons are example for the Flarm APRS format
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# With OGN software 0.2.7 flarms have the dstcall "OGFLR"
#
FLRDD89C9>OGFLR,qAS,LIDH:/115054h4543.22N/01132.84E'260/072/A=002542 !W10! id06DD89C9 +198fpm -0.8rot 7.0dB 0e +0.7kHz gps2x3
FLRDD98C6>OGFLR,qAS,LIDH:/115054h4543.21N/01132.80E'255/074/A=002535 !W83! id0ADD98C6 +158fpm -1.8rot 10.5dB 0e -0.8kHz gps2x3 s6.09 h02
ICAA8CBA8>OGFLR,qAS,MontCAIO:/231150z4512.12N\01059.03E^192/106/A=009519 !W20! id21A8CBA8 -039fpm +0.0rot 3.5dB 2e -8.7kHz gps1x2 s6.09 h43 rDF0267
ICAA8CBA8>OGFLR,qAS,MontCAIO:/114949h4512.44N\01059.12E^190/106/A=009522 !W33! id21A8CBA8 -039fpm +0.1rot 4.5dB 1e -8.7kHz gps1x2 +14.3dBm
ICA3D1C35>OGFLR,qAS,Padova:/094220h4552.41N/01202.28E'110/099/A=003982 !W96! id053D1C35 -1187fpm +0.0rot 0.8dB 2e +4.5kHz gps1x2 s6.09 h32 rDD09D0

Wyświetl plik

@ -0,0 +1,16 @@
# The following beacons are example for LiveTrack24 APRS format
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# the id25387 is the LT24 user ID that is unique.
# GPS means that the coordinates are from the GPS unit of the smartphone vs. the GSM network.
#
FLRDDE48A>OGLT24,qAS,LT24:/102606h4030.47N/00338.38W'000/018/A=002267 id25387 +000fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102608h4030.47N/00338.38W'044/018/A=002270 id25387 +000fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102611h4030.47N/00338.38W'108/000/A=002280 id25387 +001fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102612h4030.47N/00338.38W'000/000/A=002280 id25387 +000fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102615h4030.47N/00338.39W'224/003/A=002280 id25387 +000fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102616h4030.47N/00338.38W'028/003/A=002250 id25387 -009fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102621h4030.47N/00338.38W'142/001/A=002267 id25387 +001fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102628h4030.47N/00338.38W'034/000/A=002263 id25387 +000fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/102717h4030.47N/00338.38W'000/000/A=002263 id25387 +000fpm GPS
FLRDDE48A>OGLT24,qAS,LT24:/110453h4030.47N/00338.38W'154/004/A=002253 id25387 +000fpm GPS

Wyświetl plik

@ -0,0 +1,7 @@
# The following beacons are example for NAVITER's APRS format version OGNAVI-1
# source: https://github.com/glidernet/ogn-aprs-protocol
#
NAV042121>OGNAVI,qAS,NAVITER:/140648h4550.36N/01314.85E'090/152/A=001086 !W47! id0440042121 +000fpm +0.5rot
NAV04220E>OGNAVI,qAS,NAVITER:/140748h4552.27N/01155.61E'090/012/A=006562 !W81! id044004220E +060fpm +1.2rot
NAV07220E>OGNAVI,qAS,NAVITER:/125447h4557.77N/01220.19E'258/056/A=006562 !W76! id1C4007220E +180fpm +0.0rot
FLRFFFFFF>OGNAVI,NAV07220E*,qAS,NAVITER:/092002h1000.00S/01000.00W'000/000/A=003281 !W00! id2820FFFFFF +300fpm +1.7rot

Wyświetl plik

@ -0,0 +1,9 @@
# The following beacons are example for FANET (Skytraxx) APRS format
# source: https://github.com/glidernet/ogn-aprs-protocol
#
FNT1103CE>OGNFNT,qAS,FNB1103CE:/183727h5057.94N/00801.00Eg355/002/A=001042 !W10! id1E1103CE +03fpm
FNT1103CE>OGNFNT,qAS,FNB1103CE:/183729h5057.94N/00801.00Eg354/001/A=001042 !W10! id1E1103CE +07fpm
FNT1103CE>OGNFNT,qAS,FNB1103CE:/183731h5057.94N/00801.00Eg354/001/A=001042 !W10! id1E1103CE +05fpm
FNT1103CE>OGNFNT,qAS,FNB1103CE:/183734h5057.94N/00801.00Eg354/001/A=001042 !W30! id1E1103CE -10fpm
FNT1103CE>OGNFNT,qAS,FNB1103CE:/183736h5057.94N/00801.00Eg354/001/A=001042 !W40! id1E1103CE -02fpm
FNB1103CE>OGNFNT,TCPIP*,qAC,GLIDERN3:/183738h5057.95NI00801.00E&/A=001042

Wyświetl plik

@ -0,0 +1,19 @@
# The following beacons are example for the APRS format for OGN receivers
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# With OGN software 0.2.7 receivers have the dstcall "OGNSDR"
#
LILH>OGNSDR,TCPIP*,qAC,GLIDERN2:/132201h4457.61NI00900.58E&/A=000423
LILH>OGNSDR,TCPIP*,qAC,GLIDERN2:>132201h v0.2.7.RPI-GPU CPU:0.7 RAM:770.2/968.2MB NTP:1.8ms/-3.3ppm +55.7C 7/8Acfts[1h] RF:+54-1.1ppm/-0.16dB/+7.1dB@10km[19481]/+16.8dB@10km[7/13]
MontCAIO>OGNSDR,TCPIP*,qAC,GLIDERN3:/132231h4427.84NI01009.60E&/A=004822
MontCAIO>OGNSDR,TCPIP*,qAC,GLIDERN3:>132231h v0.2.7.RPI-GPU CPU:0.8 RAM:747.0/970.5MB NTP:2.8ms/-1.0ppm +73.1C 5/5Acfts[1h] RF:+69+1.3ppm/+3.53dB/+16.9dB@10km[7697]/+23.7dB@10km[3/6]
Padova>OGNSDR,TCPIP*,qAC,GLIDERN1:/132326h4525.38NI01156.29E&/A=000069
Padova>OGNSDR,TCPIP*,qAC,GLIDERN1:>132326h v0.2.7.RPI-GPU CPU:0.5 RAM:605.1/970.5MB NTP:0.5ms/-2.0ppm +65.5C 1/1Acfts[1h] RF:+0-1.1ppm/+13.97dB/+17.1dB@10km[6524]/+19.9dB@10km[5/9]
LIDH>OGNSDR,TCPIP*,qAC,GLIDERN1:/132447h4540.89NI01129.65E&/A=000328
LIDH>OGNSDR,TCPIP*,qAC,GLIDERN1:>132447h v0.2.7.RPI-GPU CPU:0.4 RAM:593.4/970.5MB NTP:3.7ms/-7.6ppm +67.7C 5/5Acfts[1h] RF:+61+1.0ppm/+12.63dB/+3.7dB@10km[27143]/+3.3dB@10km[3/6]
LZHL>OGNSDR,TCPIP*,qAC,GLIDERN3:/132457h4849.09NI01708.30E&/A=000528
LZHL>OGNSDR,TCPIP*,qAC,GLIDERN3:>132457h v0.2.7.arm CPU:0.9 RAM:75.3/253.6MB NTP:2.0ms/-15.2ppm +0.1C 2/2Acfts[1h] RF:+77+1.7ppm/+2.34dB/+6.5dB@10km[5411]/+10.1dB@10km[3/5]
BELG>OGNSDR,TCPIP*,qAC,GLIDERN3:/132507h4509.60NI00919.20E&/A=000246
BELG>OGNSDR,TCPIP*,qAC,GLIDERN3:>132507h v0.2.7.RPI-GPU CPU:1.2 RAM:35.7/455.2MB NTP:2.5ms/-5.3ppm +67.0C 1/1Acfts[1h] RF:+79+8.8ppm/+4.97dB/-0.0dB@10km[299]/+4.9dB@10km[2/3]
Saleve>OGNSDR,TCPIP*,qAC,GLIDERN1:/132624h4607.70NI00610.41E&/A=004198 Antenna: chinese, on a pylon, 20 meter above ground
Saleve>OGNSDR,TCPIP*,qAC,GLIDERN1:>132624h v0.2.7.arm CPU:1.7 RAM:812.3/1022.5MB NTP:1.8ms/+4.5ppm 0.000V 0.000A 3/4Acfts[1h] RF:+67+2.9ppm/+4.18dB/+11.7dB@10km[5018]/+17.2dB@10km[8/16]

Wyświetl plik

@ -0,0 +1,12 @@
# The following beacons are example for the APRS format for ogn trackers
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# With OGN software 0.2.7 ogn tracker have the dstcall "OGNTRK"
#
OGN3FC859>OGNTRK,qAS,LZHL:>093215h h00 v00 9sat/1 164m 1002.6hPa +20.2degC 0% 3.34V 14/-110.5dBm 1/min
OGN2FD00F>OGNTRK,qAS,LZHL:/093213h4848.78N/01708.32E'000/000/A=000538 !W12! id072FD00F -058fpm +0.0rot FL003.12 32.8dB 0e -0.8kHz gps3x5
FLRDD9C70>OGNTRK,OGN2FD00F*,qAS,LZHL:/093214h4848.77N/01708.33E'000/000/A=000515 !W56! id06DD9C70 -019fpm +0.0rot 32.2dB 0e -0.8kHz gps2x3
FLRDD9C70>OGNTRK,OGN2FD00F*,qAS,LZHL:/093021h4848.77N/01708.33E'000/000/A=000518 !W66! id06DD9C70 -019fpm +0.0rot 29.0dB 0e -0.8kHz gps2x3 s6.09 h03
OGN03AF2A>OGNTRK,qAS,LZHL:/092912h4848.77N/01708.33E'000/000/A=000535 !W53! id0703AF2A +000fpm +0.0rot FL003.15 4.5dB 1e -0.1kHz gps4x5 -11.2dBm
OGN2FD00F>OGNTRK,qAS,LZHL:>092840h h00 v00 11sat/2 165m 1001.9hPa +27.1degC 0% 3.28V 14/-111.5dBm 127/min
FLRDD9C70>OGNTRK,RELAY*,qAS,LZHL:/094124h4848.78N/01708.33E'000/000/A=000397 !W15! id06DD9C70 +099fpm +0.0rot 24.5dB 0e -1.4kHz gps10x15

Wyświetl plik

@ -0,0 +1,6 @@
# The following beacons are example for Skylines (XCsoar) APRS format
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# The id2816 is the Pilot ID from Skylines (XCsoar) and it is unique within the skyline system
#
FLRDDDD78>OGSKYL,qAS,SKYLINES:/134403h4225.90N/00144.83E'000/000/A=008438 id2816 +000fpm

Wyświetl plik

@ -0,0 +1,23 @@
# The following beacons are example for Spider APRS format
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# id3003... is the unique identifier from the SPIDER server
# LWE is the registration within the SPIDER system
# 3D is the quality of the signal 3D vs. 2D
#
FLRDDF944>OGSPID,qAS,SPIDER:/190930h3322.78S/07034.60W'000/000/A=002263 id300234010617040 +19dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/190930h3322.78S/07034.60W'000/000/A=002263 id300234010617040 +19dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/192430h3322.78S/07034.61W'000/000/A=002250 id300234010617040 +12dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/193930h3322.10S/07034.26W'273/027/A=004071 id300234010617040 +9dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/195430h3322.82S/07034.90W'000/000/A=002217 id300234010617040 +10dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/193930h3322.78S/07034.60W'348/000/A=002286 id300234010617040 +12dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/195430h3323.16S/07037.68W'302/034/A=003316 id300234010617040 +10dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/195430h3323.16S/07037.68W'302/034/A=003316 id300234010617040 +10dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/200930h3319.13S/07036.12W'128/031/A=005482 id300234010617040 +15dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/200930h3319.13S/07036.12W'128/031/A=005482 id300234010617040 +15dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/202430h3314.92S/07032.08W'138/032/A=006453 id300234010617040 +9dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/203930h3321.38S/07027.29W'104/034/A=006272 id300234010617040 +8dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/205430h3322.13S/07033.53W'296/031/A=003927 id300234010617040 +7dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/210930h3322.05S/07035.74W'165/030/A=005187 id300234010617040 +8dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/212430h3322.02S/07036.14W'281/028/A=004550 id300234010617040 +7dB LWE 3D
FLRDDF944>OGSPID,qAS,SPIDER:/213930h3322.17S/07033.97W'332/028/A=003428 id300234010617040 +7dB LWE 3D

Wyświetl plik

@ -0,0 +1,10 @@
# The following beacons are example for SPOT APRS format
# source: https://github.com/glidernet/ogn-aprs-protocol
#
# id0-28... is a unique ID within the SPOT system.
# SPOT2 is the Spot model
# GOOD is the battery status or some other help messages.
#
ICA3E7540>OGSPOT,qAS,SPOT:/161427h1448.35S/04610.86W'000/000/A=008677 id0-2860357 SPOT3 GOOD
ICA3E7540>OGSPOT,qAS,SPOT:/162923h1431.99S/04604.33W'000/000/A=006797 id0-2860357 SPOT3 GOOD
ICA3E7540>OGSPOT,qAS,SPOT:/163421h1430.38S/04604.43W'000/000/A=007693 id0-2860357 SPOT3 GOOD