1
0
mirror of synced 2026-01-14 07:29:05 +00:00
2020-09-09 15:11:45 -07:00

1594 lines
53 KiB
C++

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <limits.h>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <sys/stat.h>
#include <list>
#include <map>
#include <algorithm>
#include <boost/filesystem.hpp>
#include "utils.h"
#ifndef SIZE_MAX
/* Limit of `size_t' type. */
# if __WORDSIZE == 64
# define SIZE_MAX (18446744073709551615UL)
# else
# define SIZE_MAX (4294967295U)
# endif
#endif
const bool cPrintHistograms = false;
// Direct Python Conversion
// Original code by Yngve AAdlandsvik
// Modified by Chris Fenton - 9/7/11
// Modified by Andras Tantos - 12/3/12
enum Bit_e { Bit_Zero = 0, Bit_One = 1, Bit_Undef = 2 };
static const size_t cHeads = 5;
static const size_t cTracks = 823;
static const size_t cSectors = 32;
static const size_t cSectorSize = 512;
#ifdef _MSC_VER
#pragma optimize( "", off )
#endif
int PrintUsage(const char *aProgName) {
std::cout << "Usage: " << aProgName << " <phase> [<param> <value>]" << std::endl;
std::cout << "phase:" << std::endl;
std::cout << " 1: initial recovery" << std::endl;
std::cout << " 2: second phase recovery (incomplete)" << std::endl;
std::cout << "possible paramters for phase 1:" << std::endl;
std::cout << " --StartFileIdx <number>: Index of the first scan file to read for each head" << std::endl;
std::cout << " --EndFileIdx <number>: Index of the last scan file to read for each head - set to 0 to read all files" << std::endl;
std::cout << " --StartHead <number>: Index of the first head directory to scan" << std::endl;
std::cout << " --EndHead <number>: Index of the last head directory to scan - set to " << cHeads - 1 << " to scan all head directories" << std::endl;
return 1;
}
static const size_t cSyncSize = 9;
static const size_t cCrcSize = 32;
static const size_t cHeaderReservedSize = 4;
static const size_t cHeaderTrackSize = 12;
static const size_t cHeaderHeadSize = 10;
static const size_t cHeaderSectorSize = 6;
static const size_t cHeaderCrcSize = cCrcSize;
static const size_t cHeaderDataSize = cHeaderReservedSize + cHeaderTrackSize + cHeaderHeadSize + cHeaderSectorSize;
static const size_t cHeaderTrackOfs = cHeaderReservedSize;
static const size_t cHeaderHeadOfs = cHeaderReservedSize + cHeaderTrackSize;
static const size_t cHeaderSectorOfs = cHeaderReservedSize + cHeaderTrackSize + cHeaderHeadSize;
static const size_t cHeaderCrcOfs = cHeaderReservedSize + cHeaderTrackSize + cHeaderHeadSize + cHeaderSectorSize;
static const size_t cHeaderSize = cSyncSize + cHeaderDataSize + cHeaderCrcSize;
static const size_t cSectorDataSize = 4096;
static const size_t cSectorCrcSize = cCrcSize;
static const size_t cSectorSizeInBits = cSyncSize + cSectorDataSize + cSectorCrcSize;
class OldBitStream_c: public std::vector<uint8_t> {
public:
OldBitStream_c() { Rewind(); }
void Rewind() { mCurBytePos = 0; mCurBitPos = 7; mCurPutBitPos = 7; mCurByte = 0; }
bool GetBit() {
bool RetVal = ((*this)[mCurBytePos] & (1 << mCurBitPos)) != 0;
if (mCurBitPos == 0) {
++mCurBytePos;
mCurBitPos = 7;
} else {
--mCurBitPos;
}
return RetVal;
}
void PutBit(uint8_t aBit) {
if (aBit != 0) {
mCurByte |= 1 << mCurPutBitPos;
}
if (mCurPutBitPos == 0) {
push_back(mCurByte);
mCurByte = 0;
mCurPutBitPos = 7;
} else {
--mCurPutBitPos;
}
}
size_t GetSizeInBits() const { return size() * 8 + (7 - mCurPutBitPos); }
protected:
size_t mCurBytePos;
uint8_t mCurBitPos;
uint8_t mCurPutBitPos;
uint8_t mCurByte;
};
class BitStream_c {
public:
BitStream_c() { Clear(); }
bool Read(const char *aFileName) {
Clear();
size_t FileSize = size_t(boost::filesystem::file_size(aFileName));
if (FileSize == 0) return false;
mData.resize(FileSize,0);
std::ifstream File(aFileName, std::ios::in | std::ios::binary);
if (!File.read((char *)(&mData[0]),mData.size())) return false;
return true;
}
void Rewind() {
mPos = 0;
mSubPos = 7;
}
void Clear() {
mData.clear();
Rewind();
mCurPushBitPos = 7;
}
const Bit_e operator[] (size_t aIdx) const {
size_t ByteOfs = aIdx >> 3;
size_t Bit = aIdx & 7;
uint8_t Byte = mData[ByteOfs];
return ((Byte >> (7-Bit)) & 1) == 0 ? Bit_Zero : Bit_One;
}
Bit_e PopBit() {
if (mPos == mData.size()) return Bit_Undef;
if (mPos == mData.size()-1 && mSubPos <= mCurPushBitPos) return Bit_Undef;
Bit_e Bit = Bit_e((mData[mPos] >> mSubPos) & 1);
if (mSubPos != 0) {
--mSubPos;
} else {
mSubPos = 7;
++mPos;
}
return Bit;
}
void PushBit(Bit_e aBit) {
if (mCurPushBitPos == 7) {
mData.push_back(0);
}
uint8_t &CurByte = mData.back();
if (aBit == Bit_One) {
CurByte |= 1 << mCurPushBitPos;
}
if (mCurPushBitPos == 0) {
mCurPushBitPos = 7;
} else {
--mCurPushBitPos;
}
}
size_t size() const { return mData.size() * 8; } // TODO: this doesn't really take into account the partial bytes written
protected:
std::vector<uint8_t> mData;
size_t mPos;
uint8_t mSubPos;
uint8_t mCurPushBitPos;
};
double CrossCorrelation(const BitStream_c &aStrmA, const BitStream_c &aStrmB, int aOffset, size_t aStartOffs, size_t aSize) {
int Start = int(aStartOffs);
int End = int(aStartOffs + aSize);
// Make sure things within limits
if (End > int(aStrmA.size())) End = int(aStrmA.size());
if (End+aOffset > int(aStrmB.size())) End = int(aStrmB.size()-aOffset);
if (Start+aOffset < 0) Start = -aOffset;
if (End == Start) return 0.0;
int CrossCorr = 0;
for(int i=Start;i<End;++i) {
CrossCorr += aStrmA[i] ^ aStrmB[i+aOffset];
}
return (double)(CrossCorr) / (double)(End-Start);
}
const uint8_t cNoiseLimit = 3;
double DetermineBitLength(const std::vector<uint32_t> &aPulseLengthHistogram) {
size_t MaxVal = 10000;
// Search for the first big maximum: that is going to be our bit-time
size_t MaxLoc = 0;
for(size_t i=0;i<aPulseLengthHistogram.size();++i) {
if (aPulseLengthHistogram[i] > MaxVal) {
MaxLoc = (int)i;
MaxVal = aPulseLengthHistogram[i];
} else {
// It's not a maximum. Did we find one already?
if (MaxLoc != 0) {
// We did. It means that we've passed the first big maximum, so bail
break;
}
}
}
// Calculate the bit-length from the weigthed average of the 3 histogram samples around the maximum
double BitLengthSum = (double)(aPulseLengthHistogram[MaxLoc-1] + aPulseLengthHistogram[MaxLoc] + aPulseLengthHistogram[MaxLoc+1]);
double BitLength = ((double)aPulseLengthHistogram[MaxLoc-1] * (double)(MaxLoc-1) + (double)aPulseLengthHistogram[MaxLoc] * (double)(MaxLoc) + (double)aPulseLengthHistogram[MaxLoc+1] * (double)(MaxLoc+1)) / BitLengthSum;
return BitLength;
}
std::vector<uint8_t> MakeRuns(BitStream_c &aBitStream, double &aBitTime, std::vector<size_t> *aRunTimes = nullptr, bool aPrintHistograms = cPrintHistograms) {
std::vector<uint32_t> PulseLengthHistogram0;
std::vector<uint32_t> PulseLengthHistogram1;
PulseLengthHistogram0.resize(256,0);
PulseLengthHistogram1.resize(256,0);
size_t MaxPulseLength = 0;
std::vector<uint8_t> Runs;
size_t PulseLength = 0;
size_t NoisyPulseLength = 0;
Bit_e CurrentBit = aBitStream.PopBit();
Bit_e LastBit;
// Create histogram and runs, but do some noise-filter (runs of 1-3 pulses are ignored)
size_t BitTime = 0;
while (CurrentBit != Bit_Undef) {
LastBit = CurrentBit;
CurrentBit = aBitStream.PopBit();
++PulseLength;
++NoisyPulseLength;
if (LastBit != CurrentBit) {
/*if (NoisyPulseLength > cNoiseLimit)*/ {
// Make sure the runs start with 0. This is important since the pulse-widths of the 1's and 0's are not the same
if (Runs.size() == 0) {
if (LastBit != 0) {
Runs.push_back(100);
BitTime += 100;
if (aRunTimes != nullptr) aRunTimes->push_back(BitTime);
}
}
Runs.push_back(uint8_t(PulseLength));
BitTime += PulseLength;
if (aRunTimes != nullptr) aRunTimes->push_back(BitTime);
if (PulseLength >= PulseLengthHistogram0.size()) PulseLength = PulseLengthHistogram0.size() - 1;
if (LastBit == 0) {
PulseLengthHistogram0[PulseLength]++;
} else {
PulseLengthHistogram1[PulseLength]++;
}
if (MaxPulseLength < PulseLength) MaxPulseLength = PulseLength;
PulseLength = 0;
}
NoisyPulseLength = 0;
}
}
double BitLength0 = DetermineBitLength(PulseLengthHistogram0);
double BitLength1 = DetermineBitLength(PulseLengthHistogram1);
aBitTime = (BitLength0 + BitLength1 ) / 2.0;
/***/
if (aPrintHistograms) {
for(size_t i=0;i<=MaxPulseLength;++i) {
std::cout << DecPrinter(i,4) << " - " << DecPrinter(PulseLengthHistogram0[i],10) << " - " << DecPrinter(PulseLengthHistogram1[i],10) << std::endl;
}
}
/***/
/*****
// Calculate number of samples that are inside the range of valid bit-transitions
size_t ValidSum = PulseLengthHistogram[(int)(BitLength * 1.0 - 1)] + PulseLengthHistogram[(int)(BitLength * 1.0)] + PulseLengthHistogram[(int)(BitLength * 1.0 + 1)] + // bit-time pulses
PulseLengthHistogram[(int)(BitLength * 1.5 - 1)] + PulseLengthHistogram[(int)(BitLength * 1.5)] + PulseLengthHistogram[(int)(BitLength * 1.5 + 1)] + // 1.5 bit-time pulses
PulseLengthHistogram[(int)(BitLength * 2.0 - 1)] + PulseLengthHistogram[(int)(BitLength * 2.0)] + PulseLengthHistogram[(int)(BitLength * 2.0 + 1)]; // 2.0 bit-time pulses
size_t SumAll = 0;
for(size_t i=0;i<PulseLengthHistogram.size();++i) {
SumAll += PulseLengthHistogram[i];
}
double GoodRatio = (double)(ValidSum) / (double)(SumAll);
std::cout << "Valid ratio: " << setw(10) << GoodRatio * 100.0 << "% " << "Determined bit-length: " << BitLength << std::endl;
********/
return Runs;
}
struct PulseType_s {
uint8_t HalfBitLen: 7; // Length in half-bit sizes. 0 for invalid
bool Error:1;
};
struct PulseCategorizer_s {
const PulseType_s *Pulses[2];
size_t PulseTableSize;
};
static const PulseType_s cPulses0_Strict[] = {
{ 0, true }, // 0
{ 0, true }, // 1
{ 0, true }, // 2
{ 0, true }, // 3
{ 0, true }, // 4
{ 0, true }, // 5
{ 2, true }, // 6
{ 2, false }, // 7
{ 2, false }, // 8
{ 2, true }, // 9
{ 0, true }, // 10
{ 3, false }, // 11
{ 3, false }, // 12
{ 3, false }, // 13
{ 0, true }, // 14
{ 4, false }, // 15
{ 4, false }, // 16
{ 4, false }, // 17
{ 4, true }, // 18
{ 0, true }, // 19
{ 0, true }, // 20 or more
};
static const PulseType_s cPulses1_Strict[] = {
{ 0, true }, // 0
{ 0, true }, // 1
{ 0, true }, // 2
{ 0, true }, // 3
{ 0, true }, // 4
{ 0, true }, // 5
{ 2, true }, // 6
{ 2, false }, // 7
{ 2, false }, // 8
{ 2, false }, // 9
{ 2, true }, // 10
{ 0, true }, // 11
{ 3, false }, // 12
{ 3, false }, // 13
{ 3, false }, // 14
{ 0, true }, // 15
{ 4, false }, // 16
{ 4, false }, // 17
{ 4, false }, // 18
{ 4, true }, // 19
{ 0, true }, // 20 or more
};
static const PulseCategorizer_s cStrictCategorizer = { {cPulses0_Strict, cPulses1_Strict}, sizeof(cPulses0_Strict) / sizeof(cPulses0_Strict[0]) };
static const PulseType_s cPulses0_Linient[] = {
{ 0, true }, // 0
{ 0, true }, // 1
{ 0, true }, // 2
{ 0, true }, // 3
{ 0, true }, // 4
{ 0, true }, // 5
{ 2, true }, // 6
{ 2, false }, // 7
{ 2, false }, // 8
{ 2, true }, // 9
{ 3, true }, // 10
{ 3, false }, // 11
{ 3, false }, // 12
{ 3, false }, // 13
{ 4, true }, // 14
{ 4, false }, // 15
{ 4, false }, // 16
{ 4, false }, // 17
{ 4, true }, // 18
{ 0, true }, // 19
{ 0, true }, // 20 or more
};
static const PulseType_s cPulses1_Linient[] = {
{ 0, true }, // 0
{ 0, true }, // 1
{ 0, true }, // 2
{ 0, true }, // 3
{ 0, true }, // 4
{ 0, true }, // 5
{ 2, true }, // 6
{ 2, false }, // 7
{ 2, false }, // 8
{ 2, false }, // 9
{ 2, true }, // 10
{ 3, true }, // 11
{ 3, false }, // 12
{ 3, false }, // 13
{ 3, false }, // 14
{ 4, true }, // 15
{ 4, false }, // 16
{ 4, false }, // 17
{ 4, false }, // 18
{ 4, true }, // 19
{ 0, true }, // 20 or more
};
static const PulseCategorizer_s cLinientCategorizer = { {cPulses0_Linient, cPulses1_Linient}, sizeof(cPulses0_Strict) / sizeof(cPulses0_Strict[0]) };
static const PulseType_s cPulses0_LinientB[] = {
{ 0, true }, // 0
{ 0, true }, // 1
{ 0, true }, // 2
{ 0, true }, // 3
{ 0, true }, // 4
{ 0, true }, // 5
{ 2, true }, // 6
{ 2, false }, // 7
{ 2, false }, // 8
{ 2, true }, // 9
{ 2, true }, // 10
{ 3, false }, // 11
{ 3, false }, // 12
{ 3, false }, // 13
{ 3, true }, // 14
{ 4, false }, // 15
{ 4, false }, // 16
{ 4, false }, // 17
{ 4, true }, // 18
{ 0, true }, // 19
{ 0, true }, // 20 or more
};
static const PulseType_s cPulses1_LinientB[] = {
{ 0, true }, // 0
{ 0, true }, // 1
{ 0, true }, // 2
{ 0, true }, // 3
{ 0, true }, // 4
{ 0, true }, // 5
{ 2, true }, // 6
{ 2, false }, // 7
{ 2, false }, // 8
{ 2, false }, // 9
{ 2, true }, // 10
{ 2, true }, // 11
{ 3, false }, // 12
{ 3, false }, // 13
{ 3, false }, // 14
{ 3, true }, // 15
{ 4, false }, // 16
{ 4, false }, // 17
{ 4, false }, // 18
{ 4, true }, // 19
{ 0, true }, // 20 or more
};
static const PulseCategorizer_s cLinientBCategorizer = { {cPulses0_LinientB, cPulses1_LinientB}, sizeof(cPulses0_Strict) / sizeof(cPulses0_Strict[0]) };
PulseType_s CategorizePulse(uint8_t aPulseWidth, uint8_t aSignalLevel, const PulseCategorizer_s &aPulseCategorizer) {
if (aPulseWidth >= aPulseCategorizer.PulseTableSize) aPulseWidth = uint8_t(aPulseCategorizer.PulseTableSize - 1);
return aPulseCategorizer.Pulses[aSignalLevel & 1][aPulseWidth];
}
size_t SyncScan(const std::vector<uint8_t> &aRuns, size_t aMinLength, size_t aStart, const PulseCategorizer_s &aPulseCategorizer) {
size_t Idx = aStart;
size_t Count = 0;
while (true) {
if (Idx >= aRuns.size()) return 0;
uint8_t CurBit = Idx % 2; // The sequence is guaranteed to start with 0
PulseType_s PulseType = CategorizePulse(aRuns[Idx], CurBit, aPulseCategorizer);
if (PulseType.HalfBitLen == 2) {
++Count;
} else {
if (Count >= aMinLength) {
// TODO: only valid termination is a double-bit run. Remove that from decode and change 9 to 8 in bits beginning at the header and data
if (PulseType.HalfBitLen == 3) return Idx;
}
Count = 0;
}
++Idx;
}
}
struct DecodeResults_s {
std::vector<Bit_e> Data;
size_t ErrorRate;
};
DecodeResults_s Decode(const std::vector<uint8_t> &aRuns, size_t aStartIdx, size_t aEndIdx, double aBitTime, const PulseCategorizer_s &aPulseCategorizer) {
DecodeResults_s RetVal;
RetVal.ErrorRate = 0;
if (aEndIdx > aRuns.size()) aEndIdx = aRuns.size();
//size_t ErrorRate = 0;
Bit_e LastBit = Bit_Zero;
Bit_e CurrentBit = Bit_Zero;
//BitStream_c DecodedStream;
for(size_t Idx=aStartIdx;Idx<aEndIdx;++Idx) {
LastBit = CurrentBit;
uint8_t CurBit = Idx % 2; // The sequence is guaranteed to start with 0
uint8_t PulseWidth = aRuns[Idx];
PulseType_s PulseType = CategorizePulse(PulseWidth, CurBit, aPulseCategorizer);
if (PulseType.HalfBitLen == 0) return RetVal;
if (PulseType.Error) ++RetVal.ErrorRate;
// We always start decoding with 0-s, so assume transitions on the bit-boundaries.
switch (PulseType.HalfBitLen) {
case 2: // We don't change bit
CurrentBit = LastBit;
RetVal.Data.push_back(CurrentBit);
//DecodedStream.PushBit(CurrentBit);
break;
case 3: // Bit changes, plus pulse-location shifts by 0.5 bit-locations. This means that every other time we have to push two values in
if (LastBit == Bit_Zero) {
CurrentBit = Bit_One;
RetVal.Data.push_back(LastBit);
RetVal.Data.push_back(CurrentBit);
//DecodedStream.PushBit(CurrentBit);
} else {
CurrentBit = Bit_Zero;
RetVal.Data.push_back(CurrentBit);
//DecodedStream.PushBit(LastBit);
//DecodedStream.PushBit(CurrentBit);
}
break;
case 4: // This is the '0' in a 1-0-1 sequence.
if (LastBit != Bit_One) return RetVal;
RetVal.Data.push_back(Bit_Zero);
RetVal.Data.push_back(LastBit);
//DecodedStream.PushBit(LastBit);
//DecodedStream.PushBit(Bit_Zero);
CurrentBit = LastBit;
break;
default: return RetVal;
}
}
return RetVal;
}
std::vector<Bit_e> IntBin(uint64_t aNumber, size_t aWidth) {
std::vector<Bit_e> RetVal;
while (aWidth > 0) {
if ((aNumber >> (aWidth - 1)) && 1 == 1) {
RetVal.push_back(Bit_One);
} else {
RetVal.push_back(Bit_Zero);
}
}
return RetVal;
}
uint64_t BinInt(const std::vector<Bit_e> &aString, size_t aStartIdx, size_t aEndIdx) {
uint64_t RetVal = 0;
if (aEndIdx > aString.size()) aEndIdx = aString.size();
for (size_t Idx=aStartIdx;Idx < aEndIdx; ++Idx) {
Bit_e C = aString[Idx];
RetVal <<= 1;
if (C == Bit_One) RetVal += 1;
}
return RetVal;
}
bool Validate(std::vector<Bit_e> &aData, size_t aStartIdx, size_t aEndIdx) {
uint32_t LocalCrc = 0;
const uint32_t Polynom = (1 << 23) | (1 << 21) | (1 << 11) | (1 << 2) | (1 << 0);
uint32_t CodeWord = Polynom;
if (aEndIdx > aData.size()) aEndIdx = aData.size();
// Go through the payload portion of the data and re-calculate the CRC
for(size_t Idx=aEndIdx-cCrcSize-1;Idx>=aStartIdx;--Idx) {
Bit_e C = aData[Idx];
if (C == Bit_One) LocalCrc ^= CodeWord;
if ((CodeWord >> 31) == 1) {
CodeWord <<= 1;
CodeWord = CodeWord ^ Polynom;
} else {
CodeWord <<= 1;
}
}
uint32_t StreamCrc = uint32_t(BinInt(aData,aEndIdx-32,aEndIdx));
return (StreamCrc == LocalCrc);
}
std::vector<uint8_t> ToBytes(const std::vector<Bit_e> &aBits, size_t aStartIdx, size_t aEndIdx) {
std::vector<uint8_t> RetVal;
if (aEndIdx > aBits.size()) aEndIdx = aBits.size();
for(size_t ByteIdx=aStartIdx;ByteIdx<aEndIdx;ByteIdx+=8) {
uint8_t Byte = 0;
for(size_t BitIdx=0;BitIdx<8;++BitIdx) {
Byte <<= 1;
if (aBits[ByteIdx+BitIdx] == Bit_One) {
Byte |= 1;
}
}
RetVal.push_back(Byte);
}
return RetVal;
}
bool AreSectorsEqual(const std::vector<uint8_t> &aSec1, const std::vector<uint8_t> &aSec2) {
if (aSec1.size() != aSec2.size()) return false;
return memcmp(&aSec1[0],&aSec2[0],aSec1.size()) == 0;
}
#ifdef _MSC_VER
#pragma optimize( "", on )
#endif
class Sector_c {
public:
Sector_c() {}
void SetData(const std::vector<uint8_t> &aData, size_t aErrorRate) {
if (!IsValid()) {
mData.push_back(SectorInfo_c(aData,aErrorRate));
} else {
for (size_t Idx=0;Idx < mData.size(); ++Idx) {
if (mData[Idx] == aData) {
mData[Idx].ErrorRate = std::min(mData[Idx].ErrorRate,aErrorRate);
++mData[Idx].RecoveryCount;
return;
}
}
// Add an alternative
mData.push_back(SectorInfo_c(aData,aErrorRate));
}
}
const std::vector<uint8_t> &GetBestData() const {
CRAY_ASSERT(IsValid());
size_t BestIdx = 0;
for (size_t Idx=0;Idx < mData.size(); ++Idx) {
if (mData[Idx].BetterThan(mData[BestIdx])) BestIdx = Idx;
}
return mData[BestIdx].Data;
}
size_t GetAlternateCnt() const { CRAY_ASSERT(IsValid()); return mData.size()-1; }
const std::vector<uint8_t> &GetAlternateData(size_t aIdx) const {
CRAY_ASSERT(IsValid());
size_t BestIdx = 0;
for (size_t Idx=0;Idx < mData.size(); ++Idx) {
if (mData[Idx].BetterThan(mData[BestIdx])) BestIdx = Idx;
}
if (aIdx == BestIdx) ++aIdx;
CRAY_ASSERT(aIdx < mData.size());
return mData[aIdx].Data;
}
bool IsValid() const { return mData.size() != 0; }
bool IsUnique() const { return mData.size() == 1; }
char GetLogCode() const {
if (!IsValid()) return '.';
if (!IsUnique()) return 'M';
CRAY_ASSERT(mData[0].RecoveryCount > 0);
if (mData[0].RecoveryCount > 9) return '+';
return char('0' + mData[0].RecoveryCount);
}
protected:
class SectorInfo_c {
public:
std::vector<uint8_t> Data;
size_t ErrorRate;
size_t RecoveryCount;
SectorInfo_c(): RecoveryCount(0) {}
SectorInfo_c(const std::vector<uint8_t> &aData, size_t aErrorRate, size_t aRecoveryCount = 1): ErrorRate(aErrorRate), RecoveryCount(aRecoveryCount) {
Data.resize(aData.size());
memcpy(&Data[0],&aData[0],aData.size());
}
bool operator == (const SectorInfo_c &aB) const {
if (Data.size() != aB.Data.size()) return false;
return memcmp(&Data[0],&aB.Data[0],Data.size()) == 0;
}
bool operator == (const std::vector<uint8_t> &aB) const {
if (Data.size() != aB.size()) return false;
return memcmp(&Data[0],&aB[0],Data.size()) == 0;
}
bool operator != (const SectorInfo_c &aB) const { return ! (*this == aB); }
bool operator != (const std::vector<uint8_t> &aB) const { return ! (*this == aB); }
bool BetterThan (const SectorInfo_c &aB) const {
if (RecoveryCount > aB.RecoveryCount) return true;
if (RecoveryCount == aB.RecoveryCount && ErrorRate < aB.ErrorRate) return true;
return false;
}
};
std::vector<SectorInfo_c> mData;
};
class DiskImage_c {
public:
DiskImage_c(): mHeads(0), mTracks(0), mSectors(0) {
InitMissingSector();
}
DiskImage_c(size_t aHeads, size_t aTracks, size_t aSectors) {
InitMissingSector();
SetSize(aHeads, aTracks, aSectors);
}
void SetSize(size_t aHeads, size_t aTracks, size_t aSectors) {
mSectorData.resize(aHeads * aTracks * aSectors);
mTrackScans.resize(aHeads * aTracks);
mHeads = aHeads;
mTracks = aTracks;
mSectors = aSectors;
}
Sector_c &GetSector(size_t aHead, size_t aTrack, size_t aSector) {
if (aHead >= mHeads) throw std::range_error("head is out of range");
if (aTrack > mTracks || aTrack == 0) throw std::range_error("track is out of range");
if (aSector >= mSectors) throw std::range_error("sector is out of range");
size_t Idx = (aSector) + (aTrack-1) * mSectors + (aHead) * mTracks * mSectors;
return mSectorData[Idx];
}
bool IsValidIdx(size_t aHead, size_t aTrack, size_t aSector) const {
if (aHead >= mHeads) return false;
if (aTrack > mTracks || aTrack == 0) return false;
if (aSector >= mSectors) return false;
return true;
}
void PrintSectorStats(size_t aHead, size_t aTrack, std::ostream &aStrm, bool aTerminateLine = true, bool aPrintHeader = true) {
if (aPrintHeader) aStrm << "Head:" << DecPrinter(aHead,1) << " Track:" << DecPrinter(aTrack,3) << " - ";
size_t GoodCnt = 0;
for(size_t Sector=0;Sector<mSectors;++Sector) {
Sector_c &CurSector = GetSector(aHead,aTrack,Sector);
if (CurSector.IsValid()) ++GoodCnt;
aStrm << CurSector.GetLogCode();
}
if (aPrintHeader) aStrm << " = " << DecPrinter(GoodCnt,0);
if (aTerminateLine) aStrm << std::endl;
}
bool WriteToDisk(const char *aFileName, const char *aStatFileName, bool aPrintStats = true) {
std::ofstream OutFile(aFileName, std::ios::out | std::ios::binary);
std::ofstream StatFile(aStatFileName, std::ios::out | std::ios::binary);
if (OutFile.bad()) return false;
for(size_t Track=1;Track<=mTracks;++Track) {
for(size_t Head=0;Head<mHeads;++Head) {
if (aPrintStats) {
PrintSectorStats(Head, Track, std::cout);
PrintSectorStats(Head, Track, StatFile);
}
for(size_t Sector=0;Sector<mSectors;++Sector) {
Sector_c &CurSector = GetSector(Head,Track,Sector);
if (CurSector.IsValid()) {
const std::vector<uint8_t> &Data = CurSector.GetBestData();
OutFile.write((char *)&Data[0],Data.size());
if (OutFile.bad()) return false;
} else {
OutFile.write((char *)&mMissingSector[0],mMissingSector.size());
if (OutFile.bad()) return false;
}
}
}
}
OutFile.close();
StatFile.close();
return true;
}
bool LoadFromDisk(const char *aFileName, size_t &aMissingSectorCnt) {
aMissingSectorCnt = 0;
std::ifstream InFile(aFileName, std::ios::in | std::ios::binary);
if (InFile.bad()) return false;
for(size_t Track=1;Track<=mTracks;++Track) {
for(size_t Head=0;Head<mHeads;++Head) {
for(size_t Sector=0;Sector<mSectors;++Sector) {
Sector_c &CurSector = GetSector(Head,Track,Sector);
std::vector<uint8_t> Data;
Data.resize(cSectorSize);
InFile.read((char *)&Data[0],cSectorSize);
if(!AreSectorsEqual(Data,mMissingSector)) {
CurSector.SetData(Data,SIZE_MAX);
} else {
++aMissingSectorCnt;
}
if (InFile.bad()) return false;
}
}
}
InFile.close();
return true;
}
bool LoadScanInfo(const char *aFileName) {
std::ifstream InFile(aFileName, std::ios::in);
if (InFile.bad()) return false;
while (!InFile.eof()) {
char LineBuf[32768*4];
InFile.getline(LineBuf,sizeof(LineBuf)/sizeof(LineBuf[0]));
if (InFile.bad()) return false;
char *Token = strtok(LineBuf,",");
size_t TokenCnt = 0;
char *ScanFileName = nullptr;
int Head = -1;
int Track = -1;
while (Token != nullptr && TokenCnt < 3) {
switch (TokenCnt) {
case 0: // File name
ScanFileName = Token;
break;
case 1: // Head
Head = atoi(Token);
break;
case 2: // Sector
Track = atoi(Token);
break;
}
Token = strtok(nullptr,",");
++TokenCnt;
}
if (TokenCnt == 3 && IsValidIdx(Head,Track,0)) {
// Full line retrieved
mTrackScans[Head * mTracks + Track].push_back(std::string(ScanFileName));
}
}
InFile.close();
return true;
}
bool WriteAlternatesToDisk(const char *aBaseFileName) {
for(size_t Track=1;Track<=mTracks;++Track) {
for(size_t Head=0;Head<mHeads;++Head) {
for(size_t Sector=0;Sector<mSectors;++Sector) {
Sector_c &CurSector = GetSector(Head,Track,Sector);
if (CurSector.IsValid() && !CurSector.IsUnique()) {
size_t AlternateCnt = CurSector.GetAlternateCnt();
for (size_t Idx=0;Idx<AlternateCnt;++Idx) {
const std::vector<uint8_t> &Data = CurSector.GetAlternateData(Idx);
std::stringstream FileName;
FileName << aBaseFileName << "_" << HexPrinter((Sector + Head * mSectors + Track * mSectors * mHeads) * cSectorSize,8) << "_S" << DecPrinter(Sector,0) << "H" << DecPrinter(Head,0) << "T" << DecPrinter(Track,0) << "_" << DecPrinter(Idx,0) << ".alt";
std::ofstream OutFile(FileName.str(), std::ios::out | std::ios::binary);
if (OutFile.bad()) return false;
OutFile.write((char *)&(Data[0]),Data.size());
if (OutFile.bad()) return false;
OutFile.close();
}
}
}
}
}
return true;
}
const std::vector<std::string> GetScans(size_t aHead, size_t aTrack) const {
if (!IsValidIdx(aHead,aTrack,0)) throw std::invalid_argument("Invalid argument to GetScans");
return mTrackScans[aHead * mTracks + aTrack];
}
protected:
void InitMissingSector() {
mMissingSector.resize(cSectorSize);
for(size_t i=0;i<cSectorSize/16;++i) {
const char *Data = "<<<<MISSING>>>>>";
memcpy(&mMissingSector[i*16],Data,16);
}
}
size_t mHeads;
size_t mTracks;
size_t mSectors;
std::vector<uint8_t> mMissingSector;
std::vector<Sector_c> mSectorData;
std::vector<std::vector<std::string>> mTrackScans;
};
#ifdef _MSC_VER
#pragma optimize( "", off )
#endif
DiskImage_c Image;
struct SectorStats_s {
SectorStats_s(size_t aStartHeader,size_t aStartData): StartHeader(aStartHeader), StartData(aStartData) {}
SectorStats_s(): StartHeader(0), StartData(0) {}
size_t StartHeader;
size_t StartData;
};
struct TrackStats_s {
std::vector<std::vector<SectorStats_s>> Sectors;
size_t Track;
size_t SectorCnt;
};
TrackStats_s Extract(const std::vector<uint8_t> &aRuns, std::vector<size_t> aRunTimes, double aBitTime, DiskImage_c &aImage, size_t aExpectedHead, const PulseCategorizer_s &aPulseCategorizer, std::ostream *aRecoveryLog = nullptr) {
TrackStats_s TrackStats;
size_t CurrentRunIdx = 0;
size_t Track = 0;
// size_t Head = 0;
size_t SectorCnt = 0;
TrackStats.Sectors.resize(cSectors);
// Test code to save categorized data
//{
// fstream PulseFile("pulses.txt",ios::out);
// size_t Idx = 0;
// size_t Count = 0;
// while (true) {
// if (Idx >= aRuns.size()) break;
// uint8_t CurBit = Idx % 2; // The sequence is guaranteed to start with 0
// PulseType_s PulseType = CategorizePulse(aRuns[Idx], CurBit, aPulseCategorizer);
// if (PulseType.HalfBitLen == 0) {
// PulseFile << '.';
// } else {
// PulseFile << char(PulseType.HalfBitLen + '0');
// }
// ++Idx;
// }
//}
int LastSector = -1;
while(true) {
///////////////////////////////
// HEADER
///////////////////////////////
size_t HeaderStart = SyncScan(aRuns,50,CurrentRunIdx,aPulseCategorizer);
if (HeaderStart == 0) break;
CurrentRunIdx = HeaderStart;
DecodeResults_s Header = Decode(aRuns,HeaderStart,HeaderStart+cHeaderSize,aBitTime,aPulseCategorizer);
if (Header.Data.size() < cHeaderSize || BinInt(Header.Data,0,cSyncSize) != 0xf0) continue;
// std::cout << "first: " << DecPrinter(Runs[HeaderStart]) << std::endl;
bool IsHeaderValid = Validate(Header.Data,cSyncSize,cSyncSize+cHeaderSize);
if (!IsHeaderValid) continue;
size_t CurrentTrack = size_t(BinInt(Header.Data,cSyncSize+cHeaderTrackOfs, cSyncSize+cHeaderTrackOfs+ cHeaderTrackSize));
size_t CurrentHead = size_t(BinInt(Header.Data,cSyncSize+cHeaderHeadOfs, cSyncSize+cHeaderHeadOfs+ cHeaderHeadSize));
size_t CurrentSector = size_t(BinInt(Header.Data,cSyncSize+cHeaderSectorOfs, cSyncSize+cHeaderSectorOfs+cHeaderSectorSize));
//std::cout << "HTS: " << CurrentHead << ":" << CurrentTrack << ":" << CurrentSector << " ";
if (CurrentHead != aExpectedHead) continue;
if (Track != 0 && Track != CurrentTrack) continue;
if (!aImage.IsValidIdx(CurrentHead, CurrentTrack, CurrentSector)) continue;
if (LastSector != -1) {
// Make sure sector numbers are consequtive, but only if we don't experience gaps
if (CurrentSector != 0 && (LastSector + 1 != int(CurrentSector))) {
LastSector = -1;
continue;
}
}
Track = CurrentTrack;
// Head = CurrentHead;
///////////////////////////////
// DATA
///////////////////////////////
size_t DataStart = SyncScan(aRuns,100,HeaderStart,aPulseCategorizer);
if (DataStart == 0) break;
DecodeResults_s Data = Decode(aRuns,DataStart,DataStart+cSectorSizeInBits,aBitTime,aPulseCategorizer);
//std::cout << "data of " << Data.Data.size() << " bits found ";
if (Data.Data.size() < cSectorSizeInBits || BinInt(Data.Data,0,cSyncSize) != 0xf0) {
//std::cout << std::endl;
continue;
}
// std::cout << "first: " << DecPrinter(Runs[HeaderStart]) << std::endl;
bool IsDataValid = Validate(Data.Data,cSyncSize,cSectorSizeInBits);
if (!IsDataValid) {
if (LastSector != -1) { std::cout << "\t\t\tsector: " << CurrentSector << " killed only for CRC" << std::endl; }
//std::cout << std::endl;
continue;
}
//std::cout << "valid " << std::endl;
std::vector<uint8_t> SectorBytes = ToBytes(Data.Data,cSyncSize,cSyncSize+cSectorDataSize);
if (aRecoveryLog != nullptr) *aRecoveryLog << DecPrinter(CurrentSector) << ',';
// Save the data into the image
++SectorCnt;
Sector_c &ImgSector = aImage.GetSector(CurrentHead, CurrentTrack, CurrentSector);
ImgSector.SetData(SectorBytes, Data.ErrorRate);
// Update last sector to track continuity
std::cout << "\t\t\tfound sector: " << CurrentSector << " with previous sector: " << LastSector << std::endl;
LastSector = int(CurrentSector);
TrackStats.Sectors[CurrentSector].push_back(SectorStats_s(HeaderStart,DataStart));
//std::cout << "found sector HTS: " << setw(1) << CurrentHead << ":" << setw(3) << CurrentTrack << ":" << setw(2) << CurrentSector << " at offset: " << hex << setw(8) << HeaderStart << " data: " << DataStart << dec << setw(0) << std::endl;
// Step over the data portion
CurrentRunIdx = DataStart;
}
TrackStats.SectorCnt = SectorCnt;
TrackStats.Track = Track;
return TrackStats;
}
size_t IdxToTime(const std::vector<uint8_t> aRuns, size_t aIdx) {
aIdx = std::min(aIdx,aRuns.size()-1);
size_t Time = 0;
for(size_t Idx=0;Idx<=aIdx;++aIdx) {
Time += aRuns[Idx];
}
return Time;
}
inline double sqr(double aVal) { return aVal * aVal; }
BitStream_c FoldStream(BitStream_c &aBitStream,const std::vector<size_t> &aFoldLocations, size_t aFoldSize,uint8_t aExcludeIdx) {
/* BitStream_c RetVal;
size_t RawFoldCnt = aFoldLocations.size();
if (RawFoldCnt < 3) return RetVal;
CRAY_ASSERT(RawFoldCnt == 4); // The logic below seems to be broken for anything but four...
size_t FoldCnt = RawFoldCnt;
if ((FoldCnt & 1) == 0) --FoldCnt; // Make sure FoldCnt is odd
size_t Cutoff = FoldCnt >> 1;
if (FoldCnt > 3) FoldCnt = 3;
//size_t Offset = (RawFoldCnt > 3) ? aBitStream.size() / 10 : 0; // Try not to use the first 10% of the samples (head position might not have stabilized after the step) if possible
size_t Offset = 0;
if (RawFoldCnt <= 3) aExcludeIdx = 4; // Make sure we only do exclusions if we in fact have 4 copies at least
for(size_t Idx=0;Idx<aFoldLocations[1];++Idx) {
size_t Cnt = 0;
for(size_t Fold=0;Fold<RawFoldCnt;++Fold) {
if (Fold != aExcludeIdx) {
Cnt += aBitStream[Offset + Idx + aFoldLocations[Fold]];
}
}
RetVal.PushBit(Cnt > Cutoff ? Bit_One : Bit_Zero);
}
// Duplicate the stream to make sure we have at least one good reading of all sectors even of the one that wraps around at the fold
for(size_t Idx=0;Idx<aFoldLocations[1];++Idx) {
RetVal.PushBit(RetVal.PopBit());
}
return RetVal;*/
int LastBit = 0; // We always start from 0
BitStream_c RetVal;
size_t Offset = 0;
size_t FoldCnt = aFoldLocations.size();
for(size_t Idx=0;Idx<aFoldSize;++Idx) {
size_t Cnt = 0;
for(size_t Fold=0;Fold<FoldCnt;++Fold) {
Cnt += aBitStream[Offset + Idx + aFoldLocations[Fold]];
}
if (LastBit == 0) {
// Last bit is 0 --> write 0-s first, 1-s next
size_t Fold;
for(Fold=0;Fold<FoldCnt-Cnt;++Fold) {
RetVal.PushBit(Bit_Zero);
LastBit = 0;
}
for(;Fold<FoldCnt;++Fold) {
RetVal.PushBit(Bit_One);
LastBit = 1;
}
} else {
size_t Fold;
for(Fold=0;Fold<Cnt;++Fold) {
RetVal.PushBit(Bit_One);
LastBit = 1;
}
for(;Fold<FoldCnt;++Fold) {
RetVal.PushBit(Bit_Zero);
LastBit = 0;
}
}
}
// Duplicate the stream to make sure we have at least one good reading of all sectors even of the one that wraps around at the fold
for(size_t Idx=0;Idx<aFoldLocations[1];++Idx) {
RetVal.PushBit(RetVal.PopBit());
}
return RetVal;
}
void ExtractWithFold(BitStream_c &aBitStream,const std::vector<size_t> &aFoldLocations, size_t aFoldSize, const char *aFileName, DiskImage_c &aImage, size_t aExpectedHead, const PulseCategorizer_s &aPulseCategorizer, uint8_t aExcludeIdx, std::ostream *aRecoveryLog = nullptr) {
if (aFoldLocations.size() < 2) return;
BitStream_c FoldedStream = FoldStream(aBitStream,aFoldLocations,aFoldSize,aExcludeIdx);
FoldedStream.Rewind();
double BitTime;
std::vector<size_t> RunTimes;
std::vector<uint8_t> Runs = MakeRuns(FoldedStream,BitTime,&RunTimes,true);
TrackStats_s TrackStats;
TrackStats = Extract(Runs, RunTimes, BitTime, aImage, aExpectedHead, aPulseCategorizer, aRecoveryLog);
std::cout << "* ";
if (aImage.IsValidIdx(aExpectedHead, TrackStats.Track, 0)) {
aImage.PrintSectorStats(aExpectedHead, TrackStats.Track, std::cout, false);
} else {
std::cout << "-";
}
std::cout << " file: " << aFileName << " extacted sectors: " << TrackStats.SectorCnt;
std::cout << " *" << DecPrinter(aExcludeIdx,0) << std::endl;
}
size_t Extract(const char *aFileName, DiskImage_c &aImage, size_t aExpectedHead, const PulseCategorizer_s &aPulseCategorizer, std::ostream *aRecoveryLog = nullptr) {
BitStream_c BitStream;
if (!BitStream.Read(aFileName)) return 0;
double BitTime;
std::vector<size_t> RunTimes;
std::vector<uint8_t> Runs = MakeRuns(BitStream,BitTime,&RunTimes,true);
TrackStats_s TrackStats;
TrackStats = Extract(Runs, RunTimes, BitTime, aImage, aExpectedHead, aPulseCategorizer, aRecoveryLog);
std::cout << "- ";
if (aImage.IsValidIdx(aExpectedHead, TrackStats.Track, 0)) {
aImage.PrintSectorStats(aExpectedHead, TrackStats.Track, std::cout, false);
} else {
std::cout << "-";
}
// Print statistics
std::cout << " file: " << aFileName << " extacted sectors: " << TrackStats.SectorCnt << std::endl;
std::vector<size_t> StrideEstimates;
for(size_t Sector=0;Sector<TrackStats.Sectors.size();++Sector) {
std::vector<SectorStats_s> &Stats = TrackStats.Sectors[Sector];
if (Stats.size() > 1) {
size_t CurHeadTime = RunTimes[Stats[0].StartHeader];
size_t CurDataTime = RunTimes[Stats[0].StartData];
size_t PrevHeadTime;
size_t PrevDataTime;
for(size_t Idx=1;Idx<Stats.size();++Idx) {
PrevHeadTime = CurHeadTime;
PrevDataTime = CurDataTime;
CurHeadTime = RunTimes[Stats[Idx].StartHeader];
CurDataTime = RunTimes[Stats[Idx].StartData];
size_t HeadStride = CurHeadTime - PrevHeadTime;
size_t DataStride = CurDataTime - PrevDataTime;
// TODO: can we figure out the cut-off lines dinamically?
if (HeadStride > 2000000) HeadStride /= 2;
if (HeadStride > 3000000) HeadStride /= 3;
if (DataStride > 2000000) DataStride /= 2;
if (DataStride > 3000000) DataStride /= 3;
if (HeadStride > 1300000 && HeadStride < 1320000) StrideEstimates.push_back(HeadStride);
if (DataStride > 1300000 && DataStride < 1320000) StrideEstimates.push_back(DataStride);
}
}
}
do {
if (StrideEstimates.size() == 0) break; // We don't have enough data to do anything interesting
// Calculate average
sort(StrideEstimates.begin(),StrideEstimates.end());
size_t Sum = 0;
size_t Cnt = 0;
for(size_t i=0;i<StrideEstimates.size();++i) {
Sum += StrideEstimates[i];
++Cnt;
}
double Avg = (double)Sum/(double)Cnt;
// Calculate deviation
double Dev = 0.0;
for(size_t i=0;i<StrideEstimates.size();++i) {
Dev += sqr(Avg - StrideEstimates[i]);
}
Dev = sqrt(Dev)/Cnt;
// TODO: how to figure out the limit manually?
if (Dev > 100.0) break;
/***
vector<size_t> FoldLocations;
size_t Fold = int(Avg);
FoldLocations.push_back(0);
while (Fold < BitStream.size()) {
double MinCrossCorr = 1.0;
int MinOffset = INT_MIN;
for(int Offset=-50;Offset<=50;++Offset) {
double CrossCorr = CrossCorrelation(BitStream, BitStream, Fold + Offset, int(Avg)*1/3, int(Avg)*2/3);
CRAY_ASSERT(CrossCorr <= 1.0);
//std::cout << "\tCross correlation for offset: " << setw(3) << Offset << setw(0) << " is: " << CrossCorr << ((MinCrossCorr > CrossCorr) ? " <--" : "") << std::endl;
if (MinCrossCorr > CrossCorr) {
//std::cout << "\t\tNew minimum found" << std::endl;
MinCrossCorr = CrossCorr;
MinOffset = Offset;
}
}
std::cout << "\t\tCross-correlation: " << MinCrossCorr << std::endl;
CRAY_ASSERT(MinOffset != INT_MIN);
if (Fold + int(Avg) < BitStream.size()) {
if (MinCrossCorr < 0.1) {
FoldLocations.push_back(Fold + MinOffset);
}
}
Fold += int(Avg)+MinOffset;
}
// for(uint8_t ExcludeIdx = 0; ExcludeIdx < FoldLocations.size(); ++ExcludeIdx) {
ExtractWithFold(BitStream, FoldLocations, int(Avg), aFileName, aImage, aExpectedHead, aPulseCategorizer, 0, aRecoveryLog);
// }
***/
} while(false);
return TrackStats.Track;
}
const char *ImageFileName = "disk.img";
const char *Phase2ImageFileName = "disk_p2.img";
const char *AlternateBaseName = "disk";
const char *StatFileName = "disk.stat";
const char *Phase2StatFileName = "disk_p2.stat";
const char *InfoFileName = "scan.info";
int ScannerPhase1(CommandLine_c aCmdLine)
{
DiskImage_c Image;
Image.SetSize(cHeads,cTracks,cSectors);
// size_t LastTrack = 0;
// size_t LastHead = 0;
size_t CurrentTrack = 0;
remove(ImageFileName);
remove(StatFileName);
remove(InfoFileName);
size_t StartFileIdx = 0;
size_t EndFileIdx = 0;
size_t StartHead = 0;
size_t EndHead = cHeads-1;
try {
while(aCmdLine.HasMoreParams()) {
std::string Param = aCmdLine.GetNextParam();
if (Param == "--StartFileIdx") {
StartFileIdx = atol(aCmdLine.GetNextParam().c_str());
} else if (Param == "--EndFileIdx") {
EndFileIdx = atol(aCmdLine.GetNextParam().c_str());
} else if (Param == "--StartHead") {
StartHead = atol(aCmdLine.GetNextParam().c_str());
} else if (Param == "--EndHead") {
EndHead = atol(aCmdLine.GetNextParam().c_str());
} else {
throw Generic_x("Invalid command line parameter");
}
}
if (EndFileIdx < StartFileIdx) {
throw Generic_x("EndFileIdx must not be less than StartFileIdx");
}
if (EndHead < StartHead) {
throw Generic_x("EndHead must not be less than StartHead");
}
}
catch(std::exception &e) {
std::cout << "Error processing command line arguments: " << e.what() << std::endl;
return PrintUsage(aCmdLine.GetProgramName().c_str());
}
/***
const size_t StartFileIdx = 4215; // track 242
const size_t EndFileIdx = 4229;
const size_t StartHead = 3;
const size_t EndHead = 3;
***/
std::ofstream InfoFile;
try {
for(size_t Head=StartHead;Head<=EndHead;++Head) {
size_t FileIdx = StartFileIdx;
size_t MissCnt = 0;
do {
std::stringstream FileName;
FileName << "h" << Head << "\\" << "s" << FileIdx << "_h" << Head << ".txt";
if (!boost::filesystem::exists(FileName.str())) {
++MissCnt;
if (MissCnt > 100) break;
continue;
}
MissCnt = 0;
// if (CurrentTrack != 0) LastTrack = CurrentTrack;
std::stringstream ExtractionLog;
CurrentTrack = Extract(FileName.str().c_str(),Image,Head,cLinientCategorizer,&ExtractionLog);
try {
// Update scan file info
InfoFile.open(InfoFileName, std::ios::out | std::ios::app);
InfoFile << FileName.str() << "," << Head;
if (CurrentTrack != 0) {
InfoFile << "," << CurrentTrack << "," << ExtractionLog.str();
//Image.PrintSectorStats(Head, CurrentTrack, InfoFile, false, false);
}
InfoFile << std::endl;
InfoFile.close();
} catch (...) {
try {
InfoFile.close();
}
catch(...) {}
}
// if (LastTrack != CurrentTrack && CurrentTrack != 0 && LastTrack != 0) {
// LastHead = Head;
// }
++FileIdx;
if (FileIdx > EndFileIdx && EndFileIdx != 0) break;
} while (true);
}
}
catch (...) {
std::cout << std::endl << "Exception caught. Writing out whaterver we've gotten so far" << std::endl;
}
// By now we should have the whole image on disk, unless we skipped whole tracks. But just to be safe, overwrite the whole thing again.
Image.WriteToDisk(ImageFileName,StatFileName);
Image.WriteAlternatesToDisk(AlternateBaseName);
return 0;
}
struct ScanOffsetInfo_s {
ScanOffsetInfo_s(size_t aOffset, size_t aIdx): Offset(aOffset), Idx(aIdx) {}
size_t Offset;
size_t Idx;
};
void FixTrack(size_t aHead, size_t aTrack, DiskImage_c &aImage) {
std::vector<BitStream_c> BitStreams;
std::vector<std::vector<size_t>> RunTimes;
std::vector<std::vector<uint8_t>> Runs;
std::vector<TrackStats_s> TrackStats;
const std::vector<std::string> &Scans = aImage.GetScans(aHead, aTrack);
size_t ScanCnt = Scans.size();
for(size_t Idx = 0; Idx < ScanCnt; ++Idx) {
std::cout << "\tLoading scan data " << DecPrinter(Idx+1,0) << " out of " << DecPrinter(ScanCnt,0) << " from file: " << Scans[Idx] << std::endl;
std::stringstream ExtractionLog;
Extract(Scans[Idx].c_str(),aImage,aHead,cLinientCategorizer,&ExtractionLog);
std::cout << ExtractionLog.str() << std::endl;
}
/**************
size_t ScanCnt = Scans.size();
// Read-in all the scans and parse them into bit-streams
BitStreams.resize(ScanCnt);
Runs.resize(ScanCnt);
RunTimes.resize(ScanCnt);
TrackStats.resize(ScanCnt);
for(size_t Idx = 0; Idx < ScanCnt; ++Idx) {
std::cout << "\tLoading scan data " << DecPrinter(Idx+1,0) << " out of " << DecPrinter(ScanCnt,0) << std::endl;
if (!BitStreams[Idx].Read(Scans[Idx].c_str())) return;
double BitTime;
Runs[Idx] = MakeRuns(BitStreams[Idx],BitTime,&RunTimes[Idx]);
TrackStats[Idx] = Extract(Runs[Idx], RunTimes[Idx], BitTime, aImage, aHead, cLinientCategorizer);
if (TrackStats[Idx].Track != aTrack) {
// If track doesn't match, clear the content so it doesn't interfere with future work
TrackStats[Idx].SectorCnt = 0;
TrackStats[Idx].Sectors.resize(0);
}
}
// Select the one with the most number of recovered sectors as the 'driver'
size_t DriverIdx = 0;
for(size_t Idx=0;Idx<ScanCnt;++Idx) {
if (TrackStats[Idx].SectorCnt > TrackStats[DriverIdx].SectorCnt) {
DriverIdx = Idx;
}
}
// Calculate stride for folded scans
vector<size_t> StrideEstimates;
for(size_t Sector=0;Sector<TrackStats[DriverIdx].Sectors.size();++Sector) {
vector<SectorStats_s> &Stats = TrackStats[DriverIdx].Sectors[Sector];
if (Stats.size() > 1) {
size_t CurHeadTime = RunTimes[DriverIdx][Stats[0].StartHeader];
size_t CurDataTime = RunTimes[DriverIdx][Stats[0].StartData];
size_t PrevHeadTime;
size_t PrevDataTime;
for(size_t Idx=1;Idx<Stats.size();++Idx) {
PrevHeadTime = CurHeadTime;
PrevDataTime = CurDataTime;
CurHeadTime = RunTimes[DriverIdx][Stats[Idx].StartHeader];
CurDataTime = RunTimes[DriverIdx][Stats[Idx].StartData];
size_t HeadStride = CurHeadTime - PrevHeadTime;
size_t DataStride = CurDataTime - PrevDataTime;
// TODO: can we figure out the cut-off lines dinamically?
if (HeadStride > 2000000) HeadStride /= 2;
if (HeadStride > 3000000) HeadStride /= 3;
if (DataStride > 2000000) DataStride /= 2;
if (DataStride > 3000000) DataStride /= 3;
if (HeadStride > 1300000 && HeadStride < 1320000) StrideEstimates.push_back(HeadStride);
if (DataStride > 1300000 && DataStride < 1320000) StrideEstimates.push_back(DataStride);
}
}
}
if (StrideEstimates.size() == 0) return; // We don't have enough data to do anything interesting
sort(StrideEstimates.begin(),StrideEstimates.end());
size_t Sum = 0;
size_t Cnt = 0;
for(size_t i=0;i<StrideEstimates.size();++i) {
Sum += StrideEstimates[i];
++Cnt;
}
size_t Stride;
{
double Avg = (double)Sum/(double)Cnt;
// Calculate deviation
double Dev = 0.0;
for(size_t i=0;i<StrideEstimates.size();++i) {
Dev += sqr(Avg - StrideEstimates[i]);
}
Dev = sqrt(Dev)/Cnt;
// TODO: how to figure out the limit manually?
if (Dev > 100.0) return;
Stride = size_t(int(Avg + 0.5));
}
// Align the scans over one another
vector<size_t> ScanOffsets;
ScanOffsets.resize(ScanCnt);
for(size_t Idx = 0; Idx < ScanCnt; ++Idx) {
std::cout << "Aligning sector: " << Idx << std::endl;
if (Idx == DriverIdx) continue;
ScanOffsets[Idx] = SIZE_MAX;
vector<size_t> Anchors;
for(size_t Sector=0;Sector<TrackStats[Idx].Sectors.size();++Sector) {
vector<SectorStats_s> &DriverStats = TrackStats[DriverIdx].Sectors[Sector];
vector<SectorStats_s> &Stats = TrackStats[Idx].Sectors[Sector];
if (Stats.size() > 1 && DriverStats.size() > 1) {
// Both scans contain valid entries for the same sector. This creates an anchor
// For now we'll only use the first occurance, but we could use all of them.
int DriverHeadTime = RunTimes[DriverIdx][DriverStats[0].StartHeader];
int DriverDataTime = RunTimes[DriverIdx][DriverStats[0].StartData];
int HeadTime = RunTimes[Idx][Stats[0].StartHeader];
int DataTime = RunTimes[Idx][Stats[0].StartData];
int HeadAnchor = HeadTime - DriverHeadTime;
while(HeadAnchor < 0) HeadAnchor += Stride;
while(HeadAnchor > int(Stride)) HeadAnchor -= Stride;
int DataAnchor = DataTime - DriverDataTime;
while(DataAnchor < 0) DataAnchor += Stride;
while(DataAnchor > int(Stride)) DataAnchor -= Stride;
// Fine-tune the anchors, using cross-correlation
double CrossCorr = 0.0;
double MaxCrossCorr = 0.0;
int MaxOffset = -100;
for(int AnchorOffset=-5;AnchorOffset<=5;++AnchorOffset) {
CrossCorr = CrossCorrelation(BitStreams[DriverIdx], BitStreams[Idx], HeadAnchor + AnchorOffset, BitStreams[Idx].size()*1/3, BitStreams[Idx].size()*2/3);
std::cout << "\tCross correlation for offset: " << AnchorOffset << " is: " << CrossCorr << std::endl;
if (MaxCrossCorr < CrossCorr) {
std::cout << "\t\tNew maximum found" << std::endl;
MaxCrossCorr = CrossCorr;
MaxOffset = AnchorOffset;
}
}
if (MaxOffset == -100) {
std::cout << "\tCross correlation failed!!!" << std::endl;
} else {
HeadAnchor += MaxOffset;
DataAnchor += MaxOffset;
}
Anchors.push_back(HeadAnchor);
Anchors.push_back(DataAnchor);
}
}
// If we got no anchors, remove this scan from the pool of possible scans
if (Anchors.size() == 0) {
TrackStats[Idx].SectorCnt = 0;
TrackStats[Idx].Sectors.resize(0);
continue;
}
// Let's calculate the average offset
{
double Sum = 0.0;
for (size_t AnchorIdx = 0; AnchorIdx < Anchors.size(); ++AnchorIdx) {
Sum += Anchors[AnchorIdx];
}
double Avg = (double)Sum/(double)Anchors.size();
// Calculate deviation
double Dev = 0.0;
for (size_t AnchorIdx = 0; AnchorIdx < Anchors.size(); ++AnchorIdx) {
Dev += sqr(Avg - Anchors[AnchorIdx]);
}
Dev = sqrt(Dev)/(double)Anchors.size();
// If our deviation is too high, explcude this scan
// TODO: how to figure out the limit manually?
if (Dev > 10.0) {
TrackStats[Idx].SectorCnt = 0;
TrackStats[Idx].Sectors.resize(0);
continue;
}
ScanOffsets[Idx] = size_t(int(Avg + 0.5));
}
}
// OK, we have the possible other scans, combine all of them (including all the folds) into a single bit-stream using majority voting.
// BTW: The ScanOffsets[Driver] is set to 0, so we don't even care who the driver was any more
// First, compress ScanOffsets into a dense array
vector<ScanOffsetInfo_s> ScanOffsetInfo;
for(size_t ScanIdx = 0; ScanIdx < ScanCnt; ++ScanIdx) {
if (ScanOffsets[ScanIdx] != SIZE_MAX) {
ScanOffsetInfo.push_back(ScanOffsetInfo_s(ScanOffsets[ScanIdx],ScanIdx));
}
}
BitStream_c FoldedStream;
size_t FoldCnt = BitStreams[DriverIdx].size() / Stride - 1;
CRAY_ASSERT(FoldCnt > 0);
//size_t Offset = 0;
size_t SampleCnt = ScanOffsetInfo.size() * FoldCnt;
bool SkipLast = (SampleCnt & 1) == 0; // If we have an even number of samples, skip the last one to make majority voting possible
if (SkipLast) --SampleCnt;
size_t Cutoff = SampleCnt / 2;
std::cout << "\tGenerating folded stream from " << DecPrinter(SampleCnt,0) << " samples per bit..." << std::endl;
for(size_t Idx=0;Idx<Stride;++Idx) {
size_t Cnt = 0;
size_t LastVal;
for(size_t OffsetIdx = 0; OffsetIdx < ScanOffsetInfo.size(); ++OffsetIdx) {
size_t ScanIdx = ScanOffsetInfo[OffsetIdx].Idx;
size_t Offset = ScanOffsetInfo[OffsetIdx].Offset;
for(size_t Fold=0;Fold<FoldCnt;++Fold) {
LastVal = BitStreams[ScanIdx][Offset + Idx + Stride * Fold];
Cnt += LastVal;
}
}
if (SkipLast) Cnt -= LastVal;
FoldedStream.PushBit(Cnt > Cutoff ? Bit_One : Bit_Zero);
}
// Duplicate the stream to make sure we have at least one good reading of all sectors even of the one that wraps around at the fold
std::cout << "\tDuplicating generated stream..." << std::endl;
for(size_t Idx=0;Idx<Stride;++Idx) {
FoldedStream.PushBit(FoldedStream.PopBit());
}
std::cout << "\tDetecting sectors..." << std::endl;
FoldedStream.Rewind();
double BitTime;
vector<size_t> FoldedRunTimes;
vector<uint8_t> FoldedRuns = MakeRuns(FoldedStream,BitTime,&FoldedRunTimes,true);
TrackStats_s FoldedTrackStats;
FoldedTrackStats = Extract(FoldedRuns, FoldedRunTimes, BitTime, aImage, aHead, cLinientCategorizer);
std::cout << "\t* ";
if (aImage.IsValidIdx(aHead, FoldedTrackStats.Track, 0)) {
aImage.PrintSectorStats(aHead, FoldedTrackStats.Track, std::cout, false);
} else {
std::cout << "-";
}
std::cout << " extacted sectors: " << FoldedTrackStats.SectorCnt << std::endl;
*********/
}
/*******
for(uint8_t ExcludeIdx = 0; ExcludeIdx < 4; ++ExcludeIdx) {
for(int Offset = -2; Offset <= 2; ++Offset) {
ExtractWithFold(BitStream, size_t(int(Avg + 0.5) + Offset), aFileName, aImage, aExpectedHead, aPulseCategorizer, ExcludeIdx, aRecoveryLog);
}
}
return TrackStats.Track;
*****/
int ScannerPhase2(CommandLine_c aCmdLine) {
DiskImage_c Image;
Image.SetSize(cHeads,cTracks,cSectors);
size_t MissingSectorCnt;
std::cout << "Loading image..." << std::flush;
if (!Image.LoadFromDisk(ImageFileName,MissingSectorCnt)) return 1;
std::cout << " done with " << DecPrinter(MissingSectorCnt,0) << " sectors missing" << std::endl;
std::cout << "Loading scan info..." << std::flush;
if (!Image.LoadScanInfo(InfoFileName)) return 1;
std::cout << " done" << std::endl;
size_t MissingTrackCnt = 0;
size_t FixedTrackCnt = 0;
for(size_t Track=1;Track<=cTracks;++Track) {
for(size_t Head=0;Head<cHeads;++Head) {
bool TrackValid = true;
size_t MissingSectorsInTrack = 0;
for(size_t Sector=0;Sector<cSectors;++Sector) {
Sector_c &CurSector = Image.GetSector(Head,Track,Sector);
if (!CurSector.IsValid()) {
TrackValid = false;
++MissingSectorsInTrack;
}
}
if (!TrackValid) {
std::cout << "Attempting to fix track " << DecPrinter(Track,0) << ", head " << DecPrinter(Head,0) << "..." << std::endl;
++MissingTrackCnt;
FixTrack(Head,Track,Image);
size_t MissingSectorsInTrackAfterFix = 0;
for(size_t Sector=0;Sector<cSectors;++Sector) {
Sector_c &CurSector = Image.GetSector(Head,Track,Sector);
if (!CurSector.IsValid()) {
TrackValid = false;
++MissingSectorsInTrackAfterFix;
}
}
size_t FixedCnt = MissingSectorsInTrack-MissingSectorsInTrackAfterFix;
std::cout << "Fixed " << DecPrinter(FixedCnt,0) << " sectors out of " << DecPrinter(MissingSectorsInTrack,0) << std::endl;
if (MissingSectorsInTrackAfterFix == 0) ++FixedTrackCnt;
if (FixedCnt > 0) {
std::cout << "Writing back image..." << std::flush;
Image.WriteToDisk(Phase2ImageFileName,Phase2StatFileName,false);
std::cout << " done" << std::endl;
}
MissingSectorCnt -= FixedCnt;
}
}
}
std::cout << "A total of " << DecPrinter(MissingSectorCnt,0) << " sectors remained unfixed." << std::endl;
std::cout << "Fixed " << DecPrinter(FixedTrackCnt,0) << " tracks out of " << DecPrinter(MissingSectorCnt) << " missing." << std::endl;
return 0;
}
int main(int argc, const char* argv[]) {
CommandLine_c CmdLine(argc,argv);
if (argc < 2) return PrintUsage(argv[0]);
std::string Phase = CmdLine.GetNextParam();
if (Phase == "1") {
return ScannerPhase1(CmdLine);
} else if (Phase == "2") {
return ScannerPhase2(CmdLine);
} else {
return PrintUsage(argv[0]);
}
}