diff --git a/Eu07.BlackBox/HeadCodes.cs b/Eu07.BlackBox/HeadCodes.cs index ad482bf..ccd98ea 100644 --- a/Eu07.BlackBox/HeadCodes.cs +++ b/Eu07.BlackBox/HeadCodes.cs @@ -101,7 +101,8 @@ public static class HeadCodes var pattern = (Lights)hint; if (usual == HeadCode.Pc1 && pattern == (Lights.RedMarkerLeft | Lights.HeadlightRight | Lights.HeadlightUpper)) return Shows(HeadCode.Pc2, lit, equipped); - return (lit & ~HighBeams) == (equipped is { } lamps ? pattern & lamps : pattern); + // a recording made before the lamps were written: the signal the pattern shows, as before + return equipped is { } lamps ? (lit & ~HighBeams) == (pattern & lamps) : Of(lit) == Of(pattern); } /// diff --git a/Eu07.BlackBox/Recording.cs b/Eu07.BlackBox/Recording.cs index c731aca..50f2fdd 100644 --- a/Eu07.BlackBox/Recording.cs +++ b/Eu07.BlackBox/Recording.cs @@ -407,9 +407,15 @@ public sealed class Recording if (s.Vehicle == vehicle) return true; // The couplings tell the train exactly; a recording made before them has the distance only. - return _couplings.Count > 0 ? ConsistAt(s.Vehicle, t).Contains(vehicle) : s.Position.DistanceTo(place) <= reach; + return CouplingsWritten ? ConsistAt(s.Vehicle, t).Contains(vehicle) : s.Position.DistanceTo(place) <= reach; } + /// + /// Whether the couplings were written: a recording made since has the movement in its cab samples, and one with + /// no train of more than one vehicle has no coupling at all. + /// + private bool CouplingsWritten => _couplings.Count > 0 || (_cab.Count > 0 && _cab[0].Movement != Movement.Unknown); + /// The vehicles coupled into one train with the one given at t, itself included. public IReadOnlySet ConsistAt(int vehicle, double t) { @@ -476,7 +482,7 @@ public sealed class Recording { var timetable = LastAtOrBefore(_timetables, t, x => x.Time) is var i and >= 0 ? _timetables[i] : _timetables.FirstOrDefault(); // A recording made before the couplings were written can't tell the trains apart: the timetable stays. - if (timetable is null || CabAt(t) is not { } sample || sample.Vehicle == timetable.Vehicle || _couplings.Count == 0) + if (timetable is null || CabAt(t) is not { } sample || sample.Vehicle == timetable.Vehicle || !CouplingsWritten) return timetable; // another vehicle of the same train (the other cab, a car of it) keeps the timetable return ConsistAt(sample.Vehicle, t).Contains(timetable.Vehicle) ? timetable : null; diff --git a/Eu07.BlackBox/Records.cs b/Eu07.BlackBox/Records.cs index 89d08d7..44a7d90 100644 --- a/Eu07.BlackBox/Records.cs +++ b/Eu07.BlackBox/Records.cs @@ -240,9 +240,9 @@ public sealed record StationEvent(double Time, int Vehicle, StationKind Kind, in /// Whether the whole train stood along the platform, when its length is known: as the driver takes it, the length of /// the trains a W32 is for is the room it has there too. /// - public bool? OnPlatform => Platform != 0 ? OffPlatform <= PlatformTolerance : null; + public bool? OnPlatform => Platform != 0 ? -Distance <= PlatformTolerance && Distance + TrainLength - PlatformLength <= PlatformTolerance : null; - /// How far off the platform a train may stand and still count as along it [m]. + /// How far off the platform a train may stand at either end and still count as along it [m]. public const double PlatformTolerance = 2; /// The length of the platform [m], without the digit of its side. diff --git a/StarterNG.Tests/BlackBox/BlackBoxSignalsTests.cs b/StarterNG.Tests/BlackBox/BlackBoxSignalsTests.cs index 1a565bf..ae05cd9 100644 --- a/StarterNG.Tests/BlackBox/BlackBoxSignalsTests.cs +++ b/StarterNG.Tests/BlackBox/BlackBoxSignalsTests.cs @@ -124,6 +124,16 @@ public class HeadCodeTests Assert.Empty(summary.LightsFaults); } + [Fact] + public void An_older_recording_reads_a_pattern_asked_for_as_the_signal_it_shows() + { + // Pc 5 asked for at the tail, the rear-end signals lit, no lamps written: Pc 5 as before + var f = new BlackBoxFile().Info(); + for (double t = 0; t < 30; t += 0.5) + f.Cab(t, 1, 1000 + t * 10, 40, lightsHead: Pc1, lightsTail: 0x40, movement: 1, hintTail: 0x22); + Assert.Empty(BlackBoxReader.Read(f.End(30).Stream()).Summarize().LightsFaults); + } + [Fact] public void An_older_recording_tells_nothing_of_the_signals() { @@ -155,6 +165,32 @@ public class UserVehicleTests Assert.Equal("ROJ1", r.TimetableAt(70)!.Train); Assert.Null(r.TimetableAt(120)); } + + [Fact] + public void Single_vehicles_with_no_coupling_written_are_still_told_apart() + { + // A recording made with the movement written, where no train is longer than one vehicle: no coupling at all. + var f = new BlackBoxFile().Info() + .Timetable(0, 1, "ROJ1", "A", "B", ("A", -1, 635), ("B", 640, -1)); + for (double t = 0; t < 100; t += 0.5) + f.Cab(t, t < 50 ? 1u : 9u, 1000, 0, movement: 1); + var r = BlackBoxReader.Read(f.End(100).Stream()); + + Assert.Equal("ROJ1", r.TimetableAt(20)!.Train); + Assert.Null(r.TimetableAt(70)); + } + + [Fact] + public void An_older_recording_with_no_coupling_keeps_the_timetable() + { + var f = new BlackBoxFile().Info() + .Timetable(0, 1, "ROJ1", "A", "B", ("A", -1, 635), ("B", 640, -1)); + for (double t = 0; t < 100; t += 0.5) + f.Cab(t, t < 50 ? 1u : 2u, 1000, 0); + var r = BlackBoxReader.Read(f.End(100).Stream()); + + Assert.Equal("ROJ1", r.TimetableAt(70)!.Train); + } } /// The departures and the platforms in the summary. diff --git a/StarterNG.Tests/BlackBox/PlatformTests.cs b/StarterNG.Tests/BlackBox/PlatformTests.cs index 2b4a773..73a0145 100644 --- a/StarterNG.Tests/BlackBox/PlatformTests.cs +++ b/StarterNG.Tests/BlackBox/PlatformTests.cs @@ -47,6 +47,15 @@ public class PlatformTests Assert.Equal(1, stop.OffPlatform, 1); } + [Fact] + public void Each_end_has_its_own_tolerance() + { + // the head 1.5 m past the stop point, the tail 1.5 m past the platform's end + var stop = Stop(-1.5f, 204f, 201); + Assert.True(stop.OnPlatform); + Assert.Equal(3, stop.OffPlatform, 1); + } + [Fact] public void A_train_within_the_platform_is_along_it() {