/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * filename: m-ecat64.c                                                    *
 *                                                                         *
 * UTIL C-source: Medical Image Conversion Utility                         *
 *                                                                         *
 * purpose      : Read and Write ECAT 6.4 files                            *
 *                                                                         *
 * project      : (X)MedCon by Erik Nolf                                   *
 *                                                                         *
 * Functions    : MdcCheckECAT6()         - Check for ECAT 6.4 format      *
 *                MdcReadECAT6()          - Read ECAT 6.4 file             *
 *                MdcWriteECAT6()         - Write ECAT 6.4 file            *
 *                MdcGetSliceLocation()   - Get slice location             *
 *                MdcGetFilterCode()      - Get code number of filter      *
 *                MdcFillMainHeader()     - Fill in Main Header            *
 *                MdcFillImageSubHeader() - Fill in Image SubHeader        *
 *                MdcPrintEcatInfoDB()    - Print ECAT database info       *
 *                                                                         *
 *                                                                         *
 * Notes        : source needs m-matrix.h & m-matrix.c                     *
 *                                                                         *
 * Credits      : - CTI coders       - for creating the basic code         *
 *                - Johan Keppens    - getting it to work initially        *
 *                - Sakari Alenius   - reading using a matrix list         *
 *                                                                         *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 */

/*
   Copyright (C) 1997-2026 by Erik Nolf

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any later
   version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
   Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   59 Place - Suite 330, Boston, MA 02111-1307, USA.  */

/****************************************************************************
                              H E A D E R S
****************************************************************************/

#include "m-depend.h"

#include <stdio.h>
#ifdef LIBMDC_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef LIBMDC_HAVE_STRING_H
#include <string.h>
#endif
#ifdef LIBMDC_HAVE_STRINGS_H
#ifndef _WIN32
#include <strings.h>
#endif
#endif

#include "medcon.h"

/****************************************************************************
                              D E F I N E S
****************************************************************************/

#define MDC_NUM_BEDS_TWEAK      MDC_YES /* check on zero/one based num_beds   */

#ifdef _WIN32
#define MDC_ECAT6_RESTRICT_DIMS MDC_YES /* only square dim and max 256 (safe) */
#else
#define MDC_ECAT6_RESTRICT_DIMS MDC_NO  /* no square dim and no max  (danger) */
#endif

static Uint32 saved_mwidth;
static Uint32 saved_mheight;

static char MdcEcatDataTypes
        [MDC_MAX_ECATDATATYPES][MDC_MAX_ECATDATATYPES_SIZE]=
                      {"Unknown","ByteData","VAX Int16","VAX Int32",
                       "VAX float","IEEE float","SUN Int16","SUN Int32"};
static char MdcEcatFileTypes
        [MDC_MAX_ECATFILETYPES][MDC_MAX_ECATFILETYPES_SIZE]=
                      {"Unknown","Sinogram","PetImage","Attenuation",
                       "Normalization","Smooth File"};
static char MdcEcatAcquisitionTypes
        [MDC_MAX_ECATACQTYPES][MDC_MAX_ECATACQTYPES_SIZE]=
                      {"Undefined","Blank","Transmission",
                       "Static Emission","Dynamic Emission","Gated Emission",
                       "Transmission Rectilinear","Emission Rectilinear",
                       "Whole Body Transmission","Whole Body Static"};

static char MdcEcatFilterTypes
        [MDC_MAX_ECATFLTRTYPES][MDC_MAX_ECATFLTRTYPES_SIZE]=
                      {"None","Ramp","Butter","Hann",
                       "Hamm","Parzen","Shepp","Unknown"};

static char MdcEcatQuantificationUnits
        [MDC_MAX_ECATQUANTTYPES][MDC_MAX_ECATQUANTTYPES_SIZE]=
                      {"Total Counts","Undefined",
                       "ECAT counts/second/pixel","uCi/ml [1uCi = 37Bq]",
                       "LMRGlu","LMRGlu umol/min/100g","LMRGlu mg/min/100g",
                       "nCi/ml","Well counts","Becquerels","ml/min/100g",
                       "ml/min/g"};

static Int16 MdcEcatSystemTypes[MDC_MAX_ECATSYSTEMTYPES]=
                      {831, 911, 931, 933, 951, 953};

/****************************************************************************
                            F U N C T I O N S
****************************************************************************/
int MdcCheckECAT6(FILEINFO *fi)
{
  Mdc_Main_header mh;
  int i;

  if (mdc_mat_read_main_header(fi->ifp,&mh)) return MDC_BAD_READ;

  if (mh.system_type == MDC_ECAT6_SYST_TYPE) return MDC_FRMT_ECAT6;

  for (i=0; i < MDC_MAX_ECATSYSTEMTYPES; i++) {
     if (mh.system_type == MdcEcatSystemTypes[i])
       return MDC_FRMT_ECAT6;
  }

  return MDC_FRMT_NONE;

}


const char *MdcReadECAT6(FILEINFO *fi)
{
  FILE *fp = fi->ifp;
  IMG_DATA *id=NULL;
  DYNAMIC_DATA *dd=NULL;
  int i, error;
  const char *err;
  char *str;
  Uint32 bytes, img=0, found=0, number;
  Mdc_Main_header mh;
  Mdc_Image_subheader ish;
  Mdc_Scan_subheader ssh;
  Mdc_Attn_subheader ash;
  Mdc_Norm_subheader nsh;
  struct Mdc_MatDir entry, matrix_list[MDC_ECAT6_MAX_MATRICES];
  struct Mdc_Matval matval;
  Int16 data_type=BIT16_S;
  int bed,gate,frame,plane,data=0,nb,ng,nf,np,nd;
  int  matnum, startblk, endblk, num_matrices;
  float slice_position;

  if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Reading ECAT6:");

  if (MDC_VERBOSE) MdcPrntMesg("ECAT6 Reading <%s> ...",fi->ifname);

  /* put some defaults we use */
  fi->endian=MDC_FILE_ENDIAN=MDC_LITTLE_ENDIAN;
  fi->modality = M_PT;

  /* read the main header */
  error = mdc_mat_read_main_header(fp, &mh);
  if (error) return("ECAT6 Bad read main header");

/*
  if (MDC_INFO_DB) {
    MdcPrintEcatInfoDB(&mh);
    return NULL;
  }
*/

  if (MDC_INFO || MDC_INFO_DB) {

    MdcPrntScrn("Main Header (%d bytes)\n",MH_64_SIZE);
    MdcPrintLine('-',MDC_HALF_LENGTH);

    strncpy(mdcbufr,mh.original_file_name,20); mdcbufr[20]='\0';
    MdcPrntScrn("Original Filename        : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Software Version         : %d\n",mh.sw_version);
    MdcPrntScrn("Data Type                : %d ",mh.data_type);
    if ((mh.data_type > -1) && (mh.data_type < 8))
      MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[mh.data_type]);
    else
      MdcPrntScrn("(= Unknown)\n");
    MdcPrntScrn("System Type              : %d\n",mh.system_type);
    MdcPrntScrn("File Type                : %d ",mh.file_type);
    if ((mh.file_type > -1) && (mh.file_type < 5))
      MdcPrntScrn("(= %s)\n",MdcEcatFileTypes[mh.file_type]);
    else
      MdcPrntScrn("(= Unknown)\n");
    strncpy(mdcbufr,mh.node_id,10); mdcbufr[10]='\0';
    MdcPrntScrn("Node Id                  : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Scan Date - Day          : %d\n",mh.scan_start_day);
    MdcPrntScrn("          - Month        : %d\n",mh.scan_start_month);
    MdcPrntScrn("          - Year         : %d\n",mh.scan_start_year);
    MdcPrntScrn("          - Hour         : %d\n",mh.scan_start_hour);
    MdcPrntScrn("          - Minute       : %d\n",mh.scan_start_minute);
    MdcPrntScrn("          - Second       : %d\n",mh.scan_start_second);
    strncpy(mdcbufr,mh.isotope_code,8); mdcbufr[8]='\0';
    MdcPrntScrn("Isotope Code             : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Isotope Halflife         : %f [sec]\n",mh.isotope_halflife);
    strncpy(mdcbufr,mh.radiopharmaceutical,32); mdcbufr[32]='\0';
    MdcPrntScrn("Radiopharmaceutical      : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Gantry Tilt              : %f [degrees]\n",mh.gantry_tilt);
    MdcPrntScrn("Gantry Rotation          : %f [degrees]\n"
                                                          ,mh.gantry_rotation);
    MdcPrntScrn("Bed Elevation            : %f [cm]\n",mh.bed_elevation);
    MdcPrntScrn("Rotating Source Speed    : %d [revolutions/minute]\n"
                                       ,mh.rot_source_speed);
    MdcPrntScrn("Wobble Control Speed     : %d [revolutions/minute]\n"
                                       ,mh.wobble_speed);
    MdcPrntScrn("Transmission Source      : %d\n",mh.transm_source_type);
    MdcPrntScrn("Axial Field of View      : %f [cm]\n",mh.axial_fov);
    MdcPrntScrn("Transaxial Field of View : %f [cm]\n",mh.transaxial_fov);
    MdcPrntScrn("Transaxial Sampling Mode : %d\n",mh.transaxial_samp_mode);
    MdcPrntScrn("Coincidence Sampling Mode: %d\n",mh.coin_samp_mode);
    MdcPrntScrn("Axial Sampling Mode      : %d\n",mh.axial_samp_mode);
    MdcPrntScrn("Calibration Factor       : %f\n",mh.calibration_factor);
    MdcPrntScrn("Calibration Units        : %d ",mh.calibration_units);
    if ((mh.calibration_units > -1) && (mh.calibration_units < 12))
      MdcPrntScrn("(= %s)\n"
                    ,MdcEcatQuantificationUnits[mh.calibration_units]);
    else
      MdcPrntScrn("(= Unknown)\n");
    MdcPrntScrn("Compression Code         : %d\n",mh.compression_code);
    strncpy(mdcbufr,mh.study_name,12); mdcbufr[12]='\0';
    MdcPrntScrn("Study Name               : "); MdcPrintStr(mdcbufr);
    strncpy(mdcbufr,mh.patient_id,16); mdcbufr[16]='\0';
    MdcPrntScrn("Patient Id               : "); MdcPrintStr(mdcbufr);
    strncpy(mdcbufr,mh.patient_name,32); mdcbufr[32]='\0';
    MdcPrntScrn("Patient Name             : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Patient Sex              : "); MdcPrintChar(mh.patient_sex);
    MdcPrntScrn("\n");
    strncpy(mdcbufr,mh.patient_age,10); mdcbufr[10]='\0';
    MdcPrntScrn("Patient Age              : "); MdcPrintStr(mdcbufr);
    strncpy(mdcbufr,mh.patient_height,10); mdcbufr[10]='\0';
    MdcPrntScrn("Patient Height           : "); MdcPrintStr(mdcbufr);
    strncpy(mdcbufr,mh.patient_weight,10); mdcbufr[10]='\0';
    MdcPrntScrn("Patient Weight           : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Patient Dexterity        : ");
       MdcPrintChar(mh.patient_dexterity);
    MdcPrntScrn("\n");
    strncpy(mdcbufr,mh.physician_name,32); mdcbufr[32]='\0';
    MdcPrntScrn("Physician Name           : "); MdcPrintStr(mdcbufr);
    strncpy(mdcbufr,mh.operator_name,32); mdcbufr[32]='\0';
    MdcPrntScrn("Operator Name            : "); MdcPrintStr(mdcbufr);
    strncpy(mdcbufr,mh.study_description,32); mdcbufr[32]='\0';
    MdcPrntScrn("Study Description        : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Acquisition Type         : %d ",mh.acquisition_type);
    if ((mh.acquisition_type > -1) && (mh.acquisition_type <= 9))
      MdcPrntScrn("(= %s)\n",MdcEcatAcquisitionTypes[mh.acquisition_type]);
    else
      MdcPrntScrn("(= Unknown)\n");
    MdcPrntScrn("Bed Type                 : %d\n",mh.bed_type);
    MdcPrntScrn("Septa Type               : %d\n",mh.septa_type);
    strncpy(mdcbufr,mh.facility_name,20); mdcbufr[20]='\0';
    MdcPrntScrn("Facility Name            : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Number of Planes         : %d\n",mh.num_planes);
    MdcPrntScrn("Number of Frames         : %d\n",mh.num_frames);
    MdcPrntScrn("Number of Gates          : %d\n",mh.num_gates);
    MdcPrntScrn("Number of Bed Positions  : %d\n",mh.num_bed_pos);
    MdcPrntScrn("Initial Bed Position     : %f [cm]\n",mh.init_bed_position);
    for (i=0; i<15; i++)
    MdcPrntScrn("Bed Offset[%02d]           : %f [cm]\n",i+1
                                                           ,mh.bed_offset[i]);
    MdcPrntScrn("Plane Separation         : %f [cm]\n",mh.plane_separation);
    MdcPrntScrn("Lower Scatter Threshold  : %d [KeV]\n",mh.lwr_sctr_thres);
    MdcPrntScrn("Lower True Threshold     : %d [KeV]\n",mh.lwr_true_thres);
    MdcPrntScrn("Upper True Threshold     : %d [KeV]\n",mh.upr_true_thres);
    MdcPrntScrn("Collimator               : %6.0f\n",mh.collimator);
    strncpy(mdcbufr,mh.user_process_code,10); mdcbufr[10]='\0';
    MdcPrntScrn("User Process Code        : "); MdcPrintStr(mdcbufr);
    MdcPrntScrn("Acquisition Mode         : %d\n",mh.acquisition_mode);

  }

  if (MDC_INFO_DB) return(NULL); /* just needed db info */

  if ((mh.file_type!=MDC_ECAT6_SCAN_FILE)  &&
      (mh.file_type!=MDC_ECAT6_IMAGE_FILE) &&
      (mh.file_type!=MDC_ECAT6_ATTN_FILE)  &&
      (mh.file_type!=MDC_ECAT6_NORM_FILE) )
    return("ECAT6 Unsupported file type");

  if (mh.num_frames  <= 0 ) mh.num_frames  = 1;
  if (mh.num_gates   <= 0 ) mh.num_gates   = 1;
  if (mh.num_bed_pos <  0 ) mh.num_bed_pos = 0;

  /* fill in global FILEINFO data */
  fi->dim[0]= 6;
  fi->dim[3]= mh.num_planes;
  fi->dim[4]= mh.num_frames;
  fi->dim[5]= mh.num_gates;
  fi->dim[6]= mh.num_bed_pos + 1; /* must be 1-based */

#if MDC_NUM_BEDS_TWEAK
  /* double check num_bed_pos value due to */
  /* inconsistent use as zero or one based */
  bed = mh.num_bed_pos;
  while (!mdc_mat_lookup(fp,mdc_mat_numcod(1,1,1,0,bed),&entry) && (bed > 0)) {
       bed--;
  }
  fi->dim[6] = bed + 1;
#endif

  /* check for unsupported bed overlap */
  if (fi->dim[6] > 1) {
    float axial_width, bed_offset=mh.bed_offset[0];

    if (bed_offset < 0) bed_offset = -bed_offset;

    axial_width = mh.plane_separation * (float)fi->dim[3];
    if ((axial_width - bed_offset) >= 1.0) {
      MdcPrntWarn("ECAT6 Bed overlaps unsupported");
    }
  }

  for (i=3, number=1; i<=6; i++) number*=fi->dim[i];

  if (number == 0) return("ECAT6 No valid images specified");

  /* fill in orientation information */
  fi->pat_slice_orient = MDC_SUPINE_HEADFIRST_TRANSAXIAL; /* default! */
  str = MdcGetStrPatPos(fi->pat_slice_orient);
  MdcStringCopy(fi->pat_pos,str,strlen(str));

  if ( (strncmp(mh.user_process_code,"COR",10)==0) ||
       (strncmp(mh.user_process_code,"SAG",10)==0)) {
    /* CORONAL SLICES  or SAGITTAL SLICES
       The images Ecat 6.4 software writes are useless:
       128x128 images with the small coronal/sagittal slice in it ...
       This means their pixel_xsize & pixel_ysize doesn't quite
       fit the real world dimensions any more !!
       Therefore we don't even try to attempt writing the proper
       orientation information.
     */
  }else{
    /* "TRA" or nothing
       TRANSAXIAL SLICES (Transverse)
       Writing the proper orientation information
       See man-page `m-acr.4' for more info (!)
     */
    str = MdcGetStrPatOrient(fi->pat_slice_orient);
    MdcStringCopy(fi->pat_orient,str,strlen(str));
  }

  /* fill in patient study related information */
  fi->patient_sex[0] = mh.patient_sex; fi->patient_sex[1]='\0';
  MdcStringCopy(fi->patient_name,mh.patient_name,32);
  MdcStringCopy(fi->patient_id,mh.patient_id,16);
  fi->patient_weight   = (float)atof(mh.patient_weight);
  fi->patient_height   = (float)atof(mh.patient_height);
  fi->study_date_day   = mh.scan_start_day;
  fi->study_date_month = mh.scan_start_month;
  fi->study_date_year  = mh.scan_start_year;
  fi->study_time_hour  = mh.scan_start_hour;
  fi->study_time_minute= mh.scan_start_minute;
  fi->study_time_second= mh.scan_start_second;

  if ((mh.file_type==MDC_ECAT6_SCAN_FILE) ||
      (mh.file_type==MDC_ECAT6_IMAGE_FILE)) {
    switch (mh.acquisition_type) {
      case MDC_ECAT6_ACQTYPE_UNKNOWN :
          fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break;
      case MDC_ECAT6_ACQTYPE_BLANK :
          fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break;
      case MDC_ECAT6_ACQTYPE_TRANSMISSION :
          fi->acquisition_type = MDC_ACQUISITION_TOMO;    break;
      case MDC_ECAT6_ACQTYPE_STATIC_EMISSION :
          fi->acquisition_type = MDC_ACQUISITION_TOMO;    break;
      case MDC_ECAT6_ACQTYPE_DYNAMIC_EMISSION :
          fi->acquisition_type = MDC_ACQUISITION_DYNAMIC; break;
      case MDC_ECAT6_ACQTYPE_GATED_EMISSION :
          fi->acquisition_type = MDC_ACQUISITION_GSPECT;  break;
      case MDC_ECAT6_ACQTYPE_TRANSMISSION_RECT :
          fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break;
      case MDC_ECAT6_ACQTYPE_EMISSION_RECT :
          fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break;
      case MDC_ECAT6_ACQTYPE_WHOLE_BODY_TRANSM :
          fi->acquisition_type = MDC_ACQUISITION_UNKNOWN; break;
      case MDC_ECAT6_ACQTYPE_WHOLE_BODY_STATIC :
          fi->acquisition_type = MDC_ACQUISITION_TOMO;    break;
      default:
          fi->acquisition_type = MDC_ACQUISITION_UNKNOWN;
    }
  }else{
    fi->acquisition_type = MDC_ACQUISITION_UNKNOWN;
  }
  sprintf(mdcbufr,"ECAT%hd",mh.system_type);
  MdcStringCopy(fi->manufacturer,mdcbufr,strlen(mdcbufr));
  MdcStringCopy(fi->operator_name,mh.operator_name,32);
  MdcStringCopy(fi->study_descr,mh.study_description,32);
  MdcStringCopy(fi->study_id,mh.study_name,12);
  MdcStringCopy(fi->institution,mh.facility_name,20);
  MdcStringCopy(fi->radiopharma,mh.radiopharmaceutical,32);
  MdcStringCopy(fi->isotope_code,mh.isotope_code,8);

  fi->isotope_halflife = mh.isotope_halflife;
  fi->gantry_tilt      = mh.gantry_tilt;

  if (MDC_ECHO_ALIAS == MDC_YES) {
    MdcEchoAliasName(fi); return(NULL);
  }

  if (!MdcGetStructID(fi,number))
    return("ECAT6 Bad malloc IMG_DATA structs");

  /* always malloc dyndata structs */
  if (!MdcGetStructDD(fi,(Uint32)fi->dim[4]*fi->dim[5]*fi->dim[6]))
    return("ECAT6 Couldn't malloc DYNAMIC_DATA structs");

  /* always malloc beddata structs */
  if (!MdcGetStructBD(fi,(unsigned)fi->dim[6]))
    return("ECAT6 Couldn't malloc BED_DATA structs");

  /* fill in BED_DATA struct */
  fi->beddata[0].hoffset = mh.init_bed_position * 10.; /* mm */
  fi->beddata[0].voffset = mh.bed_elevation * 10.;     /* mm */
  for (i=1; i<fi->bednr; i++) {
    fi->beddata[i].hoffset  = mh.init_bed_position + mh.bed_offset[i-1];
    fi->beddata[i].hoffset *= 10.; /* mm */

    fi->beddata[i].voffset  = mh.bed_elevation * 10.;  /* mm */
  }

  /* ECAT6: matrices for each slice */
  num_matrices = mdc_mat_list(fp, matrix_list, MDC_ECAT6_MAX_MATRICES);
  if (num_matrices == 0)
    return("ECAT6 No matrices found");
  if ((Uint32)num_matrices > fi->number)
    return("ECAT6 Too many matrices found");

  /* sort matrices */
  if ( num_matrices > 1) {

    switch (MDC_ECAT6_SORT) {

      case MDC_ANATOMICAL: /* anatomical */
          bed = fi->dim[6]; /* one based */
          if (fi->dim[4] > 1) {
            mdc_plane_sort(matrix_list, num_matrices);
          }else{
            mdc_anatomical_sort(matrix_list, num_matrices, &mh, bed);
          }
          break;
      case MDC_BYFRAME   : /* by frame   */
          mdc_matnum_sort(matrix_list, num_matrices);
          break;
    }

  }

  for (bed=0; bed<fi->dim[6]; bed++)
  for (gate=1; gate<=fi->dim[5]; gate++)
  for (frame=1; frame<=fi->dim[4]; frame++)
  for (plane=1; plane<=fi->dim[3]; plane++) {

     if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL);

     if (img == (Uint32)num_matrices) break;

     if (fi->dynnr > 0) dd = &fi->dyndata[(fi->dim[4]*bed) + (frame-1)];

     mdc_mat_numdoc(matrix_list[img].matnum,&matval);
     nf = matval.frame; np = matval.plane; ng = matval.gate;
     nb = matval.bed;   nd = matval.data;
     matnum = mdc_mat_numcod(nf,np,ng,nd,nb);
     if (!mdc_mat_lookup(fp, matnum, &entry)) continue;
     startblk = entry.strtblk + 1;
     endblk = entry.endblk - entry.strtblk;

     switch (mh.file_type) {

      case MDC_ECAT6_SCAN_FILE:

       error = mdc_mat_read_scan_subheader(fp, startblk-1, &ssh);
       if (error) return("ECAT6 Bad read scan subheader");
       if (MDC_INFO) {
        MdcPrintLine('-',MDC_FULL_LENGTH);
        MdcPrntScrn("SINOGRAM SUBHEADER %05d:  ",img+1);
        MdcPrntScrn("Frame: %d  Plane: %d  Gate: %d  Data: %d  Bed: %d\n"
               ,frame,plane,gate,data,bed);
        MdcPrintLine('-',MDC_FULL_LENGTH);
        MdcPrntScrn("Data Type           : %d ",ssh.data_type);
        if ((ssh.data_type > -1) && (ssh.data_type < 8))
          MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[ssh.data_type]);
        else
          MdcPrntScrn("(= Unknown)\n");
        MdcPrntScrn("Number of Elements  : %d (width)\n",ssh.dimension_1);
        MdcPrntScrn("Number of Views     : %d (height)\n",ssh.dimension_2);
        MdcPrntScrn("Smoothing           : %d ",ssh.smoothing);
        switch (ssh.smoothing) {
          case 0: MdcPrntScrn("(= Not Smoothed)\n"); break;
          case 1: MdcPrntScrn("(= 9x9 Smoothing)\n"); break;
         default: MdcPrntScrn("(= Unknown)\n");
        }
        MdcPrntScrn("Processing Code     : %d\n",ssh.processing_code);
        MdcPrntScrn("Sample Distance     : %f [cm]\n",ssh.sample_distance);
        MdcPrntScrn("Isotope Halflife    : %f [sec]\n",ssh.isotope_halflife);
        MdcPrntScrn("Frame Duration (sec): %d [sec]\n"
                                                       ,ssh.frame_duration_sec);
        MdcPrntScrn("Gate Duration       : %d [ms]\n",ssh.gate_duration);
        MdcPrntScrn("R-Wave Offset       : %d [ms]\n",ssh.r_wave_offset);
        MdcPrntScrn("Scale factor        : %f\n",ssh.scale_factor);
        MdcPrntScrn("Minimum Scan Value  : %d\n",ssh.scan_min);
        MdcPrntScrn("Maximum Scan Value  : %d\n",ssh.scan_max);
        MdcPrntScrn("Total Prompts       : %d\n",ssh.prompts);
        MdcPrntScrn("Total Delayed Events: %d\n",ssh.delayed);
        MdcPrntScrn("Total Multiples     : %d\n",ssh.multiples);
        MdcPrntScrn("Total Net Trues     : %d (Prompts - Random)\n"
                                      ,ssh.net_trues);
        for (i=0; i<16; i++)
        MdcPrntScrn("Corrected   Singles [%2d] : %f\n",i+1
                                                         ,ssh.cor_singles[i]);
        for (i=0; i<16; i++)
        MdcPrntScrn("Uncorrected Singles [%2d] : %f\n",i+1
                                                         ,ssh.uncor_singles[i]);
        MdcPrntScrn("Total Average Corrected   Singles: %f\n"
                                                         ,ssh.tot_avg_cor);
        MdcPrntScrn("Total Average Uncorrected Singles: %f\n"
                                                         ,ssh.tot_avg_uncor);
        MdcPrntScrn("Total Coincidence Rage  : %d (from IPCP)\n"
                                                         ,ssh.total_coin_rate);
        MdcPrntScrn("Frame Start Time        : %d [ms]\n"
                                                         ,ssh.frame_start_time);
        MdcPrntScrn("Frame Duration          : %d [ms]\n"
                                                         ,ssh.frame_duration);
        MdcPrntScrn("Loss Correction Factor  : %f\n"
                                                     ,ssh.loss_correction_fctr);
        for (i=0; i<8; i++) MdcPrntScrn("Phy_Planes [%d]          : %d\n",i+1
                                                         ,ssh.phy_planes[i]);
       }

       /* fill in DYNAMIC_DATA struct */
       if ((dd != NULL) && (plane == (fi->dim[3]/2))) {
         /* take values from a plane halfway */
         dd->nr_of_slices        = fi->dim[3];
         dd->time_frame_start    = (float)ssh.frame_start_time;
         dd->time_frame_duration = (float)ssh.frame_duration;
       }

       /* fill in IMG_DATA struct */
       id = &fi->image[img];
       id->width = (Uint32)ssh.dimension_1;
       id->height = (Uint32)ssh.dimension_2;

       id->quant_units = 1;
       id->quant_scale = 1;
       id->calibr_units= 1;
       id->calibr_fctr = 1;

       id->quant_scale = ssh.scale_factor;

       id->pixel_xsize = id->pixel_ysize = ssh.sample_distance * 10.;/* mm */
       data_type = ssh.data_type;
       switch( data_type ) {
         case  BYTE_TYPE:  id->bits = 8; id->type = BIT8_U; break;
         case  M68K_I2  :
     /*  case  SUN_I2   : */
         case  VAX_I2   :  id->bits =16; id->type = BIT16_S; break;
         case  M68K_I4  :
     /*  case  SUN_I4   : */
         case  VAX_I4   :  id->bits =32; id->type = BIT32_S; break;
         case  IEEE_R4  :
     /*  case  SUN_R4   : */
         case  VAX_R4   :  id->bits =32; id->type = FLT32; break;
       }

       break;

      case MDC_ECAT6_IMAGE_FILE:

       error = mdc_mat_read_image_subheader(fp, startblk-1, &ish);
       if (error) return("ECAT6 Bad read image subheader");
       if (MDC_INFO) {
        MdcPrintLine('-',MDC_FULL_LENGTH);
        MdcPrntScrn("IMAGE SUBHEADER %05d:  ",img+1);
        MdcPrntScrn("Frame: %d  Plane: %d  Gate: %d  Data: %d  Bed: %d\n"
               ,frame,plane,gate,data,bed);
        MdcPrintLine('-',MDC_FULL_LENGTH);

        MdcPrntScrn("Data Type            : %d ",ish.data_type);
        if ((ish.data_type > -1) && (ish.data_type < 8))
          MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[ish.data_type]);
        else
          MdcPrntScrn("(= Unknown)\n");
        MdcPrntScrn("Number of Dimensions : %d\n",ish.num_dimensions);
        MdcPrntScrn("X Dimension          : %d\n",ish.dimension_1);
        MdcPrntScrn("Y Dimension          : %d\n",ish.dimension_2);
        MdcPrntScrn("X Offset             : %f [cm]\n",ish.x_origin);
        MdcPrntScrn("Y Offset             : %f [cm]\n",ish.y_origin);
        MdcPrntScrn("Recon Magnification Factor  : %f\n",ish.recon_scale);
        MdcPrntScrn("Quantification Scale Factor : %e\n",ish.quant_scale);
        MdcPrntScrn("Image Minimum Pixel Value   : %d\n",ish.image_min);
        MdcPrntScrn("Image Maximum Pixel Value   : %d\n",ish.image_max);
        MdcPrntScrn("Pixel Size         : %f [cm]\n",ish.pixel_size);
        MdcPrntScrn("Slice Width        : %f [cm]\n",ish.slice_width);
        MdcPrntScrn("Frame Duration     : %d [ms]\n",ish.frame_duration);
        MdcPrntScrn("Frame Start Time   : %d [ms]\n",ish.frame_start_time);
        MdcPrntScrn("Slice Location     : %d [cm]\n",ish.slice_location);
        MdcPrntScrn("Recon Start Hour   : %d\n",ish.recon_start_hour);
        MdcPrntScrn("Recon Start Minute : %d\n",ish.recon_start_minute);
        MdcPrntScrn("Recon Start Second : %d\n",ish.recon_start_sec);
        MdcPrntScrn("Gate Duration      : %d [ms]\n",ish.gate_duration);
        MdcPrntScrn("Filter code        : %d ",ish.filter_code);
        ish.filter_code = abs(ish.filter_code);
        if ((ish.filter_code > -1) && (ish.filter_code < 7))
          MdcPrntScrn("(= %s)\n",MdcEcatFilterTypes[ish.filter_code]);
        else
          MdcPrntScrn("(= Unknown)\n");
        MdcPrntScrn("Scan Matrix Number : %d\n",ish.scan_matrix_num);
        MdcPrntScrn("Normalization Matrix Number : %d\n",ish.norm_matrix_num);
        MdcPrntScrn("Attenuation Matrix Number   : %d\n"
                                              ,ish.atten_cor_matrix_num);
        MdcPrntScrn("Image Rotation              : %f [degrees]\n"
                                              ,ish.image_rotation);
        MdcPrntScrn("Plane Efficiency Correction Factor: %f\n"
                                                  ,ish.plane_eff_corr_fctr);
        MdcPrntScrn("Decay Correction Factor    : %f\n",ish.decay_corr_fctr);
        MdcPrntScrn("Loss Correction Factor     : %f\n",ish.loss_corr_fctr);
        MdcPrntScrn("Processing Code            : %d\n",ish.processing_code);
        MdcPrntScrn("Quantification Units       : %d ",ish.quant_units);
        if ((ish.quant_units > -1) && (ish.quant_units < 13))
          MdcPrntScrn("(= %s)\n",MdcEcatQuantificationUnits[ish.quant_units]);
        else
          MdcPrntScrn("(= Unknown)\n");
        MdcPrntScrn("Reconstruction Start Day   : %d\n",ish.recon_start_day);
        MdcPrntScrn("Reconstruction Start Month : %d\n"
                                             ,ish.recon_start_month);
        MdcPrntScrn("Reconstruction Start Year  : %d\n"
                                             ,ish.recon_start_year);
        MdcPrntScrn("Ecat Calibration Factor    : %f\n"
                                             ,ish.ecat_calibration_fctr);
        MdcPrntScrn("Well Counter Calibribration Factor : %f\n"
                                             ,ish.well_counter_cal_fctr);
        MdcPrntScrn("Filter Params - Cutoff Frequency : %f\n"
                                             ,ish.filter_params[0]);
        MdcPrntScrn("Filter Params - DC Component     : %f\n"
                                             ,ish.filter_params[1]);
        MdcPrntScrn("Filter Params - Ramp Slope       : %f\n"
                                             ,ish.filter_params[2]);
        MdcPrntScrn("Filter Params - (4)              : %f\n"
                                             ,ish.filter_params[3]);
        MdcPrntScrn("Filter Params - Scatter Comp 1   : %f\n"
                                             ,ish.filter_params[4]);
        MdcPrntScrn("Filter Params - Scatter Comp 2   : %f\n"
                                             ,ish.filter_params[5]);
        strncpy(mdcbufr,ish.annotation,40); mdcbufr[40]='\0';
        MdcPrntScrn("Annotation : "); MdcPrintStr(mdcbufr);

       }

       /* fill in DYNAMIC_DATA struct */
       if ((dd != NULL) && (plane == (fi->dim[3]/2))) {
         /* take values from a plane halfway */
         dd->nr_of_slices        = fi->dim[3];
         dd->time_frame_start    = (float)ish.frame_start_time;
         dd->time_frame_duration = (float)ish.frame_duration;
       }

       /* fill in IMG_DATA struct */

       id = &fi->image[img];
       id->width = (Uint32)ish.dimension_1;
       id->height = (Uint32)ish.dimension_2;
       id->recon_scale = ish.recon_scale;

       id->quant_units = ish.quant_units;
       id->quant_scale = ish.quant_scale;
       id->calibr_units= mh.calibration_units;
       id->calibr_fctr = ish.ecat_calibration_fctr;

       id->pixel_xsize = id->pixel_ysize = ish.pixel_size * 10.; /* in mm */
       id->slice_width = ish.slice_width * 10.; /* in mm */
       data_type = ish.data_type;
       switch( data_type ) {
         case  BYTE_TYPE:  id->bits = 8; id->type = BIT8_U;  break;
         case  M68K_I2  :
     /*  case  SUN_I2   : */
         case  VAX_I2   :  id->bits =16; id->type = BIT16_S; break;
         case  M68K_I4  :
     /*  case  SUN_I4   : */
         case  VAX_I4   :  id->bits =32; id->type = BIT32_S; break;
         case  IEEE_R4  :
     /*  case  SUN_R4   : */
         case  VAX_R4   :  id->bits =32; id->type = FLT32;   break;
       }

       id->slice_spacing = mh.plane_separation*10.;  /* separation in mm */

       if ( (strncmp(mh.user_process_code,"COR",10)==0) ||
            (strncmp(mh.user_process_code,"SAG",10)==0)) {
         /* CORONAL SLICES  or SAGITTAL SLICES
            The images Ecat 6.4 software writes are useless:
            128x128 images with the small coronal/sagittal slice in it ...
            This means their pixel_xsize & pixel_ysize doesn't quite
            fit the real world dimensions any more !!
            Therefore we don't even try to attempt writing the proper
            Acr/Nema variables ...
          */
       }else{ /* "TRA" or nothing */
         /* TRANSAXIAL SLICES (Transverse)             */
         /* Writing the proper Acr/Nema variables ...  */
         /* See man-page `m-acr.4' for more info (!)   */

         /* slice position with bed offset (mm) */
         if (bed == 0) {
           slice_position = mh.init_bed_position;
         }else{
           slice_position = mh.init_bed_position + mh.bed_offset[bed-1];
         }
         slice_position *= 10.; /* mm */

         MdcFillImgPos(fi,img,(Uint32)(plane-1),slice_position);
         MdcFillImgOrient(fi,img);

       }

       break;

     case MDC_ECAT6_ATTN_FILE:

       error = mdc_mat_read_attn_subheader(fp, startblk-1, &ash);
       if (error) return("ECAT6 Bad read attenuation subheader");
       if (MDC_INFO) {

        MdcPrintLine('-',MDC_FULL_LENGTH);
        MdcPrntScrn("ATTENUATION SUBHEADER %05d:  ",img+1);
        MdcPrntScrn("Frame: %d  Plane: %d  Gate: %d  Data: %d  Bed: %d\n"
               ,frame,plane,gate,data,bed);
        MdcPrintLine('-',MDC_FULL_LENGTH);
        MdcPrntScrn("Data Type                     : %d ",ash.data_type);
        if ((ash.data_type > -1) && (ash.data_type < 8))
          MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[ash.data_type]);
        else
          MdcPrntScrn("(= Unknown)\n");
        MdcPrntScrn("Attenuation Correction Method : %d\n"
                                                ,ash.attenuation_type);
        MdcPrntScrn("Number of Elements            : %d (width)\n"
                                                ,ash.dimension_1);
        MdcPrntScrn("Number of Views               : %d (height)\n"
                                                ,ash.dimension_2);
        MdcPrntScrn("Attenuation Scale Factor      : %f\n",ash.scale_factor);
        MdcPrntScrn("Ellipse X Offset              : %f [cm]\n"
                                                ,ash.x_origin);
        MdcPrntScrn("Ellipse Y Offset              : %f [cm]\n"
                                                ,ash.y_origin);
        MdcPrntScrn("Ellipse X Radius              : %f [cm]\n"
                                                ,ash.x_radius);
        MdcPrntScrn("Ellipse Y Radius              : %f [cm]\n"
                                                ,ash.y_radius);
        MdcPrntScrn("Ellipse Tilt Angle            : %f [degrees]\n"
                                                ,ash.tilt_angle);
        MdcPrntScrn("Mu-Absorption Coefficient     : %f [1/cm]\n"
                                                ,ash.attenuation_coeff);
        MdcPrntScrn("Sample Distance               : %f [cm]\n"
                                                ,ash.sample_distance);
       }

       /* fill in IMG_DATA struct */
       id = &fi->image[img];
       id->width = (Uint32)ash.dimension_1;
       id->height = (Uint32)ash.dimension_2;

       id->quant_units = 1;
       id->quant_scale = 1;
       id->calibr_units= 1;
       id->calibr_fctr = 1;

       id->quant_scale = ash.scale_factor;

       data_type = ash.data_type;
       switch( data_type ) {
         case  BYTE_TYPE:  id->bits = 8; id->type = BIT8_U;  break;
         case  M68K_I2  :
     /*  case  SUN_I2   : */
         case  VAX_I2   :  id->bits =16; id->type = BIT16_S; break;
         case  M68K_I4  :
     /*  case  SUN_I4   : */
         case  VAX_I4   :  id->bits =32; id->type = BIT32_S; break;
         case  IEEE_R4  :
     /*  case  SUN_R4   : */
         case  VAX_R4   :  id->bits =32; id->type = FLT32;   break;
       }

       break;

     case MDC_ECAT6_NORM_FILE:

       error = mdc_mat_read_norm_subheader(fp, startblk-1, &nsh);
       if (error) return("ECAT6 Bad read normalization subheader");
       if (MDC_INFO) {
        MdcPrintLine('-',MDC_FULL_LENGTH);
        MdcPrntScrn("NORMALIZATION SUBHEADER %05d:  ",img+1);
        MdcPrntScrn("Frame: %d  Plane: %d  Gate: %d  Data: %d  Bed: %d\n"
               ,frame,plane,gate,data,bed);
        MdcPrintLine('-',MDC_FULL_LENGTH);
        MdcPrntScrn("Data Type           : %d ",nsh.data_type);
        if ((nsh.data_type > -1) && (nsh.data_type < 8))
          MdcPrntScrn("(= %s)\n",MdcEcatDataTypes[nsh.data_type]);
        else
          MdcPrntScrn("(= Unknown)\n");
        MdcPrntScrn("Number of Elements  : %d (width)\n",nsh.dimension_1);
        MdcPrntScrn("Number of Views     : %d (height)\n",nsh.dimension_2);
        MdcPrntScrn("Normalization Scale Factor  : %f\n",nsh.scale_factor);
        MdcPrntScrn("Normalization Start Hour    : %d\n",nsh.norm_hour);
        MdcPrntScrn("Normalization Start Minute  : %d\n",nsh.norm_minute);
        MdcPrntScrn("Normalization Start Second  : %d\n",nsh.norm_second);
        MdcPrntScrn("Normalization Start Day     : %d\n",nsh.norm_day);
        MdcPrntScrn("Normalization Start Month   : %d\n",nsh.norm_month);
        MdcPrntScrn("Normalization Start Year    : %d\n",nsh.norm_year);
        MdcPrntScrn("Field of View Source Width  : %f [cm]\n"
                                                     ,nsh.fov_source_width);
        MdcPrntScrn("Ecat Calibration Factor     : %f\n"
                                                     ,nsh.ecat_calib_factor);
       }

       /* fill in IMG_DATA struct */
       id = &fi->image[img];
       id->width = (Uint32)nsh.dimension_1;
       id->height = (Uint32)nsh.dimension_2;

       id->quant_units = 1;
       id->quant_scale = nsh.scale_factor;
       id->calibr_units= mh.calibration_units;
       id->calibr_fctr = nsh.ecat_calib_factor;

       data_type = nsh.data_type;
       switch( data_type ) {
         case  BYTE_TYPE:  id->bits = 8; id->type = BIT8_U; break;
         case  M68K_I2  :
     /*  case  SUN_I2   : */
         case  VAX_I2   :  id->bits =16; id->type = BIT16_S; break;
         case  M68K_I4  :
     /*  case  SUN_I4   : */
         case  VAX_I4   :  id->bits =32; id->type = BIT32_S; break;
         case  IEEE_R4  :
     /*  case  SUN_R4   : */
         case  VAX_R4   :  id->bits =32; id->type = FLT32; break;
       }

       break;

    }

    bytes = id->width*id->height*MdcType2Bytes(id->type);
    bytes = MdcMatrixBlocks(bytes);

    id->buf = MdcGetImgBuffer(bytes);
    if (id->buf == NULL) return("ECAT6 Bad malloc image buffer");

    error = mdc_mat_read_matrix_data(fp,startblk,endblk,(Int16 *)id->buf);

    if (error) {
      MdcPrntWarn("ECAT6 Bad read matrix data");
      err=MdcHandleTruncated(fi,img+1,MDC_YES);
      if(err != NULL) return(err);
    }

    if (fi->truncated) break;

    img+=1;
  }

  /* check the images really found */
  if (num_matrices < fi->number) {
    found = (Uint32)num_matrices;
  }else if (img < fi->number)    {
    found = img;
  }else {
    found = fi->number;
  }
  if (found != fi->number) {
    err=MdcHandleTruncated(fi,found,MDC_YES);
    if (err != NULL) return(err);
  }

  /* fill in other FILEINFO variables */
  id = &fi->image[0];  /* first image */
  fi->dim[1] = id->width;
  fi->dim[2] = id->height;
  fi->bits = id->bits;
  fi->type = id->type;
  fi->pixdim[0]=3;
  fi->pixdim[1]=id->pixel_xsize;
  fi->pixdim[2]=id->pixel_ysize;
  fi->pixdim[3]=id->slice_width;
  if (mh.file_type == MDC_ECAT6_IMAGE_FILE) {
    MdcStringCopy(fi->filter_type,MdcEcatFilterTypes[abs(ish.filter_code)],
                                  MDC_MAX_ECATFLTRTYPES_SIZE);
    fi->reconstructed = MDC_YES;
    if (ish.decay_corr_fctr > 1.0 ) fi->decay_corrected = MDC_YES;

    MdcStringCopy(fi->recon_method,ish.annotation,strnlen(ish.annotation,40));
    if (strcmp(fi->recon_method,MDC_ECAT6_RECON_METHOD)  == 0 ) {
      strcpy(fi->recon_method,"Filtered Backprojection");
    }
  }else{
    fi->reconstructed = MDC_NO;
    strcpy(fi->recon_method,"None");
  }

  switch( data_type ) {
    case  BYTE_TYPE:  MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break;
/*  case  SUN_I2   : */
    case  M68K_I2  :  MDC_FILE_ENDIAN = MDC_BIG_ENDIAN;  break;
    case  VAX_I2   :  MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break;
/*  case  SUN_I4   : */
    case  M68K_I4  :  MDC_FILE_ENDIAN = MDC_BIG_ENDIAN;  break;
    case  VAX_I4   :  MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break;
/*  case  SUN_R4   : */
    case  IEEE_R4  :  MDC_FILE_ENDIAN = MDC_BIG_ENDIAN;  break;
    case  VAX_R4   :  MDC_FILE_ENDIAN = MDC_HOST_ENDIAN; break;
  }

  MdcCloseFile(fi->ifp);

  if (fi->truncated) return("ECAT6 Truncated image file");

  return NULL;
}

float MdcGetSliceLocation(FILEINFO *fi, Int32 img)
{
   int orient;
   float locat=0.;

   orient = MdcGetIntSliceOrient((int)fi->pat_slice_orient);

   switch (orient) {
     case MDC_TRANSAXIAL: /* z-coord */
        locat = fi->image[img].image_pos_pat[2];
        break;
     case MDC_SAGITTAL  : /* x-coord */
        locat = fi->image[img].image_pos_pat[0];
        break;
     case MDC_CORONAL   : /* y-coord */
        locat = fi->image[img].image_pos_pat[1];
        break;
   }

   if (locat < 0.) locat = -locat;

   return(locat / 10.); /* cm */

}

int MdcGetFilterCode(char *string)
{
  int i = 0;

  for (i=0; i<MDC_MAX_ECATFLTRTYPES; i++)
     if (strstr(string,MdcEcatFilterTypes[i]) != NULL) break;

  return(i);
}

void MdcFillMainHeader(FILEINFO *fi, Mdc_Main_header *mh)
{
  IMG_DATA *dd0=&fi->image[0];
  int i;
  float init_bed_position=0.;

  /* memset(mh,0,MH_64_SIZE); */
  memset(mh,0,sizeof(Mdc_Main_header));

  strncpy(mh->original_file_name,fi->ofname,20);
  mh->sw_version = 6;
  mh->system_type= 951;
  mh->file_type  = 2;

  mh->data_type = 2;     /* ECAT 6 only reads VAX Int16 */

  strncpy(mh->isotope_code,fi->isotope_code,8);
  mh->isotope_halflife = fi->isotope_halflife;
  strncpy(mh->radiopharmaceutical,fi->radiopharma,32);
  mh->calibration_units = fi->image[0].calibr_units;
  if (fi->pixdim[0] >= 3.) /* only valid for TRANSVERSE slices */
  mh->axial_fov = ((float)fi->dim[3] + 1.) * fi->pixdim[3] / 10.;
  mh->scan_start_day   = fi->study_date_day;
  mh->scan_start_month = fi->study_date_month;
  mh->scan_start_year  = fi->study_date_year;
  mh->scan_start_hour  = fi->study_time_hour;
  mh->scan_start_minute= fi->study_time_minute;
  mh->scan_start_second= fi->study_time_second;
  mh->plane_separation = fi->image[0].slice_spacing/10.; /* in cm */
  strncpy(mh->study_name,fi->study_id,12);
  mh->gantry_tilt = fi->gantry_tilt;
  strncpy(mh->patient_id,fi->patient_id,16);
  if (fi->patient_height == 0.) {
    mh->patient_height[0] = '\0';
  }else{
    sprintf(mdcbufr,"%.2f",fi->patient_height);
    strncpy(mh->patient_height,mdcbufr,10);
  }
  if (fi->patient_weight == 0.) {
    mh->patient_weight[0] = '\0';
  }else{
    sprintf(mdcbufr,"%.2f",fi->patient_weight);
    strncpy(mh->patient_weight,mdcbufr,10);
  }
  strncpy(mh->patient_name,fi->patient_name,32);
  mh->patient_sex = fi->patient_sex[0];
  strncpy(mh->operator_name,fi->operator_name,32);
  strncpy(mh->study_description,fi->study_descr,32);
  switch (fi->acquisition_type ) {
    case MDC_ACQUISITION_STATIC :
        mh->acquisition_type = MDC_ECAT6_ACQTYPE_STATIC_EMISSION;  break;
    case MDC_ACQUISITION_TOMO   :
        mh->acquisition_type = MDC_ECAT6_ACQTYPE_STATIC_EMISSION;  break;
    case MDC_ACQUISITION_DYNAMIC:
        mh->acquisition_type = MDC_ECAT6_ACQTYPE_DYNAMIC_EMISSION; break;
    case MDC_ACQUISITION_GSPECT :
        mh->acquisition_type = MDC_ECAT6_ACQTYPE_GATED_EMISSION;   break;
    default                     :
        mh->acquisition_type = MDC_ECAT6_ACQTYPE_UNKNOWN;

  }
  strncpy(mh->facility_name,fi->institution,20);
  strncpy(mh->user_process_code,MDC_PRGR,10);

  mh->num_planes = mh->num_frames = mh->num_gates = 1;
  mh->num_bed_pos = 1;

  for ( i=3; i<=fi->dim[0]; i++) {
     switch (i) {
       case 3: mh->num_planes  = fi->dim[i];     break;
       case 4: mh->num_frames  = fi->dim[i];     break;
       case 5: mh->num_gates   = fi->dim[i];     break;
       case 6: mh->num_bed_pos = fi->dim[i];     break;
       case 7: mh->num_bed_pos*= fi->dim[i];     break;
     }
  }
  mh->num_bed_pos -= 1; /* zero-based */

  /* bed positions */
  if ((fi->bednr > 0) && (fi->beddata != NULL)) {
    /* use preserved bed offsets */

    mh->init_bed_position = fi->beddata[0].hoffset / 10.; /* cm */
    mh->bed_elevation     = fi->beddata[0].voffset / 10.; /* cm */

    for (i=1; i<fi->bednr; i++) {
       if ( i==16 ) {
         MdcPrntWarn("ECAT6 Unsupported number of bed positions");
         break;
       }
       mh->bed_offset[i-1]  = fi->beddata[i].hoffset - fi->beddata[0].hoffset;
       mh->bed_offset[i-1] /= 10.; /* cm */
    }
  }else{
    /* guess bad offsets, assume adjacent bed positions */

    switch (MdcGetIntSliceOrient(fi->pat_slice_orient)) {
      case MDC_TRANSAXIAL:
          init_bed_position = dd0->image_pos_pat[2];  /* x */
          break;
      case MDC_CORONAL   :
          init_bed_position = dd0->image_pos_pat[1];  /* y */
          break;
      case MDC_SAGITTAL  :
          init_bed_position = dd0->image_pos_pat[0];  /* z */
          break;
    }
    if (init_bed_position < 0.)
      init_bed_position *= -1.;
    if (init_bed_position > dd0->slice_width)
      init_bed_position -= dd0->slice_width;

    mh->init_bed_position = init_bed_position / 10.; /* cm */

    for (i=1; i<fi->dim[6]; i++) {
       mh->bed_offset[i-1] = dd0->slice_width * (float)(fi->dim[3] * i / 10);
    }
  }
}

