Allow split decoders (into sector record and data record).

This commit is contained in:
David Given
2019-04-19 00:55:01 +02:00
parent b158692a3a
commit 5f8e0c846c
18 changed files with 375 additions and 194 deletions

View File

@@ -49,6 +49,9 @@ static const std::string BLOCK_ELEMENTS[] =
*/
nanoseconds_t Fluxmap::guessClock() const
{
if (manualClockRate != 0.0)
return manualClockRate * 1000.0;
uint32_t buckets[256] = {};
FluxmapReader fr(*this);
@@ -248,6 +251,7 @@ RawRecordVector AbstractSoftSectorDecoder::extractRecords(const RawBits& rawbits
if (matchStart == -1)
return;
#if 0
records.push_back(
std::unique_ptr<RawRecord>(
new RawRecord(
@@ -260,6 +264,7 @@ RawRecordVector AbstractSoftSectorDecoder::extractRecords(const RawBits& rawbits
)
)
);
#endif
};
while (cursor < rawbits.size())
@@ -315,6 +320,7 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
if ((end - previous) < sectors_must_be_bigger_than)
return;
#if 0
records.push_back(
std::unique_ptr<RawRecord>(
new RawRecord(
@@ -327,6 +333,7 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
)
)
);
#endif
previous = end;
};
@@ -338,4 +345,69 @@ RawRecordVector AbstractHardSectorDecoder::extractRecords(const RawBits& rawbits
return records;
}
void AbstractStatefulDecoder::decodeToSectors(Track& track)
{
Sector sector;
sector.physicalSide = track.physicalSide;
sector.physicalTrack = track.physicalTrack;
FluxmapReader fmr(*track.fluxmap);
for (;;)
{
nanoseconds_t clockPeriod = findSector(fmr, track);
if (fmr.eof() || !clockPeriod)
break;
sector.status = Sector::MISSING;
sector.data.clear();
sector.clock = clockPeriod;
_recordStart = sector.position = fmr.tell();
decodeSingleSector(fmr, track, sector);
pushRecord(fmr, track, sector);
if (sector.status != Sector::MISSING)
track.sectors.push_back(sector);
}
}
void AbstractStatefulDecoder::pushRecord(FluxmapReader& fmr, Track& track, Sector& sector)
{
RawRecord record;
record.physicalSide = track.physicalSide;
record.physicalTrack = track.physicalTrack;
record.clock = sector.clock;
record.position = _recordStart;
Fluxmap::Position here = fmr.tell();
fmr.seek(_recordStart);
record.data = toBytes(fmr.readRawBits(here, sector.clock));
track.rawrecords.push_back(record);
_recordStart = here;
}
void AbstractStatefulDecoder::discardRecord(FluxmapReader& fmr)
{
_recordStart = fmr.tell();
}
void AbstractSplitDecoder::decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector)
{
decodeHeader(fmr, track, sector);
if (sector.status == Sector::MISSING)
return;
pushRecord(fmr, track, sector);
nanoseconds_t clockPeriod = findData(fmr, track);
if (fmr.eof() || !clockPeriod)
return;
sector.clock = clockPeriod;
discardRecord(fmr);
Fluxmap::Position pos = fmr.tell();
decodeData(fmr, track, sector);
if (sector.status == Sector::DATA_MISSING)
{
fmr.seek(pos);
return;
}
}

View File

@@ -2,9 +2,12 @@
#define DECODERS_H
#include "bytes.h"
#include "sector.h"
#include "record.h"
class Sector;
class Fluxmap;
class FluxmapReader;
class RawRecord;
class RawBits;
class Track;
@@ -67,6 +70,26 @@ public:
class AbstractStatefulDecoder : public AbstractDecoder
{
public:
void decodeToSectors(Track& track);
void discardRecord(FluxmapReader& fmr);
void pushRecord(FluxmapReader& fmr, Track& track, Sector& sector);
protected:
virtual nanoseconds_t findSector(FluxmapReader& fmr, Track& track) = 0;
virtual void decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector) = 0;
private:
Fluxmap::Position _recordStart;
};
class AbstractSplitDecoder : public AbstractStatefulDecoder
{
void decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector) override;
virtual nanoseconds_t findData(FluxmapReader& fmr, Track& track) = 0;
virtual void decodeHeader(FluxmapReader& fmr, Track& track, Sector& sector) = 0;
virtual void decodeData(FluxmapReader& fmr, Track& track, Sector& sector) = 0;
};
#endif

View File

@@ -9,7 +9,7 @@
static DoubleFlag clockDecodeThreshold(
{ "--bit-error-threshold" },
"Amount of error to tolerate in pulse timing.",
0.20);
0.30);
int FluxmapReader::readOpcode(unsigned& ticks)
{
@@ -47,17 +47,17 @@ unsigned FluxmapReader::readNextMatchingOpcode(uint8_t opcode)
}
}
FluxPattern::FluxPattern(unsigned len, uint64_t pattern)
FluxPattern::FluxPattern(unsigned bits, uint64_t pattern):
FluxMatcher(bits)
{
const uint64_t TOPBIT = 1ULL << 63;
assert(pattern != 0);
bits = len;
while (!(pattern & TOPBIT))
pattern <<= 1;
length = 0;
_length = 0;
while (pattern != TOPBIT)
{
unsigned interval = 0;
@@ -67,59 +67,98 @@ FluxPattern::FluxPattern(unsigned len, uint64_t pattern)
interval++;
}
while (!(pattern & TOPBIT));
intervals.push_back(interval);
length += interval;
_intervals.push_back(interval);
_length += interval;
}
}
bool FluxPattern::matches(const unsigned* end, double& clock) const
unsigned FluxPattern::matches(const unsigned* end, double& clock) const
{
const unsigned* start = end - intervals.size();
const unsigned* start = end - _intervals.size();
unsigned candidatelength = std::accumulate(start, end, 0);
if (!candidatelength)
return false;
clock = (double)candidatelength / (double)length;
return 0;
clock = (double)candidatelength / (double)_length;
for (unsigned i=0; i<intervals.size(); i++)
for (unsigned i=0; i<_intervals.size(); i++)
{
double ii = clock * (double)intervals[i];
double ii = clock * (double)_intervals[i];
double ci = (double)start[i];
double error = fabs(1.0 - ii/ci);
if (error > clockDecodeThreshold)
return false;
return 0;
}
return true;
return _intervals.size();
}
nanoseconds_t FluxmapReader::seekToPattern(const FluxPattern& pattern)
FluxPatterns::FluxPatterns(unsigned bits, std::initializer_list<uint64_t> patterns):
FluxMatcher(bits)
{
unsigned length = pattern.intervals.size();
unsigned candidates[length];
Position positions[length];
_intervals = 0;
for (uint64_t p : patterns)
{
auto pattern = std::make_unique<FluxPattern>(bits, p);
_intervals = std::max(_intervals, pattern->intervals());
_patterns.push_back(std::move(pattern));
}
}
unsigned FluxPatterns::matches(const unsigned* intervals, double& clock) const
{
for (const auto& pattern : _patterns)
{
unsigned m = pattern->matches(intervals, clock);
if (m)
return m;
}
return 0;
}
void FluxmapReader::seek(nanoseconds_t ns)
{
unsigned ticks = ns / NS_PER_TICK;
if (ticks < _pos.ticks)
{
_pos.ticks = 0;
_pos.bytes = 0;
}
while (!eof() && (_pos.ticks < ticks))
{
unsigned t;
readOpcode(t);
}
}
nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern)
{
unsigned intervalCount = pattern.intervals();
unsigned candidates[intervalCount+1];
Fluxmap::Position positions[intervalCount+1];
for (unsigned& i : candidates)
i = 0;
for (Position& p : positions)
for (Fluxmap::Position& p : positions)
p = tell();
while (!eof())
{
unsigned interval = readNextMatchingOpcode(F_OP_PULSE);
Position current = positions[0];
for (unsigned i=0; i<(length-1); i++)
for (unsigned i=0; i<intervalCount; i++)
{
positions[i] = positions[i+1];
candidates[i] = candidates[i+1];
}
positions[length-1] = tell();
candidates[length-1] = interval;
positions[intervalCount] = tell();
candidates[intervalCount] = interval;
double clock;
if (pattern.matches(&candidates[length], clock))
unsigned m = pattern.matches(&candidates[intervalCount+1], clock);
if (m)
{
seek(current);
seek(positions[intervalCount-m]);
return clock * NS_PER_TICK;
}
}
@@ -154,3 +193,11 @@ std::vector<bool> FluxmapReader::readRawBits(unsigned count, nanoseconds_t clock
result.push_back(readRawBit(clockPeriod));
return result;
}
std::vector<bool> FluxmapReader::readRawBits(const Fluxmap::Position& until, nanoseconds_t clockPeriod)
{
std::vector<bool> result;
while (!eof() && (_pos.bytes < until.bytes))
result.push_back(readRawBit(clockPeriod));
return result;
}

View File

@@ -4,27 +4,62 @@
#include "fluxmap.h"
#include "protocol.h"
class FluxPattern
class FluxMatcher
{
public:
FluxPattern(unsigned len, uint64_t pattern);
FluxMatcher(unsigned bits):
_bits(bits)
{}
bool matches(const unsigned* intervals, double& clock) const;
virtual ~FluxMatcher() {}
unsigned bits;
std::vector<unsigned> intervals;
unsigned length;
/* Returns the number of intervals matched */
virtual unsigned matches(const unsigned* intervals, double& clock) const = 0;
virtual unsigned intervals() const = 0;
unsigned bits() const
{ return _bits; }
protected:
unsigned _bits;
};
class FluxPattern : public FluxMatcher
{
public:
FluxPattern(unsigned bits, uint64_t patterns);
unsigned matches(const unsigned* intervals, double& clock) const override;
unsigned intervals() const override
{ return _intervals.size(); }
private:
std::vector<unsigned> _intervals;
unsigned _length;
public:
friend void test_patternconstruction();
friend void test_patternmatching();
};
class FluxPatterns : public FluxMatcher
{
public:
FluxPatterns(unsigned bits, std::initializer_list<uint64_t> patterns);
unsigned matches(const unsigned* intervals, double& clock) const override;
unsigned intervals() const override
{ return _intervals; }
private:
unsigned _intervals;
std::vector<std::unique_ptr<FluxPattern>> _patterns;
};
class FluxmapReader
{
public:
struct Position
{
unsigned bytes;
unsigned ticks;
};
public:
FluxmapReader(const Fluxmap& fluxmap):
_fluxmap(fluxmap),
@@ -46,30 +81,28 @@ public:
bool eof() const
{ return _pos.bytes == _size; }
Position tell() const
Fluxmap::Position tell() const
{ return _pos; }
void seek(const Position& pos)
{ _pos = pos; }
nanoseconds_t tellNs() const
{ return _pos.ticks * NS_PER_TICK; }
void seek(const Fluxmap::Position& pos)
{ _pos = pos; _pendingZeroBits = 0; }
int readOpcode(unsigned& ticks);
unsigned readNextMatchingOpcode(uint8_t opcode);
void seek(nanoseconds_t ns);
void seekToIndexMark();
nanoseconds_t seekToPattern(const FluxPattern& pattern);
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
bool readRawBit(nanoseconds_t clockPeriod);
std::vector<bool> readRawBits(unsigned count, nanoseconds_t clockPeriod);
std::vector<bool> readRawBits(const Fluxmap::Position& until, nanoseconds_t clockPeriod);
private:
const Fluxmap& _fluxmap;
const uint8_t* _bytes;
const size_t _size;
Position _pos;
Fluxmap::Position _pos;
unsigned _pendingZeroBits;
};

View File

@@ -99,25 +99,14 @@ static uint16_t checksum(const Bytes& bytes)
return (crchi << 8) | crclo;
}
void Fb100Decoder::decodeToSectors(Track& track)
nanoseconds_t Fb100Decoder::findSector(FluxmapReader& fmr, Track& track)
{
Sector sector;
sector.physicalSide = track.physicalSide;
sector.physicalTrack = track.physicalTrack;
RawRecord record;
return fmr.seekToPattern(SECTOR_ID_PATTERN);
}
FluxmapReader fmr(*track.fluxmap);
for (;;)
void Fb100Decoder::decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector)
{
nanoseconds_t clockPeriod = fmr.seekToPattern(SECTOR_ID_PATTERN);
if (fmr.eof() || !clockPeriod)
break;
sector.clock = record.clock = clockPeriod;
sector.position = record.position = fmr.tellNs();
auto rawbits = fmr.readRawBits(FB100_RECORD_SIZE*16, clockPeriod);
record.bytes.writer().seekToEnd() += toBytes(rawbits);
auto rawbits = fmr.readRawBits(FB100_RECORD_SIZE*16, sector.clock);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
ByteReader br(bytes);
@@ -130,14 +119,16 @@ void Fb100Decoder::decodeToSectors(Track& track)
uint16_t gotPayloadCrc = checksum(payload);
if (wantIdCrc != gotIdCrc)
continue;
return;
uint8_t abssector = id[2];
sector.logicalTrack = abssector & 1;
sector.logicalTrack = abssector >> 1;
sector.logicalSide = 0;
sector.logicalSector = abssector & 1;
sector.data.clear().writer().append(id.slice(5, 12)).append(payload);
sector.status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
track.sectors.push_back(sector);
}
sector.data.writer().append(id.slice(5, 12)).append(payload);
if (wantPayloadCrc == gotPayloadCrc)
sector.status = Sector::OK;
else
sector.status = Sector::BAD_CHECKSUM;
}

View File

@@ -14,7 +14,8 @@ class Fb100Decoder : public AbstractStatefulDecoder
public:
virtual ~Fb100Decoder() {}
void decodeToSectors(Track& track) override;
nanoseconds_t findSector(FluxmapReader& fmr, Track& track) override;
void decodeSingleSector(FluxmapReader& fmr, Track& track, Sector& sector) override;
};
#endif

View File

@@ -2,11 +2,22 @@
#define FLUXMAP_H
#include "bytes.h"
#include "protocol.h"
class RawBits;
class Fluxmap
{
public:
struct Position
{
unsigned bytes = 0;
unsigned ticks = 0;
nanoseconds_t ns() const
{ return ticks * NS_PER_TICK; }
};
public:
nanoseconds_t duration() const { return _duration; }
size_t bytes() const { return _bytes.size(); }

View File

@@ -141,7 +141,7 @@ void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry,
if (sector)
{
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg);
outputFile.write((const char*) &sector->data[0], sector->data.size());
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
}
}
}

View File

@@ -12,7 +12,8 @@
#include <string.h>
#include <algorithm>
const FluxPattern SECTOR_ID_PATTERN(16, 0xd5aa);
const FluxPattern SECTOR_RECORD_PATTERN(24, MAC_SECTOR_RECORD);
const FluxPatterns SECTOR_OR_DATA_RECORD_PATTERN(24, { MAC_SECTOR_RECORD, MAC_DATA_RECORD });
static int decode_data_gcr(uint8_t gcr)
{
@@ -122,72 +123,57 @@ uint8_t decode_side(uint8_t side)
return !!(side & 0x40);
}
void MacintoshDecoder::decodeToSectors(Track& track)
nanoseconds_t MacintoshDecoder::findSector(FluxmapReader& fmr, Track& track)
{
Sector sector;
sector.physicalSide = track.physicalSide;
sector.physicalTrack = track.physicalTrack;
RawRecord record;
return fmr.seekToPattern(SECTOR_RECORD_PATTERN);
}
FluxmapReader fmr(*track.fluxmap);
nanoseconds_t MacintoshDecoder::findData(FluxmapReader& fmr, Track& track)
{
return fmr.seekToPattern(SECTOR_OR_DATA_RECORD_PATTERN);
}
bool headerIsValid = false;
for (;;)
void MacintoshDecoder::decodeHeader(FluxmapReader& fmr, Track& track, Sector& sector)
{
nanoseconds_t clockPeriod = fmr.seekToPattern(SECTOR_ID_PATTERN);
if (fmr.eof() || !clockPeriod)
break;
sector.clock = record.clock = clockPeriod;
sector.position = record.position = fmr.tellNs();
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
fmr.readRawBits(24, sector.clock);
Bytes idbytes = toBytes(fmr.readRawBits(24, clockPeriod)).slice(0, 3);
record.bytes.clear().writer() += idbytes;
switch (idbytes.reader().read_be24())
{
case MAC_SECTOR_RECORD:
{
auto header = toBytes(fmr.readRawBits(7*8, clockPeriod))
.slice(0, 7);
record.bytes.writer().seekToEnd() += header;
/* Read header. */
auto header = toBytes(fmr.readRawBits(7*8, sector.clock)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (track.physicalTrack & 0x3f))
break;
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
uint8_t encodedSide = decode_data_gcr(header[2]);
uint8_t formatByte = decode_data_gcr(header[3]);
uint8_t wantedsum = decode_data_gcr(header[4]);
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
headerIsValid = (wantedsum == gotsum);
sector.logicalTrack = track.physicalTrack;
sector.logicalSide = decode_side(encodedSide);
sector.logicalSector = encodedSector;
break;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
sector.status = Sector::DATA_MISSING; /* unintuitive but correct */
}
case MAC_DATA_RECORD:
void MacintoshDecoder::decodeData(FluxmapReader& fmr, Track& track, Sector& sector)
{
if (!headerIsValid)
break;
headerIsValid = false;
auto id = toBytes(fmr.readRawBits(24, sector.clock)).reader().read_be24();
if (id != MAC_DATA_RECORD)
return;
fmr.readRawBits(8, clockPeriod); /* skip spare byte */
auto inputbuffer = toBytes(fmr.readRawBits(MAC_ENCODED_SECTOR_LENGTH*8, clockPeriod))
/* Read data. */
fmr.readRawBits(8, sector.clock); /* skip spare byte */
auto inputbuffer = toBytes(fmr.readRawBits(MAC_ENCODED_SECTOR_LENGTH*8, sector.clock))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
record.bytes.writer().seekToEnd() += inputbuffer;
for (unsigned i=0; i<inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
sector.status = Sector::BAD_CHECKSUM;
sector.data = decode_crazy_data(inputbuffer, sector.status);
track.sectors.push_back(sector);
break;
}
}
track.rawrecords.push_back(record);
}
}

View File

@@ -1,8 +1,8 @@
#ifndef MACINTOSH_H
#define MACINTOSH_H
#define MAC_SECTOR_RECORD 0xd5aa96
#define MAC_DATA_RECORD 0xd5aaad
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
#define MAC_SECTOR_LENGTH 524 /* yes, really */
#define MAC_ENCODED_SECTOR_LENGTH 703
@@ -10,12 +10,15 @@
class Sector;
class Fluxmap;
class MacintoshDecoder : public AbstractStatefulDecoder
class MacintoshDecoder : public AbstractSplitDecoder
{
public:
virtual ~MacintoshDecoder() {}
void decodeToSectors(Track& track) override;
nanoseconds_t findSector(FluxmapReader& fmr, Track& track) override;
nanoseconds_t findData(FluxmapReader& fmr, Track& track) override;
void decodeHeader(FluxmapReader& fmr, Track& track, Sector& sector) override;
void decodeData(FluxmapReader& fmr, Track& track, Sector& sector) override;
};
#endif

View File

@@ -184,9 +184,9 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
std::cout << "\nRaw (undecoded) records follow:\n\n";
for (auto& record : track->rawrecords)
{
std::cout << fmt::format("I+{:.2f}us", record.clock / 1000.0)
std::cout << fmt::format("I+{:.2f}us", record.position.ns() / 1000.0)
<< std::endl;
hexdump(std::cout, record.bytes);
hexdump(std::cout, record.data);
std::cout << std::endl;
}
}

View File

@@ -1,20 +1,18 @@
#ifndef RECORD_H
#define RECORD_H
#include "fluxmap.h"
class RawRecord
{
public:
RawRecord() {}
RawRecord(nanoseconds_t position, nanoseconds_t clock, const Bytes& bytes):
position(position),
clock(clock),
bytes(bytes)
{}
nanoseconds_t position;
nanoseconds_t clock;
Bytes bytes;
Fluxmap::Position position;
nanoseconds_t clock = 0;
int physicalTrack = 0;
int physicalSide = 0;
Bytes data;
};
#endif

View File

@@ -9,6 +9,7 @@ const std::string Sector::statusToString(Status status)
case Status::OK: return "OK";
case Status::BAD_CHECKSUM: return "bad checksum";
case Status::MISSING: return "sector not found";
case Status::DATA_MISSING: return "present but no data found";
case Status::CONFLICT: return "conflicting data";
default: return fmt::format("unknown error {}", status);
}

View File

@@ -2,6 +2,7 @@
#define SECTOR_H
#include "bytes.h"
#include "fluxmap.h"
/*
* Note that sectors here used zero-based numbering throughout (to make the
@@ -16,6 +17,7 @@ public:
OK,
BAD_CHECKSUM,
MISSING,
DATA_MISSING,
CONFLICT,
INTERNAL_ERROR
};
@@ -23,7 +25,7 @@ public:
static const std::string statusToString(Status status);
Status status = Status::INTERNAL_ERROR;
nanoseconds_t position = 0;
Fluxmap::Position position;
nanoseconds_t clock = 0;
int physicalTrack = 0;
int physicalSide = 0;

View File

@@ -9,7 +9,7 @@
class Sector;
class Fluxmap;
class Victor9kDecoder : public AbstractSoftSectorDecoder
class Victor9kDecoder : public AbstractStatefulDecoder
{
public:
virtual ~Victor9kDecoder() {}

View File

@@ -53,6 +53,8 @@ int main(int argc, const char* argv[])
nanoseconds_t clockPeriod = track->fluxmap->guessClock();
std::cout << fmt::format(" {:.2f} us clock detected; ", (double)clockPeriod/1000.0) << std::flush;
FluxmapReader fmr(*track->fluxmap);
#if 0
clockPeriod *= clockScaleFlag;
std::cout << fmt::format("{:.2f} us bit clock; ", (double)clockPeriod/1000.0) << std::flush;
@@ -116,23 +118,24 @@ int main(int argc, const char* argv[])
lasttransition = transition;
}
}
#endif
if (dumpBitstreamFlag)
{
std::cout << "Aligned bitstream of length " << bitmap.size()
<< " follows:" << std::endl
<< std::endl;
fmr.seek(seekFlag*1000000.0);
size_t cursor = seekFlag*1000000.0 / clockPeriod;
while (cursor < bitmap.size())
std::cout << fmt::format("Aligned bitstream from {:.3f}ms follows:\n",
fmr.tell().ns() / 1000000.0);
while (!fmr.eof())
{
std::cout << fmt::format("{: 10.3f} : ", (double)cursor / clockPeriod);
std::cout << fmt::format("{: 10.3f} : ", fmr.tell().ns() / 1000000.0);
for (unsigned i=0; i<60; i++)
{
if (cursor >= bitmap.size())
if (fmr.eof())
break;
std::cout << (bitmap[cursor] ? 'X' : '-');
cursor++;
bool b = fmr.readRawBit(clockPeriod);
std::cout << (b ? 'X' : '-');
}
std::cout << std::endl;

View File

@@ -17,7 +17,7 @@ static StringFlag outputFilename(
int main(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-79:s=0");
setReaderDefaultSource(":t=0-79x2:s=0");
setReaderRevolutions(2);
setDecoderManualClockRate(4.0);
Flag::parseFlags(argc, argv);

View File

@@ -5,46 +5,46 @@
typedef std::vector<unsigned> ivector;
static void test_patternconstruction()
void test_patternconstruction()
{
{
FluxPattern fp(16, 0x0003);
assert(fp.bits == 16);
assert(fp.intervals == ivector{ 1 });
assert(fp._bits == 16);
assert(fp._intervals == ivector{ 1 });
}
{
FluxPattern fp(16, 0xc000);
assert(fp.bits == 16);
assert(fp.intervals == ivector{ 1 });
assert(fp._bits == 16);
assert(fp._intervals == ivector{ 1 });
}
{
FluxPattern fp(16, 0x0050);
assert(fp.bits == 16);
assert(fp.intervals == ivector{ 2 });
assert(fp._bits == 16);
assert(fp._intervals == ivector{ 2 });
}
{
FluxPattern fp(16, 0x0070);
assert(fp.bits == 16);
assert((fp.intervals == ivector{ 1, 1 }));
assert(fp._bits == 16);
assert((fp._intervals == ivector{ 1, 1 }));
}
{
FluxPattern fp(16, 0x0070);
assert(fp.bits == 16);
assert((fp.intervals == ivector{ 1, 1 }));
assert(fp._bits == 16);
assert((fp._intervals == ivector{ 1, 1 }));
}
{
FluxPattern fp(16, 0x0110);
assert(fp.bits == 16);
assert((fp.intervals == ivector{ 4 }));
assert(fp._bits == 16);
assert((fp._intervals == ivector{ 4 }));
}
}
static void test_patternmatching()
void test_patternmatching()
{
FluxPattern fp(16, 0x000b);
const unsigned matching[] = { 100, 100, 200, 100 };
@@ -59,11 +59,21 @@ static void test_patternmatching()
assert(fp.matches(&closematch2[4], clock));
}
void test_patternsmatching()
{
FluxPatterns fp(16, { 0x000b, 0x0015 });
const unsigned matching1[] = { 100, 100, 200, 100 };
const unsigned matching2[] = { 100, 100, 200, 200 };
double clock;
assert(fp.matches(&matching1[4], clock));
assert(fp.matches(&matching2[4], clock));
}
int main(int argc, const char* argv[])
{
test_patternconstruction();
test_patternmatching();
test_patternsmatching();
return 0;
}