diff --git a/gcs/src/components/fla/graph.jsx b/gcs/src/components/fla/graph.jsx index 1e7e37ea9..7f1895b38 100644 --- a/gcs/src/components/fla/graph.jsx +++ b/gcs/src/components/fla/graph.jsx @@ -251,13 +251,48 @@ export default function Graph({ // https://stackoverflow.com/a/8179549/10077669 const labelColor = backgroundColor.replace(/[^,]+(?=\))/, "1") + // Critical fix: Convert timestamp to Date object for time scale + let xMinValue = flightMode.TimeUS; + let xMaxValue = xMax; + + // Check if we're using a time scale + const isTimeScale = config.scales?.x?.type === "time"; + + // Convert timestamps to Date objects when using time scale + if (isTimeScale) { + xMinValue = new Date(flightMode.TimeUS); + + // Handle xMax + if (nextFlightMode !== undefined) { + xMaxValue = new Date(nextFlightMode.TimeUS); + } else { + // Stretch to the latest date + let maxTime = 0; + data.datasets.forEach((dataset) => { + dataset.data.forEach((point) => { + if (point.x > maxTime) { + maxTime = point.x; + } + }); + }); + const maxDate = new Date(maxTime); + xMaxValue = maxDate; + } + + } else { + // For non-time scales, handle next flight mode + if (nextFlightMode !== undefined) { + xMaxValue = nextFlightMode.TimeUS; + } + } + const flightModeChange = { type: "box", xScaleID: "x", yMin: "end", yMax: "start", - xMin: flightMode.TimeUS, - xMax: xMax, + xMin: xMinValue, + xMax: xMaxValue, backgroundColor: backgroundColor, borderWidth: 0, display: true, @@ -271,12 +306,6 @@ export default function Graph({ }, } - // If there is a next flight mode, then set the xMax of the current flight - // mode to the xMin of the next flight mode - if (nextFlightMode !== undefined) { - flightModeChange.xMax = nextFlightMode.TimeUS - } - annotations.push(flightModeChange) } } diff --git a/gcs/src/components/fla/graphConfigs.js b/gcs/src/components/fla/graphConfigs.js index c3d001ee0..638533d71 100644 --- a/gcs/src/components/fla/graphConfigs.js +++ b/gcs/src/components/fla/graphConfigs.js @@ -93,7 +93,7 @@ export const fgcsOptions = { tooltip: { callbacks: { title: function (context) { - return moment(context[0].parsed.x).format("HH:mm:ss") + return moment(context[0].parsed.x).format("MMMM Do YYYY, h:mm:ss a") }, }, }, diff --git a/gcs/src/fla.jsx b/gcs/src/fla.jsx index a5b2780bb..abd114169 100644 --- a/gcs/src/fla.jsx +++ b/gcs/src/fla.jsx @@ -48,6 +48,7 @@ import { setLogEvents, setFlightModeMessages, setLogType, + setUtcAvailable, setMessageFilters, setMessageMeans, setChartData, @@ -127,6 +128,7 @@ export default function FLA() { logEvents, flightModeMessages, logType, + utcAvailable, messageFilters, messageMeans, chartData, @@ -153,6 +155,8 @@ export default function FLA() { const updateFlightModeMessages = (newFlightModeMessages) => dispatch(setFlightModeMessages(newFlightModeMessages)) const updateLogType = (newLogType) => dispatch(setLogType(newLogType)) + const updateUtcAvailable = (newUtcAvailable) => + dispatch(setUtcAvailable(newUtcAvailable)) const updateMessageFilters = (newMessageFilters) => dispatch(setMessageFilters(newMessageFilters)) const updateMessageMeans = (newMessageMeans) => @@ -177,6 +181,8 @@ export default function FLA() { setLoadingFile(true) const result = await window.ipcRenderer.loadFile(file.path) + let gpsOffset = null; // To store the offset between TimeUS and TimeUTC + if (result.success) { // Load messages into states setLoadingFile(false) @@ -198,6 +204,7 @@ export default function FLA() { if (result.logType === "dataflash") { updateFlightModeMessages(loadedLogMessages.MODE) } else if (result.logType === "fgcs_telemetry") { + updateUtcAvailable(true) // Get the heartbeat messages only where index is the first or last or the mode changes const modeMessages = [] for (let i = 0; i < loadedLogMessages.HEARTBEAT.length; i++) { @@ -268,8 +275,33 @@ export default function FLA() { delete logMessageFilterDefaultState["ESC"] } + let tempLoadedLogMessages = { ...loadedLogMessages } + + if ("GPS" in loadedLogMessages && result.logType === "dataflash" && gpsOffset === null) { + const messageObj = tempLoadedLogMessages["GPS"][0] // Get the first GPS message + + // Calculate the offset + if (messageObj.GWk !== undefined && messageObj.GMS !== undefined) { + const utcTime = gpsToUTC(messageObj.GWk, messageObj.GMS); + gpsOffset = utcTime.getTime() - messageObj.TimeUS/1000; + } + + // Loop through all messages and replace TimeUS with UTC + Object.keys(tempLoadedLogMessages).forEach((key) => { + if (key !== "format" && key !== "units") { + tempLoadedLogMessages[key] = tempLoadedLogMessages[key].map((message) => { + return { + ...message, + TimeUS: message.TimeUS/1000 + gpsOffset, // Add the new property + }; + }); + } + }); + updateUtcAvailable(true) + updateFlightModeMessages(tempLoadedLogMessages.MODE) + } + if (loadedLogMessages["BAT"]) { - let tempLoadedLogMessages = { ...loadedLogMessages } let tempMsgFormat = { ...loadedLogMessages["format"] } // Load each BATT data into its own array @@ -286,6 +318,7 @@ export default function FLA() { tempLoadedLogMessages[battName].push({ ...battData, name: battName, + TimeUS: battData.TimeUS/1000 + gpsOffset }) // Add filter state for new BATT @@ -344,6 +377,20 @@ export default function FLA() { } } + function gpsToUTC(gpsWeek, gms, leapSeconds = 18) { + // GPS epoch starts at 1980-01-06 00:00:00 UTC + const gpsEpoch = new Date(Date.UTC(1980, 0, 6)); + + // Calculate total milliseconds since Unix epoch + const totalMs = + gpsEpoch.getTime() + + gpsWeek * 604_800_000 + // Convert weeks to milliseconds + gms - // Add GPS milliseconds + leapSeconds * 1_000; // Subtract leap seconds + + return new Date(totalMs); + } + // Get a list of the recent FGCS telemetry logs async function getFgcsLogs() { setRecentFgcsLogs(await window.ipcRenderer.getRecentLogs()) @@ -368,6 +415,7 @@ export default function FLA() { updateChartData({ datasets: [] }) updateMessageFilters(null) updateCustomColors({}) + updateUtcAvailable(false) updateColorIndex(0) updateLogEvents(null) updateLogType("dataflash") @@ -880,6 +928,11 @@ export default function FLA() { ), } + // Skip categories with no valid filters + if (filteredCategory.filters.length === 0) { + return null + } + return ( { state.logMessages = action.payload }, + setUtcAvailable: (state, action) => { + state.utcAvailable = action.payload + }, setLogEvents: (state, action) => { state.logEvents = action.payload }, @@ -69,6 +73,7 @@ export const { setUnits, setFormatMessages, setLogMessages, + setUtcAvailable, setLogEvents, setFlightModeMessages, setLogType,