Rework the flux decoders to add a desync opcode for separating multiple reads

in a flux stream. Finally add a unit test for the flux decoder.
This commit is contained in:
dg 2021-12-05 14:42:57 +00:00
parent 298f77f52e
commit 18d90c44dd
21 changed files with 210 additions and 47 deletions

View file

@ -30,7 +30,7 @@ public:
_config(config.amiga())
{}
RecordType advanceToNextRecord()
RecordType advanceToNextRecord() override
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
@ -38,7 +38,7 @@ public:
return SECTOR_RECORD;
}
void decodeSectorRecord()
void decodeSectorRecord() override
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))

View file

@ -144,7 +144,7 @@ public:
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
int logicalTrack;
if (physicalSide != 0)

View file

@ -231,7 +231,7 @@ public:
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
/* The format ID Character # 1 and # 2 are in the .d64 image only present
* in track 18 sector zero which contains the BAM info in byte 162 and 163.

View file

@ -98,7 +98,7 @@ public:
_config(config.ibm())
{}
RecordType advanceToNextRecord()
RecordType advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@ -130,7 +130,7 @@ public:
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
void decodeSectorRecord() override
{
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16);
@ -156,7 +156,7 @@ public:
_sector->logicalTrack = _sector->physicalCylinder;
}
void decodeDataRecord()
void decodeDataRecord() override
{
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16);

View file

@ -74,7 +74,7 @@ public:
{}
/* Search for FM or MFM sector record */
RecordType advanceToNextRecord()
RecordType advanceToNextRecord() override
{
nanoseconds_t now = _fmr->tell().ns();
@ -140,7 +140,7 @@ public:
return UNKNOWN_RECORD;
}
void decodeSectorRecord()
void decodeSectorRecord() override
{
unsigned recordSize, payloadSize, headerSize;

View file

@ -162,7 +162,7 @@ public:
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);

View file

@ -84,7 +84,7 @@ std::unique_ptr<TrackDataFlux> AbstractDecoder::decodeToSectors(
return std::move(_trackdata);
if ((r == UNKNOWN_RECORD) || (r == DATA_RECORD))
{
fmr.findEvent(F_BIT_PULSE);
fmr.skipToEvent(F_BIT_PULSE);
continue;
}
@ -106,7 +106,7 @@ std::unique_ptr<TrackDataFlux> AbstractDecoder::decodeToSectors(
r = advanceToNextRecord();
if (r != UNKNOWN_RECORD)
break;
if (fmr.findEvent(F_BIT_PULSE) == 0)
if (fmr.eof())
break;
}
recordStart = fmr.tell();

View file

@ -18,7 +18,7 @@ FluxmapReader::FluxmapReader(const Fluxmap& fluxmap):
rewind();
}
uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
void FluxmapReader::getNextEvent(int& event, unsigned& ticks)
{
ticks = 0;
@ -26,30 +26,42 @@ uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
{
uint8_t b = _bytes[_pos.bytes++];
ticks += b & 0x3f;
if (b & (F_BIT_PULSE|F_BIT_INDEX))
if (!b || (b & (F_BIT_PULSE|F_BIT_INDEX)))
{
_pos.ticks += ticks;
return b;
event = b & 0xc0;
return;
}
}
_pos.ticks += ticks;
return 0;
event = F_EOF;
}
unsigned FluxmapReader::findEvent(uint8_t target)
void FluxmapReader::skipToEvent(int event)
{
unsigned ticks = 0;
unsigned ticks;
findEvent(event, ticks);
}
bool FluxmapReader::findEvent(int event, unsigned& ticks)
{
ticks = 0;
for (;;)
{
unsigned thisTicks;
uint8_t bits = getNextEvent(thisTicks);
ticks += thisTicks;
int thisEvent;
getNextEvent(thisEvent, thisTicks);
ticks += thisTicks;
if (thisEvent == F_EOF)
return false;
if (eof())
return 0;
if (bits & target)
return ticks;
return false;
if ((event == thisEvent) || (event & thisEvent))
return true;
}
}
@ -60,8 +72,8 @@ unsigned FluxmapReader::readInterval(nanoseconds_t clock)
while (ticks <= thresholdTicks)
{
unsigned thisTicks = findEvent(F_BIT_PULSE);
if (!thisTicks)
unsigned thisTicks;
if (!findEvent(F_BIT_PULSE, thisTicks))
break;
ticks += thisTicks;
}
@ -183,8 +195,9 @@ void FluxmapReader::seek(nanoseconds_t ns)
while (!eof() && (_pos.ticks < ticks))
{
int e;
unsigned t;
getNextEvent(t);
getNextEvent(e, t);
}
_pos.zeroes = 0;
}
@ -225,7 +238,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
positions[i] = positions[i+1];
candidates[i] = candidates[i+1];
}
candidates[intervalCount] = findEvent(F_BIT_PULSE);
findEvent(F_BIT_PULSE, candidates[intervalCount]);
positions[intervalCount] = tell();
}
@ -236,7 +249,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
void FluxmapReader::seekToIndexMark()
{
findEvent(F_BIT_INDEX);
skipToEvent(F_BIT_INDEX);
_pos.zeroes = 0;
}

