propagateOrbit function fails on Epoch field format

I am feeding the same tleStruct data to the propagateOrbit function twice, but in one case propagageOrbit is successful, and in the other case it fails with the following error message:
Error using propagateOrbit
An error was encountered when propagating the orbit of spacecraft at index 1 using sdp4 orbit propagator.
error in test_TLE (line 17)
[r1,v1] = propagateOrbit(epoch_date,tleStruct,PropModel="sdp4",OutputCoordinateFrame="fixed-frame");
Caused by:
Error using - (line 43)
Cannot combine or compare a datetime array within a time zone with one without a time zone.
The tleStruct data looks identical in both cases. In the first case, I use tleread() to input the data and feed it directly to propagateOrbit. In the second case, I write the tleStruct to an Excel .csv file, read it back in as a new tleStruct, and feed it to propagateOrbit. That is when I run into issues with the epoch_date and tleStruct.Epoch fields.
Here is a sample test script:
function test_TLE()
epoch_date = datetime('09-Mar-2025 07:26:58');
tleStruct = struct;
input_pathname = pwd;
[filename, filepath] = uigetfile(fullfile(input_pathname, '*.tle'));
fullpath = strcat(filepath,filename);
[~,name,~] = fileparts(filename);
csv_filepath = strcat(filepath, name, '.csv');
if isfile(csv_filepath)
csvtext = readtable(csv_filepath);
tleStruct = table2struct(csvText);
% script fails here:
[r1,v1] = propagateOrbit(epoch_date,tleStruct, PropModel="sdp4",OutputCoordinateFrame="fixed-frame");
disp(num2str(r1);
else
tleStruct = tleread(fullpath);
[r1,v1] = propagateOrbit(epoch_date,tleStruct, PropModel="sdp4",OutputCoordinateFrame="fixed-frame");
disp(num2str(r1);
% save struct to file so you can read it in faster the next time:
tleTable = struct2table(tleStruct);
tlePath = strrep(fullpath,'.tle','.csv');
writetable(tleTable, tlePath, 'Delimiter', ',', 'fileType', 'text');
end
end
input file "day_068.tle":
1 00011U 59001A 25067.89046599 +.00002526 +00000-0 +13240-2 0 9994
2 00011 32.8783 45.0235 1450212 290.5993 54.5192 11.89297186480028
Output file "day_068.csv":
Name SatelliteCatalogNumber Epoch Bstar RightAscensionOfAscendingNode Eccentricity Inclination ArgumentOfPeriapsis MeanAnomaly MeanMotion
UNKNOWN 11 3/8/2025 21:22 0.001324 45.0235 0.1450212 32.8783 290.5993 54.5192 0.049554049
The Epoch field appears as shown above in the Excel table. When you examine it in the edit field, it says "3/8/2025 9:22:16 PM
The first time you run the script, it will read the TLE file and create a tleStruct for propagateOrbit() to read. This call is successful. It then outputs a new "day_068.csv" file to save time on subsequent function calls.
The second time you run the script, it reads the "day_068.csv" file and converts it to a tleStruct. When you attempt to read this
tleStruct into propagateOrbit(), it throws an error.
Apparently propagateOrbit calls arithUtil(a,b) which checks for datetime compatibility and throws an exception.
The time fields appear identical to me in both cases, when I examine them in the tleStruct.Epoch field. Is Excel doing something funny to the datetime structure?

1 个评论

E.g., this is an example that may look the same on output with no time zone printed by default, but will throw an error when compared because one has a time zone and the other doesn't:
dt1 = datetime('10-Mar-2025 07:26:58') % No time zone
dt1 = datetime
10-Mar-2025 07:26:58
dt2 = datetime('09-Mar-2025 07:26:58','TimeZone','UTC') % With time zone, but doesn't print it
dt2 = datetime
09-Mar-2025 07:26:58
dt1 - dt1 % works with two unzoned datetimes
ans = duration
00:00:00
dt2 - dt2 % works with two zoned datetimes
ans = duration
00:00:00
dt1 - dt2 % fails when comparing unzoned vs zoned datetimes
Error using - (line 43)
Cannot combine or compare a datetime array with a time zone with one without a time zone.

请先登录,再进行评论。

 采纳的回答

dpb
dpb 2026-7-17
编辑:dpb 2026-7-18,12:26
"Is Excel doing something funny to the datetime structure?"
Excel is notoriously bad in handling dates, particularly if it is interpreted as a date/time field. I'm not sure just where it's going wrong there, but I'd be pretty sure it's in that translation into/back out of Excel that is causing the problem.
Why not forego Excel altogether if the point is to save processing time on subsequent case and just save the desired variable(s) in native MATLAB .mat format? It/they will be read back in then identically as were in memory to the identical bit pattern besides being a much smaller file size and faster operation.
function test_TLE()
epoch_date = datetime('09-Mar-2025 07:26:58');
tleStruct = struct;
input_pathname = pwd;
[filename, filepath] = uigetfile(fullfile(input_pathname, '*.tle'));
fullpath = fullfile(filepath,filename); % more robust
[~,name,~] = fileparts(filename);
mat_filepath = fullfile(filepath, name, '.mat');
if isfile(mat_filepath)
load mat_filepath % retrieve saved tleStruct
[r1,v1] = propagateOrbit(epoch_date,tleStruct, PropModel="sdp4",OutputCoordinateFrame="fixed-frame");
disp(num2str(r1);
else
tleStruct = tleread(fullpath);
[r1,v1] = propagateOrbit(epoch_date,tleStruct, PropModel="sdp4",OutputCoordinateFrame="fixed-frame");
disp(num2str(r1);
% save struct to .mat file so you can read it in faster the next time:
save(mat_filepath,'tleStruct');
end
end
Although, there is really no point in the above code snippet as written unless it is only the test demo; one presumes at least something has been/is modified in the tleStruct struct variable in the real use case; otherwise the struct is already in memory.

11 个评论

I need to save the data in a file, because it may be days or weeks before someone reruns the test case. So just keeping it in memory is not much of a solution. But thanks for the insight.
dpb
dpb 2026-7-21,14:23
编辑:dpb 2026-7-21,14:38
That's what the above does; the point being, however, that unless there's something done to the time variable between the first and subsequent cases it is run, it won't be any different than the copy that is already in memory because your function as presently constructed doesn't have any way to update the tleStruct variable that was saved the first pass in order to rewrite a new version later; you'll have to restructure that code somehow for that to happen.
Thanks for the solution.
Kurt
That is exactly what I do. Each call to this propagation involves a different time and field of view. Hence, each propagation result is different for the same satellites.
dpb
dpb 2026-7-21,15:28
编辑:dpb 2026-7-21,16:04
As long as it is updating the tleStruct each pass before saving...the above sample code doesn't do that which is why I brought it up...just to make sure you didn't "get bit" with unexpected result by overlooking it.
And, of course, always glad to help...
As I understand it, the tleStruct is just a collection of orbital elements extracted from the TLE. It represents the state of a satellite at "epoch" time, that is, when the TLE was created. It never changes. The purpose of propagateOrbit is to predict where that satellite will be in the past/future, by extrapolating from the epoch time. So, as I work with several thousand observations, all at slightly different times, the propagation results for a given satellite will be different, due to its motion. So I think I have my bases covered.
Well, if it is fixed, then I don't see the point in saving it multiple times, simply the original is all that would be needed. But, if that were the case, it would seem more efficient to build a preprocessing tool that simply builds a .mat file for each .tle file and just use them instead from the start.
But, I don't have the TB and never done these, so I'm just trying to make sense of the presented code. If it does what you want/need correctly, that's good. <grin>
dpb
dpb 2026-7-21,17:25
编辑:dpb 2026-7-21,18:55
Good luck...although I still don't follow how the propagateOrbit() function is going to do anything different on subsequent calls unless it is the passed epoch_date value that matters and that the real top level code passes modifies it and it isn't as here overwritten by some local constant. But, as noted, I don't have the TB and haven't ever tried this; only that the original code illustrated saving the tleStruct, I thought it was also going to have to get updated with the last time.
With the continuing conversation, I think I now see there were two issues; the Q? really about having previously written the struct to Excel and that had thus changed the format of the internal date in the .tle file. So, I think I now understand that date being fixed; it's the other that is also called epoch_date in your code but that is the time for which it is desired to which to do the extrapolation from the base date; hence the difference showing up in the error.
So, in the end it is to save the base .tle file struct as a .mat so it retains TZ information as you can then generate new times to match easily enough. I may be slow, but I think I eventually caught on...<grin>
Yes, it is the difference between the TLE Epoch time and the actual observation time that affects the propagation. We take successive snapshots of the sky over the course of the night, and the satellites move in between snapshots, so each propagation result is different.
You just earned your rocket scientist's wings.
Gotcha...thanks! Looks like a plan; it would from the outside looking in then appear that the conversion to the .mat file would be a worthwhile thing if these are, indeed, used multiple times and there is an observable overhead in the tleread() function.

请先登录,再进行评论。

更多回答(1 个)

Umar
Umar 2026-7-18,4:56
Hi @Kurt,
Wanted to close the loop on the Excel/datetime thing properly before saying anything definitive.
Ran a couple of tests on my end first: round-tripped a datetime through writetable/readtable in plain MATLAB, no Excel involved — came out clean, TimeZone empty both sides, values identical. Then did the same thing with a full struct shaped like your actual tleStruct (Name, SatelliteCatalogNumber, Epoch, all the fields) — also completely clean. So that rules out writetable, readtable, struct2table, and table2struct as the cause — none of them introduce the mismatch on their own.
That leaves Excel as the only part of your workflow we haven't directly tested, and by elimination it's the most likely explanation — dpb's instinct about Excel and dates lines up with that. If you get a chance and don't mind, one quick way to confirm it directly: take a fresh CSV, open it in real Excel, save it (even without changing anything), read it back into MATLAB, and check tleStruct.Epoch.TimeZone before and after. If that's what introduces the mismatch, it'll confirm it outright rather than just by process of elimination.
That said — since what you're actually trying to do is just cache the parsed struct so you're not reprocessing that huge file every time, the practical fix doesn't depend on nailing the exact mechanism. save()/load() with a .mat file sidesteps the whole thing, since it stores the datetime exactly as it exists in memory with no text step for anything to go wrong in. Probably the simplest path forward regardless of what Excel turns out to be doing.

6 个评论

Excel does not store the timezone. It appears that writetable() strips out the timezone information and writes the localtime relative to the timezone (rather than writing a UTC timestamp.)
T1 = datetime(1980, 2, 29, 19, 00, 0)
T1 = datetime
29-Feb-1980 19:00:00
T1.TimeZone
ans = 0×0 empty char array
T2 = datetime(1980, 2, 29, 19, 00, 0, 'timezone', 'America/Chicago')
T2 = datetime
29-Feb-1980 19:00:00
T2.TimeZone
ans = 'America/Chicago'
D = table(T1, T2)
D = 1×2 table
T1 T2 ____________________ ____________________ 29-Feb-1980 19:00:00 29-Feb-1980 19:00:00
Filename = tempname() + ".xlsx";
writetable(D, Filename)
E = readtable(Filename)
E = 1×2 table
T1 T2 ____________________ ____________________ 29-Feb-1980 19:00:00 29-Feb-1980 19:00:00
E.T1.TimeZone
ans = 0×0 empty char array
E.T2.TimeZone
ans = 0×0 empty char array
OutFolder = tempdir();
filenames = unzip(Filename, OutFolder)
filenames = 1×7 cell array
{'/tmp/[Content_Ty…'} {'/tmp/_rels/.rels'} {'/tmp/xl/_rels/wo…'} {'/tmp/xl/sharedSt…'} {'/tmp/xl/styles.x…'} {'/tmp/xl/workbook…'} {'/tmp/xl/workshee…'}
dbtype( filenames{7} )
1 <?xml version="1.0" encoding="UTF-8"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac"><dimension ref="A1:B2"/><sheetViews><sheetView workbookViewId="0"/></sheetViews><sheetFormatPr defaultRowHeight="15"/><cols><col min="1" max="1" width="3.140625" customWidth="true"/><col min="2" max="2" width="3.140625" customWidth="true"/></cols><sheetData><row r="1"><c r="A1" s="0" t="s"><v>0</v></c><c r="B1" s="0" t="s"><v>1</v></c></row><row r="2"><c r="A2" s="1"><v>29280.791666666668</v></c><c r="B2" s="1"><v>29280.791666666668</v></c></row></sheetData></worksheet>
T = datetime(1980, 2, 29, 19, 00, 0, 'timezone', 'America/Chicago')
T = datetime
29-Feb-1980 19:00:00
T.TimeZone
ans = 'America/Chicago'
D = table(T);
D.T.TimeZone
ans = 'America/Chicago'
Filename = tempname();
save(Filename,'D')
clear D
load(Filename)
D.T.TimeZone
ans = 'America/Chicago'
I figured it would be something of the sort...doesn't this indicate a bug or at least an implementation flaw in writetable, though? One would have to explicitly set an output format to include the time zone field to text output, but it seems that at least a warning should inform user that the table variable may not reflect the table value when read back in. The doc mentions the difference in Excel date/time types (although the present internal doc link is broken, returning a "Content is retired" error) but I can't find any mention of the time zone issue.
I will try Walter's suggestion of saving the data as a .mat file instead of messing around with Excel. This sounds like the best solution.
dpb
dpb 2026-7-21,14:21
编辑:dpb 2026-7-21,14:56
"I will try Walter's suggestion of saving the data as a .mat file..."
Not to nit pick, but that was my first suggestion and what the code I posted above modified your initial function does.
Just for clarity I'll repeat the comment above in that you misunderstood the point being made.
"That's what the above does; the point being, however, that unless there's something done to the time variable between the first and subsequent cases it is run, it won't be any different than the copy that is already in memory because your function as presently constructed doesn't have any way to update the tleStruct variable that was saved the first pass in order to rewrite a new version later; you'll have to restructure that code somehow for that to happen."
Sorry, I have a hard time sorting out all these threads. Thanks
dpb
dpb 2026-7-21,15:04
编辑:dpb 2026-7-22,14:10
No problem, the conversation did get a little messy and I went on to bring up the point about writetable not keeping the TZ part of a datetime variable as perhaps something that should be reported as a bug.

请先登录,再进行评论。

类别

帮助中心File Exchange 中查找有关 Satellite Mission Analysis 的更多信息

产品

版本

R2025b

提问:

2026-7-17

编辑:

dpb
2026-7-22,14:10

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by