From f08949acd71eeed1504e049e252888378195774d Mon Sep 17 00:00:00 2001
From: Joel Wetzell <jwetzell@yahoo.com>
Date: Fri, 24 Jul 2020 16:09:21 -0500
Subject: [PATCH] Add Heading to ADSB and Map Updating

---
 firmware/application/apps/ais_app.cpp    |  2 +-
 firmware/application/apps/ui_adsb_rx.cpp |  7 +++-
 firmware/application/apps/ui_adsb_rx.hpp |  6 ++-
 firmware/application/ui/ui_geomap.cpp    | 20 ++++++----
 firmware/application/ui/ui_geomap.hpp    | 10 ++---
 firmware/common/adsb.cpp                 | 47 +++++++++++++++++++++++-
 firmware/common/adsb.hpp                 |  9 +++++
 firmware/common/adsb_frame.hpp           |  4 ++
 8 files changed, 87 insertions(+), 18 deletions(-)

diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp
index 89244e87..7d42be86 100644
--- a/firmware/application/apps/ais_app.cpp
+++ b/firmware/application/apps/ais_app.cpp
@@ -240,7 +240,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
 
 void AISRecentEntryDetailView::update_position() {
 	if (send_updates)
-		geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()));
+		geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading);
 }
 
 void AISRecentEntryDetailView::focus() {
diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp
index 76d22730..8a174e4c 100644
--- a/firmware/application/apps/ui_adsb_rx.cpp
+++ b/firmware/application/apps/ui_adsb_rx.cpp
@@ -97,7 +97,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
 	text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14));
 	
 	if (send_updates)
-		geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude);
+		geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading);
 }
 
 ADSBRxDetailsView::~ADSBRxDetailsView() {
@@ -172,7 +172,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
 			GeoPos::alt_unit::FEET,
 			entry_copy.pos.latitude,
 			entry_copy.pos.longitude,
-			0,
+			entry_copy.velo.heading,
 			[this]() {
 				send_updates = false;
 			});
@@ -214,6 +214,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
 		
 		if (frame.get_DF() == DF_ADSB) {
 			uint8_t msg_type = frame.get_msg_type();
+			uint8_t msg_sub = frame.get_msg_sub();
 			uint8_t * raw_data = frame.get_raw_data();
 			
 			if ((msg_type >= 1) && (msg_type <= 4)) {
@@ -236,6 +237,8 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
 					if (send_updates)
 						details_view->update(entry);
 				}
+			} else if(msg_type == 19 && (msg_sub >= 1 && msg_sub <= 4)){
+				entry.set_frame_velo(frame);
 			}
 		}
 		recent_entries_view.set_dirty(); 
diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp
index c0316623..816992ee 100644
--- a/firmware/application/apps/ui_adsb_rx.hpp
+++ b/firmware/application/apps/ui_adsb_rx.hpp
@@ -49,7 +49,7 @@ struct AircraftRecentEntry {
 	uint16_t hits { 0 };
 	uint32_t age { 0 };
 	adsb_pos pos { false, 0, 0, 0 };
-	
+	adsb_vel velo { false, 0, 0 };
 	ADSBFrame frame_pos_even { };
 	ADSBFrame frame_pos_odd { };
 	
@@ -86,6 +86,10 @@ struct AircraftRecentEntry {
 				pos = decode_frame_pos(frame_pos_even, frame_pos_odd);
 		}
 	}
+
+	void set_frame_velo(ADSBFrame& frame){
+		velo = decode_frame_velo(frame);
+	}
 	
 	void set_info_string(std::string& new_info_string) {
 		info_string = new_info_string;
diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp
index f907825a..20da068e 100644
--- a/firmware/application/ui/ui_geomap.cpp
+++ b/firmware/application/ui/ui_geomap.cpp
@@ -168,10 +168,15 @@ void GeoMap::paint(Painter& painter) {
 		// Cross
 		display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red());
 		display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red());
