private string DecodeAirbornePositionMsg(ModeSReply msg, DateTime timestamp) { // Airborne position message --> we need subsequent messages to decode AirbornePositionMsg pos = (AirbornePositionMsg)msg; if (msg.ICAO24 == null) { return("AirbornePositionMsg: No ICAO24 found."); } string icao24 = BitConverter.ToString(msg.ICAO24).Replace("-", String.Empty); // check if ICAO is already stored in lookup table ADSBInfo info = null; if (!adsbinfos.TryGetValue(icao24, out info)) { // no --> add new entry info = new ADSBInfo(); info.ICAO24 = icao24; adsbinfos.Add(icao24, info); } // adsbinfo found --> update information and calculate position // contains valid position? if (!pos.HasValidPosition) { // no --> return error meesage return("[" + info.ICAO24 + "] AirbornePositionMsg: No valid position found."); } info.ICAO24 = icao24; info.NICSupplementA = pos.NICSupplementA; // position calculated before if (!double.IsNaN(info.Lat) & !double.IsNaN(info.Lon)) { try { // use local CPR double[] localpos = pos.getLocalPosition(info.Lat, info.Lon); // we have a pos --> store in info and update timestamp info.Lat = localpos[0]; info.Lon = localpos[1]; if (pos.HasValidAltitude) { info.Alt = pos.Altitude; } info.Timestamp = timestamp; return("[" + info.ICAO24 + "] AirbornePositionMsg: Lat= " + info.Lat.ToString("F8") + ", Lon=" + info.Lon.ToString("F8") + ", Alt= " + info.Alt.ToString() + ", Time= " + info.Timestamp.ToString("HH:mm:ss.fff")); } catch (Exception ex) { } } // no position calculated before if (pos.IsOddFormat) { try { // odd message info.LastOddAirborne = pos; info.LastOddTimestamp = DateTime.UtcNow; // check if even message was received before and not older than 10secs--> calculate global CPR if ((info.LastEvenAirborne != null) && ((info.LastOddTimestamp - info.LastOddTimestamp).TotalSeconds <= 10)) { try { double[] globalpos = pos.getGlobalPosition(info.LastEvenAirborne); // we have a position --> store in info and update timestamp info.Lat = globalpos[0]; info.Lon = globalpos[1]; if (pos.HasValidAltitude) { info.Alt = pos.Altitude; } // info.Timestamp = timestamp; return("[" + info.ICAO24 + "] AirbornePositionMsg: Lat= " + info.Lat.ToString("F8") + ", Lon=" + info.Lon.ToString("F8") + ", Alt= " + info.Alt.ToString() + ", Time= " + info.Timestamp.ToString("HH:mm:ss.fff")); } catch { return("[" + info.ICAO24 + "] AirbornePositionMsg: Error while decoding position"); } } return("[" + info.ICAO24 + "] AirbornePositionMsg: No decoding possible yet"); } catch (Exception ex) { } } // even message info.LastEvenAirborne = pos; info.LastEvenTimestamp = DateTime.UtcNow; // check if odd message was received before and not older than 10secs --> calculate global CPR if ((info.LastOddAirborne != null) && ((info.LastEvenTimestamp - info.LastOddTimestamp).TotalSeconds <= 10)) { try { double[] globalpos = pos.getGlobalPosition(info.LastOddAirborne); // we have a position --> store in info and update timestamp info.Lat = globalpos[0]; info.Lon = globalpos[1]; if (pos.HasValidAltitude) { info.Alt = pos.Altitude; } // info.Timestamp = timestamp; return("[" + info.ICAO24 + "] AirbornePositionMsg: Lat= " + info.Lat.ToString("F8") + ", Lon=" + info.Lon.ToString("F8") + ", Alt= " + info.Alt.ToString() + ", Time= " + info.Timestamp.ToString("HH:mm:ss.fff")); } catch { return("[" + info.ICAO24 + "] AirbornePositionMsg: Error while decoding position"); } } else { return("[" + info.ICAO24 + "] AirbornePositionMsg:No decoding possible yet"); } }
/** * This method can only be used if another position report with a different format (even/odd) is available * and set with msg.setOtherFormatMsg(other). * @param other airborne position message of the other format (even/odd). Note that the time between * both messages should be not longer than 10 seconds! * @return globally unambiguously decoded position tuple (latitude, longitude). The positional * accuracy maintained by the Airborne CPR encoding will be approximately 5.1 meters. * A message of the other format is needed for global decoding. * @throws MissingInformationException if no position information is available in one of the messages * @throws IllegalArgumentException if input message was emitted from a different transmitter * @throws PositionStraddleError if position messages straddle latitude transition * @throws BadFormatException other has the same format (even/odd) */ public double[] getGlobalPosition(AirbornePositionMsg other) { if (!other.ICAO24.SequenceEqual(ICAO24)) { throw new IllegalArgumentException( string.Format("Transmitter of other message (%s) not equal to this (%s):", BitConverter.ToString(other.ICAO24).Replace("-", String.Empty), BitConverter.ToString(ICAO24).Replace("-", String.Empty))); } if (other.IsOddFormat == IsOddFormat) { throw new BadFormatException("Expected " + (IsOddFormat? "even":"odd") + " message format:" + other.ToString()); } if (!horizontal_position_available) { throw new MissingInformationException("No position information available!"); } if (!other.HasValidPosition) { throw new MissingInformationException("Other message has no position information."); } AirbornePositionMsg even = IsOddFormat?other:this; AirbornePositionMsg odd = IsOddFormat?this:other; // Helper for latitude single(Number of zones NZ is set to 15) double Dlat0 = 360.0 / 60.0; double Dlat1 = 360.0 / 59.0; // latitude index double j = Math.Floor((59.0 * even.CPREncodedLat - 60.0 * odd.CPREncodedLat) / ((double)(1 << 17)) + 0.5); // global latitudes double Rlat0 = Dlat0 * (mod(j, 60) + even.CPREncodedLat / ((double)(1 << 17))); double Rlat1 = Dlat1 * (mod(j, 59) + odd.CPREncodedLat / ((double)(1 << 17))); // Southern hemisphere? if (Rlat0 >= 270 && Rlat0 <= 360) { Rlat0 -= 360; } if (Rlat1 >= 270 && Rlat1 <= 360) { Rlat1 -= 360; } // Northern hemisphere? if (Rlat0 <= -270 && Rlat0 >= -360) { Rlat0 += 360; } if (Rlat1 <= -270 && Rlat1 >= -360) { Rlat1 += 360; } // ensure that the number of even longitude zones are equal if (NL(Rlat0) != NL(Rlat1)) { throw new PositionStraddleException( "The two given position straddle a transition latitude " + "and cannot be decoded. Wait for positions where they are equal."); } // Helper for longitude double Dlon0 = 360.0 / Math.Max(1.0, NL(Rlat0)); double Dlon1 = 360.0 / Math.Max(1.0, NL(Rlat1) - 1); // longitude index double NL_helper = NL(IsOddFormat?Rlat1:Rlat0); // assuming that this is the newer message double m = Math.Floor((even.CPREncodedLon * (NL_helper - 1) - odd.CPREncodedLon * NL_helper) / ((double)(1 << 17)) + 0.5); // global longitude double Rlon0 = Dlon0 * (mod(m, Math.Max(1.0, NL(Rlat0))) + even.CPREncodedLon / ((double)(1 << 17))); double Rlon1 = Dlon1 * (mod(m, Math.Max(1.0, NL(Rlat1) - 1)) + odd.CPREncodedLon / ((double)(1 << 17))); // correct longitude if (Rlon0 < -180 && Rlon0 > -360) { Rlon0 += 360; } if (Rlon1 < -180 && Rlon1 > -360) { Rlon1 += 360; } if (Rlon0 > 180 && Rlon0 < 360) { Rlon0 -= 360; } if (Rlon1 > 180 && Rlon1 < 360) { Rlon1 -= 360; } return(new double[] { IsOddFormat?Rlat1 : Rlat0, IsOddFormat?Rlon1 : Rlon0 }); }