#define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 << " [ ]" << 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 : Index of the first scan file to read for each head" << std::endl; std::cout << " --EndFileIdx : Index of the last scan file to read for each head - set to 0 to read all files" << std::endl; std::cout << " --StartHead : Index of the first head directory to scan" << std::endl; std::cout << " --EndHead : 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 { 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 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 &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 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 MakeRuns(BitStream_c &aBitStream, double &aBitTime, std::vector *aRunTimes = nullptr, bool aPrintHistograms = cPrintHistograms) { std::vector PulseLengthHistogram0; std::vector PulseLengthHistogram1; PulseLengthHistogram0.resize(256,0); PulseLengthHistogram1.resize(256,0); size_t MaxPulseLength = 0; std::vector 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= aPulseCategorizer.PulseTableSize) aPulseWidth = uint8_t(aPulseCategorizer.PulseTableSize - 1); return aPulseCategorizer.Pulses[aSignalLevel & 1][aPulseWidth]; } size_t SyncScan(const std::vector &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 Data; size_t ErrorRate; }; DecodeResults_s Decode(const std::vector &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 IntBin(uint64_t aNumber, size_t aWidth) { std::vector 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 &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 &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 ToBytes(const std::vector &aBits, size_t aStartIdx, size_t aEndIdx) { std::vector RetVal; if (aEndIdx > aBits.size()) aEndIdx = aBits.size(); for(size_t ByteIdx=aStartIdx;ByteIdx &aSec1, const std::vector &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 &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 &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 &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 Data; size_t ErrorRate; size_t RecoveryCount; SectorInfo_c(): RecoveryCount(0) {} SectorInfo_c(const std::vector &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 &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 &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 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 &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 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 mMissingSector; std::vector mSectorData; std::vector> 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> Sectors; size_t Track; size_t SectorCnt; }; TrackStats_s Extract(const std::vector &aRuns, std::vector 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 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 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 &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 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 write 0-s first, 1-s next size_t Fold; for(Fold=0;Fold &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 RunTimes; std::vector 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 RunTimes; std::vector 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 StrideEstimates; for(size_t Sector=0;Sector &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 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 100.0) break; /*** vector 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 BitStreams; std::vector> RunTimes; std::vector> Runs; std::vector TrackStats; const std::vector &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 TrackStats[DriverIdx].SectorCnt) { DriverIdx = Idx; } } // Calculate stride for folded scans vector StrideEstimates; for(size_t Sector=0;Sector &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 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 100.0) return; Stride = size_t(int(Avg + 0.5)); } // Align the scans over one another vector 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 Anchors; for(size_t Sector=0;Sector &DriverStats = TrackStats[DriverIdx].Sectors[Sector]; vector &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; 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 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 FoldedRunTimes; vector 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 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]); } }