Skip to content

Commit

Permalink
Merge pull request #103 from davidgiven/visualiser
Browse files Browse the repository at this point in the history
Add a simple disk visualiser.
  • Loading branch information
davidgiven authored Aug 27, 2019
2 parents f8b6d5e + 99335a8 commit 9923d67
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 4 deletions.
15 changes: 15 additions & 0 deletions doc/using.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,21 @@ Commands which normally take `--source` or `--dest` get a sensible default if
left unspecified. `fluxengine read ibm` on its own will read drive 0 and
write an `ibm.img` file.
## Visualisation
When doing a read (either from a real disk or from a flux file) you can use
`--write-svg=output.svg` to write out a graphical visualisation of where the
sectors are on the disk. Here's a IBM PC 1232kB disk:
![A disk visualisation](./visualiser.svg)
Blue represents data, light blue a header, and red is a bad sector. Side zero
is on the left and side one is on the right.
The visualiser is extremely primitive and you have to explicitly tell it how
big your disk is, in milliseconds. The default is 200ms (for a normal 3.5"
disk). For a 5.25" disk, use `--visualiser-period=166`.
## Extra programs
Supplied with FluxEngine, but not part of FluxEngine, are some little tools I
Expand Down
1 change: 1 addition & 0 deletions doc/visualiser.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions lib/decoders/decoders.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,25 @@ void AbstractDecoder::decodeToSectors(Track& track)

recordStart = fmr.tell();
decodeSectorRecord();
pushRecord(recordStart, fmr.tell());
Fluxmap::Position recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
if (sector.status == Sector::DATA_MISSING)
{
/* The data is in a separate record. */

sector.headerStartTime = recordStart.ns();
sector.headerEndTime = recordEnd.ns();
r = advanceToNextRecord();
if (r == DATA_RECORD)
{
recordStart = fmr.tell();
decodeDataRecord();
pushRecord(recordStart, fmr.tell());
recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
}
}
sector.dataStartTime = recordStart.ns();
sector.dataEndTime = recordEnd.ns();

if (sector.status != Sector::MISSING)
track.sectors.push_back(sector);
Expand Down
13 changes: 11 additions & 2 deletions lib/reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "decoders/decoders.h"
#include "sector.h"
#include "sectorset.h"
#include "visualiser.h"
#include "record.h"
#include "image.h"
#include "bytes.h"
Expand All @@ -17,7 +18,7 @@
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"

FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags };
FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags, &visualiserFlags };

static DataSpecFlag source(
{ "--source", "-s" },
Expand All @@ -34,6 +35,11 @@ static StringFlag destination(
"write the raw magnetic flux to this file",
"");

static StringFlag visualise(
{ "--write-svg" },
"write a visualisation of the disk to this file",
"");

static SettableFlag justRead(
{ "--just-read" },
"just read the disk and do no further processing");
Expand Down Expand Up @@ -143,7 +149,7 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
return;
}
}
if (!replacing || (replacing->status != Sector::OK))
if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK)))
{
if (!replacing)
replacing.reset(new Sector);
Expand Down Expand Up @@ -253,6 +259,9 @@ void readDiskCommand(AbstractDecoder& decoder)
std::cout << size << " bytes decoded." << std::endl;
}

if (!visualise.get().empty())
visualiseSectorsToFile(allSectors, visualise.get());

writeSectorsToFile(allSectors, outputSpec);
if (failures)
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
Expand Down
4 changes: 4 additions & 0 deletions lib/sector.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class Sector
Status status = Status::INTERNAL_ERROR;
Fluxmap::Position position;
nanoseconds_t clock = 0;
nanoseconds_t headerStartTime = 0;
nanoseconds_t headerEndTime = 0;
nanoseconds_t dataStartTime = 0;
nanoseconds_t dataEndTime = 0;
int physicalTrack = 0;
int physicalSide = 0;
int logicalTrack = 0;
Expand Down
3 changes: 3 additions & 0 deletions lib/sectorset.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class SectorSet
std::unique_ptr<Sector>& get(int track, int head, int sector);
Sector* get(int track, int head, int sector) const;

const std::map<const key_t, std::unique_ptr<Sector>>& get() const
{ return _data; }

void calculateSize(
unsigned& numTracks, unsigned& numHeads, unsigned& numSectors,
unsigned& sectorSize) const;
Expand Down
95 changes: 95 additions & 0 deletions lib/visualiser.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#define _USE_MATH_DEFINES
#include "globals.h"
#include "image.h"
#include "sector.h"
#include "sectorset.h"
#include "visualiser.h"
#include "fmt/format.h"
#include "flags.h"
#include <iostream>
#include <fstream>
#include <math.h>

FlagGroup visualiserFlags;

static IntFlag period(
{ "--visualiser-period" },
"rotational period for use by the visualiser (milliseconds)",
200);

static const int SIZE = 480;
static const int BORDER = 10;
static const int RADIUS = (SIZE/2) - (BORDER/2);
static const int CORE = 50;
static const int TRACKS = 83;
static const double TRACK_SPACING = double(RADIUS-CORE) / TRACKS;

void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename)
{
std::cout << "writing visualisation\n";
std::ofstream f(filename, std::ios::out);
if (!f.is_open())
Error() << "cannot open visualisation file";

f << fmt::format("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{0} {1} {2} {3}\">",
0, 0, SIZE*2, SIZE);

const double radians_per_ns = 2.0*M_PI / (period*1e6);

auto drawSide = [&](int side)
{
f << fmt::format("<g transform='matrix(1 0 0 -1 {} {})'>", SIZE/2 + (side*SIZE), SIZE/2);
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='none' fill='#ccc'/>", RADIUS);

for (int physicalTrack = 0; physicalTrack < TRACKS; physicalTrack++)
{
double radius = CORE + physicalTrack*TRACK_SPACING;
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='#888' stroke-width='0.5' fill='none'/>", radius);

auto drawArc = [&](const std::unique_ptr<Sector>& sector, nanoseconds_t start, nanoseconds_t end, const std::string& colour)
{
start %= period*1000000;
end %= period*1000000;
if (end < start)
end += period*1000000;

double theta1 = start * radians_per_ns;
double theta2 = end * radians_per_ns;
int large = (theta2 - theta1) >= M_PI;

f << fmt::format("\n<!-- {} {} = {} {} -->", start, end, theta1, theta2);
f << fmt::format("<path fill='none' stroke='{}' stroke-width='1.5' d='", colour);
f << fmt::format("M {} {} ", cos(theta1)*radius, sin(theta1)*radius);
f << fmt::format("A {0} {0} 0 {3} 1 {1} {2}", radius, cos(theta2)*radius, sin(theta2)*radius, large);
f << fmt::format("'><title>Track {} Head {} Sector {}; {}ms to {}ms</title></path>",
sector->logicalTrack, sector->logicalSide, sector->logicalSector,
start/1e6, end/1e6);
};

/* Sadly, SectorSets aren't indexable by physical track. */
for (const auto& e : sectors.get())
{
const auto& sector = e.second;
if ((sector->physicalSide == side) && (sector->physicalTrack == physicalTrack))
{
const char* colour = "#f00";
if (sector->status == Sector::OK)
colour = "#00f";
if (sector->headerStartTime && sector->headerEndTime)
drawArc(sector, sector->headerStartTime, sector->headerEndTime, "#0ff");
if (sector->dataStartTime && sector->dataEndTime)
drawArc(sector, sector->dataStartTime, sector->dataEndTime, colour);
}
}
}

f << "</g>";
};

f << fmt::format("<rect x='0' y='0' width='{}' height='{}' stroke='none' fill='#fff'/>", SIZE*2, SIZE);

drawSide(0);
drawSide(1);

f << "</svg>";
}
12 changes: 12 additions & 0 deletions lib/visualiser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef VISUALISER_H
#define VISUALISER_H

#include "flags.h"

class SectorSet;

extern FlagGroup visualiserFlags;

extern void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename);

#endif
1 change: 1 addition & 0 deletions mkninja.sh
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ buildlibrary libbackend.a \
lib/sectorset.cc \
lib/sql.cc \
lib/usb.cc \
lib/visualiser.cc \
lib/writer.cc \

buildlibrary libfrontend.a \
Expand Down

0 comments on commit 9923d67

Please sign in to comment.