import React, { useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, useWindowDimensions, ScrollView } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { DeviceStatusBar } from '../../src/components/DeviceStatusBar'; import { WaveformChart } from '../../src/components/WaveformChart'; import { ChannelSelector } from '../../src/components/ChannelSelector'; import { useWaveform } from '../../src/hooks/useWaveform'; import { useDataStore } from '../../src/stores/dataStore'; import { useDeviceStore } from '../../src/stores/deviceStore'; import { formatUV, formatUtc, formatCoord } from '../../src/utils/format'; export default function WaveformScreen() { const insets = useSafeAreaInsets(); const { width, height: windowHeight } = useWindowDimensions(); const channelNum = useDeviceStore((s) => s.config.channelNum); const [visible, setVisible] = useState(() => Array(6).fill(true)); const [logScale, setLogScale] = useState(true); const frame = useDataStore((s) => s.currentFrame); const data = useWaveform(visible); const toggleChannel = (idx: number) => setVisible((prev) => prev.map((v, i) => (i === idx ? !v : v))); const CHART_HEIGHT = Math.round(Math.min(300, windowHeight * 0.38)); return ( setLogScale((v) => !v)} activeOpacity={0.7} > {logScale ? 'LOG' : 'LIN'} {/* Waveform chart */} {data ? ( ) : ( 等待采样数据... )} {/* Per-channel peak values + metadata */} {frame && ( {frame.adcUV.map((ch, idx) => { if (!visible[idx] || idx >= channelNum) return null; const absMax = ch.reduce((mx, v) => Math.max(mx, Math.abs(v)), 0); return ( CH{idx + 1} {formatUV(absMax)} ); })} {frame.meta && ( 帧 #{frame.frameId} UTC: {formatUtc(frame.meta.utc)} {formatCoord(frame.meta.latitude, false)} {formatCoord(frame.meta.longitude, true)} 海拔 {frame.meta.altitude.toFixed(1)} m Roll {frame.meta.roll.toFixed(1)}° Pitch {frame.meta.pitch.toFixed(1)}° Yaw {frame.meta.yaw.toFixed(1)}° )} )} ); } const S = StyleSheet.create({ container: { flex: 1, backgroundColor: '#0d0d0d' }, toolbar: { flexDirection: 'row', alignItems: 'center', paddingRight: 10 }, chartWrap: { borderBottomWidth: 1, borderBottomColor: '#222' }, placeholder: { backgroundColor: '#1a1a1a', justifyContent: 'center', alignItems: 'center' }, placeholderText: { color: '#444', fontSize: 14 }, scaleBtn: { marginLeft: 8, paddingHorizontal: 10, paddingVertical: 5, borderRadius: 6, borderWidth: 1, borderColor: '#333', backgroundColor: '#1a1a1a', }, scaleBtnActive: { borderColor: '#4a9eff55', backgroundColor: '#0d2040' }, scaleBtnText: { color: '#555', fontSize: 11, fontWeight: '700', letterSpacing: 0.5 }, scaleBtnTextActive:{ color: '#4a9eff' }, peakScroll: { flex: 1 }, peakRow: { flexDirection: 'row', flexWrap: 'wrap', padding: 12, gap: 10 }, peakCell: { backgroundColor: '#1a1a1a', borderRadius: 8, padding: 10, minWidth: 90, alignItems: 'center', borderWidth: 1, borderColor: '#2a2a2a', }, peakLabel: { color: '#888', fontSize: 11 }, peakValue: { color: '#eee', fontSize: 13, fontWeight: '600', marginTop: 2, fontFamily: 'monospace' }, metaBlock: { margin: 12, backgroundColor: '#111', borderRadius: 8, padding: 10, gap: 3 }, metaText: { color: '#555', fontSize: 10, fontFamily: 'monospace' }, });