00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
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
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
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
00107
00108 battery_max = 12.7
00109 battery_warn = 10.0
00110 battery_min = 8.4
00111
00112
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
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
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
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
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
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
00193
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
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
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
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
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
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
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
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
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
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
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
00370 gcol = 20
00371 llwin.border(0)
00372 llwin.addstr(0, 2, "Status", curses.color_pair(1)|curses.A_BOLD)
00373
00374
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
00381
00382 drawFlightMode(4,2,maxy-3,data.flightMode)
00383
00384
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)
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
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
00420
00421
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()