| 3417 | } |
| 3418 | |
| 3419 | void RadioModel::handleMemoryStatus(int index, const QMap<QString, QString>& kvs) |
| 3420 | { |
| 3421 | // Check for removal — radio sends either "in_use=0" or "removed" (no value) |
| 3422 | if (kvs.value("in_use") == "0" || kvs.contains("removed")) { |
| 3423 | m_memories.remove(index); |
| 3424 | emit memoryRemoved(index); |
| 3425 | return; |
| 3426 | } |
| 3427 | |
| 3428 | auto& m = m_memories[index]; |
| 3429 | m.index = index; |
| 3430 | |
| 3431 | // Decode the protocol space-encoding (0x7f -> ' ') for free-text fields, |
| 3432 | // then strip any NUL/control bytes so corrupt values from the radio (or a |
| 3433 | // previously corrupted memory) never reach the UI, CSV export, or a re-send. |
| 3434 | auto decodeText = [](const QString& v) { |
| 3435 | return AetherSDR::MemoryFields::sanitizeText(QString(v).replace('\x7f', ' ')); |
| 3436 | }; |
| 3437 | |
| 3438 | for (auto it = kvs.begin(); it != kvs.end(); ++it) { |
| 3439 | const QString& k = it.key(); |
| 3440 | const QString& v = it.value(); |
| 3441 | if (k == "group") m.group = decodeText(v); |
| 3442 | else if (k == "owner") m.owner = decodeText(v); |
| 3443 | else if (k == "freq") m.freq = v.toDouble(); |
| 3444 | else if (k == "name") m.name = decodeText(v); |
| 3445 | else if (k == "mode") m.mode = AetherSDR::MemoryFields::sanitizeText(v); |
| 3446 | else if (k == "step") m.step = v.toInt(); |
| 3447 | else if (k == "repeater") m.offsetDir = AetherSDR::MemoryFields::sanitizeText(v); |
| 3448 | else if (k == "repeater_offset") m.repeaterOffset = v.toDouble(); |
| 3449 | else if (k == "tone_mode") m.toneMode = AetherSDR::MemoryFields::sanitizeText(v); |
| 3450 | else if (k == "tone_value") m.toneValue = v.toDouble(); |
| 3451 | else if (k == "squelch") m.squelch = (v == "1"); |
| 3452 | else if (k == "squelch_level") m.squelchLevel = v.toInt(); |
| 3453 | else if (k == "rx_filter_low") m.rxFilterLow = v.toInt(); |
| 3454 | else if (k == "rx_filter_high") m.rxFilterHigh = v.toInt(); |
| 3455 | else if (k == "rtty_mark") m.rttyMark = v.toInt(); |
| 3456 | else if (k == "rtty_shift") m.rttyShift = v.toInt(); |
| 3457 | else if (k == "digl_offset") m.diglOffset = v.toInt(); |
| 3458 | else if (k == "digu_offset") m.diguOffset = v.toInt(); |
| 3459 | } |
| 3460 | |
| 3461 | emit memoryChanged(index); |
| 3462 | } |
| 3463 | |
| 3464 | // ─── Raw message handler (for meter status with '#' separators) ────────────── |
| 3465 |
no test coverage detected