From 802b91964b07d4dbde5f07809d6b00f0117dff3e Mon Sep 17 00:00:00 2001 From: furrtek Date: Fri, 14 Jul 2017 10:02:21 +0100 Subject: [PATCH] ADS-B receive app debug code --- firmware/application/CMakeLists.txt | 3 +- firmware/application/protocols/adsb.hpp | 1 + firmware/application/ui_adsb_rx.cpp | 255 ++++++++++++++++++ firmware/application/ui_adsb_rx.hpp | 99 +++++++ .../{ui_adsbtx.cpp => ui_adsb_tx.cpp} | 2 +- .../{ui_adsbtx.hpp => ui_adsb_tx.hpp} | 0 firmware/application/ui_navigation.cpp | 5 +- 7 files changed, 361 insertions(+), 4 deletions(-) create mode 100644 firmware/application/ui_adsb_rx.cpp create mode 100644 firmware/application/ui_adsb_rx.hpp rename firmware/application/{ui_adsbtx.cpp => ui_adsb_tx.cpp} (99%) rename firmware/application/{ui_adsbtx.hpp => ui_adsb_tx.hpp} (100%) diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 20f65580..ca340f64 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -160,7 +160,8 @@ set(CPPSRC ${COMMON}/ui_painter.cpp ${COMMON}/ui_focus.cpp ui_about.cpp - ui_adsbtx.cpp + ui_adsb_tx.cpp + ui_adsb_rx.cpp ui_aprstx.cpp ui_modemsetup.cpp ui_alphanum.cpp diff --git a/firmware/application/protocols/adsb.hpp b/firmware/application/protocols/adsb.hpp index bcd98954..5785d680 100644 --- a/firmware/application/protocols/adsb.hpp +++ b/firmware/application/protocols/adsb.hpp @@ -29,6 +29,7 @@ namespace adsb { const char icao_id_lut[65] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"; + const uint8_t adsb_preamble[16] = { 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0 }; void make_frame_mode_s(uint8_t * const adsb_frame, const uint32_t ICAO_address); diff --git a/firmware/application/ui_adsb_rx.cpp b/firmware/application/ui_adsb_rx.cpp new file mode 100644 index 00000000..4d0c2c53 --- /dev/null +++ b/firmware/application/ui_adsb_rx.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui_adsb_rx.hpp" +#include "ui_alphanum.hpp" + +#include "adsb.hpp" +#include "string_format.hpp" +#include "portapack.hpp" +#include "baseband_api.hpp" +#include "portapack_persistent_memory.hpp" + +#include +#include + +using namespace adsb; +using namespace portapack; + +namespace ui { + +void ADSBRxView::focus() { + offset_field.focus(); + offset_field.set_value(13179); +} + +ADSBRxView::~ADSBRxView() { + //transmitter_model.disable(); + //baseband::shutdown(); +} + +void ADSBRxView::analyze(uint64_t offset) { + size_t c; + Coord lcd_x = 0, lcd_y = 0; + int16_t file_data[128]; // 256 bytes / 2 IQ / 16 bits = 64 samples + complex8_t iq_data[256]; // 256 samples + uint64_t file_offset = 0; + uint8_t data_put = 0, data_get = 0; + int16_t f_re, f_im; + int8_t re, im; + uint8_t level, bit, byte; + Color mark_color; + size_t preamble_count = 0, null_count = 0, bit_count = 0, sample_count = 0; + bool decoding = false; + float prev_mag, mag; + float threshold, threshold_low, threshold_high; + std::string bits; + std::string hex_str; + bool confidence, first_in_window, last_in_window; + uint8_t frame_index; + uint8_t adsb_frame[256]; + + // Use vectors and std::rotate ! + float shifter[16]; + uint8_t shifter_bits[16]; + + iq_file.seek(offset * 256); + + for (;;) { + if (data_put == data_get) { + auto result = iq_file.read(file_data, 256); + if (!result.is_error()) { + // Convert file's C16 to C8 + for (c = 0; c < (result.value() / 4); c++) { + f_re = file_data[(c * 2) + 0] >> 5; + f_im = file_data[(c * 2) + 1] >> 5; + iq_data[data_put] = { f_re, f_im }; + data_put++; + } + + file_offset += result.value(); + + if (file_offset >= 4096) { + text_debug_e.set("Read @ " + to_string_dec_uint(offset * 256 / 2000 / 4) + "ms "); + break; + } + } else { + text_debug_a.set("Read error"); + return; + } + } + + re = iq_data[data_get].real(); + im = iq_data[data_get].imag(); + mag = __builtin_sqrtf((re * re) + (im * im)) * k; + data_get++; + + // Only used for preamble detection and visualisation + level = (mag < 0.3) ? 0 : // Blank weak signals + (mag > prev_mag) ? 1 : 0; + + if (decoding) { + // Decode + mark_color = Color::grey(); + if (sample_count & 1) { + if ((prev_mag < threshold_low) && (mag < threshold_low)) { + // Both under window, silence. + mark_color = Color::black(); + if (null_count > 3) { + text_debug_b.set("Bits:" + bits.substr(0, 25)); + text_debug_c.set("Hex:" + hex_str.substr(0, 26)); + text_debug_d.set("DF=" + to_string_dec_uint(adsb_frame[0] >> 3) + " ICAO=" + to_string_hex((adsb_frame[1] << 16) + (adsb_frame[2] << 8) + adsb_frame[3], 6)); + decoding = false; + } else + null_count++; + + confidence = false; + if (prev_mag > mag) + bit = 1; + else + bit = 0; + + mark_color = bit ? Color::dark_red() : Color::dark_green(); + + } else { + + null_count = 0; + + first_in_window = ((prev_mag >= threshold_low) && (prev_mag <= threshold_high)); + last_in_window = ((mag >= threshold_low) && (mag <= threshold_high)); + + if ((first_in_window && !last_in_window) || (!first_in_window && last_in_window)) { + confidence = true; + if (prev_mag > mag) + bit = 1; + else + bit = 0; + } else { + confidence = false; + if (prev_mag > mag) + bit = 1; + else + bit = 0; + } + + mark_color = bit ? Color::red() : Color::green(); + } + + bits.append(bit ? "1" : "0"); + byte = bit | (byte << 1); + bit_count++; + if (!(bit_count & 7)) { + // Got one byte + hex_str += to_string_hex(byte, 2); + adsb_frame[frame_index++] = byte; + } + } + sample_count++; + } else { + // Look for preamble + mark_color = Color::white(); + + // TODO: Stop using 2 separate shift registers for float and bit... + for (c = 0; c < 15; c++) + shifter[c] = shifter[c + 1]; + shifter[15] = mag; + + for (c = 0; c < 15; c++) + shifter_bits[c] = shifter_bits[c + 1]; + shifter_bits[15] = level; + + if (!memcmp(shifter_bits, adsb_preamble, 16)) { + preamble_count++; + if (preamble_count == 1) { + // Try decoding the first frame found + decoding = true; + sample_count = 0; + frame_index = 0; + + // Compute preamble pulses power to set thresholds + threshold = (shifter[0] + shifter[2] + shifter[7] + shifter[9]) / 4; + threshold_high = threshold * 1.414; // +3dB + threshold_low = threshold * 0.707; // -3dB + } + } + } + + prev_mag = mag; + + /*if (!lcd_y && !lcd_x) { + for (c = 0; c < 16; c++) + display.fill_rectangle({c * 4, 300, 4, 16}, shifter[c] ? Color::white() : Color::blue()); + }*/ + + if (lcd_y < 188) { + mag *= 16; + // Background + display.fill_rectangle({lcd_x, 100 + lcd_y, 2, 32 - mag}, decoding ? mark_color : Color::grey()); + // Bar + display.fill_rectangle({lcd_x, 132 + lcd_y - mag, 2, mag}, Color::white()); + // Level + display.fill_rectangle({lcd_x, 132 + lcd_y, 2, 4}, decoding ? ((sample_count & 1) ? Color::white() : Color::light_grey()) : (level ? Color::white() : Color::dark_blue())); + if (lcd_x == 238) { + lcd_x = 0; + lcd_y += 40; + } else { + lcd_x += 2; + } + } + } + + text_debug_a.set("Found " + to_string_dec_uint(preamble_count) + " preambles "); +} + +ADSBRxView::ADSBRxView(NavigationView& nav) { + uint32_t c; + + //baseband::run_image(portapack::spi_flash::image_tag_adsb_rx); + + add_children({ + &labels, + &offset_field, + &button_ffw, + &text_debug_a, + &text_debug_b, + &text_debug_c, + &text_debug_d, + &text_debug_e + }); + + // File must be 16bit complex @ 2Msps ! + + auto result = iq_file.open("ADSB.C16"); + if (result.is_valid()) { + text_debug_a.set("Can't open file"); + } + + offset_field.on_change = [this, &nav](int32_t value) { + analyze(value); + }; + + button_ffw.on_select = [this, &nav](Button&) { + offset_field.set_value(offset_field.value() + 1562); + }; +} + +} /* namespace ui */ diff --git a/firmware/application/ui_adsb_rx.hpp b/firmware/application/ui_adsb_rx.hpp new file mode 100644 index 00000000..1844e36c --- /dev/null +++ b/firmware/application/ui_adsb_rx.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "file.hpp" +//#include "ui_textentry.hpp" +//#include "ui_widget.hpp" +#include "ui_navigation.hpp" +#include "ui_font_fixed_8x16.hpp" + +#include "message.hpp" +#include "portapack.hpp" + +namespace ui { + +class ADSBRxView : public View { +public: + ADSBRxView(NavigationView& nav); + ~ADSBRxView(); + + void focus() override; + + std::string title() const override { return "ADS-B debug"; }; + +private: + static constexpr float k = 1.0f / 128.0f; + + File iq_file { }; + + void analyze(uint64_t offset); + + Labels labels { + { { 0 * 8, 0 * 8 }, "Test", Color::light_grey() } + }; + + NumberField offset_field { + { 0, 0 }, + 6, + { 0, 819200 }, // * 256 -> file offset + 1, + '0' + }; + Text text_debug_e { + { 7 * 8, 0 * 16, 14 * 8, 16 }, + "-" + }; + + Text text_debug_a { + { 0 * 8, 1 * 16, 30 * 8, 16 }, + "-" + }; + Text text_debug_b { + { 0 * 8, 2 * 16, 30 * 8, 16 }, + "-" + }; + Text text_debug_c { + { 0 * 8, 3 * 16, 30 * 8, 16 }, + "-" + }; + Text text_debug_d { + { 0 * 8, 4 * 16, 30 * 8, 16 }, + "-" + }; + + Button button_ffw { + { 176, 0 * 16, 64, 16 }, + "+50ms" + }; + + /* + MessageHandlerRegistration message_handler_tx_done { + Message::ID::TXDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_txdone(message.done); + } + };*/ +}; + +} /* namespace ui */ diff --git a/firmware/application/ui_adsbtx.cpp b/firmware/application/ui_adsb_tx.cpp similarity index 99% rename from firmware/application/ui_adsbtx.cpp rename to firmware/application/ui_adsb_tx.cpp index 9181a533..0b49a99b 100644 --- a/firmware/application/ui_adsbtx.cpp +++ b/firmware/application/ui_adsb_tx.cpp @@ -20,7 +20,7 @@ * Boston, MA 02110-1301, USA. */ -#include "ui_adsbtx.hpp" +#include "ui_adsb_tx.hpp" #include "ui_alphanum.hpp" #include "adsb.hpp" diff --git a/firmware/application/ui_adsbtx.hpp b/firmware/application/ui_adsb_tx.hpp similarity index 100% rename from firmware/application/ui_adsbtx.hpp rename to firmware/application/ui_adsb_tx.hpp diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index b3666f52..d25fa57f 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -31,7 +31,8 @@ #include "portapack_persistent_memory.hpp" #include "ui_about.hpp" -#include "ui_adsbtx.hpp" +#include "ui_adsb_tx.hpp" +#include "ui_adsb_rx.hpp" #include "ui_aprstx.hpp" #include "ui_bht_tx.hpp" #include "ui_coasterp.hpp" @@ -282,7 +283,7 @@ void NavigationView::focus() { ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { add_items({ - { "ADS-B: Planes", ui::Color::grey(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, + { "ADS-B: Planes", ui::Color::orange(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, { "AIS: Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push(); } }, { "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.push(); } }, { "Audio", ui::Color::green(), nullptr, [&nav](){ nav.push(false); } },