Advertisement
Guest User

hab_alert

a guest
Jan 9th, 2017
254
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.89 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. import sys
  4. import math
  5. import json
  6. from time import sleep, time
  7. import urllib2
  8. from pprint import pprint, pformat
  9. import traceback
  10. import datetime
  11. import smtplib
  12.  
  13. ### USER SETTINGS
  14. # GPS location
  15. USER_LAT = 52.0
  16. USER_LON = 21.0
  17. USER_ALT = 100
  18. # minimal distance - kilometers
  19. USER_DIST = 700
  20. # days between re-notification for particular payload
  21. USER_CACHE_DAYS = 2
  22. # email notification
  23. USER_EMAIL_ONOFF = 1
  24. USER_EMAIL_FROMADDR = "yourname@gmail.com"
  25. USER_EMAIL_USERNAME = 'yourname'
  26. USER_EMAIL_TOADDR  = 'yourname@gmail.com'
  27. USER_EMAIL_PWD = 'yourpassword'
  28. # smsapi.pl notification - https://github.com/smsapi/smsapi-python-client
  29. USER_SMS_ONOFF = 1
  30. USER_SMS_USERNAME = 'yourname@gmail.com'
  31. USER_SMS_PWD = 'yourpassword'
  32. USER_SMS_RECIPENT = '601123456'
  33. # slack notification
  34. USER_SLACK_ONOFF = 1
  35. USER_SLACK_TOKEN = '..........'
  36. ### end USER SETTINGS
  37.  
  38.  
  39. TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
  40. DATA_FILE = sys.argv[0] + '.reports'
  41.  
  42.  
  43. ################################################################################
  44. ################################################################################
  45. ################################################################################
  46.  
  47. def google_elevation(lat, lon):
  48.     elevation_json = urllib2.urlopen('http://maps.googleapis.com/maps/api/elevation/json?locations=' + str(lat) + ',' + str(lon) + '&sensor=true')
  49.     return round(json.load(elevation_json)['results'][0]['elevation'])
  50.  
  51. def getMyPos():
  52.     return USER_LAT, USER_LON, USER_ALT
  53.  
  54. def calcDistanceBearingElevation(lat1, lon1, alt1, lat2, lon2, alt2):
  55.     '''
  56.     based on:
  57.     https://github.com/jamescoxon/dl-fldigi/blob/master/src/dl_fldigi/location.cxx
  58.     '''
  59.     def deg2rad(i_deg):
  60.         return i_deg/180.0*math.pi
  61.     def rad2deg(i_rad):
  62.         return i_rad*180/math.pi
  63.  
  64.     radius = 6371; # km
  65.     lat1 = deg2rad(lat1);
  66.     lon1 = deg2rad(lon1);
  67.     lat2 = deg2rad(lat2);
  68.     lon2 = deg2rad(lon2);
  69.     d_lon = lon2 - lon1;
  70.     sa = math.cos(lat2) * math.sin(d_lon);
  71.     sb = (math.cos(lat1) * math.sin(lat2)) - (math.sin(lat1) * math.cos(lat2) * math.cos(d_lon));
  72.     bearing = math.atan2(sa, sb);
  73.     bearing = rad2deg(bearing);
  74.     if (bearing < 0):
  75.         bearing += 360;
  76.     bearing = round(bearing);
  77.  
  78.     aa = math.sqrt((sa * sa) + (sb * sb));
  79.     ab = (math.sin(lat1) * math.sin(lat2)) + (math.cos(lat1) * math.cos(lat2) * math.cos(d_lon));
  80.     angle_at_centre = math.atan2(aa, ab);
  81.     great_circle_distance = angle_at_centre * radius;
  82.     # great_circle_distance = great_circle_distance.toFixed(2);
  83.     ta = radius + alt1 * 0.001; # m to km
  84.     tb = radius + alt2 * 0.001; # m to km
  85.     ea = (math.cos(angle_at_centre) * tb) - ta;
  86.     eb = math.sin(angle_at_centre) * tb;
  87.     elevation = math.atan2(ea, eb);
  88.     elevation = rad2deg(elevation);
  89.     # elevation = elevation.toFixed(1);
  90.  
  91.     distance = math.sqrt((ta * ta) + (tb * tb) - 2 * tb * ta * math.cos(angle_at_centre));
  92.     # distance = distance.toFixed(2);
  93.  
  94.     result = {
  95.                 "distance" : round(distance, 1),
  96.                 "great_circle_distance" : round(great_circle_distance, 1),
  97.                 "bearing" : bearing,
  98.                 "elevation" : round(elevation, 2)
  99.             }
  100.  
  101.     return result
  102.  
  103. ################################################################################
  104. ################################################################################
  105. ################################################################################
  106.  
  107.  
  108. def getFlights():
  109.     flights_json = urllib2.urlopen('http://habitat.habhub.org/habitat/_design/flight/_view/end_start_including_payloads?startkey=[' + str(int(time())) + ']&include_docs=True')
  110.     flights = json.load(flights_json)['rows']
  111.     res = [ f for f in flights if f['doc']['type'] == 'flight' ]
  112.     return res
  113.  
  114. def getTelemetry(i_payloadName, max_positions=1 ):
  115.     data_url = "http://spacenear.us/tracker/datanew.php";
  116.     data_str = "mode=position&type=positions&format=json&max_positions=" + str(max_positions) + "&position_id=0&vehicles=" + urllib2.quote(i_payloadName);
  117.     _url = data_url + '?' + data_str
  118.     res = urllib2.urlopen(_url)
  119.     res = json.load(res)
  120.     return res['positions']['position']
  121.  
  122. def getPayloads(i_flights):
  123.     all_docs = urllib2.urlopen('http://habitat.habhub.org/habitat/_design/flight/_view/end_start_including_payloads?startkey=[' + str(int(time())) + ']&include_docs=True')
  124.     all_docs = json.load(all_docs)['rows']
  125.  
  126.     flyingPayloadsIds = []
  127.     for f in i_flights:
  128.         flyingPayloadsIds.extend( f['doc']['payloads'] )
  129.  
  130.     res = []
  131.     for f in all_docs:
  132.         if f['doc']['type'] == 'payload_configuration' and f['doc']['_id'] in flyingPayloadsIds:
  133.             f['telemetry'] = getTelemetry(f['doc']['name'],1)
  134.             res.append(f)
  135.     return res
  136.  
  137.  
  138. def getNearPayloads(i_dist=700, i_pos=None, i_payloads=None):
  139.     '''
  140.     i_dist - minimal distance for a payload to be considered
  141.     i_pos - USER location tuple (lat,lon,alt)
  142.     '''
  143.     if not i_pos:
  144.         i_pos = getMyPos()
  145.     if not i_payloads:
  146.         i_payloads = getPayloads(getFlights())
  147.  
  148.     res = []
  149.     for payload in i_payloads:
  150.         telemetry = payload['telemetry'][0]
  151.         d = calcDistanceBearingElevation(
  152.                         i_pos[0], i_pos[1], i_pos[2],
  153.                         float(telemetry['gps_lat']), float(telemetry['gps_lon']), float(telemetry['gps_alt'])
  154.                     )
  155.         if d['distance'] < i_dist:
  156.             #add distance to payload
  157.             payload['distance'] = d
  158.             res.append(payload)
  159.     return res
  160.  
  161. ################################################################################
  162. ################################################################################
  163. ################################################################################
  164.  
  165. def deleteOldEntries(i_fname = None):
  166.     '''
  167.     remove old entries from cache
  168.     '''
  169.     if not i_fname:
  170.         i_fname = DATA_FILE
  171.  
  172.     data = json.loads( open(DATA_FILE).read() )
  173.     keys_to_remove = []
  174.     for p in data:
  175.         p_time = datetime.datetime.strptime(data[p]['time'], TIME_FORMAT)
  176.         delta_time = datetime.datetime.utcnow() - p_time
  177.         if delta_time.days > USER_CACHE_DAYS:
  178.             keys_to_remove.append(p)
  179.  
  180.     for ktr in keys_to_remove:
  181.         del data[ktr]
  182.  
  183.     open(DATA_FILE, 'w').write( json.dumps(data,indent=4,sort_keys=True) )
  184.  
  185. def getReportTimes():
  186.     try:        data = json.loads( open(DATA_FILE).read() )
  187.     except:     data = {}
  188.     return      data
  189.  
  190. def lastReportTime(i_payload):
  191.     try:
  192.         all_reports = getReportTimes()
  193.         payload_name = i_payload['doc']['name']
  194.         if payload_name in all_reports:
  195.             lrt = all_reports[payload_name]['time']
  196.             lrt = datetime.datetime.strptime(lrt, TIME_FORMAT)
  197.             return lrt
  198.     except:
  199.         print traceback.format_exc()
  200.         return None
  201.  
  202. ################################################################################
  203. ################################################################################
  204. ################################################################################
  205.  
  206. def niceTelemetry(i_payload):
  207.     msg_data = {}
  208.     msg_data['name'] = i_payload['doc']['name']
  209.     msg_data['dis'] = i_payload['distance']
  210.  
  211.     msg_data['lat'] = i_payload['telemetry'][0]['gps_lat']
  212.     msg_data['lon'] = i_payload['telemetry'][0]['gps_lon']
  213.     msg_data['alt'] = i_payload['telemetry'][0]['gps_alt']
  214.  
  215.     return pformat(msg_data)
  216.  
  217.  
  218. def send_mail(subj, message):
  219.     msg = "\r\n".join([
  220.         "From: %s" % USER_EMAIL_USERNAME,
  221.         "To: %s" % USER_EMAIL_TOADDR,
  222.         "Subject: %s" % subj,
  223.         "",
  224.         message
  225.         ])
  226.  
  227.     server = smtplib.SMTP('smtp.gmail.com:587')
  228.     server.starttls()
  229.     server.login(USER_EMAIL_USERNAME,USER_EMAIL_PWD)
  230.     server.sendmail(USER_EMAIL_FROMADDR, USER_EMAIL_TOADDR, msg)
  231.     server.quit()
  232.  
  233. def notify_email(i_payload):
  234.     if not USER_EMAIL_ONOFF:
  235.         return
  236.  
  237.     subj = 'hab_alert: ' + i_payload['doc']['name']
  238.     message = niceTelemetry(i_payload)
  239.     send_mail(subj, message)
  240.  
  241. def notify_sms(i_payload):
  242.     if not USER_SMS_ONOFF:
  243.         return
  244.  
  245.     try:
  246.         from smsapi.client import SmsAPI
  247.     except ImportError:
  248.         return
  249.  
  250.     api = SmsAPI()
  251.  
  252.     api.set_username(USER_SMS_USERNAME)
  253.     api.set_password(USER_SMS_PWD)
  254.     api.service('sms').action('send')
  255.  
  256.     msg = 'hab_alert: ' + niceTelemetry(i_payload)
  257.     api.set_content(msg)
  258.     api.set_to(USER_SMS_RECIPENT)
  259.  
  260.     result = api.execute()
  261.     #for r in result:       print(r.id, r.points, r.status)
  262.  
  263. def notifySlack(i_payload,slack_channel="#info"):
  264.     if not USER_SLACK_ONOFF:
  265.         return
  266.  
  267.     try:
  268.         from slackclient import SlackClient
  269.     except ImportError:
  270.         return
  271.  
  272.     subj = 'hab_alert: ' + i_payload['doc']['name']
  273.     message = niceTelemetry(i_payload)
  274.  
  275.     try:
  276.         sc = SlackClient(USER_SLACK_TOKEN)
  277.         try:
  278.             sc.api_call("chat.postMessage", channel=slack_channel, text=message, username='wintermute')
  279.         except:
  280.             print "Unable to send slack notification!"
  281.     except:
  282.         print "notifyUser failed"
  283.         print traceback.format_exc()
  284.  
  285. ################################################################################
  286. ################################################################################
  287. ################################################################################
  288.  
  289. def reportPayload(i_payload):
  290.     all_reports = getReportTimes()
  291.     p_name = i_payload['doc']['name']
  292.  
  293.     # already notified
  294.     if p_name in all_reports:
  295.         return
  296.  
  297.     #
  298.     notify_email(i_payload)
  299.     notify_sms(i_payload)
  300.     notifySlack(i_payload)
  301.  
  302.     # save to reports file
  303.     all_reports = getReportTimes()
  304.     all_reports[p_name] = {}
  305.     all_reports[p_name]['time'] = datetime.datetime.utcnow().strftime(TIME_FORMAT)
  306.     all_reports[p_name]['distance'] = i_payload['distance']
  307.     all_reports[p_name]['gps_lat'] = i_payload['telemetry'][0]['gps_lat']
  308.     all_reports[p_name]['gps_lon'] = i_payload['telemetry'][0]['gps_lon']
  309.     all_reports[p_name]['gps_alt'] = i_payload['telemetry'][0]['gps_alt']
  310.     try:
  311.         json_data = json.dumps(all_reports, indent=4,sort_keys=True)
  312.         open(DATA_FILE, 'w').write(json_data)
  313.     except:
  314.         print traceback.format_exc()
  315.         print "Error writing ", DATA_FILE
  316.  
  317. ################################################################################
  318. ################################################################################
  319. ################################################################################
  320.  
  321. def main():
  322.     try:
  323.         deleteOldEntries()
  324.     except:
  325.         print traceback.format_exc()
  326.         print "Error deleting old entries"
  327.  
  328.     try:
  329.         payloads = getNearPayloads(i_dist=USER_DIST)
  330.     except:
  331.         print traceback.format_exc()
  332.         print "Error getting near payloads"
  333.         return
  334.  
  335.     for p in payloads:
  336.         reportPayload(p)
  337.  
  338. if __name__ == '__main__':
  339.     main()
  340.  
  341.  
  342.  
  343.     '''
  344.     {'distance': {'bearing': 244.0,
  345.              'distance': 536.3,
  346.              'elevation': -1.82,
  347.              'great_circle_distance': 536.2},
  348. u'doc': {u'_id': u'120d309024cfd89edfcd528fa7873013',
  349.          u'_rev': u'1-a90f121c3cbd6b796be3b4b7dc466b83',
  350.          u'metadata': {u'description': u'0001'},
  351.          u'name': u'SP3OSJ',
  352.          u'sentences': [{u'callsign': u'SP3OSJ',
  353.                          u'checksum': u'crc16-ccitt',
  354.                          u'description': u'SP3OSJ Long flight',
  355.                          u'fields': [{u'name': u'sentence_id',
  356.                                       u'sensor': u'base.ascii_int'},
  357.                                      {u'name': u'time',
  358.                                       u'sensor': u'stdtelem.time'},
  359.                                      {u'format': u'dd.dddd',
  360.                                       u'name': u'latitude',
  361.                                       u'sensor': u'stdtelem.coordinate'},
  362.                                      {u'format': u'dd.dddd',
  363.                                       u'name': u'longitude',
  364.                                       u'sensor': u'stdtelem.coordinate'},
  365.                                      {u'name': u'altitude',
  366.                                       u'sensor': u'base.ascii_int'},
  367.                                      {u'name': u'temperature_internal',
  368.                                       u'sensor': u'base.ascii_int'},
  369.                                      {u'name': u'satellites',
  370.                                       u'sensor': u'base.ascii_int'},
  371.                                      {u'name': u'km',
  372.                                       u'sensor': u'base.ascii_float'},
  373.                                      {u'name': u'battery',
  374.                                       u'sensor': u'base.ascii_float'},
  375.                                      {u'name': u'solar',
  376.                                       u'sensor': u'base.ascii_float'}],
  377.                          u'protocol': u'UKHAS'}],
  378.          u'time_created': u'2013-09-26T21:34:32+02:00',
  379.          u'transmissions': [{u'baud': 100,
  380.                              u'description': u'SP3OSJ_100bd',
  381.                              u'encoding': u'ASCII-7',
  382.                              u'frequency': 437700000,
  383.                              u'mode': u'USB',
  384.                              u'modulation': u'RTTY',
  385.                              u'parity': u'none',
  386.                              u'shift': 470,
  387.                              u'stop': 2}],
  388.          u'type': u'payload_configuration'},
  389. u'id': u'1da797e765edc82a75f0093b479bcff8',
  390. u'key': [1484265599, 1483660800, u'1da797e765edc82a75f0093b479bcff8', 1],
  391. u'value': {u'_id': u'120d309024cfd89edfcd528fa7873013'}}
  392. '''
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement