// // ContentView.swift // mcrl-app // // Created by Simon Oberzier on 13.01.26. // import SwiftUI struct ContentView: View { @StateObject var bleManager = BluetoothManager() var experiencedG: Double { let totalVector = sqrt( pow(bleManager.sensorValues.ax, 2) + pow(bleManager.sensorValues.ay, 2) + pow(bleManager.sensorValues.az, 2) ) return max(0, totalVector - 1.0) } var body: some View { NavigationView { VStack(spacing: 20) { HStack { Label(bleManager.connectionStatus.uppercased(), systemImage: bleManager.connectionStatus == "Connected" ? "bolt.fill" : "bolt.slash.fill") .font(.system(size: 10, weight: .black, design: .monospaced)) .foregroundColor(bleManager.connectionStatus == "Connected" ? .green : .red) Spacer() Text("LIVE TELEMETRY") .font(.system(size: 10, weight: .bold)) .foregroundColor(.secondary) } .padding(.horizontal) ZStack { Circle() .trim(from: 0.1, to: 0.9) .stroke(Color.primary.opacity(0.1), style: StrokeStyle(lineWidth: 30, lineCap: .round)) .rotationEffect(.degrees(90)) Circle() .trim(from: 0.1, to: min(0.1 + (experiencedG / 3.0) * 0.8, 0.9)) // Scaled for 0-3G range .stroke( AngularGradient( gradient: Gradient(colors: [.green, .yellow, .orange, .red]), center: .center, angle: .degrees(180) ), style: StrokeStyle(lineWidth: 30, lineCap: .round) ) .rotationEffect(.degrees(90)) .animation(.interactiveSpring(response: 0.2, dampingFraction: 0.5), value: experiencedG) VStack(spacing: -2) { Text(String(format: "%.2f", experiencedG)) .font(.system(size: 64, weight: .black, design: .rounded)) Text("NET G-FORCE") .font(.system(size: 12, weight: .heavy)) .foregroundColor(.secondary) } } .frame(width: 280, height: 280) .padding(.vertical) LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 15) { DataTile(title: "LATERAL X", value: bleManager.sensorValues.ax, unit: "G", color: .blue) DataTile(title: "LONG Y", value: bleManager.sensorValues.ay, unit: "G", color: .orange) DataTile(title: "VERTICAL Z", value: bleManager.sensorValues.az, unit: "G", color: .purple) DataTile(title: "GPS STATUS", value: bleManager.sensorValues.hasFix ? 1.0 : 0.0, unit: bleManager.sensorValues.hasFix ? "FIXED" : "NO FIX", color: .green) } .padding(.horizontal) if bleManager.sensorValues.hasFix { HStack { VStack(alignment: .leading) { Text("COORDINATES").font(.system(size: 9, weight: .black)).foregroundColor(.secondary) Text("\(bleManager.sensorValues.lat, specifier: "%.5f"), \(bleManager.sensorValues.lon, specifier: "%.5f")") .font(.system(.caption, design: .monospaced)) .fontWeight(.bold) } Spacer() Image(systemName: "location.north.circle.fill") .foregroundColor(.blue) } .padding() .background(Color.primary.opacity(0.05)) .cornerRadius(12) .padding(.horizontal) } Spacer() } .navigationTitle("Pico Link") .navigationBarTitleDisplayMode(.inline) } } } struct DataTile: View { let title: String let value: Double let unit: String let color: Color var body: some View { VStack(alignment: .leading, spacing: 4) { Text(title) .font(.system(size: 9, weight: .black)) .foregroundColor(color) HStack(alignment: .lastTextBaseline, spacing: 2) { if unit == "G" { Text(String(format: "%.2f", value)) .font(.system(.title2, design: .monospaced)) .fontWeight(.bold) } Text(unit) .font(.system(size: 10, weight: .bold)) .foregroundColor(.secondary) } } .frame(maxWidth: .infinity, alignment: .leading) .padding() .background(Color.primary.opacity(0.05)) .cornerRadius(12) .overlay( RoundedRectangle(cornerRadius: 12) .stroke(color.opacity(0.2), lineWidth: 1) ) } }