diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 65b3bdf34..1020ed309 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -270,11 +270,9 @@ set(CPPSRC ui/ui_bmpview.cpp apps/ais_app.cpp apps/analog_audio_app.cpp - apps/ble_comm_app.cpp apps/ble_rx_app.cpp apps/ble_tx_app.cpp apps/capture_app.cpp - apps/ert_app.cpp apps/pocsag_app.cpp apps/soundboard_app.cpp apps/ui_about_simple.cpp diff --git a/firmware/application/apps/ble_comm_app.cpp b/firmware/application/apps/ble_comm_app.cpp index 331da938b..1f45eccb7 100644 --- a/firmware/application/apps/ble_comm_app.cpp +++ b/firmware/application/apps/ble_comm_app.cpp @@ -69,7 +69,7 @@ static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) { } void BLECommView::focus() { - options_channel.focus(); + field_frequency.focus(); } BLECommView::BLECommView(NavigationView& nav) @@ -79,10 +79,7 @@ BLECommView::BLECommView(NavigationView& nav) &field_rf_amp, &field_lna, &field_vga, - &options_channel, &field_frequency, - &label_send_adv, - &button_send_adv, &check_log, &label_packets_sent, &text_packets_sent, @@ -90,10 +87,6 @@ BLECommView::BLECommView(NavigationView& nav) field_frequency.set_step(0); - button_send_adv.on_select = [this](ImageButton&) { - this->toggle(); - }; - check_log.set_value(logging); check_log.on_select = [this](Checkbox&, bool v) { @@ -104,27 +97,8 @@ BLECommView::BLECommView(NavigationView& nav) logger->append(logs_dir.string() + "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT"); }; - options_channel.on_change = [this](size_t, int32_t i) { - // If we selected Auto don't do anything and Auto will handle changing. - if (i == 40) { - auto_channel = true; - return; - } else { - auto_channel = false; - } - - field_frequency.set_value(get_freq_by_channel_number(i)); - channel_number_rx = i; - channel_number_tx = i; - }; - - options_channel.set_selected_index(3, true); - logger = std::make_unique(); - // Generate new random Mac Address upon each new startup. - generateRandomMacAddress(randomMac); - // Setup Initial Advertise Packet. advertisePacket = build_adv_packet(); } @@ -145,86 +119,66 @@ bool BLECommView::in_tx_mode() const { return (bool)is_running_tx; } -void BLECommView::toggle() { - if (in_tx_mode()) { - sendAdvertisement(false); - } else { - sendAdvertisement(true); - } -} - BLETxPacket BLECommView::build_adv_packet() { BLETxPacket bleTxPacket; memset(&bleTxPacket, 0, sizeof(BLETxPacket)); - std::string dataString = "11094861636b524620506f7274617061636b"; - - strncpy(bleTxPacket.macAddress, randomMac, 12); + std::string dataString = "02010603030F1807084861636B5246"; strncpy(bleTxPacket.advertisementData, dataString.c_str(), dataString.length()); - // Duty cycle of 40% per 100ms advertisment periods. - strncpy(bleTxPacket.packetCount, "80", 3); - bleTxPacket.packet_count = 80; + strncpy(bleTxPacket.packetCount, "1", 2); + bleTxPacket.packet_count = 1; - bleTxPacket.packetType = PKT_TYPE_DISCOVERY; + bleTxPacket.pduType = PKT_TYPE_ADV_IND; return bleTxPacket; } -void BLECommView::sendAdvertisement(bool enable) { - if (enable) { - startTx(advertisePacket); - is_adv = true; - } else { - is_adv = false; - stopTx(); - } +void BLECommView::sendAdvertisement(void) { + startTx(advertisePacket); + ble_state = Ble_State_Advertising; } void BLECommView::startTx(BLETxPacket packetToSend) { - int randomChannel = channel_number_tx; + switch_rx_tx(false); - if (auto_channel) { - int min = 37; - int max = 39; + currentPacket = packetToSend; + packet_counter = currentPacket.packet_count; - randomChannel = min + std::rand() % (max - min + 1); - - field_frequency.set_value(get_freq_by_channel_number(randomChannel)); + switch (advCount) { + case 0: + channel_number_tx = 37; + break; + case 1: + channel_number_tx = 38; + break; + case 2: + channel_number_tx = 39; + break; } - if (!in_tx_mode()) { - switch_rx_tx(false); + field_frequency.set_value(get_freq_by_channel_number(37)); - currentPacket = packetToSend; - packet_counter = currentPacket.packet_count; + advCount++; - button_send_adv.set_bitmap(&bitmap_stop); - baseband::set_btletx(randomChannel, currentPacket.macAddress, currentPacket.advertisementData, currentPacket.packetType); - transmitter_model.set_tx_gain(47); - transmitter_model.enable(); - - is_running_tx = true; - } else { - baseband::set_btletx(randomChannel, currentPacket.macAddress, currentPacket.advertisementData, currentPacket.packetType); + if (advCount == 3) { + advCount = 0; } - if ((packet_counter % 10) == 0) { - text_packets_sent.set(to_string_dec_uint(packet_counter)); - } + baseband::set_btletx(37, deviceMAC, currentPacket.advertisementData, currentPacket.pduType); + transmitter_model.set_tx_gain(47); + transmitter_model.enable(); - is_sending = true; - - packet_counter--; + is_running_tx = true; } void BLECommView::stopTx() { - button_send_adv.set_bitmap(&bitmap_play); text_packets_sent.set(to_string_dec_uint(packet_counter)); switch_rx_tx(true); baseband::set_btlerx(channel_number_rx); + field_frequency.set_value(get_freq_by_channel_number(channel_number_rx)); receiver_model.enable(); is_running_tx = false; @@ -248,14 +202,12 @@ void BLECommView::on_data(BlePacketData* packet) { parse_received_packet(packet, (ADV_PDU_TYPE)packet->type); } -// called each 1/60th of second, so 6 = 100ms void BLECommView::on_timer() { - // Send advertise burst only once every 100ms if (++timer_counter == timer_period) { timer_counter = 0; - if (!is_adv) { - sendAdvertisement(true); + if (ble_state == Ble_State_Receiving || ble_state == Ble_State_Idle) { + sendAdvertisement(); } } } @@ -263,17 +215,18 @@ void BLECommView::on_timer() { void BLECommView::on_tx_progress(const bool done) { if (done) { if (in_tx_mode()) { - is_sending = false; + ble_state = Ble_State_Receiving; - if (packet_counter == 0) { - if (is_adv) { - sendAdvertisement(false); - } else { - stopTx(); - } - } else { - startTx(currentPacket); - } + timer_counter = 0; + timer_period = 12; + + stopTx(); + + // else + // { + // startTx(advertisePacket); + // advCount++; + // } } } } @@ -309,23 +262,28 @@ void BLECommView::parse_received_packet(const BlePacketData* packet, ADV_PDU_TYP } if (pdu_type == SCAN_REQ || pdu_type == CONNECT_REQ) { - ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)packet->data; - - std::reverse(directed_mac_data->A1, directed_mac_data->A1 + 6); - console.clear(true); - std::string str_console = ""; - std::string pduTypeStr = ""; - if (pdu_type == SCAN_REQ) { - pduTypeStr += "SCAN_REQ"; - } else if (pdu_type == CONNECT_REQ) { - pduTypeStr += "CONNECT_REQ"; - } + ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)packet->data; - str_console += "PACKET TYPE:" + pduTypeStr + "\n"; - str_console += "MY MAC:" + to_string_formatted_mac_address(randomMac) + "\n"; - str_console += "SCAN MAC:" + to_string_mac_address(directed_mac_data->A1, 6, false) + "\n"; - console.write(str_console); + std::reverse(directed_mac_data->A1, directed_mac_data->A1 + 6); + std::string directedMAC = to_string_mac_address(directed_mac_data->A1, 6, false); + + // Compare directed MAC Hex with the device MAC. + if (1) { + // std::string str_console = "Received SCAN_REQ from directed MAC: " + directedMAC + "\n"; + // console.clear(true); + // console.writeln(str_console); + } + } else if (pdu_type == CONNECT_REQ) { + ADV_PDU_PAYLOAD_TYPE_5* connectReq = (ADV_PDU_PAYLOAD_TYPE_5*)packet->data; + + std::reverse(connectReq->AdvA, connectReq->AdvA + 6); + std::string directedMAC = to_string_mac_address(connectReq->AdvA, 6, false); + + std::string str_console = "Received CONNECT_REQ from directed MAC: " + directedMAC + "\n"; + console.clear(true); + console.writeln(str_console); + } } } diff --git a/firmware/application/apps/ble_comm_app.hpp b/firmware/application/apps/ble_comm_app.hpp index 43b07b4be..1ff6311a1 100644 --- a/firmware/application/apps/ble_comm_app.hpp +++ b/firmware/application/apps/ble_comm_app.hpp @@ -76,7 +76,16 @@ class BLECommView : public View { void on_data(BlePacketData* packetData); void on_tx_progress(const bool done); void parse_received_packet(const BlePacketData* packet, ADV_PDU_TYPE pdu_type); - void sendAdvertisement(bool enable); + void sendAdvertisement(void); + + typedef enum { + Ble_State_Idle = 0, + Ble_State_Advertising = 1, + Ble_State_Receiving = 2, + Ble_State_Sending = 3 + } BleState; + + BleState ble_state{Ble_State_Idle}; NavigationView& nav_; @@ -101,8 +110,9 @@ class BLECommView : public View { uint8_t channel_number_tx = 37; uint8_t channel_number_rx = 37; bool auto_channel = false; + uint8_t advCount = 0; - char randomMac[13] = "010203040506"; + char deviceMAC[13] = "C23456789ABC"; bool is_running_tx = false; bool is_sending = false; @@ -111,7 +121,7 @@ class BLECommView : public View { int16_t timer_period{6}; // Delay each packet by 16ms. int16_t timer_counter = 0; int16_t timer_rx_counter = 0; - int16_t timer_rx_period{12}; // Poll Rx for at least 200ms. (TBD) + int16_t timer_rx_period{12}; // Poll Rx for at least 150ms. (TBD) uint32_t packet_counter{0}; BLETxPacket advertisePacket{}; @@ -147,15 +157,6 @@ class BLECommView : public View { Channel channel{ {24 * 8, 5, 6 * 8, 4}}; - Labels label_send_adv{ - {{0 * 8, 2 * 8}, "Send Advertisement:", Theme::getInstance()->fg_light->foreground}}; - - ImageButton button_send_adv{ - {21 * 8, 1 * 16, 10 * 8, 2 * 16}, - &bitmap_play, - Theme::getInstance()->fg_green->foreground, - Theme::getInstance()->fg_green->background}; - Checkbox check_log{ {24 * 8, 2 * 8}, 3, diff --git a/firmware/application/apps/ble_rx_app.cpp b/firmware/application/apps/ble_rx_app.cpp index 136e0dd52..bfd122b86 100644 --- a/firmware/application/apps/ble_rx_app.cpp +++ b/firmware/application/apps/ble_rx_app.cpp @@ -247,7 +247,7 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl }; button_send.on_select = [this, &nav](const ui::Button&) { - auto packetToSend = build_packet(); + auto packetToSend = build_packet(entry_); nav.set_on_pop([packetToSend, &nav]() { nav.replace(packetToSend); }); @@ -255,7 +255,7 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl }; button_save.on_select = [this, &nav](const ui::Button&) { - auto packetToSave = build_packet(); + auto packetToSave = build_packet(entry_); packetFileBuffer = ""; text_prompt( @@ -436,7 +436,7 @@ void BleRecentEntryDetailView::set_entry(const BleRecentEntry& entry) { set_dirty(); } -BLETxPacket BleRecentEntryDetailView::build_packet() { +BLETxPacket BleRecentEntryDetailView::build_packet(BleRecentEntry entry_) { BLETxPacket bleTxPacket; memset(&bleTxPacket, 0, sizeof(BLETxPacket)); @@ -444,8 +444,8 @@ BLETxPacket BleRecentEntryDetailView::build_packet() { strncpy(bleTxPacket.macAddress, macAddressStr.c_str(), 12); strncpy(bleTxPacket.advertisementData, entry_.dataString.c_str(), entry_.packetData.dataLen * 2); - strncpy(bleTxPacket.packetCount, "50", 3); - bleTxPacket.packet_count = 50; + strncpy(bleTxPacket.packetCount, "10", 3); + bleTxPacket.packet_count = 10; return bleTxPacket; } @@ -495,16 +495,18 @@ BLERxView::BLERxView(NavigationView& nav) &field_vga, &options_channel, &field_frequency, - &check_log, - &button_find, - &check_name, &label_sort, &options_sort, - &label_found, - &text_found_count, - &check_serial_log, &button_filter, &options_filter, + &check_name, + &check_log, + &check_serial_log, + &check_unique, + &check_duplicate_packets, + &button_find, + &label_found, + &text_found_count, &button_save_list, &button_clear_list, &button_switch, @@ -516,6 +518,29 @@ BLERxView::BLERxView(NavigationView& nav) nav_.push(entry); }; + ensure_directory(find_packet_path); + ensure_directory(log_packets_path); + ensure_directory(packet_save_path); + + // ------------------------------------------------------------------------------ + // Handle Check Boxes + // ------------------------------------------------------------------------------ + logger = std::make_unique(); + + check_name.on_select = [this](Checkbox&, bool v) { + name_enable = v; + // update the include_name instance variable value of each entry in recent entries + setAllMembersToValue(recent, &BleRecentEntry::include_name, v); + recent_entries_view.set_dirty(); + }; + + check_log.on_select = [this](Checkbox&, bool v) { + logging = v; + + if (logger && logging) + logger->append(bletx_dir.string() + "/Packets/BLETx_" + to_string_timestamp(rtc_time::now()) + ".TXT"); + }; + check_serial_log.on_select = [this](Checkbox&, bool v) { serial_logging = v; if (v) { @@ -524,12 +549,28 @@ BLERxView::BLERxView(NavigationView& nav) portapack::async_tx_enabled = false; } }; + + check_unique.on_select = [this](Checkbox&, bool v) { + uniqueParsing = v; + recent.clear(); + recent_entries_view.set_dirty(); + }; + + check_duplicate_packets.on_select = [this](Checkbox&, bool v) { + duplicatePackets = v; + recent.clear(); + recent_entries_view.set_dirty(); + }; + + check_name.set_value(name_enable); + check_log.set_value(logging); check_serial_log.set_value(serial_logging); + check_unique.set_value(uniqueParsing); + check_duplicate_packets.set_value(duplicatePackets); - ensure_directory(find_packet_path); - ensure_directory(log_packets_path); - ensure_directory(packet_save_path); - + // ------------------------------------------------------------------------------ + // Handle Buttons + // ------------------------------------------------------------------------------ filterBuffer = filter; button_filter.on_select = [this](Button&) { @@ -543,16 +584,6 @@ BLERxView::BLERxView(NavigationView& nav) }); }; - logger = std::make_unique(); - - check_log.on_select = [this](Checkbox&, bool v) { - logging = v; - - if (logger && logging) - logger->append(blerx_dir.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT"); - }; - check_log.set_value(logging); - button_save_list.on_select = [this, &nav](const ui::Button&) { listFileBuffer = ""; text_prompt( @@ -574,17 +605,18 @@ BLERxView::BLERxView(NavigationView& nav) nav.replace(); }; - field_frequency.set_step(0); - - check_name.set_value(name_enable); - - check_name.on_select = [this](Checkbox&, bool v) { - name_enable = v; - // update the include_name instance variable value of each entry in recent entries - setAllMembersToValue(recent, &BleRecentEntry::include_name, v); - recent_entries_view.set_dirty(); + button_find.on_select = [this](Button&) { + auto open_view = nav_.push(".TXT"); + open_view->on_changed = [this](std::filesystem::path new_file_path) { + on_file_changed(new_file_path); + }; }; + // ------------------------------------------------------------------------------ + // Handle Options + // ------------------------------------------------------------------------------ + field_frequency.set_step(0); + options_channel.on_change = [this](size_t index, int32_t v) { channel_index = (uint8_t)index; @@ -611,7 +643,6 @@ BLERxView::BLERxView(NavigationView& nav) filter_index = (uint8_t)index; recent.clear(); handle_filter_options(v); - uniqueParsing = filter_index == 2 ? true : false; recent_entries_view.set_dirty(); }; @@ -619,15 +650,6 @@ BLERxView::BLERxView(NavigationView& nav) options_sort.set_selected_index(sort_index, true); options_filter.set_selected_index(filter_index, true); - button_find.on_select = [this](Button&) { - auto open_view = nav_.push(".TXT"); - open_view->on_changed = [this](std::filesystem::path new_file_path) { - on_file_changed(new_file_path); - - // nav_.set_on_pop([this]() { button_play.focus(); }); - }; - }; - // Auto-configure modem for LCR RX (will be removed later) baseband::set_btlerx(channel_number); @@ -785,12 +807,17 @@ bool BLERxView::saveFile(const std::filesystem::path& path) { } void BLERxView::on_data(BlePacketData* packet) { - uint64_t macAddressEncoded = copy_mac_address_to_uint64(packet->macAddress); + uint64_t uniqueKeyEncoded = copy_mac_address_to_uint64(packet->macAddress); // Start of Packet stuffing. // Masking off the top 2 bytes to avoid invalid keys. - uint64_t key = macAddressEncoded & 0xFFFFFFFFFFFF; + uint64_t key = (uniqueKeyEncoded & 0xFFFFFFFFFFFF); + + if (duplicatePackets) { + key |= ((uint64_t)packet->type) << 48; + } + bool packetExists = false; // If found store into tempEntry to modify. @@ -866,10 +893,6 @@ void BLERxView::log_ble_packet(BlePacketData* packet) { void BLERxView::on_filter_change(std::string value) { // New filter? Reset list from recent entries. if (filter != value) { - // resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) { - // // return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos); - // return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos) && (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos); - // }); filter = value; handle_filter_options(options_filter.selected_index()); } @@ -947,7 +970,7 @@ void BLERxView::handle_entries_sort(uint8_t index) { switch (index) { case 0: sortEntriesBy( - recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true); + recent, [](const BleRecentEntry& entry) { return entry.uniqueKey & 0xFFFFFFFFFFFF; }, true); break; case 1: sortEntriesBy( @@ -965,6 +988,10 @@ void BLERxView::handle_entries_sort(uint8_t index) { sortEntriesBy( recent, [](const BleRecentEntry& entry) { return entry.nameString; }, true); break; + case 5: + sortEntriesBy( + recent, [](const BleRecentEntry& entry) { return entry.informationString; }, true); + break; default: break; } @@ -975,16 +1002,43 @@ void BLERxView::handle_entries_sort(uint8_t index) { void BLERxView::handle_filter_options(uint8_t index) { auto value = filter; switch (index) { - case 0: // filter by Data + // Data + case 0: resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) { return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos); }); break; - case 1: // filter by MAC address (All caps: e.g. AA:BB:CC:DD:EE:FF) + // MAC address (All caps: e.g. AA:BB:CC:DD:EE:FF) + case 1: resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) { return (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos); }); break; + // Name + case 2: + resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) { + return (entry.nameString.find(value) == std::string::npos); + }); + break; + // Info + case 3: + resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) { + return (entry.informationString.find(value) == std::string::npos); + }); + break; + // Vendor + case 4: + resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) { + std::string vendor_name = lookup_mac_vendor(entry.packetData.macAddress); + return (vendor_name.find(value) == std::string::npos); + }); + break; + // Channel + case 5: + resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) { + return (to_string_dec_int(entry.channelNumber).find(value) == std::string::npos); + }); + break; default: break; } @@ -1007,62 +1061,178 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry, bool success = false; - int i; - - for (i = 0; i < packet->dataLen; i++) { - data_string += to_string_hex(packet->data[i], 2); - } - - entry.dbValue = packet->max_dB - (receiver_model.lna() + receiver_model.vga() + (receiver_model.rf_amp() ? 14 : 0)); - entry.timestamp = to_string_timestamp(rtc_time::now()); - entry.dataString = data_string; - - entry.packetData.type = packet->type; - entry.packetData.size = packet->size; - entry.packetData.dataLen = packet->dataLen; - - // Mac Address of sender. - entry.packetData.macAddress[0] = packet->macAddress[0]; - entry.packetData.macAddress[1] = packet->macAddress[1]; - entry.packetData.macAddress[2] = packet->macAddress[2]; - entry.packetData.macAddress[3] = packet->macAddress[3]; - entry.packetData.macAddress[4] = packet->macAddress[4]; - entry.packetData.macAddress[5] = packet->macAddress[5]; - - entry.pduType = pdu_type; - entry.channelNumber = channel_number; - entry.numHits++; - - if (entry.vendor_status == MAC_VENDOR_UNKNOWN) { - std::string vendor_name; - entry.vendor_status = lookup_mac_vendor_status(entry.packetData.macAddress, vendor_name); - } - - // Parse Data Section into buffer to be interpretted later. - for (int i = 0; i < packet->dataLen; i++) { - entry.packetData.data[i] = packet->data[i]; - } - - entry.include_name = check_name.value(); - // Only parse name for advertisment packets and empty name entries if (pdu_type == ADV_IND || pdu_type == ADV_NONCONN_IND || pdu_type == SCAN_RSP || pdu_type == ADV_SCAN_IND) { if (uniqueParsing) { - // Add your unique beacon parsing function here. + success = parse_tracking_beacon_data(packet->data, packet->dataLen, entry.nameString, entry.informationString); } if (!success && !uniqueParsing) { success = parse_beacon_data(packet->data, packet->dataLen, entry.nameString, entry.informationString); } - } else if (pdu_type == ADV_DIRECT_IND || pdu_type == SCAN_REQ) { - ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)entry.packetData.data; - reverse_byte_array(directed_mac_data->A1, 6); + if (!uniqueParsing) { + ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)packet->data; + reverse_byte_array(directed_mac_data->A1, 6); + success = true; + } + } + + if (success) { + int i; + + for (i = 0; i < packet->dataLen; i++) { + data_string += to_string_hex(packet->data[i], 2); + } + + entry.dbValue = packet->max_dB - (receiver_model.lna() + receiver_model.vga() + (receiver_model.rf_amp() ? 14 : 0)); + entry.timestamp = to_string_timestamp(rtc_time::now()); + entry.dataString = data_string; + + entry.packetData.type = packet->type; + entry.packetData.size = packet->size; + entry.packetData.dataLen = packet->dataLen; + + // Mac Address of sender. + entry.packetData.macAddress[0] = packet->macAddress[0]; + entry.packetData.macAddress[1] = packet->macAddress[1]; + entry.packetData.macAddress[2] = packet->macAddress[2]; + entry.packetData.macAddress[3] = packet->macAddress[3]; + entry.packetData.macAddress[4] = packet->macAddress[4]; + entry.packetData.macAddress[5] = packet->macAddress[5]; + + entry.pduType = pdu_type; + entry.channelNumber = channel_number; + entry.numHits++; + + if (entry.vendor_status == MAC_VENDOR_UNKNOWN) { + std::string vendor_name; + entry.vendor_status = lookup_mac_vendor_status(entry.packetData.macAddress, vendor_name); + } + + // Parse Data Section into buffer to be interpretted later. + for (int i = 0; i < packet->dataLen; i++) { + entry.packetData.data[i] = packet->data[i]; + } + + entry.include_name = check_name.value(); } return success; } +bool BLERxView::parse_tracking_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString) { + uint8_t currentByte, currentLength, currentType = 0; + + for (currentByte = 0; currentByte < length;) { + currentLength = data[currentByte++]; + currentType = data[currentByte++]; + + // Manufacturer Specific Data (0xFF) + if (currentType == 0xFF && currentLength >= 4) { + uint16_t companyID = data[currentByte] | (data[currentByte + 1] << 8); + + // Apple AirTag / Find My + if (companyID == 0x004C && currentLength >= 6) { + uint8_t appleType = data[currentByte + 2]; + uint8_t appleLen = data[currentByte + 3]; + if (appleType == 0x12 && appleLen == 0x19 && currentLength >= 4 + appleLen) { + nameString.assign("Apple AirTag"); + informationString.assign("Find My"); + return true; + } else if (appleType == 0x02 && appleLen == 0x15) { + uint16_t major = (data[currentByte + 20] << 8) | data[currentByte + 21]; + uint16_t minor = (data[currentByte + 22] << 8) | data[currentByte + 23]; + + nameString.assign("iBeacon"); + informationString.assign(to_string_hex(major) + to_string_hex(minor)); + return true; + } + } + } + // Services + else if ((currentType == 0x02 || currentType == 0x03) && currentLength >= 3) { + for (int u = 0; u < currentLength - 1; u += 2) { + uint16_t uuid16 = data[currentByte + u] | (data[currentByte + u + 1] << 8); + if (uuid16 == 0x1802) { // Immediate Alert Service = Find Me Profile + nameString.assign("FindMe"); + informationString.assign("IAS"); + return true; + } + } + } + // Service Data + else if (currentType == 0x16 && currentLength >= 3) { + uint16_t uuid16 = data[currentByte] | (data[currentByte + 1] << 8); + + switch (uuid16) { + case 0xFD59: { // Samsung SmartTag - Unregistered + nameString.assign("Samsung SmartTag"); + informationString.assign("Unreg"); + return true; + } + case 0xFD5A: { // Samsung SmartTag - Registered + nameString.assign("Samsung SmartTag"); + informationString.assign("Reg"); + return true; + } + case 0xFD84: { + if (currentByte + 2 < length) { + uint8_t model = data[currentByte + 2]; + switch (model) { + case 0x01: + informationString.assign("Mate"); + break; + case 0x02: + informationString.assign("Pro"); + break; + case 0x03: + informationString.assign("Slim"); + break; + case 0x04: + informationString.assign("Sticker"); + break; + default: + informationString.assign("Unknown"); + break; + } + } + nameString.assign("Tile"); + return true; + } + case 0xFEAA: { + if (currentByte + 2 < length) { + uint8_t frameType = data[currentByte + 2]; + switch (frameType) { + case 0x00: + informationString.assign("UID"); + break; + case 0x10: + informationString.assign("URL"); + break; + case 0x20: + informationString.assign("TLM"); + break; + case 0x30: + informationString.assign("EID"); + break; + default: + informationString.assign("Unknown"); + break; + } + } + nameString.assign("Eddystone"); + return true; + } + } + } + + currentByte += (currentLength - 1); + } + + return false; +} + bool BLERxView::parse_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString) { uint8_t currentByte, currentLength, currentType = 0; std::string tempName = ""; diff --git a/firmware/application/apps/ble_rx_app.hpp b/firmware/application/apps/ble_rx_app.hpp index a1654e950..7b59c0b7c 100644 --- a/firmware/application/apps/ble_rx_app.hpp +++ b/firmware/application/apps/ble_rx_app.hpp @@ -71,7 +71,8 @@ typedef enum { RESERVED5 = 12, RESERVED6 = 13, RESERVED7 = 14, - RESERVED8 = 15 + RESERVED8 = 15, + UNKNOWN = 16 } ADV_PDU_TYPE; typedef enum { @@ -84,9 +85,9 @@ typedef enum { struct BleRecentEntry { using Key = uint64_t; - static constexpr Key invalid_key = 0xFFFFFFFFFFFF; + static constexpr Key invalid_key = 0xFFFFFFFFFFFFF; - uint64_t macAddress; + uint64_t uniqueKey; int dbValue; BlePacketData packetData; std::string timestamp; @@ -105,8 +106,8 @@ struct BleRecentEntry { } BleRecentEntry( - const uint64_t macAddress) - : macAddress{macAddress}, + const uint64_t uniqueKey) + : uniqueKey{uniqueKey}, dbValue{}, packetData{}, timestamp{}, @@ -122,7 +123,7 @@ struct BleRecentEntry { } Key key() const { - return macAddress; + return uniqueKey; } }; @@ -139,11 +140,11 @@ class BleRecentEntryDetailView : public View { void update_data(); void focus() override; void paint(Painter&) override; + static BLETxPacket build_packet(BleRecentEntry entry_); private: NavigationView& nav_; BleRecentEntry entry_{}; - BLETxPacket build_packet(); void on_save_file(const std::string value, BLETxPacket packetToSave); bool saveFile(const std::filesystem::path& path, BLETxPacket packetToSave); std::string packetFileBuffer{}; @@ -227,6 +228,7 @@ class BLERxView : public View { void handle_filter_options(uint8_t index); bool updateEntry(const BlePacketData* packet, BleRecentEntry& entry, ADV_PDU_TYPE pdu_type); bool parse_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString); + bool parse_tracking_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString); NavigationView& nav_; @@ -240,6 +242,7 @@ class BLERxView : public View { uint8_t sort_index{0}; uint8_t filter_index{0}; bool uniqueParsing = false; + bool duplicatePackets = false; std::string filter{}; bool logging{false}; bool serial_logging{false}; @@ -258,6 +261,8 @@ class BLERxView : public View { // disabled to always start without USB serial activated until we can make it non blocking if not connected // {"serial_log"sv, &serial_logging}, {"name"sv, &name_enable}, + {"unique_parsing"sv, &uniqueParsing}, + {"duplicate_packets"sv, &duplicatePackets}, }}; std::string str_console = ""; @@ -267,7 +272,7 @@ class BLERxView : public View { bool auto_channel = false; int16_t timer_count{0}; - int16_t timer_period{2}; // 25ms + int16_t timer_period{1}; // 25ms std::string filterBuffer{}; std::string listFileBuffer{}; @@ -283,7 +288,7 @@ class BLERxView : public View { std::filesystem::path log_packets_path{blerx_dir / u"Logs/????.TXT"}; std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"}; - static constexpr auto header_height = 9 * 8; + static constexpr auto header_height = 12 * 8; static constexpr auto switch_button_height = 3 * 16; OptionsField options_channel{ @@ -323,7 +328,8 @@ class BLERxView : public View { {"Hits", 1}, {"dB", 2}, {"Time", 3}, - {"Name", 4}}}; + {"Name", 4}, + {"Info", 5}}}; Button button_filter{ {11 * 8, 2 * 8, 7 * 8, 16}, @@ -334,7 +340,10 @@ class BLERxView : public View { 7, {{"Data", 0}, {"MAC", 1}, - {"Unique", 2}}}; + {"Name", 2}, + {"Info", 3}, + {"Vendor", 4}, + {"Channel", 5}}}; Checkbox check_log{ {10 * 8, 4 * 8 + 2}, @@ -348,36 +357,45 @@ class BLERxView : public View { "Name", true}; - Button button_find{ - {0 * 8, 7 * 8 - 2, 4 * 8, 16}, - "Find"}; - - Labels label_found{ - {{5 * 8, 7 * 8 - 2}, "Found:", Theme::getInstance()->fg_light->foreground}}; - - Text text_found_count{ - {11 * 8, 7 * 8 - 2, 20 * 8, 16}, - "0/0"}; - Checkbox check_serial_log{ {18 * 8 + 2, 4 * 8 + 2}, 7, "USB Log", true}; - // Console console{ - // {0, 10 * 8, screen_height, screen_height-80}}; + Checkbox check_unique{ + {0 * 8 + 2, 7 * 8 + 2}, + 7, + "Unique", + true}; + + Checkbox check_duplicate_packets{ + {10 * 8 + 2, 7 * 8 + 2}, + 7, + "Duplicate", + true}; + + Button button_find{ + {0 * 8, 10 * 8 - 2, 4 * 8, 16}, + "Find"}; + + Labels label_found{ + {{5 * 8, 10 * 8 - 2}, "Found:", Theme::getInstance()->fg_light->foreground}}; + + Text text_found_count{ + {11 * 8, 10 * 8 - 2, 20 * 8, 16}, + "0/0"}; Button button_clear_list{ - {2 * 8, screen_height - (16 + 32), 7 * 8, 32}, + {2 * 8, 320 - (16 + 32), 7 * 8, 32}, "Clear"}; Button button_save_list{ - {11 * 8, screen_height - (16 + 32), 11 * 8, 32}, + {11 * 8, 320 - (16 + 32), 11 * 8, 32}, "Export CSV"}; Button button_switch{ - {screen_width - 6 * 8, screen_height - (16 + 32), 4 * 8, 32}, + {240 - 6 * 8, 320 - (16 + 32), 4 * 8, 32}, "Tx"}; std::string str_log{""}; diff --git a/firmware/application/apps/ble_tx_app.cpp b/firmware/application/apps/ble_tx_app.cpp index b0ade7b1a..beb39b885 100644 --- a/firmware/application/apps/ble_tx_app.cpp +++ b/firmware/application/apps/ble_tx_app.cpp @@ -35,6 +35,7 @@ #include "rtc_time.hpp" #include "string_format.hpp" #include "file_path.hpp" +#include "usb_serial_asyncmsg.hpp" using namespace portapack; using namespace modems; @@ -123,6 +124,31 @@ static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) { namespace ui { +PKT_TYPE get_pkt_type_from_string(const std::string& type_str) { + if (type_str == "RAW") { + return PKT_TYPE_RAW; + } else if (type_str == "DISCOVERY") { + return PKT_TYPE_DISCOVERY; + } else if (type_str == "IBEACON") { + return PKT_TYPE_IBEACON; + } else if (type_str == "ADV_IND") { + return PKT_TYPE_ADV_IND; + } else if (type_str == "ADV_DIRECT_IND") { + return PKT_TYPE_ADV_DIRECT_IND; + } else if (type_str == "ADV_NONCONN_IND") { + return PKT_TYPE_ADV_NONCONN_IND; + } else if (type_str == "ADV_SCAN_IND") { + return PKT_TYPE_ADV_SCAN_IND; + } else if (type_str == "SCAN_REQ") { + return PKT_TYPE_SCAN_REQ; + } else if (type_str == "SCAN_RSP") { + return PKT_TYPE_SCAN_RSP; + } else if (type_str == "CONNECT_REQ") { + return PKT_TYPE_CONNECT_REQ; + } + return PKT_TYPE_INVALID_TYPE; +} + void BLETxView::focus() { button_open.focus(); } @@ -167,28 +193,11 @@ void BLETxView::toggle() { } } -void BLETxView::start() { - baseband::run_image(portapack::spi_flash::image_tag_btle_tx); - transmitter_model.enable(); - +void BLETxView::send_packet() { // Generate new random Mac Address. + transmitter_model.enable(); generateRandomMacAddress(randomMac); - // If this is our first run, check file. - if (!is_active()) { - File data_file; - - auto error = data_file.open(file_path); - if (error && !file_override) { - file_error(); - check_loop.set_value(false); - return; - } - - button_play.set_bitmap(&bitmap_stop); - is_running = true; - } - char advertisementData[63] = {0}; strcpy(advertisementData, packets[current_packet].advertisementData); @@ -235,9 +244,28 @@ void BLETxView::start() { } } - // Setup next packet configuration. - progressbar.set_max(packets[current_packet].packet_count); - baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, advertisementData, pduType); + progressbar.set_value(packets[current_packet].packet_count - packet_counter); + text_packets_sent.set(to_string_dec_uint(packet_counter)); + + baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, advertisementData, packets[current_packet].pduType); + + packetDone = false; +} + +void BLETxView::start() { + if (file_path.empty()) { + file_error(); + check_loop.set_value(false); + return; + } + + baseband::run_image(portapack::spi_flash::image_tag_btle_tx); + transmitter_model.enable(); + + button_play.set_bitmap(&bitmap_stop); + is_running = true; + + send_packet(); } void BLETxView::stop() { @@ -253,66 +281,67 @@ void BLETxView::stop() { is_running = false; } -void BLETxView::reset() { - transmitter_model.disable(); - baseband::shutdown(); - - start(); -} - // called each 1/60th of second, so 6 = 100ms void BLETxView::on_timer() { if (++timer_count == timer_period) { timer_count = 0; - if (is_active()) { - // Reached end of current packet repeats. - if (packet_counter == 0) { - // Done sending all packets. - if (current_packet == (num_packets - 1)) { - current_packet = 0; - - // If looping, restart from beginning. - if (check_loop.value()) { - update_current_packet(packets[current_packet], current_packet); - reset(); - } else { - stop(); - } - } else { - current_packet++; - update_current_packet(packets[current_packet], current_packet); - reset(); - } - } else { - reset(); - } - } - } - - if (++auto_channel_counter == auto_channel_period) { - auto_channel_counter = 0; - - if (auto_channel) { - int min = 37; - int max = 39; - - channel_number = min + std::rand() % (max - min + 1); - - field_frequency.set_value(get_freq_by_channel_number(channel_number)); + if (is_active() && packetDone) { + send_packet(); } } } -void BLETxView::on_tx_progress(const bool done) { +void BLETxView::on_tx_progress(const bool done, uint32_t progress) { if (done) { if (is_active()) { - if ((packet_counter % 10) == 0) { - text_packets_sent.set(to_string_dec_uint(packet_counter)); - } + transmitter_model.disable(); + if (auto_channel) { + switch (advCount) { + case 0: + channel_number = 37; + break; + case 1: + channel_number = 38; + break; + case 2: + channel_number = 39; + break; + } - packet_counter--; - progressbar.set_value(packets[current_packet].packet_count - packet_counter); + field_frequency.set_value(get_freq_by_channel_number(channel_number)); + + if (advCount == 3) { + channel_number = 37; + packet_counter--; + packetDone = true; + advCount = 0; + } else { + send_packet(); + advCount++; + } + } else { + packet_counter--; + packetDone = true; + } + } + + // Reached end of current packet repeats. + if (packet_counter == 0) { + // Done sending all packets. + if (current_packet == (num_packets - 1)) { + current_packet = 0; + + // If looping, restart from beginning. + if (check_loop.value()) { + update_current_packet(packets[current_packet], current_packet); + } else { + stop(); + } + } else { + current_packet++; + update_current_packet(packets[current_packet], current_packet); + } } } } @@ -331,7 +360,6 @@ BLETxView::BLETxView(NavigationView& nav) &label_speed, &options_speed, &options_channel, - &options_adv_type, &label_marked_data, &marked_data_sequence, &label_packet_index, @@ -371,13 +399,8 @@ BLETxView::BLETxView(NavigationView& nav) timer_count = 0; }; - options_adv_type.on_change = [this](size_t, int32_t i) { - pduType = (PKT_TYPE)i; - }; - options_speed.set_selected_index(0); - options_channel.set_selected_index(0); - options_adv_type.set_selected_index(0); + options_channel.set_selected_index(3); check_rand_mac.set_value(false); check_rand_mac.on_select = [this](Checkbox&, bool v) { @@ -482,17 +505,25 @@ void BLETxView::on_file_changed(const fs::path& new_file_path) { do { readUntil(data_file, packets[num_packets].macAddress, mac_address_size_str, ' '); readUntil(data_file, packets[num_packets].advertisementData, max_packet_size_str, ' '); + readUntil(data_file, packets[num_packets].packetType, max_packet_type_str, ' '); readUntil(data_file, packets[num_packets].packetCount, max_packet_repeat_str, '\n'); uint64_t macAddressSize = strlen(packets[num_packets].macAddress); uint64_t advertisementDataSize = strlen(packets[num_packets].advertisementData); uint64_t packetCountSize = strlen(packets[num_packets].packetCount); + uint64_t packetTypeSize = strlen(packets[num_packets].packetType); packets[num_packets].packet_count = stringToUint32(packets[num_packets].packetCount); + packets[num_packets].pduType = (PKT_TYPE)get_pkt_type_from_string(packets[num_packets].packetType); // Verify Data. - if ((macAddressSize == mac_address_size_str) && (advertisementDataSize < max_packet_size_str) && (packetCountSize < max_packet_repeat_str) && - hasValidHexPairs(packets[num_packets].macAddress, macAddressSize / 2) && hasValidHexPairs(packets[num_packets].advertisementData, advertisementDataSize / 2) && (packets[num_packets].packet_count >= 1) && (packets[num_packets].packet_count < max_packet_repeat_count)) { + if ((macAddressSize == mac_address_size_str) && + (advertisementDataSize < max_packet_size_str) && + (packetCountSize < max_packet_repeat_str) && + hasValidHexPairs(packets[num_packets].macAddress, macAddressSize / 2) && + hasValidHexPairs(packets[num_packets].advertisementData, advertisementDataSize / 2) && + (packets[num_packets].packet_count >= 1) && (packets[num_packets].packet_count < max_packet_repeat_count) && + (packetTypeSize <= max_packet_type_str)) { text_filename.set(truncate(file_path.filename().string(), 12)); } else { @@ -510,6 +541,8 @@ void BLETxView::on_file_changed(const fs::path& new_file_path) { } while (num_packets < max_num_packets); update_current_packet(packets[0], 0); + + data_file.close(); } } @@ -540,6 +573,8 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex) text_mac_address.set(formattedMacAddress); + progressbar.set_max(packet.packet_count); + packet_counter = packet.packet_count; current_packet = currentIndex; @@ -550,7 +585,7 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex) dataFile.write("\n", 1); } - dataFile.~File(); + dataFile.close(); auto result = FileWrapper::open(dataTempFilePath); diff --git a/firmware/application/apps/ble_tx_app.hpp b/firmware/application/apps/ble_tx_app.hpp index 0d8c34bc0..3f22281db 100644 --- a/firmware/application/apps/ble_tx_app.hpp +++ b/firmware/application/apps/ble_tx_app.hpp @@ -38,6 +38,8 @@ #include "utility.hpp" #include "file_path.hpp" +#include "ble_tx_app.hpp" + #include "recent_entries.hpp" #include @@ -91,8 +93,9 @@ struct BLETxPacket { char macAddress[13]; char advertisementData[63]; char packetCount[11]; + char packetType[17]; uint32_t packet_count; - PKT_TYPE packetType; + PKT_TYPE pduType; }; class BLETxView : public View { @@ -109,8 +112,8 @@ class BLETxView : public View { bool is_active() const; void toggle(); void start(); + void send_packet(); void stop(); - void reset(); void handle_replay_thread_done(const uint32_t return_code); void file_error(); bool saveFile(const std::filesystem::path& path); @@ -122,7 +125,7 @@ class BLETxView : public View { void on_data(uint32_t value, bool is_data); void on_file_changed(const std::filesystem::path& new_file_path); void on_save_file(const std::string value); - void on_tx_progress(const bool done); + void on_tx_progress(const bool done, uint32_t progress); void on_random_data_change(std::string value); void update_current_packet(BLETxPacket packet, uint32_t currentIndex); @@ -156,6 +159,8 @@ class BLETxView : public View { uint32_t packet_counter{0}; uint32_t num_packets{0}; uint32_t current_packet{0}; + uint8_t packetTxCount{0}; + bool packetDone = false; bool random_mac = false; bool file_override = false; @@ -170,17 +175,17 @@ class BLETxView : public View { std::vector markedBytes{}; CursorPos cursor_pos{}; uint8_t marked_counter = 0; + uint8_t advCount = 0; static constexpr uint8_t mac_address_size_str{12}; static constexpr uint8_t max_packet_size_str{62}; static constexpr uint8_t max_packet_repeat_str{10}; + static constexpr uint8_t max_packet_type_str{16}; static constexpr uint32_t max_packet_repeat_count{UINT32_MAX}; static constexpr uint32_t max_num_packets{32}; BLETxPacket packets[max_num_packets]; - PKT_TYPE pduType = {PKT_TYPE_DISCOVERY}; - static constexpr auto header_height = 10 * 16; static constexpr auto switch_button_height = 6 * 16; @@ -216,7 +221,7 @@ class BLETxView : public View { true}; ImageButton button_play{ - {screen_width - 2 * 8, 2 * 16, 2 * 8, 1 * 16}, + {28 * 8, 2 * 16, 2 * 8, 1 * 16}, &bitmap_play, Theme::getInstance()->fg_green->foreground, Theme::getInstance()->fg_green->background}; @@ -227,11 +232,11 @@ class BLETxView : public View { OptionsField options_speed{ {7 * 8, 6 * 8}, 3, - {{"1 ", 1}, // 16ms - {"2 ", 2}, // 32ms - {"3 ", 3}, // 48ms - {"4 ", 6}, // 100ms - {"5 ", 12}}}; // 200ms + {{"1 ", 2}, // 25ms + {"2 ", 4}, // 50ms + {"3 ", 6}, // 75ms + {"4 ", 8}, // 100ms + {"5 ", 12}}}; // 150ms OptionsField options_channel{ {11 * 8, 6 * 8}, @@ -288,7 +293,7 @@ class BLETxView : public View { {{0 * 8, 9 * 16}, "Packet Data:", Theme::getInstance()->fg_light->foreground}}; TextViewer dataEditView{ - {0, 9 * 18, screen_width, screen_height - 80}}; + {0, 9 * 18, 240, 240}}; Button button_clear_marked{ {1 * 8, 14 * 16, 13 * 8, 3 * 8}, @@ -320,7 +325,7 @@ class BLETxView : public View { Message::ID::TXProgress, [this](const Message* const p) { const auto message = *reinterpret_cast(p); - this->on_tx_progress(message.done); + this->on_tx_progress(message.done, message.progress); }}; MessageHandlerRegistration message_handler_frame_sync{ diff --git a/firmware/application/apps/ert_app.cpp b/firmware/application/external/ert/ert_app.cpp similarity index 96% rename from firmware/application/apps/ert_app.cpp rename to firmware/application/external/ert/ert_app.cpp index 0b0f1283f..567db3d06 100644 --- a/firmware/application/apps/ert_app.cpp +++ b/firmware/application/external/ert/ert_app.cpp @@ -96,22 +96,7 @@ void ERTRecentEntry::update(const ert::Packet& packet) { packet_type = packet.type(); } -namespace ui { - -template <> -void RecentEntriesTable::draw( - const Entry& entry, - const Rect& target_rect, - Painter& painter, - const Style& style) { - std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " "; - - line += (entry.packet_type == ert::Packet::Type::SCM) ? ert::format::tamper_flags_scm(entry.last_tamper_flags) : ert::format::tamper_flags(entry.last_tamper_flags); - line += (entry.received_count > 99) ? " ++" : to_string_dec_uint(entry.received_count, 3); - - line.resize(target_rect.width() / 8, ' '); - painter.draw_string(target_rect.location(), style, line); -} +namespace ui::external_app::ert_app { ERTAppView::ERTAppView(NavigationView& nav) : nav_{nav} { @@ -182,4 +167,21 @@ void ERTAppView::on_freqchg(int64_t freq) { field_frequency.set_value(freq); } -} /* namespace ui */ +} /* namespace ui::external_app::ert_app */ + +namespace ui { +template <> +void RecentEntriesTable::draw( + const Entry& entry, + const Rect& target_rect, + Painter& painter, + const Style& style) { + std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " "; + + line += (entry.packet_type == ert::Packet::Type::SCM) ? ert::format::tamper_flags_scm(entry.last_tamper_flags) : ert::format::tamper_flags(entry.last_tamper_flags); + line += (entry.received_count > 99) ? " ++" : to_string_dec_uint(entry.received_count, 3); + + line.resize(target_rect.width() / 8, ' '); + painter.draw_string(target_rect.location(), style, line); +} +} // namespace ui diff --git a/firmware/application/apps/ert_app.hpp b/firmware/application/external/ert/ert_app.hpp similarity index 98% rename from firmware/application/apps/ert_app.hpp rename to firmware/application/external/ert/ert_app.hpp index 9e12fe917..80883b5e4 100644 --- a/firmware/application/apps/ert_app.hpp +++ b/firmware/application/external/ert/ert_app.hpp @@ -104,10 +104,9 @@ class ERTLogger { LogFile log_file{}; }; +namespace ui::external_app::ert_app { + using ERTRecentEntries = RecentEntries; - -namespace ui { - using ERTRecentEntriesView = RecentEntriesView; class ERTAppView : public View { @@ -188,6 +187,6 @@ class ERTAppView : public View { void on_show_list(); }; -} /* namespace ui */ +} /* namespace ui::external_app::ert_app */ #endif /*__ERT_APP_H__*/ diff --git a/firmware/application/external/ert/main.cpp b/firmware/application/external/ert/main.cpp new file mode 100644 index 000000000..3fcbedf6d --- /dev/null +++ b/firmware/application/external/ert/main.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * 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 "ert_app.hpp" +#include "ui_navigation.hpp" +#include "external_app.hpp" + +namespace ui::external_app::ert_app { +void initialize_app(ui::NavigationView& nav) { + nav.push(); +} +} // namespace ui::external_app::ert_app + +extern "C" { + +__attribute__((section(".external_app.app_ert.application_information"), used)) application_information_t _application_information_ert = { + /*.memory_location = */ (uint8_t*)0x00000000, + /*.externalAppEntry = */ ui::external_app::ert_app::initialize_app, + /*.header_version = */ CURRENT_HEADER_VERSION, + /*.app_version = */ VERSION_MD5, + + /*.app_name = */ "ERT Meter", + /*.bitmap_data = */ { + 0x00, + 0x00, + 0x00, + 0x0F, + 0x80, + 0x7F, + 0xC0, + 0x0F, + 0xFC, + 0x0F, + 0xC2, + 0x0F, + 0x82, + 0x7F, + 0x01, + 0x0F, + 0x01, + 0x00, + 0x21, + 0x05, + 0x53, + 0x09, + 0x56, + 0x09, + 0x50, + 0x05, + 0x50, + 0x05, + 0x20, + 0xAD, + 0x00, + 0x00, + }, + /*.icon_color = */ ui::Color::green().v, + /*.menu_location = */ app_location_t::RX, + /*.desired_menu_position = */ -1, + + /*.m4_app_tag = portapack::spi_flash::image_tag_none */ {'P', 'E', 'R', 'T'}, + /*.m4_app_offset = */ 0x00000000, // will be filled at compile time +}; +} diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index 8f8385f6a..9f321ce68 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -232,6 +232,10 @@ set(EXTCPPSRC #battleship external/battleship/main.cpp external/battleship/ui_battleship.cpp + + #ert + external/ert/main.cpp + external/ert/ert_app.cpp ) set(EXTAPPLIST @@ -291,4 +295,5 @@ set(EXTAPPLIST spaceinv blackjack battleship + ert ) diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index 1bc6c4be0..a04cea6b3 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -79,6 +79,7 @@ MEMORY ram_external_app_spaceinv (rwx) : org = 0xADE60000, len = 32k ram_external_app_blackjack (rwx) : org = 0xADE70000, len = 32k ram_external_app_battleship (rwx) : org = 0xADE80000, len = 32k + ram_external_app_ert (rwx) : org = 0xADE90000, len = 32k } SECTIONS @@ -419,5 +420,10 @@ SECTIONS *(*ui*external_app*battleship*); } > ram_external_app_battleship + .external_app_ert : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.external_app.app_ert.application_information)); + *(*ui*external_app*ert*); + } > ram_external_app_ert } diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index b9fafb212..3b856975a 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -70,7 +70,6 @@ #include "ble_rx_app.hpp" #include "ble_tx_app.hpp" #include "capture_app.hpp" -#include "ert_app.hpp" #include "pocsag_app.hpp" #include "soundboard_app.hpp" @@ -131,7 +130,6 @@ const NavigationView::AppList NavigationView::appList = { {"aprsrx", "APRS", RX, Color::green(), &bitmap_icon_aprs, new ViewFactory()}, {"audio", "Audio", RX, Color::green(), &bitmap_icon_speaker, new ViewFactory()}, {"blerx", "BLE Rx", RX, Color::green(), &bitmap_icon_btle, new ViewFactory()}, - {"ert", "ERT Meter", RX, Color::green(), &bitmap_icon_ert, new ViewFactory()}, {"pocsag", "POCSAG", RX, Color::green(), &bitmap_icon_pocsag, new ViewFactory()}, {"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory()}, {"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory()}, diff --git a/firmware/baseband/proc_ble_tx.cpp b/firmware/baseband/proc_ble_tx.cpp index 57d6af9e9..c789bbfa1 100644 --- a/firmware/baseband/proc_ble_tx.cpp +++ b/firmware/baseband/proc_ble_tx.cpp @@ -30,10 +30,10 @@ #define new_way -int BTLETxProcessor::gen_sample_from_phy_bit(char* bit, char* sample, int num_bit) { +int BTLETxProcessor::gen_sample_from_phy_bit(char* bit, int8_t* sample, int num_bit) { int num_sample = (num_bit * SAMPLE_PER_SYMBOL) + (LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL); - int8_t* tmp_phy_bit_over_sampling_int8 = (int8_t*)tmp_phy_bit_over_sampling; + memset(tmp_phy_bit_over_sampling_int8, 0, sizeof(tmp_phy_bit_over_sampling_int8)); int i, j; @@ -242,6 +242,7 @@ void BTLETxProcessor::fill_adv_pdu_header(PKT_INFO* pkt, int txadd, int rxadd, i bit_out[2] = 1; bit_out[1] = 0; bit_out[0] = 0; + rxadd = 1; // Scan response assumed to be with random address. } else if (pkt->pkt_type == CONNECT_REQ) { bit_out[3] = 0; bit_out[2] = 1; @@ -279,14 +280,12 @@ void BTLETxProcessor::fill_adv_pdu_header(PKT_INFO* pkt, int txadd, int rxadd, i int BTLETxProcessor::calculate_sample_for_ADV(PKT_INFO* pkt) { pkt->num_info_bit = 0; - // gen preamble and access address - const char* AA = "AA"; - const char* AAValue = "D6BE898E"; - pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit((char*)AA, pkt->info_bit, 0, 1); - pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit((char*)AAValue, pkt->info_bit + pkt->num_info_bit, 0, 4); + pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit(AA, pkt->info_bit, 0, 1); + pkt->num_info_bit = pkt->num_info_bit + convert_hex_to_bit(AAValue, pkt->info_bit + pkt->num_info_bit, 0, 4); // get txadd and rxadd - int txadd = 0, rxadd = 0; + // Tx assumed to be random address. + int txadd = 1, rxadd = 0; pkt->num_info_bit = pkt->num_info_bit + 16; // 16 is header length @@ -332,39 +331,56 @@ int BTLETxProcessor::calculate_pkt_info(PKT_INFO* pkt) { void BTLETxProcessor::execute(const buffer_c8_t& buffer) { int8_t re, im; - // This is called at 4M/2048 = 1953Hz - for (size_t i = 0; i < buffer.count; i++) { - if (configured) { - // This is going to loop through each sample bit and push it to the output buffer. - if (sample_count > length) { - configured = false; - sample_count = 0; - - txprogress_message.done = true; - shared_memory.application_queue.push(txprogress_message); - } else { - // Real and imaginary was already calculated in gen_sample_from_phy_bit. - // It was processed from each data bit, run through a Gaussian Filter, and then ran through sin and cos table to get each IQ bit. - re = (int8_t)packets.phy_sample[sample_count++]; - im = (int8_t)packets.phy_sample[sample_count++]; - - buffer.p[i] = {re, im}; - - if (progress_count >= progress_notice) { - progress_count = 0; - txprogress_message.progress++; - txprogress_message.done = false; - shared_memory.application_queue.push(txprogress_message); - } else { - progress_count++; - } - } - } else { - re = 0; - im = 0; - - buffer.p[i] = {re, im}; + if (!configured) { + for (size_t i = 0; i < buffer.count; i++) { + buffer.p[i] = {0, 0}; // Fill the buffer with zeros if not configured } + return; + } + + if (configured) { + switch (txprogress_message.progress) { + case 1: + case 2: { + for (size_t i = 0; i < buffer.count; i++) { + buffer.p[i] = {0, 0}; // Pre and Post pad BLE Packet. + } + } break; + + case 0: { + size_t i = 0; + for (i = 0; (i < buffer.count) && (sample_count < length); i++) { + re = packets.phy_sample[sample_count++]; + im = packets.phy_sample[sample_count++]; + + buffer.p[i] = {re, im}; + } + + if (sample_count >= length && i < buffer.count) { + // Fill the rest of the buffer with zeros if we reach the end of the packet + for (; i < buffer.count; i++) { + buffer.p[i] = {0, 0}; + } + } + } break; + + case 3: { + for (size_t i = 0; i < buffer.count; i++) { + buffer.p[i] = {0, 0}; // Pre and Post pad BLE Packet. + } + + if (sample_count >= length) { + sample_count = 0; + configured = false; + + doneSending = true; + txprogress_message.done = true; + shared_memory.application_queue.push(txprogress_message); + } + } break; + } + + txprogress_message.progress++; } } @@ -397,11 +413,13 @@ void BTLETxProcessor::configure(const BTLETxConfigureMessage& message) { // Starting at sample_count 0 since packets.num_phy_sample contains every sample needed to be sent out. sample_count = 0; progress_count = 0; + repeatCount = 0; progress_notice = 64; txprogress_message.progress = 0; txprogress_message.done = false; configured = true; + doneSending = false; } int main() { diff --git a/firmware/baseband/proc_ble_tx.hpp b/firmware/baseband/proc_ble_tx.hpp index 57f22abe9..8894c00c0 100644 --- a/firmware/baseband/proc_ble_tx.hpp +++ b/firmware/baseband/proc_ble_tx.hpp @@ -80,18 +80,11 @@ class BTLETxProcessor : public BasebandProcessor { int num_info_bit; char info_bit[MAX_NUM_PHY_BYTE * 8]; // without CRC and whitening - int num_info_byte; - uint8_t info_byte[MAX_NUM_PHY_BYTE]; - int num_phy_bit; char phy_bit[MAX_NUM_PHY_BYTE * 8]; // all bits which will be fed to GFSK modulator - int num_phy_byte; - uint8_t phy_byte[MAX_NUM_PHY_BYTE]; - int num_phy_sample; - char phy_sample[2 * MAX_NUM_PHY_SAMPLE]; // GFSK output to D/A (hackrf board) - int8_t phy_sample1[2 * MAX_NUM_PHY_SAMPLE]; // GFSK output to D/A (hackrf board) + int8_t phy_sample[2 * MAX_NUM_PHY_SAMPLE]; // GFSK output to D/A (hackrf board) int space; // how many millisecond null signal shouwl be padded after this packet }; @@ -108,22 +101,27 @@ class BTLETxProcessor : public BasebandProcessor { void crc24(char* bit_in, int num_bit, char* init_hex, char* crc_result); int convert_hex_to_bit(char* hex, char* bit, int stream_flip, int octet_limit); void octet_hex_to_bit(char* hex, char* bit); - int gen_sample_from_phy_bit(char* bit, char* sample, int num_bit); + int gen_sample_from_phy_bit(char* bit, int8_t* sample, int num_bit); bool configured = false; - float tmp_phy_bit_over_sampling[MAX_NUM_PHY_SAMPLE + 2 * LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL]; + int8_t tmp_phy_bit_over_sampling_int8[MAX_NUM_PHY_SAMPLE + 2 * LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL]; float gauss_coef[LEN_GAUSS_FILTER * SAMPLE_PER_SYMBOL] = {7.561773e-09, 1.197935e-06, 8.050684e-05, 2.326833e-03, 2.959908e-02, 1.727474e-01, 4.999195e-01, 8.249246e-01, 9.408018e-01, 8.249246e-01, 4.999195e-01, 1.727474e-01, 2.959908e-02, 2.326833e-03, 8.050684e-05, 1.197935e-06}; uint32_t samples_per_bit{4}; uint32_t channel_number{37}; char macAddress[13] = "FFFFFFFFFF"; + char AA[3] = "AA"; + char AAValue[9] = "D6BE898E"; char advertisementData[63] = {0}; uint32_t length{0}; uint32_t shift_zero{}, shift_one{}; uint32_t progress_notice{}, progress_count{0}; uint32_t sample_count{0}; + uint32_t paddingCount{0}; + uint32_t repeatCount{0}; uint32_t phase{0}, sphase{0}; + bool doneSending{false}; uint8_t cur_bit{0}; uint16_t bit_pos{0}; diff --git a/sdcard/BLETX/Android Spam v4.txt b/sdcard/BLETX/Android Spam v4.txt index b4e67c027..da3f010e0 100644 --- a/sdcard/BLETX/Android Spam v4.txt +++ b/sdcard/BLETX/Android Spam v4.txt @@ -1,464 +1,464 @@ -010203040506 03032CFE06162CFE000006020AA8 1 -010203040506 03032CFE06162CFE000007020AA9 1 -010203040506 03032CFE06162CFE000008020AAA 1 -010203040506 03032CFE06162CFE000009020AAB 1 -010203040506 03032CFE06162CFE00000A020AAC 1 -010203040506 03032CFE06162CFE00000B020AAD 1 -010203040506 03032CFE06162CFE00000C020AAE 1 -010203040506 03032CFE06162CFE00000D020AAF 1 -010203040506 03032CFE06162CFE000035020AB0 1 -010203040506 03032CFE06162CFE000047020AB1 1 -010203040506 03032CFE06162CFE000048020AB2 1 -010203040506 03032CFE06162CFE000049020AB3 1 -010203040506 03032CFE06162CFE0000F0020AB4 1 -010203040506 03032CFE06162CFE0002F0020AB5 1 -010203040506 03032CFE06162CFE0003F0020AB6 1 -010203040506 03032CFE06162CFE001000020AB7 1 -010203040506 03032CFE06162CFE002000020AB8 1 -010203040506 03032CFE06162CFE003000020AB9 1 -010203040506 03032CFE06162CFE003001020ABA 1 -010203040506 03032CFE06162CFE003B41020ABB 1 -010203040506 03032CFE06162CFE003D8A020ABC 1 -010203040506 03032CFE06162CFE0052DA020ABD 1 -010203040506 03032CFE06162CFE005BC3020ABE 1 -010203040506 03032CFE06162CFE008F7D020ABF 1 -010203040506 03032CFE06162CFE00A168020AC0 1 -010203040506 03032CFE06162CFE00AA48020AC1 1 -010203040506 03032CFE06162CFE00AA91020AC2 1 -010203040506 03032CFE06162CFE00B727020AC3 1 -010203040506 03032CFE06162CFE00C95C020AC4 1 -010203040506 03032CFE06162CFE00F7D4020AC5 1 -010203040506 03032CFE06162CFE00FA72020AC6 1 -010203040506 03032CFE06162CFE0100F0020AC7 1 -010203040506 03032CFE06162CFE0102F0020AC8 1 -010203040506 03032CFE06162CFE0103F0020AC9 1 -010203040506 03032CFE06162CFE011242020ACA 1 -010203040506 03032CFE06162CFE013D8A020ACB 1 -010203040506 03032CFE06162CFE01AA91020ACC 1 -010203040506 03032CFE06162CFE01C95C020ACD 1 -010203040506 03032CFE06162CFE01E5CE020ACE 1 -010203040506 03032CFE06162CFE01EEB4020ACF 1 -010203040506 03032CFE06162CFE0200F0020AD0 1 -010203040506 03032CFE06162CFE0202F0020AD1 1 -010203040506 03032CFE06162CFE0203F0020AD2 1 -010203040506 03032CFE06162CFE02AA91020AD3 1 -010203040506 03032CFE06162CFE02C95C020AD4 1 -010203040506 03032CFE06162CFE02D815020AD5 1 -010203040506 03032CFE06162CFE02D886020AD6 1 -010203040506 03032CFE06162CFE02DD4F020AD7 1 -010203040506 03032CFE06162CFE02E2A9020AD8 1 -010203040506 03032CFE06162CFE02F637020AD9 1 -010203040506 03032CFE06162CFE0302F0020ADA 1 -010203040506 03032CFE06162CFE0303F0020ADB 1 -010203040506 03032CFE06162CFE035754020ADC 1 -010203040506 03032CFE06162CFE035764020ADD 1 -010203040506 03032CFE06162CFE038B91020ADE 1 -010203040506 03032CFE06162CFE038CC7020ADF 1 -010203040506 03032CFE06162CFE038F16020AE0 1 -010203040506 03032CFE06162CFE039F8F020AE1 1 -010203040506 03032CFE06162CFE03AA91020AE2 1 -010203040506 03032CFE06162CFE03B716020AE3 1 -010203040506 03032CFE06162CFE03C95C020AE4 1 -010203040506 03032CFE06162CFE03C99C020AE5 1 -010203040506 03032CFE06162CFE03F5D4020AE6 1 -010203040506 03032CFE06162CFE0402F0020AE7 1 -010203040506 03032CFE06162CFE0403F0020AE8 1 -010203040506 03032CFE06162CFE045754020AE9 1 -010203040506 03032CFE06162CFE045764020AEA 1 -010203040506 03032CFE06162CFE04AA91020AEB 1 -010203040506 03032CFE06162CFE04ACFC020AEC 1 -010203040506 03032CFE06162CFE04AFB8020AED 1 -010203040506 03032CFE06162CFE04C95C020AEE 1 -010203040506 03032CFE06162CFE0502F0020AEF 1 -010203040506 03032CFE06162CFE0503F0020AF0 1 -010203040506 03032CFE06162CFE050F0C020AF1 1 -010203040506 03032CFE06162CFE052CC7020AF2 1 -010203040506 03032CFE06162CFE054B2D020AF3 1 -010203040506 03032CFE06162CFE0577B1020AF4 1 -010203040506 03032CFE06162CFE057802020AF5 1 -010203040506 03032CFE06162CFE0582FD020AF6 1 -010203040506 03032CFE06162CFE058D08020AF7 1 -010203040506 03032CFE06162CFE05A963020AF8 1 -010203040506 03032CFE06162CFE05A9BC020AF9 1 -010203040506 03032CFE06162CFE05AA91020AFA 1 -010203040506 03032CFE06162CFE05C452020AFB 1 -010203040506 03032CFE06162CFE05C95C020AFC 1 -010203040506 03032CFE06162CFE060000020AFD 1 -010203040506 03032CFE06162CFE0602F0020AFE 1 -010203040506 03032CFE06162CFE0603F0020AFF 1 -010203040506 03032CFE06162CFE0660D7020B00 1 -010203040506 03032CFE06162CFE06AE20020B01 1 -010203040506 03032CFE06162CFE06C197020B02 1 -010203040506 03032CFE06162CFE06C95C020B03 1 -010203040506 03032CFE06162CFE06D8FC020B04 1 -010203040506 03032CFE06162CFE070000020B05 1 -010203040506 03032CFE06162CFE0702F0020B06 1 -010203040506 03032CFE06162CFE0703F0020B07 1 -010203040506 03032CFE06162CFE0X071C020B08 1 -010203040506 03032CFE06162CFE0744B6020B09 1 -010203040506 03032CFE06162CFE07A41C020B0A 1 -010203040506 03032CFE06162CFE07C95C020B0B 1 -010203040506 03032CFE06162CFE07F426020B0C 1 -010203040506 03032CFE06162CFE080000020B0D 1 -010203040506 03032CFE06162CFE0802F0020B0E 1 -010203040506 03032CFE06162CFE0803F0020B0F 1 -010203040506 03032CFE06162CFE090000020B10 1 -010203040506 03032CFE06162CFE0903F0020B11 1 -010203040506 03032CFE06162CFE0A0000020B12 1 -010203040506 03032CFE06162CFE0B0000020B13 1 -010203040506 03032CFE06162CFE0C0000020B14 1 -010203040506 03032CFE06162CFE0DC6BF020B15 1 -010203040506 03032CFE06162CFE0DC95C020B16 1 -010203040506 03032CFE06162CFE0DEC2B020B17 1 -010203040506 03032CFE06162CFE0E138D020B18 1 -010203040506 03032CFE06162CFE0E30C3020B19 1 -010203040506 03032CFE06162CFE0EC95C020B1A 1 -010203040506 03032CFE06162CFE0ECE95020B1B 1 -010203040506 03032CFE06162CFE0F0993020B1C 1 -010203040506 03032CFE06162CFE0F1B8D020B1D 1 -010203040506 03032CFE06162CFE0F232A020B1E 1 -010203040506 03032CFE06162CFE0F2D16020B1F 1 -010203040506 03032CFE06162CFE1E89A7020B20 1 -010203040506 03032CFE06162CFE1E8B18020B21 1 -010203040506 03032CFE06162CFE1E955B020B22 1 -010203040506 03032CFE06162CFE1EC95C020B23 1 -010203040506 03032CFE06162CFE1ED9F9020B24 1 -010203040506 03032CFE06162CFE1EE890020B25 1 -010203040506 03032CFE06162CFE1EEDF5020B26 1 -010203040506 03032CFE06162CFE1F1101020B27 1 -010203040506 03032CFE06162CFE1F181A020B28 1 -010203040506 03032CFE06162CFE1F2E13020B29 1 -010203040506 03032CFE06162CFE1F4589020B2A 1 -010203040506 03032CFE06162CFE1F4627020B2B 1 -010203040506 03032CFE06162CFE1F5865020B2C 1 -010203040506 03032CFE06162CFE1FBB50020B2D 1 -010203040506 03032CFE06162CFE1FC95C020B2E 1 -010203040506 03032CFE06162CFE1FE765020B2F 1 -010203040506 03032CFE06162CFE1FF8FA020B30 1 -010203040506 03032CFE06162CFE201C7C020B31 1 -010203040506 03032CFE06162CFE202B3D020B32 1 -010203040506 03032CFE06162CFE20330C020B33 1 -010203040506 03032CFE06162CFE20A19B020B34 1 -010203040506 03032CFE06162CFE20C95C020B35 1 -010203040506 03032CFE06162CFE20CC2C020B36 1 -010203040506 03032CFE06162CFE213C8C020B37 1 -010203040506 03032CFE06162CFE21521D020B38 1 -010203040506 03032CFE06162CFE21A04E020B39 1 -010203040506 03032CFE06162CFE2D7A23020B3A 1 -010203040506 03032CFE06162CFE350000020B3B 1 -010203040506 03032CFE06162CFE470000020B3C 1 -010203040506 03032CFE06162CFE480000020B3D 1 -010203040506 03032CFE06162CFE490000020B3E 1 -010203040506 03032CFE06162CFE5BA9B5020B3F 1 -010203040506 03032CFE06162CFE5BACD6020B40 1 -010203040506 03032CFE06162CFE5BD6C9020B41 1 -010203040506 03032CFE06162CFE5BE3D4020B42 1 -010203040506 03032CFE06162CFE5C0206020B43 1 -010203040506 03032CFE06162CFE5C0C84020B44 1 -010203040506 03032CFE06162CFE5C4833020B45 1 -010203040506 03032CFE06162CFE5C4A7E020B46 1 -010203040506 03032CFE06162CFE5C55E7020B47 1 -010203040506 03032CFE06162CFE5C7CDC020B48 1 -010203040506 03032CFE06162CFE5C8AA5020B49 1 -010203040506 03032CFE06162CFE5CC900020B4A 1 -010203040506 03032CFE06162CFE5CC901020B4B 1 -010203040506 03032CFE06162CFE5CC902020B4C 1 -010203040506 03032CFE06162CFE5CC903020B4D 1 -010203040506 03032CFE06162CFE5CC904020B4E 1 -010203040506 03032CFE06162CFE5CC905020B4F 1 -010203040506 03032CFE06162CFE5CC906020B50 1 -010203040506 03032CFE06162CFE5CC907020B51 1 -010203040506 03032CFE06162CFE5CC908020B52 1 -010203040506 03032CFE06162CFE5CC909020B53 1 -010203040506 03032CFE06162CFE5CC90A020B54 1 -010203040506 03032CFE06162CFE5CC90B020B55 1 -010203040506 03032CFE06162CFE5CC90C020B56 1 -010203040506 03032CFE06162CFE5CC90D020B57 1 -010203040506 03032CFE06162CFE5CC90E020B58 1 -010203040506 03032CFE06162CFE5CC90F020B59 1 -010203040506 03032CFE06162CFE5CC910020B5A 1 -010203040506 03032CFE06162CFE5CC911020B5B 1 -010203040506 03032CFE06162CFE5CC912020B5C 1 -010203040506 03032CFE06162CFE5CC913020B5D 1 -010203040506 03032CFE06162CFE5CC914020B5E 1 -010203040506 03032CFE06162CFE5CC915020B5F 1 -010203040506 03032CFE06162CFE5CC916020B60 1 -010203040506 03032CFE06162CFE5CC917020B61 1 -010203040506 03032CFE06162CFE5CC918020B62 1 -010203040506 03032CFE06162CFE5CC919020B63 1 -010203040506 03032CFE06162CFE5CC91A020B64 1 -010203040506 03032CFE06162CFE5CC91B020B65 1 -010203040506 03032CFE06162CFE5CC91C020B66 1 -010203040506 03032CFE06162CFE5CC91D020B67 1 -010203040506 03032CFE06162CFE5CC91E020B68 1 -010203040506 03032CFE06162CFE5CC91F020B69 1 -010203040506 03032CFE06162CFE5CC920020B6A 1 -010203040506 03032CFE06162CFE5CC921020B6B 1 -010203040506 03032CFE06162CFE5CC922020B6C 1 -010203040506 03032CFE06162CFE5CC923020B6D 1 -010203040506 03032CFE06162CFE5CC924020B6E 1 -010203040506 03032CFE06162CFE5CC925020B6F 1 -010203040506 03032CFE06162CFE5CC926020B70 1 -010203040506 03032CFE06162CFE5CC927020B71 1 -010203040506 03032CFE06162CFE5CC928020B72 1 -010203040506 03032CFE06162CFE5CC929020B73 1 -010203040506 03032CFE06162CFE5CC92A020B74 1 -010203040506 03032CFE06162CFE5CC92B020B75 1 -010203040506 03032CFE06162CFE5CC92C020B76 1 -010203040506 03032CFE06162CFE5CC92D020B77 1 -010203040506 03032CFE06162CFE5CC92E020B78 1 -010203040506 03032CFE06162CFE5CC92F020B79 1 -010203040506 03032CFE06162CFE5CC930020B7A 1 -010203040506 03032CFE06162CFE5CC931020B7B 1 -010203040506 03032CFE06162CFE5CC932020B7C 1 -010203040506 03032CFE06162CFE5CC933020B7D 1 -010203040506 03032CFE06162CFE5CC934020B7E 1 -010203040506 03032CFE06162CFE5CC935020B7F 1 -010203040506 03032CFE06162CFE5CC936020B80 1 -010203040506 03032CFE06162CFE5CC937020B81 1 -010203040506 03032CFE06162CFE5CC938020B82 1 -010203040506 03032CFE06162CFE5CC939020B83 1 -010203040506 03032CFE06162CFE5CC93A020B84 1 -010203040506 03032CFE06162CFE5CC93B020B85 1 -010203040506 03032CFE06162CFE5CC93C020B86 1 -010203040506 03032CFE06162CFE5CC93D020B87 1 -010203040506 03032CFE06162CFE5CC93E020B88 1 -010203040506 03032CFE06162CFE5CC93F020B89 1 -010203040506 03032CFE06162CFE5CC940020B8A 1 -010203040506 03032CFE06162CFE5CC941020B8B 1 -010203040506 03032CFE06162CFE5CC942020B8C 1 -010203040506 03032CFE06162CFE5CC943020B8D 1 -010203040506 03032CFE06162CFE5CC944020B8E 1 -010203040506 03032CFE06162CFE5CC945020B8F 1 -010203040506 03032CFE06162CFE5CEE3C020B90 1 -010203040506 03032CFE06162CFE6AD226020B91 1 -010203040506 03032CFE06162CFE6B1C64020B92 1 -010203040506 03032CFE06162CFE6B8C65020B93 1 -010203040506 03032CFE06162CFE6B9304020B94 1 -010203040506 03032CFE06162CFE6BA5C3020B95 1 -010203040506 03032CFE06162CFE6C42C0020B96 1 -010203040506 03032CFE06162CFE6C4DE5020B97 1 -010203040506 03032CFE06162CFE718FA4020B98 1 -010203040506 03032CFE06162CFE72EF8D020B99 1 -010203040506 03032CFE06162CFE72FB00020B9A 1 -010203040506 03032CFE06162CFE821F66020B9B 1 -010203040506 03032CFE06162CFE89BAD5020B9C 1 -010203040506 03032CFE06162CFE8A31B7020B9D 1 -010203040506 03032CFE06162CFE8A3D00020B9E 1 -010203040506 03032CFE06162CFE8A3D01020B9F 1 -010203040506 03032CFE06162CFE8A8F23020BA0 1 -010203040506 03032CFE06162CFE8AADAE020BA1 1 -010203040506 03032CFE06162CFE8B0A91020BA2 1 -010203040506 03032CFE06162CFE8B5A7B020BA3 1 -010203040506 03032CFE06162CFE8B66AB020BA4 1 -010203040506 03032CFE06162CFE8BB0A0020BA5 1 -010203040506 03032CFE06162CFE8BF79A020BA6 1 -010203040506 03032CFE06162CFE8C07D2020BA7 1 -010203040506 03032CFE06162CFE8C1706020BA8 1 -010203040506 03032CFE06162CFE8C4236020BA9 1 -010203040506 03032CFE06162CFE8C6B6A020BAA 1 -010203040506 03032CFE06162CFE8CAD81020BAB 1 -010203040506 03032CFE06162CFE8CB05C020BAC 1 -010203040506 03032CFE06162CFE8CD10F020BAD 1 -010203040506 03032CFE06162CFE8D13B9020BAE 1 -010203040506 03032CFE06162CFE8D16EA020BAF 1 -010203040506 03032CFE06162CFE8D5B67020BB0 1 -010203040506 03032CFE06162CFE8E14D7020BB1 1 -010203040506 03032CFE06162CFE8E1996020BB2 1 -010203040506 03032CFE06162CFE8E4666020BB3 1 -010203040506 03032CFE06162CFE8E5550020BB4 1 -010203040506 03032CFE06162CFE9101F0020BB5 1 -010203040506 03032CFE06162CFE9128CB020BB6 1 -010203040506 03032CFE06162CFE913B0C020BB7 1 -010203040506 03032CFE06162CFE915CFA020BB8 1 -010203040506 03032CFE06162CFE9171BE020BB9 1 -010203040506 03032CFE06162CFE917E46020BBA 1 -010203040506 03032CFE06162CFE91AA00020BBB 1 -010203040506 03032CFE06162CFE91AA01020BBC 1 -010203040506 03032CFE06162CFE91AA02020BBD 1 -010203040506 03032CFE06162CFE91AA03020BBE 1 -010203040506 03032CFE06162CFE91AA04020BBF 1 -010203040506 03032CFE06162CFE91AA05020BC0 1 -010203040506 03032CFE06162CFE91BD38020BC1 1 -010203040506 03032CFE06162CFE91C813020BC2 1 -010203040506 03032CFE06162CFE91DABC020BC3 1 -010203040506 03032CFE06162CFE92255E020BC4 1 -010203040506 03032CFE06162CFE92BBBD020BC5 1 -010203040506 03032CFE06162CFE989D0A020BC6 1 -010203040506 03032CFE06162CFE9939BC020BC7 1 -010203040506 03032CFE06162CFE994374020BC8 1 -010203040506 03032CFE06162CFE997B4A020BC9 1 -010203040506 03032CFE06162CFE99C87B020BCA 1 -010203040506 03032CFE06162CFE99D7EA020BCB 1 -010203040506 03032CFE06162CFE99F098020BCC 1 -010203040506 03032CFE06162CFE9A408A020BCD 1 -010203040506 03032CFE06162CFE9A9BDD020BCE 1 -010203040506 03032CFE06162CFE9ADB11020BCF 1 -010203040506 03032CFE06162CFE9AEEA4020BD0 1 -010203040506 03032CFE06162CFE9B7339020BD1 1 -010203040506 03032CFE06162CFE9B735A020BD2 1 -010203040506 03032CFE06162CFE9B9872020BD3 1 -010203040506 03032CFE06162CFE9BC64D020BD4 1 -010203040506 03032CFE06162CFE9BE931020BD5 1 -010203040506 03032CFE06162CFE9C0AF7020BD6 1 -010203040506 03032CFE06162CFE9C3997020BD7 1 -010203040506 03032CFE06162CFE9C4058020BD8 1 -010203040506 03032CFE06162CFE9C6BC0020BD9 1 -010203040506 03032CFE06162CFE9C888B020BDA 1 -010203040506 03032CFE06162CFE9C98DB020BDB 1 -010203040506 03032CFE06162CFE9CA277020BDC 1 -010203040506 03032CFE06162CFE9CB5F3020BDD 1 -010203040506 03032CFE06162CFE9CB881020BDE 1 -010203040506 03032CFE06162CFE9CD0F3020BDF 1 -010203040506 03032CFE06162CFE9CE3C7020BE0 1 -010203040506 03032CFE06162CFE9CEFD1020BE1 1 -010203040506 03032CFE06162CFE9CF08F020BE2 1 -010203040506 03032CFE06162CFE9D00A6020BE3 1 -010203040506 03032CFE06162CFE9D7D42020BE4 1 -010203040506 03032CFE06162CFE9DB896020BE5 1 -010203040506 03032CFE06162CFEA7E52B020BE6 1 -010203040506 03032CFE06162CFEA7EF76020BE7 1 -010203040506 03032CFE06162CFEA8001A020BE8 1 -010203040506 03032CFE06162CFEA83C10020BE9 1 -010203040506 03032CFE06162CFEA8658F020BEA 1 -010203040506 03032CFE06162CFEA8845A020BEB 1 -010203040506 03032CFE06162CFEA88B69020BEC 1 -010203040506 03032CFE06162CFEA8A00E020BED 1 -010203040506 03032CFE06162CFEA8A72A020BEE 1 -010203040506 03032CFE06162CFEA8C636020BEF 1 -010203040506 03032CFE06162CFEA8CAAD020BF0 1 -010203040506 03032CFE06162CFEA8E353020BF1 1 -010203040506 03032CFE06162CFEA8F96D020BF2 1 -010203040506 03032CFE06162CFEA90358020BF3 1 -010203040506 03032CFE06162CFEA92498020BF4 1 -010203040506 03032CFE06162CFEA9394A020BF5 1 -010203040506 03032CFE06162CFEC6936A020BF6 1 -010203040506 03032CFE06162CFEC69AFD020BF7 1 -010203040506 03032CFE06162CFEC6ABEA020BF8 1 -010203040506 03032CFE06162CFEC6EC5F020BF9 1 -010203040506 03032CFE06162CFEC7736C020BFA 1 -010203040506 03032CFE06162CFEC79B91020BFB 1 -010203040506 03032CFE06162CFEC7A267020BFC 1 -010203040506 03032CFE06162CFEC7D620020BFD 1 -010203040506 03032CFE06162CFEC7FBCC020BFE 1 -010203040506 03032CFE06162CFEC8162A020BFF 1 -010203040506 03032CFE06162CFEC85D7A020C00 1 -010203040506 03032CFE06162CFEC8777E020C01 1 -010203040506 03032CFE06162CFEC878AA020C02 1 -010203040506 03032CFE06162CFEC8C641020C03 1 -010203040506 03032CFE06162CFEC8D335020C04 1 -010203040506 03032CFE06162CFEC8E228020C05 1 -010203040506 03032CFE06162CFEC9186B020C06 1 -010203040506 03032CFE06162CFEC9836A020C07 1 -010203040506 03032CFE06162CFECA7030020C08 1 -010203040506 03032CFE06162CFECAB6B8020C09 1 -010203040506 03032CFE06162CFECAF511020C0A 1 -010203040506 03032CFE06162CFECB093B020C0B 1 -010203040506 03032CFE06162CFECB529D020C0C 1 -010203040506 03032CFE06162CFECC438E020C0D 1 -010203040506 03032CFE06162CFECC5F29020C0E 1 -010203040506 03032CFE06162CFECC754F020C0F 1 -010203040506 03032CFE06162CFECC93A5020C10 1 -010203040506 03032CFE06162CFECCBB7E020C11 1 -010203040506 03032CFE06162CFECD8256020C12 1 -010203040506 03032CFE06162CFED446A7020C13 1 -010203040506 03032CFE06162CFED5A59E020C14 1 -010203040506 03032CFE06162CFED5B5F7020C15 1 -010203040506 03032CFE06162CFED5C6CE020C16 1 -010203040506 03032CFE06162CFED654CD020C17 1 -010203040506 03032CFE06162CFED65F4E020C18 1 -010203040506 03032CFE06162CFED69B2B020C19 1 -010203040506 03032CFE06162CFED6C195020C1A 1 -010203040506 03032CFE06162CFED6E870020C1B 1 -010203040506 03032CFE06162CFED6EE84020C1C 1 -010203040506 03032CFE06162CFED7102F020C1D 1 -010203040506 03032CFE06162CFED7E3EB020C1E 1 -010203040506 03032CFE06162CFED8058C020C1F 1 -010203040506 03032CFE06162CFED820EA020C20 1 -010203040506 03032CFE06162CFED87A3E020C21 1 -010203040506 03032CFE06162CFED8F3BA020C22 1 -010203040506 03032CFE06162CFED8F4E8020C23 1 -010203040506 03032CFE06162CFED90617020C24 1 -010203040506 03032CFE06162CFED933A7020C25 1 -010203040506 03032CFE06162CFED9414F020C26 1 -010203040506 03032CFE06162CFED97EBA020C27 1 -010203040506 03032CFE06162CFED9964B020C28 1 -010203040506 03032CFE06162CFEDA0F83020C29 1 -010203040506 03032CFE06162CFEDA4577020C2A 1 -010203040506 03032CFE06162CFEDA5200020C2B 1 -010203040506 03032CFE06162CFEDAD3A6020C2C 1 -010203040506 03032CFE06162CFEDADE43020C2D 1 -010203040506 03032CFE06162CFEDAE096020C2E 1 -010203040506 03032CFE06162CFEDB8AC7020C2F 1 -010203040506 03032CFE06162CFEDBE5B1020C30 1 -010203040506 03032CFE06162CFEDC5249020C31 1 -010203040506 03032CFE06162CFEDCF33C020C32 1 -010203040506 03032CFE06162CFEDD4EC0020C33 1 -010203040506 03032CFE06162CFEDE215D020C34 1 -010203040506 03032CFE06162CFEDE577F020C35 1 -010203040506 03032CFE06162CFEDEC04C020C36 1 -010203040506 03032CFE06162CFEDEDD6F020C37 1 -010203040506 03032CFE06162CFEDEE8C0020C38 1 -010203040506 03032CFE06162CFEDEEA86020C39 1 -010203040506 03032CFE06162CFEDEF234020C3A 1 -010203040506 03032CFE06162CFEDF01E3020C3B 1 -010203040506 03032CFE06162CFEDF271C020C3C 1 -010203040506 03032CFE06162CFEDF42DE020C3D 1 -010203040506 03032CFE06162CFEDF4B02020C3E 1 -010203040506 03032CFE06162CFEDF9BA4020C3F 1 -010203040506 03032CFE06162CFEDFD433020C40 1 -010203040506 03032CFE06162CFEE020C1020C41 1 -010203040506 03032CFE06162CFEE06116020C42 1 -010203040506 03032CFE06162CFEE07634020C43 1 -010203040506 03032CFE06162CFEE09172020C44 1 -010203040506 03032CFE06162CFEE4E457020C45 1 -010203040506 03032CFE06162CFEE5440B020C46 1 -010203040506 03032CFE06162CFEE57363020C47 1 -010203040506 03032CFE06162CFEE57B57020C48 1 -010203040506 03032CFE06162CFEE5B4B0020C49 1 -010203040506 03032CFE06162CFEE5B91B020C4A 1 -010203040506 03032CFE06162CFEE5E2E9020C4B 1 -010203040506 03032CFE06162CFEE64613020C4C 1 -010203040506 03032CFE06162CFEE64CC6020C4D 1 -010203040506 03032CFE06162CFEE69877020C4E 1 -010203040506 03032CFE06162CFEE6E37E020C4F 1 -010203040506 03032CFE06162CFEE6E771020C50 1 -010203040506 03032CFE06162CFEE6E8B8020C51 1 -010203040506 03032CFE06162CFEE750CE020C52 1 -010203040506 03032CFE06162CFEF00000020C53 1 -010203040506 03032CFE06162CFEF00001020C54 1 -010203040506 03032CFE06162CFEF00002020C55 1 -010203040506 03032CFE06162CFEF00200020C56 1 -010203040506 03032CFE06162CFEF00201020C57 1 -010203040506 03032CFE06162CFEF00202020C58 1 -010203040506 03032CFE06162CFEF00203020C59 1 -010203040506 03032CFE06162CFEF00204020C5A 1 -010203040506 03032CFE06162CFEF00205020C5B 1 -010203040506 03032CFE06162CFEF00206020C5C 1 -010203040506 03032CFE06162CFEF00207020C5D 1 -010203040506 03032CFE06162CFEF00208020C5E 1 -010203040506 03032CFE06162CFEF00209020C5F 1 -010203040506 03032CFE06162CFEF0020A020C60 1 -010203040506 03032CFE06162CFEF0020B020C61 1 -010203040506 03032CFE06162CFEF0020C020C62 1 -010203040506 03032CFE06162CFEF0020D020C63 1 -010203040506 03032CFE06162CFEF0020E020C64 1 -010203040506 03032CFE06162CFEF0020F020C65 1 -010203040506 03032CFE06162CFEF00210020C66 1 -010203040506 03032CFE06162CFEF00211020C67 1 -010203040506 03032CFE06162CFEF00212020C68 1 -010203040506 03032CFE06162CFEF00213020C69 1 -010203040506 03032CFE06162CFEF00214020C6A 1 -010203040506 03032CFE06162CFEF00215020C6B 1 -010203040506 03032CFE06162CFEF00300020C6C 1 -010203040506 03032CFE06162CFEF00301020C6D 1 -010203040506 03032CFE06162CFEF00302020C6E 1 -010203040506 03032CFE06162CFEF00303020C6F 1 -010203040506 03032CFE06162CFEF00304020C70 1 -010203040506 03032CFE06162CFEF00305020C71 1 -010203040506 03032CFE06162CFEF00306020C72 1 -010203040506 03032CFE06162CFEF00307020C73 1 -010203040506 03032CFE06162CFEF00308020C74 1 -010203040506 03032CFE06162CFEF00309020C75 1 -010203040506 03032CFE06162CFEF00400020C76 1 -010203040506 03032CFE06162CFEF00E97020C77 1 \ No newline at end of file +010203040506 03032CFE06162CFE000006020AA8 DISCOVERY 1 +010203040506 03032CFE06162CFE000007020AA9 DISCOVERY 1 +010203040506 03032CFE06162CFE000008020AAA DISCOVERY 1 +010203040506 03032CFE06162CFE000009020AAB DISCOVERY 1 +010203040506 03032CFE06162CFE00000A020AAC DISCOVERY 1 +010203040506 03032CFE06162CFE00000B020AAD DISCOVERY 1 +010203040506 03032CFE06162CFE00000C020AAE DISCOVERY 1 +010203040506 03032CFE06162CFE00000D020AAF DISCOVERY 1 +010203040506 03032CFE06162CFE000035020AB0 DISCOVERY 1 +010203040506 03032CFE06162CFE000047020AB1 DISCOVERY 1 +010203040506 03032CFE06162CFE000048020AB2 DISCOVERY 1 +010203040506 03032CFE06162CFE000049020AB3 DISCOVERY 1 +010203040506 03032CFE06162CFE0000F0020AB4 DISCOVERY 1 +010203040506 03032CFE06162CFE0002F0020AB5 DISCOVERY 1 +010203040506 03032CFE06162CFE0003F0020AB6 DISCOVERY 1 +010203040506 03032CFE06162CFE001000020AB7 DISCOVERY 1 +010203040506 03032CFE06162CFE002000020AB8 DISCOVERY 1 +010203040506 03032CFE06162CFE003000020AB9 DISCOVERY 1 +010203040506 03032CFE06162CFE003001020ABA DISCOVERY 1 +010203040506 03032CFE06162CFE003B41020ABB DISCOVERY 1 +010203040506 03032CFE06162CFE003D8A020ABC DISCOVERY 1 +010203040506 03032CFE06162CFE0052DA020ABD DISCOVERY 1 +010203040506 03032CFE06162CFE005BC3020ABE DISCOVERY 1 +010203040506 03032CFE06162CFE008F7D020ABF DISCOVERY 1 +010203040506 03032CFE06162CFE00A168020AC0 DISCOVERY 1 +010203040506 03032CFE06162CFE00AA48020AC1 DISCOVERY 1 +010203040506 03032CFE06162CFE00AA91020AC2 DISCOVERY 1 +010203040506 03032CFE06162CFE00B727020AC3 DISCOVERY 1 +010203040506 03032CFE06162CFE00C95C020AC4 DISCOVERY 1 +010203040506 03032CFE06162CFE00F7D4020AC5 DISCOVERY 1 +010203040506 03032CFE06162CFE00FA72020AC6 DISCOVERY 1 +010203040506 03032CFE06162CFE0100F0020AC7 DISCOVERY 1 +010203040506 03032CFE06162CFE0102F0020AC8 DISCOVERY 1 +010203040506 03032CFE06162CFE0103F0020AC9 DISCOVERY 1 +010203040506 03032CFE06162CFE011242020ACA DISCOVERY 1 +010203040506 03032CFE06162CFE013D8A020ACB DISCOVERY 1 +010203040506 03032CFE06162CFE01AA91020ACC DISCOVERY 1 +010203040506 03032CFE06162CFE01C95C020ACD DISCOVERY 1 +010203040506 03032CFE06162CFE01E5CE020ACE DISCOVERY 1 +010203040506 03032CFE06162CFE01EEB4020ACF DISCOVERY 1 +010203040506 03032CFE06162CFE0200F0020AD0 DISCOVERY 1 +010203040506 03032CFE06162CFE0202F0020AD1 DISCOVERY 1 +010203040506 03032CFE06162CFE0203F0020AD2 DISCOVERY 1 +010203040506 03032CFE06162CFE02AA91020AD3 DISCOVERY 1 +010203040506 03032CFE06162CFE02C95C020AD4 DISCOVERY 1 +010203040506 03032CFE06162CFE02D815020AD5 DISCOVERY 1 +010203040506 03032CFE06162CFE02D886020AD6 DISCOVERY 1 +010203040506 03032CFE06162CFE02DD4F020AD7 DISCOVERY 1 +010203040506 03032CFE06162CFE02E2A9020AD8 DISCOVERY 1 +010203040506 03032CFE06162CFE02F637020AD9 DISCOVERY 1 +010203040506 03032CFE06162CFE0302F0020ADA DISCOVERY 1 +010203040506 03032CFE06162CFE0303F0020ADB DISCOVERY 1 +010203040506 03032CFE06162CFE035754020ADC DISCOVERY 1 +010203040506 03032CFE06162CFE035764020ADD DISCOVERY 1 +010203040506 03032CFE06162CFE038B91020ADE DISCOVERY 1 +010203040506 03032CFE06162CFE038CC7020ADF DISCOVERY 1 +010203040506 03032CFE06162CFE038F16020AE0 DISCOVERY 1 +010203040506 03032CFE06162CFE039F8F020AE1 DISCOVERY 1 +010203040506 03032CFE06162CFE03AA91020AE2 DISCOVERY 1 +010203040506 03032CFE06162CFE03B716020AE3 DISCOVERY 1 +010203040506 03032CFE06162CFE03C95C020AE4 DISCOVERY 1 +010203040506 03032CFE06162CFE03C99C020AE5 DISCOVERY 1 +010203040506 03032CFE06162CFE03F5D4020AE6 DISCOVERY 1 +010203040506 03032CFE06162CFE0402F0020AE7 DISCOVERY 1 +010203040506 03032CFE06162CFE0403F0020AE8 DISCOVERY 1 +010203040506 03032CFE06162CFE045754020AE9 DISCOVERY 1 +010203040506 03032CFE06162CFE045764020AEA DISCOVERY 1 +010203040506 03032CFE06162CFE04AA91020AEB DISCOVERY 1 +010203040506 03032CFE06162CFE04ACFC020AEC DISCOVERY 1 +010203040506 03032CFE06162CFE04AFB8020AED DISCOVERY 1 +010203040506 03032CFE06162CFE04C95C020AEE DISCOVERY 1 +010203040506 03032CFE06162CFE0502F0020AEF DISCOVERY 1 +010203040506 03032CFE06162CFE0503F0020AF0 DISCOVERY 1 +010203040506 03032CFE06162CFE050F0C020AF1 DISCOVERY 1 +010203040506 03032CFE06162CFE052CC7020AF2 DISCOVERY 1 +010203040506 03032CFE06162CFE054B2D020AF3 DISCOVERY 1 +010203040506 03032CFE06162CFE0577B1020AF4 DISCOVERY 1 +010203040506 03032CFE06162CFE057802020AF5 DISCOVERY 1 +010203040506 03032CFE06162CFE0582FD020AF6 DISCOVERY 1 +010203040506 03032CFE06162CFE058D08020AF7 DISCOVERY 1 +010203040506 03032CFE06162CFE05A963020AF8 DISCOVERY 1 +010203040506 03032CFE06162CFE05A9BC020AF9 DISCOVERY 1 +010203040506 03032CFE06162CFE05AA91020AFA DISCOVERY 1 +010203040506 03032CFE06162CFE05C452020AFB DISCOVERY 1 +010203040506 03032CFE06162CFE05C95C020AFC DISCOVERY 1 +010203040506 03032CFE06162CFE060000020AFD DISCOVERY 1 +010203040506 03032CFE06162CFE0602F0020AFE DISCOVERY 1 +010203040506 03032CFE06162CFE0603F0020AFF DISCOVERY 1 +010203040506 03032CFE06162CFE0660D7020B00 DISCOVERY 1 +010203040506 03032CFE06162CFE06AE20020B01 DISCOVERY 1 +010203040506 03032CFE06162CFE06C197020B02 DISCOVERY 1 +010203040506 03032CFE06162CFE06C95C020B03 DISCOVERY 1 +010203040506 03032CFE06162CFE06D8FC020B04 DISCOVERY 1 +010203040506 03032CFE06162CFE070000020B05 DISCOVERY 1 +010203040506 03032CFE06162CFE0702F0020B06 DISCOVERY 1 +010203040506 03032CFE06162CFE0703F0020B07 DISCOVERY 1 +010203040506 03032CFE06162CFE0X071C020B08 DISCOVERY 1 +010203040506 03032CFE06162CFE0744B6020B09 DISCOVERY 1 +010203040506 03032CFE06162CFE07A41C020B0A DISCOVERY 1 +010203040506 03032CFE06162CFE07C95C020B0B DISCOVERY 1 +010203040506 03032CFE06162CFE07F426020B0C DISCOVERY 1 +010203040506 03032CFE06162CFE080000020B0D DISCOVERY 1 +010203040506 03032CFE06162CFE0802F0020B0E DISCOVERY 1 +010203040506 03032CFE06162CFE0803F0020B0F DISCOVERY 1 +010203040506 03032CFE06162CFE090000020B10 DISCOVERY 1 +010203040506 03032CFE06162CFE0903F0020B11 DISCOVERY 1 +010203040506 03032CFE06162CFE0A0000020B12 DISCOVERY 1 +010203040506 03032CFE06162CFE0B0000020B13 DISCOVERY 1 +010203040506 03032CFE06162CFE0C0000020B14 DISCOVERY 1 +010203040506 03032CFE06162CFE0DC6BF020B15 DISCOVERY 1 +010203040506 03032CFE06162CFE0DC95C020B16 DISCOVERY 1 +010203040506 03032CFE06162CFE0DEC2B020B17 DISCOVERY 1 +010203040506 03032CFE06162CFE0E138D020B18 DISCOVERY 1 +010203040506 03032CFE06162CFE0E30C3020B19 DISCOVERY 1 +010203040506 03032CFE06162CFE0EC95C020B1A DISCOVERY 1 +010203040506 03032CFE06162CFE0ECE95020B1B DISCOVERY 1 +010203040506 03032CFE06162CFE0F0993020B1C DISCOVERY 1 +010203040506 03032CFE06162CFE0F1B8D020B1D DISCOVERY 1 +010203040506 03032CFE06162CFE0F232A020B1E DISCOVERY 1 +010203040506 03032CFE06162CFE0F2D16020B1F DISCOVERY 1 +010203040506 03032CFE06162CFE1E89A7020B20 DISCOVERY 1 +010203040506 03032CFE06162CFE1E8B18020B21 DISCOVERY 1 +010203040506 03032CFE06162CFE1E955B020B22 DISCOVERY 1 +010203040506 03032CFE06162CFE1EC95C020B23 DISCOVERY 1 +010203040506 03032CFE06162CFE1ED9F9020B24 DISCOVERY 1 +010203040506 03032CFE06162CFE1EE890020B25 DISCOVERY 1 +010203040506 03032CFE06162CFE1EEDF5020B26 DISCOVERY 1 +010203040506 03032CFE06162CFE1F1101020B27 DISCOVERY 1 +010203040506 03032CFE06162CFE1F181A020B28 DISCOVERY 1 +010203040506 03032CFE06162CFE1F2E13020B29 DISCOVERY 1 +010203040506 03032CFE06162CFE1F4589020B2A DISCOVERY 1 +010203040506 03032CFE06162CFE1F4627020B2B DISCOVERY 1 +010203040506 03032CFE06162CFE1F5865020B2C DISCOVERY 1 +010203040506 03032CFE06162CFE1FBB50020B2D DISCOVERY 1 +010203040506 03032CFE06162CFE1FC95C020B2E DISCOVERY 1 +010203040506 03032CFE06162CFE1FE765020B2F DISCOVERY 1 +010203040506 03032CFE06162CFE1FF8FA020B30 DISCOVERY 1 +010203040506 03032CFE06162CFE201C7C020B31 DISCOVERY 1 +010203040506 03032CFE06162CFE202B3D020B32 DISCOVERY 1 +010203040506 03032CFE06162CFE20330C020B33 DISCOVERY 1 +010203040506 03032CFE06162CFE20A19B020B34 DISCOVERY 1 +010203040506 03032CFE06162CFE20C95C020B35 DISCOVERY 1 +010203040506 03032CFE06162CFE20CC2C020B36 DISCOVERY 1 +010203040506 03032CFE06162CFE213C8C020B37 DISCOVERY 1 +010203040506 03032CFE06162CFE21521D020B38 DISCOVERY 1 +010203040506 03032CFE06162CFE21A04E020B39 DISCOVERY 1 +010203040506 03032CFE06162CFE2D7A23020B3A DISCOVERY 1 +010203040506 03032CFE06162CFE350000020B3B DISCOVERY 1 +010203040506 03032CFE06162CFE470000020B3C DISCOVERY 1 +010203040506 03032CFE06162CFE480000020B3D DISCOVERY 1 +010203040506 03032CFE06162CFE490000020B3E DISCOVERY 1 +010203040506 03032CFE06162CFE5BA9B5020B3F DISCOVERY 1 +010203040506 03032CFE06162CFE5BACD6020B40 DISCOVERY 1 +010203040506 03032CFE06162CFE5BD6C9020B41 DISCOVERY 1 +010203040506 03032CFE06162CFE5BE3D4020B42 DISCOVERY 1 +010203040506 03032CFE06162CFE5C0206020B43 DISCOVERY 1 +010203040506 03032CFE06162CFE5C0C84020B44 DISCOVERY 1 +010203040506 03032CFE06162CFE5C4833020B45 DISCOVERY 1 +010203040506 03032CFE06162CFE5C4A7E020B46 DISCOVERY 1 +010203040506 03032CFE06162CFE5C55E7020B47 DISCOVERY 1 +010203040506 03032CFE06162CFE5C7CDC020B48 DISCOVERY 1 +010203040506 03032CFE06162CFE5C8AA5020B49 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC900020B4A DISCOVERY 1 +010203040506 03032CFE06162CFE5CC901020B4B DISCOVERY 1 +010203040506 03032CFE06162CFE5CC902020B4C DISCOVERY 1 +010203040506 03032CFE06162CFE5CC903020B4D DISCOVERY 1 +010203040506 03032CFE06162CFE5CC904020B4E DISCOVERY 1 +010203040506 03032CFE06162CFE5CC905020B4F DISCOVERY 1 +010203040506 03032CFE06162CFE5CC906020B50 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC907020B51 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC908020B52 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC909020B53 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC90A020B54 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC90B020B55 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC90C020B56 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC90D020B57 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC90E020B58 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC90F020B59 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC910020B5A DISCOVERY 1 +010203040506 03032CFE06162CFE5CC911020B5B DISCOVERY 1 +010203040506 03032CFE06162CFE5CC912020B5C DISCOVERY 1 +010203040506 03032CFE06162CFE5CC913020B5D DISCOVERY 1 +010203040506 03032CFE06162CFE5CC914020B5E DISCOVERY 1 +010203040506 03032CFE06162CFE5CC915020B5F DISCOVERY 1 +010203040506 03032CFE06162CFE5CC916020B60 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC917020B61 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC918020B62 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC919020B63 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC91A020B64 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC91B020B65 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC91C020B66 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC91D020B67 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC91E020B68 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC91F020B69 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC920020B6A DISCOVERY 1 +010203040506 03032CFE06162CFE5CC921020B6B DISCOVERY 1 +010203040506 03032CFE06162CFE5CC922020B6C DISCOVERY 1 +010203040506 03032CFE06162CFE5CC923020B6D DISCOVERY 1 +010203040506 03032CFE06162CFE5CC924020B6E DISCOVERY 1 +010203040506 03032CFE06162CFE5CC925020B6F DISCOVERY 1 +010203040506 03032CFE06162CFE5CC926020B70 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC927020B71 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC928020B72 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC929020B73 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC92A020B74 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC92B020B75 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC92C020B76 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC92D020B77 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC92E020B78 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC92F020B79 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC930020B7A DISCOVERY 1 +010203040506 03032CFE06162CFE5CC931020B7B DISCOVERY 1 +010203040506 03032CFE06162CFE5CC932020B7C DISCOVERY 1 +010203040506 03032CFE06162CFE5CC933020B7D DISCOVERY 1 +010203040506 03032CFE06162CFE5CC934020B7E DISCOVERY 1 +010203040506 03032CFE06162CFE5CC935020B7F DISCOVERY 1 +010203040506 03032CFE06162CFE5CC936020B80 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC937020B81 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC938020B82 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC939020B83 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC93A020B84 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC93B020B85 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC93C020B86 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC93D020B87 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC93E020B88 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC93F020B89 DISCOVERY 1 +010203040506 03032CFE06162CFE5CC940020B8A DISCOVERY 1 +010203040506 03032CFE06162CFE5CC941020B8B DISCOVERY 1 +010203040506 03032CFE06162CFE5CC942020B8C DISCOVERY 1 +010203040506 03032CFE06162CFE5CC943020B8D DISCOVERY 1 +010203040506 03032CFE06162CFE5CC944020B8E DISCOVERY 1 +010203040506 03032CFE06162CFE5CC945020B8F DISCOVERY 1 +010203040506 03032CFE06162CFE5CEE3C020B90 DISCOVERY 1 +010203040506 03032CFE06162CFE6AD226020B91 DISCOVERY 1 +010203040506 03032CFE06162CFE6B1C64020B92 DISCOVERY 1 +010203040506 03032CFE06162CFE6B8C65020B93 DISCOVERY 1 +010203040506 03032CFE06162CFE6B9304020B94 DISCOVERY 1 +010203040506 03032CFE06162CFE6BA5C3020B95 DISCOVERY 1 +010203040506 03032CFE06162CFE6C42C0020B96 DISCOVERY 1 +010203040506 03032CFE06162CFE6C4DE5020B97 DISCOVERY 1 +010203040506 03032CFE06162CFE718FA4020B98 DISCOVERY 1 +010203040506 03032CFE06162CFE72EF8D020B99 DISCOVERY 1 +010203040506 03032CFE06162CFE72FB00020B9A DISCOVERY 1 +010203040506 03032CFE06162CFE821F66020B9B DISCOVERY 1 +010203040506 03032CFE06162CFE89BAD5020B9C DISCOVERY 1 +010203040506 03032CFE06162CFE8A31B7020B9D DISCOVERY 1 +010203040506 03032CFE06162CFE8A3D00020B9E DISCOVERY 1 +010203040506 03032CFE06162CFE8A3D01020B9F DISCOVERY 1 +010203040506 03032CFE06162CFE8A8F23020BA0 DISCOVERY 1 +010203040506 03032CFE06162CFE8AADAE020BA1 DISCOVERY 1 +010203040506 03032CFE06162CFE8B0A91020BA2 DISCOVERY 1 +010203040506 03032CFE06162CFE8B5A7B020BA3 DISCOVERY 1 +010203040506 03032CFE06162CFE8B66AB020BA4 DISCOVERY 1 +010203040506 03032CFE06162CFE8BB0A0020BA5 DISCOVERY 1 +010203040506 03032CFE06162CFE8BF79A020BA6 DISCOVERY 1 +010203040506 03032CFE06162CFE8C07D2020BA7 DISCOVERY 1 +010203040506 03032CFE06162CFE8C1706020BA8 DISCOVERY 1 +010203040506 03032CFE06162CFE8C4236020BA9 DISCOVERY 1 +010203040506 03032CFE06162CFE8C6B6A020BAA DISCOVERY 1 +010203040506 03032CFE06162CFE8CAD81020BAB DISCOVERY 1 +010203040506 03032CFE06162CFE8CB05C020BAC DISCOVERY 1 +010203040506 03032CFE06162CFE8CD10F020BAD DISCOVERY 1 +010203040506 03032CFE06162CFE8D13B9020BAE DISCOVERY 1 +010203040506 03032CFE06162CFE8D16EA020BAF DISCOVERY 1 +010203040506 03032CFE06162CFE8D5B67020BB0 DISCOVERY 1 +010203040506 03032CFE06162CFE8E14D7020BB1 DISCOVERY 1 +010203040506 03032CFE06162CFE8E1996020BB2 DISCOVERY 1 +010203040506 03032CFE06162CFE8E4666020BB3 DISCOVERY 1 +010203040506 03032CFE06162CFE8E5550020BB4 DISCOVERY 1 +010203040506 03032CFE06162CFE9101F0020BB5 DISCOVERY 1 +010203040506 03032CFE06162CFE9128CB020BB6 DISCOVERY 1 +010203040506 03032CFE06162CFE913B0C020BB7 DISCOVERY 1 +010203040506 03032CFE06162CFE915CFA020BB8 DISCOVERY 1 +010203040506 03032CFE06162CFE9171BE020BB9 DISCOVERY 1 +010203040506 03032CFE06162CFE917E46020BBA DISCOVERY 1 +010203040506 03032CFE06162CFE91AA00020BBB DISCOVERY 1 +010203040506 03032CFE06162CFE91AA01020BBC DISCOVERY 1 +010203040506 03032CFE06162CFE91AA02020BBD DISCOVERY 1 +010203040506 03032CFE06162CFE91AA03020BBE DISCOVERY 1 +010203040506 03032CFE06162CFE91AA04020BBF DISCOVERY 1 +010203040506 03032CFE06162CFE91AA05020BC0 DISCOVERY 1 +010203040506 03032CFE06162CFE91BD38020BC1 DISCOVERY 1 +010203040506 03032CFE06162CFE91C813020BC2 DISCOVERY 1 +010203040506 03032CFE06162CFE91DABC020BC3 DISCOVERY 1 +010203040506 03032CFE06162CFE92255E020BC4 DISCOVERY 1 +010203040506 03032CFE06162CFE92BBBD020BC5 DISCOVERY 1 +010203040506 03032CFE06162CFE989D0A020BC6 DISCOVERY 1 +010203040506 03032CFE06162CFE9939BC020BC7 DISCOVERY 1 +010203040506 03032CFE06162CFE994374020BC8 DISCOVERY 1 +010203040506 03032CFE06162CFE997B4A020BC9 DISCOVERY 1 +010203040506 03032CFE06162CFE99C87B020BCA DISCOVERY 1 +010203040506 03032CFE06162CFE99D7EA020BCB DISCOVERY 1 +010203040506 03032CFE06162CFE99F098020BCC DISCOVERY 1 +010203040506 03032CFE06162CFE9A408A020BCD DISCOVERY 1 +010203040506 03032CFE06162CFE9A9BDD020BCE DISCOVERY 1 +010203040506 03032CFE06162CFE9ADB11020BCF DISCOVERY 1 +010203040506 03032CFE06162CFE9AEEA4020BD0 DISCOVERY 1 +010203040506 03032CFE06162CFE9B7339020BD1 DISCOVERY 1 +010203040506 03032CFE06162CFE9B735A020BD2 DISCOVERY 1 +010203040506 03032CFE06162CFE9B9872020BD3 DISCOVERY 1 +010203040506 03032CFE06162CFE9BC64D020BD4 DISCOVERY 1 +010203040506 03032CFE06162CFE9BE931020BD5 DISCOVERY 1 +010203040506 03032CFE06162CFE9C0AF7020BD6 DISCOVERY 1 +010203040506 03032CFE06162CFE9C3997020BD7 DISCOVERY 1 +010203040506 03032CFE06162CFE9C4058020BD8 DISCOVERY 1 +010203040506 03032CFE06162CFE9C6BC0020BD9 DISCOVERY 1 +010203040506 03032CFE06162CFE9C888B020BDA DISCOVERY 1 +010203040506 03032CFE06162CFE9C98DB020BDB DISCOVERY 1 +010203040506 03032CFE06162CFE9CA277020BDC DISCOVERY 1 +010203040506 03032CFE06162CFE9CB5F3020BDD DISCOVERY 1 +010203040506 03032CFE06162CFE9CB881020BDE DISCOVERY 1 +010203040506 03032CFE06162CFE9CD0F3020BDF DISCOVERY 1 +010203040506 03032CFE06162CFE9CE3C7020BE0 DISCOVERY 1 +010203040506 03032CFE06162CFE9CEFD1020BE1 DISCOVERY 1 +010203040506 03032CFE06162CFE9CF08F020BE2 DISCOVERY 1 +010203040506 03032CFE06162CFE9D00A6020BE3 DISCOVERY 1 +010203040506 03032CFE06162CFE9D7D42020BE4 DISCOVERY 1 +010203040506 03032CFE06162CFE9DB896020BE5 DISCOVERY 1 +010203040506 03032CFE06162CFEA7E52B020BE6 DISCOVERY 1 +010203040506 03032CFE06162CFEA7EF76020BE7 DISCOVERY 1 +010203040506 03032CFE06162CFEA8001A020BE8 DISCOVERY 1 +010203040506 03032CFE06162CFEA83C10020BE9 DISCOVERY 1 +010203040506 03032CFE06162CFEA8658F020BEA DISCOVERY 1 +010203040506 03032CFE06162CFEA8845A020BEB DISCOVERY 1 +010203040506 03032CFE06162CFEA88B69020BEC DISCOVERY 1 +010203040506 03032CFE06162CFEA8A00E020BED DISCOVERY 1 +010203040506 03032CFE06162CFEA8A72A020BEE DISCOVERY 1 +010203040506 03032CFE06162CFEA8C636020BEF DISCOVERY 1 +010203040506 03032CFE06162CFEA8CAAD020BF0 DISCOVERY 1 +010203040506 03032CFE06162CFEA8E353020BF1 DISCOVERY 1 +010203040506 03032CFE06162CFEA8F96D020BF2 DISCOVERY 1 +010203040506 03032CFE06162CFEA90358020BF3 DISCOVERY 1 +010203040506 03032CFE06162CFEA92498020BF4 DISCOVERY 1 +010203040506 03032CFE06162CFEA9394A020BF5 DISCOVERY 1 +010203040506 03032CFE06162CFEC6936A020BF6 DISCOVERY 1 +010203040506 03032CFE06162CFEC69AFD020BF7 DISCOVERY 1 +010203040506 03032CFE06162CFEC6ABEA020BF8 DISCOVERY 1 +010203040506 03032CFE06162CFEC6EC5F020BF9 DISCOVERY 1 +010203040506 03032CFE06162CFEC7736C020BFA DISCOVERY 1 +010203040506 03032CFE06162CFEC79B91020BFB DISCOVERY 1 +010203040506 03032CFE06162CFEC7A267020BFC DISCOVERY 1 +010203040506 03032CFE06162CFEC7D620020BFD DISCOVERY 1 +010203040506 03032CFE06162CFEC7FBCC020BFE DISCOVERY 1 +010203040506 03032CFE06162CFEC8162A020BFF DISCOVERY 1 +010203040506 03032CFE06162CFEC85D7A020C00 DISCOVERY 1 +010203040506 03032CFE06162CFEC8777E020C01 DISCOVERY 1 +010203040506 03032CFE06162CFEC878AA020C02 DISCOVERY 1 +010203040506 03032CFE06162CFEC8C641020C03 DISCOVERY 1 +010203040506 03032CFE06162CFEC8D335020C04 DISCOVERY 1 +010203040506 03032CFE06162CFEC8E228020C05 DISCOVERY 1 +010203040506 03032CFE06162CFEC9186B020C06 DISCOVERY 1 +010203040506 03032CFE06162CFEC9836A020C07 DISCOVERY 1 +010203040506 03032CFE06162CFECA7030020C08 DISCOVERY 1 +010203040506 03032CFE06162CFECAB6B8020C09 DISCOVERY 1 +010203040506 03032CFE06162CFECAF511020C0A DISCOVERY 1 +010203040506 03032CFE06162CFECB093B020C0B DISCOVERY 1 +010203040506 03032CFE06162CFECB529D020C0C DISCOVERY 1 +010203040506 03032CFE06162CFECC438E020C0D DISCOVERY 1 +010203040506 03032CFE06162CFECC5F29020C0E DISCOVERY 1 +010203040506 03032CFE06162CFECC754F020C0F DISCOVERY 1 +010203040506 03032CFE06162CFECC93A5020C10 DISCOVERY 1 +010203040506 03032CFE06162CFECCBB7E020C11 DISCOVERY 1 +010203040506 03032CFE06162CFECD8256020C12 DISCOVERY 1 +010203040506 03032CFE06162CFED446A7020C13 DISCOVERY 1 +010203040506 03032CFE06162CFED5A59E020C14 DISCOVERY 1 +010203040506 03032CFE06162CFED5B5F7020C15 DISCOVERY 1 +010203040506 03032CFE06162CFED5C6CE020C16 DISCOVERY 1 +010203040506 03032CFE06162CFED654CD020C17 DISCOVERY 1 +010203040506 03032CFE06162CFED65F4E020C18 DISCOVERY 1 +010203040506 03032CFE06162CFED69B2B020C19 DISCOVERY 1 +010203040506 03032CFE06162CFED6C195020C1A DISCOVERY 1 +010203040506 03032CFE06162CFED6E870020C1B DISCOVERY 1 +010203040506 03032CFE06162CFED6EE84020C1C DISCOVERY 1 +010203040506 03032CFE06162CFED7102F020C1D DISCOVERY 1 +010203040506 03032CFE06162CFED7E3EB020C1E DISCOVERY 1 +010203040506 03032CFE06162CFED8058C020C1F DISCOVERY 1 +010203040506 03032CFE06162CFED820EA020C20 DISCOVERY 1 +010203040506 03032CFE06162CFED87A3E020C21 DISCOVERY 1 +010203040506 03032CFE06162CFED8F3BA020C22 DISCOVERY 1 +010203040506 03032CFE06162CFED8F4E8020C23 DISCOVERY 1 +010203040506 03032CFE06162CFED90617020C24 DISCOVERY 1 +010203040506 03032CFE06162CFED933A7020C25 DISCOVERY 1 +010203040506 03032CFE06162CFED9414F020C26 DISCOVERY 1 +010203040506 03032CFE06162CFED97EBA020C27 DISCOVERY 1 +010203040506 03032CFE06162CFED9964B020C28 DISCOVERY 1 +010203040506 03032CFE06162CFEDA0F83020C29 DISCOVERY 1 +010203040506 03032CFE06162CFEDA4577020C2A DISCOVERY 1 +010203040506 03032CFE06162CFEDA5200020C2B DISCOVERY 1 +010203040506 03032CFE06162CFEDAD3A6020C2C DISCOVERY 1 +010203040506 03032CFE06162CFEDADE43020C2D DISCOVERY 1 +010203040506 03032CFE06162CFEDAE096020C2E DISCOVERY 1 +010203040506 03032CFE06162CFEDB8AC7020C2F DISCOVERY 1 +010203040506 03032CFE06162CFEDBE5B1020C30 DISCOVERY 1 +010203040506 03032CFE06162CFEDC5249020C31 DISCOVERY 1 +010203040506 03032CFE06162CFEDCF33C020C32 DISCOVERY 1 +010203040506 03032CFE06162CFEDD4EC0020C33 DISCOVERY 1 +010203040506 03032CFE06162CFEDE215D020C34 DISCOVERY 1 +010203040506 03032CFE06162CFEDE577F020C35 DISCOVERY 1 +010203040506 03032CFE06162CFEDEC04C020C36 DISCOVERY 1 +010203040506 03032CFE06162CFEDEDD6F020C37 DISCOVERY 1 +010203040506 03032CFE06162CFEDEE8C0020C38 DISCOVERY 1 +010203040506 03032CFE06162CFEDEEA86020C39 DISCOVERY 1 +010203040506 03032CFE06162CFEDEF234020C3A DISCOVERY 1 +010203040506 03032CFE06162CFEDF01E3020C3B DISCOVERY 1 +010203040506 03032CFE06162CFEDF271C020C3C DISCOVERY 1 +010203040506 03032CFE06162CFEDF42DE020C3D DISCOVERY 1 +010203040506 03032CFE06162CFEDF4B02020C3E DISCOVERY 1 +010203040506 03032CFE06162CFEDF9BA4020C3F DISCOVERY 1 +010203040506 03032CFE06162CFEDFD433020C40 DISCOVERY 1 +010203040506 03032CFE06162CFEE020C1020C41 DISCOVERY 1 +010203040506 03032CFE06162CFEE06116020C42 DISCOVERY 1 +010203040506 03032CFE06162CFEE07634020C43 DISCOVERY 1 +010203040506 03032CFE06162CFEE09172020C44 DISCOVERY 1 +010203040506 03032CFE06162CFEE4E457020C45 DISCOVERY 1 +010203040506 03032CFE06162CFEE5440B020C46 DISCOVERY 1 +010203040506 03032CFE06162CFEE57363020C47 DISCOVERY 1 +010203040506 03032CFE06162CFEE57B57020C48 DISCOVERY 1 +010203040506 03032CFE06162CFEE5B4B0020C49 DISCOVERY 1 +010203040506 03032CFE06162CFEE5B91B020C4A DISCOVERY 1 +010203040506 03032CFE06162CFEE5E2E9020C4B DISCOVERY 1 +010203040506 03032CFE06162CFEE64613020C4C DISCOVERY 1 +010203040506 03032CFE06162CFEE64CC6020C4D DISCOVERY 1 +010203040506 03032CFE06162CFEE69877020C4E DISCOVERY 1 +010203040506 03032CFE06162CFEE6E37E020C4F DISCOVERY 1 +010203040506 03032CFE06162CFEE6E771020C50 DISCOVERY 1 +010203040506 03032CFE06162CFEE6E8B8020C51 DISCOVERY 1 +010203040506 03032CFE06162CFEE750CE020C52 DISCOVERY 1 +010203040506 03032CFE06162CFEF00000020C53 DISCOVERY 1 +010203040506 03032CFE06162CFEF00001020C54 DISCOVERY 1 +010203040506 03032CFE06162CFEF00002020C55 DISCOVERY 1 +010203040506 03032CFE06162CFEF00200020C56 DISCOVERY 1 +010203040506 03032CFE06162CFEF00201020C57 DISCOVERY 1 +010203040506 03032CFE06162CFEF00202020C58 DISCOVERY 1 +010203040506 03032CFE06162CFEF00203020C59 DISCOVERY 1 +010203040506 03032CFE06162CFEF00204020C5A DISCOVERY 1 +010203040506 03032CFE06162CFEF00205020C5B DISCOVERY 1 +010203040506 03032CFE06162CFEF00206020C5C DISCOVERY 1 +010203040506 03032CFE06162CFEF00207020C5D DISCOVERY 1 +010203040506 03032CFE06162CFEF00208020C5E DISCOVERY 1 +010203040506 03032CFE06162CFEF00209020C5F DISCOVERY 1 +010203040506 03032CFE06162CFEF0020A020C60 DISCOVERY 1 +010203040506 03032CFE06162CFEF0020B020C61 DISCOVERY 1 +010203040506 03032CFE06162CFEF0020C020C62 DISCOVERY 1 +010203040506 03032CFE06162CFEF0020D020C63 DISCOVERY 1 +010203040506 03032CFE06162CFEF0020E020C64 DISCOVERY 1 +010203040506 03032CFE06162CFEF0020F020C65 DISCOVERY 1 +010203040506 03032CFE06162CFEF00210020C66 DISCOVERY 1 +010203040506 03032CFE06162CFEF00211020C67 DISCOVERY 1 +010203040506 03032CFE06162CFEF00212020C68 DISCOVERY 1 +010203040506 03032CFE06162CFEF00213020C69 DISCOVERY 1 +010203040506 03032CFE06162CFEF00214020C6A DISCOVERY 1 +010203040506 03032CFE06162CFEF00215020C6B DISCOVERY 1 +010203040506 03032CFE06162CFEF00300020C6C DISCOVERY 1 +010203040506 03032CFE06162CFEF00301020C6D DISCOVERY 1 +010203040506 03032CFE06162CFEF00302020C6E DISCOVERY 1 +010203040506 03032CFE06162CFEF00303020C6F DISCOVERY 1 +010203040506 03032CFE06162CFEF00304020C70 DISCOVERY 1 +010203040506 03032CFE06162CFEF00305020C71 DISCOVERY 1 +010203040506 03032CFE06162CFEF00306020C72 DISCOVERY 1 +010203040506 03032CFE06162CFEF00307020C73 DISCOVERY 1 +010203040506 03032CFE06162CFEF00308020C74 DISCOVERY 1 +010203040506 03032CFE06162CFEF00309020C75 DISCOVERY 1 +010203040506 03032CFE06162CFEF00400020C76 DISCOVERY 1 +010203040506 03032CFE06162CFEF00E97020C77 DISCOVERY 1 \ No newline at end of file diff --git a/sdcard/BLETX/BLE Tx Sample.txt b/sdcard/BLETX/BLE Tx Sample.txt index 405053521..3c0662f15 100644 --- a/sdcard/BLETX/BLE Tx Sample.txt +++ b/sdcard/BLETX/BLE Tx Sample.txt @@ -1 +1 @@ -010203040506 00112233445566778899AABBCCDDEEFF 1000 \ No newline at end of file +010203040506 00112233445566778899AABBCCDDEEFF DISCOVERY 1000 \ No newline at end of file diff --git a/sdcard/BLETX/Discovery Packet.txt b/sdcard/BLETX/Discovery Packet.txt index 4b6d80a3e..8fd99eff3 100644 --- a/sdcard/BLETX/Discovery Packet.txt +++ b/sdcard/BLETX/Discovery Packet.txt @@ -1 +1 @@ -010203040506 190953445220426c7565746f6f7468204c6f7720456e65726779 1000 \ No newline at end of file +010203040506 190953445220426c7565746f6f7468204c6f7720456e65726779 DISCOVERY 1000 \ No newline at end of file