const { SatLocLogParser } = require('./helpers/satloc_log_parser'); /** * Test timestamp parsing with rollover cases */ function testTimestampRollover() { const parser = new SatLocLogParser(); console.log('=== SatLoc Timestamp Rollover Test ===\n'); // Helper function to create timestamp bytes function createTimestamp(year, month, day, hour, minute, seconds, hundredths) { const yearOffset = year - 1993; const yearLow4 = yearOffset & 0x0F; const yearHigh3 = (yearOffset >> 4) & 0x07; // Byte 4: (Y<<4) + Month const byte4 = (yearLow4 << 4) | month; // 4 bytes: ((Y>>4)<<29) + (Day<<24) + (Hour<<19) + (Minute<<13) + (Seconds<<7) + Hundredths const timeValue = ((yearHigh3 << 29) >>> 0) | (day << 24) | (hour << 19) | (minute << 13) | (seconds << 7) | hundredths; const buffer = Buffer.alloc(5); buffer[0] = byte4; buffer.writeUInt32LE(timeValue, 1); return buffer; } // Test cases const testCases = [ // Modern format (7-bit year) - uses high bits { year: 2024, month: 9, day: 11, hour: 14, minute: 30, seconds: 45, hundredths: 50, desc: 'Modern format: 2024' }, ]; // Test legacy format by creating timestamps that only use 4-bit year (force high bits to 0) const legacyTestCases = [ { year: 1993, month: 1, day: 1, hour: 0, minute: 0, seconds: 0, hundredths: 0, desc: 'Legacy format: 1993' }, { year: 2000, month: 6, day: 15, hour: 12, minute: 0, seconds: 0, hundredths: 0, desc: 'Legacy format: 2000' }, { year: 2008, month: 12, day: 31, hour: 23, minute: 59, seconds: 59, hundredths: 99, desc: 'Legacy format: 2008 (last before rollover)' }, ]; function createLegacyTimestamp(year, month, day, hour, minute, seconds, hundredths) { const yearOffset = year - 1993; const yearLow4 = yearOffset & 0x0F; // Only use low 4 bits, ignore high bits // Byte 4: (Y<<4) + Month - only low 4 bits of year const byte4 = (yearLow4 << 4) | month; // 4 bytes: NO high year bits (yearHigh3 = 0) const timeValue = (day << 24) | (hour << 19) | (minute << 13) | (seconds << 7) | hundredths; const buffer = Buffer.alloc(5); buffer[0] = byte4; buffer.writeUInt32LE(timeValue, 1); return buffer; } // Test legacy rollover cases manually by creating 4-bit only timestamps console.log('Testing legacy 4-bit rollover cases:\n'); // Create timestamps that only use 4-bit year (high 3 bits = 0) const rolloverTestCases = [ { yearOffset: 0, expectedYear: 1993, desc: 'Legacy: offset 0 -> 1993' }, { yearOffset: 7, expectedYear: 2000, desc: 'Legacy: offset 7 -> 2000' }, { yearOffset: 15, expectedYear: 2008, desc: 'Legacy: offset 15 -> 2008 (last before rollover)' }, { yearOffset: 0, expectedYear: 2009, desc: 'Legacy rollover: offset 0 -> 2009 (if detected as rollover)' }, { yearOffset: 5, expectedYear: 2014, desc: 'Legacy rollover: offset 5 -> 2014 (if detected as rollover)' }, ]; rolloverTestCases.forEach((testCase, index) => { const { yearOffset, expectedYear, desc } = testCase; // Create legacy format timestamp (high 3 bits = 0) const buffer = Buffer.alloc(5); const month = 6; const day = 15; const hour = 12; const minute = 30; const seconds = 45; const hundredths = 50; // Byte 4: only low 4 bits for year buffer[0] = (yearOffset << 4) | month; // 4 bytes: no high year bits (yearHigh3 = 0) const timeValue = (day << 24) | (hour << 19) | (minute << 13) | (seconds << 7) | hundredths; buffer.writeUInt32LE(timeValue, 1); const result = parser.parseTimestamp(buffer, 0); console.log(`${desc}:`); console.log(` Input: yearOffset=${yearOffset}, month=${month}, day=${day}`); console.log(` Bytes: [${Array.from(buffer).map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}]`); if (result) { console.log(` Parsed: ${result.year}-${result.month.toString().padStart(2, '0')}-${result.day.toString().padStart(2, '0')} ${result.hour.toString().padStart(2, '0')}:${result.minute.toString().padStart(2, '0')}:${result.seconds.toString().padStart(2, '0')}.${result.milliseconds.toString().padStart(3, '0')}`); console.log(` Year: ${result.year} (expected around ${expectedYear})`); } else { console.log(` Parsed: null (invalid)`); } console.log(); }); // Test normal cases console.log('Testing normal cases:\n'); testCases.forEach((testCase, index) => { const { year, month, day, hour, minute, seconds, hundredths, desc } = testCase; const buffer = createTimestamp(year, month, day, hour, minute, seconds, hundredths); const result = parser.parseTimestamp(buffer, 0); console.log(`${desc}:`); console.log(` Input: ${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')} ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${(hundredths * 10).toString().padStart(3, '0')}`); console.log(` Bytes: [${Array.from(buffer).map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}]`); if (result) { console.log(` Parsed: ${result.year}-${result.month.toString().padStart(2, '0')}-${result.day.toString().padStart(2, '0')} ${result.hour.toString().padStart(2, '0')}:${result.minute.toString().padStart(2, '0')}:${result.seconds.toString().padStart(2, '0')}.${result.milliseconds.toString().padStart(3, '0')}`); const match = result.year === year && result.month === month && result.day === day && result.hour === hour && result.minute === minute && result.seconds === seconds && result.milliseconds === hundredths * 10; console.log(` Match: ${match ? 'PASS' : 'FAIL'}`); } else { console.log(` Parsed: null (invalid)`); } console.log(); }); // Test legacy format cases console.log('Testing legacy format cases:\n'); legacyTestCases.forEach((testCase, index) => { const { year, month, day, hour, minute, seconds, hundredths, desc } = testCase; const buffer = createLegacyTimestamp(year, month, day, hour, minute, seconds, hundredths); const result = parser.parseTimestamp(buffer, 0); console.log(`${desc}:`); console.log(` Input: ${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')} ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${(hundredths * 10).toString().padStart(3, '0')}`); console.log(` Bytes: [${Array.from(buffer).map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}]`); if (result) { console.log(` Parsed: ${result.year}-${result.month.toString().padStart(2, '0')}-${result.day.toString().padStart(2, '0')} ${result.hour.toString().padStart(2, '0')}:${result.minute.toString().padStart(2, '0')}:${result.seconds.toString().padStart(2, '0')}.${result.milliseconds.toString().padStart(3, '0')}`); // For legacy formats, we expect rollover correction for recent years const expectedYear = year <= 2008 && (new Date().getFullYear() - year) > 15 ? year + 16 : year; const match = result.year === expectedYear && result.month === month && result.day === day && result.hour === hour && result.minute === minute && result.seconds === seconds && result.milliseconds === hundredths * 10; console.log(` Expected Year: ${expectedYear} (rollover ${expectedYear !== year ? 'applied' : 'not applied'})`); console.log(` Match: ${match ? 'PASS' : 'FAIL'}`); } else { console.log(` Parsed: null (invalid)`); } console.log(); }); } if (require.main === module) { testTimestampRollover(); } module.exports = { testTimestampRollover };