First actual disk visualisations!

This commit is contained in:
David Given
2024-01-18 01:01:54 +01:00
parent 9e2d6f890f
commit 4faf936a21
9 changed files with 256 additions and 141 deletions

View File

@@ -70,6 +70,7 @@ std::shared_ptr<TrackDataFlux> Decoder::decodeToSectors(
_trackdata = std::make_shared<TrackDataFlux>();
_trackdata->fluxmap = fluxmap;
_trackdata->trackInfo = trackInfo;
_trackdata->rotationalPeriod = globalConfig()->drive().rotational_period_ms() * 1e6;
FluxmapReader fmr(*fluxmap);
_fmr = &fmr;

View File

@@ -23,6 +23,7 @@ struct TrackDataFlux
std::shared_ptr<const Fluxmap> fluxmap;
std::vector<std::shared_ptr<const Record>> records;
std::vector<std::shared_ptr<const Sector>> sectors;
nanoseconds_t rotationalPeriod;
};
struct TrackFlux

View File

@@ -21,7 +21,6 @@ public:
_fluxVisualiserWidget = FluxVisualiserWidget::create();
_mainWindow->fluxViewContainer->layout()->addWidget(
_fluxVisualiserWidget);
_fluxVisualiserWidget->refresh();
connect(_mainWindow->fluxSideComboBox,
QOverload<int>::of(&QComboBox::activated),
@@ -29,6 +28,17 @@ public:
&FluxVisualiserWidget::setVisibleSide);
}
public:
void setTrackData(std::shared_ptr<const TrackFlux> track)
{
_fluxVisualiserWidget->setTrackData(track);
}
void setDiskData(std::shared_ptr<const DiskFlux> disk)
{
_fluxVisualiserWidget->setDiskData(disk);
}
private:
MainWindow* _mainWindow;
FluxVisualiserWidget* _fluxVisualiserWidget;

View File

@@ -4,6 +4,10 @@ class MainWindow;
class FluxComponent
{
public:
virtual void setTrackData(std::shared_ptr<const TrackFlux> track) = 0;
virtual void setDiskData(std::shared_ptr<const DiskFlux> disk) = 0;
public:
static FluxComponent* create(MainWindow* mainWindow);
};

View File