-	} else {
-		draw_bearing(r.center(), angle_, 10, Color::red());
-		//center tag above bearing
-		painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_);
+	} else if (angle_ < 360){
+		//if we have a valid angle just draw bearing
+		draw_bearing({ 120, 32 + 144 }, angle_, 10, Color::red());
+		painter.draw_string({ 120 - ((int)tag_.length() * 8 / 2), 32 + 144 - 32 }, style(), tag_);
+	}
+	else {
+		//draw a small cross
+		display.fill_rectangle({ r.center() - Point(8, 1), { 16, 2 } }, Color::red());
+		display.fill_rectangle({ r.center() - Point(1, 8), { 2, 16 } }, Color::red());
 	}
 }
 
@@ -231,7 +236,7 @@ void GeoMap::set_mode(GeoMapMode mode) {
 	mode_ = mode;
 }
 
-void GeoMap::draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color) {
+void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) {
 	Point arrow_a, arrow_b, arrow_c;
 	
 	for (size_t thickness = 0; thickness < 3; thickness++) {
@@ -254,9 +259,10 @@ void GeoMapView::focus() {
 		nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
 }
 
-void GeoMapView::update_position(float lat, float lon) {
+void GeoMapView::update_position(float lat, float lon, uint16_t angle) {
 	lat_ = lat;
 	lon_ = lon;
+	angle_ = angle;
 	geopos.set_lat(lat_);
 	geopos.set_lon(lon_);
 	geomap.move(lon_, lat_);
@@ -307,7 +313,7 @@ GeoMapView::GeoMapView(
 	GeoPos::alt_unit altitude_unit,
 	float lat,
 	float lon,
-	float angle,
+	uint16_t angle,
 	const std::function<void(void)> on_close
 ) : nav_ (nav),
 	altitude_ (altitude),
diff --git a/firmware/application/ui/ui_geomap.hpp b/firmware/application/ui/ui_geomap.hpp
index 45a9479b..e4e5ebff 100644
--- a/firmware/application/ui/ui_geomap.hpp
+++ b/firmware/application/ui/ui_geomap.hpp
@@ -130,7 +130,7 @@ public:
 	}
 
 private:
-	void draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color);
+	void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
 	
 	GeoMapMode mode_ { };
 	File map_file { };
@@ -141,7 +141,7 @@ private:
 	int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
 	float lat_ { };
 	float lon_ { };
-	float angle_ { };
+	uint16_t angle_ { };
 	std::string tag_ { };
 };
 
@@ -154,7 +154,7 @@ public:
 		GeoPos::alt_unit altitude_unit,
 		float lat,
 		float lon,
-		float angle,
+		uint16_t angle,
 		const std::function<void(void)> on_close = nullptr
 	);
 	GeoMapView(NavigationView& nav,
@@ -173,7 +173,7 @@ public:
 	
 	void focus() override;
 	
-	void update_position(float lat, float lon);
+	void update_position(float lat, float lon, uint16_t angle);
 	
 	std::string title() const override { return "Map view"; };
 
@@ -190,7 +190,7 @@ private:
 	GeoPos::alt_unit altitude_unit_ { };
 	float lat_ { };
 	float lon_ { };
-	float angle_ { };
+	uint16_t angle_ { };
 	std::function<void(void)> on_close_ { nullptr };
 	
 	bool map_opened { };
diff --git a/firmware/common/adsb.cpp b/firmware/common/adsb.cpp
index c730bbec..1831e89d 100644
--- a/firmware/common/adsb.cpp
+++ b/firmware/common/adsb.cpp
@@ -300,8 +300,8 @@ void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint
 	
 	v_rate_coded = (v_rate / 64) + 1;
 	
-	velo_ew_abs = abs(velo_ew);
-	velo_ns_abs = abs(velo_ns);
+	velo_ew_abs = abs(velo_ew) + 1; 
+	velo_ns_abs = abs(velo_ns) + 1;
 	v_rate_coded_abs = abs(v_rate_coded);
 	
 	make_frame_adsb(frame, ICAO_address);
@@ -317,4 +317,47 @@ void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint
 	frame.make_CRC();
 }
 
