Skip to content

Commit

Permalink
feat: add ResolveOrExtracDecFromHeaders to internal/utils module in @…
Browse files Browse the repository at this point in the history
…observerly/skysolve

feat: add ResolveOrExtracDecFromHeaders to internal/utils module in @observerly/skysolve
  • Loading branch information
michealroberts committed Jan 24, 2025
1 parent 3cae9d6 commit fffd5e1
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
29 changes: 29 additions & 0 deletions internal/utils/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,32 @@ func ResolveOrExtractRAFromHeaders(value float32, header fits.FITSHeader) (float
}

/*****************************************************************************************************************/

func ResolveOrExtractDecFromHeaders(value float32, header fits.FITSHeader) (float32, error) {
// First, pick a candidate Dec (v):
v := value

// If the candidate Dec (v) is NaN, try to get it from the header:
if math.IsNaN(float64(v)) {
dec, exists := header.Floats["DEC"]
if !exists {
return float32(math.NaN()), fmt.Errorf("dec header not found in the supplied FITS file")
}
v = dec.Value
}

// Validate the candidate Dec (v) is a valid float32:
if math.IsNaN(float64(v)) {
return float32(math.NaN()), fmt.Errorf("dec value needs to be a valid float32")
}

// Validate the candidate Dec (v) is within the range [-90, 90]:
if v < -90 || v > 90 {
return float32(math.NaN()), fmt.Errorf("dec value is out of range: %f", v)
}

// Return the candidate Dec (v):
return v, nil
}

/*****************************************************************************************************************/
128 changes: 128 additions & 0 deletions internal/utils/headers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ func newFITSHeaderWithRA(ra float32) fits.FITSHeader {
}
}

// newFITSHeaderWithDec is a helper that returns a fits.FITSHeader
// populated with an Dec value.
func newFITSHeaderWithDec(dec float32) fits.FITSHeader {
return fits.FITSHeader{
Floats: map[string]fits.FITSHeaderFloat{
"DEC": {
Value: dec,
Comment: "Right Ascension",
},
},
}
}

/*****************************************************************************************************************/

// newFITSHeaderNoRA is a helper that returns a fits.FITSHeader
Expand All @@ -42,6 +55,14 @@ func newFITSHeaderNoRA() fits.FITSHeader {
}
}

// newFITSHeaderNoDec is a helper that returns a fits.FITSHeader
// with no Dec entry in the Floats map.
func newFITSHeaderNoDec() fits.FITSHeader {
return fits.FITSHeader{
Floats: map[string]fits.FITSHeaderFloat{},
}
}

/*****************************************************************************************************************/

func TestRAValueIsNotNaNAndWithinRange(t *testing.T) {
Expand Down Expand Up @@ -150,3 +171,110 @@ func TestRAValueIsNaNAndRAHeaderNaN(t *testing.T) {
}

/*****************************************************************************************************************/

func TestDecValueIsNotNaNAndWithinRange(t *testing.T) {
value := float32(45.0) // well within -90..+90
header := newFITSHeaderNoDec()

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if got != value {
t.Errorf("Expected %f, got %f", value, got)
}
}

func TestDecValueIsNotNaNAndOutOfRangeNegative(t *testing.T) {
value := float32(-91.0)
header := newFITSHeaderNoDec()

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err == nil {
t.Fatalf("Expected an error for Dec < -90, but got none")
}
if !math.IsNaN(float64(got)) {
t.Errorf("Expected NaN for out-of-range Dec, got %f", got)
}
}

func TestDecValueIsNotNaNAndOutOfRangePositive(t *testing.T) {
value := float32(91.0)
header := newFITSHeaderNoDec()

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err == nil {
t.Fatalf("Expected an error for Dec > +90, but got none")
}
if !math.IsNaN(float64(got)) {
t.Errorf("Expected NaN for out-of-range Dec, got %f", got)
}
}

func TestDecValueIsNaNAndDecHeaderFoundAndValid(t *testing.T) {
value := float32(math.NaN())
header := newFITSHeaderWithDec(30.0) // valid Dec

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err != nil {
t.Fatalf("Unexpected error when Dec is pulled from header: %v", err)
}
expected := float32(30.0)
if got != expected {
t.Errorf("Expected Dec = %f, got %f", expected, got)
}
}

func TestDecValueIsNaNAndDecHeaderMissing(t *testing.T) {
value := float32(math.NaN())
header := newFITSHeaderNoDec()

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err == nil {
t.Fatalf("Expected an error when Dec header is missing, but got none")
}
if !math.IsNaN(float64(got)) {
t.Errorf("Expected NaN when Dec header is missing, got %f", got)
}
}

func TestDecValueIsNaNAndDecHeaderOutOfRangeNegative(t *testing.T) {
value := float32(math.NaN())
header := newFITSHeaderWithDec(-95.0)

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err == nil {
t.Fatalf("Expected an error for Dec < -90, but got none")
}
if !math.IsNaN(float64(got)) {
t.Errorf("Expected NaN for out-of-range Dec, got %f", got)
}
}

func TestDecValueIsNaNAndDecHeaderOutOfRangePositive(t *testing.T) {
value := float32(math.NaN())
header := newFITSHeaderWithDec(100.0)

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err == nil {
t.Fatalf("Expected an error for Dec > +90, but got none")
}
if !math.IsNaN(float64(got)) {
t.Errorf("Expected NaN for out-of-range Dec, got %f", got)
}
}

func TestDecValueIsNaNAndDecHeaderNaN(t *testing.T) {
value := float32(math.NaN())
header := newFITSHeaderWithDec(float32(math.NaN()))

got, err := ResolveOrExtractDecFromHeaders(value, header)
if err == nil {
t.Fatalf("Expected an error for Dec=NaN in header, but got none")
}
if !math.IsNaN(float64(got)) {
t.Errorf("Expected NaN when Dec=NaN in header, got %f", got)
}
}

/*****************************************************************************************************************/

0 comments on commit fffd5e1

Please sign in to comment.