| 388 | // ── VFO: get/set frequency ───────────────────────────────────────────────── |
| 389 | |
| 390 | QString TciProtocol::cmdVfo(const QStringList& args, bool isSet) |
| 391 | { |
| 392 | if (args.isEmpty()) return {}; |
| 393 | int trx = args[0].toInt(); |
| 394 | auto* s = sliceForTrx(trx); |
| 395 | if (!s) return {}; |
| 396 | |
| 397 | if (!isSet) { |
| 398 | // Get: vfo:trx,channel,freq_hz; |
| 399 | long long hz = static_cast<long long>(std::round(s->frequency() * 1e6)); |
| 400 | return QStringLiteral("vfo:%1,0,%2;").arg(trx).arg(hz); |
| 401 | } |
| 402 | |
| 403 | // Set: vfo:trx,channel,freq_hz |
| 404 | if (args.size() < 3) return {}; |
| 405 | bool ok; |
| 406 | long long hz = args[2].toLongLong(&ok); |
| 407 | if (!ok || hz < 0) return {}; |
| 408 | double mhz = hz / 1e6; |
| 409 | |
| 410 | // Same policy as RigctlProtocol::cmdSetFreq: recenter only for tunes |
| 411 | // that leave the pan's current span (band changes); in-span retunes |
| 412 | // (Doppler steps from satellite trackers) use autopan=0 so the pan |
| 413 | // stays put. |
| 414 | RadioModel* model = m_model; |
| 415 | QMetaObject::invokeMethod(s, [s, model, mhz]() { |
| 416 | bool inSpan = false; |
| 417 | if (auto* pan = model ? model->panadapter(s->panId()) : nullptr) { |
| 418 | const double halfBw = pan->bandwidthMhz() / 2.0; |
| 419 | inSpan = halfBw > 0.0 && qAbs(mhz - pan->centerMhz()) <= halfBw; |
| 420 | } |
| 421 | if (inSpan) s->setFrequency(mhz); |
| 422 | else s->tuneAndRecenter(mhz); |
| 423 | }, Qt::QueuedConnection); |
| 424 | |
| 425 | m_pendingNotification = QStringLiteral("vfo:%1,0,%2;").arg(trx).arg(hz); |
| 426 | return {}; |
| 427 | } |
| 428 | |
| 429 | // ── Modulation: get/set mode ─────────────────────────────────────────────── |
| 430 |
nothing calls this directly
no test coverage detected