#!/bin/sh

#             $HOME/.lcmodel/bruker/bin2raw      (26 May 2020)
#             =============================

#     Makes an LCModel RAW file from a Bruker fid file (also using the 
# corresponding "acqp" or "acqu" and "method" files).  Also makes the files 
# cpStart and extraInfo for LCMgui.

#     Note that the data are not scaled; TRAMP is set to 1.0.  Therefore,
# calibration phantoms will not produce estimates of absolute concentrations.
# For Water-Scaling, read the section "Water-Scaling", including its 
# subsection "Bruker Data", in the latest LCModel manual.

#             Specific Instructions for this File
#             ===================================

#     The following requirements must be met:
#     --------------------------------------

#     (1) You have selected a file with the name "fid" that contains your 
# time-domain data.  The files "acqp" (or "acqu") and "method" must be in the 
# same directory.
#-------------------

#     (2) The following cannot be used with CSI data:  In the unlikely case 
# that you have already corrected for the group shift of Bruker's digital 
# filter, change SHIFT_DONE=0 to  SHIFT_DONE=1 (one) in the following line:

SHIFT_DONE=0

# (with no space on either side of the "=").

#-------------------

#     (3) Normally, the filename should always be "fid".  You can require 
# this by changing REQUIRE_FILENAME_FID=0 to REQUIRE_FILENAME_FID=1 (one) in 
# the following line:

REQUIRE_FILENAME_FID=0

#--------------------------------------------------------------------------

#     Also, for old ParaVision versions with no file "method":
#     --------------------------------------------------------

#     (4) Only if the file "method" is not present: If you know the number of 
# points in "fid" that must be skipped (the group delay of the Bruker 
# digital filter), set it in the following line (with no space on either side 
# of the "="):

NSKIP=0
#-------------------

#     (5)  With ParaVision 2 and the Bruker macro VSEL_Scan_Tool, "fid" 
# should already be in analog mode, and NSKIP is unnecessary. 
# [Do Item (2) above.]  However, it may be necessary to use the file "imnd" 
# (rather than the file "acqp") for TE TR and the number of averages.  In 
# this case, you must change USE_IMND=0 to USE_IMND=1 (one) in the following 
# line:

USE_IMND=0

# With USE_IMND=1, changes suggested by Michael Czisch are used to parse the
# file "imnd", which must be in the same directory as "fid".
#-------------------

#     (6) Only for (the very old, pre-Avance) sequential acquisition mode, 
# where the imaginary part of a complex data pair is acquired one sample time 
# after the real part, should you change SEQACQ=F below to SEQACQ=T.  

SEQACQ=F
#-------------------

#     Normally, you should not have to read further.  However, more 
# information is given in the General Instructions at the end of this script.

#============================================================================

#                   Start of This Script
#                   ====================
#
# Function for checking if a variable is non-numeric (adapted from B. Blinn)
#
IsNonNumeric() {
    expr "$1" + 1  >/dev/null  2>&1
    if [ $?  -ge  2 ]
    then
	return 0
    fi
    return 1
}

# Initial definitions.
# Note that $LCM_DIR has a "/" at the end and $BIN2RAW_DIR does not.
#
DATA_FILE=$1
LCM_DIR=$2
MET_H2O=$3
BIN2RAW_DIR=$2$3

CP_START=$BIN2RAW_DIR/cpStart
EXTRA_INFO=$BIN2RAW_DIR/extraInfo
ERROR=$BIN2RAW_DIR/error
RAW=$BIN2RAW_DIR/RAW
SDDEGP=20.0
NUNFIL_FACTOR=1
#-----------------------------------------------------------------------------

#      Check for the names & existence of the files fid & acqp (or acqu), 
# which must be in the same directory.
#
if [ $REQUIRE_FILENAME_FID -eq 1 ]
then
    BASENAME=`basename $1`
    if [ "$BASENAME"  !=  fid ]
    then
        echo "  The following file, which you selected, must have the name fid:

$1"  >  $ERROR
        exit 0
    fi
fi

