diff --git a/firmware/application/apps/ble_rx_app.cpp b/firmware/application/apps/ble_rx_app.cpp index 2e06f41c7..136e0dd52 100644 --- a/firmware/application/apps/ble_rx_app.cpp +++ b/firmware/application/apps/ble_rx_app.cpp @@ -57,95 +57,6 @@ std::string pad_string_with_spaces(int snakes) { return paddedStr; } -struct GainEntry { - uint8_t lna; - uint8_t vga; - uint8_t gain; -}; - -// Only LNA with VGA 0-4 is tested to be accurate. Max zeroized gain tested to be 16dBm. -// Beyond that it is hard to tell distance to transmitting device. -// Test was conducted within a few inches of the device. -// Device was transmitting at 0dBm. -constexpr GainEntry gain_table[] = - { - {40, 0, 19}, - {32, 0, 18}, - {24, 0, 15}, - {16, 0, 8}, - {8, 0, 2}, - {0, 0, 0}, - {40, 2, 20}, - {32, 2, 22}, - {24, 2, 14}, - {16, 2, 8}, - {8, 2, 2}, - {0, 2, 0}, - {40, 4, 21}, - {32, 4, 22}, - {24, 4, 15}, - {16, 4, 10}, - {8, 4, 3}, - {0, 4, 0}, - {40, 6, 26}, - {32, 6, 22}, - {24, 6, 15}, - {16, 6, 10}, - {8, 6, 4}, - {0, 6, 0}, - {40, 8, 26}, - {32, 8, 26}, - {24, 8, 18}, - {16, 8, 12}, - {8, 8, 6}, - {0, 8, 1}, - {40, 10, 26}, - {32, 10, 26}, - {24, 10, 20}, - {16, 10, 15}, - {8, 10, 8}, - {0, 10, 3}, - {40, 12, 26}, - {32, 12, 26}, - {24, 12, 23}, - {16, 12, 17}, - {8, 12, 10}, - {0, 12, 4}, - {40, 14, 26}, - {32, 14, 26}, - {24, 14, 25}, - {16, 14, 19}, - {8, 14, 12}, - {0, 14, 6}, - {40, 16, 26}, - {32, 16, 26}, - {24, 16, 26}, - {16, 16, 20}, - {8, 16, 13}, - {0, 16, 7}, - {40, 18, 26}, - {32, 18, 26}, - {24, 18, 26}, - {16, 18, 21}, - {8, 18, 14}, - {0, 18, 8}, - {40, 20, 26}, - {32, 20, 26}, - {24, 20, 26}, - {16, 20, 23}, - {8, 20, 16}, - {0, 20, 10}, -}; - -uint8_t get_total_gain(uint8_t lna, uint8_t vga) { - for (const auto& entry : gain_table) { - if (entry.lna == lna && entry.vga == vga) - return entry.gain; - } - - return 0; -} - uint64_t copy_mac_address_to_uint64(const uint8_t* macAddress) { uint64_t result = 0; @@ -275,10 +186,10 @@ void RecentEntriesTable::draw( if (!entry.nameString.empty() && entry.include_name) { line = entry.nameString; - if (line.length() < 10) { - line += pad_string_with_spaces(10 - line.length()); + if (line.length() < 17) { + line += pad_string_with_spaces(17 - line.length()); } else { - line = truncate(line, 10); + line = truncate(line, 17); } } else { line = to_string_mac_address(entry.packetData.macAddress, 6, false); @@ -289,12 +200,12 @@ void RecentEntriesTable::draw( if (!entry.informationString.empty()) { hitsStr = entry.informationString; } else { - hitsStr = "Hits: " + to_string_dec_int(entry.numHits); + hitsStr = to_string_dec_int(entry.numHits); } // Pushing single digit values down right justified. int hitsDigits = hitsStr.length(); - uint8_t hits_spacing = 14 - hitsDigits; + uint8_t hits_spacing = 8 - hitsDigits; // Pushing single digit values down right justified. std::string dbStr = to_string_dec_int(entry.dbValue); @@ -879,17 +790,29 @@ void BLERxView::on_data(BlePacketData* packet) { // Start of Packet stuffing. // Masking off the top 2 bytes to avoid invalid keys. - BleRecentEntry tempEntry; + uint64_t key = macAddressEncoded & 0xFFFFFFFFFFFF; + bool packetExists = false; - if (updateEntry(packet, tempEntry, (ADV_PDU_TYPE)packet->type)) { - auto& entry = ::on_packet(recent, macAddressEncoded & 0xFFFFFFFFFFFF); + // If found store into tempEntry to modify. + auto it = find(recent, key); + if (it != recent.end()) { + recent.push_front(*it); + recent.erase(it); + updateEntry(packet, recent.front(), (ADV_PDU_TYPE)packet->type); + packetExists = true; + } else { + recent.emplace_front(key); + truncate_entries(recent); - // Preserve exisisting data from entry. - tempEntry.macAddress = macAddressEncoded; - tempEntry.numHits = ++entry.numHits; + packetExists = updateEntry(packet, recent.front(), (ADV_PDU_TYPE)packet->type); - entry = tempEntry; + // If parsing failed, remove entry. + if (!packetExists) { + recent.erase(recent.begin()); + } + } + if (packetExists) { handle_filter_options(options_filter.selected_index()); handle_entries_sort(options_sort.selected_index()); @@ -899,7 +822,7 @@ void BLERxView::on_data(BlePacketData* packet) { while (it != searchList.end()) { std::string searchStr = (std::string)*it; - if (entry.dataString.find(searchStr) != std::string::npos) { + if (recent.front().dataString.find(searchStr) != std::string::npos) { searchList.erase(it); found_count++; break; @@ -1000,13 +923,10 @@ void BLERxView::on_timer() { timer_count = 0; if (auto_channel) { - int min = 37; - int max = 39; + field_frequency.set_value(get_freq_by_channel_number(channel_number)); + baseband::set_btlerx(channel_number); - int randomChannel = min + std::rand() % (max - min + 1); - - field_frequency.set_value(get_freq_by_channel_number(randomChannel)); - baseband::set_btlerx(randomChannel); + channel_number = (channel_number < 39) ? channel_number + 1 : 37; } } if (ble_rx_error != BLE_RX_NO_ERROR) { @@ -1093,7 +1013,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry, data_string += to_string_hex(packet->data[i], 2); } - entry.dbValue = 2 * (packet->max_dB - get_total_gain(receiver_model.lna(), receiver_model.vga())); + 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; @@ -1111,6 +1031,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry, entry.pduType = pdu_type; entry.channelNumber = channel_number; + entry.numHits++; if (entry.vendor_status == MAC_VENDOR_UNKNOWN) { std::string vendor_name; @@ -1125,8 +1046,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry, 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 (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. } @@ -1145,6 +1065,7 @@ bool BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry, 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 = ""; for (currentByte = 0; currentByte < length;) { currentLength = data[currentByte++]; @@ -1154,18 +1075,25 @@ bool BLERxView::parse_beacon_data(const uint8_t* data, uint8_t length, std::stri for (int i = 0; ((i < currentLength - 1) && (currentByte < length)); i++) { // parse the name of bluetooth device: 0x08->Shortened Local Name; 0x09->Complete Local Name if (currentType == 0x08 || currentType == 0x09) { - nameString += (char)data[currentByte]; + tempName += (char)data[currentByte]; } currentByte++; } - } - if (nameString.empty()) { - nameString = "None"; + if (!tempName.empty()) { + nameString = tempName; + break; + } } informationString = ""; + if (!informationString.empty()) { + // Option to change title of Hits Column. + // Setting to default for now. + columns.set(1, "Hits", 7); + } + return true; } diff --git a/firmware/application/apps/ble_rx_app.hpp b/firmware/application/apps/ble_rx_app.hpp index 34b8565ec..a1654e950 100644 --- a/firmware/application/apps/ble_rx_app.hpp +++ b/firmware/application/apps/ble_rx_app.hpp @@ -267,7 +267,7 @@ class BLERxView : public View { bool auto_channel = false; int16_t timer_count{0}; - int16_t timer_period{6}; // 100ms + int16_t timer_period{2}; // 25ms std::string filterBuffer{}; std::string listFileBuffer{}; @@ -386,9 +386,9 @@ class BLERxView : public View { BleRecentEntries recent{}; BleRecentEntries tempList{}; - const RecentEntriesColumns columns{{ - {"Name", 10}, - {"Information", 13}, + RecentEntriesColumns columns{{ + {"Name", 17}, + {"Hits", 7}, {"dBm", 4}, }}; diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index 22cd562a8..2655f37f9 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -186,13 +186,14 @@ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t send_message(&message); } -void set_fsk(const size_t deviation) { +void set_fsk(const uint8_t samplesPerSymbol, const uint32_t syncWord, const uint8_t syncWordLength, const uint32_t preamble, const uint8_t preambleLength, uint16_t numDataBytes) { const FSKRxConfigureMessage message{ - taps_200k_decim_0, - taps_16k0_decim_1, - taps_11k0_channel, - 2, - deviation}; + samplesPerSymbol, + syncWord, + syncWordLength, + preamble, + preambleLength, + numDataBytes}; send_message(&message); } diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index 15c56eac2..84d5f292a 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -80,7 +80,7 @@ void set_pitch_rssi(int32_t avg, bool enabled); void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count); void kill_afsk(); void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); -void set_fsk(const size_t deviation); +void set_fsk(const uint32_t samplesPerSymbol, const uint32_t syncWord, const uint8_t syncWordLength, const uint32_t preamble, const uint8_t preambleLength, uint16_t numDataBytes); void set_aprs(const uint32_t baudrate); void set_btlerx(uint8_t channel_number); diff --git a/firmware/application/recent_entries.cpp b/firmware/application/recent_entries.cpp index 2cbb7d9bf..8f6abb95b 100644 --- a/firmware/application/recent_entries.cpp +++ b/firmware/application/recent_entries.cpp @@ -24,7 +24,7 @@ namespace ui { RecentEntriesColumns::RecentEntriesColumns( - const std::initializer_list columns) + std::initializer_list columns) : _columns{columns} { } diff --git a/firmware/application/recent_entries.hpp b/firmware/application/recent_entries.hpp index 83e6189bf..b5a33fec1 100644 --- a/firmware/application/recent_entries.hpp +++ b/firmware/application/recent_entries.hpp @@ -133,14 +133,27 @@ class RecentEntriesColumns { public: using ContainerType = std::vector; - RecentEntriesColumns( - const std::initializer_list columns); + RecentEntriesColumns(std::initializer_list columns); - ContainerType::const_iterator begin() const { return std::begin(_columns); } - ContainerType::const_iterator end() const { return std::end(_columns); } + ContainerType::iterator begin() { return _columns.begin(); } + ContainerType::iterator end() { return _columns.end(); } + ContainerType::const_iterator begin() const { return _columns.begin(); } + ContainerType::const_iterator end() const { return _columns.end(); } + + void set(size_t index, const std::string& name, size_t width) { + if (index < _columns.size()) { + _columns[index] = {name, width}; + } + } + + const RecentEntriesColumn& at(size_t index) const { + return _columns.at(index); + } + + size_t size() const { return _columns.size(); } private: - const ContainerType _columns; + ContainerType _columns; }; class RecentEntriesHeader : public Widget { diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 28d094dc4..d995932b6 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -397,13 +397,6 @@ set(MODE_CPPSRC ) DeclareTargets(PSON sonde) -### FSK TX - -set(MODE_CPPSRC - proc_fsk.cpp -) -DeclareTargets(PFSK fsktx) - ### FSK RX set(MODE_CPPSRC @@ -411,6 +404,12 @@ set(MODE_CPPSRC ) DeclareTargets(PFSR fskrx) +### FSK TX + +set(MODE_CPPSRC + proc_fsk.cpp +) +DeclareTargets(PFSK fsktx) ### Microphone transmit diff --git a/firmware/baseband/proc_btlerx.cpp b/firmware/baseband/proc_btlerx.cpp index 24c3f646f..e8ff7eafa 100644 --- a/firmware/baseband/proc_btlerx.cpp +++ b/firmware/baseband/proc_btlerx.cpp @@ -27,7 +27,7 @@ #include "event_m4.hpp" -float BTLERxProcessor::get_phase_diff(const complex16_t& sample0, const complex16_t& sample1) { +inline float BTLERxProcessor::get_phase_diff(const complex16_t& sample0, const complex16_t& sample1) { // Calculate the phase difference between two samples. float dI = sample1.real() * sample0.real() + sample1.imag() * sample0.imag(); float dQ = sample1.imag() * sample0.real() - sample1.real() * sample0.imag(); @@ -36,7 +36,7 @@ float BTLERxProcessor::get_phase_diff(const complex16_t& sample0, const complex1 return phase_diff; } -uint32_t BTLERxProcessor::crc_init_reorder(uint32_t crc_init) { +inline uint32_t BTLERxProcessor::crc_init_reorder(uint32_t crc_init) { int i; uint32_t crc_init_tmp, crc_init_input, crc_init_input_tmp; @@ -62,7 +62,7 @@ uint32_t BTLERxProcessor::crc_init_reorder(uint32_t crc_init) { return (crc_init_tmp); } -uint_fast32_t BTLERxProcessor::crc_update(uint_fast32_t crc, const void* data, size_t data_len) { +inline uint_fast32_t BTLERxProcessor::crc_update(uint_fast32_t crc, const void* data, size_t data_len) { const unsigned char* d = (const unsigned char*)data; unsigned int tbl_idx; @@ -76,7 +76,7 @@ uint_fast32_t BTLERxProcessor::crc_update(uint_fast32_t crc, const void* data, s return crc & 0xffffff; } -uint_fast32_t BTLERxProcessor::crc24_byte(uint8_t* byte_in, int num_byte, uint32_t init_hex) { +inline uint_fast32_t BTLERxProcessor::crc24_byte(uint8_t* byte_in, int num_byte, uint32_t init_hex) { uint_fast32_t crc = init_hex; crc = crc_update(crc, byte_in, num_byte); @@ -84,7 +84,7 @@ uint_fast32_t BTLERxProcessor::crc24_byte(uint8_t* byte_in, int num_byte, uint32 return (crc); } -bool BTLERxProcessor::crc_check(uint8_t* tmp_byte, int body_len, uint32_t crc_init) { +inline bool BTLERxProcessor::crc_check(uint8_t* tmp_byte, int body_len, uint32_t crc_init) { int crc24_checksum; crc24_checksum = crc24_byte(tmp_byte, body_len, crc_init); // 0x555555 --> 0xaaaaaa. maybe because byte order @@ -96,7 +96,7 @@ bool BTLERxProcessor::crc_check(uint8_t* tmp_byte, int body_len, uint32_t crc_in return (crc24_checksum != checksumReceived); } -void BTLERxProcessor::scramble_byte(uint8_t* byte_in, int num_byte, const uint8_t* scramble_table_byte, uint8_t* byte_out) { +inline void BTLERxProcessor::scramble_byte(uint8_t* byte_in, int num_byte, const uint8_t* scramble_table_byte, uint8_t* byte_out) { int i; for (i = 0; i < num_byte; i++) { @@ -104,7 +104,7 @@ void BTLERxProcessor::scramble_byte(uint8_t* byte_in, int num_byte, const uint8_ } } -int BTLERxProcessor::verify_payload_byte(int num_payload_byte, ADV_PDU_TYPE pdu_type) { +inline int BTLERxProcessor::verify_payload_byte(int num_payload_byte, ADV_PDU_TYPE pdu_type) { // Should at least have 6 bytes for the MAC Address. // Also ensuring that there is at least 1 byte of data. if (num_payload_byte <= 6) { @@ -131,26 +131,27 @@ int BTLERxProcessor::verify_payload_byte(int num_payload_byte, ADV_PDU_TYPE pdu_ return 0; } -void BTLERxProcessor::resetOffsetTracking() { +inline void BTLERxProcessor::resetOffsetTracking() { frequency_offset = 0.0f; frequency_offset_estimate = 0.0f; phase_buffer_index = 0; memset(phase_buffer, 0, sizeof(phase_buffer)); } -void BTLERxProcessor::resetBitPacketIndex() { +inline void BTLERxProcessor::resetBitPacketIndex() { memset(rb_buf, 0, sizeof(rb_buf)); packet_index = 0; bit_index = 0; } -void BTLERxProcessor::resetToDefaultState() { +inline void BTLERxProcessor::resetToDefaultState() { parseState = Parse_State_Begin; resetOffsetTracking(); resetBitPacketIndex(); + crc_init_internal = crc_init_reorder(crc_initalVale); } -void BTLERxProcessor::demodulateFSKBits(int num_demod_byte) { +inline void BTLERxProcessor::demodulateFSKBits(int num_demod_byte) { for (; packet_index < num_demod_byte; packet_index++) { for (; bit_index < 8; bit_index++) { if (samples_eaten >= (int)dst_buffer.count) { @@ -168,6 +169,15 @@ void BTLERxProcessor::demodulateFSKBits(int num_demod_byte) { // phaseSum /= (SAMPLE_PER_SYMBOL); // phaseSum -= frequency_offset; + /* + alternate method. faster, but less precise. with this, you need to check against this: if (samples_eaten >= (int)dst_buffer.count + SAMPLE_PER_SYMBOL) (not so good...) + int I0 = dst_buffer.p[samples_eaten].real(); + int Q0 = dst_buffer.p[samples_eaten].imag(); + int I1 = dst_buffer.p[samples_eaten + 1 * SAMPLE_PER_SYMBOL].real(); + int Q1 = dst_buffer.p[samples_eaten + 1 * SAMPLE_PER_SYMBOL].imag(); + bool bitDecision = (I0 * Q1 - I1 * Q0) > 0 ? 1 : 0; + */ + bool bitDecision = (phaseSum > 0.0f); rb_buf[packet_index] = rb_buf[packet_index] | (bitDecision << bit_index); @@ -178,9 +188,9 @@ void BTLERxProcessor::demodulateFSKBits(int num_demod_byte) { } } -void BTLERxProcessor::handleBeginState() { +inline void BTLERxProcessor::handleBeginState() { uint32_t validAccessAddress = DEFAULT_ACCESS_ADDR; - uint32_t accesssAddress = 0; + static uint32_t accesssAddress = 0; int hit_idx = (-1); @@ -191,8 +201,10 @@ void BTLERxProcessor::handleBeginState() { phaseDiff += get_phase_diff(dst_buffer.p[i + j], dst_buffer.p[i + j + 1]); } - phase_buffer[phase_buffer_index] = phaseDiff / (SAMPLE_PER_SYMBOL); + // disabled, due to not used anywhere + /* phase_buffer[phase_buffer_index] = phaseDiff / (SAMPLE_PER_SYMBOL); phase_buffer_index = (phase_buffer_index + 1) % ROLLING_WINDOW; + */ bool bitDecision = (phaseDiff > 0); @@ -200,14 +212,15 @@ void BTLERxProcessor::handleBeginState() { int errors = __builtin_popcount(accesssAddress ^ validAccessAddress) & 0xFFFFFFFF; - if (errors <= 4) { + if (!errors) { hit_idx = i + SAMPLE_PER_SYMBOL; - for (int k = 0; k < ROLLING_WINDOW; k++) { + // disabled, due to not used anywhere + /* for (int k = 0; k < ROLLING_WINDOW; k++) { frequency_offset_estimate += phase_buffer[k]; } - frequency_offset = frequency_offset_estimate / ROLLING_WINDOW; + */ break; } @@ -215,7 +228,7 @@ void BTLERxProcessor::handleBeginState() { if (hit_idx == -1) { // Process more samples. - samples_eaten = dst_buffer.count + 1; + samples_eaten = (int)dst_buffer.count + 1; return; } @@ -224,7 +237,7 @@ void BTLERxProcessor::handleBeginState() { parseState = Parse_State_PDU_Header; } -void BTLERxProcessor::handlePDUHeaderState() { +inline void BTLERxProcessor::handlePDUHeaderState() { if (samples_eaten > (int)dst_buffer.count) { return; } @@ -232,6 +245,7 @@ void BTLERxProcessor::handlePDUHeaderState() { demodulateFSKBits(NUM_PDU_HEADER_BYTE); if (packet_index < NUM_PDU_HEADER_BYTE || bit_index != 0) { + resetToDefaultState(); return; } @@ -244,14 +258,14 @@ void BTLERxProcessor::handlePDUHeaderState() { // Not a valid Advertise Payload. if ((payload_len < 6) || (payload_len > 37)) { - parseState = Parse_State_Begin; + resetToDefaultState(); return; } else { parseState = Parse_State_PDU_Payload; } } -void BTLERxProcessor::handlePDUPayloadState() { +inline void BTLERxProcessor::handlePDUPayloadState() { const int num_demod_byte = (payload_len + 3); if (samples_eaten > (int)dst_buffer.count) { @@ -261,6 +275,7 @@ void BTLERxProcessor::handlePDUPayloadState() { demodulateFSKBits(num_demod_byte + NUM_PDU_HEADER_BYTE); if (packet_index < (num_demod_byte + NUM_PDU_HEADER_BYTE) || bit_index != 0) { + resetToDefaultState(); return; } @@ -316,26 +331,24 @@ void BTLERxProcessor::handlePDUPayloadState() { void BTLERxProcessor::execute(const buffer_c8_t& buffer) { if (!configured) return; + // a less computationally expensive method max_dB = -128; - - real = -128; - imag = -128; - - auto* ptr = buffer.p; - auto* end = &buffer.p[buffer.count]; - - while (ptr < end) { - float dbm = mag2_to_dbm_8bit_normalized(ptr->real(), ptr->imag(), 1.0f, 50.0f); - - if (dbm > max_dB) { - max_dB = dbm; - real = ptr->real(); - imag = ptr->imag(); + uint32_t max_squared = 0; + int8_t imag = 0; + int8_t real = 0; + void* src_p = buffer.p; + while (src_p < &buffer.p[buffer.count]) { + const uint32_t sample = *__SIMD32(src_p)++; + const uint32_t mag_sq = __SMUAD(sample, sample); + if (mag_sq > max_squared) { + max_squared = mag_sq; + imag = ((complex8_t*)src_p)->imag(); + real = ((complex8_t*)src_p)->real(); } - - ptr++; } + max_dB = mag2_to_dbm_8bit_normalized(real, imag, 1.0f, 50.0f); + // 4Mhz 2048 samples // Decimated by 4 to achieve 2048/4 = 512 samples at 1 sample per symbol. decim_0.execute(buffer, dst_buffer); @@ -366,11 +379,9 @@ void BTLERxProcessor::on_message(const Message* const message) { void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) { channel_number = message.channel_number; - decim_0.configure(taps_BTLE_2M_PHY_decim_0.taps); + decim_0.configure(taps_BTLE_Dual_PHY.taps); configured = true; - - crc_init_internal = crc_init_reorder(crc_initalVale); } int main() { diff --git a/firmware/baseband/proc_btlerx.hpp b/firmware/baseband/proc_btlerx.hpp index 10e8a878a..3f0aa7f4e 100644 --- a/firmware/baseband/proc_btlerx.hpp +++ b/firmware/baseband/proc_btlerx.hpp @@ -133,8 +133,6 @@ class BTLERxProcessor : public BasebandProcessor { uint8_t payload_len{0}; uint8_t pdu_type{0}; int32_t max_dB{0}; - int8_t real{0}; - int8_t imag{0}; uint16_t packet_index{0}; uint8_t bit_index{0}; diff --git a/firmware/baseband/proc_fsk_rx.cpp b/firmware/baseband/proc_fsk_rx.cpp index 0bd1b0b81..930df3bf7 100644 --- a/firmware/baseband/proc_fsk_rx.cpp +++ b/firmware/baseband/proc_fsk_rx.cpp @@ -24,6 +24,7 @@ */ #include "proc_fsk_rx.hpp" +#include "dsp_decimate.hpp" #include "event_m4.hpp" @@ -32,135 +33,253 @@ #include #include -using namespace std; -using namespace dsp::decimate; +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif -namespace { -/* Count of bits that differ between the two values. */ -uint8_t diff_bit_count(uint32_t left, uint32_t right) { - uint32_t diff = left ^ right; - uint8_t count = 0; - for (size_t i = 0; i < sizeof(diff) * 8; ++i) { - if (((diff >> i) & 0x1) == 1) - ++count; +float FSKRxProcessor::detect_peak_power(const buffer_c8_t& buffer, int N) { + int32_t power = 0; + + // Initial window power + for (int i = 0; i < N; i++) { + int16_t i_sample = buffer.p[i].real(); + int16_t q_sample = buffer.p[i].imag(); + power += i_sample * i_sample + q_sample * q_sample; } - return count; -} -} // namespace + power = power / N; -/* AudioNormalizer ***************************************/ + // Convert to dB over noise floor + float power_db = 10.0f * log10f((float)power / noise_floor); -void AudioNormalizer::execute_in_place(const buffer_f32_t& audio) { - // Decay min/max every second (@24kHz). - if (counter_ >= 24'000) { - // 90% decay factor seems to work well. - // This keeps large transients from wrecking the filter. - max_ *= 0.9f; - min_ *= 0.9f; - counter_ = 0; - calculate_thresholds(); - } + // If too weak, treat as no signal + if (power_db <= 0.0f) return 0; - counter_ += audio.count; - - for (size_t i = 0; i < audio.count; ++i) { - auto& val = audio.p[i]; - - if (val > max_) { - max_ = val; - calculate_thresholds(); - } - if (val < min_) { - min_ = val; - calculate_thresholds(); - } - - if (val >= t_hi_) - val = 1.0f; - else if (val <= t_lo_) - val = -1.0f; - else - val = 0.0; - } + return power_db; } -void AudioNormalizer::calculate_thresholds() { - auto center = (max_ + min_) / 2.0f; - auto range = (max_ - min_) / 2.0f; +void FSKRxProcessor::agc_correct_iq(const buffer_c8_t& buffer, int N, float measured_power) { + float power_db = 10.0f * log10f(measured_power / noise_floor); + float error_db = target_power_db - power_db; - // 10% off center force either +/-1.0f. - // Higher == larger dead zone. - // Lower == more false positives. - auto threshold = range * 0.1; - t_hi_ = center + threshold; - t_lo_ = center - threshold; -} - -/* FSKRxProcessor ******************************************/ - -void FSKRxProcessor::clear_data_bits() { - data = 0; - bit_count = 0; -} - -void FSKRxProcessor::handle_sync(bool inverted) { - clear_data_bits(); - has_sync_ = true; - inverted = inverted; - word_count = 0; -} - -void FSKRxProcessor::process_bits(const buffer_c8_t& buffer) { - // Process all of the bits in the bits queue. - while (buffer.count > 0) { - // Wait until data_ is full. - if (bit_count < data_bit_count) - continue; - - // Wait for the sync frame. - if (!has_sync_) { - if (diff_bit_count(data, sync_codeword) <= 2) - handle_sync(/*inverted=*/false); - else if (diff_bit_count(data, ~sync_codeword) <= 2) - handle_sync(/*inverted=*/true); - continue; - } - } -} - -/* FSKRxProcessor ***************************************/ - -FSKRxProcessor::FSKRxProcessor() { -} - -void FSKRxProcessor::execute(const buffer_c8_t& buffer) { - if (!configured) { + if (error_db <= 0) { return; } - // Decimate by current decim 0 and decim 1. + float gain_scalar = powf(10.0f, error_db / 20.0f); + + for (int i = 0; i < N; i++) { + buffer.p[i] = {(int8_t)(buffer.p[i].real() * gain_scalar), (int8_t)(buffer.p[i].imag() * gain_scalar)}; + } +} + +float FSKRxProcessor::get_phase_diff(const complex16_t& sample0, const complex16_t& sample1) { + // Calculate the phase difference between two samples. + float dI = sample1.real() * sample0.real() + sample1.imag() * sample0.imag(); + float dQ = sample1.imag() * sample0.real() - sample1.real() * sample0.imag(); + float phase_diff = atan2f(dQ, dI); + + return phase_diff; +} + +void FSKRxProcessor::demodulateFSKBits(const buffer_c16_t& decimator_out, int num_demod_byte) { + for (; packet_index < num_demod_byte; packet_index++) { + for (; bit_index < 8; bit_index++) { + if (samples_eaten >= (int)decimator_out.count) { + return; + } + + float phaseSum = 0.0f; + for (int k = 0; k < SAMPLE_PER_SYMBOL - 1; ++k) { + float phase = get_phase_diff( + decimator_out.p[samples_eaten + k], + decimator_out.p[samples_eaten + k + 1]); + phaseSum += phase; + } + + phaseSum /= (SAMPLE_PER_SYMBOL - 1); + phaseSum -= frequency_offset; + + bool bitDecision = (phaseSum > 0.0f); + + rb_buf[packet_index] |= (bitDecision << (7 - bit_index)); + + samples_eaten += SAMPLE_PER_SYMBOL; + } + + bit_index = 0; + } +} + +void FSKRxProcessor::resetPreambleTracking() { + frequency_offset = 0.0f; + frequency_offset_estimate = 0.0f; + phase_buffer_index = 0; + memset(phase_buffer, 0, sizeof(phase_buffer)); +} + +void FSKRxProcessor::resetBitPacketIndex() { + packet_index = 0; + bit_index = 0; +} + +void FSKRxProcessor::resetToDefaultState() { + parseState = Parse_State_Wait_For_Peak; + peak_timeout = 0; + fskPacketData.power = 0.0f; + resetPreambleTracking(); + resetBitPacketIndex(); +} + +void FSKRxProcessor::handlePreambleState(const buffer_c16_t& decimator_out) { + const uint32_t validPreamble = DEFAULT_PREAMBLE; + static uint32_t preambleValue = 0; + + int hit_idx = -1; + + for (; samples_eaten < (int)decimator_out.count; samples_eaten += SAMPLE_PER_SYMBOL) { + float phaseSum = 0.0f; + + for (int j = 0; j < SAMPLE_PER_SYMBOL - 1; j++) { + phaseSum += get_phase_diff(decimator_out.p[samples_eaten + j], decimator_out.p[samples_eaten + j + 1]); + } + + phase_buffer[phase_buffer_index] = phaseSum / (SAMPLE_PER_SYMBOL - 1); + phase_buffer_index = (phase_buffer_index + 1) % ROLLING_WINDOW; + + bool bitDecision = (phaseSum > 0.0f); + preambleValue = (preambleValue << 1) | bitDecision; + + int errors = __builtin_popcountl(preambleValue ^ validPreamble) & 0xFFFFFFFF; + + if (errors == 0) { + hit_idx = samples_eaten + SAMPLE_PER_SYMBOL; + fskPacketData.syncWord = preambleValue; + fskPacketData.max_dB = max_dB; + + for (int k = 0; k < ROLLING_WINDOW; k++) { + frequency_offset_estimate += phase_buffer[k]; + } + + frequency_offset = frequency_offset_estimate / ROLLING_WINDOW; + + fskPacketData.frequency_offset_hz = (frequency_offset * demod_input_fs) / (2.0f * M_PI); + + preambleValue = 0; + break; + } + } + + if (hit_idx == -1) { + samples_eaten = samples_eaten; + return; + } + + samples_eaten = hit_idx; + parseState = Parse_State_Sync; +} + +void FSKRxProcessor::handleSyncWordState(const buffer_c16_t& decimator_out) { + const int syncword_bytes = 4; + const uint32_t validSyncWord = DEFAULT_SYNC_WORD; + + if ((int)decimator_out.count - samples_eaten <= 0) { + return; + } + + demodulateFSKBits(decimator_out, syncword_bytes); + + if (packet_index < syncword_bytes || bit_index != 0) { + return; + } + + uint32_t receivedSyncWord = (rb_buf[0] << 24) | (rb_buf[1] << 16) | (rb_buf[2] << 8) | rb_buf[3]; + + int errors = __builtin_popcountl(receivedSyncWord ^ validSyncWord) & 0xFFFFFFFF; + + if (errors <= 3) { + fskPacketData.syncWord = receivedSyncWord; + parseState = Parse_State_PDU_Payload; + memset(fskPacketData.data, 0, sizeof(fskPacketData.data)); + } else { + resetToDefaultState(); + } + + memset(rb_buf, 0, sizeof(rb_buf)); + resetBitPacketIndex(); +} + +void FSKRxProcessor::handlePDUPayloadState(const buffer_c16_t& decimator_out) { + if ((int)decimator_out.count - samples_eaten <= 0) { + return; + } + + demodulateFSKBits(decimator_out, NUM_DATA_BYTE); + + if (packet_index < NUM_DATA_BYTE || bit_index != 0) { + return; + } + + fskPacketData.dataLen = NUM_DATA_BYTE; + + // Copy the decoded bits to the packet data + for (int i = 0; i < NUM_DATA_BYTE; i++) { + fskPacketData.data[i] |= rb_buf[i]; + } + + FSKRxPacketMessage data_message{&fskPacketData}; + shared_memory.application_queue.push(data_message); + + memset(rb_buf, 0, sizeof(rb_buf)); + + resetToDefaultState(); +} + +void FSKRxProcessor::execute(const buffer_c8_t& buffer) { + if (!configured || parseState == Parse_State_Parsing_Data) return; + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); feed_channel_stats(decim_1_out); - spectrum_samples += decim_1_out.count; + samples_eaten = 0; - if (spectrum_samples >= spectrum_interval_samples) { - spectrum_samples -= spectrum_interval_samples; - channel_spectrum.feed(decim_1_out, channel_filter_low_f, - channel_filter_high_f, channel_filter_transition); - } + while ((int)decim_1_out.count - samples_eaten > 0) { + if ((parseState == Parse_State_Wait_For_Peak) || (parseState == Parse_State_Preamble)) { + float power = detect_peak_power(buffer, buffer.count); - // process_bits(); + if (power) { + parseState = Parse_State_Preamble; + agc_power = power; + fskPacketData.power = power; + } else { + break; + } + } - // Update the status. - samples_processed += buffer.count; + if (agc_power) { + agc_correct_iq(buffer, buffer.count, agc_power); + } - if (samples_processed >= stat_update_threshold) { - // send_packet(data); - samples_processed -= stat_update_threshold; + if (parseState == Parse_State_Preamble) { + peak_timeout++; + + // 960,000 fs / 2048 samples = 468.75 Hz, so 55 calls is about 0.053 seconds before timeout. + if (peak_timeout == 4) { + resetToDefaultState(); + } else { + handlePreambleState(decim_1_out); + } + } + + if (parseState == Parse_State_Sync) { + handleSyncWordState(decim_1_out); + } + + if (parseState == Parse_State_PDU_Payload) { + handlePDUPayloadState(decim_1_out); + } } } @@ -171,7 +290,7 @@ void FSKRxProcessor::on_message(const Message* const message) { break; case Message::ID::UpdateSpectrum: case Message::ID::SpectrumStreamingConfig: - channel_spectrum.on_message(message); + // channel_spectrum.on_message(message); break; case Message::ID::SampleRateConfig: @@ -179,7 +298,7 @@ void FSKRxProcessor::on_message(const Message* const message) { break; case Message::ID::CaptureConfig: - capture_config(*reinterpret_cast(message)); + // capture_config(*reinterpret_cast(message)); break; default: @@ -188,83 +307,65 @@ void FSKRxProcessor::on_message(const Message* const message) { } void FSKRxProcessor::configure(const FSKRxConfigureMessage& message) { - // Extract message variables. - deviation = message.deviation; - channel_decimation = message.channel_decimation; - // channel_filter_taps = message.channel_filter; - - channel_spectrum.set_decimation_factor(1); -} - -void FSKRxProcessor::capture_config(const CaptureConfigMessage& message) { - if (message.config) { - audio_output.set_stream(std::make_unique(message.config)); - } else { - audio_output.set_stream(nullptr); - } + SAMPLE_PER_SYMBOL = message.samplesPerSymbol; + DEFAULT_SYNC_WORD = message.syncWord; + NUM_SYNC_WORD_BYTE = message.syncWordLength; + DEFAULT_PREAMBLE = message.preamble; + NUM_PREAMBLE_BYTE = message.preambleLength; + NUM_DATA_BYTE = message.numDataBytes; } void FSKRxProcessor::sample_rate_config(const SampleRateConfigMessage& message) { const auto sample_rate = message.sample_rate; - // The actual sample rate is the requested rate * the oversample rate. - // See oversample.hpp for more details on oversampling. baseband_fs = sample_rate * toUType(message.oversample_rate); baseband_thread.set_sampling_rate(baseband_fs); - // TODO: Do we need to use the taps that the decimators get configured with? channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * sample_rate; channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * sample_rate; channel_filter_transition = taps_200k_decim_1.transition_normalized * sample_rate; - // Compute the scalar that corrects the oversample_rate to be x8 when computing - // the spectrum update interval. The original implementation only supported x8. - // TODO: Why is this needed here but not in proc_replay? There must be some other - // assumption about x8 oversampling in some component that makes this necessary. const auto oversample_correction = toUType(message.oversample_rate) / 8.0; // The spectrum update interval controls how often the waterfall is fed new samples. spectrum_interval_samples = sample_rate / (spectrum_rate_hz * oversample_correction); spectrum_samples = 0; - // For high sample rates, the M4 is busy collecting samples so the - // waterfall runs slower. Reduce the update interval so it runs faster. - // NB: Trade off: looks nicer, but more frequent updates == more CPU. if (sample_rate >= 1'500'000) spectrum_interval_samples /= (sample_rate / 750'000); switch (message.oversample_rate) { case OversampleRate::x4: // M4 can't handle 2 decimation passes for sample rates needing x4. - decim_0.set().configure(taps_200k_decim_0.taps); + decim_0.set().configure(taps_200k_decim_0.taps); decim_1.set(); break; case OversampleRate::x8: // M4 can't handle 2 decimation passes for sample rates <= 600k. if (message.sample_rate < 600'000) { - decim_0.set().configure(taps_200k_decim_0.taps); - decim_1.set().configure(taps_200k_decim_1.taps); + decim_0.set().configure(taps_200k_decim_0.taps); + decim_1.set().configure(taps_200k_decim_1.taps); } else { // Using 180k taps to provide better filtering with a single pass. - decim_0.set().configure(taps_180k_wfm_decim_0.taps); + decim_0.set().configure(taps_180k_wfm_decim_0.taps); decim_1.set(); } break; case OversampleRate::x16: - decim_0.set().configure(taps_200k_decim_0.taps); - decim_1.set().configure(taps_200k_decim_1.taps); + decim_0.set().configure(taps_200k_decim_0.taps); + decim_1.set().configure(taps_200k_decim_1.taps); break; case OversampleRate::x32: - decim_0.set().configure(taps_200k_decim_0.taps); - decim_1.set().configure(taps_16k0_decim_1.taps); + decim_0.set().configure(taps_200k_decim_0.taps); + decim_1.set().configure(taps_16k0_decim_1.taps); break; case OversampleRate::x64: - decim_0.set().configure(taps_200k_decim_0.taps); - decim_1.set().configure(taps_16k0_decim_1.taps); + decim_0.set().configure(taps_200k_decim_0.taps); + decim_1.set().configure(taps_16k0_decim_1.taps); break; default: @@ -282,33 +383,12 @@ void FSKRxProcessor::sample_rate_config(const SampleRateConfigMessage& message) // size_t channel_filter_input_fs = decim_1_output_fs; // size_t channel_filter_output_fs = channel_filter_input_fs / channel_decimation; - size_t demod_input_fs = decim_1_output_fs; - - send_packet((uint32_t)demod_input_fs); + demod_input_fs = decim_1_output_fs; // Set ready to process data. configured = true; } -void FSKRxProcessor::flush() { - // word_extractor.flush(); -} - -void FSKRxProcessor::reset() { - clear_data_bits(); - has_sync_ = false; - inverted = false; - word_count = 0; - - samples_processed = 0; -} - -void FSKRxProcessor::send_packet(uint32_t data) { - data_message.is_data = true; - data_message.value = data; - shared_memory.application_queue.push(data_message); -} - /* main **************************************************/ int main() { diff --git a/firmware/baseband/proc_fsk_rx.hpp b/firmware/baseband/proc_fsk_rx.hpp index 25c790af2..fecbf0075 100644 --- a/firmware/baseband/proc_fsk_rx.hpp +++ b/firmware/baseband/proc_fsk_rx.hpp @@ -30,7 +30,6 @@ #include "dsp_decimate.hpp" #include "dsp_demodulate.hpp" -#include "dsp_iir_config.hpp" #include "dsp_fir_taps.hpp" #include "spectrum_collector.hpp" @@ -46,21 +45,6 @@ #include #include -/* Normalizes audio stream to +/-1.0f */ -class AudioNormalizer { - public: - void execute_in_place(const buffer_f32_t& audio); - - private: - void calculate_thresholds(); - - uint32_t counter_ = 0; - float min_ = 99.0f; - float max_ = -99.0f; - float t_hi_ = 1.0; - float t_lo_ = 1.0; -}; - /* A decimator that just returns the source buffer. */ class NoopDecim { public: @@ -111,44 +95,92 @@ class MultiDecimator { class FSKRxProcessor : public BasebandProcessor { public: - FSKRxProcessor(); void execute(const buffer_c8_t& buffer) override; void on_message(const Message* const message) override; private: - size_t baseband_fs = 1024000; // aka: sample_rate + static constexpr int ROLLING_WINDOW{32}; + static constexpr uint16_t MAX_BUFFER_SIZE{512}; + + enum Parse_State { + Parse_State_Wait_For_Peak = 0, + Parse_State_Preamble, + Parse_State_Sync, + Parse_State_PDU_Payload, + Parse_State_Parsing_Data + }; + + size_t baseband_fs = 960000; uint8_t stat_update_interval = 10; uint32_t stat_update_threshold = baseband_fs / stat_update_interval; - static constexpr auto spectrum_rate_hz = 50.0f; + + float detect_peak_power(const buffer_c8_t& buffer, int N); + void agc_correct_iq(const buffer_c8_t& buffer, int N, float measured_power); + float get_phase_diff(const complex16_t& sample0, const complex16_t& sample1); + void demodulateFSKBits(const buffer_c16_t& decimator_out, int num_demod_byte); + void resetPreambleTracking(); + void resetBitPacketIndex(); + void resetToDefaultState(); + + void handlePreambleState(const buffer_c16_t& decimator_out); + void handleSyncWordState(const buffer_c16_t& decimator_out); + void handlePDUPayloadState(const buffer_c16_t& decimator_out); void configure(const FSKRxConfigureMessage& message); - void capture_config(const CaptureConfigMessage& message); void sample_rate_config(const SampleRateConfigMessage& message); - void flush(); - void reset(); - void send_packet(uint32_t data); - void process_bits(const buffer_c8_t& buffer); - void clear_data_bits(); - void handle_sync(bool inverted); - - /* Returns true if the batch has as sync frame. */ - bool has_sync() const { return has_sync_; } - - /* Set once app is ready to receive messages. */ - bool configured = false; - - /* Buffer for decimated IQ data. */ - std::array dst{}; + std::array dst{}; const buffer_c16_t dst_buffer{ dst.data(), dst.size()}; - /* Buffer for demodulated audio. */ - std::array audio{}; - const buffer_f32_t audio_buffer{audio.data(), audio.size()}; + uint8_t rb_buf[MAX_BUFFER_SIZE]; + + dsp::demodulate::FM demod{}; + int rb_head{-1}; + int32_t g_threshold{0}; + uint8_t channel_number{0}; + + uint16_t process = 0; + + bool configured{false}; + FskPacketData fskPacketData{}; + + Parse_State parseState{Parse_State_Wait_For_Peak}; + + int sample_idx{0}; + int samples_eaten{0}; + + int32_t max_dB{0}; + int8_t real{0}; + int8_t imag{0}; + + uint16_t peak_timeout{0}; + float noise_floor{12.0}; // Using LNA 40 and VGA 20. 10.0 was 40/0 ratio. + float target_power_db{5.0}; + float agc_power{0.0f}; + + float frequency_offset_estimate{0.0f}; + float frequency_offset{0.0f}; + float phase_buffer[ROLLING_WINDOW] = {0.0f}; + int phase_buffer_index = 0; + + uint16_t packet_index{0}; + uint8_t bit_index{0}; + size_t demod_input_fs{0}; + + uint8_t SAMPLE_PER_SYMBOL{1}; + uint32_t DEFAULT_PREAMBLE{0xAAAAAAAA}; + uint32_t DEFAULT_SYNC_WORD{0xFFFFFFFF}; + uint8_t NUM_SYNC_WORD_BYTE{4}; + uint8_t NUM_PREAMBLE_BYTE{4}; + uint16_t NUM_DATA_BYTE = MAX_BUFFER_SIZE - NUM_SYNC_WORD_BYTE - NUM_PREAMBLE_BYTE; + + SpectrumCollector channel_spectrum{}; + size_t spectrum_interval_samples = 0; + size_t spectrum_samples = 0; + static constexpr auto spectrum_rate_hz = 50.0f; - /* The actual type will be configured depending on the sample rate. */ MultiDecimator< dsp::decimate::FIRC8xR16x24FS4Decim4, dsp::decimate::FIRC8xR16x24FS4Decim8> @@ -159,9 +191,7 @@ class FSKRxProcessor : public BasebandProcessor { NoopDecim> decim_1{}; - /* Filter to 24kHz and demodulate. */ dsp::decimate::FIRAndDecimateComplex channel_filter{}; - size_t deviation = 3750; // fir_taps_real<32> channel_filter_taps = 0; size_t channel_decimation = 2; int32_t channel_filter_low_f = 0; @@ -172,51 +202,6 @@ class FSKRxProcessor : public BasebandProcessor { FMSquelch squelch{}; uint64_t squelch_history = 0; - // /* LPF to reduce noise. POCSAG supports 2400 baud, but that falls - // * nicely into the transition band of this 1800Hz filter. - // * scipy.signal.butter(2, 1800, "lowpass", fs=24000, analog=False) */ - // IIRBiquadFilter lpf{{{0.04125354f, 0.082507070f, 0.04125354f}, - // {1.00000000f, -1.34896775f, 0.51398189f}}}; - - /* Attempts to de-noise and normalize signal. */ - AudioNormalizer normalizer{}; - - /* Handles writing audio stream to hardware. */ - AudioOutput audio_output{}; - - /* Holds the data sent to the app. */ - AFSKDataMessage data_message{false, 0}; - - /* Used to keep track of how many samples were processed - * between status update messages. */ - uint32_t samples_processed = 0; - - /* Number of bits in 'data_' member. */ - static constexpr uint8_t data_bit_count = sizeof(uint32_t) * 8; - - /* Sync frame codeword. */ - static constexpr uint32_t sync_codeword = 0x12345678; - - /* When true, sync frame has been received. */ - bool has_sync_ = false; - - /* When true, bit vales are flipped in the codewords. */ - bool inverted = false; - - uint32_t data = 0; - uint8_t bit_count = 0; - uint8_t word_count = 0; - - /* LPF to reduce noise. POCSAG supports 2400 baud, but that falls - * nicely into the transition band of this 1800Hz filter. - * scipy.signal.butter(2, 1800, "lowpass", fs=24000, analog=False) */ - IIRBiquadFilter lpf{{{0.04125354f, 0.082507070f, 0.04125354f}, - {1.00000000f, -1.34896775f, 0.51398189f}}}; - - SpectrumCollector channel_spectrum{}; - size_t spectrum_interval_samples = 0; - size_t spectrum_samples = 0; - /* NB: Threads should be the last members in the class definition. */ BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive}; RSSIThread rssi_thread{}; diff --git a/firmware/common/dsp_fir_taps.hpp b/firmware/common/dsp_fir_taps.hpp index 6b73820ad..c1f5e9787 100644 --- a/firmware/common/dsp_fir_taps.hpp +++ b/firmware/common/dsp_fir_taps.hpp @@ -1649,4 +1649,36 @@ static constexpr fir_taps_real<24> taps_BTLE_2M_PHY_decim_0 = { }}, }; + +// Tested to be better at capturing both 4.0 and 5.0 device. Better attenuation at channel end. +static constexpr fir_taps_real<24> taps_BTLE_Dual_PHY = { + .low_frequency_normalized = -750000.0f / 4000000.0f, + .high_frequency_normalized = 750000.0f / 4000000.0f, + .transition_normalized = 250000.0f / 4000000.0f, + .taps = {{3, + -5, + -97, + -144, + 317, + 1099, + 396, + -2887, + -4814, + 1912, + 18134, + 32767, + 32767, + 18134, + 1912, + -4814, + -2887, + 396, + 1099, + 317, + -144, + -97, + -5, + 3}}, +}; + #endif /*__DSP_FIR_TAPS_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 798f0762a..74427ae54 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -134,6 +134,7 @@ class Message { NoaaAptRxConfigure = 77, NoaaAptRxStatusData = 78, NoaaAptRxImageData = 79, + FSKPacket = 80, MAX }; @@ -452,6 +453,17 @@ struct BlePacketData { uint8_t dataLen; }; +struct FskPacketData { + int8_t real; + int8_t imag; + int max_dB; + uint8_t data[512]; + uint16_t dataLen; + uint64_t syncWord; + float power; + float frequency_offset_hz; +}; + class BLEPacketMessage : public Message { public: constexpr BLEPacketMessage( @@ -463,6 +475,17 @@ class BLEPacketMessage : public Message { BlePacketData* packet{nullptr}; }; +class FSKRxPacketMessage : public Message { + public: + constexpr FSKRxPacketMessage( + FskPacketData* packet) + : Message{ID::FSKPacket}, + packet{packet} { + } + + FskPacketData* packet{nullptr}; +}; + class CodedSquelchMessage : public Message { public: constexpr CodedSquelchMessage( @@ -1127,24 +1150,27 @@ class FSKConfigureMessage : public Message { class FSKRxConfigureMessage : public Message { public: constexpr FSKRxConfigureMessage( - const fir_taps_real<24> decim_0_filter, - const fir_taps_real<32> decim_1_filter, - const fir_taps_real<32> channel_filter, - const size_t channel_decimation, - const size_t deviation) + const uint8_t samplesPerSymbol, + const uint32_t syncWord, + const uint8_t syncWordLength, + const uint32_t preamble, + const uint8_t preambleLength, + const uint16_t numDataBytes) : Message{ID::FSKRxConfigure}, - decim_0_filter(decim_0_filter), - decim_1_filter(decim_1_filter), - channel_filter(channel_filter), - channel_decimation{channel_decimation}, - deviation{deviation} { + samplesPerSymbol(samplesPerSymbol), + syncWord(syncWord), + syncWordLength(syncWordLength), + preamble(preamble), + preambleLength(preambleLength), + numDataBytes(numDataBytes) { } - const fir_taps_real<24> decim_0_filter; - const fir_taps_real<32> decim_1_filter; - const fir_taps_real<32> channel_filter; - const size_t channel_decimation; - const size_t deviation; + const uint8_t samplesPerSymbol; + const uint32_t syncWord; + const uint8_t syncWordLength; + const uint32_t preamble; + const uint8_t preambleLength; + const uint16_t numDataBytes; }; class POCSAGConfigureMessage : public Message {