agmission/Development/server/tests/test_timestamp_rollover.js

166 lines
7.8 KiB
JavaScript

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 };