diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp
index 89244e87e7da56524b517610222255423388a950..7d42be8606f74fa2a08824b08e5d087c2b242db0 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 76d22730c68b46d06a8bf76fb30d1ad5538ad770..ba08048129954ea195b44a6f871a886666c79abd 100644
--- a/firmware/application/apps/ui_adsb_rx.cpp
+++ b/firmware/application/apps/ui_adsb_rx.cpp
@@ -92,12 +92,16 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
 		text_last_seen.set(to_string_dec_uint(age / 60) + " minutes ago");
 	
 	text_infos.set(entry_copy.info_string);
-	
+	if(entry_copy.velo.heading < 360 && entry_copy.velo.speed >=0){ //I don't like this but...
+		text_info2.set("Hdg:" + to_string_dec_uint(entry_copy.velo.heading) + " Spd:" + to_string_dec_int(entry_copy.velo.speed));
+	}else{
+		text_info2.set("");
+	}
 	text_frame_pos_even.set(to_string_hex_array(entry_copy.frame_pos_even.get_raw_data(), 14));
 	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() {
@@ -123,6 +127,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
 		&text_airline,
 		&text_country,
 		&text_infos,
+		&text_info2,
 		&text_frame_pos_even,
 		&text_frame_pos_odd,
 		&button_see_map
@@ -172,7 +177,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
 			GeoPos::alt_unit::FEET,
 			entry_copy.pos.latitude,
 			entry_copy.pos.longitude,
-			0,
+			entry_copy.velo.heading,
 			[this]() {
 				send_updates = false;
 			});
@@ -199,7 +204,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
 
 	auto frame = message->frame;
 	uint32_t ICAO_address = frame.get_ICAO_address();
-	
+
 	if (frame.check_CRC() && frame.get_ICAO_address()) {
 		rtcGetTime(&RTCD1, &datetime);
 		auto& entry = ::on_packet(recent, ICAO_address);
@@ -214,6 +219,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)) {
@@ -224,10 +230,10 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
 				entry.set_frame_pos(frame, raw_data[6] & 4);
 				
 				if (entry.pos.valid) {
-					str_info = "Alt:" + to_string_dec_uint(entry.pos.altitude) +
-						" Lat" + to_string_dec_int(entry.pos.latitude) +
+					str_info = "Alt:" + to_string_dec_int(entry.pos.altitude) +
+						" Lat:" + to_string_dec_int(entry.pos.latitude) +
 						"." + to_string_dec_int((int)abs(entry.pos.latitude * 1000) % 100, 2, '0') +
-						" Lon" + to_string_dec_int(entry.pos.longitude) +
+						" Lon:" + to_string_dec_int(entry.pos.longitude) +
 						"." + to_string_dec_int((int)abs(entry.pos.longitude * 1000) % 100, 2, '0');
 					
 					entry.set_info_string(str_info);
@@ -236,6 +242,13 @@ 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);
+				logentry += "Type:" + to_string_dec_uint(msg_sub) +
+							" Hdg:" + to_string_dec_uint(entry.velo.heading) +
+							" Spd: "+ to_string_dec_int(entry.velo.speed);
+				if (send_updates)
+					details_view->update(entry);
 			}
 		}
 		recent_entries_view.set_dirty(); 
diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp
index c0316623394bdbc794d3f6a9351aa7e7e3154312..dd7adb5a5d5d33b46b3d1c9ff3719cd3302b382b 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, 999 };
 	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;
@@ -146,8 +150,8 @@ private:
 		{ { 0 * 8, 2 * 16 }, "Last seen:", Color::light_grey() },
 		{ { 0 * 8, 3 * 16 }, "Airline:", Color::light_grey() },
 		{ { 0 * 8, 5 * 16 }, "Country:", Color::light_grey() },
-		{ { 0 * 8, 12 * 16 }, "Even position frame:", Color::light_grey() },
-		{ { 0 * 8, 14 * 16 }, "Odd position frame:", Color::light_grey() }
+		{ { 0 * 8, 13 * 16 }, "Even position frame:", Color::light_grey() },
+		{ { 0 * 8, 15 * 16 }, "Odd position frame:", Color::light_grey() }
 	};
 	
 	Text text_callsign {
@@ -174,17 +178,23 @@ private:
 		{ 0 * 8, 6 * 16, 30 * 8, 16 },
 		"-"
 	};
