| 1497 | } |
| 1498 | |
| 1499 | void MainWindow::wireDaxIq() |
| 1500 | { |
| 1501 | // ── DAX IQ wiring (all platforms, established once at construction) ── |
| 1502 | // |
| 1503 | // The DAX-IQ data path is independent of the DAX *audio* bridge: it needs |
| 1504 | // only PanadapterStream (VITA-49 IQ routing) and the applet, both of which |
| 1505 | // exist at construction on every platform. Wiring it here — instead of |
| 1506 | // lazily inside startDax() on Mac/PipeWire — means a DAX-IQ channel works |
| 1507 | // without first enabling DAX audio, and the enable/disable/rate connections |
| 1508 | // survive a DAX-audio stop (they were never tied to the bridge's lifetime, |
| 1509 | // so stopDax() can't tear them down and leave a half-wired path that |
| 1510 | // double-creates streams). This is the single source of IQ-side wiring; |
| 1511 | // startDax() wires only the audio-bridge connections. (Same stream-status |
| 1512 | // registration need as #1820 / RADE RX on Windows.) |
| 1513 | if (m_appletPanel && m_appletPanel->daxIqApplet() && m_radioModel.panStream()) { |
| 1514 | connect(&m_radioModel, &RadioModel::statusReceived, |
| 1515 | this, [this](const QString& obj, const QMap<QString,QString>& kvs) { |
| 1516 | if (!obj.startsWith("stream ")) return; |
| 1517 | const QStringList parts = obj.split(QLatin1Char(' '), Qt::SkipEmptyParts); |
| 1518 | if (parts.size() < 2) return; |
| 1519 | bool ok = false; |
| 1520 | quint32 streamId = parts[1].toUInt(&ok, 0); |
| 1521 | if (!ok) return; |
| 1522 | const bool removed = parts.contains(QStringLiteral("removed")) |
| 1523 | || kvs.contains(QStringLiteral("removed")); |
| 1524 | if (removed) { |
| 1525 | m_radioModel.panStream()->unregisterIqStream(streamId); |
| 1526 | m_radioModel.daxIqModel().handleStreamRemoved(streamId); |
| 1527 | qCDebug(lcDax) << "MainWindow: unregistered removed DAX IQ stream" |
| 1528 | << "0x" + QString::number(streamId, 16); |
| 1529 | return; |
| 1530 | } |
| 1531 | if (kvs.value("type") != "dax_iq") return; |
| 1532 | if (!streamStatusBelongsToUs(kvs, m_radioModel.ourClientHandle())) { |
| 1533 | qCDebug(lcDax) << "MainWindow: ignoring DAX IQ stream for another client" |
| 1534 | << "stream=0x" + QString::number(streamId, 16) |
| 1535 | << "owner=" << kvs.value("client_handle"); |
| 1536 | return; |
| 1537 | } |
| 1538 | qCDebug(lcDax) << "MainWindow: DAX IQ stream status" << obj |
| 1539 | << "keys=" << kvs.keys() |
| 1540 | << "ch=" << kvs.value("daxiq_channel") |
| 1541 | << "ip=" << kvs.value("ip"); |
| 1542 | m_radioModel.daxIqModel().applyStreamStatus(streamId, kvs); |
| 1543 | int ch = kvs.value("daxiq_channel").toInt(); |
| 1544 | if (streamId && ch >= 1 && ch <= 4) |
| 1545 | m_radioModel.panStream()->registerIqStream(streamId, ch); |
| 1546 | }); |
| 1547 | |
| 1548 | connect(m_radioModel.panStream(), &PanadapterStream::iqDataReady, |
| 1549 | &m_radioModel.daxIqModel(), &DaxIqModel::feedRawIqPacket); |
| 1550 | connect(&m_radioModel.daxIqModel(), &DaxIqModel::iqLevelReady, |
| 1551 | m_appletPanel->daxIqApplet(), &DaxIqApplet::setDaxIqLevel); |
| 1552 | connect(m_appletPanel->daxIqApplet(), &DaxIqApplet::iqEnableRequested, |
| 1553 | &m_radioModel.daxIqModel(), &DaxIqModel::createStream); |
| 1554 | connect(m_appletPanel->daxIqApplet(), &DaxIqApplet::iqDisableRequested, |
| 1555 | &m_radioModel.daxIqModel(), &DaxIqModel::removeStream); |
| 1556 | connect(m_appletPanel->daxIqApplet(), &DaxIqApplet::iqRateChanged, |
nothing calls this directly
no test coverage detected