From f009d43d7544c9b96db9ff402dd963bfc84b71f8 Mon Sep 17 00:00:00 2001 From: Nedifinita Date: Sat, 6 Dec 2025 01:03:12 +0800 Subject: [PATCH] feat: enhance message handling and logging in demodulation and TCP channel --- android/app/src/main/cpp/demod.cpp | 96 ++++++++++++++++++- android/app/src/main/cpp/native-lib.cpp | 40 ++++---- .../flutter/RtlTcpChannelHandler.kt | 79 +++++++++++---- 3 files changed, 179 insertions(+), 36 deletions(-) diff --git a/android/app/src/main/cpp/demod.cpp b/android/app/src/main/cpp/demod.cpp index 4723fe4..99809f7 100644 --- a/android/app/src/main/cpp/demod.cpp +++ b/android/app/src/main/cpp/demod.cpp @@ -1,4 +1,5 @@ #include "demod.h" +#include bool is_message_ready = false; @@ -188,6 +189,10 @@ void decodeBatch() function_bits = (code_words[i] >> 11) & 0x3; int addressBits = (code_words[i] >> 13) & 0x3ffff; address = (addressBits << 3) | frame; + + __android_log_print(ANDROID_LOG_DEBUG, "DEMOD", "addr_cw: raw=0x%08X addr=%u func=%d frame=%d bch_err=%d parity_err=%d", + code_words[i], address, function_bits, frame, code_words_bch_error[i] ? 1 : 0, parityError ? 1 : 0); + numeric_msg = ""; alpha_msg = ""; alpha_bit_buffer_bits = 0; @@ -200,6 +205,9 @@ void decodeBatch() int messageBits = (code_words[i] >> 11) & 0xfffff; if (parityError) parity_errors++; if (code_words_bch_error[i]) bch_errors++; + + __android_log_print(ANDROID_LOG_DEBUG, "DEMOD", "msg_cw: raw=0x%08X msgbits=0x%05X bch_err=%d parity_err=%d", + code_words[i], messageBits, code_words_bch_error[i] ? 1 : 0, parityError ? 1 : 0); for (int j = 16; j >= 0; j -= 4) { @@ -325,6 +333,7 @@ void processBasebandSample(double sample) if (got_SC) { + __android_log_print(ANDROID_LOG_DEBUG, "DEMOD", "sync_found: inverted=%d bits=0x%08X", bit_inverted ? 1 : 0, bits); bits = 0; bit_cnt = 0; code_words[0] = POCSAG_SYNCCODE; @@ -381,5 +390,90 @@ void processOneSample(int8_t i, int8_t q) float deviation; double fmDemod = phaseDiscri.phaseDiscriminatorDelta(iq, magsqRaw, deviation); - processBasebandSample(fmDemod); + double filt = lowpassBaud.filter(fmDemod); + + if (!got_SC) { + preambleMovingAverage(filt); + dc_offset = preambleMovingAverage.asDouble(); + } + + bool data = (filt - dc_offset) >= 0.0; + + if (data != prev_data) { + sync_cnt = SAMPLES_PER_SYMBOL / 2; + } else { + sync_cnt--; + + if (sync_cnt <= 0) { + if (bit_inverted) { + data_bit = data; + } else { + data_bit = !data; + } + + bits = (bits << 1) | data_bit; + bit_cnt++; + + if (bit_cnt > 32) { + bit_cnt = 32; + } + + if (bit_cnt == 32 && !got_SC) { + if (bits == POCSAG_SYNCCODE) { + got_SC = true; + bit_inverted = false; + } else if (bits == POCSAG_SYNCCODE_INV) { + got_SC = true; + bit_inverted = true; + } else if (pop_cnt(bits ^ POCSAG_SYNCCODE) <= 3) { + uint32_t corrected_cw; + if (bchDecode(bits, corrected_cw) && corrected_cw == POCSAG_SYNCCODE) { + got_SC = true; + bit_inverted = false; + } + } else if (pop_cnt(bits ^ POCSAG_SYNCCODE_INV) <= 3) { + uint32_t corrected_cw; + if (bchDecode(~bits, corrected_cw) && corrected_cw == POCSAG_SYNCCODE) { + got_SC = true; + bit_inverted = true; + } + } + + if (got_SC) { + __android_log_print(ANDROID_LOG_DEBUG, "DEMOD", "sync_found: inverted=%d bits=0x%08X", bit_inverted ? 1 : 0, bits); + bits = 0; + bit_cnt = 0; + code_words[0] = POCSAG_SYNCCODE; + word_cnt = 1; + } + } else if (bit_cnt == 32 && got_SC) { + uint32_t corrected_cw; + code_words_bch_error[word_cnt] = !bchDecode(bits, corrected_cw); + code_words[word_cnt] = corrected_cw; + word_cnt++; + + if (word_cnt == 1 && corrected_cw != POCSAG_SYNCCODE) { + got_SC = false; + bit_inverted = false; + } + + if (word_cnt == PAGERDEMOD_BATCH_WORDS) { + decodeBatch(); + batch_num++; + word_cnt = 0; + } + + bits = 0; + bit_cnt = 0; + + if (address > 0 && !numeric_msg.empty()) { + is_message_ready = true; + } + } + + sync_cnt = SAMPLES_PER_SYMBOL; + } + } + + prev_data = data; } \ No newline at end of file diff --git a/android/app/src/main/cpp/native-lib.cpp b/android/app/src/main/cpp/native-lib.cpp index 85e69ac..3407ac2 100644 --- a/android/app/src/main/cpp/native-lib.cpp +++ b/android/app/src/main/cpp/native-lib.cpp @@ -255,27 +255,33 @@ void clientThread(std::string host, int port) std::lock_guard demodLock(demodDataMutex); processOneSample(i_ds, q_ds); + if (is_message_ready) + { + std::ostringstream ss; + std::lock_guard msgLock(msgMutex); + + std::string message_content; + if (function_bits == 3) { + message_content = alpha_msg; + } else { + message_content = numeric_msg; + } + if (message_content.empty()) { + message_content = alpha_msg.empty() ? numeric_msg : alpha_msg; + } + + ss << "[MSG]" << address << "|" << function_bits << "|" << message_content; + messageBuffer.push_back(ss.str()); + + is_message_ready = false; + numeric_msg.clear(); + alpha_msg.clear(); + } + acc_i = acc_q = 0; decim_counter = 0; } } - - if (is_message_ready) - { - std::ostringstream ss; - - std::lock_guard demodLock(demodDataMutex); - std::lock_guard msgLock(msgMutex); - - std::string message_content = alpha_msg.empty() ? numeric_msg : alpha_msg; - ss << "[MSG]" << address << "|" << function_bits << "|" << message_content; - messageBuffer.push_back(ss.str()); - - is_message_ready = false; - - numeric_msg.clear(); - alpha_msg.clear(); - } } if (n < 0 && running) diff --git a/android/app/src/main/kotlin/org/noxylva/lbjconsole/flutter/RtlTcpChannelHandler.kt b/android/app/src/main/kotlin/org/noxylva/lbjconsole/flutter/RtlTcpChannelHandler.kt index b6dd5b8..a796d25 100644 --- a/android/app/src/main/kotlin/org/noxylva/lbjconsole/flutter/RtlTcpChannelHandler.kt +++ b/android/app/src/main/kotlin/org/noxylva/lbjconsole/flutter/RtlTcpChannelHandler.kt @@ -61,6 +61,7 @@ class RtlTcpChannelHandler : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { android.util.Log.d("RTL-TCP", "evt_listen") this.eventSink = events + lastConnectedState = true startPolling() } @@ -77,40 +78,82 @@ class RtlTcpChannelHandler : EventChannel.StreamHandler { android.util.Log.w("RTL-TCP", "evt_null"); return; } - - val connected = isConnected() - val strength = getSignalStrength() - val logs = pollMessages() - val regex = "\\[MSG\\]\\s*(\\d+)\\|(-?\\d+)\\|(.*)".toRegex() + val connected = try { + isConnected() + } catch (e: Exception) { + android.util.Log.e("RTL-TCP", "isConnected() failed", e) + false + } - if (connected != lastConnectedState || connected) { + val strength = try { + getSignalStrength() + } catch (e: Exception) { + android.util.Log.e("RTL-TCP", "getSignalStrength() failed", e) + 0.0 + } + + val logs = try { + pollMessages() + } catch (e: Exception) { + android.util.Log.e("RTL-TCP", "pollMessages() failed", e) + "" + } + + val regex = "\\[MSG\\]\\s*(\\d+)\\|(-?\\d+)\\|(.*)".toRegex() + + android.util.Log.d("RTL-TCP", "poll: connected=$connected magsqRaw=$strength logsLen=${logs.length}") + if (logs.isNotEmpty()) { + val preview = if (logs.length > 1000) logs.substring(0, 1000) + "..." else logs + android.util.Log.d("RTL-TCP", "pollLogs: $preview") + } + + if (connected != lastConnectedState) { val statusMap = mutableMapOf() statusMap["connected"] = connected statusMap["magsqRaw"] = strength - eventSink?.success(statusMap) + try { + eventSink?.success(statusMap) + } catch (e: Exception) { + android.util.Log.e("RTL-TCP", "eventSink status send failed", e) + } lastConnectedState = connected } if (logs.isNotEmpty()) { regex.findAll(logs).forEach { match -> try { - val dataMap = mutableMapOf() - dataMap["address"] = match.groupValues[1] - dataMap["func"] = match.groupValues[2] - - val gbkBytes = match.groupValues[3].toByteArray(Charsets.ISO_8859_1) - val utf8String = String(gbkBytes, Charset.forName("GBK")) - dataMap["numeric"] = utf8String - - dataMap["magsqRaw"] = strength + val addr = match.groupValues[1] + val func = match.groupValues[2] + val raw = match.groupValues[3] + android.util.Log.d("RTL-TCP", "msg_match: addr=$addr func=$func raw_len=${raw.length}") - eventSink?.success(dataMap) + val gbkBytes = raw.toByteArray(Charsets.ISO_8859_1) + val utf8String = try { + String(gbkBytes, Charset.forName("GBK")) + } catch (e: Exception) { + android.util.Log.e("RTL-TCP", "GBK decode failed", e) + raw + } + + android.util.Log.d("RTL-TCP", "msg_decoded: addr=$addr func=$func numeric=$utf8String") + + val dataMap = mutableMapOf() + dataMap["address"] = addr + dataMap["func"] = func + dataMap["numeric"] = utf8String + dataMap["magsqRaw"] = strength + + try { + eventSink?.success(dataMap) + } catch (e: Exception) { + android.util.Log.e("RTL-TCP", "eventSink data send failed", e) + } } catch (e: Exception) { android.util.Log.e("RTL-TCP", "decode_fail", e) } } } - + handler.postDelayed(this, 200) } })