public void updateRtt(Instant timeSent, long ackDelay) {

        Instant timeReceived = Instant.now();
        this.latestRtt = Duration.between(timeReceived, timeSent).toMillis();

        if (firstRttSample == Instant.MIN) {
            this.minRtt = this.latestRtt;
            this.smoothedRtt = this.latestRtt;
            this.rttVar = this.latestRtt / 2;
            this.firstRttSample = timeReceived;
        }

        // min_rtt ignores acknowledgment delay.
        this.minRtt = min(this.minRtt, this.latestRtt);

        // Limit ack_delay by max_ack_delay after handshake confirmation.
        if (conn.getHandshakeState().isConfirmed()) {
            ackDelay = min(ackDelay, this.maxAckDelay);
        }

        // Adjust for acknowledgment delay if plausible.
        long adjustedRtt = this.latestRtt;
        if (this.latestRtt >= this.minRtt + ackDelay) {
            adjustedRtt = this.latestRtt - ackDelay;
        }

        long currentRttVar = Math.abs(this.smoothedRtt - adjustedRtt);
        this.rttVar = (3 * this.rttVar + currentRttVar) / 4;
        this.smoothedRtt = (7 * this.smoothedRtt + adjustedRtt) / 8;
    }
ref: https://www.rfc-editor.org/rfc/rfc9002.html#section-a.7
src/main/java/core/packetproxy/quic/service/RttEstimator.java