void MdcFillImageSubHeader(FILEINFO *fi,Mdc_Image_subheader *ish
                            ,int type,Int32 img, Int32 matnum, Uint32 NEWSIZE)
{
  IMG_DATA *id = &fi->image[img];
  Uint32 fnr;
  Int32  fstart=0, fduration=0;

  /* memset(ish,0,ISH_64_SIZE); */
  memset(ish,0,sizeof(Mdc_Image_subheader));

  fnr = id->frame_number;
  if ((fi->dynnr > 0) && (fnr > 0)) {
    fstart    = (Int32)fi->dyndata[fnr-1].time_frame_start;
    fduration = (Int32)fi->dyndata[fnr-1].time_frame_duration;
  }else{
    fstart = 0; fduration = 0;
  }

  ish->data_type = 2;           /* ECAT 6 only reads VAX Int16 */

  ish->num_dimensions = 2;
  if (fi->diff_size || NEWSIZE) {
    ish->dimension_1 = fi->mwidth;
    ish->dimension_2 = fi->mheight;
  }else{
    ish->dimension_1 = id->width;
    ish->dimension_2 = id->height;
  }
  ish->recon_scale = id->recon_scale;

  if (ish->data_type == 1 || ish->data_type == 2) {
    if (id->rescaled) {
      ish->image_min = (Int16) id->rescaled_min;
      ish->image_max = (Int16) id->rescaled_max;
    }else{
      ish->image_min = (Int16) id->min;
      ish->image_max = (Int16) id->max;
    }
  }else{ /* data types too big for an Int16 */
    ish->image_min   = 0;
    ish->image_max   = 0;
  }
  ish->pixel_size  = ((id->pixel_xsize + id->pixel_ysize)/2.) / 10.;
  ish->slice_width = id->slice_width / 10.;
#ifdef MDC_USE_SLICE_SPACING
  if (fi->number > 1) ish->slice_width = id->slice_spacing / 10.;
#endif
  ish->frame_duration   = fduration;
  ish->frame_start_time = fstart;
  ish->slice_location   = (Int16)MdcGetSliceLocation(fi,img);
  ish->filter_code      = -(MdcGetFilterCode(fi->filter_type));

  ish->scan_matrix_num  = matnum;
  ish->norm_matrix_num  = matnum;
  ish->atten_cor_matrix_num = matnum;

  ish->quant_units      = id->quant_units;

  if (id->rescaled) {
    ish->quant_scale = id->rescaled_fctr;
    ish->ecat_calibration_fctr = 1.;
  }else{
    ish->quant_scale = id->quant_scale;
    ish->ecat_calibration_fctr = id->calibr_fctr;
  }

  if (strcmp(fi->recon_method,"Filtered Backprojection") == 0 ) {
    strncpy(ish->annotation,MDC_ECAT6_RECON_METHOD,40);
  }else{
    strncpy(ish->annotation,fi->recon_method,40);
  }
}

