mirror of
https://github.com/RFnexus/modem73.git
synced 2026-04-27 14:30:33 +00:00
only copy data tones for constellation diagram, density fix
This commit is contained in:
parent
be38e04153
commit
042b6ce6c6
2 changed files with 42 additions and 26 deletions
|
|
@ -140,13 +140,14 @@ public:
|
||||||
|
|
||||||
// Set up constellation callback for UI display
|
// Set up constellation callback for UI display
|
||||||
#ifdef WITH_UI
|
#ifdef WITH_UI
|
||||||
decoder_->constellation_callback = [](const DSP::Complex<float>* symbols, int count, int mod_bits) {
|
decoder_->constellation_callback = [this](const DSP::Complex<float>* symbols, int count, int mod_bits) {
|
||||||
if (g_ui_state) {
|
if (g_ui_state) {
|
||||||
// DSP::Complex<float> is layout-compatible with std::complex<float>
|
// DSP::Complex<float> is layout-compatible with std::complex<float>
|
||||||
g_ui_state->update_constellation(
|
g_ui_state->update_constellation(
|
||||||
reinterpret_cast<const std::complex<float>*>(symbols),
|
reinterpret_cast<const std::complex<float>*>(symbols),
|
||||||
count,
|
count,
|
||||||
mod_bits
|
mod_bits,
|
||||||
|
decoder_->seed_off
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -890,8 +891,7 @@ public:
|
||||||
config_.carrier_threshold_db = new_config.carrier_threshold_db;
|
config_.carrier_threshold_db = new_config.carrier_threshold_db;
|
||||||
config_.p_persistence = new_config.p_persistence;
|
config_.p_persistence = new_config.p_persistence;
|
||||||
config_.slot_time_ms = new_config.slot_time_ms;
|
config_.slot_time_ms = new_config.slot_time_ms;
|
||||||
|
|
||||||
|
|
||||||
// TX blanking
|
// TX blanking
|
||||||
config_.tx_blanking_enabled = new_config.tx_blanking_enabled;
|
config_.tx_blanking_enabled = new_config.tx_blanking_enabled;
|
||||||
|
|
||||||
|
|
|
||||||
60
tnc_ui.hh
60
tnc_ui.hh
|
|
@ -176,23 +176,33 @@ struct TNCUIState {
|
||||||
std::atomic<bool> constellation_valid{false};
|
std::atomic<bool> constellation_valid{false};
|
||||||
std::atomic<int64_t> constellation_update_time{0};
|
std::atomic<int64_t> constellation_update_time{0};
|
||||||
|
|
||||||
void update_constellation(const std::complex<float>* points, int count, int mod_bits) {
|
void update_constellation(const std::complex<float>* points, int count, int mod_bits, int seed_off = -1) {
|
||||||
std::lock_guard<std::mutex> lock(constellation_mutex);
|
std::lock_guard<std::mutex> lock(constellation_mutex);
|
||||||
|
|
||||||
// Copy points
|
// copy data tones only
|
||||||
int n = std::min(count, CONSTELLATION_SIZE);
|
static const int BLOCK_LEN = 5; // from Common::block_length
|
||||||
for (int i = 0; i < n; ++i) {
|
int n = 0;
|
||||||
constellation_points[i] = points[i];
|
for (int i = 0; i < count && n < CONSTELLATION_SIZE; ++i) {
|
||||||
|
if (seed_off >= 0 && (i % BLOCK_LEN) == seed_off) continue;
|
||||||
|
constellation_points[n++] = points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build density map
|
// Build density map
|
||||||
constellation_density.fill(0);
|
constellation_density.fill(0);
|
||||||
|
|
||||||
// Scale factor based on modulation (higher order = larger spread)
|
// Scale factor matched to actual constellation extents + headroom for noise
|
||||||
float scale = 1.5f;
|
float scale;
|
||||||
if (mod_bits >= 4) scale = 2.0f; // QAM16+
|
switch (mod_bits) {
|
||||||
if (mod_bits >= 6) scale = 2.5f; // QAM64+
|
case 1: scale = 1.5f; break; // BPSK (extent 1.00)
|
||||||
if (mod_bits >= 8) scale = 3.0f; // QAM256+
|
case 2: scale = 1.3f; break; // QPSK (extent 0.71)
|
||||||
|
case 3: scale = 1.5f; break; // 8PSK (extent 0.92)
|
||||||
|
case 4: scale = 1.7f; break; // QAM16 (extent 0.95)
|
||||||
|
case 6: scale = 2.0f; break; // QAM64 (extent 1.08)
|
||||||
|
case 8: scale = 2.3f; break; // QAM256 (extent 1.15)
|
||||||
|
case 10: scale = 2.5f; break; // QAM1024 (extent 1.19)
|
||||||
|
case 12: scale = 2.5f; break; // QAM4096 (extent 1.21)
|
||||||
|
default: scale = 1.5f; break;
|
||||||
|
}
|
||||||
|
|
||||||
int half = CONSTELLATION_GRID / 2;
|
int half = CONSTELLATION_GRID / 2;
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
|
|
@ -2880,18 +2890,17 @@ private:
|
||||||
int gx = (int)(dx * scale_x);
|
int gx = (int)(dx * scale_x);
|
||||||
int gy = (int)(dy * scale_y);
|
int gy = (int)(dy * scale_y);
|
||||||
|
|
||||||
// Accumulate density (handles downscaling)
|
// Ensure at least 1 grid cell per display cell (prevents striping)
|
||||||
|
int gx_end = std::max(gx + 1, std::min((int)((dx + 1) * scale_x), grid_size));
|
||||||
|
int gy_end = std::max(gy + 1, std::min((int)((dy + 1) * scale_y), grid_size));
|
||||||
|
|
||||||
int density = 0;
|
int density = 0;
|
||||||
int samples = 0;
|
|
||||||
int gx_end = std::min((int)((dx + 1) * scale_x), grid_size);
|
|
||||||
int gy_end = std::min((int)((dy + 1) * scale_y), grid_size);
|
|
||||||
for (int sy = gy; sy < gy_end; ++sy) {
|
for (int sy = gy; sy < gy_end; ++sy) {
|
||||||
for (int sx = gx; sx < gx_end; ++sx) {
|
for (int sx = gx; sx < gx_end; ++sx) {
|
||||||
density += state_.constellation_density[sy * grid_size + sx];
|
int d = state_.constellation_density[sy * grid_size + sx];
|
||||||
samples++;
|
if (d > density) density = d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (samples > 0) density /= samples;
|
|
||||||
|
|
||||||
// Map density to character
|
// Map density to character
|
||||||
int char_idx = (density * (num_chars - 1)) / peak;
|
int char_idx = (density * (num_chars - 1)) / peak;
|
||||||
|
|
@ -2915,12 +2924,18 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw center crosshair
|
// Draw center crosshair (only if cell is empty)
|
||||||
int mid_y = height / 2;
|
int mid_y = height / 2;
|
||||||
int mid_x = width / 2;
|
int mid_x = width / 2;
|
||||||
attron(A_DIM);
|
int mid_gx = (int)(mid_x * scale_x);
|
||||||
mvaddch(y + 1 + mid_y, x + 1 + mid_x, '+');
|
int mid_gy = (int)(mid_y * scale_y);
|
||||||
attroff(A_DIM);
|
bool center_empty = (mid_gx < grid_size && mid_gy < grid_size &&
|
||||||
|
state_.constellation_density[mid_gy * grid_size + mid_gx] == 0);
|
||||||
|
if (center_empty) {
|
||||||
|
attron(A_DIM);
|
||||||
|
mvaddch(y + 1 + mid_y, x + 1 + mid_x, '+');
|
||||||
|
attroff(A_DIM);
|
||||||
|
}
|
||||||
|
|
||||||
// Show modulation name in top-right of box
|
// Show modulation name in top-right of box
|
||||||
const char* mod_name = "";
|
const char* mod_name = "";
|
||||||
|
|
@ -2935,8 +2950,9 @@ private:
|
||||||
case 12: mod_name = "QAM4096"; break;
|
case 12: mod_name = "QAM4096"; break;
|
||||||
}
|
}
|
||||||
if (mod_name[0]) {
|
if (mod_name[0]) {
|
||||||
|
int name_len = strlen(mod_name);
|
||||||
attron(A_DIM);
|
attron(A_DIM);
|
||||||
mvaddstr(y, x + width - 6, mod_name);
|
mvaddstr(y, x + width + 1 - name_len, mod_name);
|
||||||
attroff(A_DIM);
|
attroff(A_DIM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue