Compare commits

...

1 Commits

Author SHA1 Message Date
David Given
f634988356 Add support for emitting index sync bytes to the Northstar encoder. This tells
the FluxEngine to wait for the next index pulse before continuing the write.
This also allows FluxEngine to decode its own encoded bytestreams, allowing
encodedecodetest to work.
2022-03-14 22:11:08 +01:00
6 changed files with 211 additions and 159 deletions

View File

@@ -88,16 +88,6 @@ public:
now = tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 21e6)) {
seekToIndexMark();
return 0;
}
int msSinceIndex = std::round(now / 1e6);
/* Note that the seekToPattern ignores the sector pulses, so if
@@ -108,11 +98,6 @@ public:
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
_sector->headerStartTime = tell().ns();
/* Discard a possible partial sector. */
if (_sector->headerStartTime > (getFluxmapDuration() - 21e6)) {
return 0;
}
int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6);
int sectorFoundTime;
@@ -132,7 +117,16 @@ public:
void decodeSectorRecord() override
{
nanoseconds_t before = tell().ns();
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
nanoseconds_t after = tell().ns();
/* Discard any sectors which span the end of a revolution. This can sometimes
* cause spurious bad sectors which can trigger conflicts. */
if (int(before / 200e9) != int(after / 200e9))
return;
unsigned recordSize, payloadSize, headerSize;
if (id == MFM_ID) {

View File

@@ -12,155 +12,165 @@
#define GAP_FILL_SIZE_DD 62
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define TOTAL_SECTOR_BYTES ()
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{
int preambleSize = 0;
int encodedSectorSize = 0;
int gapFillSize = 0;
int preHeaderGapFillSize = 0;
int preambleSize = 0;
int encodedSectorSize = 0;
int preHeaderGapFillSize = 0;
bool doubleDensity;
bool doubleDensity;
switch (sector->data.size()) {
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
switch (sector->data.size())
{
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD +
NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD +
NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else {
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true) {
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true) {
writer.write_8(northstarChecksum(sectorData.slice(2)));
} else {
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
if (sector->logicalSector != 9) {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize;
} else {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
}
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else
{
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true)
writer.write_8(0xFB); /* Double-density has two sync characters */
bool lastBit = false;
writer += sector->data;
if (doubleDensity == true)
writer.write_8(northstarChecksum(sectorData.slice(2)));
else
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (doubleDensity == true) {
encodeMfm(bits, cursor, fullSector, lastBit);
}
else {
encodeFm(bits, cursor, fullSector);
}
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size()
<< ") expected: " << fullSector->size() << " got "
<< fullSectorSize;
/* A few bytes of sector postamble just so the blank skip area doesn't start
* immediately after the data. */
for (int i = 0; i < 10; i++)
fullSector->push_back(GAP2_FILL_BYTE);
bool lastBit = false;
if (doubleDensity == true)
encodeMfm(bits, cursor, fullSector, lastBit);
else
encodeFm(bits, cursor, fullSector);
}
class NorthstarEncoder : public AbstractEncoder
{
public:
NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.northstar())
{}
NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.northstar())
{
}
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
std::vector<std::shared_ptr<const Sector>> collectSectors(
int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < 35))
{
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
if ((physicalTrack >= 0) && (physicalTrack < 35))
{
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sector =
image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
std::unique_ptr<Fluxmap> encode(int physicalTrack,
int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors,
const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
return std::unique_ptr<Fluxmap>();
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
return std::unique_ptr<Fluxmap>();
const auto& sector = *sectors.begin();
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
} else {
clockRateUs /= 2.00;
}
const auto& sector = *sectors.begin();
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD)
bitsPerRevolution /= 2; // FM
else
clockRateUs /= 2.00;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
auto fluxmap = std::make_unique<Fluxmap>();
bool first = true;
for (const auto& sectorData : sectors)
{
if (!first)
fluxmap->appendIndex();
first = false;
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
write_sector(bits, cursor, sectorData);
fluxmap->appendBits(bits, clockRateUs * 1e3);
}
if (cursor > bits.size())
Error() << "track data overrun";
// if (fluxmap->duration() > 200e6)
// Error() << "track data overrun";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
return fluxmap;
}
private:
const NorthstarEncoderProto& _config;
const NorthstarEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(
const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
}

View File

@@ -56,4 +56,3 @@ Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock)
return *this;
}

View File

@@ -17,41 +17,60 @@ public:
unsigned zeroes = 0;
nanoseconds_t ns() const
{ return ticks * NS_PER_TICK; }
{
return ticks * NS_PER_TICK;
}
operator std::string () {
operator std::string()
{
return fmt::format("[b:{}, t:{}, z:{}]", bytes, ticks, zeroes);
}
};
public:
Fluxmap() {}
Fluxmap() {}
Fluxmap(const std::string& s)
{
appendBytes((const uint8_t*) s.c_str(), s.size());
}
Fluxmap(const std::string& s)
{
appendBytes((const uint8_t*)s.c_str(), s.size());
}
Fluxmap(const Bytes bytes):
_bytes(bytes)
{}
Fluxmap(const Bytes& bytes)
{
appendBytes(bytes);
}
nanoseconds_t duration() const { return _duration; }
unsigned ticks() const { return _ticks; }
size_t bytes() const { return _bytes.size(); }
const Bytes& rawBytes() const { return _bytes; }
nanoseconds_t duration() const
{
return _duration;
}
unsigned ticks() const
{
return _ticks;
}
size_t bytes() const
{
return _bytes.size();
}
const Bytes& rawBytes() const
{
return _bytes;
}
const uint8_t* ptr() const
{
if (!_bytes.empty())
return &_bytes[0];
return NULL;
}
{
if (!_bytes.empty())
return &_bytes[0];
return NULL;
}
Fluxmap& appendInterval(uint32_t ticks);
Fluxmap& appendPulse();
Fluxmap& appendIndex();
Fluxmap& appendDesync();
Fluxmap& appendDesync();
Fluxmap& appendBytes(const Bytes& bytes);
Fluxmap& appendBytes(const uint8_t* ptr, size_t len);
@@ -61,14 +80,14 @@ public:
return appendBytes(&byte, 1);
}
Fluxmap& appendBits(const std::vector<bool>& bits, nanoseconds_t clock);
Fluxmap& appendBits(const std::vector<bool>& bits, nanoseconds_t clock);
void precompensate(int threshold_ticks, int amount_ticks);
std::vector<std::unique_ptr<const Fluxmap>> split() const;
std::unique_ptr<const Fluxmap> rescale(double scale) const;
private:
uint8_t& findLastByte();
uint8_t& findLastByte();
private:
nanoseconds_t _duration = 0;

View File

@@ -322,10 +322,10 @@ encodedecodetest() {
echo " format=$format"
echo " configs=$*"
echo " fluxx=flux"
echo "build $OBJDIR/$format.encodedecode.scp.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*"
echo " format=$format"
echo " configs=$*"
echo " fluxx=scp"
#echo "build $OBJDIR/$format.encodedecode.scp.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*"
#echo " format=$format"
#echo " configs=$*"
#echo " fluxx=scp"
}
buildlibrary libagg.a \
@@ -677,6 +677,7 @@ encodedecodetest rx50
encodedecodetest tids990
encodedecodetest victor9k_ss
encodedecodetest victor9k_ds
encodedecodetest northstar87 scripts/northstar87_test.textpb
# vim: sw=4 ts=4 et

View File

@@ -0,0 +1,29 @@
image_reader {
img {
tracks: 35
sides: 1
trackdata {
sector_size: 256
sector_range {
start_sector: 0
sector_count: 10
}
}
}
}
image_writer {
img {
tracks: 35
sides: 1
trackdata {
sector_size: 256
sector_range {
start_sector: 0
sector_count: 10
}
}
}
}