View file

@ -93,8 +93,9 @@ public:
return (_fluxmap.duration());
}
uint8_t getNextEvent(unsigned& ticks);
unsigned findEvent(uint8_t bits);
void getNextEvent(int& event, unsigned& ticks);
void skipToEvent(int event);
bool findEvent(int event, unsigned& ticks);
unsigned readInterval(nanoseconds_t clock); /* with debounce support */
/* Important! You can only reliably seek to 1 bits. */

View file

@ -56,6 +56,12 @@ Fluxmap& Fluxmap::appendIndex()
return *this;
}
Fluxmap& Fluxmap::appendDesync()
{
appendByte(F_DESYNC);
return *this;
}
void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
{
uint8_t junk = 0xff;

View file

@ -46,6 +46,7 @@ public:
Fluxmap& appendInterval(uint32_t ticks);
Fluxmap& appendPulse();
Fluxmap& appendIndex();
Fluxmap& appendDesync();
Fluxmap& appendBytes(const Bytes& bytes);
Fluxmap& appendBytes(const uint8_t* ptr, size_t len);

View file

@ -68,14 +68,15 @@ public:
while (!fmr.eof())
{
unsigned ticks;
uint8_t bits = fmr.getNextEvent(ticks);
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
timestamp += ticks;
if (bits & F_BIT_PULSE)
if (event & F_BIT_PULSE)
data[timestamp*channels + 0] = 0x7f;
if (_config.index_markers() && (bits & F_BIT_INDEX))
if (_config.index_markers() && (event & F_BIT_INDEX))
data[timestamp*channels + 1] = 0x7f;
}

View file

@ -97,7 +97,7 @@ public:
ByteWriter fluxdataWriter(fluxdata);
if (_config.align_with_index())
fmr.findEvent(F_BIT_INDEX);
fmr.skipToEvent(F_BIT_INDEX);
int revolution = 0;
unsigned revTicks = 0;
@ -107,12 +107,14 @@ public:
while (revolution < 5)
{
unsigned ticks;
uint8_t bits = fmr.getNextEvent(ticks);
int event;
fmr.getNextEvent(event, ticks);
ticksSinceLastPulse += ticks;
totalTicks += ticks;
revTicks += ticks;
if (fmr.eof() || (bits & F_BIT_INDEX))
if (fmr.eof() || (event & F_BIT_INDEX))
{
auto* revheader = &trackheader.revolution[revolution];
write_le32(revheader->offset, startOffset + sizeof(ScpTrack));
@ -124,7 +126,7 @@ public:
startOffset = fluxdataWriter.pos;
}
if (bits & F_BIT_PULSE)
if (event & F_BIT_PULSE)
{
unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25;
while (t >= 0x10000)

View file

@ -43,7 +43,8 @@ public:
while (!fmr.eof())
{
unsigned ticks;
uint8_t bits = fmr.getNextEvent(ticks);
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
@ -55,9 +56,9 @@ public:
of << fmt::format("#{} ", (uint64_t)(timestamp * NS_PER_TICK));
}
if (bits & F_BIT_PULSE)
if (event & F_BIT_PULSE)
of << "1p ";
if (bits & F_BIT_INDEX)
if (event & F_BIT_INDEX)
of << "1i ";
lasttimestamp = timestamp;

View file

@ -30,8 +30,6 @@ static std::shared_ptr<Fluxmap> readFluxmap(FluxSource& fluxsource, unsigned cyl
"{0:.0} ms in {1} bytes\n",
fluxmap->duration()/1e6,
fluxmap->bytes());
if (outputFluxSink)
outputFluxSink->writeFlux(cylinder, head, *fluxmap);
return fluxmap;
}
@ -83,10 +81,13 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
auto track = std::make_unique<TrackFlux>();
std::set<std::shared_ptr<Sector>> track_sectors;
std::set<std::shared_ptr<Record>> track_records;
Fluxmap totalFlux;
for (int retry = config.decoder().retries(); retry >= 0; retry--)
{
auto fluxmap = readFluxmap(fluxsource, cylinder, head);
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
{
auto trackdata = decoder.decodeToSectors(fluxmap, cylinder, head);
@ -105,6 +106,7 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
track_records.insert(trackdata->records.begin(), trackdata->records.end());
track->trackDatas.push_back(std::move(trackdata));
}
auto collected_sectors = collect_sectors(track_sectors);
std::cout << fmt::format("{} distinct sectors; ", collected_sectors.size());
@ -147,6 +149,9 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
std::cout << retry << " retries remaining" << std::endl;
}
if (outputFluxSink)
outputFluxSink->writeFlux(cylinder, head, totalFlux);
if (config.decoder().dump_records())
{
std::cout << "\nRaw (undecoded) records follow:\n\n";

View file

@ -511,6 +511,7 @@ runtest bytes-test tests/bytes.cc
runtest compression-test tests/compression.cc
runtest csvreader-test tests/csvreader.cc
runtest flags-test tests/flags.cc
runtest fluxmapreader-test tests/fluxmapreader.cc
runtest fluxpattern-test tests/fluxpattern.cc
runtest fmmfm-test tests/fmmfm.cc
runtest greaseweazle-test tests/greaseweazle.cc

View file

@ -89,7 +89,8 @@ enum
{
F_BIT_PULSE = 0x80,
F_BIT_INDEX = 0x40,
F_DESYNC = 0xc0
F_DESYNC = 0x00,
F_EOF = 0x100 /* synthetic, only produced by library */
};
struct frame_header

View file

@ -257,10 +257,11 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
FluxmapReader fmr(inFluxmap);
fmr.seek((double)period*0.1); /* skip first 10% and last 10% as contains junk */
fmr.findEvent(F_BIT_PULSE);
fmr.skipToEvent(F_BIT_PULSE);
while (fmr.tell().ns() < ((double)period*0.9))
{
unsigned ticks = fmr.findEvent(F_BIT_PULSE);
unsigned ticks;
fmr.findEvent(F_BIT_PULSE, ticks);
if (ticks < numColumns)
frequencies[row][ticks]++;
}

View file

@ -92,7 +92,8 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap)
while (!fr.eof())
{
unsigned interval = fr.findEvent(F_BIT_PULSE);
unsigned interval;
fr.findEvent(F_BIT_PULSE, interval);
if (interval > 0xff)
continue;
buckets[interval]++;
@ -234,7 +235,9 @@ int mainInspect(int argc, const char* argv[])
nanoseconds_t lasttransition = 0;
while (!fmr.eof())
{
ticks += fmr.findEvent(F_BIT_PULSE);
unsigned thisTicks;
fmr.findEvent(F_BIT_PULSE, thisTicks);
ticks += thisTicks;
nanoseconds_t transition = ticks*NS_PER_TICK;
nanoseconds_t next;

89
tests/fluxmapreader.cc Normal file
View file

@ -0,0 +1,89 @@
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "fmt/format.h"
#include "tests.h"
#include <sstream>
static Fluxmap fluxmap(Bytes {
F_DESYNC,
F_BIT_PULSE | 0x30,
F_BIT_INDEX | 0x30,
F_BIT_PULSE | F_BIT_INDEX | 0x30,
0x30, F_BIT_PULSE | 0x30,
F_DESYNC,
F_BIT_PULSE | 0x30,
0x30,
F_DESYNC,
0x30,
F_BIT_PULSE | 0x30
});
#define ASSERT_NEXT_EVENT(wantedEvent, wantedTicks) \
do { \
unsigned gotTicks; \
int gotEvent; \
fmr.getNextEvent(gotEvent, gotTicks); \
assert((gotEvent == (wantedEvent)) && (gotTicks == (wantedTicks))); \
} while(0)
#define ASSERT_READ_SPECIFIC_EVENT(wantedEvent, wantedTicks) \
do { \
unsigned gotTicks; \
fmr.findEvent(wantedEvent, gotTicks); \
assertThat(gotTicks).isEqualTo(wantedTicks); \
} while(0)
void test_read_all_events()
{
FluxmapReader fmr(fluxmap);
ASSERT_NEXT_EVENT(F_DESYNC, 0);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x30);
ASSERT_NEXT_EVENT(F_BIT_INDEX, 0x30);
ASSERT_NEXT_EVENT(F_BIT_PULSE | F_BIT_INDEX, 0x30);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x60);
ASSERT_NEXT_EVENT(F_DESYNC, 0);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x30);
ASSERT_NEXT_EVENT(F_DESYNC, 0x30);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x60);
ASSERT_NEXT_EVENT(F_EOF, 0);
ASSERT_NEXT_EVENT(F_EOF, 0);
}
void test_read_pulses()
{
FluxmapReader fmr(fluxmap);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x30);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x30);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x90);
}
void test_read_indices()
{
FluxmapReader fmr(fluxmap);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 0x30);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 6*0x30);
}
void test_read_desyncs()
{
FluxmapReader fmr(fluxmap);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0xf0);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0x60);
}
int main(int argc, const char* argv[])
{
test_read_all_events();
test_read_pulses();
test_read_indices();
test_read_desyncs();
return 0;
}

38
tests/tests.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef TESTS_H
#define TESTS_H
class AssertionError
{};
template <class T>
class Subject
{
public:
Subject(const std::string& filename, int lineno, T value):
_filename(filename),
_lineno(lineno),
_value(value) {}
public:
void isEqualTo(T wanted)
{
if (_value != wanted)
fail(fmt::format("wanted {}, got {}", wanted, _value));
}
private:
void fail(const std::string& message)
{
Error() << "assertion failed: " << _filename << ":" << _lineno << ": " << message;
}
private:
const std::string _filename;
int _lineno;
T _value;
};
#define assertThat(value) Subject(__FILE__, __LINE__, value)
#endif