@@ -212,6 +212,22 @@ def check_events(self):
212212 if event .x < xres and event .y < yres and event .x >= 0 and event .y >= 0 :
213213 self .siyi .spot_temp = self .get_pixel_temp (event .x , event .y )
214214 self .update_title ()
215+ x = (2 * event .x / float (xres ))- 1.0
216+ y = (2 * event .y / float (yres ))- 1.0
217+ aspect_ratio = float (xres )/ yres
218+ slant_range = self .siyi .get_slantrange (x ,y ,self .siyi .siyi_settings .thermal_fov ,aspect_ratio )
219+ if slant_range is None :
220+ return
221+ latlonalt = self .siyi .get_latlonalt (slant_range , x , y ,
222+ self .siyi .siyi_settings .thermal_fov ,
223+ aspect_ratio )
224+ if latlonalt is None :
225+ return
226+ latlon = (latlonalt [0 ],latlonalt [1 ])
227+ self .siyi .mpstate .map .add_object (mp_slipmap .SlipIcon ('SIYIClick' , latlon ,
228+ self .siyi .click_icon , layer = 'SIYI' , rotation = 0 , follow = False ))
229+
230+
215231
216232
217233class SIYIModule (mp_module .MPModule ):
@@ -249,6 +265,7 @@ def __init__(self, mpstate):
249265 ('temp_hz' , float , 5 ),
250266 ('thermal_port' , int , 7002 ),
251267 ('logfile' , str , 'SIYI_log.bin' ),
268+ ('thermal_fov' , float , 18 ),
252269 ])
253270 self .add_completion_function ('(SIYISETTING)' ,
254271 self .siyi_settings .completion )
@@ -279,6 +296,7 @@ def __init__(self, mpstate):
279296 self .target_pos = None
280297 self .last_map_ROI = None
281298 self .icon = self .mpstate .map .icon ('camera-small-red.png' )
299+ self .click_icon = self .mpstate .map .icon ('flag.png' )
282300 self .last_target_send = time .time ()
283301 self .last_rate_display = time .time ()
284302 self .yaw_controller = PI_controller (self .siyi_settings , 'yaw_gain_P' , 'yaw_gain_I' , 'yaw_gain_IMAX' )
@@ -684,7 +702,7 @@ def update_title(self):
684702 def update_status (self ):
685703 if self .attitude is None :
686704 return
687- self .console .set_status ('SIYI' , 'SIYI (%.1f,%.1f,%.1f) rf =%.1f' % (
705+ self .console .set_status ('SIYI' , 'SIYI (%.1f,%.1f,%.1f) SR =%.1f' % (
688706 self .attitude [0 ], self .attitude [1 ], self .attitude [2 ],
689707 self .rf_dist ), row = 6 )
690708 if self .tmin is not None :
@@ -733,18 +751,81 @@ def get_gimbal_attitude(self):
733751 roll = self .attitude [0 ]
734752 return yaw , pitch , roll
735753
736- def get_latlon (self , slant_range ):
737- '''get lat/lon given vehicle orientation, camera orientation and slant range'''
754+ def get_slantrange (self ,x ,y ,FOV ,aspect_ratio ):
755+ '''
756+ get range to ground
757+ x and y are from -1 to 1, relative to center of camera view
758+ '''
759+ if self .rf_dist > 0 :
760+ # use rangefinder if possible
761+ return self .rf_dist
762+ gpi = self .master .messages .get ('GLOBAL_POSITION_INT' ,None )
763+ if not gpi :
764+ return None
765+ (lat ,lon ,alt ) = gpi .lat * 1.0e-7 ,gpi .lon * 1.0e-7 ,gpi .alt * 1.0e-3
766+ ground_alt = self .module ('terrain' ).ElevationModel .GetElevation (lat , lon )
767+ if alt <= ground_alt :
768+ return None
769+ if self .attitude is None :
770+ return None
771+ pitch = self .attitude [1 ]
772+ if pitch >= 0 :
773+ return None
774+ pitch -= y * FOV * 0.5 / aspect_ratio
775+ pitch = min (pitch , - 1 )
776+
777+ # start with flat earth
778+ sin_pitch = math .sin (abs (math .radians (pitch )))
779+ sr = (alt - ground_alt ) / sin_pitch
780+
781+ # iterate to make more accurate
782+ for i in range (3 ):
783+ (lat2 ,lon2 ,alt2 ) = self .get_latlonalt (sr ,x ,y ,FOV ,aspect_ratio )
784+ ground_alt2 = self .module ('terrain' ).ElevationModel .GetElevation (lat2 , lon2 )
785+ # adjust for height at this point
786+ sr += (alt2 - ground_alt2 ) / sin_pitch
787+ return sr
788+
789+
790+
791+ def get_view_vector (self , x , y , FOV , aspect_ratio ):
792+ '''
793+ get ground lat/lon given vehicle orientation, camera orientation and slant range
794+ x and y are from -1 to 1, relative to center of camera view
795+ positive x is to the right
796+ positive y is down
797+ '''
738798 att = self .master .messages .get ('ATTITUDE' ,None )
739799 if att is None :
740800 return None
801+ v = Vector3 (1 , 0 , 0 )
802+ m = Matrix3 ()
803+ (roll ,pitch ,yaw ) = (math .radians (self .attitude [0 ]),math .radians (self .attitude [1 ]),math .radians (self .attitude [2 ]))
804+ yaw += att .yaw
805+ FOV_half = math .radians (0.5 * FOV )
806+ yaw += FOV_half * x
807+ pitch -= y * FOV_half / aspect_ratio
808+ m .from_euler (roll , pitch , yaw )
809+ v = m * v
810+ return v
811+
812+ def get_latlonalt (self , slant_range , x , y , FOV , aspect_ratio ):
813+ '''
814+ get ground lat/lon given vehicle orientation, camera orientation and slant range
815+ x and y are from -1 to 1, relative to center of camera view
816+ '''
817+ if slant_range is None :
818+ return None
819+ v = self .get_view_vector (x ,y ,FOV ,aspect_ratio )
820+ if v is None :
821+ return None
741822 gpi = self .master .messages .get ('GLOBAL_POSITION_INT' ,None )
742823 if gpi is None :
743824 return None
744- cam_att = self . attitude
745- v = Vector3 ( slant_range , 0 , 0 )
746- m = Matrix3 ( )
747- m . from_euler ( )
825+ v *= slant_range
826+ ( lat , lon , alt ) = ( gpi . lat * 1.0e-7 , gpi . lon * 1.0e-7 , gpi . alt * 1.0e-3 )
827+ ( lat , lon ) = mp_util . gps_offset ( lat , lon , v . y , v . x )
828+ return ( lat , lon , alt - v . z )
748829
749830 def update_target (self ):
750831 '''update position targetting'''
@@ -823,6 +904,21 @@ def get_gps_time(self, tnow):
823904 week_ms = (epoch_seconds % SEC_PER_WEEK ) * 1000 + ((t_ms // 200 ) * 200 )
824905 return week , week_ms
825906
907+ def show_thermal_fov (self ):
908+ '''show thermal FOV polygon'''
909+ points = []
910+ FOV = self .siyi_settings .thermal_fov
911+ aspect_ratio = 640.0 / 512.0
912+ for (x ,y ) in [(- 1 ,- 1 ),(1 ,- 1 ),(1 ,1 ),(- 1 ,1 ),(- 1 ,- 1 )]:
913+ latlonalt = self .get_latlonalt (self .get_slantrange (x ,y ,FOV ,aspect_ratio ),x ,y ,FOV ,aspect_ratio )
914+ if latlonalt is None :
915+ return
916+ (lat ,lon ) = (latlonalt [0 ],latlonalt [1 ])
917+ points .append ((lat ,lon ))
918+ self .mpstate .map .add_object (mp_slipmap .SlipPolygon ('SIYIThermalFOV' , points , layer = 'SIYI' ,
919+ linewidth = 2 , colour = (0 ,0 ,120 )))
920+
921+
826922 def mavlink_packet (self , m ):
827923 '''process a mavlink message'''
828924 mtype = m .get_type ()
@@ -836,6 +932,8 @@ def mavlink_packet(self, m):
836932 self .micros64 (),
837933 math .degrees (m .roll ), math .degrees (m .pitch ), math .degrees (m .yaw ),
838934 math .degrees (m .rollspeed ), math .degrees (m .pitchspeed ), math .degrees (m .yawspeed ))
935+ if mtype == 'GLOBAL_POSITION_INT' :
936+ self .show_thermal_fov ()
839937
840938
841939 def idle_task (self ):
0 commit comments