mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
commit
fd4b409cd5
12 changed files with 194 additions and 84 deletions
|
|
@ -109,6 +109,7 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
|
|||
case fifteenSeconds = 15
|
||||
case thirtySeconds = 30
|
||||
case oneMinute = 60
|
||||
case twoMinutes = 120
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 600
|
||||
case fifteenMinutes = 900
|
||||
|
|
@ -134,6 +135,8 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
|
|||
return "Thirty Seconds"
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
case .twoMinutes:
|
||||
return "Two Minutes"
|
||||
case .fiveMinutes:
|
||||
return "Five Minutes"
|
||||
case .tenMinutes:
|
||||
|
|
|
|||
|
|
@ -962,7 +962,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
|
||||
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
|
||||
positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
|
||||
positionPacket.timestamp = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
|
||||
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
|
||||
positionPacket.satsInView = UInt32(LocationHelper.satsInView)
|
||||
|
||||
// Get Errors without some speed
|
||||
if LocationHelper.currentSpeed >= 5 {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,31 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
return timestamp
|
||||
}
|
||||
|
||||
|
||||
static var satsInView: Int {
|
||||
|
||||
// If we have a position we have a sat
|
||||
var sats = 1
|
||||
|
||||
if shared.locationManager.location?.verticalAccuracy ?? 0 > 0 {
|
||||
sats = 4
|
||||
|
||||
if 0...15 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
sats = 12
|
||||
} else if 16...30 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
sats = 10
|
||||
} else if 31...45 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
sats = 8
|
||||
} else if 46...60 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
sats = 6
|
||||
}
|
||||
|
||||
} else if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 && 60...300 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
sats = 3
|
||||
} else if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 && shared.locationManager.location?.horizontalAccuracy ?? 0 > 300 {
|
||||
sats = 2
|
||||
}
|
||||
return sats
|
||||
}
|
||||
|
||||
private let locationManager = CLLocationManager()
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ struct NodeDetail: View {
|
|||
let mostRecent = node.positions?.lastObject as! PositionEntity
|
||||
|
||||
if mostRecent.coordinate != nil {
|
||||
|
||||
|
||||
let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: mostRecent.latitude!, longitude: mostRecent.longitude!)
|
||||
|
||||
|
||||
let regionBinding = Binding<MKCoordinateRegion>(
|
||||
get: {
|
||||
MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005))
|
||||
|
|
@ -54,28 +54,30 @@ struct NodeDetail: View {
|
|||
interactionModes: [.all],
|
||||
showsUserLocation: true,
|
||||
userTrackingMode: .constant(.follow),
|
||||
annotationItems: annotations)
|
||||
{ location in
|
||||
annotationItems: annotations) { location in
|
||||
|
||||
return MapAnnotation(
|
||||
coordinate: location.coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0),
|
||||
content: {
|
||||
|
||||
NodeAnnotation(time: location.time!)
|
||||
}
|
||||
coordinate: location.coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0),
|
||||
content: {
|
||||
|
||||
NodeAnnotation(time: location.time!)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea(.all, edges: [.leading, .trailing])
|
||||
.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.70)
|
||||
|
||||
}
|
||||
Text(mostRecent.satsInView > 0 ? "Sats: \(mostRecent.satsInView)" : " ")
|
||||
.offset( y:-40)
|
||||
}
|
||||
Text("Sats: \(mostRecent.satsInView)").offset( y:-40)
|
||||
|
||||
} else {
|
||||
|
||||
HStack {
|
||||
|
||||
}
|
||||
.padding([.top], 40)
|
||||
.padding([.top], 60)
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
|
|
@ -89,12 +91,8 @@ struct NodeDetail: View {
|
|||
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Text("AKA").font(.largeTitle)
|
||||
.foregroundColor(.gray).fixedSize()
|
||||
.offset(y:5)
|
||||
CircleText(text: node.user?.shortName ?? "???", color: .accentColor, circleSize: 75, fontSize: 26)
|
||||
}
|
||||
.padding()
|
||||
|
||||
Divider()
|
||||
|
||||
|
|
@ -105,15 +103,14 @@ struct NodeDetail: View {
|
|||
Image(hwModelString)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 200, height: 200)
|
||||
.frame(width: 75, height: 75)
|
||||
.cornerRadius(5)
|
||||
|
||||
Text(String(hwModelString))
|
||||
.foregroundColor(.gray)
|
||||
.font(.largeTitle).fixedSize()
|
||||
.font(.title).fixedSize()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
|
||||
if node.snr > 0 {
|
||||
|
|
@ -432,7 +429,6 @@ struct NodeDetail: View {
|
|||
}
|
||||
}
|
||||
.offset( y:-40)
|
||||
.padding(.bottom, -40)
|
||||
}
|
||||
.edgesIgnoringSafeArea([.leading, .trailing])
|
||||
.navigationTitle((node.user != nil) ? String(node.user!.longName ?? "Unknown") : "Unknown")
|
||||
|
|
|
|||
|
|
@ -21,57 +21,87 @@ struct PositionLog: View {
|
|||
var body: some View {
|
||||
|
||||
NavigationStack {
|
||||
|
||||
ScrollView {
|
||||
|
||||
Grid(alignment: .topLeading, horizontalSpacing: 2) {
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//Add a table for mac and ipad
|
||||
}
|
||||
|
||||
GridRow {
|
||||
|
||||
Text("Lat / Long")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Sat")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Alt")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Spd")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Hd")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Timestamp")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
Divider()
|
||||
ForEach(node.positions!.reversed() as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
|
||||
GridRow {
|
||||
Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
|
||||
.font(.caption2)
|
||||
Text(String(mappin.satsInView))
|
||||
.font(.caption2)
|
||||
Text(String(mappin.altitude))
|
||||
.font(.caption2)
|
||||
Text(String(mappin.speed))
|
||||
.font(.caption2)
|
||||
Text(String(mappin.heading))
|
||||
.font(.caption2)
|
||||
Text(mappin.time?.formattedDate(format: "MM/dd/yy hh:mm") ?? "Unknown time")
|
||||
.font(.caption2)
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//Add a table for mac and ipad
|
||||
VStack {
|
||||
Table(node.positions!.reversed() as! [PositionEntity]) {
|
||||
TableColumn("SeqNo") { position in
|
||||
Text(String(position.seqNo))
|
||||
}
|
||||
.width(75)
|
||||
TableColumn("Latitude") { position in
|
||||
Text(String(format: "%.6f", position.latitude ?? 0))
|
||||
}
|
||||
TableColumn("Longitude") { position in
|
||||
Text(String(format: "%.6f", position.longitude ?? 0))
|
||||
}
|
||||
TableColumn("Altitude") { position in
|
||||
Text(String(position.altitude))
|
||||
}
|
||||
.width(75)
|
||||
TableColumn("Sats") { position in
|
||||
Text(String(position.satsInView))
|
||||
}
|
||||
.width(75)
|
||||
TableColumn("Speed") { position in
|
||||
Text(String(position.speed))
|
||||
}
|
||||
.width(75)
|
||||
TableColumn("Heading") { position in
|
||||
Text(String(position.heading))
|
||||
}
|
||||
TableColumn("Time Stamp") { position in
|
||||
Text(position.time?.formattedDate(format: "MM/dd/yy hh:mm") ?? "Unknown time")
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.leading, 15)
|
||||
.padding(.trailing, 5)
|
||||
|
||||
} else {
|
||||
|
||||
ScrollView {
|
||||
// Use a grid on iOS as a table only shows a single column
|
||||
Grid(alignment: .topLeading, horizontalSpacing: 2) {
|
||||
|
||||
GridRow {
|
||||
|
||||
Text("Latitude")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Longitude")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Sats")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Alt")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
Text("Timestamp")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
Divider()
|
||||
ForEach(node.positions!.reversed() as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
|
||||
GridRow {
|
||||
Text(String(mappin.latitude ?? 0))
|
||||
.font(.caption2)
|
||||
Text(String(mappin.longitude ?? 0))
|
||||
.font(.caption2)
|
||||
Text(String(mappin.satsInView))
|
||||
.font(.caption2)
|
||||
Text(String(mappin.altitude))
|
||||
.font(.caption2)
|
||||
Text(mappin.time?.formattedDate(format: "MM/dd/yy hh:mm") ?? "Unknown time")
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.leading, 15)
|
||||
.padding(.trailing, 5)
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
|
||||
Button(role: .destructive) {
|
||||
|
|
@ -95,11 +125,10 @@ struct PositionLog: View {
|
|||
|
||||
if clearPositions(destNum: node.num, context: context) {
|
||||
|
||||
print("Clear Position Log Failed")
|
||||
print("Successfully Cleared Position Log")
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
print("Clear Position Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,8 @@ struct CannedMessagesConfig: View {
|
|||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Save Canned Messages Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
|
|
|
|||
|
|
@ -145,9 +145,9 @@ struct ExternalNotificationConfig: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Save External Notification Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
|
|
|
|||
|
|
@ -161,7 +161,8 @@ struct MQTTConfig: View {
|
|||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Save MQTT Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
|
|
|
|||
|
|
@ -109,9 +109,9 @@ struct RangeTestConfig: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Save Range Test Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ struct SerialConfig: View {
|
|||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Save Serial Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,8 @@ struct TelemetryConfig: View {
|
|||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Save Telemetry Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ struct PositionFlags: OptionSet
|
|||
static let Dop = PositionFlags(rawValue: 8)
|
||||
static let Hvdop = PositionFlags(rawValue: 16)
|
||||
static let Satsinview = PositionFlags(rawValue: 32)
|
||||
static let SeqNos = PositionFlags(rawValue: 64)
|
||||
static let SeqNo = PositionFlags(rawValue: 64)
|
||||
static let Timestamp = PositionFlags(rawValue: 128)
|
||||
static let Speed = PositionFlags(rawValue: 256)
|
||||
static let Heading = PositionFlags(rawValue: 512)
|
||||
|
|
@ -33,6 +33,7 @@ struct PositionConfig: View {
|
|||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
@State var hasFlagChanges = false
|
||||
|
||||
@State var smartPositionEnabled = true
|
||||
@State var deviceGpsEnabled = true
|
||||
|
|
@ -246,7 +247,7 @@ struct PositionConfig: View {
|
|||
if includeDop { pf.insert(.Dop) }
|
||||
if includeHvdop { pf.insert(.Hvdop) }
|
||||
if includeSatsinview { pf.insert(.Satsinview) }
|
||||
if includeSeqNo { pf.insert(.SeqNos) }
|
||||
if includeSeqNo { pf.insert(.SeqNo) }
|
||||
if includeTimestamp { pf.insert(.Timestamp) }
|
||||
if includeSpeed { pf.insert(.Speed) }
|
||||
if includeHeading { pf.insert(.Heading) }
|
||||
|
|
@ -300,14 +301,13 @@ struct PositionConfig: View {
|
|||
if pf.contains(.Dop) { self.includeDop = true } else { self.includeDop = false }
|
||||
if pf.contains(.Hvdop) { self.includeHvdop = true } else { self.includeHvdop = false }
|
||||
if pf.contains(.Satsinview) { self.includeSatsinview = true } else { self.includeSatsinview = false }
|
||||
if pf.contains(.SeqNos) { self.includeSeqNo = true } else { self.includeSeqNo = false }
|
||||
if pf.contains(.SeqNo) { self.includeSeqNo = true } else { self.includeSeqNo = false }
|
||||
if pf.contains(.Timestamp) { self.includeTimestamp = true } else { self.includeTimestamp = false }
|
||||
if pf.contains(.Speed) { self.includeSpeed = true } else { self.includeSpeed = false }
|
||||
if pf.contains(.Heading) { self.includeHeading = true } else { self.includeHeading = false }
|
||||
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
|
||||
}
|
||||
}
|
||||
.onChange(of: deviceGpsEnabled) { newDeviceGps in
|
||||
|
|
@ -352,8 +352,60 @@ struct PositionConfig: View {
|
|||
if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: includeAltitude || includeAltitudeMsl || includeGeoidalSeparation || includeDop || includeHvdop || includeSatsinview || includeSeqNo || includeTimestamp || includeSpeed || includeHeading) { newFlags in
|
||||
// hasChanges = true
|
||||
.onChange(of: includeAltitude) { altFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Altitude)
|
||||
if existingValue != altFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeAltitudeMsl) { altMslFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.AltitudeMsl)
|
||||
if existingValue != altMslFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeSatsinview) { satsFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Satsinview)
|
||||
if existingValue != satsFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeSeqNo) { seqFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.SeqNo)
|
||||
if existingValue != seqFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeTimestamp) { timestampFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Timestamp)
|
||||
if existingValue != timestampFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeTimestamp) { timestampFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Timestamp)
|
||||
if existingValue != timestampFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeSpeed) { speedFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Speed)
|
||||
if existingValue != speedFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeHeading) { headingFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Heading)
|
||||
if existingValue != headingFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeGeoidalSeparation) { geoSepFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.GeoidalSeparation)
|
||||
if existingValue != geoSepFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeDop) { dopFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Dop)
|
||||
if existingValue != dopFlag { hasChanges = true }
|
||||
}
|
||||
.onChange(of: includeHvdop) { hvdopFlag in
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
let existingValue = pf.contains(.Hvdop)
|
||||
if existingValue != hvdopFlag { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue