python-ogn-client
Repository: https://github.com/glidernet/python-ogn-client
PyPI: https://pypi.python.org/pypi/ogn-client
A python3 module for the Open Glider Network. It can be used to connect to the OGN-APRS-Servers and to parse APRS-/OGN-Messages.
Usage
Parser
Parse APRS/OGN packet.
from ogn.parser import parse_aprs, parse_ogn_beacon from datetime import datetime beacon = parse_aprs("FLRDDDEAD>APRS,qAS,EDER:/114500h5029.86N/00956.98E'342/049/A=005524 id0ADDDEAD -454fpm -1.1rot 8.8dB 0e +51.2kHz gps4x5", reference_date=datetime(2016,1,1,11,46)) beacon.update(parse_ogn_beacon(beacon['comment']))
APRS-Client
Connect to OGN and display all incoming beacons.
from ogn.client import AprsClient from ogn.parser import parse_aprs, parse_ogn_beacon, ParseError def process_beacon(raw_message): if raw_message[0] == '#': print('Server Status: {}'.format(raw_message)) return try: beacon = parse_aprs(raw_message) beacon.update(parse_ogn_beacon(beacon['comment'])) print('Received {beacon_type} from {name}'.format(**beacon)) except ParseError as e: print('Error, {}'.format(e.message)) client = AprsClient(aprs_user='N0CALL') client.connect() try: client.run(callback=process_beacon, autoreconnect=True) except KeyboardInterrupt: print('\nStop ogn gateway') client.disconnect()
ogn-python
Repository: https://github.com/glidernet/ogn-python
A full featured gateway with built-in database.
It uses ogn-client to connect to OGN and to save all received beacons into a database with SQLAlchemy.
Other submodules process this data afterwards.
To schedule tasks like fetching data from ddb, Celery with Redis is used.
More examples can be found at ogn-python/wiki/Examples.
This example connects with the default database from ogn-python and shows the sum of received messages per receiver in the last hour:
from datetime import datetime, timedelta from sqlalchemy import func from ogn.model import AircraftBeacon from ogn.commands.dbutils import session def show_last_hour(): query = session.query(AircraftBeacon.receiver_name, func.count(AircraftBeacon.id)) \ .filter(AircraftBeacon.timestamp > (datetime.utcnow() - timedelta(hours=1))) \ .group_by(AircraftBeacon.receiver_name) \ .order_by(AircraftBeacon.receiver_name) for [receiver_name, message_count] in query.all(): print("{0:9s}: {1:5d}".format(receiver_name, message_count)) if __name__ == '__main__': show_last_hour()
Subscribing to OGN Beacons in Python with libfap (by tobiz)
Overview
This Python code illustrates how to access an OGN Beacon, read data lines and parse them into fields.
Parsing of fields is done by libfap.py which is available in github. A version of libfap.py is included here
since at the time of writing the github version has an error, this is being rectified (the libfap.py author has been advised). An amended version
of libfap.py is temporarily included to simplify running the example.
The code shows that to maintain a socket connection to an OGN Beacon a 'keepalive' must be sent at time intervals
less than 30 minutes. Failure to do so will result in zero length lines being returned. The socket can be configured
as 'Readonly' even if a 'keepalive' write is used.
This code is offered as an illustration of how to contact and access an OGN Beacon in Python, it is not definitive and may contain errors (although it has received limited testing). Corrections and/or modifications are welcome.
Developments: OGN-Flight-Logger
An application, based on this example is under development in github, see OGN Flight Logger V2. This application intends to log all flight durations and maximum altitudes attained during each flight of a specified fleet at a nominated location to an SQLite3 database. It is in development as an Eclipse PyDev project.
OGN Flight Logger is now operational and can be used to output on a daily basis a list of flights, their registration, start, finish times, duration and maximum altitude attained as a .csv file. V2 has been enhanced to include a set of track points for each flight as .gpx and/or .igc files for each flight; this now available in github. An option has been added to deleted flight and track .csv/.igc files after a specified number of days.
Dependencies
To run this example code the following Python modules and other libraries must be installed in addition to the standard ones:
Python
- ctypes
- libfap.py (the github version should be used when it has been corrected and the version in this example removed)
Other
- libfap (the 'c' library is used by libfap.py)
Notes concerning the code
The following notes concerning the fuction of the code are provided to assist in getting it to work.
- the git hub version of libfap.py loads libfap as 'libfap.so'. However it has been found that as of this wiki libfap (under ubuntu 14.04) installs as 'libfap.so.6', hence the change to the first few lines below (it is assumed no change is needed for use under Windows);
- in the main body of the example, see 'ogn_main.py', you should change the value of FILTER to whatever you require, see the APRS web pages for details. The example FILTER is for 25km radius of Sutton Bank in the UK and is used for illustration only.
- The socket is by default configured as a Blocking Read, hence on each read it blocks, (ie waits and the program is suspended) approximately 20s until the OGN Beacon sends a response. This is the case if no data traffic is being broadcast by the Beacon otherwise reads will be satisfied any time the beacon sends data.
- libfap.py is used to parse the data lines returned from the OGN Beacon, the example code illustrates how to do this but does nothing with the result. Note that a line returned by the Beacon may not have fields which libfap.py parses in which case these can be returned as pointers which if referenced raise ValueError Exceptions; these need to be handled appropriately.
- The 'keepalive' to keep the socket open are the lines "rtn = sock_file.write("#Python Example App\n\n"); sock_file.flush()". If flush() is not called the 'writes' are buffered and not sent, the result being the Beacon will start returning zero length lines after approx 30 minutes of being opened. The text on the write is arbitary except it must start with '#' and end with newline '\n'.
Acknowledgements
This example is based on that provided in github by Tom Hayward (kd7lxl). I would like to thank Tom for providing libfap.py and the original example.
For additional details on libfap.py go to github.
Python code example
# # Python code to show access to OGN Beacons # #-------------------- Corrected libfap.py Start------------------ from ctypes import * from datetime import datetime import socket import time try: # Try loading linux library # libfap = cdll.LoadLibrary('libfap.so') libfap = cdll.LoadLibrary('libfap.so.6') except OSError: try: # Try loading Mac OS X library libfap = cdll.LoadLibrary('libfap.dylib') except OSError: try: # This might find the dll for Windows, but it has not been tested libfap = cdll.LoadLibrary('libfap') except OSError: raise OSError, 'Could not find libfap.' time_t = c_long fap_error_code_t = c_int ( fapPACKET_NO, fapPACKET_SHORT, fapPACKET_NOBODY, fapSRCCALL_NOAX25, fapSRCCALL_BADCHARS, fapDSTPATH_TOOMANY, fapDSTCALL_NONE, fapDSTCALL_NOAX25, fapDIGICALL_NOAX25, fapDIGICALL_BADCHARS, fapTIMESTAMP_INV_LOC, fapTIMESTAMP_INV_OBJ, fapTIMESTAMP_INV_STA, fapTIMESTAMP_INV_GPGGA, fapTIMESTAMP_INV_GPGLL, fapPACKET_INVALID, fapNMEA_INV_CVAL, fapNMEA_LARGE_EW, fapNMEA_LARGE_NS, fapNMEA_INV_SIGN, fapNMEA_INV_CKSUM, fapGPRMC_FEWFIELDS, fapGPRMC_NOFIX, fapGPRMC_INV_TIME, fapGPRMC_INV_DATE, fapGPRMC_DATE_OUT, fapGPGGA_FEWFIELDS, fapGPGGA_NOFIX, fapGPGLL_FEWFIELDS, fapGPGLL_NOFIX, fapNMEA_UNSUPP, fapOBJ_SHORT, fapOBJ_INV, fapOBJ_DEC_ERR, fapITEM_SHORT, fapITEM_INV, fapITEM_DEC_ERR, fapLOC_SHORT, fapLOC_INV, fapLOC_LARGE, fapLOC_AMB_INV, fapMICE_SHORT, fapMICE_INV, fapMICE_INV_INFO, fapMICE_AMB_LARGE, fapMICE_AMB_INV, fapMICE_AMB_ODD, fapCOMP_INV, fapMSG_INV, fapWX_UNSUPP, fapUSER_UNSUPP, fapDX_INV_SRC, fapDX_INF_FREQ, fapDX_NO_DX, fapTLM_INV, fapTLM_LARGE, fapTLM_UNSUPP, fapEXP_UNSUPP, fapSYM_INV_TABLE, fapNOT_IMPLEMENTED, fapNMEA_NOFIELDS, fapNO_APRS ) = map(c_int, xrange(62)) # fap_packet_type_t ( fapLOCATION, fapOBJECT, fapITEM, fapMICE, fapNMEA, fapWX, fapMESSAGE, fapCAPABILITIES, fapSTATUS, fapTELEMETRY, fapTELEMETRY_MESSAGE, fapDX_SPOT, fapEXPERIMENTAL ) = map(c_int, xrange(13)) ( fapPOS_COMPRESSED, fapPOS_UNCOMPRESSED, fapPOS_MICE, fapPOS_NMEA ) = map(c_int, xrange(4)) class fap_wx_report_t(Structure): _fields_ = [ ('wind_gust', POINTER(c_double)), ('wind_dir', POINTER(c_uint)), ('wind_speed', POINTER(c_double)), ('temp', POINTER(c_double)), ('temp_in', POINTER(c_double)), ('rain_1h', POINTER(c_double)), ('rain_24h', POINTER(c_double)), ('rain_midnight', POINTER(c_double)), ('humidity', POINTER(c_uint)), ('humidity_in', POINTER(c_uint)), ('pressure', POINTER(c_double)), ('luminosity', POINTER(c_uint)), ('snow_24h', POINTER(c_double)), ('soft', c_char_p), ] class fap_telemetry_t(Structure): _fields_ = [ ('seq', c_uint), ('val1', c_double), ('val2', c_double), ('val3', c_double), ('val4', c_double), ('val5', c_double), ('bits', c_byte * 8), ] class fap_packet_t(Structure): _fields_ = [ ('error_code', POINTER(fap_error_code_t)), # POINTER(fap_error_code_t) # ('error_message', c_char_p), ('type', POINTER(c_int)), # POINTER(fap_packet_type_t) ('orig_packet', c_char_p), ('orig_packet_len', c_uint), ('header', c_char_p), ('body', c_char_p), ('body_len', c_uint), ('src_callsign', c_char_p), ('dst_callsign', c_char_p), ('path', POINTER(c_char_p)), ('path_len', c_uint), ('latitude', POINTER(c_double)), ('longitude', POINTER(c_double)), ('format', POINTER(c_int)), # POINTER(fap_pos_format_t) ('pos_resolution', POINTER(c_double)), ('pos_ambiguity', POINTER(c_uint)), ('dao_datum_byte', c_byte), ('altitude', POINTER(c_double)), ('course', POINTER(c_uint)), ('speed', POINTER(c_double)), ('symbol_table', c_byte), ('symbol_code', c_byte), ('messaging', POINTER(c_short)), ('destination', c_char_p), ('message',c_char_p ), ('message_ack', c_char_p), ('message_nack', c_char_p), ('message_id', c_char_p), ('comment', c_char_p), ('comment_len', c_uint), ('object_or_item_name', c_char_p), ('alive', POINTER(c_short)), ('gps_fix_status', POINTER(c_short)), ('radio_range', POINTER(c_uint)), ('phg', c_char_p), ('timestamp', POINTER(time_t)), ('raw_timestamp', c_char_p), # Added ('nmea_checksum_ok', POINTER(c_short)), ('wx_report', POINTER(fap_wx_report_t)), ('telemetry', POINTER(fap_telemetry_t)), ('messagebits', c_char_p), ('status', c_char_p), ('status_len', c_uint), ('capabilities', POINTER(c_char_p)), ('capabilities_len', c_uint), ] def get_timestamp(self): return datetime.fromtimestamp(self.timestamp[0]) def __repr__(self): return '%s(\'%s:%s\')' % (self.__class__.__name__, self.header, self.body) libfap.fap_parseaprs.restype = POINTER(fap_packet_t) libfap.fap_explain_error.argtypes = [fap_error_code_t] libfap.fap_explain_error.restype = c_char_p libfap.fap_mice_mbits_to_message.argtypes = [c_char_p] libfap.fap_mice_mbits_to_message.restype = c_char_p libfap.fap_distance.argtypes = [c_double, c_double, c_double, c_double] libfap.fap_distance.restype = c_double libfap.fap_direction.argtypes = [c_double, c_double, c_double, c_double] libfap.fap_direction.restype = c_double libfap.fap_count_digihops.argtypes = [POINTER(fap_packet_t)] libfap.fap_count_digihops.restype = c_int libfap.fap_check_ax25_call.argtypes = [c_char_p, c_short] libfap.fap_check_ax25_call.restype = c_char_p libfap.fap_kiss_to_tnc2.argtypes = [c_char_p, c_uint, c_char_p, c_uint, POINTER(c_uint)] libfap.fap_kiss_to_tnc2.restype = c_int libfap.fap_tnc2_to_kiss.argtypes = [c_char_p, c_uint, c_uint, c_char_p, c_uint] libfap.fap_tnc2_to_kiss.restype = c_int libfap.fap_ax25_to_tnc2.argtypes = [c_char_p, c_uint, c_char_p, c_uint] libfap.fap_ax25_to_tnc2.restype = c_int libfap.fap_tnc2_to_ax25.argtypes = [c_char_p, c_uint, c_char_p, c_uint] libfap.fap_tnc2_to_ax25.restype = c_int libfap.fap_free.argtypes = [POINTER(fap_packet_t)] #---------------------- Corrected libfap.py End---------------- #----------------------ogn_main.py start----------------------- print "Start Python_Test" # create socket & connect to server sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('aprs.glidernet.org', 14580)) print "Socket sock connected" # logon to OGN APRS network USER = "PythonEx" # Set to your username for sending data PASSCODE = -1 # Passcode = -1 is readonly, set to your passcode for useranme to send data FILTER_DETAILS = "filter r/+54.2288/-1.2096/25\n " login = 'user %s pass %s vers Python_Example 0.0.1 %s' % (USER, PASSCODE , FILTER_DETAILS) sock.send(login) # Make the connection to the server sock_file = sock.makefile() # Initialise libfap.py for parsing returned lines print "libfap_init" libfap.fap_init() keepalive_count = 1 keepalive_time = time.time() start_time = time.time() for i in range(1000000): #Loop for a long time with a count, illustrative only current_time = time.time() elapsed_time = current_time - keepalive_time if (current_time - keepalive_time) > 600: # keepalives every 10mins try: rtn = sock_file.write("#Python Example App\n\n") # Make sure keepalive gets sent. If not flushed then buffered sock_file.flush() run_time = time.time() - start_time print "Send keepalive no: ", keepalive_count, " After elapsed_time: ", int((current_time - keepalive_time)), " After runtime: ", int(run_time), " secs" keepalive_time = current_time keepalive_count = keepalive_count + 1 except Exception, e: print ('something\'s wrong with socket write. Exception type is %s' % (`e`)) print "In main loop. Count= ", i try: # Read packet string from socket packet_str = sock_file.readline() print "packet string length is: ", len(packet_str), " packet is: ", packet_str except socket.error: print "Socket error on readline" continue # Parse packet using libfap.py into fields to process, eg: packet = libfap.fap_parseaprs(packet_str, len(packet_str), 0) print 'Callsign is: %s' % (packet[0].src_callsign) print 'Packet body returned is: %s\n' % (packet[0].body) # A zero length line should not be return if keepalives are being sent # A zero length line will only be returned after ~30m if keepalives are not sent if len(packet_str) == 0: print "Read returns zero length string. Failure. Orderly closeout" break # Close libfap.py to avoid memory leak libfap.fap_cleanup() # close socket -- must be closed to avoid buffer overflow sock.shutdown(0) sock.close() print "Python Test Exit"
OGN to SilentWings interface
SWiface OGN to Silent Wings Studio interface (Python)