+
+	Text text_info2 {
+		{0*8, 7*16, 30*8, 16},
+		"-"
+	};
+
 	Text text_frame_pos_even {
-		{ 0 * 8, 13 * 16, 30 * 8, 16 },
+		{ 0 * 8, 14 * 16, 30 * 8, 16 },
 		"-"
 	};
 	Text text_frame_pos_odd {
-		{ 0 * 8, 15 * 16, 30 * 8, 16 },
+		{ 0 * 8, 16 * 16, 30 * 8, 16 },
 		"-"
 	};
 	
 	Button button_see_map {
-		{ 8 * 8, 8 * 16, 14 * 8, 3 * 16 },
+		{ 8 * 8, 9 * 16, 14 * 8, 3 * 16 },
 		"See on map"
 	};
 };
diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp
index f907825a7466204ffb30928e91f355456f337eff..91fcb545e648737c5c1bc70478b794944e0759fb 100644
--- a/firmware/application/ui/ui_geomap.cpp
+++ b/firmware/application/ui/ui_geomap.cpp
@@ -59,7 +59,7 @@ GeoPos::GeoPos(
 	set_altitude(0);
 	set_lat(0);
 	set_lon(0);
-	
+
 	const auto changed_fn = [this](int32_t) {
 		float lat_value = lat();
 		float lon_value = lon();
@@ -163,15 +163,22 @@ void GeoMap::paint(Painter& painter) {
 		prev_x_pos = x_pos;
 		prev_y_pos = y_pos;
 	}
-	
+	//center tag above point
+	if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces
+		painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_);
+	}
 	if (mode_ == PROMPT) {
 		// 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 {
+	} else if (angle_ < 360){
+		//if we have a valid angle draw bearing
 		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 {
+		//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 +238,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,11 +261,12 @@ 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;
 	geopos.set_lat(lat_);
 	geopos.set_lon(lon_);
+	geomap.set_angle(angle);
 	geomap.move(lon_, lat_);
 	geomap.set_dirty();
 }
@@ -269,7 +277,7 @@ void GeoMapView::setup() {
 	geopos.set_altitude(altitude_);
 	geopos.set_lat(lat_);
 	geopos.set_lon(lon_);
-	
+
 	geopos.on_change = [this](int32_t altitude, float lat, float lon) {
 		altitude_ = altitude;
 		lat_ = lat;
@@ -307,7 +315,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),
@@ -328,6 +336,7 @@ GeoMapView::GeoMapView(
 	
 	geomap.set_mode(mode_);
 	geomap.set_tag(tag);
+	geomap.set_angle(angle);
 	geomap.move(lon_, lat_);
 	
 	geopos.set_read_only(true);
diff --git a/firmware/application/ui/ui_geomap.hpp b/firmware/application/ui/ui_geomap.hpp
index 45a9479b1046db1ab0acbdb83e0e8cb5a20ec132..30fd69830436364a5df3e5ef6d6fc1f603e7ee84 100644
--- a/firmware/application/ui/ui_geomap.hpp
+++ b/firmware/application/ui/ui_geomap.hpp
@@ -129,8 +129,12 @@ public:
 		tag_ = new_tag;
 	}
 
+	void set_angle(uint16_t new_angle){
+		angle_ = new_angle;
+	}
+
 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 +145,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 +158,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 +177,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 +194,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 c730bbecbb97bd18c434b056e52a4f2cbf492099..3f8f4c23118cac696de720a8a580e23763d2ec1c 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,52 @@ 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 raw_ew = ((frame_data[5] & 0x03) << 8) | frame_data[6];
+		int32_t velo_ew = raw_ew - 1; //velocities are all offset by one (this is part of the spec)
+
+		int32_t raw_ns = ((frame_data[7] & 0x7f) << 3) | (frame_data[8] >> 5);
+		int32_t velo_ns = raw_ns - 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]&0x04) velo_ew *= -1; //check ew direction sign
+		if(frame_data[7]&0x80) 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
+			int16_t heading_temp = (int16_t)(atan2(velo_ew,velo_ns) * 180.0 / pi); 
+			// We don't want negative values but a 0-360 scale. 
+			if (heading_temp < 0) heading_temp += 360.0;
+			velo.heading = (uint16_t)heading_temp;
+		}
+		
+	}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 4e042ff21f6f17ee83c6df62ff30fe8212d6afe8..82d177e496e383e84bc7d14542e28e1d6f6d74ac 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 36685843d0e849d3262068bd5ec3b91c300487d9..a93abe6fb5403578dc009e0886f3e74a15222a49 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];
 	}