@@ -3,21 +3,26 @@
#include "lib/fluxmap.h"
#include "lib/flux.h"
#include "lib/layout.h"
#include "lib/decoders/fluxmapreader.h"
#include "globals.h"
#include "fluxvisualiserwidget.h"
#include <QWheelEvent>
#include <QFrame>
#include <QConicalGradient>
#include <range/v3/all.hpp>
W_OBJECT_IMPL(FluxVisualiserWidget)
class DiskFlux;
class TrackFlux;
static const int TRACKS = 82;
static constexpr int SIDES = 2;
static constexpr int TRACKS = 82;
static constexpr int SLOTS = 300;
static const float VBORDER = 1.0;
static const float VOUTER_RADIUS = 50.0;
static const float VINNER_RADIUS = 10.0;
static constexpr float VBORDER = 1.0;
static constexpr float VOUTER_RADIUS = 50.0;
static constexpr float VINNER_RADIUS = 10.0;
class FluxVisualiserWidgetImpl : public FluxVisualiserWidget
{
@@ -36,6 +41,8 @@ public:
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFrameShape(QFrame::NoFrame);
setScene(_scene);
recompute();
}
public:
@@ -48,7 +55,7 @@ public:
void setVisibleSide(int mode)
{
_viewMode = mode;
refresh();
redraw();
}
void setTrackData(std::shared_ptr<const TrackFlux> track)
@@ -56,35 +63,46 @@ public:
key_t key = {
track->trackInfo->physicalTrack, track->trackInfo->physicalSide};
_tracks[key] = track;
recompute(
track->trackInfo->physicalSide, track->trackInfo->physicalTrack);
}
void setDiskData(std::shared_ptr<const DiskFlux> disk)
{
_tracks.clear();
for (const auto& track : disk->tracks)
{
key_t key = {track->trackInfo->physicalTrack,
track->trackInfo->physicalSide};
_tracks[key] = track;
}
recompute();
}
public:
void refresh() override
private:
void redraw()
{
_scene->clear();
switch (_viewMode)
{
case SIDE_0:
drawSide(VOUTER_RADIUS, VOUTER_RADIUS, 0);
_scene->setSceneRect(
0.0, 0.0, VOUTER_RADIUS * 2, VOUTER_RADIUS * 2);
break;
case SIDE_1:
drawSide(VOUTER_RADIUS, VOUTER_RADIUS);
drawSide(VOUTER_RADIUS, VOUTER_RADIUS, 1);
_scene->setSceneRect(
0.0, 0.0, VOUTER_RADIUS * 2, VOUTER_RADIUS * 2);
break;
case BOTH_SIDES:
drawSide(VOUTER_RADIUS, VOUTER_RADIUS);
drawSide(VOUTER_RADIUS, VOUTER_RADIUS * 3 + VBORDER);
drawSide(VOUTER_RADIUS, VOUTER_RADIUS, 0);
drawSide(VOUTER_RADIUS, VOUTER_RADIUS * 3 + VBORDER, 1);
_scene->setSceneRect(
0.0, 0.0, VOUTER_RADIUS * 2, VOUTER_RADIUS * 4 + VBORDER);
@@ -94,26 +112,145 @@ public:
fitInView(sceneRect(), Qt::KeepAspectRatio);
}
private:
void drawSide(float x, float y)
void drawSide(float x, float y, int side)
{
QPen black(QColorConstants::Black);
black.setWidth(0);
float step = (VOUTER_RADIUS - VINNER_RADIUS) / TRACKS;
for (int track = 0; track < 82; track++)
{
QConicalGradient gradient(x, y, 0);
gradient.setStops(_viewData[side][track].gradientStops);
QBrush brush(gradient);
QPen pen(brush, step * 1.15);
float r = VOUTER_RADIUS - track * step;
_scene->addEllipse(x - r, y - r, r * 2, r * 2, black);
_scene->addEllipse(x - r, y - r, r * 2, r * 2, pen);
}
}
void recompute()
{
for (int side = 0; side < SIDES; side++)
for (int track = 0; track < TRACKS; track++)
recompute(side, track);
}
void recompute(int side, int track)
{
VData& vdata = _viewData[side][track];
QGradientStops& stops = vdata.gradientStops;
stops.clear();
ranges::fill(vdata.slot, VSlot());
auto it = _tracks.find(key_t(track, side));
if (it != _tracks.end())
{
const TrackFlux& trackFlux = *it->second;
for (auto& trackDataFlux : trackFlux.trackDatas)
{
nanoseconds_t rotationalPeriod =
trackDataFlux->rotationalPeriod;
if (rotationalPeriod == 0.0)
rotationalPeriod = 200e6;
const Fluxmap& fm = *trackDataFlux->fluxmap;
FluxmapReader fmr(fm);
fmr.seekToIndexMark();
nanoseconds_t indexTimeNs = fmr.tell().ns();
fmr.rewind();
int slotIndex = -1;
for (;;)
{
int event;
unsigned ticks;
fmr.getNextEvent(event, ticks);
if (event & F_BIT_PULSE)
{
nanoseconds_t ns = fmr.tell().ns();
while (ns < indexTimeNs)
ns += rotationalPeriod;
ns = fmod(ns - indexTimeNs, rotationalPeriod);
int newIndex = (ns / rotationalPeriod) * SLOTS;
if (slotIndex != -1)
{
while (
(slotIndex < newIndex) && (slotIndex < SLOTS))
{
vdata.slot[slotIndex].count++;
slotIndex++;
}
}
vdata.slot[newIndex].pulses++;
slotIndex = newIndex;
}
if (event & F_EOF)
break;
}
if (slotIndex != -1)
vdata.slot[slotIndex].count++;
}
float maxDensity = 0.0;
for (int i = 0; i < SLOTS; i++)
{
VSlot& slot = vdata.slot[i];
if (slot.count != 0)
{
float factor = (float)slot.pulses / (float)slot.count;
maxDensity = std::max(maxDensity, factor);
}
}
maxDensity = 300;
for (int i = 0; i < SLOTS; i++)
{
VSlot& slot = vdata.slot[i];
if (slot.count == 0)
stops.append(
QPair((float)i / SLOTS, QColorConstants::LightGray));
else
{
float factor =
(float)slot.pulses / (float)slot.count / maxDensity;
int c = factor * 255.0;
stops.append(QPair((float)i / SLOTS, QColor(c, c, c)));
}
}
}
if (stops.empty())
{
stops.append(QPair(0.0, QColorConstants::LightGray));
stops.append(QPair(1.0, QColorConstants::LightGray));
}
redraw();
}
private:
typedef std::pair<unsigned, unsigned> key_t;
struct VSlot
{
int count;
int pulses;
};
struct VData
{
QGradientStops gradientStops;
VSlot slot[SLOTS];
};
QGraphicsScene* _scene;
std::map<key_t, std::shared_ptr<const TrackFlux>> _tracks;
int _viewMode = BOTH_SIDES;
VData _viewData[SIDES][TRACKS];
};
FluxVisualiserWidget* FluxVisualiserWidget::create()

View File

@@ -7,9 +7,6 @@ class FluxVisualiserWidget : public QGraphicsView
W_OBJECT(FluxVisualiserWidget)
public:
virtual void refresh() = 0;
W_SLOT(refresh)
virtual void setVisibleSide(int mode) = 0;
W_SLOT(setVisibleSide)

View File

@@ -9,7 +9,12 @@
#include <QThreadPool>
#include <QtConcurrent>
class TrackFlux;
class DiskFlux;
Q_DECLARE_METATYPE(const ConfigProto*)
W_REGISTER_ARGTYPE(std::shared_ptr<const TrackFlux>)
W_REGISTER_ARGTYPE(std::shared_ptr<const DiskFlux>)
extern QThreadPool workerThreadPool;

View File

@@ -37,15 +37,6 @@ public:
&QAbstractButton::clicked,
this,
&MainWindowImpl::readDisk);
connect(revolutionsSlider,
&QSlider::valueChanged,
revolutionsSpinBox,
&QSpinBox::setValue);
connect(revolutionsSpinBox,
QOverload<int>::of(&QSpinBox::valueChanged),
revolutionsSlider,
&QSlider::setValue);
}
public:
@@ -56,6 +47,18 @@ public:
{
},
/* A track has been read. */
[&](const TrackReadLogMessage& m)
{
_fluxComponent->setTrackData(m.track);
},
/* A complete disk has been read. */
[&](const DiskReadLogMessage& m)
{
_fluxComponent->setDiskData(m.disk);
},
/* Large-scale operation start. */
[this](const BeginOperationLogMessage& m)
{

View File

@@ -347,118 +347,6 @@
<string>Flux view</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="radioButton_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>By index pulse</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>By sector number</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Revolution:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="revolutionsSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksAbove</enum>
</property>
<property name="tickInterval">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="revolutionsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Alignment:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QFrame" name="fluxViewContainer">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">QFrame {
background: white;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12"/>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0">
<item>
@@ -498,6 +386,75 @@ background: white;
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QFrame" name="fluxViewContainer">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">QFrame {
background: white;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12"/>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Alignment:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="radioButton_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>By index pulse</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>By sector number</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">