Skip to content

AM carrier level reads stale: setAmCarrierLevel() omits the optimistic local update its sibling TX setters have #3711

Description

@jensenpat

Summary

TransmitModel::setAmCarrierLevel(int level) sends the radio command but never updates its local member m_amCarrierLevel. It is the only TX setter missing the optimistic local-cache update that all its siblings perform, so transmitModel().amCarrierLevel() returns a stale value until the radio's asynchronous status echo arrives.

Location

src/models/TransmitModel.cpp, ~line 618:

void TransmitModel::setAmCarrierLevel(int level) {
    level = qBound(0, level, 100);
    emit commandReady(QString("transmit set am_carrier=%1").arg(level));   // <-- m_amCarrierLevel never set
}

Compare the sibling setters in the same file, which all cache locally before/with the signal:

  • setRfPower → sets m_rfPower, then emit stateChanged()
  • setMicLevel → sets m_micLevel, then emit micStateChanged()
  • setDexpLevel → sets m_dexpLevel (+ m_companderLevel), then emit phoneStateChanged()

FlexLib's own Radio.cs AMCarrierLevel property setter also caches locally before SendCommand.

Impact

After the GUI (or a TCI client, or the #3646 automation bridge's get transmit) changes AM carrier level, amCarrierLevel() stays stale until the radio echoes am_carrier_level= back (parsed in applyTransmitStatus, ~line 143). Any consumer that reads the value right after a change gets the old number.

Severity: low/cosmetic — the command itself is sent correctly and the radio applies it; the model just lags behind the widget instead of being immediately consistent.

Steps to reproduce

  1. Connect to a radio.
  2. Change the AM carrier level via the Phone applet slider (or transmit set am_carrier=N).
  3. Immediately read transmitModel().amCarrierLevel().

Observed: the value does not reflect the change until the radio's status echo arrives. Measured live on a FLEX-8400M: did not converge within a 2 s poll window, whereas RF power / mic gain / DEXP converge in ~0 ms because they update optimistically.
Expected: amCarrierLevel() reflects the new value immediately, like every other TX setpoint.

Suggested fix

Mirror the sibling pattern (AM carrier is a phone/VOX-group property, so emit phoneStateChanged()):

void TransmitModel::setAmCarrierLevel(int level) {
    level = qBound(0, level, 100);
    if (m_amCarrierLevel != level) {
        m_amCarrierLevel = level;
        emit phoneStateChanged();
    }
    emit commandReady(QString("transmit set am_carrier=%1").arg(level));
}

Notes for the fix:

  • The command keyword am_carrier is correct (matches FlexLib Radio.cs:8384) — do not change it.
  • Verify no GUI feedback loop is introduced: the PhoneApplet AM-carrier slider's valueChanged lambda (src/gui/PhoneApplet.cpp ~line 123) only calls setAmCarrierLevel when !m_updatingFromModel, and the model→widget sync (~line 398) uses a QSignalBlocker. Confirm a single user change does not produce repeated transmit set am_carrier commands.

How it was found

Surfaced during live QA on a FLEX-8400M using the #3646 agent automation bridge — model-level validation via get transmit caught the AM carrier slider not converging while every other TX control did.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUIUser interfacebugSomething isn't workinggood first issueGood for newcomersmaintainer-reviewRequires maintainer review before any action is takenpriority: lowLow priorityprotocolSmartSDR protocol

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions