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