github.com/astrogo/fitsio@v0.3.0/utils.go (about) 1 // Copyright 2015 The astrogo Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package fitsio 6 7 import ( 8 "bytes" 9 "fmt" 10 "math/big" 11 "reflect" 12 "strconv" 13 "strings" 14 ) 15 16 // var ( 17 // g_debug = false 18 // ) 19 // func printf(format string, args ...interface{}) (int, error) { 20 // if g_debug { 21 // return fmt.Printf(format, args...) 22 // } 23 // return 0, nil 24 // } 25 26 // alignBlock returns a size adjusted to align at a FITS block size 27 func alignBlock(sz int) int { 28 padding := padBlock(sz) 29 return sz + padding 30 } 31 32 // padBlock returns the amount of padding to align to a FITS block size 33 func padBlock(sz int) int { 34 padding := (blockSize - (sz % blockSize)) % blockSize 35 return padding 36 } 37 38 // processString is utilized by DecodeHDU to process string-type values in the header 39 // it uses a 3-state machine to process double single quotes 40 func processString(s string) (string, int, error) { 41 var buf bytes.Buffer 42 43 state := 0 44 for i, char := range s { 45 quote := (char == '\'') 46 switch state { 47 case 0: 48 if !quote { 49 return "", i, fmt.Errorf("fitsio: string does not start with a quote (%q)", s) 50 } 51 state = 1 52 case 1: 53 if quote { 54 state = 2 55 } else { 56 buf.WriteRune(char) 57 state = 1 58 } 59 case 2: 60 if quote { 61 buf.WriteRune(char) 62 state = 1 63 } else { 64 return strings.TrimRight(buf.String(), " "), i, nil 65 } 66 } 67 } 68 if s[len(s)-1] == '\'' { 69 return strings.TrimRight(buf.String(), " "), len(s), nil 70 } 71 return "", 0, fmt.Errorf("fitsio: string ends prematurely (%q)", s) 72 } 73 74 // parseHeaderLine parses a 80-byte line from an input header FITS block. 75 // transliteration of CFITSIO's ffpsvc. 76 func parseHeaderLine(bline []byte) (*Card, error) { 77 var err error 78 var card Card 79 80 valpos := 0 81 keybeg := 0 82 keyend := 0 83 84 const ( 85 kLINE = 80 86 ) 87 88 var ( 89 kHIERARCH = []byte("HIERARCH ") 90 kCOMMENT = []byte("COMMENT ") 91 kCONTINUE = []byte("CONTINUE") 92 kHISTORY = []byte("HISTORY ") 93 kEND = []byte("END ") 94 kEMPTY = []byte(" ") 95 ) 96 97 if len(bline) != kLINE { 98 return nil, fmt.Errorf("fitsio: invalid header line length") 99 } 100 101 // support for ESO HIERARCH keywords: find the '=' 102 if bytes.HasPrefix(bline, kHIERARCH) { 103 idx := bytes.Index(bline, []byte("=")) 104 if idx < 0 { 105 // no value indicator 106 card.Comment = strings.TrimRight(string(bline[8:]), " ") 107 return &card, nil 108 } 109 valpos = idx + 1 // point after '=' 110 keybeg = len(kHIERARCH) 111 keyend = idx 112 113 } else if len(bline) < 9 || 114 bytes.HasPrefix(bline, kCOMMENT) || 115 bytes.HasPrefix(bline, kCONTINUE) || 116 bytes.HasPrefix(bline, kHISTORY) || 117 bytes.HasPrefix(bline, kEND) || 118 bytes.HasPrefix(bline, kEMPTY) || 119 !bytes.HasPrefix(bline[8:], []byte("= ")) { // no '= ' in cols 9-10 120 121 // no value, so the comment extends from cols 9 - 80 122 card.Comment = strings.TrimRight(string(bline[8:]), " ") 123 124 if bytes.HasPrefix(bline, kCOMMENT) { 125 card.Name = "COMMENT" 126 } else if bytes.HasPrefix(bline, kCONTINUE) { 127 card.Name = "CONTINUE" 128 str := strings.TrimSpace(string(bline[len(kCONTINUE):])) 129 value, _, err := processString(str) 130 if err != nil { 131 return nil, err 132 } 133 card.Comment = value 134 return &card, nil 135 136 } else if bytes.HasPrefix(bline, kHISTORY) { 137 card.Name = "HISTORY" 138 } else if bytes.HasPrefix(bline, kEND) { 139 card.Name = "END" 140 } else if bytes.HasPrefix(bline, kEMPTY) || 141 !bytes.HasPrefix(bline[8:], []byte("= ")) { 142 card.Name = "" 143 } 144 145 return &card, nil 146 } else { 147 valpos = 10 148 keybeg = 0 149 keyend = 8 150 } 151 152 card.Name = strings.TrimSpace(string(bline[keybeg:keyend])) 153 154 // find number of leading blanks 155 nblanks := 0 156 for _, c := range bline[valpos:] { 157 if c != ' ' { 158 break 159 } 160 nblanks += 1 161 } 162 163 if nblanks+valpos == len(bline) { 164 // the absence of a value string is legal and simply indicates 165 // that the keyword value is undefined. 166 // don't write an error message in this case 167 return &card, nil 168 } 169 170 i := valpos + nblanks 171 switch bline[i] { 172 case '/': // start of the comment 173 i += 1 174 case '\'': // quoted string value ? 175 str, idx, err := processString(string(bline[i:])) 176 if err != nil { 177 return nil, err 178 } 179 switch { 180 case len(str) <= 69: // don't exceed 70-char null-terminated string length 181 card.Value = str 182 case len(str) > 69: 183 card.Value = str[:70] 184 } 185 i += idx 186 187 case '(': // a complex value 188 idx := bytes.IndexByte(bline[i:], ')') 189 if idx < 0 { 190 return nil, fmt.Errorf("fitsio: complex keyword missing closing ')' (%q)", string(bline)) 191 } 192 var x, y float64 193 str := strings.TrimSpace(string(bline[i : i+idx+1])) 194 _, err = fmt.Sscanf(str, "(%f,%f)", &x, &y) 195 if err != nil { 196 return nil, err 197 } 198 card.Value = complex(x, y) 199 i += idx + 1 200 201 default: // integer, float or logical FITS value string 202 v0 := bline[i] 203 value := "" 204 205 // find the end of the token 206 if valend := bytes.Index(bline[i:], []byte(" /")); valend < 0 { 207 value = string(bline[i:]) 208 } else { 209 value = string(bline[i : i+valend]) 210 } 211 i += len(value) 212 213 if (v0 >= '0' && v0 <= '9') || v0 == '+' || v0 == '-' { 214 value = strings.TrimSpace(value) 215 if strings.ContainsAny(value, ".DE") { 216 value = strings.Replace(value, "D", "E", 1) // converts D type floats to E type 217 x, err := strconv.ParseFloat(value, 64) 218 if err != nil { 219 return nil, err 220 } 221 card.Value = x 222 } else { 223 x, err := strconv.ParseInt(value, 10, 64) 224 if err != nil { 225 switch err := err.(type) { 226 case *strconv.NumError: 227 // try math/big.Int 228 if err.Err == strconv.ErrRange { 229 var x big.Int 230 _, err := fmt.Sscanf(value, "%v", &x) 231 if err != nil { 232 return nil, err 233 } 234 card.Value = x 235 } 236 default: 237 return nil, err 238 } 239 } else { 240 card.Value = int(x) 241 } 242 } 243 } else if v0 == 'T' { 244 card.Value = true 245 } else if v0 == 'F' { 246 card.Value = false 247 } else { 248 return nil, fmt.Errorf("fitsio: invalid card line (%q)", string(bline)) 249 } 250 } 251 252 idx := bytes.IndexByte(bline[i:], '/') 253 if idx < 0 { 254 // no comment 255 return &card, err 256 } 257 258 com := bline[i+idx+1:] 259 card.Comment = strings.TrimSpace(string(com)) 260 return &card, err 261 } 262 263 // makeHeaderLine makes a 80-byte line (or more) for a header FITS block from a Card. 264 // transliterated from CFITSIO's ffmkky. 265 func makeHeaderLine(card *Card) ([]byte, error) { 266 var err error 267 const kLINE = 80 268 var ( 269 kCONTINUE = []byte("CONTINUE") 270 ) 271 272 buf := new(bytes.Buffer) 273 buf.Grow(kLINE) 274 275 if card == nil { 276 return nil, fmt.Errorf("fitsio: nil Card") 277 } 278 279 switch card.Name { 280 case "", "COMMENT", "HISTORY": 281 str := card.Comment 282 vlen := len(str) 283 for i := 0; i < vlen; i += 72 { 284 end := i + 72 285 if end > vlen { 286 end = vlen 287 } 288 _, err = fmt.Fprintf(buf, "%-8s%-72s", card.Name, str[i:end]) 289 if err != nil { 290 return nil, err 291 } 292 } 293 return buf.Bytes(), err 294 case "END": 295 _, err = fmt.Fprintf(buf, "%-80s", "END") 296 if err != nil { 297 return nil, err 298 } 299 return buf.Bytes(), err 300 } 301 302 klen := len(card.Name) 303 304 if klen <= 8 && verifyCardName(card) == nil { 305 // a normal FITS keyword 306 _, err = fmt.Fprintf(buf, "%-8s= ", card.Name) 307 if err != nil { 308 return nil, err 309 } 310 klen = 10 311 } else { 312 // use the ESO HIERARCH convention for longer keyword names 313 314 if strings.Contains(card.Name, "=") { 315 return nil, fmt.Errorf( 316 "fitsio: illegal keyword name. contains an equal sign [%s]", 317 card.Name, 318 ) 319 } 320 key := card.Name 321 // dont repeat HIERARCH if the keyword already contains it 322 if !strings.HasPrefix(card.Name, "HIERARCH ") && 323 !strings.HasPrefix(card.Name, "hierarch ") { 324 key = "HIERARCH " + card.Name 325 } 326 n, err := fmt.Fprintf(buf, "%s= ", key) 327 if err != nil { 328 return nil, err 329 } 330 klen = n 331 } 332 333 if card.Value == nil { 334 // this case applies to normal keywords only 335 if klen == 10 { 336 // keywords with no value have no '=' 337 buf.Bytes()[8] = ' ' 338 if card.Comment != "" { 339 comment := " / " + card.Comment 340 max := len(comment) 341 if max > kLINE-klen { 342 max = kLINE - klen 343 } 344 _, err = fmt.Fprintf(buf, "%s", comment[:max]) 345 if err != nil { 346 return nil, err 347 } 348 } 349 } 350 } else { 351 buflen := buf.Len() 352 //valstr := "" 353 n := 0 354 switch v := card.Value.(type) { 355 case string: 356 vstr := "''" 357 if v != "" { 358 vstr = fmt.Sprintf("'%-8s'", v) 359 } 360 if len(vstr) < kLINE-buflen { 361 n, err = fmt.Fprintf(buf, "%-20s", vstr) 362 if err != nil { 363 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 364 } 365 } else { 366 // string too long. 367 // use CONTINUE blocks. 368 // replace last character of string with '&' 369 ampersand := len("&") 370 quotes := len("''") 371 spacesz := len(" ") 372 sz := kLINE - buflen - ampersand - quotes 373 vstr = fmt.Sprintf("'%-8s'", v[:sz]+"&") 374 n, err = fmt.Fprintf(buf, "%-20s", vstr) 375 if err != nil { 376 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 377 } 378 contlen := len(kCONTINUE) 379 blocksz := kLINE - contlen - ampersand - quotes - spacesz 380 for i := sz; i < len(v); i += blocksz { 381 end := i + blocksz 382 amper := "&" 383 if end > len(v) { 384 end = len(v) 385 amper = "" 386 } 387 vv := v[i:end] 388 vstr := fmt.Sprintf("'%-8s'", vv+amper) 389 n, err = fmt.Fprintf(buf, "%s %-20s", string(kCONTINUE), vstr) 390 if err != nil { 391 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 392 } 393 } 394 // fill buffer up to 80-byte mark so any remaining comment 395 // will have to be handled by a separate 'COMMENT' line 396 n = buf.Len() 397 align80 := (kLINE - (n % kLINE)) % kLINE 398 if align80 > 0 { 399 _, err = buf.Write(bytes.Repeat([]byte(" "), align80)) 400 if err != nil { 401 return nil, err 402 } 403 } 404 405 n = 0 406 buflen = buf.Len() % kLINE 407 } 408 409 case bool: 410 vv := "F" 411 if v { 412 vv = "T" 413 } 414 n, err = fmt.Fprintf(buf, "%20s", vv) 415 if err != nil { 416 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 417 } 418 419 case int: 420 n, err = fmt.Fprintf(buf, "%20d", v) 421 if err != nil { 422 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 423 } 424 425 case float64: 426 n, err = fmt.Fprintf(buf, "%#20G", v) 427 if err != nil { 428 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 429 } 430 431 case complex128: 432 n, err = fmt.Fprintf(buf, "(%10f,%10f)", real(v), imag(v)) 433 if err != nil { 434 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 435 } 436 437 case big.Int: 438 n, err = fmt.Fprintf(buf, "%s", v.String()) 439 if err != nil { 440 return nil, fmt.Errorf("fitsio: error writing card value [%s]: %v", card.Name, err) 441 } 442 443 default: 444 panic(fmt.Errorf("fitsio: invalid card value [%s]: %#v (%T)", card.Name, v, v)) 445 } 446 447 if n+buflen > kLINE { 448 return nil, fmt.Errorf("fitsio: value-string too big (%d) for card [%s]: %v\nbuf=|%s|", 449 n, card.Name, card.Value, string(buf.Bytes()), 450 ) 451 } 452 453 buflen = buf.Len() % kLINE 454 455 comment := " / " + card.Comment 456 max := len(comment) 457 // if card.Comment == "my-comment" { 458 // fmt.Printf("max=%d\n", max) 459 // fmt.Printf("buf=%d\n", buflen) 460 // fmt.Printf("buf=%d\n", buf.Len()) 461 // fmt.Printf("dif=%d\n", kLINE-buflen) 462 // } 463 464 if max > kLINE-buflen || (buf.Len() > kLINE && (buf.Len()%kLINE) == 0) { 465 // append a 'COMMENT' line 466 if buflen > 0 { 467 _, err = buf.Write(bytes.Repeat([]byte(" "), kLINE-buflen)) 468 if err != nil { 469 return nil, err 470 } 471 } 472 comline, err := makeHeaderLine(&Card{Name: "COMMENT", Comment: card.Comment}) 473 if err != nil { 474 return nil, err 475 } 476 _, err = buf.Write(comline) 477 if err != nil { 478 return nil, err 479 } 480 } else { 481 _, err = fmt.Fprintf(buf, "%s", comment[:max]) 482 if err != nil { 483 return nil, err 484 } 485 } 486 487 } 488 489 n := buf.Len() 490 align80 := (kLINE - (n % kLINE)) % kLINE 491 if align80 > 0 { 492 _, err = buf.Write(bytes.Repeat([]byte(" "), align80)) 493 } 494 return buf.Bytes(), err 495 } 496 497 // verifyCardName verifies a Card name conforms to the FITS standard. 498 // Must contain only capital letters, digits, minus or underscore chars. 499 // Trailing spaces are allowed. 500 func verifyCardName(card *Card) error { 501 var err error 502 spaces := false 503 504 max := len(card.Name) 505 if max > 8 { 506 max = 8 507 } 508 509 for idx, c := range card.Name { 510 switch { 511 case (c >= 'A' && c <= 'Z') || 512 (c >= '0' && c <= '9') || 513 c == '-' || c == '_': 514 if spaces { 515 return fmt.Errorf("fitsio: card name contains embedded space(s): %q", card.Name) 516 } 517 case c == ' ': 518 spaces = true 519 default: 520 return fmt.Errorf( 521 "fitsio: card name contains illegal character %q (idx=%d)", 522 card.Name, idx, 523 ) 524 } 525 } 526 527 return err 528 } 529 530 // typeFromForm returns a FITS Type corresponding to a FITS TFORM string 531 func typeFromForm(form string, htype HDUType) (Type, error) { 532 var err error 533 var typ Type 534 535 switch htype { 536 case BINARY_TBL: 537 j := strings.IndexAny(form, "PQABCDEIJKLMX") 538 if j < 0 { 539 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) 540 } 541 repeat := 1 542 if j > 0 { 543 r, err := strconv.ParseInt(form[:j], 10, 32) 544 if err != nil { 545 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) 546 } 547 repeat = int(r) 548 } 549 slice := false 550 dsize := 0 551 hsize := 0 552 switch form[j] { 553 case 'P': 554 j += 1 555 slice = true 556 dsize = 2 * 4 557 case 'Q': 558 j += 1 559 slice = true 560 dsize = 2 * 8 561 } 562 tc, ok := g_fits2tc[BINARY_TBL][form[j]] 563 if !ok { 564 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no typecode found)", form) 565 } 566 rt, ok := g_fits2go[BINARY_TBL][form[j]] 567 if !ok { 568 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no Type found)", form) 569 } 570 571 elemsz := 0 572 switch form[j] { 573 case 'A': 574 elemsz = repeat 575 repeat = 1 576 case 'X': 577 elemsz = 1 578 const nbits = 8 579 sz := repeat + (nbits-(repeat%nbits))%nbits 580 repeat = sz / nbits 581 582 case 'L', 'B': 583 elemsz = 1 584 case 'I': 585 elemsz = 2 586 case 'J', 'E': 587 elemsz = 4 588 case 'K', 'D', 'C': 589 elemsz = 8 590 case 'M': 591 elemsz = 16 592 } 593 594 switch slice { 595 case true: 596 hsize = elemsz 597 typ = Type{ 598 tc: -tc, 599 len: repeat, 600 dsize: dsize, 601 hsize: hsize, 602 gotype: reflect.SliceOf(rt), 603 } 604 605 case false: 606 dsize = elemsz 607 if repeat > 1 { 608 typ = Type{ 609 tc: tc, 610 len: repeat, 611 dsize: dsize, 612 hsize: hsize, 613 gotype: reflect.ArrayOf(repeat, rt), 614 } 615 } else { 616 typ = Type{ 617 tc: tc, 618 len: repeat, 619 dsize: dsize, 620 hsize: hsize, 621 gotype: rt, 622 } 623 } 624 } 625 626 if typ.dsize*typ.len == 0 { 627 if form != "0A" { 628 return typ, fmt.Errorf("fitsio: invalid dtype! form=%q typ=%#v\n", form, typ) 629 } 630 } 631 632 case ASCII_TBL: 633 // fmt.Printf("### form %q\n", form) 634 j := strings.IndexAny(form, "ADEFI") 635 if j < 0 { 636 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) 637 } 638 j = strings.Index(form, ".") 639 if j == -1 { 640 j = len(form) 641 } 642 repeat := 1 643 r, err := strconv.ParseInt(form[1:j], 10, 32) 644 if err != nil { 645 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s)", form) 646 } 647 repeat = int(r) 648 649 tc, ok := g_fits2tc[ASCII_TBL][form[0]] 650 if !ok { 651 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no typecode found)", form) 652 } 653 rt, ok := g_fits2go[ASCII_TBL][form[0]] 654 if !ok { 655 return typ, fmt.Errorf("fitsio: invalid TFORM format (%s) (no Type found)", form) 656 } 657 658 dsize := 0 659 hsize := 0 660 661 switch form[0] { 662 case 'A': 663 dsize = repeat 664 repeat = 1 665 case 'I': 666 dsize = repeat 667 repeat = 1 668 case 'D', 'E': 669 dsize = repeat 670 repeat = 1 671 case 'F': 672 dsize = repeat 673 repeat = 1 674 } 675 676 typ = Type{ 677 tc: tc, 678 len: repeat, 679 dsize: dsize, 680 hsize: hsize, 681 gotype: rt, 682 } 683 // fmt.Printf(">>> %#v (%v)\n", typ, typ.gotype.Name()) 684 685 if typ.dsize*typ.len == 0 { 686 if form != "0A" { 687 return typ, fmt.Errorf("fitsio: invalid dtype! form=%q typ=%#v\n", form, typ) 688 } 689 } 690 } 691 692 return typ, err 693 } 694 695 // txtfmtFromForm returns a suitable go-fmt format from a FITS TFORM 696 func txtfmtFromForm(form string) string { 697 var format string 698 var code rune 699 m := -1 700 w := 14 701 702 fmt.Sscanf(form, "%c%d.%d", &code, &w, &m) 703 704 switch form[0] { 705 case 'A': 706 format = fmt.Sprintf("%%%d.%ds", w, w) // Aw -> %ws 707 case 'I': 708 format = fmt.Sprintf("%%%dd", w) // Iw -> %wd 709 case 'B': 710 format = fmt.Sprintf("%%%db", w) // Bw -> %wb, binary 711 case 'O': 712 format = fmt.Sprintf("%%%do", w) // Ow -> %wo, octal 713 case 'Z': 714 format = fmt.Sprintf("%%%dX", w) // Zw -> %wX, hexadecimal 715 case 'F': 716 if m != -1 { 717 format = fmt.Sprintf("%%%d.%df", w, m) // Fw.d -> %w.df 718 } else { 719 format = fmt.Sprintf("%%%df", w) // Fw -> %wf 720 } 721 case 'E', 'D': 722 if m != -1 { 723 format = fmt.Sprintf("%%%d.%de", w, m) // Fw.d -> %w.df 724 } else { 725 format = fmt.Sprintf("%%%de", w) // Ew -> %we 726 } 727 case 'G': 728 if m != -1 { 729 format = fmt.Sprintf("%%%d.%dg", w, m) // Fw.d -> %w.df 730 } else { 731 format = fmt.Sprintf("%%%dg", w) // Gw -> %wg 732 } 733 } 734 return format 735 } 736 737 // formFromGoType returns a suitable FITS TFORM string from a reflect.Type 738 func formFromGoType(rt reflect.Type, htype HDUType) string { 739 hdr := "" 740 var t reflect.Type 741 switch rt.Kind() { 742 case reflect.Array: 743 hdr = fmt.Sprintf("%d", rt.Len()) 744 t = rt.Elem() 745 case reflect.Slice: 746 hdr = "Q" 747 t = rt.Elem() 748 default: 749 t = rt 750 } 751 752 dict, ok := g_gotype2FITS[t.Kind()] 753 if !ok { 754 return "" 755 } 756 757 form, ok := dict[htype] 758 if !ok { 759 return "" 760 } 761 762 return hdr + form 763 } 764 765 var g_gotype2FITS = map[reflect.Kind]map[HDUType]string{ 766 767 reflect.Bool: { 768 ASCII_TBL: "", 769 BINARY_TBL: "L", 770 }, 771 772 reflect.Int: { 773 ASCII_TBL: "I4", 774 BINARY_TBL: "K", 775 }, 776 777 reflect.Int8: { 778 ASCII_TBL: "I4", 779 BINARY_TBL: "B", 780 }, 781 782 reflect.Int16: { 783 ASCII_TBL: "I4", 784 BINARY_TBL: "I", 785 }, 786 787 reflect.Int32: { 788 ASCII_TBL: "I4", 789 BINARY_TBL: "J", 790 }, 791 792 reflect.Int64: { 793 ASCII_TBL: "I4", 794 BINARY_TBL: "K", 795 }, 796 797 reflect.Uint: { 798 ASCII_TBL: "I4", 799 BINARY_TBL: "V", 800 }, 801 802 reflect.Uint8: { 803 ASCII_TBL: "I4", 804 BINARY_TBL: "B", 805 }, 806 807 reflect.Uint16: { 808 ASCII_TBL: "I4", 809 BINARY_TBL: "U", 810 }, 811 812 reflect.Uint32: { 813 ASCII_TBL: "I4", 814 BINARY_TBL: "V", 815 }, 816 817 reflect.Uint64: { 818 ASCII_TBL: "I4", 819 BINARY_TBL: "V", 820 }, 821 822 reflect.Uintptr: { 823 ASCII_TBL: "", 824 BINARY_TBL: "", 825 }, 826 827 reflect.Float32: { 828 ASCII_TBL: "E26.17", // must write as float64 since we can only read as such 829 BINARY_TBL: "E", 830 }, 831 832 reflect.Float64: { 833 ASCII_TBL: "E26.17", 834 BINARY_TBL: "D", 835 }, 836 837 reflect.Complex64: { 838 ASCII_TBL: "", 839 BINARY_TBL: "C", 840 }, 841 842 reflect.Complex128: { 843 ASCII_TBL: "", 844 BINARY_TBL: "M", 845 }, 846 847 reflect.Array: { 848 ASCII_TBL: "", 849 BINARY_TBL: "", 850 }, 851 852 reflect.Slice: { 853 ASCII_TBL: "", 854 BINARY_TBL: "", 855 }, 856 857 reflect.String: { 858 ASCII_TBL: "A80", 859 BINARY_TBL: "80A", 860 }, 861 } 862 863 var g_fits2go = map[HDUType]map[byte]reflect.Type{ 864 ASCII_TBL: { 865 'A': reflect.TypeOf((*string)(nil)).Elem(), 866 'I': reflect.TypeOf((*int)(nil)).Elem(), 867 'E': reflect.TypeOf((*float64)(nil)).Elem(), 868 'D': reflect.TypeOf((*float64)(nil)).Elem(), 869 'F': reflect.TypeOf((*float64)(nil)).Elem(), 870 }, 871 872 BINARY_TBL: { 873 'A': reflect.TypeOf((*string)(nil)).Elem(), 874 'B': reflect.TypeOf((*byte)(nil)).Elem(), 875 'L': reflect.TypeOf((*bool)(nil)).Elem(), 876 'I': reflect.TypeOf((*int16)(nil)).Elem(), 877 'J': reflect.TypeOf((*int32)(nil)).Elem(), 878 'K': reflect.TypeOf((*int64)(nil)).Elem(), 879 'E': reflect.TypeOf((*float32)(nil)).Elem(), 880 'D': reflect.TypeOf((*float64)(nil)).Elem(), 881 'C': reflect.TypeOf((*complex64)(nil)).Elem(), 882 'M': reflect.TypeOf((*complex128)(nil)).Elem(), 883 'X': reflect.TypeOf((*byte)(nil)).Elem(), 884 }, 885 } 886 887 var g_fits2tc = map[HDUType]map[byte]typecode{ 888 ASCII_TBL: { 889 'A': tcString, 890 'I': tcInt64, 891 'E': tcFloat64, 892 'D': tcFloat64, 893 'F': tcFloat64, 894 }, 895 896 BINARY_TBL: { 897 'A': tcString, 898 'B': tcByte, 899 'L': tcBool, 900 'I': tcInt16, 901 'J': tcInt32, 902 'K': tcInt64, 903 'E': tcFloat32, 904 'D': tcFloat64, 905 'C': tcComplex64, 906 'M': tcComplex128, 907 'X': tcByte, 908 }, 909 }