diff --git a/conf/airframes/twinstar1.xml b/conf/airframes/twinstar1.xml index 5003ab2fa6..182f6afebc 100644 --- a/conf/airframes/twinstar1.xml +++ b/conf/airframes/twinstar1.xml @@ -108,7 +108,6 @@ - # Virtual AC diff --git a/conf/control_panel.xml.example b/conf/control_panel.xml.example index 51fc6d472e..049830ed1c 100644 --- a/conf/control_panel.xml.example +++ b/conf/control_panel.xml.example @@ -79,6 +79,19 @@ + + + + + + + + + + + + + diff --git a/conf/flight_plans/generic.xml b/conf/flight_plans/generic.xml index 83a87003dd..581506665d 100644 --- a/conf/flight_plans/generic.xml +++ b/conf/flight_plans/generic.xml @@ -1,22 +1,42 @@ + + + + + + + + + + - - - - - + - - + + + + + + + + + + + - - + + + + + + + diff --git a/sw/airborne/gps.c b/sw/airborne/gps.c index 06a3d436e0..100916b8bd 100644 --- a/sw/airborne/gps.c +++ b/sw/airborne/gps.c @@ -34,12 +34,13 @@ #include "estimator.h" #include "ap_downlink.h" #include "infrared.h" +#include "nav.h" uint16_t last_gps_msg_t; /** cputime of the last gps message */ void estimator_update_state_gps( void ) { - float gps_east = gps_utm_east / 100. - NAV_UTM_EAST0; - float gps_north = gps_utm_north / 100. - NAV_UTM_NORTH0; + float gps_east = gps_utm_east / 100. - nav_utm_east0; + float gps_north = gps_utm_north / 100. - nav_utm_north0; float falt = gps_alt / 100.; EstimatorSetPos(gps_east, gps_north, falt); float fspeed = gps_gspeed / 100.; diff --git a/sw/airborne/main_ap.c b/sw/airborne/main_ap.c index b41689c4cf..a470d80d3f 100644 --- a/sw/airborne/main_ap.c +++ b/sw/airborne/main_ap.c @@ -77,7 +77,7 @@ uint8_t rc_settings_mode = RC_SETTINGS_MODE_NONE; uint8_t fatal_error_nb = 0; static const uint16_t version = 1; -uint8_t pprz_mode = PPRZ_MODE_MANUAL; +uint8_t pprz_mode = PPRZ_MODE_AUTO2; uint8_t vertical_mode = VERTICAL_MODE_MANUAL; uint8_t lateral_mode = LATERAL_MODE_MANUAL; diff --git a/sw/airborne/nav.c b/sw/airborne/nav.c index f524fef840..2335c01cdf 100644 --- a/sw/airborne/nav.c +++ b/sw/airborne/nav.c @@ -377,7 +377,6 @@ void nav_home(void) { */ void nav_update(void) { compute_dist2_to_home(); - auto_nav(); } diff --git a/sw/ground_segment/cockpit/horizon.ml b/sw/ground_segment/cockpit/horizon.ml index ec1b684fc6..2242bef1ec 100644 --- a/sw/ground_segment/cockpit/horizon.ml +++ b/sw/ground_segment/cockpit/horizon.ml @@ -1,3 +1,4 @@ +open Printf open Latlong let affine_pos_and_angle xw yw angle = @@ -5,6 +6,7 @@ let affine_pos_and_angle xw yw angle = let sin_a = sin angle in [| cos_a ; sin_a ; ~-. sin_a; cos_a; xw ; yw |] +let affine_pos xw yw = affine_pos_and_angle xw yw 0. let arc = fun n r start stop -> let s = (stop -. start) /. float (n-1) in @@ -20,39 +22,82 @@ let floats_of_points = fun ps -> done; a +let ruler = fun ?(index_on_right=false) ~text_props ~max ~scale ~w ~index_width ~step ~h root -> + let r = GnoCanvas.group root in + let height = scale *. float max in + let _ = GnoCanvas.rect ~x1:0. ~y1:(-.height) ~x2:w ~y2:height ~fill_color:"#808080" r in + for i = 0 to max/step do + let i = i * step in + let y = -. scale *. float i in + let text = Printf.sprintf "%d" i in + let _ = GnoCanvas.text ~text ~props:(text_props@[`ANCHOR `EAST]) ~y ~x:(w*.0.75) r in + let _ = GnoCanvas.line ~points:[|w*.0.8;y;w;y|] ~fill_color:"white" r in + let y = y -. float step /. 2. *. scale in + let _ = GnoCanvas.line ~points:[|w*.0.8;y;w;y|] ~fill_color:"white" r in + () + done; + (** Yellow index *) + let _ = GnoCanvas.line ~points:[|0.;0.;w;0.|] ~fill_color:"yellow" root in + let s = index_width in + let idx = GnoCanvas.polygon ~points:[|0.;0.;-.s;s/.2.;-.s;-.s/.2.|] ~fill_color:"yellow" root in + if index_on_right then + idx#affine_absolute (affine_pos_and_angle w 0. pi); + + (** Mask (bottom & top) *) + let _ = GnoCanvas.rect ~x1:0. ~y1:(-.height) ~x2:w ~y2:(-.h) ~fill_color:"black" root in + let _ = GnoCanvas.rect ~x1:0. ~y1:height ~x2:w ~y2:h ~fill_color:"black" root in + r + + class h = fun ?packing size -> let canvas = GnoCanvas.canvas ~aa:true ~width:size ~height:size ?packing () in + let _ = + canvas#set_center_scroll_region false; + in + let size = float size in let size2 = size /. 2. in + let left_margin = size2 /. 10. in let pitch_scale = fun pitch -> pitch *. size2 *. 2. in + let speed_scale = size2 /. 10. in + let alt_scale = size2 /. 50. in + let speed_width = size2/.5. in + let alt_width = size2/.2.5 in + let index_width = size2 /. 15. in + + let xc = left_margin +. speed_width +. size2 + and yc = size2*.1.1 in + + let text_props = [`FONT "Sans 10"; `ANCHOR `CENTER; `FILL_COLOR "white"] in let disc = GnoCanvas.group canvas#root in let top = GnoCanvas.rect ~x1:(-.size2) ~y1:(-.size2*.5.) ~x2:size2 ~y2:0. ~fill_color:"#0099cb" disc and bottom = GnoCanvas.rect ~x1:(-.size2) ~y1:0. ~x2:size2 ~y2:(size2*.5.) ~fill_color:"#986701" disc - and line = GnoCanvas.line ~props:[`WIDTH_PIXELS 4] ~points:[|-.size2;0.;size2;0.|] ~fill_color:"white" disc in + and line = GnoCanvas.line ~props:[`WIDTH_PIXELS 4] ~points:[|-.size2;0.;size2;0.|] ~fill_color:"white" disc + and _ = GnoCanvas.line ~points:[|0.;-.size2;0.;size2|] ~fill_color:"white" disc + in let grads = fun ?(text=false) n s a b -> for i = 0 to n do - let a = float i *. a in - let y = pitch_scale ((Deg>>Rad)(a+.b)) in + let deg = float i *. a +. b in + let y = pitch_scale ((Deg>>Rad)deg) in ignore (GnoCanvas.line ~points:[|-.s; y; s; y|] ~fill_color:"white" disc); ignore (GnoCanvas.line ~points:[|-.s; -.y; s; -.y|] ~fill_color:"white" disc); if text then - let text = Printf.sprintf "-%d" (truncate a) + let text = Printf.sprintf "%d" (truncate deg) and x = 2.*.s in - ignore (GnoCanvas.text ~props:[`ANCHOR `CENTER; `FILL_COLOR "white"] ~text ~y ~x disc); - ignore (GnoCanvas.text ~props:[`ANCHOR `CENTER; `FILL_COLOR "white"] ~text ~y ~x:(-.x) disc); - let y = -. y - and text = Printf.sprintf "-%d" (truncate a) in - ignore (GnoCanvas.text ~props:[`ANCHOR `CENTER; `FILL_COLOR "white"] ~text ~y ~x disc); - ignore (GnoCanvas.text ~props:[`ANCHOR `CENTER; `FILL_COLOR "white"] ~text ~y ~x:(-.x) disc); + ignore (GnoCanvas.text ~props:text_props ~text ~y:(-.y) ~x disc); + ignore (GnoCanvas.text ~props:text_props ~text ~y:(-.y) ~x:(-.x) disc); + let text = "-"^text in + ignore (GnoCanvas.text ~props:text_props ~text ~y ~x disc); + ignore (GnoCanvas.text ~props:text_props ~text ~y ~x:(-.x) disc); done in let _ = grads 10 (size2/.10.) 5. 2.5; grads 5 (size2/.7.) 10. 5.; grads ~text:true 5 (size2/.5.) 10. 10. in - let mask = GnoCanvas.group canvas#root in + let mask = GnoCanvas.group ~x:xc ~y:yc canvas#root in let center = GnoCanvas.ellipse ~x1:(-3.) ~y1:(-.3.) ~x2:3. ~y2:3. ~fill_color:"black" mask in let pi6 = pi/.6. in let n = 20 in @@ -68,9 +113,60 @@ class h = fun ?packing size -> ignore (GnoCanvas.polygon ~fill_color:"black" ~points mask); let s = size2/. 5. in ignore (GnoCanvas.line ~props:[`WIDTH_PIXELS 4] ~points:[|-.x;0.;-.x-.s;0.;-.x-.s;s|] ~fill_color:"black" mask); - ignore (GnoCanvas.line ~props:[`WIDTH_PIXELS 4] ~points:[|x;0.;x+.s;0.;x+.s;s|] ~fill_color:"black" mask) in + ignore (GnoCanvas.line ~props:[`WIDTH_PIXELS 4] ~points:[|x;0.;x+.s;0.;x+.s;s|] ~fill_color:"black" mask); + + (* Top and bottom graduations *) + let g = fun a -> + let l = GnoCanvas.line~props:[`WIDTH_PIXELS 2] ~fill_color:"white" ~points:[|0.;-.size2;0.;-.1.2*.size2|] mask in + l#affine_relative (affine_pos_and_angle 0. 0. ((Deg>>Rad)a)) in + for i = 0 to 4 do + let a = float (i*10) in + g a; g (-.a) + done; + let _30 = fun a -> + let t = GnoCanvas.text ~text:"30" ~props:text_props ~x:0. ~y:(-1.1*.size2) mask in + t#affine_relative (affine_pos_and_angle 0. 0. ((Deg>>Rad)a)) in + _30 30.; _30 (-30.) + in + + let speed = + let g = GnoCanvas.group ~x:left_margin ~y:yc canvas#root in + let r = ruler ~text_props ~index_on_right:true ~max:50 ~scale:speed_scale ~w:speed_width ~step:2 ~index_width ~h:(0.75*.size2) g in + let mx = + GnoCanvas.text ~x:(speed_width/.2.) ~y:(-0.85*.size2) ~props:text_props g + and mi = + GnoCanvas.text ~x:(speed_width/.2.) ~y:(0.80*.size2) ~props:text_props g in + mx#set [`FILL_COLOR "yellow"]; + mi#set [`FILL_COLOR "yellow"]; + r, mi, mx + + and alt = + let g = GnoCanvas.group ~x:(xc+.size2) ~y:yc canvas#root in + ruler ~text_props ~max:3000 ~scale:alt_scale ~w:alt_width ~step:10 + ~index_width ~h:(0.75*.size2) g + in object (self) - method draw = fun roll pitch -> - disc#affine_absolute (affine_pos_and_angle 0. (pitch_scale pitch) (-.roll)) + method set_attitude = fun roll pitch -> + disc#affine_absolute (affine_pos_and_angle xc (yc+.pitch_scale pitch) (-.roll)) + + val mutable max_speed = 0. + val mutable min_speed = max_float + method set_speed = fun (s:float) -> + let (r, mi, mx) = speed in + r#affine_absolute (affine_pos 0. (speed_scale*.s)); + min_speed <- min min_speed s; + max_speed <- max max_speed s; + mi#set [`TEXT (sprintf "%.1f" min_speed)]; + mx#set [`TEXT (sprintf "%.1f" max_speed)] + initializer + let (r, _, _) = speed in + ignore (r#connect#event (function + `BUTTON_PRESS ev -> + max_speed <- 0.; min_speed <- max_float; true + | _ -> false)) + + method set_alt = fun (s:float) -> + alt#affine_absolute (affine_pos 0. (alt_scale*.s)) + end diff --git a/sw/ground_segment/cockpit/map2d.ml b/sw/ground_segment/cockpit/map2d.ml index 9e0b8f061b..5948338bd6 100644 --- a/sw/ground_segment/cockpit/map2d.ml +++ b/sw/ground_segment/cockpit/map2d.ml @@ -32,6 +32,36 @@ open Latlong let float_attr = fun xml a -> float_of_string (ExtXml.attrib xml a) let int_attr = fun xml a -> int_of_string (ExtXml.attrib xml a) +let pvect (x1,y1) (x2,y2) = x1*.y2-.y1*.x2 + +let angle (x1,y1) (x2,y2) (x3,y3) = + pvect (x3-.x2, y3-.y2) (x1-.x2,y1-.y2) + +let rec n_first n = function + [] -> [] + | x::xs -> if n > 0 then x::n_first (n-1) xs else [] + + +let discretise = fun n l -> + match l with + [] -> l + | first::ps -> + match List.rev l with + [] -> failwith "impossible" + | last::_ -> + let rec angles i previous = function + [] -> failwith "" + | [p1] -> [(angle first p1 previous, i), p1] + | p1::p2::ps -> + ((angle p2 p1 previous, i), p1)::angles (i+1) p1 (p2::ps) in + let l = angles 0 last l in + let l = List.sort (fun x y -> compare x y) l in + List.map snd (n_first n l) + +let rec list_casso x = function + [] -> raise Not_found + | (a,b)::abs -> if x = b then a else list_casso x abs + let soi = string_of_int let sof = string_of_float @@ -50,10 +80,10 @@ let _ = let fp_example = path_fps // "example.xml" let speech = ref false - let ign = ref false - let get_bdortho = ref "" +let auto_center_new_ac = ref false +let no_alarm = ref false let say = fun s -> if !speech then @@ -178,31 +208,28 @@ let map_from_region = fun (geomap:G.widget) () -> (************ Strip handling ***********************************************) module Strip = struct type t = { - ac_id: string; gauge: GRange.progress_bar ; labels: (string * (GBin.event_box * GMisc.label)) list } - let panel = Hashtbl.create 3 - let pad = 4 let i2s = string_of_int let labels_name = [| [| "AP" ; "alt" ; "->" |]; [| "RC"; "climb"; "/" |]; [| "GPS"; "speed"; "" |]; - [| "settings" ; "throttle"; "rate" |]; [| ""; "wind"; "dir" |]; + [| "settings" ; "throttle"; "" |] |] let labels_print = [| [| "AP" ; "alt" ; "->" |]; [| "RC"; "climb"; "->" |]; [| "GPS"; "speed"; "" |]; - [| "CAL" ; "throttle"; "rate" |]; [| ""; "wind"; "dir" |]; + [| "CAL" ; "throttle"; "" |] |] let gen_int = let i = ref (-1) in fun () -> incr i; !i - let rows = Array.length labels_name + let rows = Array.length labels_name + 1 (** add a strip to the panel *) - let add (widget: GPack.table) ac_id config color = + let add (widget: GPack.table) config color select center_ac commit_moves = (* number of the strip *) let strip_number = gen_int () in if strip_number > 1 then widget#set_rows strip_number; @@ -220,16 +247,19 @@ module Strip = struct let plane_color = GBin.event_box ~width:10 ~height:10 ~packing:(strip#attach ~top:0 ~left: 1) () in plane_color#coerce#misc#modify_bg [`NORMAL, `NAME color]; + ignore (plane_color#event#connect#button_press ~callback:(fun _ -> select (); true)); + let h = GPack.hbox ~packing:plane_color#add () in + let ft = GMisc.label ~text: "00:00:00" ~packing:h#add () in + ft#set_width_chars 8; + add_label ("flight_time_value") (plane_color, ft); + let block_name = GMisc.label ~text: "______" ~packing:h#add () in + add_label ("block_name_value") (plane_color, block_name); + (* battery and flight time *) - let bat_table = GPack.table ~rows: 2 ~columns: 1 ~packing: (strip#attach ~top:1 ~left:0) () in - let pb = GRange.progress_bar ~orientation: `BOTTOM_TO_TOP ~packing: (bat_table#attach ~top:0 ~left:0) () in + let pb = GRange.progress_bar ~orientation: `BOTTOM_TO_TOP ~packing:(strip#attach ~top:1 ~left:0) () in pb#coerce#misc#modify_fg [`PRELIGHT, `NAME "green"]; - pb#coerce#misc#modify_font_by_name "sans 18"; - let eb = GBin.event_box ~packing: (bat_table#attach ~top:1 ~left:0) () in - let ft = GMisc.label ~text: "00:00:00" ~packing:eb#add () in - ft#set_width_chars 8; - add_label ("flight_time_value") (eb, ft); + pb#coerce#misc#modify_font_by_name "sans 12"; let left_box = GPack.table ~rows ~columns: 6 ~col_spacings: 5 ~packing: (strip#attach ~top: 1 ~left: 1) () in @@ -245,10 +275,12 @@ module Strip = struct add_label (s^"_value") (eb, lvalue); ) a ) labels_name; - Hashtbl.add panel ac_id {ac_id = ac_id; gauge=pb ; labels= (!strip_labels)} + let b = GButton.button ~label:"Center A/C" ~packing:(left_box#attach ~top:4 ~left:0 ~right:2) () in + ignore(b#connect#clicked ~callback:center_ac); + let b = GButton.button ~label:"Commit Moves" ~packing:(left_box#attach ~top:4 ~left:2 ~right:4) () in + ignore (b#connect#clicked ~callback:commit_moves); + {gauge=pb ; labels= (!strip_labels)} - (** find an aircraft in the list of aircraft *) - let find ac_id = Hashtbl.find panel ac_id (** set a label *) let set_label strip name value = @@ -373,8 +405,8 @@ module Edit = struct let close_fp = fun () -> match !current_fp with None -> () (* Nothing to close *) - | Some (fp, _filename) -> - fp#destroy (); + | Some (_fp, _filename, window) -> + window#destroy (); current_fp := None let load_xml_fp = fun geomap accel_group ?(xml_file=path_fps) xml -> @@ -386,7 +418,7 @@ module Edit = struct window#add_accel_group accel_group; window#show(); ignore (window#connect#destroy ~callback:close_fp); - current_fp := Some (fp,xml_file); + current_fp := Some (fp,xml_file, window); fp let labelled_entry = fun ?width_chars text value h -> @@ -455,14 +487,14 @@ module Edit = struct None -> GToolbox.message_box "Error" "Load a flight plan first"; failwith "create_wp" - | Some (fp,_) -> + | Some (fp,_,_) -> fp#add_waypoint geo let save_fp = fun () -> match !current_fp with None -> () (* Nothing to save *) - | Some (fp, filename) -> + | Some (fp, filename,_) -> match GToolbox.select_file ~title:"Save Flight Plan" ~filename () with None -> () | Some file -> @@ -479,7 +511,7 @@ module Edit = struct (** Calibration of chosen image (requires a dummy flight plan) *) let calibrate_map = fun (geomap:G.widget) accel_group () -> match !current_fp with - | Some (_fp,_) -> GToolbox.message_box "Error" "Close current flight plan before calibration" + | Some (_fp,_,_) -> GToolbox.message_box "Error" "Close current flight plan before calibration" | None -> match GToolbox.select_file ~filename:default_path_maps ~title:"Open Image" () with None -> () @@ -626,7 +658,7 @@ module Edit = struct begin match !current_fp with None -> () - | Some (fp, _) -> + | Some (fp, _,_) -> fp#insert_path (List.map (fun (wp,_,_,_,r) -> (wp, r)) (List.rev ! !path)); end; path := ref [] @@ -734,10 +766,16 @@ module Live = struct ir_page : Pages.infrared; gps_page : Pages.gps; pfd_page : Pages.pfd; - settings_page : Pages.settings option + misc_page : Pages.misc; + settings_page : Pages.settings option; + strip : Strip.t; + mutable first_pos : bool } let live_aircrafts = Hashtbl.create 3 + let get_ac = fun vs -> + let ac_id = Pprz.string_assoc "ac_id" vs in + Hashtbl.find live_aircrafts ac_id let aircraft_pos_msg = fun track wgs84 heading altitude speed climb -> let h = @@ -814,10 +852,12 @@ module Live = struct (Xml.children stages); List.sort compare !blocks - let menu_entry_of_block = fun ac (id, name) -> - let send_msg = fun () -> - Ground_Pprz.message_send "map2d" "JUMP_TO_BLOCK" - ["ac_id", Pprz.String ac; "block_id", Pprz.Int id] in + let jump_to_block = fun ac_id id -> + Ground_Pprz.message_send "map2d" "JUMP_TO_BLOCK" + ["ac_id", Pprz.String ac_id; "block_id", Pprz.Int id] + + let menu_entry_of_block = fun ac_id (id, name) -> + let send_msg = fun () -> jump_to_block ac_id id in `I (name, send_msg) let reset_waypoints = fun fp () -> @@ -853,7 +893,8 @@ module Live = struct let track = new MapTrack.track ~name ~color:color geomap in - ignore (ac_menu_fact#add_item "Center A/C" ~callback:(center geomap track)); + let center_ac = center geomap track in + ignore (ac_menu_fact#add_item "Center A/C" ~callback:center_ac); ignore (ac_menu_fact#add_item "Clear Track" ~callback:(fun () -> track#clear_map2D)); ignore (ac_menu_fact#add_item "Resize Track" ~callback:(fun () -> resize_track ac_id track)); @@ -861,10 +902,12 @@ module Live = struct let jump_block_entries = List.map (menu_entry_of_block ac_id) blocks in + let commit_moves = fun () -> + commit_changes ac_id in let sm = ac_menu_fact#add_submenu "Datalink" in let dl_menu = [ `M ("Jump to block", jump_block_entries); - `I ("Commit Moves", (fun () -> commit_changes ac_id)); + `I ("Commit Moves", commit_moves); `I ("Event 1", (fun () -> send_event ac_id 1)); `I ("Event 2", (fun () -> send_event ac_id 2))] in @@ -903,6 +946,11 @@ module Live = struct ~packing: (ac_notebook#append_page ~tab_label: pfd_label#coerce) () in let pfd_page = new Pages.pfd pfd_frame in + let misc_label = GMisc.label ~text: "Misc" () in + let misc_frame = GBin.frame ~shadow_type: `NONE + ~packing: (ac_notebook#append_page ~tab_label:misc_label#coerce) () in + let misc_page = new Pages.misc ~packing:misc_frame#add misc_frame in + let settings_page = try let xml_settings = Xml.children (ExtXml.child fp_xml "dl_settings") in @@ -916,8 +964,18 @@ module Live = struct with _ -> None in - fp#hide (); + fp#connect_activated (fun node -> + if XmlEdit.tag node = "block" then + let block = XmlEdit.attrib node "name" in + let id = list_casso block blocks in + jump_to_block ac_id id); ignore (reset_wp_menu#connect#activate (reset_waypoints fp)); + + let select_this_tab = + let n = fp_notebook#page_num ac_frame#coerce in + fun () -> fp_notebook#goto_page n in + + let strip = Strip.add strip_table config color select_this_tab center_ac commit_moves in Hashtbl.add live_aircrafts ac_id { track = track; color = color; fp_group = fp ; config = config ; fp = fp_xml; ac_name = name; @@ -926,9 +984,10 @@ module Live = struct ir_page = ir_page; gps_page = gps_page; pfd_page = pfd_page; - settings_page = settings_page - }; - ignore (Strip.add strip_table ac_id config color) + misc_page = misc_page; + settings_page = settings_page; + strip = strip; first_pos = true + } (** Bind to message while catching all the esceptions of the callback *) let safe_bind = fun msg cb -> @@ -955,46 +1014,32 @@ module Live = struct end let get_wind_msg = fun sender vs -> - try - let ac_strip = Strip.find (Pprz.string_assoc "ac_id" vs) in - let set_label = fun label_name field_name -> - Strip.set_label ac_strip label_name - (Printf.sprintf "%.1f" (Pprz.float_assoc field_name vs)) - in - set_label "wind" "wspeed"; - set_label "dir" "dir" - with - Not_found -> () + let ac = get_ac vs in + let value = fun field_name -> sprintf "%.1f" (Pprz.float_assoc field_name vs) in + ac.misc_page#set_wind_speed (value "wspeed"); + ac.misc_page#set_wind_dir (value "dir") let get_fbw_msg = fun sender vs -> - try - let ac_strip = Strip.find (Pprz.string_assoc "ac_id" vs) in - let status = Pprz.string_assoc "rc_status" vs in - Strip.set_label ac_strip "RC" status; - Strip.set_color ac_strip "RC" - (match status with - "LOST" -> "orange" + let ac = get_ac vs in + let status = Pprz.string_assoc "rc_status" vs in + Strip.set_label ac.strip "RC" status; + Strip.set_color ac.strip "RC" + (match status with + "LOST" -> "orange" | "REALLY_LOST" -> "red" | _ -> "white") - with - Not_found -> () + let get_engine_status_msg = fun sender vs -> - try - let ac_strip = Strip.find (Pprz.string_assoc "ac_id" vs) in - Strip.set_label ac_strip "throttle" - (string_of_float (Pprz.float_assoc "throttle" vs)); - Strip.set_bat ac_strip (Pprz.float_assoc "bat" vs) - with - Not_found -> () + let ac = get_ac vs in + Strip.set_label ac.strip "throttle" + (string_of_float (Pprz.float_assoc "throttle" vs)); + Strip.set_bat ac.strip (Pprz.float_assoc "bat" vs) let get_if_calib_msg = fun sender vs -> - try - let ac_strip = Strip.find (Pprz.string_assoc "ac_id" vs) in - Strip.set_label ac_strip "settings" (Pprz.string_assoc "if_mode" vs) - with - Not_found -> () + let ac = get_ac vs in + Strip.set_label ac.strip "settings" (Pprz.string_assoc "if_mode" vs) let listen_wind_msg = fun () -> safe_bind "WIND" get_wind_msg @@ -1038,19 +1083,23 @@ module Live = struct end - let listen_flight_params = fun () -> + let listen_flight_params = fun geomap -> let get_fp = fun _sender vs -> - let ac_id = Pprz.string_assoc "ac_id" vs in - let ac_strip = Strip.find ac_id in - let ac = Hashtbl.find live_aircrafts ac_id in + let ac = get_ac vs in let pfd_page = ac.pfd_page in pfd_page#set_roll (Pprz.float_assoc "roll" vs); pfd_page#set_pitch (Pprz.float_assoc "pitch" vs); pfd_page#set_alt (Pprz.float_assoc "alt" vs); pfd_page#set_climb (Pprz.float_assoc "climb" vs); + pfd_page#set_speed (Pprz.float_assoc "speed" vs); let a = fun s -> Pprz.float_assoc s vs in let wgs84 = { posn_lat = (Deg>>Rad)(a "lat"); posn_long = (Deg>>Rad)(a "long") } in aircraft_pos_msg ac.track wgs84 (a "course") (a "alt") (a "speed") (a "climb"); + if !auto_center_new_ac && ac.first_pos then begin + center geomap ac.track (); + ac.first_pos <- false + end; + let set_label lbl_name field_name = let s = if (a field_name) < 0. @@ -1059,7 +1108,7 @@ module Live = struct else Printf.sprintf "%.1f" (a field_name) in - Strip.set_label ac_strip lbl_name s + Strip.set_label ac.strip lbl_name s in set_label "alt" "alt"; set_label "speed" "speed"; @@ -1068,9 +1117,7 @@ module Live = struct safe_bind "FLIGHT_PARAM" get_fp; let get_ns = fun _sender vs -> - let ac_id = Pprz.string_assoc "ac_id" vs in - let ac_strip = Strip.find ac_id in - let ac = Hashtbl.find live_aircrafts ac_id in + let ac = get_ac vs in let a = fun s -> Pprz.float_assoc s vs in let wgs84 = { posn_lat = (Deg>>Rad)(a "target_lat"); posn_long = (Deg>>Rad)(a "target_long") } in carrot_pos_msg ac.track wgs84; @@ -1080,9 +1127,10 @@ module Live = struct let b = String.sub b 0 (min 10 (String.length b)) in highlight_fp ac cur_block cur_stage; let set_label = fun l f -> - Strip.set_label ac_strip l (Printf.sprintf "%.1f" (Pprz.float_assoc f vs)) in + Strip.set_label ac.strip l (Printf.sprintf "%.1f" (Pprz.float_assoc f vs)) in set_label "->" "target_alt"; - set_label "/" "target_climb" + set_label "/" "target_climb"; + Strip.set_label ac.strip "block_name" b in safe_bind "NAV_STATUS" get_ns; @@ -1118,25 +1166,22 @@ module Live = struct let get_ap_status = fun _sender vs -> - let ac_id = Pprz.string_assoc "ac_id" vs in - - let ac_strip = Strip.find ac_id in - let ac = Hashtbl.find live_aircrafts ac_id in + let ac = get_ac vs in ap_status_msg ac.track ( float_of_int (Pprz.int32_assoc "flight_time" vs )); let ap_mode = Pprz.string_assoc "ap_mode" vs in if ap_mode <> ac.last_ap_mode then begin say (sprintf "%s, %s" ac.ac_name ap_mode); ac.last_ap_mode <- ap_mode end; - Strip.set_label ac_strip "AP" (Pprz.string_assoc "ap_mode" vs); - Strip.set_color ac_strip "AP" (if ap_mode="HOME" then "red" else "white"); + Strip.set_label ac.strip "AP" (Pprz.string_assoc "ap_mode" vs); + Strip.set_color ac.strip "AP" (if ap_mode="HOME" then "red" else "white"); let gps_mode = Pprz.string_assoc "gps_mode" vs in - Strip.set_label ac_strip "GPS" gps_mode; - Strip.set_color ac_strip "GPS" (if gps_mode<>"3D" then "red" else "white"); + Strip.set_label ac.strip "GPS" gps_mode; + Strip.set_color ac.strip "GPS" (if gps_mode<>"3D" then "red" else "white"); let ft = let t = Int32.to_int (Int32.of_string (Pprz.string_assoc "flight_time" vs)) in Printf.sprintf "%02d:%02d:%02d" (t / 3600) ((t mod 3600) / 60) ((t mod 3600) mod 60) in - Strip.set_label ac_strip "flight_time" ft + Strip.set_label ac.strip "flight_time" ft in safe_bind "AP_STATUS" get_ap_status; @@ -1149,13 +1194,14 @@ module Live = struct (** Not_found catched by safe_bind *) let wp_id = Pprz.int_assoc "wp_id" vs in let a = fun s -> Pprz.float_assoc s vs in - let geo = { posn_lat = (Deg>>Rad)(a "lat"); posn_long = (Deg>>Rad)(a "long") } in + let geo = { posn_lat = (Deg>>Rad)(a "lat"); posn_long = (Deg>>Rad)(a "long") } + and altitude = a "alt" in (** No indexed access to waypoints: iter and compare: *) List.iter (fun w -> let (i, w) = ac.fp_group#index w in if i = wp_id then begin - if not w#moved then w#set geo; + if not w#moved then w#set ~altitude ~update:true geo; raise Exit (** catched by safe_bind *) end) ac.fp_group#waypoints @@ -1218,7 +1264,7 @@ end (** module Live *) let keys_help = fun () -> GToolbox.message_box ~title:"Keys" ~ok:"Close" "Zoom: Mouse Wheel, PgUp, PgDown\n\ - Pan: Shift Middle, Arrows\n\ + Pan: Left, Arrows\n\ Load Map Tile: Right\n\ Select Region: Shift-Left + Drag\n\ Create Waypoint: Ctrl-Left\n\ @@ -1277,6 +1323,7 @@ let _main = and maximize = ref false and projection= ref G.UTM and auto_ortho = ref false + and mplayer = ref "" and plugin_window = ref "" in let options = [ "-b", Arg.String (fun x -> ivy_bus := x), "Bus\tDefault is 127.255.255.25:2010"; @@ -1284,12 +1331,16 @@ let _main = "-ref", Arg.Set_string geo_ref, "Geographic ref (default '')"; "-zoom", Arg.Set_float zoom, "Initial zoom"; "-center", Arg.Set_string center, "Initial map center"; + "-center_ac", Arg.Set auto_center_new_ac, "Centers the map on any new A/C"; "-plugin", Arg.Set_string plugin_window, "External X application (launched the id of the plugin window as argument)"; + "-mplayer", Arg.Set_string mplayer, "Launch mplayer with the given argument as X plugin"; "-mercator", Arg.Unit (fun () -> projection:=G.Mercator),"Switch to (Google Maps) Mercator projection"; "-lambertIIe", Arg.Unit (fun () -> projection:=G.LambertIIe),"Switch to LambertIIe projection"; "-ign", Arg.String (fun s -> ign:=true; IGN.data_path := s), "IGN tiles path"; "-ortho", Arg.Set_string get_bdortho, "IGN tiles path"; + "-no_alarm", Arg.Set no_alarm, "Disables alarm page"; "-auto_ortho", Arg.Set auto_ortho, "IGN tiles path"; + "-google_fill", Arg.Set GM.auto, "Google maps auto fill"; "-speech", Arg.Set speech, "Speech"; "-m", Arg.String (fun x -> map_files := x :: !map_files), "Map description file"] in Arg.parse (options) @@ -1304,7 +1355,7 @@ let _main = IGN.cache_path := var_maps_path; (** window for map2d **) - let window = GWindow.window ~title: "Map2d" ~border_width:1 ~width:1024 ~height:768 () in + let window = GWindow.window ~title: "Map2d" ~border_width:1 ~width:1024 ~height:750 () in if !maximize then window#maximize (); let vbox= GPack.vbox ~packing: window#add () in @@ -1317,7 +1368,7 @@ let _main = ignore (window#connect#destroy ~callback:quit); ignore (vertical_situation#connect#destroy ~callback:quit); - let geomap = new G.widget ~projection:!projection () in + let geomap = new G.widget ~height:500 ~projection:!projection () in let menu_fact = new GMenu.factory geomap#file_menu in let accel_group = menu_fact#accel_group in @@ -1368,7 +1419,7 @@ let _main = ignore (geomap#factory#add_separator ()); let paned = GPack.paned ~show:true `VERTICAL ~packing:(vbox#pack ~expand:true) () in - let frame1 = GPack.vbox ~height:600 () in + let frame1 = GPack.vbox () in paned#pack1 ~shrink:true (*** ~expand:true ***) frame1#coerce; let hpaned = GPack.paned ~show:true `HORIZONTAL ~packing:paned#add2 () in @@ -1387,9 +1438,12 @@ let _main = let hpaned3 = GPack.paned ~show:true `HORIZONTAL ~packing:hpaned2#add2 () in (** Alerts text frame *) - let alert_page = GBin.frame ~packing:hpaned3#add1 () in + let packing = if !no_alarm then fun _ -> () else hpaned3#add1 in + let alert_page = GBin.frame ~packing () in let my_alert = new Pages.alert alert_page in + if !mplayer <> "" then + plugin_window := sprintf "mplayer -nomouseinput '%s' -wid " !mplayer; if !plugin_window <> "" then begin let plugin_width=400 and plugin_height=300 in let frame2 = GPack.vbox ~width:plugin_width () in @@ -1417,7 +1471,7 @@ let _main = Live.safe_bind "NEW_AIRCRAFT" (fun _sender vs -> Live.one_new_ac geomap fp_notebook (Pprz.string_assoc "ac_id" vs)); (** Listen for all messages on ivy *) - Live.listen_flight_params (); + Live.listen_flight_params geomap; Live.listen_wind_msg (); Live.listen_fbw_msg (); Live.listen_engine_status_msg (); @@ -1441,8 +1495,8 @@ let _main = (** Center the map as required *) if !center <> "" then begin - try geomap#center (Latlong.of_string !center) with - _ -> GToolbox.message_box "Error" (sprintf "Cannot center at '%s' (no ref ?)" !center) + set_georef_if_none geomap (Latlong.of_string !center); + geomap#center (Latlong.of_string !center) end; say "Welcome to paparazzi"; diff --git a/sw/ground_segment/cockpit/pages.ml b/sw/ground_segment/cockpit/pages.ml index 7a95a5ca42..543810410f 100644 --- a/sw/ground_segment/cockpit/pages.ml +++ b/sw/ground_segment/cockpit/pages.ml @@ -130,14 +130,41 @@ object (this) method set_roll r = roll <- r; - horizon#draw ((Deg>>Rad)roll) ((Deg>>Rad)pitch) + horizon#set_attitude ((Deg>>Rad)roll) ((Deg>>Rad)pitch) method set_pitch p = pitch <- p; - horizon#draw ((Deg>>Rad)roll) ((Deg>>Rad)pitch) - method set_alt (a:float) = () + horizon#set_attitude ((Deg>>Rad)roll) ((Deg>>Rad)pitch) + method set_alt (a:float) = horizon#set_alt a method set_climb (c:float) = () + method set_speed (c:float) = horizon#set_speed c end + +(*****************************************************************************) +(* Misc page *) +(*****************************************************************************) +class misc ~packing (widget: GBin.frame) = + let table = GPack.table + ~rows: 2 + ~columns: 2 + ~row_spacings: 5 + ~col_spacings: 40 + ~packing + () in + let label = fun text i j ->GMisc.label ~text ~packing:(table#attach ~top:i ~left:j) () in + let _init = + ignore (label "Wind speed" 0 0); + ignore (label "Wind direction" 1 0) in + let wind_speed = label "" 0 1 + and wind_dir = label "" 1 1 in + object + method set_wind_speed s = wind_speed#set_text s + method set_wind_dir s = wind_dir#set_text s + end + +(*****************************************************************************) +(* Dataling settings paged *) +(*****************************************************************************) class settings = fun xml_settings callback -> let sw = GBin.scrolled_window ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC () in let vbox = GPack.vbox ~packing:sw#add_with_viewport () in diff --git a/sw/ground_segment/cockpit/pages.mli b/sw/ground_segment/cockpit/pages.mli index 07ebf7c5e0..2573fbaaa6 100644 --- a/sw/ground_segment/cockpit/pages.mli +++ b/sw/ground_segment/cockpit/pages.mli @@ -15,6 +15,7 @@ class gps : GBin.frame -> end class pfd : GBin.frame -> object + method set_speed : float -> unit method set_alt : float -> unit method set_climb : float -> unit method set_pitch : float -> unit @@ -26,3 +27,10 @@ class settings : Xml.xml list -> (int -> float -> unit) -> method set : int -> float -> unit method widget : GObj.widget end +class misc : + packing:(GObj.widget -> unit) -> + GBin.frame -> + object + method set_wind_dir : string -> unit + method set_wind_speed : string -> unit + end diff --git a/sw/ground_segment/tmtc/server.ml b/sw/ground_segment/tmtc/server.ml index ad5c2722bc..2e857ee246 100644 --- a/sw/ground_segment/tmtc/server.ml +++ b/sw/ground_segment/tmtc/server.ml @@ -305,7 +305,7 @@ let send_cam_status = fun a -> match a.nav_ref with None -> () (* No geo ref for camera target *) | Some nav_ref -> - let h = a.alt -. float (Srtm.of_utm a.pos) in + let h = a.alt -. float (try Srtm.of_utm a.pos with _ -> 0) in let dx = h *. tan (a.cam.phi -. a.roll) and dy = h *. tan (a.cam.theta +. a.pitch) in let alpha = -. a.course in diff --git a/sw/lib/ocaml/mapCanvas.ml b/sw/lib/ocaml/mapCanvas.ml index be0f3131e0..43a29c8258 100644 --- a/sw/lib/ocaml/mapCanvas.ml +++ b/sw/lib/ocaml/mapCanvas.ml @@ -43,6 +43,10 @@ let distance = fun (x1,y1) (x2,y2) -> sqrt ((x1-.x2)**2.+.(y1-.y2)**2.) let _ = Srtm.add_path "SRTM" +let affine_pos_and_angle xw yw angle = + let cos_a = cos angle in + let sin_a = sin angle in + [| cos_a ; sin_a ; ~-. sin_a; cos_a; xw ; yw |] type projection = Mercator (* 1e-6 = 1 world unit, y axis reversed *) @@ -82,6 +86,38 @@ let set_opacity = fun pixbuf opacity -> pixbuf +type drawing = NotDrawing | Rectangle of float*float | Polygon of (float*float) list + + +let float_array_of_points = fun l -> + Array.of_list (List.fold_right (fun (x,y) r -> x::y::r) l []) + +let pvect (x1,y1) (x2,y2) = x1*.y2-.y1*.x2 + +let rec convexify = fun l -> + match l with + [] | [_] | [_;_] -> l + | (x1,y1)::(x2,y2)::(x3,y3)::l -> + if pvect (x3-.x2, y3-.y2) (x1-.x2,y1-.y2) < 0. + then convexify ((x1,y1)::(x3,y3)::l) + else (x1,y1)::convexify ((x2,y2)::(x3,y3)::l) + +let convex = fun l -> + match convexify l with + [] -> [] + | (x3,y3)::ps -> + (** Remove last bad points *) + let rec loop = fun l -> + match l with + [] | [_] -> l + | (x2,y2)::(x1,y1)::pts -> + if pvect (x3-.x2, y3-.y2) (x1-.x2,y1-.y2) < 0. + then loop ((x1,y1)::pts) + else l in + (x3,y3)::List.rev (loop (List.rev ps)) + + + (** basic canvas with menubar ************************************** * (the vertical display in map2.ml is an instance of basic_widget)* *******************************************************************) @@ -103,15 +139,32 @@ class basic_widget = fun ?(height=800) ?width ?(projection = Mercator) ?georef ( let spin_button = GEdit.spin_button ~rate:0. ~digits:2 ~width:50 (*** ~height:20 ***) ~packing:top_bar#pack () in let canvas = GnoCanvas.canvas ~packing:(frame#pack ~expand:true) () in - let background = GnoCanvas.group canvas#root in + let background = GnoCanvas.group canvas#root + and still = GnoCanvas.group canvas#root in let view_cbs = Hashtbl.create 3 in (* Store for view event callback *) let region_rectangle = GnoCanvas.rect canvas#root ~props:[`WIDTH_PIXELS 2; `OUTLINE_COLOR "red"] in + let region_polygon = GnoCanvas.polygon canvas#root ~props:[`WIDTH_PIXELS 2; `OUTLINE_COLOR "red"] in + + let s = 50. in + let s2 = s/.2. and s4=s/.4. in + let points = [|0.;0.; s2;s2; s4;s2; s4;s; -.s4;s; -.s4;s2; -.s2;s2|] in + let props = [`FILL_COLOR "#a0a0ff"; `FILL_STIPPLE (Gdk.Bitmap.create_from_data ~width:2 ~height:2 "\002\001")] in + let arrow = fun x y angle -> + let a = GnoCanvas.polygon still ~points ~props in + a#affine_relative (affine_pos_and_angle x y angle); + a in + let north_arrow = arrow (1.5*.s) 0. 0. + and south_arrow = arrow (1.5*.s) (3.*.s) LL.pi + and west_arrow = arrow 0. (1.5*.s) (-.LL.pi/.2.) + and east_arrow = arrow (3.*.s) (1.5*.s) (LL.pi/.2.) in object (self) (** GUI attributes *) val background = background + method still = still + method top_still = 3.5*.s val adj = GData.adjustment ~value:1. ~lower:0.05 ~upper:10. @@ -126,10 +179,12 @@ class basic_widget = fun ?(height=800) ?width ?(projection = Mercator) ?georef ( val mutable projection = projection val mutable georef = georef val mutable dragging = None - val mutable grouping = None + val mutable drawing = NotDrawing val mutable region = None (* Rectangle selected region *) + val mutable polygon = None (* Polygon selected region *) method region = region + method polygon = polygon (** initialization of instance attributes *) @@ -140,9 +195,10 @@ class basic_widget = fun ?(height=800) ?width ?(projection = Mercator) ?georef ( (** callback bindings *) canvas#coerce#misc#modify_bg [`NORMAL, `NAME "black"]; - ignore (canvas#event#connect#motion_notify (self#mouse_motion)); - ignore (canvas#event#connect#button_press (self#button_press)); + ignore (canvas#event#connect#button_press self#button_press); ignore (canvas#event#connect#button_release self#button_release); + ignore (canvas#event#connect#motion_notify self#mouse_motion); + ignore (canvas#event#connect#button_press self#button_press); ignore (canvas#event#connect#after#key_press self#key_press) ; ignore (canvas#event#connect#enter_notify (fun _ -> self#canvas#misc#grab_focus () ; false)); ignore (canvas#event#connect#any self#any_event); @@ -264,22 +320,32 @@ class basic_widget = fun ?(height=800) ?width ?(projection = Mercator) ?georef ( method zoom = fun value -> adj#set_value value - - (** Mouse button events *******************************************) - method button_press = fun ev -> - let xc = GdkEvent.Button.x ev in - let yc = GdkEvent.Button.y ev in - match GdkEvent.Button.button ev with - 1 when Gdk.Convert.test_modifier `SHIFT (GdkEvent.Button.state ev) && not (Gdk.Convert.test_modifier `CONTROL (GdkEvent.Button.state ev)) -> - let (x1,y1) = self#window_to_world xc yc in - grouping <- Some (x1,y1); - region_rectangle#set [`X1 x1; `Y1 y1; `X2 x1; `Y2 y1]; - true - | 2 when Gdk.Convert.test_modifier `SHIFT (GdkEvent.Button.state ev) -> - dragging <- Some (xc, yc); - true - | _ -> false - + + (** events *******************************************) + method button_press = fun ev -> + let xc = GdkEvent.Button.x ev + and yc = GdkEvent.Button.y ev + and state = GdkEvent.Button.state ev in + let (x1,y1) = self#window_to_world xc yc in + try let _ = canvas#get_item_at x1 y1 in false with + Not_found -> + begin + match GdkEvent.Button.button ev with + 1 -> + begin + match Gdk.Convert.modifier state with + [`SHIFT] -> + drawing <- Rectangle (x1,y1); + region_rectangle#set [`X1 x1; `Y1 y1; `X2 x1; `Y2 y1]; + true + | [] -> + drawing <- Polygon [(x1, y1)]; + true; + | _ -> false + end + | _ -> false + end + method mouse_motion = fun ev -> if georef <> None then begin let xc = GdkEvent.Motion.x ev @@ -288,38 +354,42 @@ class basic_widget = fun ?(height=800) ?width ?(projection = Mercator) ?georef ( self#display_geo (self#geo_string (self#of_world (xw,yw))); self#display_alt (self#of_world (xw,yw)); begin - match dragging with - Some (x0, y0 ) -> - let (x, y) = self#canvas#get_scroll_offsets in - self#canvas#scroll_to (x+truncate (x0-.xc)) (y+truncate (y0-.yc)) - | None -> () - end; - begin - match grouping with - Some starting_point -> - let starting_point = self#of_world starting_point in + match drawing with + Rectangle (x1,y1) -> + let starting_point = self#of_world (x1,y1) in let starting_point = LL.utm_of LL.WGS84 starting_point in let current_point = LL.utm_of LL.WGS84 (self#of_world (xw, yw)) in let (east, north) = LL.utm_sub current_point starting_point in region_rectangle#set [`X2 xw; `Y2 yw]; self#display_group (sprintf "[%.1fkm %.1fkm]" (east/.1000.) (north/.1000.)) - | None -> () - end + | Polygon ps -> + let points = convex ((xw,yw)::ps) in + drawing <- Polygon points; + region_polygon#set [`POINTS (float_array_of_points points)] + | _ -> () + end; end; false method button_release = fun ev -> - match GdkEvent.Button.button ev, grouping with - 2, _ -> - dragging <- None; false - | 1, Some starting_point -> - let xc = GdkEvent.Button.x ev in - let yc = GdkEvent.Button.y ev in - let current_point = self#window_to_world xc yc in - region <- Some (starting_point, current_point); - self#display_group ""; - grouping <- None; - false + match GdkEvent.Button.button ev with + 1 -> + begin + let xc = GdkEvent.Button.x ev in + let yc = GdkEvent.Button.y ev in + let current_point = self#window_to_world xc yc in + match drawing with + Rectangle (x1,y1) -> + region <- Some ((x1,y1), current_point); + self#display_group ""; + drawing <- NotDrawing; + true + | Polygon points -> + drawing <- NotDrawing; + polygon <- Some points; + true + | _ -> false + end | _ -> false method key_press = fun ev -> @@ -414,7 +484,35 @@ class basic_widget = fun ?(height=800) ?width ?(projection = Mercator) ?georef ( let t = GnoCanvas.text ~x:x1 ~y:y1 ~text:text ~props:[`FILL_COLOR fill_color; `X_OFFSET x_offset; `Y_OFFSET y_offset] group in t#show (); t - + + initializer + let replace_still = fun _ -> + let (x, y) = canvas#get_scroll_offsets in + let (xc, yc) = canvas#window_to_world (float x) (float y) in + let z = 1./.self#current_zoom in + still#affine_absolute [|z;0.;0.;z;xc;yc|] + in + self#connect_view replace_still; + let move_timer = ref (Glib.Timeout.add 0 (fun _ -> false)) in + let move dx dy = function + `BUTTON_PRESS _ -> + let scroll = fun _ -> + let (x, y) = canvas#get_scroll_offsets in + canvas#scroll_to (x+dx) (y+dy) ; true in + move_timer := Glib.Timeout.add 50 scroll; + true + | `BUTTON_RELEASE _ -> + Glib.Timeout.remove !move_timer; + true + | _ -> false in + let up = move 0 (-pan_step) + and down = move 0 pan_step + and left = move (-pan_step) 0 + and right = move pan_step 0 in + ignore (north_arrow#connect#event up); + ignore (south_arrow#connect#event down); + ignore (west_arrow#connect#event left); + ignore (east_arrow#connect#event right) end diff --git a/sw/lib/ocaml/mapFP.ml b/sw/lib/ocaml/mapFP.ml index 2a9161d4b8..5dd55e454c 100644 --- a/sw/lib/ocaml/mapFP.ml +++ b/sw/lib/ocaml/mapFP.ml @@ -34,7 +34,7 @@ let rec assoc_nocase at = function if String.uppercase at = String.uppercase a then v else assoc_nocase at avs (** Connect a change in the XML editor to the graphical rep *) -let update_wp utm_ref wp = function +let update_wp utm_ref (wp:MapWaypoints.waypoint) = function XmlEdit.Deleted -> wp#delete | XmlEdit.New_child _ -> failwith "update_wp" | XmlEdit.Modified attribs -> @@ -216,7 +216,7 @@ class flight_plan = fun ?edit geomap color fp_dtd xml -> ) path - method connect_selection = fun cb -> XmlEdit.connect_selection xml_tree_view cb + method connect_activated = fun cb -> XmlEdit.connect_activated xml_tree_view cb initializer ( (** Create a graphic waypoint when it is created from the xml editor *) diff --git a/sw/lib/ocaml/mapFP.mli b/sw/lib/ocaml/mapFP.mli index 0f3ea97dda..3bd970d76a 100644 --- a/sw/lib/ocaml/mapFP.mli +++ b/sw/lib/ocaml/mapFP.mli @@ -43,7 +43,7 @@ class flight_plan : method xml : Xml.xml method insert_path : (MapWaypoints.waypoint * float) list -> unit method highlight_stage : int -> int -> unit - method connect_selection : (XmlEdit.node->unit) -> unit + method connect_activated : (XmlEdit.node->unit) -> unit end (** Extracts [lat0] and [Lon0] attributes *) diff --git a/sw/lib/ocaml/mapWaypoints.ml b/sw/lib/ocaml/mapWaypoints.ml index f28fab4e97..971ec72b09 100644 --- a/sw/lib/ocaml/mapWaypoints.ml +++ b/sw/lib/ocaml/mapWaypoints.ml @@ -71,7 +71,7 @@ class waypoint = fun (group:group) (name :string) ?(alt=0.) wgs84 -> end method alt = alt method label = label - method xy = let a = item#i2w_affine in (a.(4), a.(5)) (*** item#i2w 0. 0. causes Seg Fault !***) + method xy = let a = item#i2w_affine in (a.(4), a.(5)) method move dx dy = item#move dx dy; label#move dx dy method edit = let dialog = GWindow.window ~border_width:10 ~title:"Waypoint Edit" () in @@ -81,7 +81,14 @@ class waypoint = fun (group:group) (name :string) ?(alt=0.) wgs84 -> let s = sprintf "WGS84 %s" (geomap#geo_string wgs84) in let ename = GEdit.entry ~text:name ~editable:false ~packing:dvbx#add () in let e_pos = GEdit.entry ~text:s ~packing:dvbx#add () in - let ea = GEdit.entry ~text:(string_of_float alt) ~packing:dvbx#add () in + let ha = GPack.hbox ~packing:dvbx#add () in + let minus10= GButton.button ~label:"-10" ~packing:ha#add () in + let ea = GEdit.entry ~text:(string_of_float alt) ~packing:ha#add () in + let plus10= GButton.button ~label:"+10" ~packing:ha#add () in + let change_alt = fun x -> + ea#set_text (string_of_float (float_of_string ea#text +. x)) in + ignore(minus10#connect#pressed (fun _ -> change_alt (-10.))); + ignore(plus10#connect#pressed (fun _ -> change_alt (10.))); let callback = fun _ -> self#set_name ename#text; @@ -100,21 +107,18 @@ class waypoint = fun (group:group) (name :string) ?(alt=0.) wgs84 -> (fun e -> ignore (e#connect#activate ~callback)) [ename; e_pos; ea]; ok#grab_default (); - ignore(ok#connect#clicked ~callback:dialog#destroy); + ignore(ok#connect#clicked ~callback:(fun _ -> callback (); dialog#destroy ())); dialog#show () - + val mutable motion = false method event (ev : GnoCanvas.item_event) = begin match ev with | `BUTTON_PRESS ev -> begin match GdkEvent.Button.button ev with - | 2 -> self#edit - | 3 -> - if (GToolbox.question_box ~title:"Confirm delete" ~buttons:["Cancel";"Delete"] ~default:2 (sprintf "Delete '%s' ?" name)) = 2 then - self#delete | 1 -> + motion <- false; let x = GdkEvent.Button.x ev and y = GdkEvent.Button.y ev in x0 <- x; y0 <- y; @@ -126,6 +130,7 @@ class waypoint = fun (group:group) (name :string) ?(alt=0.) wgs84 -> | `MOTION_NOTIFY ev -> let state = GdkEvent.Motion.state ev in if Gdk.Convert.test_modifier `BUTTON1 state then begin + motion <- true; let x = GdkEvent.Motion.x ev and y = GdkEvent.Motion.y ev in let dx = geomap#current_zoom *. (x-. x0) @@ -138,7 +143,8 @@ class waypoint = fun (group:group) (name :string) ?(alt=0.) wgs84 -> | `BUTTON_RELEASE ev -> if GdkEvent.Button.button ev = 1 then begin item#ungrab (GdkEvent.Button.time ev); - moved <- true + if not motion then + self#edit end | _ -> () end; @@ -149,11 +155,17 @@ class waypoint = fun (group:group) (name :string) ?(alt=0.) wgs84 -> method deleted = deleted method item = item method pos = geomap#of_world self#xy - method set wgs84 = + method set ?altitude ?(update=false) wgs84 = let (xw, yw) = geomap#world_of wgs84 and (xw0, yw0) = self#xy and z = geomap#zoom_adj#value in - self#move ((xw-.xw0)*.z) ((yw-.yw0)*.z) + self#move ((xw-.xw0)*.z) ((yw-.yw0)*.z); + begin + match altitude with + Some a -> alt <- a + | _ -> () + end; + if update then updated () method delete = deleted <- true; (* BOF *) item#destroy (); diff --git a/sw/lib/ocaml/mapWaypoints.mli b/sw/lib/ocaml/mapWaypoints.mli index 40e28c8d4a..10488197ed 100644 --- a/sw/lib/ocaml/mapWaypoints.mli +++ b/sw/lib/ocaml/mapWaypoints.mli @@ -50,7 +50,7 @@ class waypoint : method label : GnoCanvas.text method move : float -> float -> unit method name : string - method set : Latlong.geographic -> unit + method set : ?altitude:float -> ?update:bool -> Latlong.geographic -> unit method set_name : string -> unit method xy : float * float method zoom : float -> unit diff --git a/sw/lib/ocaml/xmlEdit.ml b/sw/lib/ocaml/xmlEdit.ml index face68bc90..acf98124ad 100644 --- a/sw/lib/ocaml/xmlEdit.ml +++ b/sw/lib/ocaml/xmlEdit.ml @@ -379,10 +379,10 @@ let connect = fun ((model, path):node) cb -> let current_cb = try model#get ~row ~column:event with _ -> fun _ -> () in model#set ~row ~column:event (fun e -> cb e; current_cb e) -let selection_cbs = Hashtbl.create 3 -let connect_selection = fun (model,_) cb -> - let l = try Hashtbl.find selection_cbs model with Not_found -> [] in - Hashtbl.replace selection_cbs model (cb::l) +let activated_cbs = Hashtbl.create 3 +let connect_activated = fun (model,_) cb -> + let l = try Hashtbl.find activated_cbs model with Not_found -> [] in + Hashtbl.replace activated_cbs model (cb::l) @@ -483,13 +483,16 @@ let create = fun ?(edit=true) dtd xml -> let attribs = tree_model#get ~row ~column:attributes in attribs_model#clear (); tag_of_last_selection := tree_model#get ~row ~column:tag_col; - set_attributes attribs_model attribs; - let cbs = try (Hashtbl.find selection_cbs tree_model) with Not_found -> [] in - List.iter (fun cb -> cb (tree_model, path)) cbs + set_attributes attribs_model attribs | _ -> () in let _c = tree_view#selection#connect#after#changed ~callback:selection_changed in + let _ = tree_view#connect#after#row_activated ~callback: + (fun path vcol -> + let cbs = try (Hashtbl.find activated_cbs tree_model) with Not_found -> [] in + List.iter (fun cb -> cb (tree_model, path)) cbs) in + if edit then begin let _c = add_context_menu tree_model tree_view (tree_menu_popup dtd) in let _c = add_context_menu attribs_model attribs_view ~noselection_menu:(add_one_menu dtd tree_view) (attribs_menu_popup dtd tree_view) in diff --git a/sw/lib/ocaml/xmlEdit.mli b/sw/lib/ocaml/xmlEdit.mli index 1d60054b16..299628549d 100644 --- a/sw/lib/ocaml/xmlEdit.mli +++ b/sw/lib/ocaml/xmlEdit.mli @@ -65,7 +65,7 @@ val add_child : node -> tag -> attributes -> node (** Modifications *) val connect : node -> (event -> unit) -> unit -val connect_selection : t -> (node -> unit) -> unit +val connect_activated : t -> (node -> unit) -> unit (** To be kept informed about modifications *) val selection : t -> node diff --git a/sw/simulator/sitl.ml b/sw/simulator/sitl.ml index 733c1a8be6..ea979798dd 100644 --- a/sw/simulator/sitl.ml +++ b/sw/simulator/sitl.ml @@ -107,6 +107,8 @@ module Make(A:Data.MISSION) = struct rc_channels; ignore (on_off#connect#toggled (fun () -> sliders#coerce#misc#set_sensitive on_off#active)); + on_off#set_active false; + let send_ppm = fun () -> if on_off#active then send_ppm () in diff --git a/sw/tools/gen_flight_plan.ml b/sw/tools/gen_flight_plan.ml index e900fd80fc..6679fcad62 100644 --- a/sw/tools/gen_flight_plan.ml +++ b/sw/tools/gen_flight_plan.ml @@ -374,7 +374,7 @@ let rec print_stage = fun index_of_waypoints sectors x -> lprintf "return;\n" | "set" -> stage (); - let var = parsed_attrib x "var" in + let var = ExtXml.attrib x "var" in let valuee = parsed_attrib x "value" in lprintf "%s = %s;\n" var valuee; begin @@ -474,16 +474,23 @@ let print_blocks = fun index_of_waypoints sectors bs -> List.iter (fun b -> incr block; print_block index_of_waypoints sectors b !block) bs -let define_home = fun waypoints -> - let rec loop i = function +let define_waypoint_ids = fun waypoints -> + let i = ref 0 in + List.iter (fun w -> + Xml2h.define (sprintf "WP_%s" (name_of w)) (string_of_int !i); + incr i) + waypoints + +let home = fun waypoints -> + let rec loop = function [] -> failwith "Waypoint 'HOME' required" | w::ws -> if name_of w = "HOME" then begin - Xml2h.define "WP_HOME" (string_of_int i); (float_attrib w "x", float_attrib w "y") end else - loop (i+1) ws in - loop 0 waypoints + loop ws in + loop waypoints + let check_distance = fun (hx, hy) max_d wp -> let x = float_attrib wp "x" @@ -717,7 +724,8 @@ let _ = let waypoints = dummy_waypoint :: Xml.children waypoints in - let (hx, hy) = define_home waypoints in + define_waypoint_ids waypoints; + let (hx, hy) = home waypoints in List.iter (check_distance (hx, hy) mdfh) waypoints; Xml2h.define "WAYPOINTS" "{ \\";