DIRNAME=`dirname $1`
IMND=$DIRNAME/imnd
METHOD=$DIRNAME/method

ACQP=$DIRNAME/acqp
if [ -r  "$ACQP" ]
then
    if (fgrep 'ParaVision 360' $ACQP >/dev/null) 
    then
	exec  3<&0 <$ACQP
	while read A1
	do
	    if  echo "$A1" | fgrep '##$ACQ_RxFilterInfo=' >/dev/null
	    then
		read LINE
		NL1=`echo "$LINE" | cut -d'(' -f2 `
		NSKIP=`echo "$NL1" | cut -d'.' -f1`
		break
	    fi
	done
	exec 0<&3 3<&-
    fi
fi
    
if [ ! -r  "$ACQP" ]
then
    ACQP=$DIRNAME/acqu
fi
if [ -r  "$ACQP" ]
then
    if (fgrep 'ParaVision 360' $ACQP >/dev/null) 
    then
	NUNFIL_FACTOR=4
    fi
else
    echo "     There is no readable file acqp or acqu in the following directory:

$DIRNAME

The file, acqp or acqu, corresponding to your selected fid file must be in 
this same directory.
Remove all spaces from file names and directory (folder) names."  >  $ERROR
    exit 0
fi

if [ "$USE_IMND" = "1"   -a   ! -r "$IMND" ]
then
    echo "     The following imnd file does not exist or is not readable:

$IMND

The file, imnd, corresponding to your selected fid file must be in the 
same directory."  >  $ERROR
    exit 0
fi
#-----------------------------------------------------------------------------

# Define temporary files to hold files cleaned of (non-Unix) return characters.
#
ACQP_CLEAN=$BIN2RAW_DIR/acqClean
IMND_CLEAN=$BIN2RAW_DIR/imndClean
METHOD_CLEAN=$BIN2RAW_DIR/methodClean

# Delete possible old files, e.g., after "Reload" Button. 
#
rm  -f  $CP_START
rm  -f  $EXTRA_INFO
rm  -f  $ERROR
rm  -f  $RAW
rm  -f  $ACQP_CLEAN
rm  -f  $IMND_CLEAN
rm  -f  $METHOD_CLEAN
#-----------------------------------------------------------------------------

# Make files cleaned of carriage returns
#
tr  -d '\015'  <$ACQP  >$ACQP_CLEAN
ACQP=$ACQP_CLEAN

if [ -r  "$IMND" ]
then
    tr  -d '\015'  <$IMND  >$IMND_CLEAN
    IMND=$IMND_CLEAN
fi
if [ -r  "$METHOD" ]
then
    tr  -d '\015'  <$METHOD  >$METHOD_CLEAN
    METHOD=$METHOD_CLEAN
fi
#-----------------------------------------------------------------------------

# Extract info for cpStart from ACQP file.

# First make TITLE
#

# Time & date
#
TIME_DATE=`awk '/##.ACQ_time=/ { getline
                             print $0 }' $ACQP`
TITLE="$TIME_DATE"

#------------
# PRESS/STEAM
#
SEQ_NAME=`awk '/##.ACQ_method=/ {getline
                                 print $0}' $ACQP`
TITLE="$TITLE  $SEQ_NAME"
SEQ_LC=`echo "$SEQ_NAME" | tr '[A-Z]' '[a-z]'`

case  "$SEQ_LC"  in
    *vsel_ste* | "<steam>" )  SEQ=STEAM    ;;
    *vsel_se* | "<press>" )  SEQ=PRESS    ;;
          * )  SEQ=UNKNOWN  ;;
esac

#------------------------------------------------
# TE/TR/NS
# "awk ... printf ("%.0f"... " rounds and replaces non-numeric values with 0
#

if [ "$USE_IMND" = "1" ]
then
   TE=`awk -F= '/##.IMND_echo_time=/ { printf ("%.0f", $2) }' $IMND`
   TR=`awk -F= '/##.IMND_rep_time=/ { printf ("%.0f", $2*1000) }' $IMND`
   NS=`awk -F=  '/##.IMND_n_averages=/ { printf ("%d", $2) }' $IMND`
else
   # Set NS = ACQ_ns * NA
   #
    NS=`awk -F=  '/##.ACQ_ns=/ { ns = $2 }; \
                  /##.NA=/ { ns *= $2
                            print ns }' $ACQP`
    if [ -r "$METHOD" ]
    then
        TE=`awk -F= '/##.PVM_EchoTime=/ { printf ("%.0f", $2) }' $METHOD`
        TR=`awk -F= '/##.PVM_RepetitionTime=/ { printf ("%.0f", $2) }' $METHOD`
    else
        TE=`awk '/##.ACQ_echo_time=/ { getline
                                       printf ("%.0f", $1) }' $ACQP`
        TR=`awk '/##.ACQ_repetition_time=/ { getline
                                             printf ("%.0f", $1) }' $ACQP`
    fi
fi
#
if IsNonNumeric $TE
then
    TE="NaN"
elif [ $TE -le 0 ]
then
    TE="NaN"
fi
#
if IsNonNumeric $TR
then
    TR="NaN"
elif [ $TR -le 0 ]
then
    TR="NaN"
fi
#
if IsNonNumeric $NS
then
    NS="NaN"
elif [ $NS -le 0 ]
then
    NS="NaN"
fi
TITLE="$TITLE  TE/TR/NS=$TE/$TR/$NS"

#--------------------------------------------------------------------------
# Voxel VOLUME
#
if [ -r $METHOD ]
then
    # Probably will have to modified for future extension to the multivoxel 
    #   case.
    # Some system language settings output European formatting of numbers, 
    #   with commas instead of decimal points.  The "tr" command below and 
    #   further down is to correct for this.
    #
    VOLUME=`awk '/##.PVM_VoxArrSize=/ { getline
                                        printf ("%.3e", $1 * $2 * $3) }' \
                                           $METHOD | tr ',' '.'`
    VOL_INT=`awk '/##.PVM_VoxArrSize=/ { getline
                                printf ("%.0f", 1.e6 * $1 * $2 * $3) }' \
                                           $METHOD`
else
    VOLUME=`awk '/##.ACQ_fov=/ { getline
                                 printf ("%.3e", $1 * $2 * $3) }' \
                                    $ACQP | tr ',' '.'`
    VOL_INT=`awk '/##.ACQ_fov=/ { getline
                                  printf ("%.0f", 1.e6 * $1 * $2 * $3) }' \
                                     $ACQP`
fi
#
if IsNonNumeric $VOL_INT
then
    VOLUME="NaN"
elif [ $VOL_INT -le 0 ]
then
    VOLUME="NaN"
fi
#
TITLE="$TITLE  VOI=$VOLUME"
#--------------------------------------------------------------------------

# If necessary only put end of fid pathname in TITLE, so that TITLE does not 
#   have more than LENGTH_MAX characters.
# You can set LENGTH_MAX below, but it must not exceed 240.
#
LENGTH_MAX=240
LENGTH_TITLE=`expr "$TITLE" : '.*'`
LENGTH_FILENAME=`expr "$1" : '.*'`
FREE_SPACE=`expr $LENGTH_MAX - $LENGTH_FILENAME - $LENGTH_TITLE`

# Allow for 2 spaces between filename & rest of TITLE.
#
INDEX_START=`expr 2 - $FREE_SPACE`
if [ $INDEX_START -le 0 ]
then
    TITLE="$1  $TITLE"
else
    # Allow 3 spaces for ellipsis at start of filename.
    #
    INDEX_START=`expr $INDEX_START + 3`
    if [ $INDEX_START -lt $LENGTH_FILENAME ]
    then
        FILENAME_OUT=`echo "$1" | cut -c $INDEX_START-$LENGTH_FILENAME`
        TITLE="...$FILENAME_OUT  $TITLE"
    fi
fi
TITLE="title= '$TITLE'"
#----------------------------------------------------------------------------

# Get the rest of the entries
#
FILRAW="filraw= '$RAW'"
FILPS="filps= '${LCM_DIR}ps'"
HZPPPM=`awk -F=  '/##.SFO1=/ { printf ( "%.4f", $2 ) }' $ACQP | tr ',' '.'`
DELTAT=`awk -F=  '/##.SW_h=/ { printf ( "%.4e", 1 / $2 ) }' $ACQP | tr ',' '.'`

#      Get dimensions for a mutli-voxel data file.  If integer values are not 
# found, then do not output NUNFIL and set others to 1.  NUNFIL is the number 
# of complex pairs and must be halved.
#
exec  3<&0 <$ACQP
while read A1
do
    if  echo "$A1" | grep "##.ACQ_size" >/dev/null
    then
	# This next line has the dimensions we seek.
	read NUNFIL NDCOLS NDROWS NDSLIC
	break
    fi
done
exec 0<&3 3<&-
if  IsNonNumeric $NUNFIL 
then
#      Check for very old version of TopSpin acqu
#
    NUNFIL=`awk -F=  '/##.TD=/ { printf ( "%d", $2 ) }' $ACQP`
fi
if  IsNonNumeric $NUNFIL
then
    echo "     Cannot get NUNFIL: check your file acqp or acqu." > $ERROR
    exit 0
else
    NUNFIL=`expr $NUNFIL \* $NUNFIL_FACTOR / 2`
    echo "nunfil=  $NUNFIL"  >  $CP_START
fi
if  IsNonNumeric $NDCOLS 
then
    NDCOLS=1
fi
if  IsNonNumeric $NDROWS 
then
    NDROWS=1
fi
if  IsNonNumeric $NDSLIC 
then
    NDSLIC=1
fi
#------------------------------------------------------------------------------

# Finish cpStart file.
#
echo "$TITLE
$FILRAW
$FILPS
hzpppm= $HZPPPM
deltat= $DELTAT
ndcols= $NDCOLS
ndrows= $NDROWS
ndslic= $NDSLIC
sddegp= $SDDEGP"  >>  $CP_START
#-----------------------------------------------------------------------------

# Make extraInfo file.

BUFFER="hzpppm  $HZPPPM"

if [ $NDCOLS -eq 1   -a   $NDROWS -eq 1   -a   $NDSLIC -eq 1 ]
then
    BUFFER="$BUFFER
singleVoxel  1"
fi

if [ "$TE"  !=  NaN ]
then
    BUFFER="$BUFFER
echot  $TE.0"
fi

if [ "$SEQ"  !=  UNKNOWN ]
then
    BUFFER="$BUFFER
seq  $SEQ"
fi

echo  "$BUFFER"  >  $EXTRA_INFO
#-----------------------------------------------------------------------------

# Produce $RAW file

#-------------------------------------------------
# First write Namelists to BUFFER and then to $RAW.

BUFFER=" \$SEQPAR
 hzpppm=$HZPPPM"

if [ "$TE"  !=  NaN ]
then
    BUFFER="$BUFFER
 echot=$TE.0"
fi

if [ "$SEQ"  !=  UNKNOWN ]
then
    BUFFER="$BUFFER
 seq='$SEQ'"
fi

if [ "$VOLUME"  =  NaN ]
then
    VOLUME=1.0
fi

echo "$BUFFER
 \$END
 \$NMID  
 bruker=T
 seqacq=$SEQACQ
 fmtdat='(2e14.5)'
 tramp=1.0
 volume=$VOLUME
 \$END"  >  $RAW
#-----------------------------------------------------------------------------

# Get NSKIP from $METHOD

DEAD_TIME="0.0"

if [ $NSKIP -eq 0   -a   $SHIFT_DONE -eq 0   -a   -r "$METHOD" ]
then

# Check for special case of Slice_Fid mode

    MODE=`awk -F= '/##.LocalizationMode=/ { printf ("%s", $2) }' $METHOD`
    if [ "$MODE" = Slice_FID ]
    then
	DEAD_TIME=`awk -F= '/##.PVM_EchoTime=/ { printf ("%.5f", $2) }' $METHOD`
	if [ "$DEAD_TIME" = "" ]
	then
	    DEAD_TIME="0.0"
	fi
    fi

    NSKIP=`awk -F= '/##.PVM_DigShift=/ { printf ("%d", $2) }' $METHOD`
    if  IsNonNumeric  $NSKIP
    then
	NSKIP=0
    fi
