diff --git a/firmware/application/protocols/adsb.cpp b/firmware/application/protocols/adsb.cpp index bd233c5d..5312e811 100644 --- a/firmware/application/protocols/adsb.cpp +++ b/firmware/application/protocols/adsb.cpp @@ -26,25 +26,102 @@ namespace adsb { -void make_frame_mode_s(uint8_t * const adsb_frame, const uint32_t ICAO_address) { - adsb_frame[0] = (17 << 3) | 5; // DF and CA - adsb_frame[1] = ICAO_address >> 16; - adsb_frame[2] = (ICAO_address >> 8) & 0xFF; - adsb_frame[3] = ICAO_address & 0xFF; - - memset(&adsb_frame[4], 0, 10); +uint8_t adsb_frame::get_DF() { + return (raw_data[0] >> 3); } -void generate_frame_id(uint8_t * const adsb_frame, const uint32_t ICAO_address, std::string & callsign) { +uint8_t adsb_frame::get_msg_type() { + return (raw_data[4] >> 3); +} + +uint32_t adsb_frame::get_ICAO_address() { + return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3]; +} + +void adsb_frame::clear() { + index = 0; + memset(raw_data, 0, 14); +} + +void adsb_frame::push_byte(uint8_t byte) { + if (index >= 14) + return; + + raw_data[index++] = byte; +} + +uint8_t adsb_frame::get_byte(uint8_t index) { + if (index >= 14) + return 0; + + return raw_data[index]; +} + +std::string adsb_frame::get_callsign() { + uint64_t callsign_coded = 0; + uint32_t c; + std::string callsign = ""; + + // Frame bytes to long + for (c = 5; c < 11; c++) { + callsign_coded <<= 8; + callsign_coded |= raw_data[c]; + } + + // Long to 6-bit characters + for (c = 0; c < 8; c++) { + callsign.append(1, icao_id_lut[(callsign_coded >> 42) & 0x3F]); + callsign_coded <<= 6; + } + + return callsign; +} + +void adsb_frame::make_CRC() { + uint8_t adsb_crc[14]; // Temp buffer + uint8_t b, c, s, bitn; + const uint32_t crc_poly = 0x1205FFF; + + // Clear CRC + raw_data[11] = 0x00; + raw_data[12] = 0x00; + raw_data[13] = 0x00; + + // Compute CRC + memcpy(adsb_crc, raw_data, 14); + for (c = 0; c < 11; c++) { + for (b = 0; b < 8; b++) { + if ((adsb_crc[c] << b) & 0x80) { + for (s = 0; s < 25; s++) { + bitn = (c * 8) + b + s; + if ((crc_poly >> s) & 1) adsb_crc[bitn >> 3] ^= (0x80 >> (bitn & 7)); + } + } + } + } + + // Insert CRC in frame + memcpy(&raw_data[11], &adsb_crc[11], 3); +} + +void make_frame_mode_s(adsb_frame& frame, const uint32_t ICAO_address) { + frame.clear(); + frame.push_byte((17 << 3) | 5); // DF17 and CA + frame.push_byte(ICAO_address >> 16); + frame.push_byte(ICAO_address >> 8); + frame.push_byte(ICAO_address & 0xFF); +} + +void generate_frame_id(adsb_frame& frame, const uint32_t ICAO_address, std::string & callsign) { std::string callsign_formatted(8, '_'); uint64_t callsign_coded = 0; uint32_t c, s; char ch; - make_frame_mode_s(adsb_frame, ICAO_address); + make_frame_mode_s(frame, ICAO_address); + + frame.push_byte(4 << 3); // TC = 4: Aircraft ID - adsb_frame[4] = 4 << 3; // TC = 4: Aircraft ID - // Translate and encode callsign for (c = 0; c < 8; c++) { ch = callsign[c]; @@ -62,18 +139,18 @@ void generate_frame_id(uint8_t * const adsb_frame, const uint32_t ICAO_address, // Insert callsign in frame for (c = 0; c < 6; c++) - adsb_frame[c + 5] = (callsign_coded >> ((5 - c) * 8)) & 0xFF; + frame.push_byte((callsign_coded >> ((5 - c) * 8)) & 0xFF); - ADSB_generate_CRC(adsb_frame); + frame.make_CRC(); } -void generate_frame_emergency(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint8_t code) { - make_frame_mode_s(adsb_frame, ICAO_address); +void generate_frame_emergency(adsb_frame& frame, const uint32_t ICAO_address, const uint8_t code) { + make_frame_mode_s(frame, ICAO_address); - adsb_frame[4] = (28 << 3) + 1; // TC = 28 (Emergency), subtype = 1 (Emergency) - adsb_frame[5] = code << 5; + frame.push_byte((28 << 3) + 1); // TC = 28 (Emergency), subtype = 1 (Emergency) + frame.push_byte(code << 5); - ADSB_generate_CRC(adsb_frame); + frame.make_CRC(); } /* @@ -118,31 +195,4 @@ void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, } */ -void ADSB_generate_CRC(uint8_t * const in_frame) { - uint8_t adsb_crc[14]; // Temp buffer - uint8_t b, c, s, bitn; - const uint32_t crc_poly = 0x1205FFF; - - // Clear CRC - in_frame[11] = 0x00; - in_frame[12] = 0x00; - in_frame[13] = 0x00; - - // Compute CRC - memcpy(adsb_crc, in_frame, 14); - for (c = 0; c < 11; c++) { - for (b = 0; b < 8; b++) { - if ((adsb_crc[c] << b) & 0x80) { - for (s = 0; s < 25; s++) { - bitn = (c * 8) + b + s; - if ((crc_poly >> s) & 1) adsb_crc[bitn >> 3] ^= (0x80 >> (bitn & 7)); - } - } - } - } - - // Insert CRC in frame - memcpy(&in_frame[11], &adsb_crc[11], 3); -} - } /* namespace adsb */ diff --git a/firmware/application/protocols/adsb.hpp b/firmware/application/protocols/adsb.hpp index 5785d680..6b047cd0 100644 --- a/firmware/application/protocols/adsb.hpp +++ b/firmware/application/protocols/adsb.hpp @@ -26,19 +26,35 @@ #ifndef __ADSB_H__ #define __ADSB_H__ +#define ADSB_PREAMBLE_LENGTH 16 + 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 }; +struct adsb_frame { +public: + uint8_t get_DF(); + uint8_t get_msg_type(); + uint32_t get_ICAO_address(); + std::string get_callsign(); - void make_frame_mode_s(uint8_t * const adsb_frame, const uint32_t ICAO_address); - - void generate_frame_id(uint8_t * const adsb_frame, const uint32_t ICAO_address, std::string & callsign); - //void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude, - // const float latitude, const float longitude); - void generate_frame_emergency(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint8_t code); - - void ADSB_generate_CRC(uint8_t * const in_message); + void push_byte(uint8_t byte); + uint8_t get_byte(uint8_t index); // DEBUG + void make_CRC(); + void clear(); + +private: + uint8_t index { 0 }; + uint8_t raw_data[14] { }; // 112 bits at most +}; + +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(adsb_frame& frame, const uint32_t ICAO_address); +void generate_frame_id(adsb_frame&, const uint32_t ICAO_address, std::string& callsign); +//void generate_frame_pos(uint8_t * const adsb_frame, const uint32_t ICAO_address, const uint32_t altitude, +// const float latitude, const float longitude); +void generate_frame_emergency(adsb_frame&, const uint32_t ICAO_address, const uint8_t code); } /* namespace adsb */ diff --git a/firmware/application/ui_adsb_rx.cpp b/firmware/application/ui_adsb_rx.cpp index 4d0c2c53..80ec631d 100644 --- a/firmware/application/ui_adsb_rx.cpp +++ b/firmware/application/ui_adsb_rx.cpp @@ -47,32 +47,28 @@ ADSBRxView::~ADSBRxView() { //baseband::shutdown(); } -void ADSBRxView::analyze(uint64_t offset) { - size_t c; +bool ADSBRxView::analyze(uint64_t offset) { Coord lcd_x = 0, lcd_y = 0; + adsb_frame frame; 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; + uint32_t c; 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 prev_mag = 0, 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]; + std::pair shifter[ADSB_PREAMBLE_LENGTH]; - // Use vectors and std::rotate ! - float shifter[16]; - uint8_t shifter_bits[16]; - - iq_file.seek(offset * 256); + iq_file.seek(offset * 2048); // 256 for (;;) { if (data_put == data_get) { @@ -80,21 +76,21 @@ void ADSBRxView::analyze(uint64_t offset) { 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_re = file_data[(c * 2) + 0] >> 5; // >> 8 (<< 3 amp.) f_im = file_data[(c * 2) + 1] >> 5; - iq_data[data_put] = { f_re, f_im }; + iq_data[data_put] = { (int8_t)f_re, (int8_t)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 "); + if (file_offset >= 2048) { + //text_debug_e.set("Read @ " + to_string_dec_uint(offset * 256 / 2000 / 4) + "ms "); break; } } else { text_debug_a.set("Read error"); - return; + return false; } } @@ -110,6 +106,8 @@ void ADSBRxView::analyze(uint64_t offset) { if (decoding) { // Decode mark_color = Color::grey(); + + // 1 bit lasts 2 samples if (sample_count & 1) { if ((prev_mag < threshold_low) && (mag < threshold_low)) { // Both under window, silence. @@ -117,7 +115,15 @@ void ADSBRxView::analyze(uint64_t offset) { 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)); + text_debug_d.set("DF=" + to_string_dec_uint(frame.get_DF()) + " ICAO=" + to_string_hex(frame.get_ICAO_address(), 6)); + if ((frame.get_DF() == 17) && (frame.get_msg_type() >= 1) && (frame.get_msg_type() <= 4)) { + text_debug_a.set("Callsign:" + frame.get_callsign()); + return true; + } else { + text_debug_a.set("No ID data"); + return false; + } + decoding = false; } else null_count++; @@ -154,13 +160,13 @@ void ADSBRxView::analyze(uint64_t offset) { mark_color = bit ? Color::red() : Color::green(); } - bits.append(bit ? "1" : "0"); + bits.append(bit ? "1" : "0"); // DEBUG 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; + hex_str += to_string_hex(byte, 2); // DEBUG + frame.push_byte(byte); } } sample_count++; @@ -168,25 +174,26 @@ void ADSBRxView::analyze(uint64_t offset) { // Look for preamble mark_color = Color::white(); - // TODO: Stop using 2 separate shift registers for float and bit... - for (c = 0; c < 15; c++) + // Shift + for (c = 0; c < (ADSB_PREAMBLE_LENGTH - 1); c++) shifter[c] = shifter[c + 1]; - shifter[15] = mag; + shifter[15] = std::make_pair(mag, level); - for (c = 0; c < 15; c++) - shifter_bits[c] = shifter_bits[c + 1]; - shifter_bits[15] = level; - - if (!memcmp(shifter_bits, adsb_preamble, 16)) { + // Compare + for (c = 0; c < ADSB_PREAMBLE_LENGTH; c++) { + if (shifter[c].second != adsb_preamble[c]) + break; + } + + if (c == ADSB_PREAMBLE_LENGTH) { 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 = (shifter[0].first + shifter[2].first + shifter[7].first + shifter[9].first) / 4; threshold_high = threshold * 1.414; // +3dB threshold_low = threshold * 0.707; // -3dB } @@ -200,28 +207,29 @@ void ADSBRxView::analyze(uint64_t offset) { 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; + if (preamble_count) { + if (lcd_y < 188) { + mag *= 16; + // Background + display.fill_rectangle({lcd_x, 100 + lcd_y, 2, 32 - (int)mag}, decoding ? mark_color : Color::grey()); + // Bar + display.fill_rectangle({lcd_x, 132 + lcd_y - (int)mag, 2, (int)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 "); + return false; } ADSBRxView::ADSBRxView(NavigationView& nav) { - uint32_t c; //baseband::run_image(portapack::spi_flash::image_tag_adsb_rx); @@ -244,11 +252,16 @@ ADSBRxView::ADSBRxView(NavigationView& nav) { } offset_field.on_change = [this, &nav](int32_t value) { - analyze(value); + // TODO }; button_ffw.on_select = [this, &nav](Button&) { - offset_field.set_value(offset_field.value() + 1562); + while (!analyze(f_offset)) { + f_offset++; + } + offset_field.set_value(f_offset); + f_offset++; + //offset_field.set_value(offset_field.value() + 1562); }; } diff --git a/firmware/application/ui_adsb_rx.hpp b/firmware/application/ui_adsb_rx.hpp index 1844e36c..4e6d0bc9 100644 --- a/firmware/application/ui_adsb_rx.hpp +++ b/firmware/application/ui_adsb_rx.hpp @@ -45,8 +45,9 @@ private: static constexpr float k = 1.0f / 128.0f; File iq_file { }; + size_t f_offset { 0 }; - void analyze(uint64_t offset); + bool analyze(uint64_t offset); Labels labels { { { 0 * 8, 0 * 8 }, "Test", Color::light_grey() } @@ -82,8 +83,8 @@ private: }; Button button_ffw { - { 176, 0 * 16, 64, 16 }, - "+50ms" + { 184, 0 * 16, 56, 16 }, + "FFW" }; /* diff --git a/firmware/application/ui_adsb_tx.cpp b/firmware/application/ui_adsb_tx.cpp index 0b49a99b..aaef02f0 100644 --- a/firmware/application/ui_adsb_tx.cpp +++ b/firmware/application/ui_adsb_tx.cpp @@ -23,7 +23,6 @@ #include "ui_adsb_tx.hpp" #include "ui_alphanum.hpp" -#include "adsb.hpp" #include "string_format.hpp" #include "portapack.hpp" #include "baseband_api.hpp" @@ -56,19 +55,19 @@ void ADSBTxView::generate_frame() { uint32_t c; std::string str_debug; - generate_frame_id(adsb_frame, sym_icao.value_hex_u64(), callsign); + generate_frame_id(frame, sym_icao.value_hex_u64(), callsign); memset(adsb_bin, 0, 112); // Convert to binary (1 byte per bit, faster for baseband code) for (c = 0; c < 112; c++) { - if ((adsb_frame[c >> 3] << (c & 7)) & 0x80) + if ((frame.get_byte(c >> 3) << (c & 7)) & 0x80) adsb_bin[c] = 1; } // Display in hex for debug - text_frame_a.set(to_string_hex_array(&adsb_frame[0], 7)); - text_frame_b.set(to_string_hex_array(&adsb_frame[7], 7)); + //text_frame_a.set(to_string_hex_array(frame.get_byte(0), 7)); + //text_frame_b.set(to_string_hex_array(frame.get_byte(7), 7)); button_callsign.set_text(callsign); } @@ -79,7 +78,7 @@ bool ADSBTxView::start_tx() { memcpy(shared_memory.bb_data.data, adsb_bin, 112); baseband::set_adsb(); - transmitter_model.set_tuning_frequency(1090000000); // FOR TESTING - DEBUG + transmitter_model.set_tuning_frequency(434000000); // DEBUG transmitter_model.set_sampling_rate(4000000U); transmitter_model.set_rf_amp(true); transmitter_model.set_vga(40); diff --git a/firmware/application/ui_adsb_tx.hpp b/firmware/application/ui_adsb_tx.hpp index 2dc03d60..b45938cf 100644 --- a/firmware/application/ui_adsb_tx.hpp +++ b/firmware/application/ui_adsb_tx.hpp @@ -21,6 +21,7 @@ */ #include "ui.hpp" +#include "adsb.hpp" #include "ui_textentry.hpp" #include "ui_widget.hpp" #include "ui_navigation.hpp" @@ -31,6 +32,8 @@ #include "transmitter_model.hpp" #include "portapack.hpp" +using namespace adsb; + namespace ui { class ADSBTxView : public View { @@ -55,7 +58,7 @@ private: std::string callsign = "KLM1023 "; - uint8_t adsb_frame[14]; // 112 bit data block as 14 bytes + adsb_frame frame { }; uint8_t adsb_bin[112]; // 112 bit data block bool start_tx(); diff --git a/firmware/tools/make_bitmap.py b/firmware/tools/make_bitmap.py old mode 100644 new mode 100755 diff --git a/firmware/tools/make_dcs.py b/firmware/tools/make_dcs.py old mode 100644 new mode 100755