monitor.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # AscTec Autopilot Console Monitor
00003 # Copyright (C) 2010, CCNY Robotics Lab
00004 # William Morris <morris@ee.ccny.cuny.edu>
00005 #
00006 # http://robotics.ccny.cuny.edu
00007 #
00008 # This program is free software: you can redistribute it and/or modify
00009 # it under the terms of the GNU General Public License as published by
00010 # the Free Software Foundation, either version 3 of the License, or
00011 # (at your option) any later version.
00012 #
00013 # This program is distributed in the hope that it will be useful,
00014 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 # GNU General Public License for more details.
00017 #
00018 # You should have received a copy of the GNU General Public License
00019 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 
00021 ################################
00022 # This code may not be pretty but is seems to work.
00023 # Commands
00024 # q = Quit
00025 # r = Record Bag File
00026 # f = Test flashing the screen
00027 # b = Test the terminal bell
00028 
00029 import roslib; roslib.load_manifest('asctec_mon')
00030 import rospy
00031 import curses
00032 import subprocess
00033 import os
00034 import signal
00035 
00036 from asctec_msgs.msg import LLStatus
00037 from asctec_msgs.msg import IMUCalcData
00038 from asctec_msgs.msg import GPSData
00039 
00040 myscreen = curses.initscr()
00041 curses.start_color()
00042 curses.halfdelay(1)
00043 curses.noecho()
00044 curses.curs_set(0)
00045 (maxx,maxy) = myscreen.getmaxyx()
00046 llwin = curses.newwin(11, maxy, maxx-11, 0)
00047 gpswin = curses.newwin(3, maxy, maxx-14, 0)
00048 recwin = curses.newwin(3, maxy, maxx-17, 0)
00049 imuwin = curses.newwin(maxx-17, maxy, 0, 0)
00050 alarm = 0
00051 alarm_count = 0
00052 alarm_interval = 10
00053 gps_lock = 1
00054 imu_lock = 1
00055 ll_lock = 1
00056 rec_status = 0
00057 rec_enable = 0
00058 rec_cmd = ["rosbag", "record", "-a", "-o","asctec"]
00059 rec_dir = str(os.environ['HOME'])+"/ros/bags"
00060 rec_process = None
00061 bag_name = None
00062 
00063 def drawSignedVal(r,c,w,val,val_max,val_min,big):
00064     center = int(w/2)
00065     if (val > val_max):
00066         val = val_max
00067     if (val < val_min):
00068         val = val_min
00069     if big:
00070         # Draw Top
00071         imuwin.addch(r, c, curses.ACS_ULCORNER)
00072         for n in range(c+1, c+w):
00073             if (n == c+center):
00074                 imuwin.addch(r, n, curses.ACS_TTEE)
00075             else:
00076                 imuwin.addch(r, n, curses.ACS_HLINE)
00077         imuwin.addch(r, c+w, curses.ACS_URCORNER)
00078         r = r + 1
00079 
00080     # Draw Middle
00081     imuwin.addch(r, c, curses.ACS_VLINE)
00082     bar = int(float(val / val_max * center))
00083     if (bar == 0):
00084         imuwin.addch(r, c+center, curses.ACS_VLINE)
00085     elif (bar >= 0):
00086         imuwin.addstr(r, c+center, " "*bar, curses.color_pair(4))
00087         imuwin.addch(r, c+center, curses.ACS_VLINE,curses.color_pair(4))
00088     else:
00089         imuwin.addstr(r, c+center+bar+1, " "*(-1*bar), curses.color_pair(4))
00090         imuwin.addch(r, c+center, curses.ACS_VLINE,curses.color_pair(4))
00091     imuwin.addch(r, c+w, curses.ACS_VLINE)
00092     r = r + 1
00093 
00094     if big:
00095         # Draw Bottom
00096         imuwin.addch(r, c, curses.ACS_LLCORNER)
00097         for n in range(c+1, c+w):
00098             if (n == c+center):
00099                 imuwin.addch(r, n, curses.ACS_BTEE)
00100             else:
00101                 imuwin.addch(r, n, curses.ACS_HLINE)
00102         imuwin.addch(r, c+w, curses.ACS_LRCORNER)
00103 
00104 def drawBattery(r,c,w,battery_val):
00105     global alarm
00106     # Battery Settings
00107     # Taken from http://en.wikipedia.org/wiki/Lithium-ion_polymer_battery
00108     battery_max = 12.7   # Maximum Voltage
00109     battery_warn = 10.0  # Warning Voltage
00110     battery_min = 8.4    # Minimum Voltage
00111 
00112     # Draw Top
00113     llwin.addch(r, c, curses.ACS_ULCORNER)
00114     for n in range(c+1, c+w):
00115         llwin.addch(r, n, curses.ACS_HLINE)
00116     llwin.addch(r, c+w, curses.ACS_URCORNER)
00117     r = r + 1
00118 
00119     # Draw Middle
00120     llwin.addch(r, c, curses.ACS_VLINE)
00121     b = int((battery_val - battery_min)/(battery_max-battery_min)*w)
00122     if battery_val > battery_warn: 
00123         alarm = 0
00124         llwin.addstr(r, c+1, " " * b, curses.color_pair(4))
00125     else:
00126         alarm = 1
00127         llwin.addstr(r, c+1, " " * b, curses.color_pair(5))
00128     llwin.addch(r, c+w, curses.ACS_VLINE)
00129     r = r + 1
00130 
00131     # Draw Bottom
00132     llwin.addch(r, c, curses.ACS_LLCORNER)
00133     for n in range(c+1, c+w):
00134         llwin.addch(r, n, curses.ACS_HLINE)
00135     llwin.addch(r, c+w, curses.ACS_LRCORNER)
00136 
00137 def drawStatusMode(r,c,w,data):
00138     # Draw Top
00139     size = int(w / 3)-1
00140     llwin.addch(r, c, curses.ACS_ULCORNER)
00141     for n in range(c+1, c+w):
00142         if ((n%(size+c))-2):
00143           llwin.addch(r, n, curses.ACS_HLINE)
00144         else:
00145           llwin.addch(r, n, curses.ACS_TTEE)
00146     llwin.addch(r, c+w, curses.ACS_URCORNER)
00147     r = r + 1
00148 
00149     size = size + 2
00150     pos = c+(size*0)
00151     llwin.addch(r, pos, curses.ACS_VLINE)
00152     if (data.compass_enabled):
00153         llwin.addstr(r,pos+1,"Compass",curses.color_pair(3)|curses.A_BOLD)
00154     else:
00155         llwin.addstr(r,pos+1,"Compass",curses.color_pair(0))
00156 
00157     pos = c+(size*1)
00158     llwin.addch(r, pos, curses.ACS_VLINE)
00159     llwin.addstr(r,pos+1,"Flight Time: "+str(data.up_time)+" sec",curses.color_pair(0))
00160 
00161     pos = c+(size*2)
00162     llwin.addch(r, pos, curses.ACS_VLINE)
00163     llwin.addstr(r,pos+1,"CPU: "+str(data.cpu_load),curses.color_pair(0))
00164 
00165     pos = c+w
00166     llwin.addch(r, pos, curses.ACS_VLINE)
00167     r = r + 1
00168 
00169     # Draw Bottom
00170     size = int(w / 3)-1
00171     llwin.addch(r, c, curses.ACS_LLCORNER)
00172     for n in range(c+1, c+w):
00173         if ((n%(size+c))-2):
00174           llwin.addch(r, n, curses.ACS_HLINE)
00175         else:
00176           llwin.addch(r, n, curses.ACS_BTEE)
00177     llwin.addch(r, c+w, curses.ACS_LRCORNER)
00178 
00179 def drawFlightMode(r,c,w,flightMode):
00180     # Draw Top
00181     size = int(w / 5)-2
00182     llwin.addch(r, c, curses.ACS_ULCORNER)
00183     for n in range(c+1, c+w):
00184         if (((n%(size+c))-2) or (n/size+c) > 6):
00185           llwin.addch(r, n, curses.ACS_HLINE)
00186         else:
00187           llwin.addch(r, n, curses.ACS_TTEE)
00188     llwin.addch(r, c+w, curses.ACS_URCORNER)
00189     r = r + 1
00190 
00191     size = size + 2
00192     # There are 5 Flight Modes but the bit index of the serial active
00193     # mode is currently unknown
00194     pos = c+(size*0)
00195     llwin.addch(r, pos, curses.ACS_VLINE)
00196     if ((flightMode|0b01111111)!=0b11111111):
00197         llwin.addstr(r,pos+1,"Emergency",curses.color_pair(0))
00198     else:
00199         llwin.addstr(r,pos+1,"Emergency",curses.color_pair(2)|curses.A_BOLD)
00200 
00201     pos = c+(size*1)
00202     llwin.addch(r, pos, curses.ACS_VLINE)
00203     if ((flightMode|0b11111101)!=0b11111111):
00204         llwin.addstr(r,pos+1,"Height Control",curses.color_pair(0))
00205     else:
00206         llwin.addstr(r,pos+1,"Height Control",curses.color_pair(3)|curses.A_BOLD)
00207 
00208     pos = c+(size*2)
00209     llwin.addch(r, pos, curses.ACS_VLINE)
00210     if ((flightMode|0b11111011)!=0b11111111):
00211         llwin.addstr(r,pos+1,"GPS Mode",curses.color_pair(0))
00212     else:
00213         llwin.addstr(r,pos+1,"GPS Mode",curses.color_pair(3)|curses.A_BOLD)
00214 
00215     pos = c+(size*3)
00216     llwin.addch(r, pos, curses.ACS_VLINE)
00217     if ((flightMode|0b11011111)!=0b11111111):
00218         llwin.addstr(r,pos+1,"Serial Enable",curses.color_pair(0))
00219     else:
00220         llwin.addstr(r,pos+1,"Serial Enable",curses.color_pair(3)|curses.A_BOLD)
00221 
00222     pos = c+(size*4)
00223     llwin.addch(r, pos, curses.ACS_VLINE)
00224     # FIXME: This is probably the wrong bitmask
00225     if ((flightMode|0b10111111)!=0b11111111):
00226         llwin.addstr(r,pos+1,"Serial Active",curses.color_pair(0))
00227     else:
00228         llwin.addstr(r,pos+1,"Serial Active",curses.color_pair(3)|curses.A_BOLD)
00229     pos = c+w
00230     llwin.addch(r, pos, curses.ACS_VLINE)
00231     r = r + 1
00232 
00233     # Draw Bottom
00234     size = int(w / 5)-2
00235     llwin.addch(r, c, curses.ACS_LLCORNER)
00236     for n in range(c+1, c+w):
00237         if (((n%(size+c))-2) or (n/size+c) > 6):
00238           llwin.addch(r, n, curses.ACS_HLINE)
00239         else:
00240           llwin.addch(r, n, curses.ACS_BTEE)
00241     llwin.addch(r, c+w, curses.ACS_LRCORNER)
00242 
00243 def record_update():
00244     global rec_status
00245     global rec_cmd
00246     global rec_dir
00247     global rec_process
00248     global rec_enable
00249     global bag_name
00250 
00251     recwin.clear()
00252     if rec_status:
00253         recattr = curses.color_pair(2)
00254     else:
00255         recattr = curses.color_pair(0)
00256     recwin.attrset(recattr)
00257     (rec_maxx,rec_maxy) = recwin.getmaxyx()
00258     rec_maxy = rec_maxy - 2 # remove space for left and right border
00259     recwin.border(0)
00260     
00261     if rec_enable != rec_status:
00262         if rec_enable:
00263             rec_process = subprocess.Popen(rec_cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=rec_dir)
00264         else:
00265             bag_name = None
00266         rec_status = rec_enable
00267           
00268     if rec_status:
00269         if (bag_name == None or bag_name == ''):
00270             process = subprocess.Popen(['lsof -c record -Fn -- | grep active | cut -c2-'], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=rec_dir)
00271             bag_name = process.stdout.readline().rstrip()
00272         recwin.addstr(0,2,"Flight Data Recording",curses.color_pair(2)|curses.A_BOLD)
00273         recwin.attrset(curses.color_pair(0))
00274         recwin.addstr(1, 2, "Filename: ")
00275         recwin.addstr(1,12,bag_name)
00276     else:
00277         if (rec_process != None):
00278             rec_process.send_signal(signal.SIGINT)
00279         recwin.addstr(0,2,"Flight Data Recorder",curses.color_pair(1)|curses.A_BOLD)
00280         recwin.attrset(curses.color_pair(0))
00281         recwin.addstr(1, 2, "Command: ")
00282         recwin.addstr(1,11,' '.join(rec_cmd))
00283     if rec_process != None:
00284       if rec_process.poll() == 0:
00285         rec_process = None
00286 def gps_callback(data):
00287     global gps_lock
00288     gps_lock = 1
00289     gpswin.clear()
00290     (gps_maxx,gps_maxy) = imuwin.getmaxyx()
00291     gps_maxy = gps_maxy - 2 # remove space for left and right border
00292     gcol = 25
00293     gpswin.border(0)
00294     gpswin.addstr(0, 2, "GPS", curses.color_pair(1)|curses.A_BOLD)
00295     lat_val = float(data.latitude)/float(10**7)
00296     gpswin.addstr(1, 2, 'Lat: {0:+012.7f}'.format(lat_val))
00297     lon_val = float(data.longitude)/float(10**7)
00298     gpswin.addstr(1, 21, 'Lon: {0:+012.7f}'.format(lon_val))
00299     height_val = float(data.height)/1000.0
00300     gpswin.addstr(1, 40, 'Height: {0: 7.3f}m'.format(height_val))
00301     heading_val = float(data.heading)/1000.0
00302     gpswin.addstr(1, 58, 'Heading: {0: 7.3f}'.format(heading_val))
00303     gps_lock = 0
00304 
00305 def imu_callback(data):
00306     global imu_lock
00307     imu_lock = 1
00308     imuwin.clear()
00309     (imu_maxx,imu_maxy) = imuwin.getmaxyx()
00310     imu_maxy = imu_maxy - 2 # remove space for left and right border
00311     gcol = 25
00312     imuwin.border(0)
00313     imuwin.addstr(0, 2, "AscTec Quadrotor Console Monitor", curses.color_pair(1)|curses.A_BOLD)
00314 
00315     pos = 1
00316     if (imu_maxx > 16):
00317         pos_inc = 3
00318         big = 1
00319     else:
00320         pos_inc = 1
00321         big = 0
00322 
00323     # Height Graph
00324     ################################
00325     height = float(data.height)/1000.0
00326     imuwin.addstr(pos+big, 2, 'Height:    {0: 8.3f}m'.format(height))
00327     drawSignedVal(pos,gcol,imu_maxy-(gcol+1),height,10.0,-10.0,big)
00328     pos = pos + pos_inc
00329     
00330     # Roll Graph
00331     ################################
00332     roll = float(data.angle_roll)/1000.0
00333     imuwin.addstr(pos+big, 2, "Roll:      %+08.3f"%roll)
00334     imuwin.addch(pos+big, 21, curses.ACS_DEGREE)
00335     drawSignedVal(pos,gcol,imu_maxy-(gcol+1),roll,90.0,-90.0,big)
00336     pos = pos + pos_inc
00337 
00338     # Pitch Graph
00339     ################################
00340     pitch = float(data.angle_nick)/1000.0
00341     imuwin.addstr(pos+big, 2, "Pitch:     %+08.3f"%pitch)
00342     imuwin.addch(pos+big, 21, curses.ACS_DEGREE)
00343     drawSignedVal(pos,gcol,imu_maxy-(gcol+1),pitch,180.0,-180.0,big)
00344     pos = pos + pos_inc
00345 
00346     # Yaw Graph
00347     ################################
00348     yaw = float(data.angle_yaw)/1000.0 -180
00349     imuwin.addstr(pos+big, 2, "Fused Yaw: %+08.3f"%yaw)
00350     imuwin.addch(pos+big, 21, curses.ACS_DEGREE)
00351     drawSignedVal(pos,gcol,imu_maxy-(gcol+1),yaw,180.0,-180.0,big)
00352     pos = pos + pos_inc
00353 
00354     # Compass Graph
00355     ################################
00356     mag = float(data.mag_heading)/1000.0 -180
00357     imuwin.addstr(pos+big, 2, "Compass:   %+08.3f"%mag)
00358     imuwin.addch(pos+big, 21, curses.ACS_DEGREE)
00359     drawSignedVal(pos,gcol,imu_maxy-(gcol+1),mag,180.0,-180.0,big)
00360     pos = pos + pos_inc
00361 
00362     imu_lock = 0
00363 
00364 def callback(data):
00365     global ll_lock
00366     ll_lock = 1
00367     llwin.clear()
00368     (maxx,maxy) = llwin.getmaxyx()
00369     maxy = maxy - 2 # remove space for left and right border
00370     gcol = 20
00371     llwin.border(0)
00372     llwin.addstr(0, 2, "Status", curses.color_pair(1)|curses.A_BOLD)
00373 
00374     # Battery Monitor
00375     ################################
00376     battery_val = float(data.battery_voltage_1)/1000.0
00377     llwin.addstr(2, 2, 'Battery: {0:.3f}V'.format(battery_val))
00378     drawBattery(1,gcol,maxy-(gcol+1),float(data.battery_voltage_1)/1000)
00379 
00380     # Flight Mode Monitor
00381     ################################
00382     drawFlightMode(4,2,maxy-3,data.flightMode)
00383 
00384     # Status Monitor
00385     ################################
00386     drawStatusMode(7,2,maxy-3,data)
00387 
00388     ll_lock = 0
00389 
00390 def listener():
00391     global imuwin, maxx, maxy
00392     global alarm, alarm_count, alarm_interval
00393     global rec_enable
00394 
00395     rospy.init_node('asctec_monitor')
00396     rospy.Subscriber("asctec/LL_STATUS", LLStatus, callback)
00397     rospy.Subscriber("asctec/IMU_CALCDATA", IMUCalcData, imu_callback)
00398     rospy.Subscriber("asctec/GPS_DATA", GPSData, gps_callback)
00399     curses.init_pair(1, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
00400     curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
00401     curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK)
00402     curses.init_pair(4, curses.COLOR_BLACK, curses.COLOR_WHITE)
00403     curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_RED)
00404     r = rospy.Rate(10) # Hz
00405     (maxx,maxy) = myscreen.getmaxyx()
00406     while not rospy.is_shutdown():
00407         c = myscreen.getch()
00408         if c == ord('f'): curses.flash()
00409         elif c == ord('b'): curses.beep()
00410         elif c == ord('r'): rec_enable = not rec_enable
00411         elif c == ord('q'): break  # Exit the while()
00412         elif c == curses.KEY_HOME: x = y = 0
00413         (current_maxx,current_maxy) = myscreen.getmaxyx()
00414         if (current_maxx != maxx or current_maxy != maxy):
00415             (maxx,maxy) = myscreen.getmaxyx()
00416             gpswin.mvwin(maxx-14, 0)
00417             llwin.mvwin(maxx-11, 0)
00418             imuwin = curses.newwin(maxx-14, maxy, 0, 0)
00419             #imuwin.refresh()
00420             #llwin.refresh()
00421             #gpswin.refresh()
00422         if (alarm):
00423             alarm_count = alarm_count + 1
00424             if (alarm_count == alarm_interval):
00425                 alarm_count = 0
00426                 curses.flash()
00427                 curses.beep()
00428         if (not gps_lock):
00429             gpswin.refresh()
00430         if (not imu_lock):
00431             imuwin.refresh()
00432         if (not ll_lock):
00433             llwin.refresh()
00434         record_update()
00435         recwin.refresh()
00436         r.sleep()
00437     curses.nocbreak(); myscreen.keypad(0); curses.echo(); curses.curs_set(1)
00438     curses.endwin()
00439 
00440 if __name__ == '__main__':
00441     listener()


asctec_mon
Author(s): William Morris
autogenerated on Tue Jan 7 2014 11:04:22