From e4ab65bb7dc6e6c62fb9183a3ae0931b876405f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CCade?= Date: Sat, 28 Dec 2024 08:49:16 -0600 Subject: [PATCH] fixed parse errors --- tle.go | 152 ++++++++++++++++++++++++++-------------------------- tle_test.go | 22 ++++++++ 2 files changed, 98 insertions(+), 76 deletions(-) diff --git a/tle.go b/tle.go index ec8a01a..dbb1a6a 100644 --- a/tle.go +++ b/tle.go @@ -53,11 +53,7 @@ type TLE struct { } // Parses TLE (2LE) and 3LE formats. -// Features: -// - Supports Alpha 5 format -// - Validates the checksums -// - Converts the epoch to a time.Time -// - Converts numbers to correct type +// Features: Supports Alpha 5 format, validates the checksums, converts the epoch to a time.Time, converts numbers to correct type func Parse(txt string) (TLE, error) { var ( trimmedTxt = strings.TrimSpace(txt) @@ -81,72 +77,10 @@ func Parse(txt string) (TLE, error) { } // Parse the NORAD ID from the first line - result.NoradIdStr = result.Line1[2:7] - if _, err := strconv.Atoi(string(result.NoradIdStr[0])); err == nil { - noradInt, err := strconv.Atoi(result.NoradIdStr) - if err != nil { - return TLE{}, err - } - result.NoradId = noradInt - } else { - rest := result.NoradIdStr[1:] - firstChar := result.NoradIdStr[0] - switch firstChar { - case 'A': - result.NoradId, err = strconv.Atoi("10" + rest) - case 'B': - result.NoradId, err = strconv.Atoi("11" + rest) - case 'C': - result.NoradId, err = strconv.Atoi("12" + rest) - case 'D': - result.NoradId, err = strconv.Atoi("13" + rest) - case 'E': - result.NoradId, err = strconv.Atoi("14" + rest) - case 'F': - result.NoradId, err = strconv.Atoi("15" + rest) - case 'G': - result.NoradId, err = strconv.Atoi("16" + rest) - case 'H': - result.NoradId, err = strconv.Atoi("17" + rest) - case 'J': - result.NoradId, err = strconv.Atoi("18" + rest) - case 'K': - result.NoradId, err = strconv.Atoi("19" + rest) - case 'L': - result.NoradId, err = strconv.Atoi("20" + rest) - case 'M': - result.NoradId, err = strconv.Atoi("21" + rest) - case 'N': - result.NoradId, err = strconv.Atoi("22" + rest) - case 'P': - result.NoradId, err = strconv.Atoi("23" + rest) - case 'Q': - result.NoradId, err = strconv.Atoi("24" + rest) - case 'R': - result.NoradId, err = strconv.Atoi("25" + rest) - case 'S': - result.NoradId, err = strconv.Atoi("26" + rest) - case 'T': - result.NoradId, err = strconv.Atoi("27" + rest) - case 'U': - result.NoradId, err = strconv.Atoi("28" + rest) - case 'V': - result.NoradId, err = strconv.Atoi("29" + rest) - case 'W': - result.NoradId, err = strconv.Atoi("30" + rest) - case 'X': - result.NoradId, err = strconv.Atoi("31" + rest) - case 'Y': - result.NoradId, err = strconv.Atoi("32" + rest) - case 'Z': - result.NoradId, err = strconv.Atoi("33" + rest) - default: - return TLE{}, errors.New("invalid NORAD ID Alpha-5 format") - } - - if err != nil { - return TLE{}, err - } + result.NoradIdStr = strings.TrimSpace(result.Line1[2:7]) + result.NoradId, err = parseNoradId(result.NoradIdStr) + if err != nil { + return TLE{}, err } result.Classification = result.Line1[7:8] @@ -191,7 +125,7 @@ func Parse(txt string) (TLE, error) { } // line 2 - secondNoradIdStr := result.Line2[2:7] + secondNoradIdStr := strings.TrimSpace(result.Line2[2:7]) if secondNoradIdStr != result.NoradIdStr { return TLE{}, errors.New("line 1 and line 2 NORAD IDs do not match") } @@ -217,17 +151,17 @@ func Parse(txt string) (TLE, error) { return TLE{}, err } - result.MeanAnomalyDegrees, err = strconv.ParseFloat(result.Line2[43:51], 64) + result.MeanAnomalyDegrees, err = strconv.ParseFloat(strings.TrimSpace(result.Line2[43:51]), 64) if err != nil { return TLE{}, err } - result.MeanMotion, err = strconv.ParseFloat(result.Line2[52:63], 64) + result.MeanMotion, err = strconv.ParseFloat(strings.TrimSpace(result.Line2[52:63]), 64) if err != nil { return TLE{}, err } - result.EpochRevolutionCount, err = strconv.Atoi(result.Line2[63:68]) + result.EpochRevolutionCount, err = strconv.Atoi(strings.TrimSpace(result.Line2[63:68])) if err != nil { return TLE{}, err } @@ -269,7 +203,7 @@ func convertYearAndDayToDate(twoDigitYear, day string) (time.Time, error) { nanosecondsFloat := dayFloat * 24.0 * 60.0 * 60.0 * 1e9 ns := time.Duration(nanosecondsFloat) - // subtract a day the .Date adds a day + // subtract a day because the .Date adds a day return time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC).Add(ns).Add(-time.Hour * 24), nil } @@ -342,3 +276,69 @@ func parseBStar(bstar string) (float64, error) { return strconv.ParseFloat(parseableStr, 64) } + +func parseNoradId(s string) (int, error) { + if _, err := strconv.Atoi(string(s[0])); err == nil { + noradInt, err := strconv.Atoi(s) + if err != nil { + return 0, err + } + return noradInt, nil + } else { + rest := s[1:] + firstChar := s[0] + switch firstChar { + // I and O are not used + case 'A': + return strconv.Atoi("10" + rest) + case 'B': + return strconv.Atoi("11" + rest) + case 'C': + return strconv.Atoi("12" + rest) + case 'D': + return strconv.Atoi("13" + rest) + case 'E': + return strconv.Atoi("14" + rest) + case 'F': + return strconv.Atoi("15" + rest) + case 'G': + return strconv.Atoi("16" + rest) + case 'H': + return strconv.Atoi("17" + rest) + case 'J': + return strconv.Atoi("18" + rest) + case 'K': + return strconv.Atoi("19" + rest) + case 'L': + return strconv.Atoi("20" + rest) + case 'M': + return strconv.Atoi("21" + rest) + case 'N': + return strconv.Atoi("22" + rest) + case 'P': + return strconv.Atoi("23" + rest) + case 'Q': + return strconv.Atoi("24" + rest) + case 'R': + return strconv.Atoi("25" + rest) + case 'S': + return strconv.Atoi("26" + rest) + case 'T': + return strconv.Atoi("27" + rest) + case 'U': + return strconv.Atoi("28" + rest) + case 'V': + return strconv.Atoi("29" + rest) + case 'W': + return strconv.Atoi("30" + rest) + case 'X': + return strconv.Atoi("31" + rest) + case 'Y': + return strconv.Atoi("32" + rest) + case 'Z': + return strconv.Atoi("33" + rest) + default: + return 0, errors.New("invalid NORAD ID Alpha-5 format") + } + } +} diff --git a/tle_test.go b/tle_test.go index d594ee1..4de0557 100644 --- a/tle_test.go +++ b/tle_test.go @@ -196,6 +196,28 @@ ISS (ZARYA) assert.Nil(t, err) assert.Equal(t, localExpected, got) }) + + t.Run("Many different TLEs will parse", func(t *testing.T) { + tles := []string{ + `ISS (ZARYA) +1 25544U 98067A 20274.51782528 .00000867 00000-0 22813-4 0 9994 +2 25544 51.6441 93.0000 0001400 11.0000 349.0000 15.49300070250767`, + `1 58465U 23185D 24363.11104941 .00028654 00000-0 95911-3 0 9999 +2 58465 97.4051 67.6717 0007093 330.0760 30.0075 15.30897368 59670`, + `1 33591U 09005A 24363.16300298 .00000451 00000-0 26450-3 0 9992 +2 33591 99.0226 60.7854 0014697 104.8667 255.4134 14.13241242819028`, + `1 38771U 12049A 24363.09173364 .00000381 00000-0 19401-3 0 9993 +2 38771 98.6040 55.7087 0001839 103.7914 256.3468 14.21393899637091`, + `1 9478U 76101A 24362.62107380 -.00000010 00000-0 00000+0 0 9998 +2 9478 6.9318 311.1675 0107070 341.0025 254.4654 0.97590429118242`, + } + + for _, raw := range tles { + tle, err := Parse(raw) + assert.Nil(t, err) + assert.NotNil(t, tle) + } + }) } func TestYearAndDayToDate(t *testing.T) {