static void MdcResetSizes(FILEINFO *fi)
{
  fi->mwidth = saved_mwidth;
  fi->mheight= saved_mheight;
}

const char *MdcWriteECAT6(FILEINFO *fi)
{
  IMG_DATA *id;
  Mdc_Main_header mh;
  Mdc_Image_subheader ish;
  Uint8 *buf, *maxbuf;
  Uint16 type, FREE;
  Int32 matnum, data=0, bed, gate, frame, plane, img=0;
  Uint32 size, NEWSIZE=0;

  if (MDC_FILE_STDOUT == MDC_YES)
    return("ECAT6 Writing to stdout unsupported for this format");

  MDC_WRITE_ENDIAN = MDC_LITTLE_ENDIAN; /* always (VAX) */

  if (XMDC_GUI == MDC_NO) {
    MdcDefaultName(fi,MDC_FRMT_ECAT6,fi->ofname,fi->ifname);
  }

  if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Writing ECAT6:");

  if (MDC_VERBOSE) MdcPrntMesg("ECAT6 Writing <%s> ...",fi->ofname);

  /* check for colored files */
  if (fi->map == MDC_MAP_PRESENT)
    return("ECAT6 Colored files unsupported");

  if (MdcKeepFile(fi->ofname)) {
    return("ECAT6 File exists!!");
  }

  if (MDC_FORCE_INT != MDC_NO) {
    if (MDC_FORCE_INT != BIT16_S) {
      MdcPrntWarn("ECAT6 Only Int16 pixels supported");
    }
  }

  /* check some integrities */

  /* check integrity of planes, frames, gates, beds */
  if (fi->dim[3] > MDC_ECAT6_MAX_PLANES)
    return("ECAT6 number of planes too big (1024)");
  if (fi->dim[4] > MDC_ECAT6_MAX_FRAMES)
    return("ECAT6 number of frames too big (512)");
  if (fi->dim[5] > MDC_ECAT6_MAX_GATES)
    return("ECAT6 number of gates  too big (64)");
  if ((fi->dim[6]*fi->dim[7]) > MDC_ECAT6_MAX_BEDS)
    return("ECAT6 number of beds   too big (16)");


#if MDC_ECAT6_RESTRICT_DIMS
  /* check dimensions (ECAT only 64, 128, 256) */
  /* we don't do downsaling                    */
  if (fi->mwidth > MDC_ECAT6_MAX_DIMS || fi->mheight > MDC_ECAT6_MAX_DIMS)
    return("ECAT6 dimensions too big (256)");
#endif

  /* get maximum dimension */
  if (fi->mwidth > fi->mheight) size = fi->mwidth;
  else size = fi->mheight;

#if MDC_ECAT6_RESTRICT_DIMS
  /* allow only 64, 128, 256 */
  if      (size <=  64) NEWSIZE=64;
  else if (size <= 128) NEWSIZE=128;
  else if (size <= 256) NEWSIZE=256;
#endif

  /* save the original dimensions anyway */
  saved_mwidth = fi->mwidth;
  saved_mheight= fi->mheight;

  /* change to new dimensions */
  if (NEWSIZE) {
    fi->mwidth = NEWSIZE;
    fi->mheight= NEWSIZE;
  }

  MdcFillMainHeader(fi,&mh);

  if ( (fi->ofp = mdc_mat_create(fi->ofname,&mh)) == NULL) {
    MdcResetSizes(fi);
    return("Couldn't create file");
  }

  for (bed=0; bed <= mh.num_bed_pos; bed++)
  for (gate=1; gate <= mh.num_gates; gate++)
  for (frame=1; frame <= mh.num_frames; frame++)
  for (plane=1; plane <= mh.num_planes; plane++) {

     if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL);

     id = &fi->image[img];

     if ((id->type != BIT16_S) || MDC_QUANTIFY || MDC_CALIBRATE) {
       buf = MdcGetImgBIT16_S(fi, (Uint32)img);
       FREE=MDC_YES;  type=BIT16_S;
     }else{
       buf = id->buf;
       FREE=MDC_NO; type=id->type;
     }

     matnum = mdc_mat_numcod(frame,plane,gate,data,bed);
     MdcFillImageSubHeader(fi,&ish,type,img,matnum,NEWSIZE);

     if (fi->diff_size || NEWSIZE) {
       size = fi->mwidth * fi->mheight * MdcType2Bytes(type);

       maxbuf = MdcGetResizedImage(fi, buf, type, (Uint32)img);
       if (maxbuf == NULL) {
         MdcResetSizes(fi);
         return("ECAT6 Bad malloc maxbuf");
       }

       if (FREE) MdcFree(buf);

       FREE=MDC_YES;

     }else{ /* NEWSIZE is normally always set */
        size = id->width * id->height * MdcType2Bytes(type);
        maxbuf = buf;
     }

     matnum = mdc_mat_numcod(frame,plane,gate,data,bed);
     if (mdc_mat_write_image(fi->ofp,matnum,&ish,(Uint16 *)maxbuf,(Int32)size))
     {
       MdcResetSizes(fi);
       return("ECAT6 Bad write image matrix");
     }

     img+=1;

     if (FREE) MdcFree(maxbuf);
  }

  MdcCheckQuantitation(fi);

  MdcCloseFile(fi->ofp);

  MdcResetSizes(fi);

  return NULL;
}

