TEM Viewer

Log-log plot with measured vs modeled data, station switching, and scale options.

TEM Decay Curves

Source Code

#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
#include <cmath>
#include <cstdlib>

#include "AZPlottingClass.h"

void loadTEMData(AZPlottingClass *plot, int stationIndex)
{
    QVector<double> time, measured, model;
    
    double baseAmplitude = 1000 + stationIndex * 200;
    double tau = 0.02 + stationIndex * 0.005;
    
    for (double t = 0.01; t <= 100; t *= 1.08) {
        time.append(t);
        
        // Measured with noise
        double r = baseAmplitude * std::exp(-t / 1000.0 / tau);
        r *= (1 + 0.05 * (std::rand() % 100 - 50) / 50.0);
        measured.append(r);
        
        // Clean model
        model.append(baseAmplitude * std::exp(-t / 1000.0 / tau));
    }
    
    plot->clearData();
    plot->clearMarkerLines();
    
    // Measured data with markers
    plot->addLineMarkerSeries(time, measured, "Measured", Qt::blue,
                              AZPlottingClass::LineStyle::None,
                              AZPlottingClass::MarkerStyle::Circle,
                              1.5, 5.0);
    
    // Model line (dashed)
    plot->addDataSeries(time, model, "Model", Qt::red,
                        AZPlottingClass::LineStyle::Dashed, 1.5);
    
    // Noise floor reference
    plot->addHorizontalLine(1.0, Qt::gray, "Noise Floor", 0.5);
    
    plot->setTitle(QString("TEM Decay - Station %1").arg((stationIndex + 1) * 100));
    plot->autoScaleAll();
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    QWidget window;
    window.setWindowTitle("TEM Decay Viewer");
    window.resize(800, 550);
    
    QVBoxLayout *mainLayout = new QVBoxLayout(&window);
    mainLayout->setContentsMargins(10, 10, 10, 10);
    
    // Control bar
    QHBoxLayout *controlLayout = new QHBoxLayout();
    
    QLabel *label = new QLabel("Station:");
    QComboBox *stationCombo = new QComboBox();
    stationCombo->addItems({"Station 100", "Station 200", "Station 300", "Station 400"});
    
    QLabel *scaleLabel = new QLabel("Y Scale:");
    QComboBox *scaleCombo = new QComboBox();
    scaleCombo->addItems({"Logarithmic", "Linear", "SymLog"});
    
    QPushButton *resetBtn = new QPushButton("Reset View");
    
    controlLayout->addWidget(label);
    controlLayout->addWidget(stationCombo);
    controlLayout->addSpacing(20);
    controlLayout->addWidget(scaleLabel);
    controlLayout->addWidget(scaleCombo);
    controlLayout->addStretch();
    controlLayout->addWidget(resetBtn);
    
    mainLayout->addLayout(controlLayout);
    
    // Plot
    AZPlottingClass *plot = new AZPlottingClass(&window);
    plot->setXAxisScale(AZPlottingClass::ScaleType::Logarithmic);
    plot->setYAxisScale(AZPlottingClass::ScaleType::Logarithmic);
    plot->setXAxisLabel("Time (ms)");
    plot->setYAxisLabel("dB/dt (nV/m²)");
    plot->applyProfessionalStyle();
    plot->setInteractive(true, true);
    plot->setCrosshairEnabled(true);
    plot->setStatsVisible(true);
    plot->setStatsPosition(Qt::AlignBottom | Qt::AlignLeft);
    
    mainLayout->addWidget(plot, 1);
    
    loadTEMData(plot, 0);
    
    // Connect station selector
    QObject::connect(stationCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
                     [plot](int idx) { loadTEMData(plot, idx); });
    
    // Connect scale selector
    QObject::connect(scaleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
                     [plot](int idx) {
        switch (idx) {
            case 0: plot->setYAxisScale(AZPlottingClass::ScaleType::Logarithmic); break;
            case 1: plot->setYAxisScale(AZPlottingClass::ScaleType::Linear); break;
            case 2: plot->setYAxisScale(AZPlottingClass::ScaleType::SymLog); break;
        }
        plot->autoScaleAll();
    });
    
    QObject::connect(resetBtn, &QPushButton::clicked, [plot]() { plot->resetZoom(); });
    
    window.show();
    return app.exec();
}