public void BaseStationMessageTranslator_Translate_Translates_Messages_Correctly() { ExcelWorksheetData worksheet = new ExcelWorksheetData(TestContext); BaseStationMessage translated = _Implementation.Translate(worksheet.String("Text"), 123); Assert.AreEqual(Enum.Parse(typeof(BaseStationMessageType), worksheet.String("MessageType")), translated.MessageType); Assert.AreEqual(Enum.Parse(typeof(BaseStationTransmissionType), worksheet.String("TransmissionType")), translated.TransmissionType); Assert.AreEqual(Enum.Parse(typeof(BaseStationStatusCode), worksheet.String("StatusCode")), translated.StatusCode); Assert.AreEqual(worksheet.Int("SessionId"), translated.SessionId); Assert.AreEqual(worksheet.Int("AircraftId"), translated.AircraftId); Assert.AreEqual(worksheet.String("Icao24"), translated.Icao24); Assert.AreEqual(worksheet.Int("FlightId"), translated.FlightId); Assert.AreEqual(worksheet.DateTime("MessageGenerated"), translated.MessageGenerated); Assert.AreEqual(worksheet.DateTime("MessageLogged"), translated.MessageLogged); Assert.AreEqual(worksheet.String("Callsign"), translated.Callsign); Assert.AreEqual(worksheet.NFloat("Altitude"), translated.Altitude); Assert.AreEqual(worksheet.NFloat("GroundSpeed"), translated.GroundSpeed); Assert.AreEqual(worksheet.NFloat("Track"), translated.Track); Assert.AreEqual(worksheet.NDouble("Latitude"), translated.Latitude); Assert.AreEqual(worksheet.NDouble("Longitude"), translated.Longitude); Assert.AreEqual(worksheet.NFloat("VerticalRate"), translated.VerticalRate); Assert.AreEqual(worksheet.NInt("Squawk"), translated.Squawk); Assert.AreEqual(worksheet.NBool("SquawkHasChanged"), translated.SquawkHasChanged); Assert.AreEqual(worksheet.NBool("Emergency"), translated.Emergency); Assert.AreEqual(worksheet.NBool("IdentActive"), translated.IdentActive); Assert.AreEqual(worksheet.NBool("OnGround"), translated.OnGround); Assert.AreEqual(worksheet.Bool("IsMlat"), translated.IsMlat); }
public void BaseStationMessageTranslator_Translate_Ignores_TransmissionType_For_All_NonTransmission_Messages() { foreach (BaseStationMessageType messageType in Enum.GetValues(typeof(BaseStationMessageType))) { if (messageType == BaseStationMessageType.Unknown) { continue; } foreach (BaseStationTransmissionType transmissionType in Enum.GetValues(typeof(BaseStationTransmissionType))) { if (transmissionType == BaseStationTransmissionType.None) { continue; } string text = String.Format("{0},{1},5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,,,,,,,,,,,,", BaseStationMessageHelper.ConvertToString(messageType), BaseStationMessageHelper.ConvertToString(transmissionType)); string errorText = String.Format("{0} and {1}", messageType, transmissionType); BaseStationMessage message = _Implementation.Translate(text, 123); if (messageType == BaseStationMessageType.Transmission) { Assert.AreEqual(transmissionType, message.TransmissionType, errorText); } else { Assert.AreEqual(BaseStationTransmissionType.None, message.TransmissionType, errorText); } } } }
/// <summary> /// Returns false if the message doesn't carry useful information and can be discarded. /// </summary> /// <param name="message"></param> /// <returns></returns> private static bool IsMeaningfulMessage(BaseStationMessage message) { return(message.MessageType == BaseStationMessageType.Transmission && message.TransmissionType != BaseStationTransmissionType.AllCallReply && message.TransmissionType != BaseStationTransmissionType.None && message.Icao24 != null && message.Icao24.Length == 6); }
public void TestInitialise() { _Server = Factory.Singleton.Resolve <IRebroadcastServer>(); _Listener = new Mock <IListener>(MockBehavior.Default) { DefaultValue = DefaultValue.Mock }.SetupAllProperties(); _BroadcastProvider = new Mock <IBroadcastProvider>(MockBehavior.Default) { DefaultValue = DefaultValue.Mock }.SetupAllProperties(); _SentBytes = new List <byte[]>(); _BroadcastProvider.Setup(r => r.Send(It.IsAny <byte[]>())).Callback((byte[] bytes) => _SentBytes.Add(bytes)); _Server.Listener = _Listener.Object; _Server.BroadcastProvider = _BroadcastProvider.Object; _ExceptionCaughtEvent = new EventRecorder <EventArgs <Exception> >(); _ExceptionCaughtEvent.EventRaised += DefaultExceptionCaughtHandler; _OnlineChangedEvent = new EventRecorder <EventArgs>(); _Server.ExceptionCaught += _ExceptionCaughtEvent.Handler; _Server.OnlineChanged += _OnlineChangedEvent.Handler; _Port30003Message = new BaseStationMessage() { Icao24 = "313233" }; }
/// <summary> /// Updates the counters of total messages for a flight. /// </summary> /// <param name="message"></param> /// <param name="flight"></param> private static void UpdateMessageCounters(BaseStationMessage message, BaseStationFlight flight) { switch (message.TransmissionType) { case BaseStationTransmissionType.IdentificationAndCategory: ++flight.NumIDMsgRec; break; case BaseStationTransmissionType.SurfacePosition: ++flight.NumSurPosMsgRec; break; case BaseStationTransmissionType.AirbornePosition: ++flight.NumAirPosMsgRec; break; case BaseStationTransmissionType.AirborneVelocity: ++flight.NumAirVelMsgRec; break; case BaseStationTransmissionType.SurveillanceAlt: ++flight.NumSurAltMsgRec; break; case BaseStationTransmissionType.SurveillanceId: ++flight.NumSurIDMsgRec; break; case BaseStationTransmissionType.AirToAir: ++flight.NumAirToAirMsgRec; break; case BaseStationTransmissionType.AllCallReply: ++flight.NumAirCallRepMsgRec; break; } if (message.Latitude == null && message.Longitude == null && message.GroundSpeed == null && message.Track == null && message.VerticalRate == null) { ++flight.NumModeSMsgRec; } else { ++flight.NumADSBMsgRec; if (message.Latitude != null && message.Longitude != null) { ++flight.NumPosMsgRec; } } }
public void BaseStationMessageTranslator_Translate_Only_Translate_Status_Codes_For_Status_Messages() { foreach (BaseStationMessageType messageType in Enum.GetValues(typeof(BaseStationMessageType))) { if (messageType == BaseStationMessageType.Unknown) { continue; } foreach (BaseStationStatusCode statusCode in Enum.GetValues(typeof(BaseStationStatusCode))) { if (statusCode == BaseStationStatusCode.None) { continue; } string text = String.Format("{0},1,5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,{1},,,,,,,,,,,", BaseStationMessageHelper.ConvertToString(messageType), BaseStationMessageHelper.ConvertToString(statusCode)); string errorText = String.Format("{0} and {1}", messageType, statusCode); BaseStationMessage message = _Implementation.Translate(text, 123); if (messageType == BaseStationMessageType.StatusChange) { Assert.AreEqual(statusCode, message.StatusCode, errorText); } else { Assert.AreEqual(BaseStationMessageHelper.ConvertToString(statusCode), message.Callsign, errorText); } } } }
private void AddSupplementaryValue(BaseStationMessage message, Action <BaseStationSupplementaryMessage> setValue) { if (message.Supplementary == null) { message.Supplementary = new BaseStationSupplementaryMessage(); } setValue(message.Supplementary); }
/// <summary> /// Creates database records and updates internal objects to track an aircraft that is currently transmitting messages. /// </summary> /// <param name="message"></param> private void TrackFlight(BaseStationMessage message) { if (IsTransmissionMessage(message)) { lock (_SyncLock) { if (_Session != null) { var localNow = Provider.LocalNow; FlightRecords flightRecords; if (!_FlightMap.TryGetValue(message.Icao24, out flightRecords)) { flightRecords = new FlightRecords(); _Database.StartTransaction(); try { flightRecords.Aircraft = FetchOrCreateAircraft(localNow, message.Icao24); flightRecords.Flight = CreateFlight(localNow, flightRecords.Aircraft.AircraftID, message.Callsign); flightRecords.EndTimeUtc = Provider.UtcNow; _Database.EndTransaction(); } catch (ThreadAbortException) { } catch (Exception ex) { Debug.WriteLine(String.Format("BaseStationDatabaseWriter.Plugin.TrackFlight caught exception {0}", ex.ToString())); _Database.RollbackTransaction(); throw; } _FlightMap.Add(message.Icao24, flightRecords); } else { if (flightRecords.Flight.Callsign.Length == 0 && !String.IsNullOrEmpty(message.Callsign)) { var databaseVersion = _Database.GetFlightById(flightRecords.Flight.FlightID); flightRecords.Flight.Callsign = databaseVersion.Callsign = message.Callsign; _Database.UpdateFlight(databaseVersion); } } var flight = flightRecords.Flight; flightRecords.EndTimeUtc = Provider.UtcNow; flight.EndTime = localNow; if (message.SquawkHasChanged.GetValueOrDefault()) { flight.HadAlert = true; } if (message.IdentActive.GetValueOrDefault()) { flight.HadSpi = true; } if (message.Squawk == 7500 || message.Squawk == 7600 || message.Squawk == 7700) { flight.HadEmergency = true; } UpdateFirstLastValues(message, flight, flightRecords); UpdateMessageCounters(message, flight); } } } }
public void TestInitialise() { _Compressor = Factory.Singleton.Resolve <IBaseStationMessageCompressor>(); _Message = new BaseStationMessage(); _Message.MessageType = BaseStationMessageType.Transmission; _Message.TransmissionType = BaseStationTransmissionType.SurveillanceAlt; _Message.Icao24 = "405012"; }
public void BaseStationMessageTranslator_Translate_Parses_Transmission_Types_Correctly() { foreach(BaseStationTransmissionType transmissionType in Enum.GetValues(typeof(BaseStationTransmissionType))) { if(transmissionType == BaseStationTransmissionType.None) continue; string typeText = BaseStationMessageHelper.ConvertToString(transmissionType); BaseStationMessage message = _Implementation.Translate(String.Format("MSG,{0},5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,,,,,,,,,,,,", typeText)); Assert.AreEqual(transmissionType, message.TransmissionType, transmissionType.ToString()); } }
public void BaseStationMessageTranslator_Translate_Parses_Status_Codes_Correctly() { foreach(BaseStationStatusCode statusCode in Enum.GetValues(typeof(BaseStationStatusCode))) { if(statusCode == BaseStationStatusCode.None) continue; string text = String.Format("STA,1,5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,{0},,,,,,,,,,,", BaseStationMessageHelper.ConvertToString(statusCode)); BaseStationMessage message = _Implementation.Translate(text); Assert.AreEqual(statusCode, message.StatusCode, statusCode.ToString()); } }
public void BaseStationMessageTranslator_Translate_Parses_Message_Types_Correctly() { foreach(BaseStationMessageType messageType in Enum.GetValues(typeof(BaseStationMessageType))) { if(messageType == BaseStationMessageType.Unknown) continue; string messageTypeText = BaseStationMessageHelper.ConvertToString(messageType); string text = String.Format("{0},1,5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,,,,,,,,,,,,", messageTypeText); BaseStationMessage message = _Implementation.Translate(text); Assert.AreEqual(messageType, message.MessageType, messageType.ToString()); } }
private BaseStationMessage CreateBaseStationMessage(Aircraft aircraft, long startVersionExclusive, DateTime now) { var result = new BaseStationMessage() { Icao24 = aircraft.Icao24, MessageGenerated = now, MessageLogged = now, MessageType = BaseStationMessageType.Transmission, TransmissionType = BaseStationTransmissionType.SurfacePosition, StatusCode = BaseStationStatusCode.OK, }; bool versionQualifies <T>(VersionedValue <T> value) => value.Version > startVersionExclusive && value.Value != null; if (versionQualifies(aircraft.AltitudeFeet)) { result.Altitude = (int)aircraft.AltitudeFeet; } if (versionQualifies(aircraft.Callsign)) { result.Callsign = aircraft.Callsign; } if (versionQualifies(aircraft.GroundSpeedKnots)) { result.GroundSpeed = aircraft.GroundSpeedKnots; } if (versionQualifies(aircraft.OnGround)) { result.OnGround = aircraft.OnGround; } if (versionQualifies(aircraft.SpecialPurposeIndicator)) { result.IdentActive = aircraft.SpecialPurposeIndicator; } if (versionQualifies(aircraft.Squawk)) { result.Squawk = ConvertSquawk(aircraft.Squawk); } if (versionQualifies(aircraft.Track)) { result.Track = aircraft.Track; } if (versionQualifies(aircraft.VerticalRateFeetPerSecond)) { result.VerticalRate = (int)aircraft.VerticalRateFeetPerSecond; } if (versionQualifies(aircraft.Latitude) || versionQualifies(aircraft.Longitude)) { result.Latitude = aircraft.Latitude; result.Longitude = aircraft.Longitude; } return(result); }
public void BaseStationMessage_ToString_Only_Shows_1_Decimal_Place_For_GroundSpeed() { var message = new BaseStationMessage() { GroundSpeed = 0.654321F, }; var text = message.ToBaseStationString(); Assert.AreNotEqual(-1, text.IndexOf(",0.7,")); }
public void TestInitialise() { _OriginalFactory = Factory.TakeSnapshot(); _Clock = new ClockMock(); _Timer = TestUtilities.CreateMockImplementation <ITimer>(); _Compressor = TestUtilities.CreateMockImplementation <IBaseStationMessageCompressor>(); _Server = Factory.Resolve <IRebroadcastServer>(); _Feed = TestUtilities.CreateMockInstance <IFeed>(); _Listener = TestUtilities.CreateMockInstance <IListener>(); _Feed.SetupGet(r => r.Listener).Returns(_Listener.Object); _Connector = new MockConnector <INetworkConnector, INetworkConnection>(); _AircraftListJsonBuilder = TestUtilities.CreateMockImplementation <IAircraftListJsonBuilder>(); _AircraftListJson = new AircraftListJson(); _AircraftListJsonBuilderArgs = null; _IgnoreInvisibleFeeds = null; _FallbackToDefault = null; _AircraftListJsonBuilder.Setup(r => r.Build(It.IsAny <AircraftListJsonBuilderArgs>(), It.IsAny <bool>(), It.IsAny <bool>())).Callback((AircraftListJsonBuilderArgs args, bool ignoreInvisibleFeeds, bool fallbackToDefault) => { _AircraftListJsonBuilderArgs = args; _IgnoreInvisibleFeeds = ignoreInvisibleFeeds; _FallbackToDefault = fallbackToDefault; }).Returns(_AircraftListJson); _AircraftList = TestUtilities.CreateMockImplementation <IBaseStationAircraftList>(); _Feed.SetupGet(r => r.AircraftList).Returns(_AircraftList.Object); _SnapshotAircraft = new List <IAircraft>(); long of1, of2; _AircraftList.Setup(m => m.TakeSnapshot(out of1, out of2)).Returns(_SnapshotAircraft); _Server.UniqueId = 1; _Server.Name = "It's the code word"; _Server.Format = RebroadcastFormat.Port30003; _Server.Feed = _Feed.Object; _Server.Connector = _Connector.Object; _ExceptionCaughtEvent = new EventRecorder <EventArgs <Exception> >(); _ExceptionCaughtEvent.EventRaised += DefaultExceptionCaughtHandler; _OnlineChangedEvent = new EventRecorder <EventArgs>(); _Server.ExceptionCaught += _ExceptionCaughtEvent.Handler; _Server.OnlineChanged += _OnlineChangedEvent.Handler; _Port30003Message = new BaseStationMessage() { Icao24 = "313233" }; }
public void BaseStationMessage_ToString_Only_Shows_6_Decimal_Places_For_Latitude_And_Longitude() { var message = new BaseStationMessage() { Latitude = 0.123456789012345, Longitude = 0.543210987654321, }; var text = message.ToBaseStationString(); Assert.AreNotEqual(-1, text.IndexOf(",0.123457,")); Assert.AreNotEqual(-1, text.IndexOf(",0.543211,")); }
/// <summary> /// See interface docs. /// </summary> /// <param name="message"></param> /// <returns></returns> public byte[] Compress(BaseStationMessage message) { byte[] result = null; if (IsMeaningfulMessage(message)) { using (var stream = new MemoryStream()) { using (var writer = new BinaryWriter(stream)) { stream.WriteByte(0); // <-- reserve a byte for the buffer length writer.Write((ushort)0); // <-- reserve a word for the checksum stream.WriteByte((byte)(int)message.TransmissionType); EncodeIcao(stream, message.Icao24); long optionalFlagsOffset = stream.Position; stream.WriteByte(0); // <-- reserve two bytes for the optional fields flags stream.WriteByte(0); // <-- " OptionalFields optionalFields = OptionalFields.None; if (!String.IsNullOrEmpty(message.Callsign)) { optionalFields |= OptionalFields.CallSign; EncodeString(stream, message.Callsign); } if (message.Altitude != null) { optionalFields |= OptionalFields.Altitude; EncodeFloatInt(stream, message.Altitude.Value); } if (message.GroundSpeed != null) { optionalFields |= OptionalFields.GroundSpeed; EncodeFloatShort(writer, message.GroundSpeed.Value); } if (message.Track != null) { optionalFields |= OptionalFields.Track; EncodeFloatShort(writer, message.Track.Value * 10f); } if (message.Latitude != null) { optionalFields |= OptionalFields.Latitude; EncodeFloat(writer, (float)message.Latitude.Value); } if (message.Longitude != null) { optionalFields |= OptionalFields.Longitude; EncodeFloat(writer, (float)message.Longitude.Value); } if (message.VerticalRate != null) { optionalFields |= OptionalFields.VerticalRate; EncodeFloatShort(writer, message.VerticalRate.Value); } if (message.Squawk != null) { optionalFields |= OptionalFields.Squawk; EncodeShort(writer, (short)Math.Max(short.MinValue, Math.Min(short.MaxValue, message.Squawk.Value))); } CompressedFlags flags = CompressedFlags.None; bool hasFlag = false; if (message.SquawkHasChanged != null) { optionalFields |= OptionalFields.SquawkHasChanged; hasFlag = true; if (message.SquawkHasChanged.Value) { flags |= CompressedFlags.SquawkHasChanged; } } if (message.Emergency != null) { optionalFields |= OptionalFields.Emergency; hasFlag = true; if (message.Emergency.Value) { flags |= CompressedFlags.Emergency; } } if (message.IdentActive != null) { optionalFields |= OptionalFields.IdentActive; hasFlag = true; if (message.IdentActive.Value) { flags |= CompressedFlags.IdentActive; } } if (message.OnGround != null) { optionalFields |= OptionalFields.OnGround; hasFlag = true; if (message.OnGround.Value) { flags |= CompressedFlags.OnGround; } } if (hasFlag) { stream.WriteByte((byte)flags); } stream.Seek(optionalFlagsOffset, SeekOrigin.Begin); EncodeShort(writer, (short)optionalFields); } result = stream.ToArray(); if (result.Length != 0) { result[0] = (byte)result.Length; var checksum = CalculateChecksum(result); result[1] = (byte)(checksum & 0x00ff); result[2] = (byte)((checksum & 0xff00) >> 8); } } } return(result ?? new byte[0]); }
/// <summary> /// Updates the FirstX / LastX pairs of values of an in-store flight record. /// </summary> /// <param name="message"></param> /// <param name="flight"></param> /// <param name="flightRecords"></param> /// <param name="isMlat"></param> private static void UpdateFirstLastValues(BaseStationMessage message, BaseStationFlight flight, FlightRecords flightRecords, bool isMlat) { bool isLocationZeroZero = message.Latitude.GetValueOrDefault() == 0F && message.Longitude.GetValueOrDefault() == 0F; if (message.Latitude != null && !isLocationZeroZero) { if (flight.FirstLat == null) { flight.FirstLat = message.Latitude; } flight.LastLat = message.Latitude; } if (message.Longitude != null && !isLocationZeroZero) { if (flight.FirstLon == null) { flight.FirstLon = message.Longitude; } flight.LastLon = message.Longitude; } if (message.Track != null) { if (flight.FirstTrack == null) { flight.FirstTrack = message.Track; } flight.LastTrack = message.Track; } if (!isMlat) { if (message.Altitude != null) { if (flight.FirstAltitude == null) { flight.FirstAltitude = message.Altitude; } flight.LastAltitude = message.Altitude; } if (message.GroundSpeed != null) { if (flight.FirstGroundSpeed == null) { flight.FirstGroundSpeed = message.GroundSpeed; } flight.LastGroundSpeed = message.GroundSpeed; } if (message.OnGround != null) { if (flightRecords.OnGround == null) { flightRecords.OnGround = flight.FirstIsOnGround = message.OnGround.Value; } flight.LastIsOnGround = message.OnGround.Value; } if (message.Squawk != null) { if (flight.FirstSquawk == null) { flight.FirstSquawk = message.Squawk; } flight.LastSquawk = message.Squawk; } if (message.VerticalRate != null) { if (flight.FirstVerticalRate == null) { flight.FirstVerticalRate = message.VerticalRate; } flight.LastVerticalRate = message.VerticalRate; } } }
public void BaseStationMessageCompressor_Compress_And_Decompress_Work_As_Expected() { ExcelWorksheetData worksheet = new ExcelWorksheetData(TestContext); var messageIn = new BaseStationMessage(); messageIn.MessageType = worksheet.ParseEnum <BaseStationMessageType>("MessageType"); messageIn.TransmissionType = worksheet.ParseEnum <BaseStationTransmissionType>("TransmissionType"); messageIn.StatusCode = worksheet.ParseEnum <BaseStationStatusCode>("StatusCode"); messageIn.Icao24 = worksheet.EString("Icao24"); messageIn.SessionId = worksheet.Int("SessionId"); messageIn.AircraftId = worksheet.Int("AircraftId"); messageIn.FlightId = worksheet.Int("FlightId"); messageIn.MessageGenerated = worksheet.DateTime("MessageGenerated"); messageIn.MessageLogged = worksheet.DateTime("MessageLogged"); messageIn.Callsign = worksheet.String("Callsign"); messageIn.Altitude = worksheet.NInt("Altitude"); messageIn.GroundSpeed = worksheet.NInt("GroundSpeed"); messageIn.Track = worksheet.NFloat("Track"); messageIn.Latitude = worksheet.NDouble("Latitude"); messageIn.Longitude = worksheet.NDouble("Longitude"); messageIn.VerticalRate = worksheet.NInt("VerticalRate"); messageIn.Squawk = worksheet.NInt("Squawk"); messageIn.SquawkHasChanged = worksheet.NBool("SquawkHasChanged"); messageIn.Emergency = worksheet.NBool("Emergency"); messageIn.IdentActive = worksheet.NBool("IdentActive"); messageIn.OnGround = worksheet.NBool("OnGround"); int expectedLength = worksheet.Int("Length"); byte[] bytes = _Compressor.Compress(messageIn); Assert.AreEqual(expectedLength, bytes.Length); DateTime earliestDate = DateTime.Now; BaseStationMessage messageOut = _Compressor.Decompress(bytes); DateTime latestDate = DateTime.Now; if (bytes.Length == 0) { Assert.IsNull(messageOut); } else { Assert.AreEqual(messageIn.MessageType, messageOut.MessageType); Assert.AreEqual(messageIn.TransmissionType, messageOut.TransmissionType); Assert.AreEqual(BaseStationStatusCode.None, messageOut.StatusCode); Assert.AreEqual(messageIn.Icao24, messageOut.Icao24); Assert.AreEqual(0, messageOut.SessionId); Assert.AreEqual(0, messageOut.AircraftId); Assert.AreEqual(0, messageOut.FlightId); Assert.AreEqual((double)earliestDate.Ticks, (double)messageOut.MessageGenerated.Ticks, (double)latestDate.Ticks - (double)earliestDate.Ticks); Assert.AreEqual((double)earliestDate.Ticks, (double)messageOut.MessageLogged.Ticks, (double)latestDate.Ticks - (double)earliestDate.Ticks); Assert.AreEqual(messageIn.Callsign, messageOut.Callsign); Assert.AreEqual(messageIn.Altitude, messageOut.Altitude); Assert.AreEqual(messageIn.GroundSpeed, messageOut.GroundSpeed); Assert.AreEqual(messageIn.Track, messageOut.Track); if (messageIn.Latitude == null) { Assert.IsNull(messageOut.Latitude); } else { Assert.AreEqual(messageIn.Latitude.Value, messageOut.Latitude.Value, 0.000001); } if (messageIn.Longitude == null) { Assert.IsNull(messageOut.Longitude); } else { Assert.AreEqual(messageIn.Longitude.Value, messageOut.Longitude.Value, 0.000001); } Assert.AreEqual(messageIn.VerticalRate, messageOut.VerticalRate); Assert.AreEqual(messageIn.Squawk, messageOut.Squawk); Assert.AreEqual(messageIn.SquawkHasChanged, messageOut.SquawkHasChanged); Assert.AreEqual(messageIn.Emergency, messageOut.Emergency); Assert.AreEqual(messageIn.IdentActive, messageOut.IdentActive); Assert.AreEqual(messageIn.OnGround, messageOut.OnGround); } }
/// <summary> /// Creates database records and updates internal objects to track an aircraft that is currently transmitting messages. /// </summary> /// <param name="message"></param> /// <param name="isMlat"></param> /// <remarks> /// This defers the recording of the flight for as long as possible so that if the database is locked then we don't /// have a record of the flight, and the next message will try again to record the flight. /// </remarks> private void TrackFlight(BaseStationMessage message, bool isMlat) { if (IsTransmissionMessage(message)) { lock (_SyncLock) { if (_Session != null) { var localNow = Provider.LocalNow; var icao24 = CustomConvert.Icao24(message.Icao24); if (icao24 > 0) { FlightRecords flightRecords; if (!_FlightMap.TryGetValue(icao24, out flightRecords)) { flightRecords = new FlightRecords() { Aircraft = new BaseStationAircraft() { ModeS = message.Icao24, FirstCreated = localNow, }, Flight = new BaseStationFlight() { Callsign = message.Callsign, StartTime = localNow, NumADSBMsgRec = 0, NumAirCallRepMsgRec = 0, NumAirPosMsgRec = 0, NumAirToAirMsgRec = 0, NumAirVelMsgRec = 0, NumIDMsgRec = 0, NumModeSMsgRec = 0, NumPosMsgRec = 0, NumSurAltMsgRec = 0, NumSurIDMsgRec = 0, NumSurPosMsgRec = 0, }, }; _FlightMap.Add(icao24, flightRecords); } var flight = flightRecords.Flight; flightRecords.EndTimeUtc = Provider.UtcNow; flight.EndTime = localNow; if (!isMlat) { if (message.SquawkHasChanged.GetValueOrDefault()) { flight.HadAlert = true; } if (message.IdentActive.GetValueOrDefault()) { flight.HadSpi = true; } if (message.Squawk == 7500 || message.Squawk == 7600 || message.Squawk == 7700) { flight.HadEmergency = true; } } UpdateFirstLastValues(message, flight, flightRecords, isMlat); UpdateMessageCounters(message, flight); if (!String.IsNullOrEmpty(message.Callsign)) { if (message.Callsign != flightRecords.Flight.Callsign) { flightRecords.Flight.Callsign = message.Callsign; } } } } } } }
/// <summary> /// Returns true if the message holds values transmitted from a vehicle. /// </summary> /// <param name="message"></param> /// <returns></returns> private bool IsTransmissionMessage(BaseStationMessage message) { return(!String.IsNullOrEmpty(message.Icao24) && message.MessageType == BaseStationMessageType.Transmission && message.TransmissionType != BaseStationTransmissionType.None); }
/// <summary> /// See interface docs. /// </summary> /// <param name="json"></param> /// <returns></returns> public IEnumerable <BaseStationMessageEventArgs> ConvertIntoBaseStationMessageEventArgs(AirnavXRangeJson json) { var result = new List <BaseStationMessageEventArgs>(); foreach (var jsonAircraft in json.Aircraft) { var message = new BaseStationMessage() { MessageType = BaseStationMessageType.Transmission, TransmissionType = BaseStationTransmissionType.AirToAir, // This needs to be something other than "None" or "AllCallReply" if we want to have these messages work with the message compressor StatusCode = BaseStationStatusCode.None, }; if (jsonAircraft.SignalLevel != null) { message.SignalLevel = (int)(jsonAircraft.SignalLevel + -0.5); } if (jsonAircraft.Icao24 != null) { message.Icao24 = jsonAircraft.Icao24; } if (jsonAircraft.Altitude != null) { message.Altitude = jsonAircraft.Altitude; } if (jsonAircraft.Callsign != null) { message.Callsign = jsonAircraft.Callsign; } if (jsonAircraft.GroundSpeed != null) { message.GroundSpeed = jsonAircraft.GroundSpeed; } if (jsonAircraft.Track != null) { message.Track = jsonAircraft.Track; } if (jsonAircraft.Squawk != null) { message.Squawk = jsonAircraft.Squawk; } if (jsonAircraft.VerticalRate != null) { message.VerticalRate = jsonAircraft.VerticalRate; } if (jsonAircraft.OnGround != null) { message.OnGround = jsonAircraft.OnGround; } if (jsonAircraft.Latitude != null || jsonAircraft.Longitude != null) { if (jsonAircraft.Latitude != null) { message.Latitude = jsonAircraft.Latitude; } if (jsonAircraft.Longitude != null) { message.Longitude = jsonAircraft.Longitude; } } result.Add(new BaseStationMessageEventArgs(message)); } return(result); }
/// <summary> /// See interface docs. /// </summary> /// <param name="text"></param> /// <param name="signalLevel"></param> /// <returns></returns> public BaseStationMessage Translate(string text, int?signalLevel) { var result = new BaseStationMessage() { SignalLevel = signalLevel }; if (!String.IsNullOrEmpty(text)) { var isMlat = false; string[] parts = text.Split(','); for (int c = 0; c < parts.Length; c++) { string chunk = parts[c]; try { if (!String.IsNullOrEmpty(chunk)) { switch (c) { case 0: result.MessageType = BaseStationMessageHelper.ConvertToBaseStationMessageType(chunk, ref isMlat); break; case 1: result.TransmissionType = result.MessageType == BaseStationMessageType.Transmission ? BaseStationMessageHelper.ConvertToBaseStationTransmissionType(chunk) : BaseStationTransmissionType.None; break; case 2: result.SessionId = ParseInt(chunk); break; case 3: result.AircraftId = ParseInt(chunk); break; case 4: result.Icao24 = chunk == null ? null : chunk.ToUpperInvariant(); break; case 5: result.FlightId = ParseInt(chunk); break; case 6: result.MessageGenerated = ParseDate(chunk); break; case 7: result.MessageGenerated = ParseTime(result.MessageGenerated, chunk); break; case 8: result.MessageLogged = ParseDate(chunk); break; case 9: result.MessageLogged = ParseTime(result.MessageLogged, chunk); break; case 10: if (result.MessageType == BaseStationMessageType.StatusChange) { result.StatusCode = BaseStationMessageHelper.ConvertToBaseStationStatusCode(chunk); } else { result.Callsign = chunk.Replace("@", "").Trim(); } break; case 11: result.Altitude = ParseInt(chunk); break; case 12: result.GroundSpeed = ParseFloat(chunk); break; case 13: result.Track = ParseFloat(chunk); break; case 14: result.Latitude = ParseDouble(chunk); break; case 15: result.Longitude = ParseDouble(chunk); break; case 16: result.VerticalRate = ParseInt(chunk); break; case 17: result.Squawk = ParseInt(chunk); if (result.Squawk == 0) { result.Squawk = null; } break; case 18: result.SquawkHasChanged = ParseBool(chunk); break; case 19: result.Emergency = ParseBool(chunk); break; case 20: result.IdentActive = ParseBool(chunk); break; case 21: result.OnGround = ParseBool(chunk); break; } } } catch (Exception ex) { Debug.WriteLine(String.Format("BaseStationMessageTranslator.Translate caught exception: {0}", ex.ToString())); // I would prefer to pass ex as the inner exception to this. However Microsoft's Application.ThreadException unhandled exception handler // strips off all outer exceptions and only shows the bottom-most exception - i.e., in our case, the exception from a Parse method. This // is not useful in isolation, we need to know what was being translated, the context in which the exception was thrown. So I have ended // up with this, which is not very nice but shows enough information in the unhandled exception handler to allow diagnosis of the problem. throw new BaseStationTranslatorException($"{ex.Message} while translating \"{chunk}\" (chunk {c}) in \"{text}\""); } } result.IsMlat = isMlat; } return(result); }
public void BaseStationMessageTranslator_Translate_Parses_Unknown_Message_Type_Correctly() { BaseStationMessage message = _Implementation.Translate("NEW2ME,1,5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,,,,,,,,,,,,", 123); Assert.AreEqual(BaseStationMessageType.Unknown, message.MessageType); }
/// <summary> /// See interface docs. /// </summary> /// <param name="buffer"></param> /// <returns></returns> public BaseStationMessage Decompress(byte[] buffer) { BaseStationMessage result = null; if (buffer.Length > 0 && (int)buffer[0] == buffer.Length) { using (var stream = new MemoryStream(buffer)) { using (var reader = new BinaryReader(stream)) { result = new BaseStationMessage(); stream.ReadByte(); // <-- length of buffer, we can skip this ushort checksum = reader.ReadUInt16(); // the checksum is skipped as well - caller should ensure it's a valid message before decompression result.MessageType = BaseStationMessageType.Transmission; result.StatusCode = BaseStationStatusCode.None; result.TransmissionType = (BaseStationTransmissionType)stream.ReadByte(); result.Icao24 = DecodeIcao(stream); result.MessageGenerated = result.MessageLogged = DateTime.Now; OptionalFields optionalFields = (OptionalFields)DecodeShort(reader); if ((optionalFields & OptionalFields.CallSign) != 0) { result.Callsign = DecodeString(stream); } if ((optionalFields & OptionalFields.Altitude) != 0) { result.Altitude = (int)DecodeFloatInt(stream); } if ((optionalFields & OptionalFields.GroundSpeed) != 0) { result.GroundSpeed = (int)DecodeFloatShort(reader); } if ((optionalFields & OptionalFields.Track) != 0) { result.Track = DecodeFloatShort(reader) / 10f; } if ((optionalFields & OptionalFields.Latitude) != 0) { result.Latitude = DecodeFloat(reader); } if ((optionalFields & OptionalFields.Longitude) != 0) { result.Longitude = DecodeFloat(reader); } if ((optionalFields & OptionalFields.VerticalRate) != 0) { result.VerticalRate = (int)DecodeFloatShort(reader); } if ((optionalFields & OptionalFields.Squawk) != 0) { result.Squawk = DecodeShort(reader); } bool hasSquawkHasChanged = (optionalFields & OptionalFields.SquawkHasChanged) != 0; bool hasEmergency = (optionalFields & OptionalFields.Emergency) != 0; bool hasIdentActive = (optionalFields & OptionalFields.IdentActive) != 0; bool hasOnGround = (optionalFields & OptionalFields.OnGround) != 0; if (hasSquawkHasChanged || hasEmergency || hasIdentActive || hasOnGround) { CompressedFlags flags = (CompressedFlags)stream.ReadByte(); if (hasSquawkHasChanged) { result.SquawkHasChanged = (flags & CompressedFlags.SquawkHasChanged) != 0; } if (hasEmergency) { result.Emergency = (flags & CompressedFlags.Emergency) != 0; } if (hasIdentActive) { result.IdentActive = (flags & CompressedFlags.IdentActive) != 0; } if (hasOnGround) { result.OnGround = (flags & CompressedFlags.OnGround) != 0; } } } } } return(result); }
public void BaseStationMessageTranslator_Translate_Converts_Zero_Squawks_To_Null() { BaseStationMessage message = _Implementation.Translate("MSG,1,5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,ABC,,,,,,,0,,,,", 123); Assert.AreEqual(null, message.Squawk); }
public void BaseStationMessageTranslator_Translate_Strips_At_Signs_In_Callsign() { BaseStationMessage message = _Implementation.Translate("MSG,1,5,2056,7404F2,11267,2008/11/28,23:48:18.611,2008/11/28,23:53:19.161,A@B,,,,,,,,,,,", 123); Assert.AreEqual("AB", message.Callsign); }
/// <summary> /// See interface docs. /// </summary> /// <param name="aircraftListJson"></param> /// <returns></returns> public IEnumerable <BaseStationMessageEventArgs> ConvertIntoBaseStationMessageEventArgs(AircraftListJson aircraftListJson) { var result = new List <BaseStationMessageEventArgs>(); foreach (var json in aircraftListJson.Aircraft) { var message = new BaseStationMessage() { MessageType = BaseStationMessageType.Transmission, TransmissionType = BaseStationTransmissionType.AirToAir, // This needs to be something other than "None" or "AllCallReply" if we want to have these messages work with the message compressor StatusCode = BaseStationStatusCode.None, }; if (json.Callsign != null) { message.Callsign = json.Callsign; } if (json.Emergency != null) { message.Emergency = json.Emergency; } if (json.GroundSpeed != null) { message.GroundSpeed = json.GroundSpeed; } if (json.Icao24 != null) { message.Icao24 = json.Icao24; } if (json.OnGround != null) { message.OnGround = json.OnGround; } if (json.SignalLevel != null) { message.SignalLevel = json.SignalLevel; } if (json.Track != null) { message.Track = json.Track; } if (json.VerticalRate != null) { message.VerticalRate = json.VerticalRate; } if (json.Latitude != null || json.Longitude != null) { if (json.Latitude != null) { message.Latitude = json.Latitude; } if (json.Longitude != null) { message.Longitude = json.Longitude; } if (json.PositionIsMlat != null) { message.IsMlat = json.PositionIsMlat.Value; } } if (json.Squawk != null) { int squawk; if (int.TryParse(json.Squawk, out squawk)) { message.Squawk = squawk; } } if (json.AltitudeType != null) { AddSupplementaryValue(message, r => r.AltitudeIsGeometric = json.AltitudeType == 1); } if (json.CallsignIsSuspect != null) { AddSupplementaryValue(message, r => r.CallsignIsSuspect = json.CallsignIsSuspect); } if (json.SpeedType != null) { AddSupplementaryValue(message, r => r.SpeedType = (SpeedType)json.SpeedType); } if (json.TargetAltitude != null) { AddSupplementaryValue(message, r => r.TargetAltitude = json.TargetAltitude); } if (json.TargetTrack != null) { AddSupplementaryValue(message, r => r.TargetHeading = json.TargetTrack); } if (json.TrackIsHeading != null) { AddSupplementaryValue(message, r => r.TrackIsHeading = json.TrackIsHeading); } if (json.TransponderType != null) { AddSupplementaryValue(message, r => r.TransponderType = (TransponderType)json.TransponderType); } if (json.VerticalRateType != null) { AddSupplementaryValue(message, r => r.VerticalRateIsGeometric = json.VerticalRateType == 1); } if (json.AirPressureInHg != null) { AddSupplementaryValue(message, r => r.PressureSettingMb = AirPressure.InHgToMillibars(json.AirPressureInHg)); } if (json.Altitude != null) { // When pressure altitude calculations were introduced the sending side was modified to // send Altitude, GeometricAltitude and AltitudeType as a set. However, before that was // introduced they'd only be sent if they changed. if (json.GeometricAltitude == null || json.AltitudeType == null) { message.Altitude = json.Altitude; } else { switch (json.AltitudeType) { case (int)AltitudeType.Barometric: message.Altitude = json.Altitude; break; case (int)AltitudeType.Geometric: message.Altitude = json.GeometricAltitude; break; default: throw new NotImplementedException(); } } } var eventArgs = new BaseStationMessageEventArgs(message, isOutOfBand: false, isSatcomFeed: json.IsSatcomFeed); result.Add(eventArgs); } return(result); }
public void TestInitialise() { _Implementation = new BaseStationMessage(); }
/// <summary> /// Converts the message passed across to the bytes that are expected to be transmitted for it. /// </summary> /// <param name="message"></param> /// <returns></returns> private byte[] ExpectedBytes(BaseStationMessage message) { return(Encoding.ASCII.GetBytes(String.Concat(message.ToBaseStationString(), "\r\n"))); }