+// Decoding method from dump1090
+adsb_vel decode_frame_velo(ADSBFrame& frame){
+	adsb_vel velo {false, 0, 0};
+
+	uint8_t * frame_data = frame.get_raw_data();
+	uint8_t velo_type = frame.get_msg_sub();
+
+	if(velo_type >= 1 && velo_type <= 4){ //vertical rate is always present
+
+		velo.v_rate = (((frame_data[8] & 0x07 ) << 6) | ((frame_data[9]) >> 2) - 1) * 64;
+
+		if((frame_data[8] & 0x8) >> 3) velo.v_rate *= -1; //check v_rate sign
+	}
+
+	if(velo_type == 1 || velo_type == 2){ //Ground Speed
+		int32_t velo_ew = (((frame_data[5] & 0x03) << 8) | frame_data[6]) - 1;
+		int32_t velo_ns = ((frame_data[7] & 0x7f) << 3) | ((frame_data[8]) >> 5) - 1;
+
+		if (velo_type == 2){ // supersonic indicator so multiply by 4
+			velo_ew = velo_ew << 2;
+			velo_ns = velo_ns << 2;
+		}
+
+		if((frame_data[5]&4) >> 2) velo_ew *= -1; //check ew direction sign
+		if((frame_data[7]&0x80) >> 7) velo_ns *= -1; //check ns direction sign
+
+		velo.speed = sqrt(velo_ns*velo_ns + velo_ew*velo_ew);
+		if(velo.speed){
+			//calculate heading in degrees from ew/ns velocities
+			velo.heading = (uint16_t)(atan2(velo_ew,velo_ns) * 180.0 / pi); 
+			// We don't want negative values but a 0-360 scale. 
+			if (velo.heading < 0) velo.heading += 360.0;
+		}
+		
+	}else if(velo_type == 3 || velo_type == 4){ //Airspeed
+		velo.valid = frame_data[5] & (1<<2);
+		velo.heading = ((((frame_data[5] & 0x03)<<8) | frame_data[6]) * 45) << 7;
+	} 
+
+	return velo;
+
+}
+
 } /* namespace adsb */
diff --git a/firmware/common/adsb.hpp b/firmware/common/adsb.hpp
index 4e042ff2..82d177e4 100644
--- a/firmware/common/adsb.hpp
+++ b/firmware/common/adsb.hpp
@@ -56,6 +56,13 @@ struct adsb_pos {
 	int32_t altitude;
 };
 
+struct adsb_vel {
+	bool valid;
+	int32_t speed;  //knot
+	uint16_t heading; //degree
+	int32_t v_rate; //ft/min
+};
+
 const float CPR_MAX_VALUE = 131072.0;
 
 const float adsb_lat_lut[58] = {
@@ -89,6 +96,8 @@ adsb_pos decode_frame_pos(ADSBFrame& frame_even, ADSBFrame& frame_odd);
 void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed,
 	const float angle, const int32_t v_rate);
 
+adsb_vel decode_frame_velo(ADSBFrame& frame);
+
 //void encode_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code);
 
 void encode_frame_squawk(ADSBFrame& frame, const uint32_t squawk);
diff --git a/firmware/common/adsb_frame.hpp b/firmware/common/adsb_frame.hpp
index 36685843..a93abe6f 100644
--- a/firmware/common/adsb_frame.hpp
+++ b/firmware/common/adsb_frame.hpp
@@ -41,6 +41,10 @@ public:
 		return (raw_data[4] >> 3);
 	}
 
+	uint8_t get_msg_sub() {
+		return (raw_data[4] & 7);
+	}
+
 	uint32_t get_ICAO_address() {
 		return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3];
 	}
-- 
GitLab