void TCPStreamDialog::fillRoundTripTime()
{
    QString dlg_title = QString(tr("Round Trip Time")) + streamDescription();
    setWindowTitle(dlg_title);
    title_->setText(dlg_title);
    sequence_num_map_.clear();

    QCustomPlot *sp = ui->streamPlot;
    sp->xAxis->setLabel(sequence_number_label_);
    sp->xAxis->setNumberFormat("f");
    sp->xAxis->setNumberPrecision(0);
    sp->yAxis->setLabel(round_trip_time_ms_label_);

    base_graph_->setLineStyle(QCPGraph::lsLine);

    QVector<double> seq_no, rtt;
    guint32 seq_base = 0;
    struct unack *unack = NULL, *u = NULL;
    for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        if (compareHeaders(seg)) {
            seq_base = seg->th_seq;
            break;
        }
    }
    for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        if (compareHeaders(seg)) {
            guint32 seqno = seg->th_seq - seq_base;
            if (seg->th_seglen && !rtt_is_retrans(unack, seqno)) {
                double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
                u = rtt_get_new_unack(rt_val, seqno);
                if (!u) return;
                rtt_put_unack_on_list(&unack, u);
            }
        } else {
            guint32 ack_no = seg->th_ack - seq_base;
            double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
            struct unack *v;

            for (u = unack; u; u = v) {
                if (ack_no > u->seqno) {
                    seq_no.append(u->seqno);
                    rtt.append((rt_val - u->time) * 1000.0);
                    sequence_num_map_.insert(u->seqno, seg);
                    v = u->next;
                    rtt_delete_unack_from_list(&unack, u);
                } else {
                    v = u->next;
                }
            }
        }
    }
    base_graph_->setData(seq_no, rtt);
}
void TCPStreamDialog::fillWindowScale()
{
    QString dlg_title = QString(tr("Window Scaling")) + streamDescription();
    setWindowTitle(dlg_title);
    title_->setText(dlg_title);

    QCustomPlot *sp = ui->streamPlot;
    base_graph_->setLineStyle(QCPGraph::lsLine);

    QVector<double> rel_time, win_size;
    for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        if (!compareHeaders(seg)) {
            continue;
        }

        double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
        guint16 flags = seg->th_flags;

        if ((flags & (TH_SYN|TH_RST)) == 0) {
            rel_time.append(ts - ts_offset_);
            win_size.append(seg->th_win);
        }
    }
    base_graph_->setData(rel_time, win_size);
    sp->yAxis->setLabel(window_size_label_);
}
void TCPStreamDialog::fillTcptrace()
{
    QString dlg_title = QString(tr("Sequence Numbers (tcptrace)")) + streamDescription();
    setWindowTitle(dlg_title);
    title_->setText(dlg_title);

    QCustomPlot *sp = ui->streamPlot;
    sp->yAxis->setLabel(sequence_number_label_);

    base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot));

    seg_graph_->setVisible(true);
    ack_graph_->setVisible(true);
    rwin_graph_->setVisible(true);

    QVector<double> seq_time, seq, sb_time, sb_center, sb_span, ackrwin_time, ack, rwin;
    for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_;
        if (compareHeaders(seg)) {
            // Forward direction: seq + data
            seq_time.append(ts);
            seq.append(seg->th_seq - seq_offset_);

            // QCP doesn't have a segment graph type. For now, fake
            // it with error bars.
            if (seg->th_seglen > 0) {
                double half = seg->th_seglen / 2.0;
                sb_time.append(ts);
                sb_center.append(seg->th_seq - seq_offset_ + half);
                sb_span.append(half);
            }
        } else {
            // Reverse direction: ACK + RWIN
            if (! (seg->th_flags & TH_ACK)) {
                // SYNs and RSTs do not necessarily have ACKs
                continue;
            }
            double ackno = seg->th_ack - seq_offset_;
            ackrwin_time.append(ts);
            ack.append(ackno);
            rwin.append(ackno + seg->th_win);
        }
    }
    base_graph_->setData(seq_time, seq);
    seg_graph_->setDataValueError(sb_time, sb_center, sb_span);
    ack_graph_->setData(ackrwin_time, ack);
    rwin_graph_->setData(ackrwin_time, rwin);
}
void TCPStreamDialog::fillStevens()
{
    QString dlg_title = QString(tr("Sequence Numbers (Stevens)")) + streamDescription();
    setWindowTitle(dlg_title);
    title_->setText(dlg_title);

    QCustomPlot *sp = ui->streamPlot;
    sp->yAxis->setLabel(sequence_number_label_);

    // True Stevens-style graphs don't have lines but I like them - gcc
    base_graph_->setLineStyle(QCPGraph::lsStepLeft);

    QVector<double> rel_time, seq;
    for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        if (!compareHeaders(seg)) {
            continue;
        }

        double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
        rel_time.append(ts - ts_offset_);
        seq.append(seg->th_seq - seq_offset_);
    }
    base_graph_->setData(rel_time, seq);
}
// Fill in graph data based upon what was read into the rlc_graph struct.
void LteRlcGraphDialog::fillGraph()
{
    QCustomPlot *sp = ui->rlcPlot;

    // We should always have 4 graphs, but cover case if no channel was chosen.
    if (sp->graphCount() < 1) {
        return;
    }

    tracer_->setGraph(NULL);

    base_graph_->setLineStyle(QCPGraph::lsNone);       // dot
    reseg_graph_->setLineStyle(QCPGraph::lsNone);      // dot
    acks_graph_->setLineStyle(QCPGraph::lsStepLeft);   // to get step effect...
    nacks_graph_->setLineStyle(QCPGraph::lsNone);      // dot, but bigger.

    // Will show all graphs with data we find.
    for (int i = 0; i < sp->graphCount(); i++) {
        sp->graph(i)->clearData();
        sp->graph(i)->setVisible(true);
    }

    // N.B. ssDisc is really too slow. TODO: work out how to turn off aliasing, or experiment
    // with ssCustom.  Other styles tried didn't look right.
    // GTK version was speeded up noticibly by turning down aliasing level...
    base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));
    reseg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));
    acks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));
    // NACKs are shown bigger than others.
    nacks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_*2));

    // Map timestamps -> segments in first pass.
    time_stamp_map_.clear();
    for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        if (!compareHeaders(seg)) {
            continue;
        }
        double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;

        time_stamp_map_.insertMulti(ts, seg);
    }

    // Now sequence numbers.
    QVector<double> seq_time, seq,
                    reseg_seq_time, reseg_seq,
                    acks_time, acks,
                    nacks_time, nacks;
    for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
        if (compareHeaders(seg)) {
            if (!seg->isControlPDU) {
                // Data
                if (seg->isResegmented) {
                    reseg_seq_time.append(ts);
                    reseg_seq.append(seg->SN);
                }
                else {
                    seq_time.append(ts);
                    seq.append(seg->SN);
                }
            }
            else {
                // Status (ACKs/NACKs)
                acks_time.append(ts);
                acks.append(seg->ACKNo-1);
                for (int n=0; n < seg->noOfNACKs; n++) {
                    nacks_time.append(ts);
                    nacks.append(seg->NACKs[n]);
                }
            }
        }
    }

    // Add the data from the graphs.
    base_graph_->setData(seq_time, seq);
    reseg_graph_->setData(reseg_seq_time, reseg_seq);
    acks_graph_->setData(acks_time, acks);
    nacks_graph_->setData(nacks_time, nacks);

    sp->setEnabled(true);

    // Auto-size...
    mouseMoved(NULL);
    resetAxes();

    tracer_->setGraph(base_graph_);

    // XXX QCustomPlot doesn't seem to draw any sort of focus indicator.
    sp->setFocus();
}
void TCPStreamDialog::fillThroughput()
{
    QString dlg_title = QString(tr("Throughput")) + streamDescription();
#ifdef MA_1_SECOND
    dlg_title.append(tr(" (1s MA)"));
#else
    dlg_title.append(QString(tr(" (%1 Segment MA)")).arg(moving_avg_period_));
#endif
    setWindowTitle(dlg_title);
    title_->setText(dlg_title);

    QCustomPlot *sp = ui->streamPlot;
    sp->yAxis->setLabel(segment_length_label_);
    sp->yAxis2->setLabel(average_throughput_label_);
    sp->yAxis2->setLabelColor(QColor(graph_color_2));
    sp->yAxis2->setTickLabelColor(QColor(graph_color_2));
    sp->yAxis2->setVisible(true);

    tput_graph_->setVisible(true);

    if (!graph_.segments || !graph_.segments->next) {
        dlg_title.append(tr(" [not enough data]"));
        return;
    }

    QVector<double> rel_time, seg_len, tput_time, tput;
    int oldest = 0;
    guint64 sum = 0;
    // Financial charts don't show MA data until a full period has elapsed.
    // The Rosetta Code MA examples start spitting out values immediately.
    // For now use not-really-correct initial values just to keep our vector
    // lengths the same.
    for (struct segment *seg = graph_.segments->next; seg != NULL; seg = seg->next) {
        if (!compareHeaders(seg)) {
            continue;
        }

        double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_;

        rel_time.append(ts);
        seg_len.append(seg->th_seglen);

#ifdef MA_1_SECOND
        while (ts - rel_time[oldest] > 1.0 && oldest < rel_time.size()) {
            sum -= seg_len[oldest];
            oldest++;
        }
#else
        if (seg_len.size() > moving_avg_period_) {
            sum -= seg_len[oldest];
            oldest++;
        }
#endif

        double dtime = ts - rel_time[oldest];
        double av_tput;
        sum += seg->th_seglen;
        if (dtime > 0.0) {
            av_tput = sum * 8.0 / dtime;
        } else {
            av_tput = 0.0;
        }

        // Add a data point only if our time window has advanced. Otherwise
        // update the most recent point. (We might want to show a warning
        // for out-of-order packets.)
        if (tput_time.size() > 0 && ts <= tput_time.last()) {
            tput[tput.size() - 1] = av_tput;
        } else {
            tput.append(av_tput);
            tput_time.append(ts);
        }
    }
    base_graph_->setData(rel_time, seg_len);
    tput_graph_->setData(tput_time, tput);
}
void TCPStreamDialog::fillGraph()
{
    QCustomPlot *sp = ui->streamPlot;

    if (sp->graphCount() < 1) return;

    base_graph_->setLineStyle(QCPGraph::lsNone);
    tracer_->setGraph(NULL);

    // base_graph_ is always visible.
    for (int i = 0; i < sp->graphCount(); i++) {
        sp->graph(i)->clearData();
        sp->graph(i)->setVisible(i == 0 ? true : false);
    }

    base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));

    sp->xAxis->setLabel(time_s_label_);
    sp->xAxis->setNumberFormat("gb");
    sp->xAxis->setNumberPrecision(6);
    sp->yAxis->setNumberFormat("f");
    sp->yAxis->setNumberPrecision(0);
    sp->yAxis2->setVisible(false);
    sp->yAxis2->setLabel(QString());

    if (!cap_file_) {
        QString dlg_title = QString(tr("No Capture Data"));
        setWindowTitle(dlg_title);
        title_->setText(dlg_title);
        sp->setEnabled(false);
        sp->yAxis->setLabel(QString());
        sp->replot();
        return;
    }

    ts_offset_ = 0;
    seq_offset_ = 0;
    bool first = true;
    guint64 bytes_fwd = 0;
    guint64 bytes_rev = 0;
    int pkts_fwd = 0;
    int pkts_rev = 0;

    time_stamp_map_.clear();
    for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
        if (!compareHeaders(seg)) {
            bytes_rev += seg->th_seglen;
            pkts_rev++;
            continue;
        }
        bytes_fwd += seg->th_seglen;
        pkts_fwd++;
        double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
        if (first) {
            if (ts_origin_conn_) ts_offset_ = ts;
            if (seq_origin_zero_) seq_offset_ = seg->th_seq;
            first = false;
        }
        time_stamp_map_.insertMulti(ts - ts_offset_, seg);
    }

    switch (graph_.type) {
    case GRAPH_TSEQ_STEVENS:
        fillStevens();
        break;
    case GRAPH_TSEQ_TCPTRACE:
        fillTcptrace();
        break;
    case GRAPH_THROUGHPUT:
        fillThroughput();
        break;
    case GRAPH_RTT:
        fillRoundTripTime();
        break;
    case GRAPH_WSCALE:
        fillWindowScale();
        break;
    default:
        break;
    }
    sp->setEnabled(true);

    stream_desc_ = tr("%1 %2 pkts, %3 %4 %5 pkts, %6 ")
            .arg(UTF8_RIGHTWARDS_ARROW)
            .arg(gchar_free_to_qstring(format_size(pkts_fwd, format_size_unit_none|format_size_prefix_si)))
            .arg(gchar_free_to_qstring(format_size(bytes_fwd, format_size_unit_bytes|format_size_prefix_si)))
            .arg(UTF8_LEFTWARDS_ARROW)
            .arg(gchar_free_to_qstring(format_size(pkts_rev, format_size_unit_none|format_size_prefix_si)))
            .arg(gchar_free_to_qstring(format_size(bytes_rev, format_size_unit_bytes|format_size_prefix_si)));
    mouseMoved(NULL);
    resetAxes();
    tracer_->setGraph(base_graph_);

    // XXX QCustomPlot doesn't seem to draw any sort of focus indicator.
    sp->setFocus();
}