import { describe, expect, it } from 'vitest'; import { TAU, angularTravelConstraint, crankSliderDisplacement, formatKinematicValidationReport, phaseRatioConstraint, runKinematicValidation, type KinematicValidationSuite, type ScalarTrack, willisPlanetaryConstraint } from './kinematicIntegrity'; describe('kinematic integrity validators', () => { it('computes crank-slider top and bottom dead-center positions from geometry', () => { const crankRadiusMeters = 0.04; const rodLengthMeters = 0.14; expect(crankSliderDisplacement(0, crankRadiusMeters, rodLengthMeters)).toBeCloseTo( rodLengthMeters + crankRadiusMeters, 12 ); expect(crankSliderDisplacement(Math.PI, crankRadiusMeters, rodLengthMeters)).toBeCloseTo( rodLengthMeters - crankRadiusMeters, 12 ); }); it('passes a correctly constrained fixed-ring planetary gear set', () => { const sunTeeth = 24; const ringTeeth = 72; const carrierRatio = sunTeeth / (sunTeeth + ringTeeth); const sun = scalarTrack('sun', (timeSeconds) => TAU * timeSeconds); const ring = scalarTrack('ring', () => 0); const carrier = scalarTrack('carrier', (timeSeconds) => carrierRatio * sun.sample(timeSeconds)); const suite: KinematicValidationSuite = { id: 'planetary.valid', machineId: 'planetary-gearbox', title: 'Valid planetary gearbox', durationSeconds: 1, sampleCount: 49, constraints: [ willisPlanetaryConstraint({ sunAngle: sun, ringAngle: ring, carrierAngle: carrier, sunTeeth, ringTeeth, tolerance: { absolute: 1e-10, relative: 1e-10 } }), phaseRatioConstraint({ driver: sun, driven: carrier, ratio: carrierRatio, tolerance: { absolute: 1e-10, relative: 1e-10 } }), angularTravelConstraint({ track: carrier, expectedDeltaRadians: TAU * carrierRatio, tolerance: { absolute: 1e-10, relative: 1e-10 } }) ] }; const report = runKinematicValidation([suite]); expect(report.ok, formatKinematicValidationReport(report)).toBe(true); }); it('fails a planetary gearbox when the carrier ratio violates Willis equation', () => { const sunTeeth = 24; const ringTeeth = 72; const sun = scalarTrack('sun', (timeSeconds) => TAU * timeSeconds); const ring = scalarTrack('ring', () => 0); const carrier = scalarTrack('carrier', (timeSeconds) => 0.3 * sun.sample(timeSeconds)); const suite: KinematicValidationSuite = { id: 'planetary.invalid', machineId: 'planetary-gearbox', title: 'Invalid planetary gearbox', durationSeconds: 1, sampleCount: 25, constraints: [ willisPlanetaryConstraint({ sunAngle: sun, ringAngle: ring, carrierAngle: carrier, sunTeeth, ringTeeth, tolerance: { absolute: 1e-10, relative: 1e-10 } }) ] }; const report = runKinematicValidation([suite]); expect(report.ok).toBe(false); expect(report.errorCount).toBeGreaterThan(0); expect(report.suiteReports[0].issues.some((issue) => issue.code === 'planetary.willis')).toBe(true); }); }); function scalarTrack(id: string, sample: (timeSeconds: number) => number): ScalarTrack { return { id, units: 'radians', sample }; }