void MdcPrintEcatInfoDB(Mdc_Main_header *mh)
{
  char Unknown[8]="Unknown";

  Uint32 i, patient_strlen, study_strlen;

  patient_strlen = strnlen(mh->patient_name,32);
  study_strlen   = strnlen(mh->study_name,12);

  /* remove # from strings, because it is used as field separator! */
  for (i=0; i<patient_strlen; i++) {
     if ( mh->patient_name[i] == '#' ) {
       mh->patient_name[i]='$';
     }
  }

  /* print database info: study_name */
  if (study_strlen != 6) {
    MdcPrntScrn("%s",Unknown);
  }else{
    MdcPrntScrn("%.12s",mh->study_name);
  }
  MdcPrntScrn("# ");

  /* print database info: patient_name */
  if (patient_strlen == 0) {
    MdcPrntScrn("%-35s",Unknown);
  }else{
    MdcPrntScrn("%-35.32s",mh->patient_name);
  }

  MdcPrntScrn("#");

  /* print database info: scan date */
  MdcPrntScrn("%02d-",mh->scan_start_day);
  switch (mh->scan_start_month) {
    case  1: MdcPrntScrn("Jan"); break;
    case  2: MdcPrntScrn("Feb"); break;
    case  3: MdcPrntScrn("Mar"); break;
    case  4: MdcPrntScrn("Apr"); break;
    case  5: MdcPrntScrn("May"); break;
    case  6: MdcPrntScrn("Jun"); break;
    case  7: MdcPrntScrn("Jul"); break;
    case  8: MdcPrntScrn("Aug"); break;
    case  9: MdcPrntScrn("Sep"); break;
    case 10: MdcPrntScrn("Oct"); break;
    case 11: MdcPrntScrn("Nov"); break;
    case 12: MdcPrntScrn("Dec"); break;
  }
  MdcPrntScrn("-%4d",mh->scan_start_year);
  MdcPrntScrn("\n");
}