fi

#---------------------------------------------------------
# Append binary fid data converted into ASCII with bin2asc.
#
    $HOME/.lcmodel/bruker/bin2asc  $DATA_FILE  $RAW  $NSKIP  $NUNFIL  $NDCOLS \
                                   $NDROWS  $NDSLIC  $DEAD_TIME  $DELTAT \
                                   $SHIFT_DONE

#----------------------------------------------------------------------------
#
# You must always exit this script with "exit 0"
#
exit 0

#=============================================================================
#=============================================================================
#=============================================================================

#            General Instructions for writing your own my-bin2raw
#            ====================================================

#     If you want to modify this file, use this file as a template only; copy 
# it to my-bin2raw (in the same directory as bin2raw), and then edit 
# my-bin2raw.
#     LCMgui always first attempts to execute my-bin2raw.  If LCMgui cannot 
# find my-bin2raw, then it executes bin2raw.
#     bin2raw will be overwritten by LCMgui updates, but your my-bin2raw will 
# not be overwritten.

#      LCMgui executes this script and supplies 3 arguments to it, i.e., it 
# executes
#   $HOME/.lcmodel/other/bin2raw  $1  $2  $3
#----------------------------------------------------------------------------

#      The 3 arguments supplied by LCMgui are:
#
# $1 = the absolute pathname of the data file that you selected to be 
#      analyzed.
#
# $2 = the absolute pathname (with a "/" at the end) of the temporary 
#      directory where you must put the output from this script.
#
# $3 = met if the data file contains your water-suppressed data (or if the 
#          data file contains both your water-suppressed data and your 
#          unsuppressed water reference data for eddy-current correction).
#    = h2o if the data file contains only the unsuppressed water reference 
#          data.

# (C programs can access $3 as argv[3], etc.)
#----------------------------------------------------------------------------

#     This script must output the following files:

# $2$3/RAW = LCModel RAW file.

# $2h2o/RAW = LCModel RAW file of the unsuppressed water reference data for
#             eddy-current correction. 
#                  This is only possible if your data file contains both the
#             unsuppressed and suppressed data.  In this case, $3 = "met", and 
#             you output both $2$3/RAW and $2h2o/RAW.
#                  If the unsuppressed and suppressed are in different files,
#             then you only output $2$3/RAW, first when $3 = "met", and later 
#             LCMgui calls other/bin2raw again with another $1 and with 
#             $3 = "h2o"

# $2$3/cpStart : sets any default Control Parameters (which you can later 
#                  modify in LCMgui) that can be obtained from (the headers 
#                  of) your data file.
#                The format illustrated below must be strictly followed; i.e.:
#                  control_parameter= value
#                e.g.,
#                  hzpppm= $HZPPPM
#                with NO space between the control parameter and the "=" and 
#                with space between the "=" and the value.

# $2$3/extraInfo : As illustrated below, this file can contain "echot" and 
#                    "seq", with which LCMgui will attempt to automatically 
#                    select the correct default BASIS file of model spectra 
#                    with these sequence parameters.
#                  With the three "voiPosition*", LCMgui will produce an error 
#                    window if these three in the unsuppressed reference and 
#                    water-suppressed files do not agree (indicating spectra 
#                    from different voxels).
#                  With the "headerInfo*, the default path of the directory 
#                    that archives the LCModel results is determined.
#                  The format is illustrated below.  It is the same as for 
#                    cpStart, except that there is no "=".  There still must 
#                    be space between the parameter and the value.

# $2$3/error : Error messages on aborts should go here.  They will then be 
#                displayed by LCMgui, PROVIDED that you then exit with 
#                "exit 0"; i.e., a "successful" exit.  It is essential that 
#                ALL exits (successful and error exits) be with "exit 0".
#              From a C program, you must also always exit with "exit(0)".

#============================================================================
