From c523b05d23c7a609f5543d0bd994acd1632ad0ad Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 23 Dec 2023 22:47:56 -0800 Subject: [PATCH] Format headings and speed better --- Meshtastic/Helpers/BLEManager.swift | 41 +++++----- Meshtastic/Helpers/LocationsHandler.swift | 43 +++++----- .../Nodes/Helpers/Map/PositionPopover.swift | 37 +++++---- .../Views/Nodes/Helpers/NodeListItem.swift | 19 ++--- Meshtastic/Views/Settings/AppSettings.swift | 12 ++- Meshtastic/Views/Settings/GPSStatus.swift | 78 ++++++++++--------- Meshtastic/Views/Settings/RouteRecorder.swift | 65 ++++++++-------- 7 files changed, 158 insertions(+), 137 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 360e0723..fd302375 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -969,25 +969,30 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate if #available(iOS 17.0, macOS 14.0, *) { - /// Throw out crappy locations and only send a position if we are connected to a device - if fromNodeNum <= 0 || LocationsHandler.shared.lastLocation.horizontalAccuracy < 0 || LocationsHandler.shared.lastLocation.horizontalAccuracy > 100 { - return false - } - positionPacket.latitudeI = Int32(LocationsHandler.shared.lastLocation.coordinate.latitude * 1e7) - positionPacket.longitudeI = Int32(LocationsHandler.shared.lastLocation.coordinate.longitude * 1e7) - let timestamp = LocationsHandler.shared.lastLocation.timestamp - positionPacket.time = UInt32(timestamp.timeIntervalSince1970) - positionPacket.timestamp = UInt32(timestamp.timeIntervalSince1970) - positionPacket.altitude = Int32(LocationsHandler.shared.lastLocation.altitude) - positionPacket.satsInView = UInt32(LocationsHandler.satsInView) - let currentSpeed = LocationsHandler.shared.lastLocation.speed - if currentSpeed > 0 && (!currentSpeed.isNaN || !currentSpeed.isInfinite) { - positionPacket.groundSpeed = UInt32(currentSpeed * 3.6) - } - let currentHeading = LocationsHandler.shared.lastLocation.course - if currentHeading > 0 && (!currentHeading.isNaN || !currentHeading.isInfinite) { - positionPacket.groundTrack = UInt32(currentHeading) + if let lastLocation = LocationsHandler.shared.locationsArray.last { + + /// Throw out crappy locations and only send a position if we are connected to a device + if fromNodeNum <= 0 || lastLocation.horizontalAccuracy < 0 || lastLocation.horizontalAccuracy > 100 { + return false + } + positionPacket.latitudeI = Int32(lastLocation.coordinate.latitude * 1e7) + positionPacket.longitudeI = Int32(lastLocation.coordinate.longitude * 1e7) + let timestamp = lastLocation.timestamp + positionPacket.time = UInt32(timestamp.timeIntervalSince1970) + positionPacket.timestamp = UInt32(timestamp.timeIntervalSince1970) + positionPacket.altitude = Int32(lastLocation.altitude) + positionPacket.satsInView = UInt32(LocationsHandler.satsInView) + let currentSpeed = lastLocation.speed + if currentSpeed > 0 && (!currentSpeed.isNaN || !currentSpeed.isInfinite) { + positionPacket.groundSpeed = UInt32(currentSpeed * 3.6) + } + let currentHeading = lastLocation.course + if currentHeading > 0 && (!currentHeading.isNaN || !currentHeading.isInfinite) { + positionPacket.groundTrack = UInt32(currentHeading) + } + } + } else { if fromNodeNum <= 0 || LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) == 0.0 { return false diff --git a/Meshtastic/Helpers/LocationsHandler.swift b/Meshtastic/Helpers/LocationsHandler.swift index 3d8cf278..7e172139 100644 --- a/Meshtastic/Helpers/LocationsHandler.swift +++ b/Meshtastic/Helpers/LocationsHandler.swift @@ -19,7 +19,7 @@ import CoreLocation var locationsArray: [CLLocation] var enableSmartPosition: Bool - @Published var lastLocation = CLLocation() + //@Published var lastLocation = CLLocation() @Published var isStationary = false @Published var count = 0 @@ -59,9 +59,9 @@ import CoreLocation var locationAdded: Bool if enableSmartPosition { locationAdded = addLocation(loc) + //print("Added Location \(self.count): \(loc)") } else { locationsArray.append(loc) - self.lastLocation = loc locationAdded = true } if !locationAdded { @@ -96,32 +96,33 @@ import CoreLocation return false } locationsArray.append(location) - lastLocation = location return true } static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090) static var satsInView: Int { - // If we have a position we have a sat - var sats = 1 - if shared.lastLocation.verticalAccuracy > 0 { - sats = 4 - if 0...5 ~= shared.lastLocation.horizontalAccuracy { - sats = 12 - } else if 6...15 ~= shared.lastLocation.horizontalAccuracy { - sats = 10 - } else if 16...30 ~= shared.lastLocation.horizontalAccuracy { - sats = 9 - } else if 31...45 ~= shared.lastLocation.horizontalAccuracy { - sats = 7 - } else if 46...60 ~= shared.lastLocation.horizontalAccuracy { - sats = 5 + var sats = 0 + if let newLocation = shared.locationsArray.last{ + sats = 1 + if newLocation.verticalAccuracy > 0 { + sats = 4 + if 0...5 ~= newLocation.horizontalAccuracy { + sats = 12 + } else if 6...15 ~= newLocation.horizontalAccuracy { + sats = 10 + } else if 16...30 ~= newLocation.horizontalAccuracy { + sats = 9 + } else if 31...45 ~= newLocation.horizontalAccuracy { + sats = 7 + } else if 46...60 ~= newLocation.horizontalAccuracy { + sats = 5 + } + } else if newLocation.verticalAccuracy < 0 && 60...300 ~= newLocation.horizontalAccuracy { + sats = 3 + } else if newLocation.verticalAccuracy < 0 && newLocation.horizontalAccuracy > 300 { + sats = 2 } - } else if shared.lastLocation.verticalAccuracy < 0 && 60...300 ~= shared.lastLocation.horizontalAccuracy { - sats = 3 - } else if shared.lastLocation.verticalAccuracy < 0 && shared.lastLocation.horizontalAccuracy > 300 { - sats = 2 } return sats } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift index ea7a550c..9b062da4 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift @@ -111,20 +111,18 @@ struct PositionPopover: View { let degrees = Angle.degrees(Double(position.heading)) Label { let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) - Text("Heading: \(heading.formatted())") - .foregroundColor(.primary) + Text("Heading: \(heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))") } icon: { Image(systemName: "location.north") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) - .rotationEffect(degrees) + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + .rotationEffect(degrees) } .padding(.bottom, 5) /// Speed - let formatter = MeasurementFormatter() + let speed = Measurement(value: Double(position.speed), unit: UnitSpeed.kilometersPerHour) Label { - Text("Speed: \(formatter.string(from: Measurement(value: Double(position.speed), unit: UnitSpeed.kilometersPerHour)))") - // .font(.footnote) + Text("Speed: \(speed.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))))") .foregroundColor(.primary) } icon: { Image(systemName: "gauge.with.dots.needle.33percent") @@ -132,17 +130,18 @@ struct PositionPopover: View { .frame(width: 35) } .padding(.bottom, 5) - - /// Distance - if locationsHandler.lastLocation.distance(from: CLLocation(latitude: LocationsHandler.DefaultLocation.latitude, longitude: LocationsHandler.DefaultLocation.longitude)) > 0.0 { - let metersAway = position.coordinate.distance(from:CLLocationCoordinate2D(latitude: locationsHandler.lastLocation.coordinate.latitude, longitude: locationsHandler.lastLocation.coordinate.longitude)) - Label { - Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))") - .foregroundColor(.primary) - } icon: { - Image(systemName: "lines.measurement.horizontal") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) + if let lastLocation = locationsHandler.locationsArray.last { + /// Distance + if lastLocation.distance(from: CLLocation(latitude: LocationsHandler.DefaultLocation.latitude, longitude: LocationsHandler.DefaultLocation.longitude)) > 0.0 { + let metersAway = position.coordinate.distance(from:CLLocationCoordinate2D(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)) + Label { + Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))") + .foregroundColor(.primary) + } icon: { + Image(systemName: "lines.measurement.horizontal") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + } } } Spacer() diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index b8fafe88..a4692ec9 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -64,16 +64,17 @@ struct NodeListItem: View { HStack { let lastPostion = node.positions!.reversed()[0] as! PositionEntity if #available(iOS 17.0, macOS 14.0, *) { - let myCoord = CLLocation(latitude: LocationsHandler.shared.lastLocation.coordinate.latitude, longitude: LocationsHandler.shared.lastLocation.coordinate.longitude) - if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationsHandler.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationsHandler.DefaultLocation.latitude { - let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude) - let metersAway = nodeCoord.distance(from: myCoord) - Image(systemName: "lines.measurement.horizontal") - .font(.callout) - .symbolRenderingMode(.hierarchical) - DistanceText(meters: metersAway).font(.callout) + if let currentLocation = LocationsHandler.shared.locationsArray.last { + let myCoord = CLLocation(latitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude) + if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationsHandler.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationsHandler.DefaultLocation.latitude { + let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude) + let metersAway = nodeCoord.distance(from: myCoord) + Image(systemName: "lines.measurement.horizontal") + .font(.callout) + .symbolRenderingMode(.hierarchical) + DistanceText(meters: metersAway).font(.callout) + } } - } else { let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index f6baab3d..f42015fb 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -52,8 +52,16 @@ struct AppSettings: View { .font(.footnote) } if locationHelper.locationManager.location?.courseAccuracy ?? 0 > 0 { - Label("Heading \(String(format: "%.2f", locationHelper.locationManager.location?.course ?? 0))°", systemImage: "location.circle") - .font(.footnote) + let degrees = Angle.degrees(locationHelper.locationManager.location?.course ?? 0) + Label { + let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) + Text("Heading: \(heading.formatted(.measurement(width: .narrow)))") + } icon: { + Image(systemName: "location.north") + .symbolRenderingMode(.hierarchical) + .rotationEffect(degrees) + } + .font(.footnote) } if locationHelper.locationManager.location?.speedAccuracy ?? 0 > 0 { Label("Speed \(speed.formatted())", systemImage: "speedometer") diff --git a/Meshtastic/Views/Settings/GPSStatus.swift b/Meshtastic/Views/Settings/GPSStatus.swift index ea8a9c52..931a7167 100644 --- a/Meshtastic/Views/Settings/GPSStatus.swift +++ b/Meshtastic/Views/Settings/GPSStatus.swift @@ -13,48 +13,52 @@ struct GPSStatus: View { @ObservedObject var locationsHandler: LocationsHandler = LocationsHandler.shared var body: some View { - let horizontalAccuracy = Measurement(value: locationsHandler.lastLocation.horizontalAccuracy, unit: UnitLength.meters) - let verticalAccuracy = Measurement(value: locationsHandler.lastLocation.verticalAccuracy, unit: UnitLength.meters) - let altitiude = Measurement(value: locationsHandler.lastLocation.altitude, unit: UnitLength.meters) - let speed = Measurement(value: locationsHandler.lastLocation.speed, unit: UnitSpeed.kilometersPerHour) - let speedAccuracy = Measurement(value: locationsHandler.lastLocation.speedAccuracy, unit: UnitSpeed.metersPerSecond) - let courseAccuracy = Measurement(value: locationsHandler.lastLocation.courseAccuracy, unit: UnitAngle.degrees) - Label("Coordinate \(String(format: "%.5f", locationsHandler.lastLocation.coordinate.latitude)), \(String(format: "%.5f", LocationsHandler.shared.lastLocation.coordinate.longitude))", systemImage: "mappin") - .font(.footnote) - .textSelection(.enabled) - HStack { - Label("Accuracy \(horizontalAccuracy.formatted())", systemImage: "scope") + + if let newLocation = locationsHandler.locationsArray.last { + let horizontalAccuracy = Measurement(value: newLocation.horizontalAccuracy, unit: UnitLength.meters) + let verticalAccuracy = Measurement(value: newLocation.verticalAccuracy, unit: UnitLength.meters) + let altitiude = Measurement(value: newLocation.altitude, unit: UnitLength.meters) + let speed = Measurement(value: newLocation.speed, unit: UnitSpeed.kilometersPerHour) + let speedAccuracy = Measurement(value: newLocation.speedAccuracy, unit: UnitSpeed.metersPerSecond) + let courseAccuracy = Measurement(value: newLocation.courseAccuracy, unit: UnitAngle.degrees) + Label("Coordinate \(String(format: "%.5f", newLocation.coordinate.latitude)), \(String(format: "%.5f", newLocation.coordinate.longitude))", systemImage: "mappin") .font(.footnote) - Label("Sats Estimate \(LocationsHandler.satsInView)", systemImage: "sparkles") - .font(.footnote) - } - HStack { - if locationsHandler.lastLocation.verticalAccuracy > 0 { - Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2") + .textSelection(.enabled) + HStack { + Label("Accuracy \(horizontalAccuracy.formatted())", systemImage: "scope") .font(.footnote) + Label("Sats Estimate \(LocationsHandler.satsInView)", systemImage: "sparkles") + .font(.footnote) + } - Label("Accuracy \(verticalAccuracy.formatted())", systemImage: "lines.measurement.vertical") - .font(.caption2) - } - HStack { - let degrees = Angle.degrees(LocationsHandler.shared.lastLocation.course) - Label { - let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) - Text("Heading: \(heading.formatted())") - } icon: { - Image(systemName: "location.north") - .symbolRenderingMode(.hierarchical) - .rotationEffect(degrees) + HStack { + if newLocation.verticalAccuracy > 0 { + Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2") + .font(.footnote) + } + Label("Accuracy \(verticalAccuracy.formatted())", systemImage: "lines.measurement.vertical") + .font(.caption2) } - .font(.footnote) - Label("Accuracy \(courseAccuracy.formatted())", systemImage: "safari") - .font(.caption2) - } - HStack { - Label("Speed \(speed.formatted())", systemImage: "speedometer") + HStack { + let degrees = Angle.degrees(newLocation.course) + Label { + let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) + Text("Heading: \(heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))") + } icon: { + Image(systemName: "location.north") + .symbolRenderingMode(.hierarchical) + .rotationEffect(degrees) + } .font(.footnote) - Label("Accuracy \(speedAccuracy.formatted())", systemImage: "gauge.with.dots.needle.bottom.50percent.badge.plus") - .font(.caption2) + Label("Accuracy \(courseAccuracy.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))", systemImage: "safari") + .font(.caption2) + } + HStack { + Label("Speed \(speed.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))))", systemImage: "speedometer") + .font(.footnote) + Label("Accuracy \(speedAccuracy.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))))", systemImage: "gauge.with.dots.needle.bottom.50percent.badge.plus") + .font(.caption2) + } } } } diff --git a/Meshtastic/Views/Settings/RouteRecorder.swift b/Meshtastic/Views/Settings/RouteRecorder.swift index 3a168866..d8253c97 100644 --- a/Meshtastic/Views/Settings/RouteRecorder.swift +++ b/Meshtastic/Views/Settings/RouteRecorder.swift @@ -123,40 +123,43 @@ struct RouteRecorder: View { .padding() Divider() VStack(alignment: .leading) { - let horizontalAccuracy = Measurement(value: locationsHandler.lastLocation.horizontalAccuracy, unit: UnitLength.meters) - let verticalAccuracy = Measurement(value: locationsHandler.lastLocation.verticalAccuracy, unit: UnitLength.meters) - let altitiude = Measurement(value: locationsHandler.lastLocation.altitude, unit: UnitLength.meters) - let speed = Measurement(value: locationsHandler.lastLocation.speed, unit: UnitSpeed.kilometersPerHour) - List { - Label("Coordinate \(String(format: "%.5f", locationsHandler.lastLocation.coordinate.latitude)), \(String(format: "%.5f", locationsHandler.lastLocation.coordinate.longitude))", systemImage: "mappin") - .textSelection(.enabled) - Label("Horizontal Accuracy \(horizontalAccuracy.formatted())", systemImage: "scope") - if locationsHandler.lastLocation.verticalAccuracy > 0 { - Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2") - } - Label("Vertical Accuracy \(verticalAccuracy.formatted())", systemImage: "lines.measurement.vertical") - Label("Satellites Estimate \(LocationHelper.satsInView)", systemImage: "sparkles") - Label("\(locationsHandler.isStationary ? "Moving" : "Stationary")", systemImage: locationsHandler.isStationary ? "figure.walk.motion" : "figure.stand") - if locationsHandler.lastLocation.speedAccuracy > 0 { - Label("Speed \(speed.formatted())", systemImage: "speedometer") - } - if locationsHandler.lastLocation.courseAccuracy > 0 { - /// Heading - let degrees = Angle.degrees(Double(locationsHandler.lastLocation.course)) - Label { - let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) - /// Text("Heading: \(heading.formatted())") - Text("Heading \(String(format: "%.2f", locationsHandler.lastLocation.course))°") - .foregroundColor(.primary) - } icon: { - Image(systemName: "location.circle") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) - .rotationEffect(degrees) + if let lastLocation = locationsHandler.locationsArray.last { + + let horizontalAccuracy = Measurement(value: lastLocation.horizontalAccuracy, unit: UnitLength.meters) + let verticalAccuracy = Measurement(value: lastLocation.verticalAccuracy, unit: UnitLength.meters) + let altitiude = Measurement(value: lastLocation.altitude, unit: UnitLength.meters) + let speed = Measurement(value: lastLocation.speed, unit: UnitSpeed.kilometersPerHour) + List { + Label("Coordinate \(String(format: "%.5f", lastLocation.coordinate.latitude)), \(String(format: "%.5f", lastLocation.coordinate.longitude))", systemImage: "mappin") + .textSelection(.enabled) + Label("Horizontal Accuracy \(horizontalAccuracy.formatted())", systemImage: "scope") + if lastLocation.verticalAccuracy > 0 { + Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2") + } + Label("Vertical Accuracy \(verticalAccuracy.formatted())", systemImage: "lines.measurement.vertical") + Label("Satellites Estimate \(LocationHelper.satsInView)", systemImage: "sparkles") + Label("\(locationsHandler.isStationary ? "Moving" : "Stationary")", systemImage: locationsHandler.isStationary ? "figure.walk.motion" : "figure.stand") + if lastLocation.speedAccuracy > 0 { + Label("Speed \(speed.formatted())", systemImage: "speedometer") + } + if lastLocation.courseAccuracy > 0 { + /// Heading + let degrees = Angle.degrees(Double(lastLocation.course)) + Label { + let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) + /// Text("Heading: \(heading.formatted())") + Text("Heading \(String(format: "%.2f", lastLocation.course))°") + .foregroundColor(.primary) + } icon: { + Image(systemName: "location.circle") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + .rotationEffect(degrees) + } } } + .listStyle(.plain) } - .listStyle(.plain) } } }