
c
c
c     
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose:
c     This subroutine reads general gait and marker position information
c     from a number of different file formats.  It decides which file type
c     is going to be read and calls the appropriate file reading subroutine.
c     The decision as to which file type is read is based upon the file name
c     extension after the . in the file name. This can be overridden with the
c     command line options. Actually, this routine does not read the files at all,
c     it decides the appropriate options and file types and calls the needed
c     file reading subroutine.
c
c     The following file formats are supported
c     
c    TR3 - Old vicon marker trajectory files
c    TRU - Old file type used at OSU to hold analysis results
c    C3D - AMASS combined marker/analog data files
c    ANZ - ANZ binary file format
c    FPD - Old vicon file format for force plate data
c    GLS/GL1/GL2 - Special file format for force plate/EMG data used at OSU only
c    TXT - Used for very special situations to import text data- such build normal angle
c          and kinetics files for gait using averaged data.
c    P3D - Motion Analysis Corporation marker trajectory files
c    ANA - Motion Analysis Corporation analog data files from ADS program
c
c
c    There are bunches of options, many of which apply only to a single file type. See the
c    documentation for listings of these or look at the file reading routines.
c
c    Lastly, there is code here that is commented out which allows ANZ to more easily
c    open files when it is compiled with Language Systems Fortran on the Macintosh. THis
c    was for my own personal experimentation, but I left it in here in case someone
c    might want to experiment. Look the comments in the anz_file.for file before you
c    start following around with this, however...
c
c    Routines called:
c      GetFileName
c      InitFullBodyAnalysis
c      InitTimeDistAnalysis
c      InitUpperExtremAnalysis
c      ReadTextFile
c      read_ads_file
c      read_anz_file
c      read_c3d_file
c      read_fpd_file
c      read_gls_file
c      read_gls2_file
c      read_p3d_file
c      read_tr3_file
c      read_tru_file
c      zero_force_flags
c      zero_motion_flags
c
      subroutine read_file( CMND,MF,SF,FF,JF,AF,
     &                      CF,EF,OF,data_read     )
      implicit none
      include 'a_const.inc'     
 
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_names.inc'
      include 'a_mrk.inc'
      include 'a_gls2.inc'
      include 'a_frc.inc'

      record /CmndLine/       CMND
      record /marker_flag/    MF
      record /segment_flag/   SF
      record /force_flag/     FF
      record /joint_flag/     JF
      record /avg_pos_flag/   AF
      record /cycle_flag/     CF
      record /emg_flag/       EF
      record /option_flag/    OF
      
      logical*4 data_read
                    
      real*4    RevRM(3,3),ZeroK(8),AutoZeroKPct
      integer*4 num_file_types,file_type_num,i,name_length,j,DownRezIntvl,
     &          AnalysisType,BeginFrm,SkipFrmKistler,NumPlt,plt
      character extension*3,file_type(12)*3,filename*60,LineText*80,
     &          PltType(4)*3
      logical*4 nameOK,GetFileName,newCS,OldGain,ZeroChnl,CalibrateData,
     &          DownRez,OptionUsed,UseHead,InitCycle,SwapBytes,NoFLags,
     &          SetMrkName,Skip1,Oops,Bipolar,
     &          Diagnose,MakeMtn60Hz,IgnoreExMrk,CKCNoMedAkl,EMGOnly,
     &          RevDir,P3dCm,ManZeroKistler,AutoZeroKistler,OldMayoLCS,
     &          AutoZeroBertec,ZeroSegParam,OSUFile

      integer*4 TR3_FILE,TRU_FILE,C3D_FILE,ANZ_FILE,FPD_FILE,M_FILE,GLS_FILE,
     &          GLS1_FILE,GLS2_FILE,TEXT_FILE,P3D_FILE,ANA_FILE,NO_FILE
                     
      data TR3_FILE,TRU_FILE,C3D_FILE,ANZ_FILE,FPD_FILE,M_FILE,GLS_FILE,
     &     GLS1_FILE,GLS2_FILE,TEXT_FILE,P3D_FILE,ANA_FILE,NO_FILE
     &     / 1,2,3,4,5,6,7,8,9,10,11,12,0 /

      data num_file_types / 12 /
      data file_type / 'TR3','TRU','C3D','ANZ','FPD','M  ',        
     &                 'GLS','GL1','GL2','TXT','P3D','ANA' /
      data RevRM / -1.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,1.0 /
                          
      if ( CMND.NumCmnd .lt. 2 ) goto 1000 !error- there must a filename present
                                           ! as the entry after READ
c
c     First check for proper options if any are present
c     check for file type option.
c     NOTE: If options are not present the default is ANZ file, full body marker
c     set, and motion data.                                         
c
      file_type_num= NO_FILE
      newCS=    .false.
      OldGain=  .false.         
      ZeroChnl= .true.
      CalibrateData= .true.
      DownRez=  .false.                      
      DownRezIntvl= 1
      AnalysisType= GP.analysis_type
      UseHead= .true.                           
      SwapBytes= .false.
      Skip1=     .false.
      InitCycle= .false.
      NoFlags=   .false.
      SetMrkName= .true.
      Oops= .false.
      Bipolar= .true.
      BeginFrm= 1                                          
      RevDir=      .false.
      AutoZeroBertec= .false.          ! specific to Mayo ANA files
      ManZeroKistler= .false.           ! specifc to Mayo ANA files
      AutoZeroKistler= .false.          ! specific to Mayo ANA files
      AutoZeroKPct= 5.0                 ! specific to Kistler and ANA files
      SkipFrmKistler= 20                ! specific to Kistler and ANA files
      Diagnose=    .false.              ! specific to P3D and ANA files
      MakeMtn60Hz= .false.              ! specific to P3D files
      IgnoreExMrk= .true.               ! specific to P3D files
      CKCNoMedAkl= .false.              ! specific to CKC analyses
      EMGOnly=     .false.    
      P3dCm=       .true.
      OSUFile=     .false.
      NumPlt= 0
      OldMayoLCS=  .false.
      do i= 1, 4
        PltType(i)= 'NON'
      end do
      if ( CMND.NumOpt(1) .gt. 0 ) then                 
        do j= 1, CMND.NumOpt(1)
          OptionUsed= .false.  
          do i= 1, num_file_types                         
            if ( CMND.OptText(j,1) .eq. file_type(i) ) then
              file_type_num= i
              OptionUsed= .true.
            end if
          end do ! i                          
          if ( file_type_num .eq. GLS1_FILE ) file_type_num= M_FILE
          if ( file_type_num .eq. GLS2_FILE ) file_type_num= GLS_FILE
          if ( .not. OptionUsed ) then      
            if ( CMND.OptText(j,1) .eq. 'NCZ' ) then
              newCS=    .true.
            else if ( CMND.OptText(j,1) .eq. 'OGN' ) then
              OldGain=  .true.
            else if ( CMND.OptText(j,1) .eq. 'NOZ' ) then
              ZeroChnl= .false.
            else if ( CMND.OptText(j,1) .eq. 'NOC' ) then
              CalibrateData= .false.      
            else if ( CMND.OptText(j,1) .eq. 'BEG' ) then
              if ( .not. CMND.OptNumOK(j,1) ) goto 1050
              if ( CMND.OptIntVal(j,1) .lt. 1 ) goto 1050
              BeginFrm= CMND.OptIntVal(j,1)
            else if ( CMND.OptText(j,1) .eq. 'DRZ' ) then
              if ( .not. CMND.OptNumOK(j,1) ) then
                DownRezIntvl= 10
              else
                if ( (CMND.OptIntVal(j,1) .lt. 1) .or.
     &               (CMND.OptIntVal(j,1) .gt. 100)    ) goto 1040 ! error
                DownRezIntvl= CMND.OptIntVal(j,1)
              end if
              DownRez= .true.                            
            else if ( CMND.OptText(j,1) .eq. 'OSU' ) then
              AnalysisType= FULL_ANL
              OSUFile= .true.
            else if ( CMND.OptText(j,1) .eq. 'UPP' ) then
              AnalysisType= UPR_ANL
            else if ( CMND.OptText(j,1) .eq. 'TDP' ) then
              AnalysisType= TDP_ANL
            else if ( CMND.OptText(j,1) .eq. 'CKC' ) then      
              AnalysisType= CKC_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'F3R' ) then      
              AnalysisType= F3DR_ANL
              MakeMtn60Hz=  .true.
              P3dCm=  .false.
            else if ( CMND.OptText(j,1) .eq. 'F3L' ) then      
              AnalysisType= F3DL_ANL
              MakeMtn60Hz=  .true.
              P3dCm=  .false.
            else if ( CMND.OptText(j,1) .eq. 'HH ' ) then
              AnalysisType= HH_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'CC ' ) then      
              AnalysisType= CC_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'CCR' ) then      
              AnalysisType= CCR_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'CCL' ) then      
              AnalysisType= CCL_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'MAY' ) then      
              AnalysisType= MAYO_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'MIR' ) then      
              AnalysisType= MIR_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'MIL' ) then      
              AnalysisType= MIL_ANL
              MakeMtn60Hz=  .true.
            else if ( CMND.OptText(j,1) .eq. 'NOH' ) then
              UseHead= .false.      
            else if ( CMND.OptText(j,1) .eq. 'BYT' ) then
              SwapBytes= .true.      
            else if ( CMND.OptText(j,1) .eq. 'NOF' ) then
              NoFlags= .true.      
            else if ( CMND.OptText(j,1) .eq. 'NMN' ) then
              SetMrkName= .false.
            else if ( CMND.OptText(j,1) .eq. 'SKI' ) then
              Skip1= .true.
            else if ( CMND.OptText(j,1) .eq. 'OOP' ) then
              Oops= .true.
            else if ( CMND.OptText(j,1) .eq. 'BIP' ) then
              Bipolar= .true.
            else if ( CMND.OptText(j,1) .eq. 'UNI' ) then
              Bipolar= .false.
            else if ( CMND.OptText(j,1) .eq. 'BER' ) then
              if ( (.not. CMND.OptNumOK(j,1)) .or. 
     &             (CMND.OptIntVal(j,1) .lt. 0) .or.
     &             (CMND.OptIntVal(j,1) .gt. MAX_PLT)  ) goto 3000 ! error
              plt= CMND.OptIntVal(j,1)
              if ( plt .gt. NumPlt ) NumPlt= plt
              PltType(plt)= 'BER'
            else if ( CMND.OptText(j,1) .eq. 'KOL' ) then
              if ( (.not. CMND.OptNumOK(j,1)) .or. 
     &             (CMND.OptIntVal(j,1) .lt. 0) .or.
     &             (CMND.OptIntVal(j,1) .gt. MAX_PLT)  ) goto 3000 ! error
              plt= CMND.OptIntVal(j,1)
              if ( plt .gt. NumPlt ) NumPlt= plt
              PltType(plt)= 'KOL'
            else if ( CMND.OptText(j,1) .eq. 'KNE' ) then
              if ( (.not. CMND.OptNumOK(j,1)) .or. 
     &             (CMND.OptIntVal(j,1) .lt. 0) .or.
     &             (CMND.OptIntVal(j,1) .gt. MAX_PLT)  ) goto 3000 ! error
              plt= CMND.OptIntVal(j,1)
              if ( plt .gt. NumPlt ) NumPlt= plt
              PltType(plt)= 'KNE'
            else if ( CMND.OptText(j,1) .eq. 'OFP' ) then
              OldMayoLCS=  .true.
            else if ( CMND.OptText(j,1) .eq. 'DIA' ) then
              Diagnose=  .true.
            else if ( CMND.OptText(j,1) .eq. 'MM6' ) then
              MakeMtn60Hz=  .true.                                
            else if ( CMND.OptText(j,1) .eq. 'RXM' ) then
              IgnoreExMrk=  .false.
            else if ( CMND.OptText(j,1) .eq. 'NMA' ) then
              CKCNoMedAkl=  .true.
            else if ( CMND.OptText(j,1) .eq. 'EMG' ) then
              EMGOnly=  .true.
            else if ( CMND.OptText(j,1) .eq. 'REV' ) then
              RevDir=  .true.
            else if ( CMND.OptText(j,1) .eq. 'MET' ) then
              P3dCm=  .false.
            else if ( CMND.OptText(j,1) .eq. 'CM ' ) then
              P3dCm=  .true.
            else if ( CMND.OptText(j,1) .eq. 'MZK' ) then
              ManZeroKistler=   .true.
              AutoZeroKistler=  .false.
            else if ( CMND.OptText(j,1) .eq. 'AZK' ) then
              AutoZeroKistler=  .true.   
              ManZeroKistler=   .false.
            else if ( CMND.OptText(j,1) .eq. 'AZB' ) then
              AutoZeroBertec=  .true.   
            else if ( CMND.OptText(j,1) .eq. 'AZP' ) then
              if ( (.not. CMND.OptNumOK(j,1)) .or.
     &             (CMND.OptRealVal(j,1) .lt. 0.0) .or.
     &             (CMND.OptRealVal(j,1) .gt. 100.0)    ) goto 2020
              AutoZeroKPct= CMND.OptRealVal(j,1)
              AutoZeroKistler=  .true.   
              ManZeroKistler=   .false.
            else if ( CMND.OptText(j,1) .eq. 'ASF' ) then
              if ( (.not. CMND.OptNumOK(j,1)) .or.
     &             (CMND.OptIntVal(j,1) .lt. 0) .or.
     &             (CMND.OptIntVal(j,1) .gt. 100)    ) goto 2030
              SkipFrmKistler= CMND.OptIntVal(j,1)
              AutoZeroKistler=  .true.   
              ManZeroKistler=   .false.
            else        
              goto 1020 ! bad option
            end if                                                     
          end if                                                    
        end do ! j                            
      end if ! options are present                                  
c                    
c     Now that the options have been figured out- determine the file type
c     based on either the option or the extension of the filename given
c     imeediately after the READ command.  If a file type option was given
c     then it is assumed that the file will not need an extension added.
c     If no file type option is given and no valid file extension is found
c     then .ANZ is added to the filename.
c
c      if ( CMND.CmndText(2)(1:1) .eq. '*' ) then	! MAC code
c        file_type_num= ANZ_FILE							! MAC
c        do i= 1, 60									! MAC
c          filename(i:i)= ' '						! MAC
c        end do                    					! MAC     
c        filename(1:1)= '*'							! MAC
c      else	  										! MAC
        LineText= CMND.Text(1)
        nameOK= GetFileName( LineText,CMND.CmndStart(2),
     &                       filename,extension,name_length  )
        if ( .not. nameOK ) goto 1030 !error- messed up file name
c      end if										! MAC
      if ( file_type_num .eq. NO_FILE ) then ! check filename extension for type
        file_type_num= NO_FILE            ! since file type option not given
        do i= 1, num_file_types                          
          if ( extension .eq. file_type(i) ) file_type_num= i
        end do             
        if ( (extension(1:1) .eq. 'R') .or. (extension(1:1) .eq. 'M') ) 
     &    file_type_num= M_FILE ! Old GLS file
        if ( file_type_num .eq. GLS1_FILE ) file_type_num= M_FILE ! GLS1
        if ( file_type_num .eq. GLS2_FILE ) file_type_num= GLS_FILE ! GLS2
        if ( file_type_num .eq. 0 ) then ! ANZ is default file type
          file_type_num= ANZ_FILE
          filename= filename(1:name_length)//'.'//file_type(file_type_num) 
        end if                                        
      endif                                
c
c     dam- Feb. 9, 1993
c     If manual zeroing of Kistler force plate is given as an option then
c     read in the last 8 entries on the line as the zero voltages of the
c     plate. This is to fix a screwup in which I did not ground out (zero)
c     the Kistler plate and it has a significant drift in the nonloaded
c     plate voltage levels.
c
      if ( ManZeroKistler ) then 
        if ( CMND.NumCmnd .ne. 10 ) goto 2000
        do i= 1, 8
          if ( .not. CMND.CmndNumOK(i+2) ) goto 2010
          ZeroK(i)= CMND.CmndRealVal(i+2)
        end do
      end if      
c
c     Read the data from the proper file
c
      if ( file_type_num .eq. TR3_FILE ) then
        call zero_all_motion_params(.true.)
        call zero_motion_flags( MF,SF,JF,AF,CF )
        call read_tr3_file( filename,newCS,SetMrkName,MF,OF,data_read)
                               
      else if ( file_type_num .eq. TRU_FILE ) then
        call zero_all_motion_params(.true.)
        call zero_motion_flags( MF,SF,JF,AF,CF )   
        call read_tru_file( filename,newCS,SetMrkName,MF,JF,CF,
     &                      OF,data_read )
                                            
      else if ( file_type_num .eq. C3D_FILE ) then                           
        newCS= .true.                      
        call zero_all_motion_params(.true.)
        call zero_motion_flags( MF,SF,JF,AF,CF )
        call zero_force_flags( FF,EF )
        call read_c3d_file( filename,newCS,SetMrkName,AnalysisType,
     &                      ZeroChnl,CalibrateData,DownRez,DownRezIntvl,
     &                      Oops,Bipolar,MF,FF,EF,OF,data_read )
        if ( data_read .and. (AnalysisType .eq. FULL_ANL) ) then
 	  call InitFullBodyAnalysis(.true.,.true.)
          if ( OSUFile ) then ! hack because of bad channel in the plate
             do i= 1, FP.nfrm
                FpData(mz,i,1)= 0.0
             end do
          end if
        else if ( data_read .and. (AnalysisType .eq. UPR_ANL) ) then
          call InitUpperExtremAnalysis(UseHead,.true.)
        else if ( data_read .and. (AnalysisType .eq. TDP_ANL) ) then
          call InitTimeDistAnalysis(.true.)
        end if

      else if ( file_type_num .eq. ANZ_FILE ) then                 
        call zero_all_motion_params(.true.)
        call zero_all_force_params
        call zero_motion_flags( MF,SF,JF,AF,CF )
        call zero_force_flags( FF,EF )
        call read_anz_file( filename,MF,SF,FF,JF,AF,
     &                      CF,EF,OF,data_read,SwapBytes,
     &                      NoFlags,Skip1 )
        if ( (GP.analysis_type .eq. FULL_ANL) .or.
     &       (GP.analysis_type .eq. HH_ANL)       ) then
          call InitFullBodyAnalysis(.false.,.false.)
        else if ( GP.analysis_type .eq. UPR_ANL ) then
          call InitUpperExtremAnalysis(UseHead,.false.)
        else if ( GP.analysis_type .eq. TDP_ANL ) then
          call InitTimeDistAnalysis(.false.)      
        else if ( GP.analysis_type .eq. CKC_ANL ) then
          call InitCKCAnalysis
        else if ( GP.analysis_type .eq. CC_ANL ) then
          call InitCCAnalysis( .false. )
        else if ( GP.analysis_type .eq. CCR_ANL ) then
          call InitCCRAnalysis
        else if ( GP.analysis_type .eq. CCL_ANL ) then
          call InitCCLAnalysis
        else if ( GP.analysis_type .eq. MAYO_ANL ) then
          call InitMayoAnalysis( .false. )
        else if ( GP.analysis_type .eq. MIR_ANL ) then
          call InitMayoRtAnalysis
        else if ( GP.analysis_type .eq. MIL_ANL ) then
          call InitMayoLfAnalysis
        else if ( GP.analysis_type .eq. F3DR_ANL ) then
          call InitF3DAnalysis(rt)
        else if ( GP.analysis_type .eq. F3DL_ANL ) then
          call InitF3DAnalysis(lf)
        end if            
                              
      else if ( file_type_num .eq. FPD_FILE ) then
        call zero_all_force_params
        call zero_force_flags( FF,EF )
        call read_fpd_file( filename,newCS,FF,OF,data_read )
        
      else if ( file_type_num .eq. M_FILE ) then       
        call zero_all_force_params
        call zero_force_flags( FF,EF )
        call read_gls_file( filename,newCS,OldGain,ZeroChnl,DownRez,
     &                      DownRezIntvl,BeginFrm,
     &                      FF,EF,OF,data_read )
                                        
      else if ( file_type_num .eq. GLS_FILE ) then
        call zero_all_force_params
        call zero_force_flags( FF,EF )        
        call read_gls2_file( filename,newCS,OldGain,ZeroChnl,CalibrateData,
     &                       DownRez,DownRezIntvl,BeginFrm,
     &                       FF,EF,OF,data_read )
        
      else if ( file_type_num .eq. TEXT_FILE ) then
        call ReadTextFile( filename,CMND,MF,SF,JF,FF,
     &                     EF,CF,OF )
        
      else if ( file_type_num .eq. P3D_FILE ) then
	ZeroSegParam= .true.
	if ( GP.analysis_type .eq. F3DR_ANL .and. SF.RightSideAlign )
     &	   ZeroSegParam= .false.
	if ( GP.analysis_type .eq. F3DL_ANL .and. SF.LeftSideAlign )
     &	   ZeroSegParam= .false.
        call zero_all_motion_params(ZeroSegParam)
        call zero_motion_flags( MF,SF,JF,AF,CF )
        call read_p3d_file( filename,AnalysisType,Diagnose,MakeMtn60Hz,
     &                      IgnoreExMrk,CKCNoMedAkl,P3dCm,
     &                      MF,OF,data_read )
        if ( AnalysisType .eq. HH_ANL ) then
          call InitFullBodyAnalysis(.false.,.false.)
          GP.analysis_type= HH_ANL
          SF.RightSideAlign= .false.
          SF.LeftSideAlign=  .false.
        else if ( AnalysisType .eq. CKC_ANL ) then
          call InitCKCAnalysis
          SF.RightSideAlign= .false.
          SF.LeftSideAlign=  .false.
        else if ( AnalysisType .eq. CC_ANL ) then
          call InitCCAnalysis( .true. )
        else if ( AnalysisType .eq. CCR_ANL ) then
          call InitCCRAnalysis
        else if ( AnalysisType .eq. CCL_ANL ) then
          call InitCCLAnalysis
        else if ( AnalysisType .eq. MAYO_ANL ) then
          call InitMayoAnalysis( .true. )
        else if ( AnalysisType .eq. MIR_ANL ) then
          call InitMayoRtAnalysis
        else if ( AnalysisType .eq. MIL_ANL ) then
          call InitMayoLfAnalysis
        else if ( AnalysisType .eq. F3DR_ANL ) then
          call InitF3DAnalysis(rt)
        else if ( AnalysisType .eq. F3DL_ANL ) then
          call InitF3DAnalysis(lf)
        end if

      else if ( file_type_num .eq. ANA_FILE ) then
        call zero_all_force_params
        call zero_force_flags( FF,EF )
        call read_ads_file( filename,AnalysisType,CalibrateData,Diagnose,
     &                      NumPlt,PltType,EMGOnly,OldMayoLCS,
     &                      ManZeroKistler,ZeroK,AutoZeroKistler,AutoZeroKPct,
     &                      SkipFrmKistler,AutoZeroBertec,ZeroChnl,
     &                      FF,EF,OF,data_read )

      end if                                               
c
c     Reverse GCS of marker or force plate information if needed
c      
      if ( ( (file_type_num .eq. P3D_FILE) .or.
     &       (file_type_num .eq. C3D_FILE)     ) .and. RevDir ) then
        do i= 1, MP.num_mrk
          do j= 1, MP.nfrm
            MrkData(x,j,i)= -MrkData(x,j,i)
            MrkData(y,j,i)= -MrkData(y,j,i)
          end do
        end do
      end if                                  
      if ( ( (file_type_num .eq. ANA_FILE) .or.
     &       (file_type_num .eq. C3D_FILE)     ) .and. 
     &     RevDir .and. (FP.num_plt .gt. 0)        ) then
        do i= 1, FP.num_plt
          call MatMult( RevRM,FP.plt_loc(i).LCS,FP.plt_loc(i).LCS )
          FP.plt_loc(i).LCS(x,cntr)= -FP.plt_loc(i).LCS(x,cntr)
          FP.plt_loc(i).LCS(y,cntr)= -FP.plt_loc(i).LCS(y,cntr)
        end do
      end if                                  
      return                                        
                                                                              
1000  write(6,1005)
      if ( OF.ToLog ) write(3,1005)
1005  format(' Error- You must give the name of the file to be read.')
      return

1010  write(6,1015)
      if ( OF.ToLog ) write(3,1015)
1015  format(' Error- Only one option allowed: file type.')
      return                    
                            
1020  write(6,1025) CMND.OptText(j,1)                   
      if ( OF.ToLog ) write(3,1025) CMND.OptText(j,1)
1025  format(' Error- ',a3,' is not a valid option for the READ command.'/
     &         '        Valid options are: '/
     &         '          Files:     TR3,TRU,ANZ,C3D,FPD,GLS,P3D,ANA'/
     &         '          Motion:    NewCoordZ,DecreaseReZ,NoMarkerNames'/
     &         '                     REVerse'/
     &         '          Force/EMG: NOZero,OldGaiN,NOCalibrate,BIPolar,UNIpolar'/
     &         '                     readEMGonly REVerse'/
     &         '          Analyses:  OSU,UPPerextremity,NOHead,TimeDistParam'/
     &         '                     HelenHayes,ClosedKineticChain'/
     &         '                     ckcNoMedialAnkle,ClevelandClinic'/
     &         '                     ClevelandClinicRight,ClevelandClinicLeft'/
     &         '                     Foot3dRight,Foot3dLeft'/
     &         '                     MAYo,MayoInitRight,MayoInitLeft'/
     &         '          ANZ Files: swapBYTes,NOFlags,SKIpblock1'/
     &         '          P3D Files: MakeMtn60hz,ReadeXtraneousMarkers,DIAgnose'/
     &         '          ANA Files: BERtec=#,KistlerOLd=#,KistlerNEw=#,DIAgnose'/
     &         '                     ManuallyZeroKistler, AutoZeroKistler'/
     &         '                     AutoZeroPercent,OldForcePlatearrangement'/
     &         '                     AutoZeroBertec')
      return                               

1030  write(6,1035)
      if ( OF.ToLog ) write(3,1035)
1035  format(' Error- File name as given invalid.')
      return

1040  write(6,1045) CMND.OptIntVal(j,1)
      if ( OF.ToLog ) write(3,1045) CMND.OptIntVal(j,1)
1045  format(' Error- The decrease time resolution interval ',i5,' is invalid'/
     &       '        Valid intervals are from 1 to 100')
      return

1050  write(6,1055) CMND.OptIntVal(j,1)
      if ( OF.ToLog ) write(3,1055) CMND.OptIntVal(j,1)    
1055  format(' Error- The beginning frame number ',i5,' is not valid'/
     &       '        It must be an integer >1')
      return

2000  write(6,2005) CMND.NumCmnd-2
      if ( OF.ToLog ) write(3,2005) CMND.NumCmnd-2
2005  format(' Error- When the manual zero load voltage option is given, there'/
     &       '        must be 8 voltages in the command. There are ',i2,' here')
      return

2010  write(6,2015) i,CMND.CmndText(i+2)
      if ( OF.ToLog ) write(3,2015) i,CMND.CmndText(i+2)
2015  format(' Error- The zero load voltage of sensor # ',i1,' of the Kistler'/
     &       '        force plate is not valid: ',a3)
      return

2020  write(6,2025) CMND.OptText(j,1)
      if ( OF.ToLog ) write(3,2025) CMND.OptText(j,1)
2025  format(' Error- ',a3,' is not a valid percentage for the auto zeroing of'/
     &       '        the Kistler force plate signals. Valid numbers are from'/
     &       '        1 to 100. The default is 5 %')
      return
                                                                    
2030  write(6,2035) CMND.OptText(j,1)
      if ( OF.ToLog ) write(3,2035) CMND.OptText(j,1)
2035  format(' Error- ',a3,' is not a valid number of frames to skip in the'
     &       '        zeroing of the Kistler force plate signals. '/
     &       '        Valid numbers of Frames are 1 to 100')
      return

3000  write(6,3005) CMND.OptText(j,1),MAX_PLT
      if ( OF.ToLog ) write(3,3005) CMND.OptText(j,1),MAX_PLT
3005  format(' Error- ',a3,' is not a valid force plate number'/
     &       '        The force plate type options must include a force plate'/
     &       '        number between 1 and ',i1,'. For example, /BER=1/KOL=2')
      return
      end
c           
c
c                                                                            
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Isolate a file name and its extension from a text string 
c
      function GetFileName( line,pos1,filename,extension,name_length )
      implicit none                 
         
      integer*4 pos1,name_length,bracket
      character line*(*),filename*60,extension*3
      logical*4 GetFileName

      integer*4 i,j,MaxChr

      if ( pos1 .le. 0 ) goto 999
      GetFileName= .true.
      MaxChr= len(line)
      i= pos1 
      do while ( (line(i:i) .ne. ' ') .and. (i .lt. MaxChr) )
        i= i + 1
      end do
      if ( i .eq. MaxChr ) then ! do another check just in case
        i= pos1
        do while ( (line(i:i) .ne. line(MaxChr:MaxChr)) .and. (i .lt. MaxChr) )
          i= i + 1
        end do
      end if                                              
      if ( i .ge. MaxChr ) goto 999      !error- messed up filename
      do j= 1, 60
        filename(j:j)= ' '
      end do
      name_length= i - pos1
      do j= 1, name_length
        filename(j:j)= line(pos1+j-1:pos1+j-1)
      end do
      do j= 1, 3
        extension(j:j)= ' '
      end do
      i= name_length
      do while ( (filename(i:i) .ne. '.') .and. (i .gt. 1) )
        i= i - 1
      end do
      bracket= index(filename,']')
      if ( bracket .gt. 0 ) then ! filename has path information
        if ( i .lt. bracket ) return ! no extension on filename
      end if
      if ( i .gt. 56 ) i= 56  
      do j= 1, 3
        extension(j:j)= filename(i+j:i+j)
      end do
      return
999   GetFileName= .false.
      return
      end
c
c
c                          
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Read 3D marker trajectories and analog data from the files produced
c              by AMASS. The AnalysisType flag allows different marker sets to be
c              read in.
c
c     Routines called:
c       AssignFullAnlMrkName
c
      subroutine read_c3d_file( filename,newCS,SetMrkName,AnalysisType,
     &                          ZeroPlt,Calibrate,DownRez,DownRezIntvl,
     &                          Oops,Bipolar,MF,FF,EF,OF,data_read )
      implicit none
      include 'a_const.inc'
                 
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_mrk.inc'
      include 'a_gls2.inc'
      include 'a_frc.inc'
      include 'a_emg.inc'                    
      include 'a_names.inc'
                                         
      record /marker_flag/   MF
      record /force_flag/    FF
      record /emg_flag/      EF
      record /option_flag/   OF
      integer*4 AnalysisType,DownRezIntvl
      character filename*60                                  
      logical*4 data_read,newCS,SetMrkName,ZeroPlt,Calibrate,DownRez,Oops,
     &          Bipolar
                                    
      integer*2 nummrk,numchnl,frmstart,frmend,sc(2),frm1_ptr,
     &          avratio,buffer(256),vfr(2),mxintgap,prm_ptr
      integer*4 record_num,word_in_rec,prev_record,words_to_frm,numfrm,
     &          axis,mrk,c3d_tru(21),strt,end,error,frm,frm_length,NewMrk,
     &          afrm,efrm,chnl,i,plt,drzfrm,j,k
      real*4    scale,tscale,temp,vfrate,tvfrate,rbuffer,tmpld(6),sum
      character BlankName*20
      logical*4 DoFRC,DoEMG
                               
      equivalence ( sc(1),scale )
      equivalence ( vfr(1),vfrate )
c      data c3d_tru /9,20,8,24,7,22,1,2,12,21,11,25,10,23,3,16,15,14,19,18,17/
       data c3d_tru /14,15,16,1,22,7,8,9,17,18,19,3,23,10,11,12,20,21,2,24,25/
       data BlankName / 'None Assigned       ' /
c                    
c     Open the file
c
      data_read= .false.
      open(unit=1,file=filename,access='direct',status='old',
     &     recl=512,iostat=error,err=1000                    )
                
c                                   
c     read in file header
c
      frm= 0
      read(1,rec=1,err=1010,iostat=error) 
     &  prm_ptr,nummrk,numchnl,frmstart,frmend,mxintgap,
     &  tscale,frm1_ptr,avratio,tvfrate
c     &  sc(1),sc(2),frm1_ptr,avratio,vfr(1),vfr(2)
      numfrm= frmend-frmstart+1
      if ( avratio .gt. 0 ) then
        numchnl= numchnl/avratio
      else
        numchnl= 0
      end if
c
c     Added 12/93 by dam
c     Convert real*4 numbers from vax/pdp format to pc format
c
      call SwapReal4(tscale,scale)
      call SwapReal4(tvfrate,vfrate)
c      scale= 1.0/3.0
c      vfrate= 50.0
c
c     Report header data for diagnostic purposes
c
      write(6,10) numfrm,nummrk,numchnl,vfrate,avratio,frmstart,scale
      if ( OF.ToLog ) 
     &  write(3,10) numfrm,nummrk,numchnl,vfrate,avratio,frmstart,scale
10    format(/' C3d file header indicates:'//
     &        5x,i3,' frames'/
     &        5x,i3,' markers'/
     &        5x,i3,' analog channels'/
     &        1x,f7.3,' video frame rate (Hz)'/
     &        5x,i3,' analog/video sample ratio'/
     &        5x,i3,' starting frame number'/
     &        5x,e11.3,' scale factor'/)
      if ( avratio .lt. 1 ) avratio= 1
c                                                                      
c     Assign/Check over motion data parameters
c                           
      MP.nfrm= numfrm
      if ( MP.nfrm .gt. MAX_MTN ) then
        write(6,13) MP.nfrm,MAX_MTN
        if ( OF.ToLog ) write(3,13) MP.nfrm,MAX_MTN
13      format(' Warning- ',i5,' frames of data in the file but only',i3/
     &         '          are allowed.  The data will be truncated.')
        MP.nfrm= MAX_MTN
      end if
      MP.first_frm= frmstart
      MP.num_mrk= nummrk
      MP.frm_rate= vfrate
      rMtnFrmRate= MP.frm_rate
      MP.first_frm= frmstart
c
c     Assign/check over analog data parameters
c       First 18 channels are assumed to be force plate data
c
      if ( (FP.frm_rate .gt. 0).and. (numchnl .eq. 0) ) then 
        DoFRC= .false.
        avratio= 0
        FP.av_frm_ratio= float(FP.frm_rate)/rMtnFrmRate
      else if ( (numchnl .gt. 0) .and. (numchnl .lt. 18) ) then
        DoFRC= .false.
c        avratio= 0
        write(6,500) numchnl
        if (OF.ToLog) write(3,500) numchnl
500     format(' Warning- There should be at least 18 channels of analog data'/
     &         '          in the C3D file but there are only ',i3,' channels'/
     &         '          Analog data (force/EMG) is being ignored...')
      else if ( numchnl .ge. 18 ) then
        DoFRC= .true.
        FP.num_plt= 3
        FP.frm_rate= avratio*vfrate
        FP.av_frm_ratio= avratio
        FP.nfrm= avratio*numfrm
        if ( DownRez ) then
          FP.nfrm= FP.nfrm/DownRezIntvl
          write(6,620) DownRezIntvl
          if ( OF.ToLog ) write(3,620) DownRezIntvl
620       format(' NOTE- Only every 1 in ',i4,' samples of force and EMG data'/
     &           '       will be read in from the file'/)
        end if
        write(6,600) FP.nfrm
        if ( OF.ToLog ) write(3,600) FP.nfrm
600     format(' 3 force plates with ',i5,' samples are present in file')
        if ( ZeroPlt ) then
          write(6,630)
          if ( OF.ToLog ) write(3,630)
630       format(' NOTE- The force plate a/d data is being zeroed with the'/
     &           '       minimum average signal of 40 sections of the data')
        end if 
        if ( Bipolar ) then
          write(6,635)
          if ( OF.ToLog ) write(3,635)
635       format(' NOTE- The force plate a/d data is assumed to be BIPOLAR')
        end if 
        if ( FP.nfrm .gt. MAX_ELC ) then
          write(6,510) FP.nfrm,MAX_ELC                         
          if (OF.ToLog) write(3,510) FP.nfrm,MAX_ELC
510       format(' Warning- There are ',i5,' frames of analog data in the file'/
     &           '          but a maximum of ',i5,' are allowed. The data will'/
     &           '          be truncated.')
          FP.nfrm= MAX_ELC
        end if
        do plt= 1, FP.num_plt
          do j= 1, 2
            FP.plt_size(j,plt)= PltSize(j,plt)
          end do                              
          do j= x, cntr
            do k= x, z
              FP.plt_loc(plt).LCS(k,j)= PltLoc(k,j,plt)
            end do           
          end do
        end do ! plt
      else
        DoFRC= .false.
        FP.num_plt= 0
        FP.nfrm= 0
        avratio= 0
      end if
c
c     Check whether EMG data is present: numchnl>18
c    
      if ( numchnl .gt. 18 ) then
        DoEMG= .true.
        EP.NumChnl= numchnl-18
        write(6,610) EP.NumChnl
        if ( OF.ToLog ) write(3,610) EP.NumChnl
610     format(' There are ',i3,' EMG channels in the file'/)
        if ( EP.NumChnl .gt. MAX_EMG ) then
          write(6,530) EP.NumChnl,MAX_EMG
          if (OF.ToLog) write(3,530) EP.NumChnl,MAX_EMG
530       format(' Warning- There are ',i5,' channels of EMG data in the file'/
     &           '          but a maximum of ',i5,' are allowed. The data will'/
     &           '          be truncated.')                         
          EP.NumChnl= MAX_EMG
        end if
        do i= 1, EP.NumChnl
          EP.Surface(i)= .true.
          EP.Name(i)= 'None Assigned'
        end do
        do i= 1, 40
          EP.Filter(i:i)= ' '
        end do
        EP.SampleRate= FP.frm_rate
        if ( FP.nfrm .gt. MAX_EMF ) then
          write(6,520) FP.nfrm,MAX_EMF
          if (OF.ToLog) write(3,520) FP.nfrm,MAX_EMF
520       format(' Warning- There are ',i5,' frames of EMG data in the file'/
     &           '          but a maximum of ',i5,' are allowed. The data will'/
     &           '          be truncated.')
          EP.NumSamples= MAX_EMF
        else
          EP.NumSamples= FP.nfrm
        end if
      else if ( numchnl .gt. 0 ) then
        DoEMG= .false.
        EF.read= .false.
      end if
c
c     Marker Set Check - This must be changed if another marker set is used
c
      if ( (nummrk .ne. 21) .and.
     &     (AnalysisType .eq. FULL_ANL) ) goto 1020  ! error- not enough markers
      if ( (nummrk .eq. 6) .and. (AnalysisType .ne. TDP_ANL) ) 
     &  write(6,700)
700     format(' NOTE- 6 markers being read from C3D file but analysis type'/
     &         '       is not set to TimeDistanceParameters.  If that is what'/
     &         '       you actually want then redo the read with the'/
     &         '       /TDP option')
      if ( (nummrk .eq. 21) .and. (AnalysisType .eq. FULL_ANL) )
     &   MP.num_mrk= 25
c       
c     read in marker position data.  Note the array c3d_tru is used as an
c     indirection operator to line up the marker data in the marker_data
c     array in the same order that is for a tru file.  I.e., the markers
c     are in different orders in the two files.
c
c     Also read force plate and EMG analog data if it is present
c       (Added 9/91 by dam)                           
c       
      efrm= 0
      drzfrm= DownRezIntvl
      frm_length= nummrk*4 + numchnl*avratio
      prev_record= 0
      write(6,101)
101   format(' ')                                                       
      do frm= 1, MP.nfrm
        if ( OF.UpDate .and. (mod(frm,10) .eq. 0) ) 
     &    write(6,102) frm,MP.nfrm
102     format('+Reading frame # ',i3,'/',i3,'.')
        words_to_frm= (frm-1)*frm_length
        record_num= words_to_frm/256. + 0.001                            
        word_in_rec= (words_to_frm - float(record_num)*256.) + 0.001 + 1
        record_num= record_num + frm1_ptr
        if ( record_num .ne. prev_record ) then
          read(1,rec=record_num,err=1010,iostat=error) buffer
          prev_record= record_num                         
        end if         
c
c       read marker positions in this frame...  Ignore camera bits & residuals
c                                               stored in the 4th word/marker
c
        do mrk= 1, nummrk
          if ( AnalysisType .eq. FULL_ANL ) then
            NewMrk= c3d_tru(mrk)
          else
            NewMrk= mrk
          end if
          do axis= x, z
            if ( word_in_rec .gt. 256 ) then
              word_in_rec= word_in_rec - 256
              prev_record= prev_record + 1
              read(1,rec=prev_record,err=1010,iostat=error) buffer
            end if                            
            MrkData(axis,frm,NewMrk)= buffer(word_in_rec)*scale*0.001
            word_in_rec= word_in_rec + 1
          end do ! axis                                                       
          if (.not. newCS) then
            MrkData(z,frm,NewMrk)= MrkData(z,frm,NewMrk) - 0.483
            temp= MrkData(x,frm,NewMrk)                   ! swap x & y because
            MrkData(x,frm,NewMrk)= MrkData(y,frm,NewMrk) ! ANZ's CoordSystem is
            MrkData(y,frm,NewMrk)= -temp                  ! rotated relative to
          else if ( Oops ) then
            MrkData(z,frm,NewMrk)= -MrkData(z,frm,NewMrk)
c            temp= MrkData(x,frm,NewMrk)                   ! swap x & y because
            MrkData(y,frm,NewMrk)= -MrkData(y,frm,NewMrk) ! ANZ's CoordSystem is
c            MrkData(y,frm,NewMrk)= -temp                  ! rotated relative to
          end if                                           ! AMASS's
          word_in_rec= word_in_rec + 1 ! skip 4th word of marker position data
        end do ! mrk
c                                       
c       Now read the force and EMG data.  File should contain a/d integers
c       NOTE that the 20.0/4096.0 term below is directly tied to the gain of
c       a/d's and that depending upon whether the force plate a/d data has
c       been zeroed half of the voltage range must zeroed. The subtraction
c       occurs if the plates have NOT been zeroed and the a/d's are set to 
c       unipolar (meaning their values are only positive). Normally this 
c       subtraction should not be needed.
c
        do afrm= 1, avratio
          if ( .not. DownRez ) then
            efrm= efrm + 1
          else if ( DownRez .and. (drzfrm .eq. DownRezIntvl) ) then
            efrm= efrm + 1
            drzfrm= 1
          else
            drzfrm= drzfrm + 1
          end if
          do chnl= 1, numchnl           
            if ( word_in_rec .gt. 256 ) then
              word_in_rec= word_in_rec - 256         
              prev_record= prev_record + 1
              read(1,rec=prev_record,err=1010,iostat=error) buffer
            end if        
            rbuffer= (20.0/4096.0)*float(buffer(word_in_rec))
            if ( Bipolar ) rbuffer= rbuffer - 10.0
            if ( chnl .le. 6 ) then ! plate 1
              FpData(chnl,efrm,1)= rbuffer
            else if ( chnl .le. 12 ) then ! plate 2
              FpData(chnl-6,efrm,2)= rbuffer
            else if ( chnl .le. 18 ) then ! plate 3
              FpData(chnl-12,efrm,3)= rbuffer
            else if ( (chnl .gt. 18) .and. 
     &                (chnl .le. 18+EP.NumChnl) ) then
              EMGData(efrm,chnl-18)= rbuffer
            end if
            word_in_rec= word_in_rec + 1
          end do         
        end do         
      end do ! frm
      data_read= .true.
      MF.read= .true.
      if ( DoFRC ) FF.read= .true.
      if ( DoEMG ) EF.read= .true.
      GP.MtnFile= filename(1:40)
      close( unit=1 )                             
      write(6,550)
      if (OF.ToLog) write(3,550)
550   format(' C3D file read completed.'/)
c                                       
c     Set marker names if SetMrkName flag is true
c                                           
      if ( SetMrkName ) then
        if ( AnalysisType .eq. FULL_ANL )  call AssignFullAnlMrkName
      end if             
c
c     if force plate data was present then convert it from integer values to
c     voltages and then pass it through the force plate calibration matrix
c     to produce forces and moments (in N and Nm).
c
      if ( DoFRC .and. ZeroPlt ) then
        do plt= 1, FP.num_plt
           call ZeroForcePlateSignals(FpData(1,1,plt),FP.nfrm)
        end do
      end if
      if ( DoFRC .and. Calibrate ) then
        write(6,560)
        if (OF.ToLog) write(3,560)
560     format(' Converting force plate data...')
        do efrm= 1, FP.nfrm
          do plt= 1, FP.num_plt
            do i= fx, mz
              tmpld(i)= FpData(i,efrm,plt)
            end do
            do axis= fx, mz
              FpData(axis,efrm,plt)= 0.0
              do i= 1, 6
                FpData(axis,efrm,plt)= 
     &            FpData(axis,efrm,plt) + CalMatrix(axis,i,plt)*tmpld(i)
              end do ! i
            end do ! axis
          end do ! plt
        end do ! efrm
      end if                            
      return                              
                                                  
1000  write(6,1005) filename,error                        
      if ( OF.ToLog ) write(3,1005) filename,error
1005  format(' Error- Trouble opening ',a60/
     &       '        Error number ',i2,' occurred.')
      return

1010  write(6,1015) error,filename
      if ( OF.ToLog ) write(3,1015) error,filename
1015  format(' Error- Error number ',i2,' occurred while reading ',a60)
      return

1020  write(6,1025)
      if ( OF.ToLog ) write(3,1025) 
1025  format(' Error- This c3d file cannot be used!'/
     &       '        To be analyzed the file must have 21 markers.')
      return             
      end
c
c     Copyright by Dwight Meglan 1993
c
c     Identify region which the smallest change 
c     over it and call this the zero region. Use with all force plate
c     signal channels
c
      subroutine ZeroForcePlateSignals(data,npnt)
      implicit none

      real*4 data(6,*)
      integer*4 npnt

      integer*4 MAX_REGION
      parameter (MAX_REGION=50)

      real*4    RegionAvg(6,MAX_REGION),RegionMin(6,MAX_REGION),
     &          RegionMax(6,MAX_REGION),RegionRange(6,MAX_REGION),sum,
     &          minval
      integer*4 frm,RegionStart(MAX_REGION),RegionEnd(MAX_REGION),NumRegion,
     &          ld,MinRegion,i
      logical*4 UseAllLoads

      UseAllLoads= .false.
      frm= 1
      NumRegion= 40
      do i= 1, NumRegion
         RegionStart(i)= frm
         RegionEnd(i)= frm + float(npnt)/float(NumRegion)
         frm= RegionEnd(i) + 1
      end do
      RegionEnd(NumRegion)= npnt
      if ( RegionStart(NumRegion) .gt. RegionEnd(NumRegion) )
     &     RegionStart(NumRegion)= RegionEnd(NumRegion)-1
      do i= 1, NumRegion
         do ld= 1, 6
            RegionMin(ld,i)= data(ld,RegionStart(i))
            RegionMax(ld,i)= data(ld,RegionStart(i))
            sum= 0.0
            do frm= RegionStart(i), RegionEnd(i)
               if ( RegionMin(ld,i) .gt. data(ld,frm) )
     &              RegionMin(ld,i)= data(ld,frm)
               if ( RegionMax(ld,i) .lt. data(ld,frm) )
     &              RegionMax(ld,i)= data(ld,frm)
               sum= sum + data(ld,frm)
            end do
            RegionAvg(ld,i)= sum/(RegionEnd(i)-RegionStart(i)+1)
            RegionRange(ld,i)= RegionMax(ld,i) - RegionMin(ld,i)
         end do
      end do

      MinRegion= 1
      minval= 0.0
      if ( UseAllLoads ) then
         do ld= 1, 6
            minval= minval + abs(RegionRange(ld,1))
         end do
      else
         minval= abs(RegionRange(3,1))
      end if
      do i= 2, NumRegion
         if ( UseAllLoads ) then
            sum= 0.0
            do ld= 1, 6
               sum= sum + abs(RegionRange(ld,i))
            end do
         else
            sum= abs(RegionRange(3,i))
         end if
         if ( sum .lt. minval ) then
            MinRegion= i
            minval= sum
         end if
      end do

c      write(6,100) MinRegion,RegionAvg(1,MinRegion),RegionAvg(2,MinRegion),
c     &             RegionAvg(3,MinRegion),RegionAvg(4,MinRegion),
c     &             RegionAvg(5,MinRegion),RegionAvg(6,MinRegion)
c100   format(1x,i2,6(1x,f12.6))

      do frm= 1, npnt
         do ld= 1, 6
            data(ld,frm)= data(ld,frm) - RegionAvg(ld,MinRegion)
         end do
      end do

      return
      end
c
c     Copyright by Dwight Meglan 1993
c
c     Identify region which the smallest change 
c     over it and call this the zero region. Use with individual
c     signal channels
c
      subroutine ZeroSignal(data,npnt)
      implicit none

      real*4 data(*)
      integer*4 npnt

      integer*4 MAX_REGION
      parameter (MAX_REGION=50)

      real*4    RegionAvg(MAX_REGION),RegionMin(MAX_REGION),
     &          RegionMax(MAX_REGION),RegionRange(MAX_REGION),sum,
     &          minval
      integer*4 frm,RegionStart(MAX_REGION),RegionEnd(MAX_REGION),NumRegion,
     &          MinRegion,MaxRegion,i

      frm= 1
      NumRegion= 40
      do i= 1, NumRegion
         RegionStart(i)= frm
         RegionEnd(i)= frm + npnt/NumRegion
         frm= RegionEnd(i) + 1
      end do
      RegionEnd(NumRegion)= npnt
      if ( RegionStart(NumRegion) .gt. RegionEnd(NumRegion) )
     &     RegionStart(NumRegion)= RegionEnd(NumRegion)-1
      do i= 1, NumRegion
         RegionMin(i)= data(RegionStart(i))
         RegionMax(i)= data(RegionStart(i))
         sum= 0.0
         do frm= RegionStart(i), RegionEnd(i)
            if ( RegionMin(i) .gt. data(frm) )
     &           RegionMin(i)= data(frm)
            if ( RegionMax(i) .lt. data(frm) )
     &           RegionMax(i)= data(frm)
            sum= sum + data(frm)
         end do
         RegionAvg(i)= sum/(RegionEnd(i)-RegionStart(i)+1)
         RegionRange(i)= RegionMax(i) - RegionMin(i)
      end do

      MinRegion= 1
      minval= abs(RegionRange(1))
      do i= 2, NumRegion
         if ( abs(RegionRange(i)) .lt. minval ) then
            MinRegion= i
            minval= abs(RegionRange(i))
         end if
      end do

      do frm= 1, npnt
         data(frm)= data(frm) - RegionAvg(MinRegion)
      end do

      return
      end
c
c
c
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Assign text names for the markers used in the full body analysis
c
      subroutine AssignFullAnlMrkName
      implicit none

      include 'a_const.inc'
      include 'a_struct.inc'
      include 'a_prm.inc'

      MP.mrk_name(1)= 'RTAS'
      MP.mrk_name(2)= 'SACR'
      MP.mrk_name(3)= 'LFAS'
      MP.mrk_name(4)= 'RTHP'
      MP.mrk_name(5)= 'LFHP'
      MP.mrk_name(6)= 'NECK'
      MP.mrk_name(7)= 'RTKN'
      MP.mrk_name(8)= 'RTAK'
      MP.mrk_name(9)= 'RTTO'
      MP.mrk_name(10)= 'LFKN'
      MP.mrk_name(11)= 'LFAK'
      MP.mrk_name(12)= 'LFTO'
      MP.mrk_name(13)= 'MSAC'
      MP.mrk_name(14)= 'RTSH'
      MP.mrk_name(15)= 'RTEL'
      MP.mrk_name(16)= 'RTWR'
      MP.mrk_name(17)= 'LFSH'
      MP.mrk_name(18)= 'LFEL'
      MP.mrk_name(19)= 'LFWR'
      MP.mrk_name(20)= 'RTHE'
      MP.mrk_name(21)= 'LFHE'
      MP.mrk_name(22)= 'RTTH'
      MP.mrk_name(23)= 'LFTH'
      MP.mrk_name(24)= 'RTCF'
      MP.mrk_name(25)= 'LFCF'
      return                     
      end
c
c
c             
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Read the 3D marker trajectory data from the VICON TR3 file format.
c              This routine is set up to only read the OSU marker set.
c
c     Routines called:
c       AssignFullAnlMrkName
c
      subroutine read_tr3_file( filename,newCS,SetMrkName,
     &                          MF,OF,data_read )
      implicit none
      include 'a_const.inc'

      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_mrk.inc'
      include 'a_names.inc'                                 

      record /marker_flag/   MF
      record /option_flag/   OF
      character filename*60
      logical*4 data_read,newCS,SetMrkName
                           
      real*4    point(3)
      integer*4 temp(4),pntr,mrk,axis,i,tr3_tru(25),error,frm
      integer*2 frmstart,frmend,numhdr,junk(4),nummrk
      character newname*40,mrk_names(25)*8

      data tr3_tru /14,15,16,1,22,7,30,30,8,9,17,18,19,3,23,10,30,30,11,12,
     &              20,21,2,24,25                                   /
      data mrk_names / 'rtshldr  ','rtelbow ','rtwrist ','rtasis  ','rtfmstk ',
     &                 'rtknee   ','raknee  ','raakle  ','rtankle ','rttoe   ',
     &                 'lfshldr  ','lfelbow ','lfwrist ','lfasis  ','lffmstk ',
     &                 'lfknee   ','laknee  ','laakle  ','lfankle ','lftoe   ',
     &                 'rtheel   ','lfheel  ','sacstk  ','rttbstk ','lftbstk '/
c 
c     Open file
c                         
      data_read= .false.
      open(unit=1,file=filename,access='direct',status='old',recl=4,
     &     form='unformatted',err=1000,iostat=error                  )
c
c     Read in the header of the tr3 file
c        
      frm= 0
      read(1,rec=1,err=1010,iostat=error) numhdr,nummrk,frmstart,frmend,(junk(i),i=1,4)
      MP.nfrm= frmend-frmstart+1
      if ( MP.nfrm .gt. MAX_MTN ) then
        write(6,13) MP.nfrm,MAX_MTN
        if ( OF.ToLog ) write(3,13) MP.nfrm,MAX_MTN
13      format(' Warning- ',i5,' frames of data in the file but only',i3/
     &         '          are allowed.  The data will be truncated.')
        MP.nfrm= MAX_MTN
      end if
      MP.num_mrk= nummrk
      MP.frm_rate= 50
      rMtnFrmRate= 50.0
      if ( FP.frm_rate .gt. 0 ) 
     &  FP.av_frm_ratio= float(FP.frm_rate)/rMtnFrmRate
      MP.cycle_first_frm(1)= frmstart
      MP.cycle_last_frm(1)=  frmend
      MP.first_frm= frmstart
c
c     Assign marker names- This must be changed if the marker configuration
c     is modified
c 
c      do mrk= 1, MP.num_mrk
c        MP.mrk_name(tr3_tru(mrk))= mrk_names(mrk)(1:4)
c      end do ! mrk
      if ( SetMrkName ) then
        call AssignFullAnlMrkName
      else
        do mrk= 1, MP.num_mrk
          MP.mrk_name(mrk)= '    '
        end do
      end if
c             
c     Read in the marker position data.  Note that marker#'s 7,8,17,18 are
c     skipped.  These markers existed in the older tr3 files but are not
c     used anymore.  Also, note that the indirection array tr3_tru is
c     used to place the markers in the same position as they would be if
c     a tru file were read in.                   
c
      write(6,101)
101   format(' ')
      pntr= numhdr+1                                                 
      do frm= 1, MP.nfrm
        if ( OF.UpDate .and. (mod(frm,10) .eq. 0) ) 
     &    write(6,102) frm,MP.nfrm
102     format('+Reading frame # ',i3,'/',i3,'.')
        do mrk= 1, MP.num_mrk
          if ( (mrk .ne. 7)  .and. (mrk .ne. 8) .and.
     &         (mrk .ne. 17) .and. (mrk .ne. 18)      ) then
            read(1,rec=pntr,err=1010,iostat=error) temp(1),temp(2),temp(3),temp(4)
            if ( temp(1) .eq. '00008000'X ) then ! this is -0 when the data
              write(6,30) mrk_names(mrk), frm !    is bad, but this causes a crash !!
              if ( OF.ToLog ) write(3,30) mrk_names(mrk),frm
30            format(' Bad position value found for ',a8,' at frame ',i3)
              do axis= x, z
                MrkData(axis,frm,tr3_tru(mrk))= 0.0
              end do ! axis
            else              
              read(1,rec=pntr,err=1010,iostat=error) point(x),point(y),point(z),
     &                                           junk(1),junk(2)
c                                       
c             swap x and y axes since ANZ has the global coordinate system
c             rotated about the z axis relative to that used previously
c                              
              MrkData(x,frm,tr3_tru(mrk))= point(y)            
              MrkData(y,frm,tr3_tru(mrk))= -point(x)
              MrkData(z,frm,tr3_tru(mrk))= point(z)
              if ( .not. newCS ) MrkData(z,frm,tr3_tru(mrk))=
     &                              MrkData(z,frm,tr3_tru(mrk)) + 0.483 !m
            end if
          end if
          pntr= pntr + 1
        end do ! mrk
      end do ! frm                                                  
      MF.read= .true.
      data_read= .true.
      GP.MtnFile= filename(1:40)
      write(6,20)
      if ( OF.ToLog ) write(3,20)
20    format(' Marker position data has all been successfully read')
      close( unit=1 )
      return
      
1000  write(6,1005) filename,error
      if ( OF.ToLog ) write(3,1005) filename,error
1005  format(' Error- Error opening ',a60/
     &       '        Error number ',i2,' occurred.')
      return              

1010  write(6,1015) error,filename
      if ( OF.ToLog ) write(3,1015) error,filename
1015  format(' Error- Error number ',i2,' occurred while reading ',a60)
      return
      end     
                                                                    
c
c                           
c                        
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Read the old OSU TRU file format containing either markers and/or angle
c              data. This routine is set up to only read the OSU marker set. Gait events
c              are also in this file.
c
c     Routines called:
c       AssignFullAnlMrkName
c       CheckGaitPattern
c
      subroutine read_tru_file( filename,newCS,SetMrkName,MF,JF,
     &                          CF,OF,data_read )
      implicit none
      include 'a_const.inc'
                                                         
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_mrk.inc'
      include 'a_names.inc'
      include 'a_jnt.inc'
                                                                        
      record /marker_flag/   MF
      record /joint_flag/    JF
      record /cycle_flag/    CF             
      record /option_flag/   OF
      character filename*60
      logical*4 data_read,newCS,CheckGaitPattern,SetMrkName
                                                                              
      real*4     meter,VecMag,sign(3),temp
      integer*2  junk,anat_name_ptr,word_size_of_frm,NumFrm,FrmRate,NumMrk,
     &           frm1_ptr,value,gait_event
      integer*4  ptr,mrk,axis,frm,i,anz_axis(3),error,anz_ang(7),ang_order(3)
      character  mrk_names(34)*8,pair(4)*2
      logical*4  AngleOnly
                   
      data meter,sign /0.0254,-1.0,1.0,1.0/
      data anz_axis / 2,1,3 /
      data anz_ang  / 7,3,2,1,6,5,4 /
      data ang_order / 1,3,2 /
c
c     Open file
c
      data_read= .false.
      open(unit=1,file=filename,access='direct',status='old',
     &     recl=2,carriagecontrol='none',
     &     form='formatted',recordtype='fixed',iostat=error,err=1000       )
                         
c                                   
c     read in file header parameters
c                
      AngleOnly= .false.
      error= 0   
      read(1,101,rec=1,err=1010,iostat=error) NumFrm
      MP.nfrm= NumFrm
101   format(a2)      
      if ( MP.nfrm .gt. MAX_MTN ) then
        write(6,13) MP.nfrm,MAX_MTN
        if ( OF.ToLog ) write(3,13) MP.nfrm,MAX_MTN
13      format(' Warning- ',i5,' frames of data in the file but only',i3/
     &         '          are allowed.  The data will be truncated.')
        MP.nfrm= MAX_MTN
      end if
c      read(1,101,rec=2,err=1010,iostat=error) motion_params(intv)
      read(1,101,rec=3,err=1010,iostat=error) FrmRate
      MP.frm_rate= FrmRate
      rMtnFrmRate= float(FrmRate)
      if ( FP.frm_rate .gt. 0 ) 
     &  FP.av_frm_ratio= float(FP.frm_rate)/rMtnFrmRate
      read(1,101,rec=4,err=1010,iostat=error) word_size_of_frm
      read(1,101,rec=6,err=1010,iostat=error) anat_name_ptr
      read(1,101,rec=7,err=1010,iostat=error) frm1_ptr
      MP.first_frm= 1
c                                                 
c     read in marker names
c
      if ( SetMrkName ) then
        call AssignFullAnlMrkName
      else
        ptr= anat_name_ptr
        read(1,101,rec=ptr,err=1010,iostat=error) NumMrk
        MP.num_mrk= NumMrk
        if ( (NumMrk .eq. 7) .or. (word_size_of_frm .eq. 23) ) then 
          JF.angle_EUl= .true.                  ! joint angles only
          JF.TRU_angle= .true.
          AngleOnly= .true.
        else if ( NumMrk .lt. 27 ) then
          goto 1020 ! error- not enough markers              
        else if ( NumMrk .gt. 27 ) then
          MP.num_mrk= 27
          JF.angle_EUL= .true.
          JF.TRU_angle= .true.
        end if
        if ( .not. AngleOnly ) then
          do mrk= 1, MP.num_mrk
            do i= 1, 4                  
              ptr= ptr+1
              read(1,101,rec=ptr,err=1010,iostat=error) pair(i)
            end do ! i
            mrk_names(mrk)= pair(1)//pair(2)//pair(3)//pair(4)
            MP.mrk_name(mrk)= mrk_names(mrk)(1:4)
          end do ! mrk
        end if
      end if ! set marker names or read them in...
c           
c     Read marker position data.  Note if TRU file has been previously analyzed
c     by the gait lab OUT program then it contains joint angle data in addition
c     to marker position data.  The angle data is skipped during reading in the
c     file since it will be computed in the subsequent analysis.
c                                    
      MP.num_rhs= 0
      MP.num_lhs= 0                   
      MP.num_rto= 0
      MP.num_lto= 0
      write(6,201)
201   format(' ')
      do frm= 1, MP.nfrm
        if ( OF.UpDate .and. (mod(frm,10) .eq. 0) )
     &    write(6,202) frm,MP.nfrm
202     format('+Reading frame # ',i3,'/',i3,'.')
        ptr= frm1_ptr+(frm-1)*word_size_of_frm
        read(1,101,rec=ptr,err=1010,iostat=error) junk
        read(1,101,rec=ptr+1,err=1010,iostat=error) gait_event
        GaitEvent(frm)= gait_event
        if ( gait_event .eq. 1 ) then
          MP.num_rhs= MP.num_rhs + 1
          MP.rhs_frm(MP.num_rhs)= frm   
        else if ( gait_event .eq. 2 ) then
          MP.num_lhs= MP.num_lhs + 1
          MP.lhs_frm(MP.num_lhs)= frm
        else if ( gait_event .eq. 3 ) then
          MP.num_rto= MP.num_rto + 1
          MP.rto_frm(MP.num_rto)= frm
        else if ( gait_event .eq. 4 ) then
          MP.num_lto= MP.num_lto + 1
          MP.lto_frm(MP.num_lto)= frm
        end if
        ptr= ptr+2
        if ( .not. AngleOnly ) then
          do mrk= 1, NumMrk
            do axis= x, z                                     
              read(1,101,rec=ptr,err=1010,iostat=error) value
              if ( mrk .le. 27 ) then ! marker position data
                MrkData(anz_axis(axis),frm,mrk)= float(value)*0.1*meter*sign(axis)
              else ! angle data
                JangEUL(ang_order(axis),frm,anz_ang(mrk-27))= 
     &                float(value)/10.0
              end if
              ptr= ptr+1
            end do ! axis
            if ( (.not. newCS) .and. (mrk .le. 27) )
     &         MrkData(z,frm,mrk)= MrkData(z,frm,mrk) + 0.483 !m
          end do ! mrk
          if ( mrk .ge. 27 ) then
            temp= JangEUL(2,frm,pvlb)
            JangEUL(2,frm,pvlb)= JangEUL(3,frm,pvlb) 
            JangEUL(3,frm,pvlb)= temp
          end if
        else ! data is angles only
          do mrk= 1, 7
            do axis= x, z
              read(1,101,rec=ptr,err=1010,iostat=error) value
              JangEUL(ang_order(axis),frm,anz_ang(mrk))= float(value)*0.1
              ptr= ptr + 1
            end do
          end do
          temp= JangEUL(2,frm,pvlb)
          JangEUL(2,frm,pvlb)= JangEUL(3,frm,pvlb) 
          JangEUL(3,frm,pvlb)= temp
        end if
      end do ! frm
      close( unit=1 )
c
c     Quick test to see if TRU file has had its missing markers ( rt hip,
c     lf hip, mid sacrum, and neck created in OUT.  If so then the markers
c     in this file may be analyzed directly without sending the marker data
c     through the Analyze_Fix_Marker_Data subroutine.  This option is primarily
c     to allow older marker data to crunched by ANALYZE to check whether
c     the angles generated in OUT are similar to those generated by ANALYZE
c     using the exact same marker data.
c
      if ( AngleOnly ) then
        write(6,111)
111     format(/' NOTE- This TRU file contains only joint angles')
      else if ( VecMag( MrkData(x,1,MRK_NUM(rt).hip) ) .lt. 0.0001 ) then
        write(6,110)
        if ( OF.ToLog ) write(3,110)
110     format(/' This tru file has not been converted- It does not contain'/
     &          ' calculated markers such as the hips.  Thus, this data can'/
     &          ' not be used by itself to calculate segment and joint info,'/
     &          ' but rather must be read in and have its markers adjusted'/
     &          ' in the same way as TR3 files are adjusted.'/)
      else
        MF.interpolated= .true.
        MF.adjusted=     .true.
      end if
c
c     Check the gait event pattern and set the appropriate cycle parameters
c
      CF.event= .false.
      if ( CheckGaitPattern(OF) ) CF.event= .true.
      if ( JF.TRU_angle .and. (.not. AngleOnly) )  write(6,120)
120   format(' Joint angles have been read in from TRU file.')
      data_read= .true.
      if ( .not. AngleOnly ) MF.read= .true.
      GP.MtnFile= filename(1:40)
      return
                            
1000  write(6,1005) filename,error
      if ( OF.ToLog ) write(3,1005) filename,error
1005  format(' Error- Error opening ',a60/
     &       '        Error number ',i2,' occurred.'/)
      return      

1010  write(6,1015) error,filename
      if ( OF.ToLog ) write(3,1015) error,filename
1015  format(' Error- Error number ',i2,' occurred while reading ',a60)
      return

1020  write(6,1025)
      if ( OF.ToLog ) write(3,1025)
1025  format(' Error- This TRU file cannot be analyzed!'/
     &       '        It contains less than 27 markers.')
      return
      end     
c                  
c                   
c
c
c     Copyright by Dwight Meglan 1990
c
c 
c     Purpose: Read the force plate and EMG data from the old VICON FPD file format.
c
      subroutine Read_Fpd_File( filename,newCS,FF,OF,data_read )
      implicit none
      include 'a_const.inc'
      
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_gls2.inc'
      include 'a_frc.inc'              
      include 'a_names.inc'

      record /force_flag/    FF
      record /option_flag/   OF
      character filename*60
      logical*4 data_read,newCS

      real*4    real_buffer(6),newton,meter
      integer*4 data_start_ptr,data_end_ptr,ptr,data_point,i,j,k,flip(3),
     &          sample_num,chnl_num,plate_num,newptr,error,frm,chnl,plt
      integer*2 int_buffer(12)
      logical*4 Done
      data flip / 1,3,2 / ! redirection array...
      data newton,meter  / 4.4481,0.0254 /  ! N/lb , m/in
c                                                                
c     Assign force plate locations and sizes.  Note that the flip redirection
c     array is being used because the 2nd and 3rd plates are accidentally
c     switched in the VICON fpd files.
c
      do i= 1, 3
        do j= 1, 2                 
          FP.plt_size(j,i)= PltSize(j,flip(i))
        end do
        do j= x, cntr
          do k= x, z
            FP.plt_loc(i).LCS(k,j)= PltLoc(k,j,flip(i))
          end do
        end do
      end do
      if ( .not. newCS ) then
        do i= 1, 3
          FP.plt_loc(i).LCS(z,cntr)= -0.483 !m
        end do ! i
      end if
c                                                    
c       Open file                                                         
c
      data_read= .false.
      open(unit=1,file=filename,access='direct',status='old',iostat=error,
     &     err=1000 )
c
c     Get data file pointers
c              
      read(1,rec=1,err=1010,iostat=error) int_buffer
      FP.num_plt= int_buffer(2)
      data_start_ptr= int_buffer(3)+2*FP.num_plt
      data_end_ptr=   int_buffer(4)*FP.num_plt+2*FP.num_plt
c
c     read in forceplate data
c
      write(6,101)
101   format(' ')
      sample_num= 1               
      Done= .false.            
      ptr= data_start_ptr
      do while( (ptr .le. data_end_ptr) .and. (.not. Done) ) 
        if ( OF.UpDate .and. (mod(sample_num,10) .eq. 0) ) 
     &    write(6,102) sample_num
102     format('+Reading sample # ',i5,'.')
        do plate_num= 1, FP.num_plt
          newptr= ptr + plate_num-1
          read(1,rec=newptr,err=1010,iostat=error) real_buffer
          do chnl_num= 1, 6
            FpData(chnl_num,sample_num,plate_num)= real_buffer(chnl_num)
          end do     
        end do                      
        ptr= ptr + FP.num_plt
        sample_num= sample_num+1  
        if ( sample_num .gt. MAX_ELC ) then
          write(6,13) MAX_ELC
          if ( OF.ToLog ) write(3,13) MAX_ELC
13        format(' Warning- This file has more than ',i5,' sets of data '/
     &           '          which is the maximum that can be read.  The data'/
     &           '          will be truncated at this many data sets.')
          Done= .true.
          sample_num= MAX_ELC+1
        end if
      end do        
      FP.nfrm= sample_num-1
      do plt= 1, FP.num_plt
        do chnl= 1, 6
          FpAvg(chnl,plt)= 0.0
          FpSD(chnl,plt)=  0.0
          FpZero(chnl,plt)= 0.0
        end do
        do frm= 1, FP.nfrm
          do chnl= 1, 6
            FpData(chnl,frm,plt)= FpData(chnl,frm,plt)*newton
          end do ! chnl
          do chnl= 3, 6
            FpData(chnl,frm,plt)= FpData(chnl,frm,plt)*meter
          end do ! chnl
          do chnl= 1, 6
            FpAvg(chnl,plt)= FpAvg(chnl,plt) + FpData(chnl,frm,plt)
          end do
        end do ! frm
        do chnl= 1, 6
          FpAvg(chnl,plt)= FpAvg(chnl,plt)/float(FP.nfrm)
          do frm= 1, FP.nfrm
            FpSD(chnl,plt)= FpSD(chnl,plt) + 
     &                          ( FpData(chnl,frm,plt) - 
     &                            FpAvg(chnl,plt) )**2
          end do    
          FpSD(chnl,plt)= sqrt( FpSD(chnl,plt)/
     &                              (float(FP.nfrm)-1.0) )
        end do ! chnl
      end do ! plt
      FP.frm_rate= 50             ! fixed number of samples per second
      close( unit=1 )                  ! not sure about this number....
      data_read= .true.
      FF.read= .true.
      GP.FrcFile= filename(1:40)
      return
                          
1000  write(6,1005) filename,error
      if ( OF.ToLog ) write(3,1005) filename,error
1005  format(' Error- Trouble opening ',a60/
     &       '        Error number ',i2,' occurred.'/)
      return      
      
1010  write(6,1015) error,filename,newptr
      if ( OF.ToLog ) write(3,1015) error,filename,newptr
1015  format(' Error- Error # ',i2,' occured while reading ',a60/
     &       '        at pointer # ',i4,'.')
      return      
      end
c
c                         
c                         
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Read the force plate and EMG information from the old OSU GLS file format.
c
      subroutine read_gls_file( filename,newCS,OldGain,ZeroChnl,DownRez,
     &                          DownRezIntvl,BeginFrm,
     &                          FF,EF,OF,data_read )
      implicit none
      include 'a_const.inc'
      
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_gls2.inc'
      include 'a_frc.inc'
      include 'a_emg.inc'
      include 'a_names.inc'  
                          
      record /force_flag/    FF
      record /emg_flag/      EF
      record /option_flag/   OF
      integer*4 DownRezIntvl,BeginFrm
      character filename*60
      logical*4 data_read,newCS,OldGain,ZeroChnl,DownRez
                 
      real*4    gain,ChnlGain(32),kg,newton,meter,ZeroData(6,100,4)
      integer*4 end,error,FrcChnlNum(6,3),PltCode(12),ChnlCode,
     &          EMGChnlNum(MAX_EMG),frm,plt,axis,i,j,k,load,RealFrm
      integer*2 HdrPtr,group,f1,f2,f3                  
      character Root*60,mfile*60,rfile*60,FrcNames(6)*2,blank*20
                               
      structure / GenInfo /              ! These structures are based upon
        union                            ! the program EPLOT written by
          map                            ! Jim Feller at OSU to extract and
            integer*2 array(17)          ! plot EMG signals from the old
          end map                        ! GLS files
          map
            integer*2   fill
            integer*2   ChnlHdrPtr
            integer*2   MiscHdrPtr
            integer*2   NumSample
            integer*2   SampleStart
            integer*2   Mode
            integer*2   TakeNum
            integer*2   Base
            character*9 Date
            character*1 fill2
            character*8 Time
          end map
        end union
      end structure

      structure / MiscInfo /
        union
          map
            integer*2 array(6)
          end map
          map
            integer*2 weight
            integer*2 direction
            integer*2 foot
            real*4    convertFactor
            integer*2 SampleRate
          end map
        end union
      end structure
             
      structure / ChnlGain /
        real*8 FWord
        real*8 Gain                            
      end structure

      structure / ChnlInfo /
        union    
          map     
            integer*2 array(256)
          end map
          map
            record /ChnlGain/ Num(32)
          end map
        end union
      end structure
                 
      structure / ChnlData /
        union
          map
            integer*2 IntVal(32)
          end map
          map
            character*64 Ascii
          end map
        end union
      end structure

      record /GenInfo/  HDR
      record /MiscInfo/ MISC         
      record /ChnlInfo/ CHNL
      record /ChnlData/ DATA
                                                                 
      data PltCode  / 3,2,1,6,5,4,3,2,1,6,5,4 /
      data FrcNames / 'fx','fy','fz','mx','my','mz' /
      data blank / '                    '/
      data kg / 2.2241 / ! convert lbs to kg
      data newton,meter  / 4.4482,0.0254 /  ! N/lb, m/in
c                
c     Assign force plate sizes and locations to FP.  Only 2 plates
c     present in GLS files. NOTE: In GLS only the second and third plates in 
c     the lab are active, therefore the second and third plate sizes and 
c     locations are assigned to plates 1 and 2.
c                                              
      do i= 1, 2                                                
        do j= 1, 2
          FP.plt_size(j,i)= PltSize(j,i+1)
        end do
        do j= x, cntr
          do k= x, z
            FP.plt_loc(i).LCS(k,j)= PltLoc(k,j,i+1)
          end do           
        end do
      end do
c      if ( .not. newCS ) then
c        do i= 1, 2
c          FP.plt_loc(i).LCS(z,cntr)= -0.483 !m
c        end do ! i
c      end if

      FF.read= .false.
      EF.read= .false.
c                                                                 
c     Form file root name and open M and R files
c                  
      end= 0
      i= 60
      do while( (i .gt. 0) .and. (end .eq. 0) ) 
        if ( filename(i:i) .eq. '.' ) end= i-1
        i= i - 1
      end do
      if ( end .gt. 56 ) end= 56
      if ( end .gt. 0 ) then
        Root= filename(1:end)
        mfile= Root(1:end)//'.M'//filename(end+3:end+4)
        rfile= Root(1:end)//'.R'//filename(end+3:end+4)
      else                                     
        Root= filename
        mfile= filename(1:index(filename,' ')-1)//'.M'//filename(end+3:end+4)
        rfile= filename(1:index(filename,' ')-1)//'.R'//filename(end+3:end+4)
      end if
      open ( unit=1,file=mfile,status='OLD',access='DIRECT',
     &       iostat=error,err=1000 )
      open ( unit=2,file=rfile,status='OLD',access='DIRECT',
     &       iostat=error,err=1010 )
      GP.FrcFile= rfile(1:40)
c                               
c     Files have been opened successfully.  Now read in M file header info
c                     
      read(1,rec=4,err=1020,iostat=error) HdrPtr
      do i= 1, 17               
        read(1,rec=HdrPtr+i,err=1020,iostat=error) HDR.array(i)
      end do
      do i= 1, 6
        read(1,rec=HDR.MiscHdrPtr+i,err=1020,iostat=error) MISC.array(i)
      end do
      do i= 1, 256
        read(1,rec=HDR.ChnlHdrPtr+i,err=1020,iostat=error) CHNL.array(i)
      end do
      close(unit=1)
c
c     Determine which of the electrical channels are from force plates
c     and which are from EMG's
c
      do plt= 1, 3    ! zero out physical channel pointers for force plates
        do i= 1, 6
          FrcChnlNum(i,plt)= 0
        end do   
      end do
      EP.NumChnl= 0        
      do i= 1, 31
        group= CHNL.Num(i).FWord / 1.0d9
        f1= group/100              ! force channel number
        f2= mod(abs(group),100)/10 ! force plate number - 1
        f3= mod(abs(group),10)
        gain= sngl( CHNL.Num(i).Gain / 1.0d5 )
        ChnlGain(i)= gain / 1000.
        if ( ChnlGain(i) .le. 0.0 ) ChnlGain(i)= 1.0
        if ( f1 .gt. 0 ) then ! it's a force channel...
          if ( f1 .gt. 2 ) goto 1030 ! error bad plate number
          if ( f1 .gt. FP.num_plt ) FP.num_plt= f1
          ChnlCode= 6*(f1-1) + f2 + f3**2                   
          if ( (ChnlCode .gt. 0) .and. (ChnlCode .lt. 7) ) then ! plate # 1
            FrcChnlNum(PltCode(ChnlCode),1)= i
          else if ( (ChnlCode .gt. 6) .and. (ChnlCode .lt. 13) ) then ! plate #2
            FrcChnlNum(PltCode(ChnlCode),2)= i
          else
            goto 1040 ! bad force plate channel code
          end if
        else if ( f1 .lt. 0 ) then ! EMG channel
          if ( EP.NumChnl .lt. MAX_EMG ) then    ! limit # of channels
            EP.NumChnl= EP.NumChnl + 1
            EMGChnlNum(EP.NumChnl)= i
            EP.CodeNum(EP.NumChnl)= abs(f1)
            EP.Surface(EP.NumChnl)= .true.
            EP.Name(EP.NumChnl)= blank
          end if 
        end if
      end do
      do plt= 1, FP.num_plt   ! check whether all 6 pointers for each plate
        do i= 1, 6                 ! have been set- if not...
          if ( FrcChnlNum(i,plt) .eq. 0 ) goto 1060 ! error
        end do                                             
      end do
c                
c     Assign various run parameters   NOTE- there seems to be something
c     wrong in the sampling rate recorded in the gls file because everyone
c     tells me that the data is taken at 500 Hz but the file header says
c     that the sampling rate is 125 Hz.  I have hard coded the sampling
c     rate at 500 as a result.
c
      if ( BeginFrm .gt. HDR.NumSample-96 ) goto 1070 ! error
      if ( .not. DownRez ) DownRezIntvl= 1
      if ( ((HDR.NumSample-96)/DownRezIntvl)-BeginFrm .gt. MAX_ELC ) then
        write(6,101) ((HDR.NumSample-96)/DownRezIntvl)-BeginFrm,MAX_ELC
101     format(' Warning- File contains ',i5,' samples but a maximum of'/
     &         '          ',i5,' is allowed.  File will be truncated.')
        FP.nfrm=    MAX_ELC
        EP.NumSamples= MAX_ELC
      else
        FP.nfrm=    ((HDR.NumSample-96)/float(DownRezIntvl))-BeginFrm
        EP.NumSamples= ((HDR.NumSample-96)/float(DownRezIntvl))-BeginFrm
      end if                
      FP.frm_rate=     500/float(DownRezIntvl) ! should really be MISC.SampleRate   
      EP.SampleRate=   500/float(DownRezIntvl) ! should really be MISC.SampleRate
      if ( DownRez ) write(6,102) FP.frm_rate
102   format(' Reading file at an effective sample rate of ',i5' Hz')
      if ( BeginFrm .gt. 1 ) write(6,106) BeginFrm
106   format(' Reading file starting at frame # ',i5)
      if ( rMtnFrmRate .gt. 0.0 ) then            
        FP.av_frm_ratio= float(FP.frm_rate)/rMtnFrmRate
      else                   
        FP.av_frm_ratio= 1.0
      end if                                           
      GP.weight= float(MISC.weight)*MISC.convertFactor/kg      
      FP.FirstFoot= MISC.foot
      FP.Direction= MISC.direction
c
c     Read in the first 96 samples which contain the zero levels
c
      do frm= 1, 96
        read(2,rec=frm,iostat=error,err=1050) DATA.Ascii
        do plt= 1, FP.num_plt
          do axis= fx, mz
            ZeroData(axis,frm,plt)= ( float(DATA.IntVal(FrcChnlNum(axis,plt)))/
     &                                ChnlGain(FrcChnlNum(axis,plt)) )*newton
          end do ! axis
          do axis= mx, mz
            ZeroData(axis,frm,plt)= FpData(axis,frm,plt)*meter*10.0
          end do ! axis       ! the 10x is purely based upon observed error
        end do ! plt          ! in calculation of center of pressure
      end do
c                      
c     Now read in force plate and emg data from R file
c
      write(6,103)
103   format(' ')          
      RealFrm= 97 + BeginFrm - 1              
      do frm= 1, FP.nfrm
        if ( OF.UpDate .and. (mod(frm,20) .eq. 0) ) 
     &    write(6,104) frm,FP.nfrm
104     format('+Reading sample # ',i5,'/',i5,'.')
        do i= 1, DownRezIntvl
          read(2,rec=RealFrm+i-1,iostat=error,err=1050) DATA.Ascii
        end do      
        RealFrm= RealFrm + DownRezIntvl                               
        do plt= 1, FP.num_plt
          do axis= fx, mz
            FpData(axis,frm,plt)= ( float(DATA.IntVal(FrcChnlNum(axis,plt)))/
     &                                 ChnlGain(FrcChnlNum(axis,plt)) )*newton
          end do ! axis 
          do axis= mx, mz
            FpData(axis,frm,plt)= FpData(axis,frm,plt)*meter*10.0
          end do ! axis       ! the 10x is purely based upon observed error
        end do ! plt          ! in calculation of center of pressure
        do i= 1, EP.NumChnl     
          EMGData(frm,i)= float(DATA.IntVal(EMGChnlNum(i)))/
     &                    ChnlGain(EMGChnlNum(i))*(20.0/4096.0) - 10.0
        end do    
      end do ! frm
      close(unit=2)                              
      FF.read= .true.
      if ( EP.NumChnl .gt. 0 ) EF.read= .true.
      data_read= .true.
c      
c     Correct mx and my signals on plates 1 and 2.  The correction factors
c     were determined empirically by placing weights in 9 positions on the
c     plates and evaluating the error in moments measured.  Also, a factor of
c     100 correction to mz of plate 2 is applied.  This is a strictly empirical
c     quantity based upon the fact that the mz returned by plate #2 appears to
c     be about 25x too large...
c
      if ( FP.num_plt .ge. 1 ) then
        do frm= 1, FP.nfrm
          FpData(mx,frm,1)= 1.1796*FpData(mx,frm,1)
          FpData(my,frm,1)= 1.1779*FpData(my,frm,1)
        end do
      end if
      if ( FP.num_plt .ge. 2 ) then
        do frm= 1, FP.nfrm
          FpData(mx,frm,2)= 1.1861*FpData(mx,frm,2)
          FpData(my,frm,2)= 1.1727*FpData(my,frm,2)
          FpData(mz,frm,2)= FpData(mz,frm,2)/25.0
        end do
      end if
c                
c     Correct gain on plate #2 my and mz channels.  On all data acquired before 
c     May 1989, this plate had its gains on this channels set to 1000 rather
c     than 4000.
c
      if ( OldGain ) then                              
        do frm= 1, FP.nfrm
          FpData(my,frm,2)= 4.0*FpData(my,frm,2)
          FpData(mz,frm,2)= 4.0*FpData(mz,frm,2)
        end do
      end if
c
c     Calculate average and standard deviation of force plate loads      
c
      do plt= 1, FP.num_plt
        do load= 1, 6
          FpAvg(load,plt)=  0.0
          FpSD(load,plt)=   0.0
          FpZero(load,plt)= 0.0
        end do
      end do
      do plt= 1, FP.num_plt
        do load= 1, 6
          do frm= 1, 96
            FpZero(load,plt)= FpZero(load,plt) + ZeroData(load,frm,plt)
          end do
          FpZero(load,plt)= FpZero(load,plt)/96.0
        end do
      end do
      if ( ZeroChnl ) then
        do plt= 1, FP.num_plt
          do load= 1, 6
            do frm= 1, FP.nfrm
              FpData(load,frm,plt)= FpData(load,frm,plt) - 
     &                                 FpZero(load,plt)
            end do
            FpZero(load,plt)= 0.0
          end do
        end do
      end if
      do plt= 1, FP.num_plt
        do frm= 1, FP.nfrm
          do load= 1, 6
            FpAvg(load,plt)= FpAvg(load,plt) + FpData(load,frm,plt)
          end do
        end do ! frm
        do load= 1, 6
          FpAvg(load,plt)= FpAvg(load,plt)/float(FP.nfrm)
          do frm= 1, FP.nfrm
            FpSD(load,plt)= FpSD(load,plt) + 
     &                          ( FpData(load,frm,plt) - 
     &                            FpAvg(load,plt) )**2
          end do    
          FpSD(load,plt)= sqrt( FpSD(load,plt)/
     &                              (float(FP.nfrm)-1.0) )
        end do ! load
      end do ! plt
      return        
      
1000  write(6,1005) mfile,error
      if ( OF.ToLog ) write(3,1005) mfile,error
1005  format(' Error - Could not open GLS Mfile : ',a60/
     &       '         Error # ',i3,' occured.')
      return
      
1010  write(6,1015) rfile,error
      if ( OF.ToLog ) write(3,1015) rfile,error
1015  format(' Error - Could not open GLS Rfile : ',a60/
     &       '         Error # ',i3,' occured.')
      close(unit=1)
      return
      
1020  write(6,1025) error
      if ( OF.ToLog ) write(3,1025) error
1025  format(' Error - Trouble reading M file header information.'/
     &       '         Error # ',i3,' occured.')
      close(unit=1)
      close(unit=2)
      return
 
1030  write(6,1035) f1
      if ( OF.ToLog ) write(3,1035) f1
1035  format(' Error - Bad information in M file header.'/
     &       '         ',i3,' force plates indicated but only',
     &       ' 0,1 or 2 allowed.')
      close(unit=2)
      return

1040  write(6,1045) ChnlCode
      if ( OF.ToLog ) write(3,1045) ChnlCode
1045  format(' Error - Bad information in M file header.'/
     &       '         Force plate channel code calculated to be ',i3/
     &       '         but only codes from 1 to 12 allowed.')
      close(unit=2)                             
      return                                                  
      
1050  write(6,1055) frm,error
      if ( OF.ToLog ) write(3,1055) frm,error
1055  format(' Error - Trouble reading R file data at frame ',i5,'.'/
     &       '         Error # ',i3,' occured.')
      close(unit=2)
      return                                         
      
1060  write(6,1065) FrcNames(i),plt
      if ( OF.ToLog ) write(3,1065) FrcNames(i),plt
1065  format(' Error - The data channel # of the ',a2,' axis of plate ',i2/
     &       '         is set to 0 which is not physically possible.'/
     &       '         Something is not right with the M file header.')
      close(unit=2)

1070  write(6,1075) BeginFrm,HDR.NumSample-96
      if ( OF.ToLog ) write(3,1075) BeginFrm,HDR.NumSample-96
1075  format(' Error - The beginning frame #, ',i5,', is greater than the total'/
     &       '         number of frames, ',i5,' in the file')
      close(unit=2)
      return       
      end   
c                                                                    
c                                    
c                   
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Read the force plate and EMG data from the second generation OSU GLS
c              file format.
c
      subroutine read_gls2_file( filename,NewCS,OldGain,ZeroChnl,CalibrateData,
     &                           DownRez,DownRezIntvl,BeginFrm,
     &                           FF,EF,OF,data_read )
      implicit none
      include 'a_const.inc'

      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_gls2.inc'
      include 'a_frc.inc'
      include 'a_emg.inc'
      include 'a_names.inc'
                          
      record /force_flag/    FF
      record /emg_flag/      EF
      record /option_flag/   OF
      integer*4 DownRezIntvl,BeginFrm
      character filename*60          
      logical*4 data_read,newCS,OldGain,ZeroChnl,CalibrateData,DownRez
                                 
      record /FileHeader/ FileHdr
      record /SyncHeader/ SyncHdr
      record /FrcHeader/  FrcHdr(4)
      record /EMGHeader/  EMGHdr(MAX_EMG)
         
      integer*4 MaxPlts,MaxEMG,ErrNum,i,j,k,FrcPtr,FrcDataLen,plt,axis,frm,
     &          MaxSample,emg,EMGDataLen,EMGPtr,chnl,frmb,StartPtr,
     &          OrigNumSample
      integer*2 TempData(3*MAX_ELC)
      real*4    RawPltData(6,MAX_ELC),meter,newton,AmpGain(6),kg
      character CharRun*2
      logical*4 Error
               
      data MaxPlts,MaxEMG,MaxSample / 4,MAX_EMG,MAX_ELC /
      data newton,meter,kg / 4.4418,0.3048,2.2209 / ! N/lb, m/ft, kg/lb
                                     
c              
c     Open GLS2 file and read in file, force and EMG headers
c                                               
      data_read=    .false.
      FF.read= .false.
      EF.read= .false.
      open( unit=1,file=filename,access='DIRECT',status='OLD',
     &      iostat=ErrNum,err=1000 ) 
      call readGLS2Bytes( 1,FileHdrLen,FileHdr,Error,ErrNum )
      if ( Error ) goto 1010                    
      Gls2FileHdr= FileHdr   ! place in common block for later recall during
      if ( FileHdr.NumPlt .gt. MaxPlts ) then   ! saving of gls2 format
        write(6,100) FileHdr.NumPlt,MaxPlts,MaxPlts
        if ( OF.ToLog ) write(3,100) FileHdr.NumPlt,MaxPlts,MaxPlts
100     format( ' File indicates ',i3,' force plates, but only ',i3,
     &          ' plates allowed.'/' Reading only the first ',i3,' plates...'/)
        FileHdr.NumPlt= MaxPlts
      end if
      if ( FileHdr.NumEMG .gt. MaxEMG  ) then
        write(6,110) FileHdr.NumEMG,MaxEMG,MaxEMG
        if ( OF.ToLog ) write(3,110) FileHdr.NumEMG,MaxEMG,MaxEMG
110     format( ' File indicates ',i3,' EMG channels, but only ',i3,
     &          ' channels allowed.'/
     &          ' Reading only the first ',i3,' channels...'/)
        FileHdr.NumEMG= MaxEMG
      end if
      do i= 1, FileHdr.NumPlt
        FrcPtr= FileHdr.FrcPtr + (i-1)*FrcHdrLen
        call readGLS2Bytes( FrcPtr,FrcHdrLen,FrcHdr(i),Error,ErrNum )
        if ( Error ) goto 1010
      end do             
      do i= 1, FileHdr.NumEMG
        EMGPtr= FileHdr.EMGPtr + (i-1)*EMGHdrLen
        call readGLS2Bytes( EMGPtr,EMGHdrLen,EMGHdr(i),Error,ErrNum )
        if ( Error ) goto 1010
      end do
c                                                                  
c     Now read in force plate data and assign force plate params from Force
c     Headers.  Convert force plate voltages to forces and moments using
c     plate calibration matrices.  These loads are relative to the imbedded
c     coordinate system of the plate.                   
c                      
      if ( .not. DownRez ) DownRezIntvl= 1
      if ( DownRez ) write(6,125) DownRezIntvl
125   format(' Reading data by skipping ',i3,' frames at a time')
      if ( BeginFrm .gt. 1 ) write(6,127) BeginFrm
127   format(' Starting to read at frame # ',i5)
      write(6,128)
128   format(/)
      if ( FileHdr.NumPlt .gt. 0 ) then
        do plt= 1, FileHdr.NumPlt          
          if ( BeginFrm .gt. FrcHdr(plt).NumSample ) goto 1040 ! error
          OrigNumSample= FrcHdr(plt).NumSample
          if ( OrigNumSample .gt. 3*MAX_ELC )  then
            write(6,129) FrcHdr(plt).NumSample,plt,3*MAX_ELC
129         format(' Warning- ',i5,' samples are present in plate # ',i1,' but'/
     &             '          only ',i5,' can be handled.  The number of '/
     &             '          available samples will be truncated to this')
            OrigNumSample= 3*MAX_ELC
            FrcHdr(plt).NumSample= OrigNumSample
          end if
          FrcHdr(plt).NumSample= ( FrcHdr(plt).NumSample - BeginFrm + 1 )/
     &                           DownRezIntvl
c          if ( OldGain ) then ! correct the amp gain which was changed for all
c            do axis= fx, mz   ! force plate data after May 1989....
c              AmpGain(axis)= OldAmpGain(axis,plt)
c            end do
c          else
c            do axis= fx, mz
c              AmpGain(axis)= NewAmpGain(axis,plt)
c            end do                                                
c          end if                                    
          if ( FrcHdr(plt).NumSample .gt. MaxSample ) then
            write(6,120) FrcHdr(plt).Numsample,plt,MaxSample,MaxSample
            if ( OF.ToLog ) write(3,120) FrcHdr(plt).Numsample,plt,
     &                                           MaxSample,MaxSample
120         format(' File indicates ',i5,' remaining samples for plate # ',i1,
     &             ' but only ',i4,' allowed.'/
     &             ' Reading only the first ',i4,' samples...'/)   
            FrcHdr(plt).NumSample= MaxSample
          end if
          FrcDataLen= OrigNumSample*2
          if ( OF.UpDate ) write(6,130) plt,FileHdr.NumPlt           
130       format('+Reading force plate # ',i1,'/',i1)              
          do axis= fx, mz                                          
            FrcPtr= FrcHdr(plt).DataPtr(axis) + (BeginFrm-1)*2
            call ReadGLS2Bytes( FrcPtr,FrcDataLen,TempData,Error,ErrNum )
            if ( Error ) goto 1020
            if ( FrcHdr(plt).Type .eq. 31 ) then ! previously saved by ANZ
              frmb= 1 
              do frm= 1, FrcHdr(plt).NumSample
                if ( frmb .gt. OrigNumSample ) frmb= OrigNumSample
                RawPltData(axis,frm)= FrcHdr(plt).ConvFactor(axis)*
     &                                float(TempData(frmb))
                frmb= frmb + DownRezIntvl
              end do ! frm
            else 
              frmb= 1
              do frm= 1, FrcHdr(plt).NumSample                     
                if ( frmb .gt. OrigNumSample ) frmb= OrigNumSample
                RawPltData(axis,frm)= (20.0/4096.0)*float(TempData(frmb))
                frmb= frmb + DownRezIntvl
              end do ! frm
              if ( ZeroChnl ) then
                do frm= 1, FrcHdr(plt).NumSample         
                  RawPltData(axis,frm)= RawPltData(axis,frm) -
     &                                  (20.0/4096.0)*
     &                                  FrcHdr(plt).Zero(axis)
                end do ! frm
              end if
            end if
          end do ! axis
          do axis= fx, mz 
            do frm= 1, FrcHdr(plt).NumSample
              if ( (FrcHdr(plt).Type .ne. 31) .and.
     &             CalibrateData                 ) then ! original GLS data
                FpData(axis,frm,plt)= 0.0
                do i= 1, 6
                  FpData(axis,frm,plt)= FpData(axis,frm,plt) +
     &                                     CalMatrix(axis,i,plt)*
     &                                     RawPltData(i,frm)
                end do ! i
              else
                FpData(axis,frm,plt)= RawPltData(axis,frm)
              end if
            end do ! frm           
          end do ! axis
        end do ! plt                              
        data_read=    .true.
        FF.read= .true.
        GP.FrcFile= filename(1:40)
        GP.comment= FileHdr.diag
        if ( FileHdr.Run .lt. 10 ) then
          CharRun= ' '//char(48+FileHdr.Run)
        else if ( FileHdr.Run .lt. 20 ) then
          CharRun= '1'//char(48+FileHdr.Run-10)
        else
          CharRun= '2'//char(48+FileHdr.Run-20)
        end if                        
        k= 40
        do while ( (k .gt. 1) .and. (FileHdr.name(k:k) .eq. ' ') )
          k= k - 1
        end do
c        GP.name=    FileHdr.name(1:k)//'  Run # '//CharRun
c        GP.weight=  FileHdr.weight/kg
c        GP.age=     FileHdr.age
        FP.nfrm= FrcHdr(1).NumSample
        FP.frm_rate= FrcHdr(1).SampleRate/DownRezIntvl
        if ( rMtnFrmRate .gt. 0.0 ) then
          FP.av_frm_ratio= float(FP.frm_rate)/rMtnFrmRate
        else           
          FP.av_frm_ratio= 1.0
        end if
        FP.num_plt= FileHdr.NumPlt
        do plt= 1, FileHdr.NumPlt
          do j= 1, 2
            FP.plt_size(j,plt)= PltSize(j,plt)
          end do                              
          do j= x, cntr
            do k= x, z
              FP.plt_loc(plt).LCS(k,j)= PltLoc(k,j,plt)
            end do           
          end do
        end do ! plt
c        if ( .not. newCS ) then    
c          do plt= 1, FileHdr.NumPlt  
c            FP.plt_loc(plt).LCS(z,cntr)= -0.483 !m
c          end do 
c        end if
      end if ! force plates are present
c       
c     Read EMG data
c                                            
      if ( FileHdr.NumEMG .gt. 0 ) then     
        do emg= 1, FileHdr.NumEMG
          if ( BeginFrm .gt. EMGHdr(emg).NumSample ) goto 1050 ! error
          OrigNumSample= EMGHdr(emg).NumSample
          if ( OrigNumSample .gt. 3*MAX_EMF )  then
            write(6,139) EMGHdr(emg).NumSample,emg,3*MAX_EMF
139         format(' Warning- ',i5,' samples are present in emg # ',i2,' but'/
     &             '          only ',i5,' can be handled.  The number of '/
     &             '          available samples will be truncated to this')
            OrigNumSample= 3*MAX_EMF
            EMGHdr(emg).NumSample= OrigNumSample
          end if
          EMGHdr(emg).NumSample= (EMGHdr(emg).NumSample - BeginFrm + 1)/
     &                           DownRezIntvl
          if ( EMGHdr(emg).NumSample .gt. MAX_EMF ) then                  
            write(6,140) EMGHdr(emg).Numsample,emg,MAX_EMF,MAX_EMF
            if ( OF.ToLog ) write(3,140) EMGHdr(plt).Numsample,emg,
     &                                           MAX_EMF,MAX_EMF
140         format(' File indicates ',i5,' samples for EMG # ',i2,
     &             ' but only ',i4,' allowed.'/
     &             ' Reading only the first ',i4,' samples...'/)
            EMGHdr(emg).NumSample= MAX_EMF
          end if                  
          if ( OF.UpDate ) write(6,150) emg,FileHdr.NumEMG    
150       format('+Reading EMG channel # ',i2,'/',i2)
          EMGDataLen= EMGHdr(emg).NumSample*2*DownRezIntvl
          StartPtr= EMGHdr(emg).DataPtr + (BeginFrm-1)*2
          call ReadGLS2Bytes( StartPtr,EMGDataLen,TempData,
     &                        Error,ErrNum ) 
          if ( Error ) goto 1030
          frmb= 1                            
          do frm= 1, EMGHdr(emg).NumSample
            if ( frmb .gt. OrigNumSample ) frmb= OrigNumSample
            EMGData(frm,emg)= (20.0/4096.0)*float(TempData(frmb))
            frmb= frmb + DownRezIntvl
          end do ! frm
          EP.Name(emg)= EMGHdr(emg).Name(1:20)
          EP.Surface(emg)= .true.
          if ( EMGHdr(emg).ElecType .eq. 1 ) EP.Surface(emg)= .false.
          EP.CodeNum(emg)= EMGHdr(emg).PhysChnl
        end do ! emg
        GP.FrcFile= filename(1:40)
        if ( FileHdr.Run .lt. 10 ) then
          CharRun= ' '//char(48+FileHdr.Run)
        else if ( FileHdr.Run .lt. 20 ) then
          CharRun= '1'//char(48+FileHdr.Run-10)
        else
          CharRun= '2'//char(48+FileHdr.Run-20)
        end if                        
        k= 40
        do while ( (k .gt. 1) .and. (FileHdr.name(k:k) .eq. ' ') )
          k= k - 1
        end do
c        GP.name=    FileHdr.name(1:k)//'  Run # '//CharRun
c        GP.weight=  FileHdr.weight/kg
c        GP.age=     FileHdr.age    
c        GP.comment= FileHdr.diag
        EP.NumChnl= FileHdr.NumEMG
        EP.NumSamples= EMGHdr(1).NumSample
        EP.SampleRate= EMGHdr(1).SampleRate/DownRezIntvl
        data_read=    .true.
        EF.read= .true.
      end if ! emg's are present
      close( unit=1 )    
c
c     Calculate average and standard deviation of force plate loads      
c
      do plt= 1, FP.num_plt
        do chnl= 1, 6
          FpAvg(chnl,plt)=  0.0
          FpSD(chnl,plt)=   0.0
          if ( ZeroChnl ) then
            FpZero(chnl,plt)= 0.0
          else
            FpZero(chnl,plt)= FrcHdr(plt).Zero(chnl)*(20.0/4096.0)
          end if
        end do
        do frm= 1, FP.nfrm
          do chnl= 1, 6
            FpAvg(chnl,plt)= FpAvg(chnl,plt) + FpData(chnl,frm,plt)
          end do
        end do ! frm
        do chnl= 1, 6
          FpAvg(chnl,plt)= FpAvg(chnl,plt)/float(FP.nfrm)
          do frm= 1, FP.nfrm
            FpSD(chnl,plt)= FpSD(chnl,plt) + 
     &                          ( FpData(chnl,frm,plt) - 
     &                            FpAvg(chnl,plt) )**2
          end do    
          FpSD(chnl,plt)= sqrt( FpSD(chnl,plt)/
     &                              (float(FP.nfrm)-1.0) )
        end do ! chnl
      end do ! plt
      return

1000  write(6,1005) filename,ErrNum         
      if ( OF.ToLog ) write(3,1005) filename,ErrNum
1005  format(' Error - Trouble opening ',a60/
     &       '         Error number ',i3,' occured.')
      return     
                 
1010  write(6,1015) filename,ErrNum
      if ( OF.ToLog ) write(3,1015) filename,ErrNum
1015  format(' Error - Trouble reading the headers of ',a60/
     &       '         Error number ',i3,' occured.')
      close(unit=1)                                  
      return
                                            
1020  write(6,1025) filename,ErrNum
      if ( OF.ToLog ) write(3,1025) filename,ErrNum
1025  format(' Error - Trouble reading the force plate data from ',a60/
     &       '         Error number ',i3,' occured.')
      close(unit=1)                                  
      return                                      
      
1030  write(6,1035) filename,ErrNum
      if ( OF.ToLog ) write(3,1035) filename,ErrNum
1035  format(' Error - Trouble reading the EMG data from ',a60/
     &       '         Error number ',i3,' occured.')                 
      close(unit=1)                                  
      return

1040  write(6,1045) BeginFrm,FrcHdr(plt).NumSample
      if ( OF.ToLog ) write(3,1045) BeginFrm,FrcHdr(plt).NumSample
1045  format(' Error - Option indicates file should be read starting at frame'/
     &       ' #',i5/
     &       '         but there are only ',i5,' frames of data')
      close(unit=1)                                  
      return

1050  write(6,1055) BeginFrm,EMGHdr(emg).NumSample
      if ( OF.ToLog ) write(3,1055) BeginFrm,EMGHdr(emg).NumSample
1055  format(' Error - Option indicates file should be read starting at frame'/
     &       ' #',i5/
     &       '         but there are only ',i5,' frames of data')
      close(unit=1)                                  
      return
      end
c
c                    
c        
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Read a 512 byte record from the GLS2 file
c
      subroutine ReadGls2Bytes( BeginPtr,DataLen,Data,Error,ErrNum )
      implicit none
         
      integer*4 BeginPtr,DataLen,ErrNum
      integer*1 Data(*)
      logical*4 Error

      integer*4 RecNum,RecPtr,DataPtr
      integer*1 Record(512)
                                         
      error= .true.
      ErrNum= 999    ! err # to indicate bad BeginPtr or DataLen
      If ( (BeginPtr .gt. 0) .and. (DataLen .gt. 0) ) then
        RecNum= (BeginPtr-1)/512 + 1     ! record # to be read
        RecPtr= (BeginPtr-1) - (RecNum-1)*512 + 1 ! first byte within record
        DataPtr= 1
        do while ( DataPtr .le. DataLen )
          read(1,rec=RecNum,err=1000,iostat=ErrNum) Record
          do while( (RecPtr .le. 512) .and. (DataPtr .le. DataLen) )
            Data(DataPtr)= Record(RecPtr)
            RecPtr=  RecPtr + 1
            DataPtr= DataPtr + 1
          end do
          RecPtr= 1                      
          RecNum= RecNum + 1
        end do
        error= .false.
        ErrNum= 0
      end if
1000  return     
      end
c                                        
c       
c                          
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose:
c     This subroutine saves general gait and marker position information
c     into a number of different file formats.  It decides which file type
c     is going to be saved and calls the appropriate file saving subroutine. Several
c     of file formats are supported: TRU, ANZ, GLS
c                                
c     Routines called:
c       GetFileName
c       save_gls2_file
c       save_tru_file
c       write_anz_file
c
      subroutine save_file( CMND,
     &                      MF,SF,FF,JF,AF,
     &                      CF,EF,OF,data_saved    )
      implicit none
                 
      include 'a_struct.inc'

      logical*4 data_saved
      record /CmndLine/       CMND
      record /marker_flag/    MF
      record /segment_flag/   SF
      record /force_flag/     FF
      record /joint_flag/     JF
      record /avg_pos_flag/   AF
      record /cycle_flag/     CF
      record /emg_flag/       EF
      record /option_flag/    OF      
                              
      integer*4 num_file_types,file_type_num,i,name_length,j
      character extension*3,file_type(3)*3,filename*60,LineText*80
      logical*4 nameOK,GetFileName,NoFlags,OptUsed,UseEuler,UseJCS
                 
      data num_file_types / 3 /
      data file_type / 'TRU','ANZ','GLS' /
              
      if ( CMND.NumCmnd .ne. 2 ) goto 1000 ! error- there must be a filename present
                                       ! as the entry after SAVe
c
c     First check for proper options if any are present
c     check for file type option.
c     NOTE: If options are not present the default is ANZ file, full body marker
c     set, and motion data.          
c
      file_type_num= 0
      NoFlags=  .false.
      UseEuler= .true.
      UseJCS=   .false.
      if ( CMND.NumOpt(1) .gt. 0 ) then
        do j= 1, CMND.NumOpt(1)                                   
          OptUsed= .false.
          do i= 1, num_file_types 
            if ( CMND.OptText(j,1) .eq. file_type(i) ) then
              file_type_num= i
              OptUsed= .true.
            end if
          end do ! i
          if ( .not. OptUsed ) then
            if ( CMND.OptText(j,1) .eq. 'NOF' )  then
              NoFlags= .true.
            else if ( CMND.OptText(j,1) .eq. 'EUL' ) then
              UseEuler= .true.
              UseJCS= .false.
            else if ( CMND.OptText(j,1) .eq. 'JCS' ) then
              UseEuler= .false.
              UseJCS= .true.
            else
              goto 1010 ! error - bad option
            end if
          end if
        end do ! j
      end if ! options are present
c
c     Now that the options have been figured out- determine the file type
c     based on either the option or the extension of the filename given
c     immediately after the READ command.  If a file type option was given
c     then it is assumed that the file will not need an extension added.
c     If no file type option is given and no valid file extension is found
c     then .ANZ is added to the filename.
c                           
c      if ( CMND.CmndText(2)(1:1) .eq. '*' ) then	! MAC code
c        file_type_num= 2							! MAC
c        do i= 1, 60									! MAC
c          filename(i:i)= ' '						! MAC
c        end do                         				! MAC
c        filename(1:1)= '*'							! MAC
c      else											! MAC
        LineText= CMND.Text(1)
        nameOK= GetFileName( LineText,CMND.CmndStart(2),
     &                       filename,extension,name_length  )
        if ( .not. nameOK ) goto 1030 !error- messed up file name
c      end if										! MAC
      if ( file_type_num .eq. 0 ) then ! check filename extension for type
        file_type_num= 0               ! since file type option not given
        do i= 1, num_file_types
          if ( extension .eq. file_type(i) ) file_type_num= i
        end do
        if ( file_type_num .eq. 0 )  file_type_num= 2
      endif
      if ( extension .eq. '   ' ) 
     &  filename= filename(1:name_length)//'.'//file_type(file_type_num)
      
      if ( file_type_num .eq. 1 ) then
        call save_tru_file( filename,UseEuler,UseJCS,
     &                      MF,JF,AF,OF,data_saved )

      else if ( file_type_num .eq. 2 ) then
        call write_anz_file( filename,MF,SF,FF,JF,AF,
     &                       CF,EF,OF,NoFlags,data_saved )

      else if ( file_type_num .eq. 3 ) then
         call save_gls2_file( filename,EF,FF,SF,
     &                        OF,data_saved )
      end if                   
      return
                                                                              
1000  write(6,1005)
      if ( OF.ToLog ) write(3,1005)
1005  format(' Error- You must give the name of the file to be saved.')
      return

1010  write(6,1015) CMND.OptText(j,1)
      if ( OF.ToLog ) write(3,1015) CMND.OptText(j,1)
1015  format(' Error- ',a3,' is not valid option'/
     &       '        Valid options are: /([ANZ],TRU,GLS)'/
     &       '                           /([EULer],JCS)'/
     &       '                           /NOFlags')
      return                    
                                               
1030  write(6,1035) filename
      if ( OF.ToLog ) write(3,1035) filename
1035  format(' Error- File name as given is invalid.'/
     &       '        Filename checked: ',a60 )
      return
      end
c
c
c                           
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Save the force plate and EMG data in a GLS2 format file. The main reason
c              for this is that the force plate data will be in real units rather than
c              A/D units because it should have been evaluated with the force plate
c              calibration matrix upon being read into ANZ.
c
      subroutine save_gls2_file( filename,EF,FF,SF,
     &                           OF,data_saved )
      implicit none
      include 'a_const.inc'
                          
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_names.inc'
      include 'a_emg.inc'
      include 'a_gls2.inc'
      include 'a_frc.inc'

      record /emg_flag/     EF
      record /force_flag/   FF
      record /segment_flag/ SF
      record /option_flag/  OF

      character filename*60
      logical*4 data_saved
                 
      record /FileHeader/ FileHdr
      record /SyncHeader/ SyncHdr
      record /FrcHeader/  FrcHdr(4)                
      record /EMGHeader/  EMGHdr(MAX_EMG)

      character*1 TempHdr(6144)
      integer*2 IntBuffer(MAX_ELC)
      integer*4 ErrNum,plt,i,chnl,FilePtr,frm,HdrPtr
      real*4    RealBuffer(MAX_ELC),KgtoLbs,NtoLbs,MtoIn,ScaleFactor

      data NtoLbs,MtoIn,KgtoLbs / 0.2248,39.3701,0.1124 /

      if ( (.not. FF.read) .and. (.not. EF.read) ) goto 1000 ! error
      data_saved= .false.
c
c     Open GLS2 output file
c
      open( unit=1,file=filename,access='direct',form='unformatted',
     &      recl=128,status='unknown',err=1010,iostat=ErrNum )
c
c     Setup the file header
c                 
      FileHdr= Gls2FileHdr  ! use header of previous Gls2 file as start point
      FileHdr.Len=     512
      FileHdr.Version= 2
      if ( FF.Filtered .or. EF.Filtered ) then
        FileHdr.FilterFlag= 1
      else  
        FileHdr.FilterFlag= 0
      end if
      FileHdr.SyncPtr= 0  ! Sync Channels not supported right now...
      FileHdr.NumSync= 0
      FileHdr.NumPlt=  FP.num_plt
      FileHdr.FrcPtr=  FileHdr.Len + 256*FileHdr.NumSync + 1
      FileHdr.NumEmg=  EP.NumChnl
      FileHdr.EmgPtr=  FileHdr.Len + 256*FileHdr.NumSync +
     &                               256*FileHdr.NumPlt  + 1
      if ( SF.Param ) then
        FileHdr.RtLegLen= ( SP(rtcf).length + SP(rtth).length )*MtoIn
        FileHdr.LfLegLen= ( SP(lfcf).length + SP(lfth).length )*MtoIn
      end if
      if ( GP.age .gt. 0 )    FileHdr.Age=    GP.age
      if ( GP.weight .gt. 0 ) FileHdr.Weight= GP.weight*KgtoLbs
      if ( GP.name(1:1) .ne. ' ' ) FileHdr.Name=   GP.name
      if ( FF.read ) then          
        if ( FP.frm_rate .gt. 0 )
     &    FileHdr.Seconds= float(FP.nfrm)/float(FP.frm_rate)
      else     
        if ( FP.frm_rate .gt. 0 )
     &    FileHdr.Seconds= float(EP.NumSamples)/float(EP.SampleRate)
      end if
      if ( GP.comment(1:5) .ne. '     ' ) 
     &   FileHdr.Comments= GP.comment(1:40)
c                 
c     Setup force plate headers
c                                      
      if ( FF.read ) then
        do plt= 1, FP.num_plt
          FrcHdr(plt).Len=  256                     
          FrcHdr(plt).Type= 31  ! rather than the original 30
          FrcHdr(plt).SampleRate= FP.frm_rate
          FrcHdr(plt).NumSample=  FP.nfrm
          FrcHdr(plt).Name= ' Plate #'//char(plt)
          do i= x, z
            FrcHdr(plt).PltPos(i)= FP.plt_loc(plt).LCS(i,cntr)*MtoIn
          end do
          FrcHdr(plt).NumChnl= 6
        end do ! plt                             
      end if        
c                             
c     Setup the EMG channel headers
c
      if ( EF.read ) then
        do chnl= 1, EP.NumChnl
          EmgHdr(chnl).Len=  256
          EmgHdr(chnl).Type= 41   ! rather than the original 40
          EmgHdr(chnl).SampleRate= EP.SampleRate
          EmgHdr(chnl).NumSample=  EP.NumSamples
          EmgHdr(chnl).Name=       EP.Name(chnl)        
          EmgHdr(chnl).PhysChnl=   EP.CodeNum(chnl)
		  EMGHdr(chnl).ElecType=   1
		  if ( .not. EP.Surface(chnl) ) EMGHdr(chnl).ElecType= 0
          EmgHdr(chnl).Filter=     EP.Filter
        end do ! chnl                                       
      end if                  
c                   
c     Save Force Plate Data           
c
      FilePtr= FileHdr.Len + 256*FileHdr.NumSync + 256*FileHdr.NumPlt +
     &                       256*FileHdr.NumEmg + 1
      do plt= 1, FP.num_plt
        do chnl= fx, mz
          FrcHdr(plt).DataPtr(chnl)= FilePtr
          if ( chnl .le. fz ) then
            do frm= 1, FP.nfrm
              RealBuffer(frm)= FpData(chnl,frm,plt)*NtoLbs
            end do
          else                                                           
            do frm= 1, FP.nfrm
              RealBuffer(frm)= FpData(chnl,frm,plt)*NtoLbs*MtoIn/12.0
            end do                    
          end if
          call CalcScaleFactor( RealBuffer,FP.nfrm,
     &                          FrcHdr(plt).ConvFactor(chnl),
     &                          FrcHdr(plt).Min(chnl),
     &                          FrcHdr(plt).Max(chnl)    )
          if ( abs(FrcHdr(plt).ConvFactor(chnl)) .lt. 1.0e-7 )  ! just in case...
     &      FrcHdr(plt).ConvFactor(chnl)= 1.0
          do frm= 1, FP.nfrm
            IntBuffer(frm)= RealBuffer(frm)/FrcHdr(plt).ConvFactor(chnl)
          end do                                                
          Call WriteGLS2Bytes( IntBuffer,FP.nfrm*2,FilePtr,ErrNum )
          if ( ErrNum .ne. 0 ) goto 1020
          FrcHdr(plt).Zero(chnl)= 0
        end do ! chnl
      end do ! plt
c                       
c     Save EMG Data
c
      do chnl= 1, EP.NumChnl   
        EmgHdr(chnl).DataPtr= FilePtr
        do frm= 1, EP.NumSamples
          RealBuffer(frm)= EMGData(frm,chnl)
        end do
        call CalcScaleFactor( RealBuffer,EP.NumSamples,
     &                        EmgHdr(chnl).ConvFactor,
     &                        EmgHdr(chnl).Min,
     &                        EmgHdr(chnl).Max    )
        if ( abs(EmgHdr(chnl).ConvFactor) .lt. 1.0e-7 )  ! just in case...
     &    EmgHdr(chnl).ConvFactor= 1.0
        do frm= 1, EP.NumSamples
          IntBuffer(frm)= EMGData(frm,chnl)/EmgHdr(chnl).ConvFactor
        end do
        call WriteGLS2Bytes( IntBuffer,EP.NumSamples*2,FilePtr,ErrNum )
        if ( ErrNum .ne. 0 ) goto 1020
      end do
c
c     Now that data has been written out save header information
c     
      HdrPtr= 1 
      call FillGLS2Header( FileHdr,FileHdr.Len,HdrPtr,TempHdr )
c      do i= 1, FileHdr.NumSync
c        call FillGLS2Header( SyncHdr(i),SyncHdr(i).Len,HdrPtr,TempHdr )
c      end do
      do i= 1, FileHdr.NumPlt
        call FillGLS2Header( FrcHdr(i),FrcHdr(i).Len,HdrPtr,TempHdr )
      end do
      do i= 1, FileHdr.NumEmg
        call FillGLS2Header( EmgHdr(i),EmgHdr(i).Len,HdrPtr,TempHdr )
      end do         
      FilePtr= 1
      call WriteGLS2Bytes( TempHdr,HdrPtr-1,FilePtr,ErrNum )
      if ( ErrNum .ne. 0 ) goto 1020
      close ( unit=1 )
      data_saved= .true.
      return    

1000  write(6,1005)
      if ( OF.ToLog ) write(3,1005)
1005  format(' Error - Neither force or EMG data has been read in yet, thus'/
     &       '         it is not possible to save a GLS2 format file')
      return
      
1010  write(6,1015) filename,ErrNum
      if ( OF.ToLog ) write(3,1015) filename,ErrNum
1015  format(' Error - Trouble opening ',a60/
     &       '         Error # ',i4,' occured')
      return
      
1020  write(6,1025) filename,ErrNum
      if ( OF.ToLog ) write(3,1025) filename,ErrNum
1025  format(' Error - Trouble writing to ',a60/
     &       '         Error # ',i4,' occured')
      close( unit=1 )
      return
      end 
c                                              
c                    
c
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Calculate the scale factor needed for each data channel saved in the
c              GLS2 file.
c
      subroutine CalcScaleFactor( Data,DataLen,ScaleFactor,Min,Max )
      implicit none
      
      real*4    Data(1),ScaleFactor,Min,Max
      integer*4 DataLen
      
      integer*4 i
      real*4    absmax

      absmax= abs(Data(1))
      Min=    Data(1)
      Max=    Data(1)
      do i= 1, DataLen
        if ( abs(Data(i)) .gt. absmax ) absmax= abs(Data(i))
        if ( Data(i) .gt. Max ) Max= Data(i)
        if ( Data(i) .lt. Min ) Min= Data(i)
      end do
      ScaleFactor= absmax/30000.0              
      return                 
      end                             
c                  
c
c                      
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Write out a 512 byte record to the GLS2 file
c
      subroutine WriteGLS2Bytes( Data,DataLen,FilePtr,ErrNum )
      implicit none

      integer*1 Data(*)       
      integer*4 DataLen,FilePtr,ErrNum

      integer*1 Record(512)      
      integer*4 RecPtr,RecNum,DataPtr,i
         
      ErrNum= 999
      if ( (FilePtr .gt. 0) .and. (DataLen .gt. 0) ) then
        RecNum= (FilePtr-1)/512 + 1
        RecPtr= (FilePtr-1) - (RecNum-1)*512 + 1
        DataPtr= 1
        if ( RecPtr .gt. 1 ) read(1,rec=RecNum,iostat=ErrNum) Record
        if ( ErrNum .ne. 0 ) then
          do i= 1, 512
            Record(i)= 0
          end do
        end if
        do while ( DataPtr .le. DataLen )
          do while ( (RecPtr .le. 512) .and. (DataPtr .le. DataLen) )
            Record(RecPtr)= Data(DataPtr)
            RecPtr=  RecPtr + 1
            DataPtr= DataPtr + 1
            FilePtr= FilePtr + 1
          end do
          do while ( RecPtr .le. 512 )
            Record(RecPtr)= 0
            RecPtr= RecPtr + 1
          end do
          write(1,rec=RecNum,err=999,iostat=ErrNum) Record
          RecPtr= 1
          RecNum= RecNum + 1
        end do
        ErrNum= 0
      end if
999   return
      end
c
c
c
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Set up the header data block to the GLS2 file
c
      subroutine FillGLS2Header( InHdr,InHdrLen,OutHdrPtr,OutHdr )
      implicit none

      character*1 InHdr(*),OutHdr(*)
      integer*4 InHdrLen,OutHdrPtr

      integer*4 i

      if ( (InHdrLen .gt. 0) .and. (OutHdrPtr .gt. 0) ) then
        i= 1
        do while ( i .le. InHdrLen )
          OutHdr(OutHdrPtr)= InHdr(i)
          OutHdrPtr= OutHdrPtr + 1
          i= i + 1
        end do
      end if
      return
      end 
c                                     
c            
c
c
c     Copyright by Dwight Meglan 1990
c
c     Purpose: Save the markers, angles, and gait events in the old OSU TRU file format
c
      subroutine Save_TRU_file( filename,save_EUL,save_JCS,
     &                          MF,JF,AF,OF,data_saved )
      implicit none
      include 'a_const.inc'
                                                   
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_mrk.inc'    
      include 'a_jnt.inc'
      include 'a_names.inc'

      record /marker_flag/    MF
      record /joint_flag/     JF
      record /avg_pos_flag/   AF
      record /option_flag/    OF        
                                             
      character filename*60
      logical*4 data_saved,save_EUL,save_JCS

      real*4     meter                                           
      integer*2  junk,anat_name_ptr,num_mrk,
     &           word_size_of_frm,frm1_ptr,end_ptr,jnt_num(7),value,
     &           side_frm_size,value1,value2,value3,interval,frm_rate,
     &           num_frm,gait_events(MAX_MTN)
      integer*4  ptr,mrk,axis,frm,i,j,open_status,error
      character  mrk_names(34)*8,pair*2,ans*1,filename2*60

      data meter /0.0254/
      data jnt_num / 7,3,2,1,6,5,4 /                                    
      data mrk_names
     &/'R       ','W       ','B       ','RH      ','LH      ','N       ',
     & 'RK      ','RA      ','RT      ','LK      ','LA      ','LT      ',
     & 'MS      ','RS      ','RE      ','RW      ','LS      ','LE      ',
     & 'LW      ','RQ      ','LQ      ','RF      ','LF      ','RB      ',
     & 'LB      ','RZ      ','LZ      ','PELVIC  ','R.HIP   ','R.KNEE  ',
     & 'R.ANKLE ','L.HIP   ','L.KNEE  ','L.ANKLE '                         /
c           
c     Check options and figure out whether they are valid
c                
      data_saved= .false.
      if ( save_EUL .and. (.not. JF.angle_EUL) ) goto 1030
      if ( save_JCS .and. (.not. JF.angle_JCS) ) goto 1040      
c                
c     open the output file 
c                               
      open( unit=2,file=filename,status='unknown',access='direct',
     &      recl=2,carriagecontrol='none',
     &      form='formatted',recordtype='fixed',iostat=open_status,err=1000 )
c                
c     write out file header parameters
c                     
      error= 0                                            
      num_frm= MP.nfrm
      write(2,125,rec=1,err=1010,iostat=error) num_frm
125   format(a2)
      interval= 1
      write(2,rec=2,err=1010,iostat=error) interval
      frm_rate= nint(rMtnFrmRate)
      write(2,rec=3,err=1010,iostat=error) frm_rate
      word_size_of_frm= 83 ! 2+27*3      
      if ( save_EUL .or. save_JCS ) word_size_of_frm= 104 ! 2+27*3+7*3
      write(2,rec=4,err=1010,iostat=error) word_size_of_frm
      side_frm_size= -1
      write(2,rec=5,err=1010,iostat=error) side_frm_size
      frm1_ptr= word_size_of_frm + 1
      end_ptr= frm1_ptr + MP.nfrm*word_size_of_frm - 1
      anat_name_ptr= end_ptr + 1
      write(2,rec=6,err=1010,iostat=error) anat_name_ptr                
      write(2,rec=7,err=1010,iostat=error) frm1_ptr
      write(2,rec=8,err=1010,iostat=error) end_ptr
c                                       
c     write out marker names
c
      ptr= anat_name_ptr        
      num_mrk= 27
      if ( save_EUL .or. save_JCS ) num_mrk= 34
      write(2,rec=ptr,err=1010,iostat=error) num_mrk
      do mrk= 1, num_mrk
        do i= 1, 7, 2            
          ptr= ptr+1            
          write(2,rec=ptr,err=1010,iostat=error) mrk_names(mrk)(i:i+1)
        end do ! i                                        
      end do ! mrk
c                
c     Create an array with the gait events in it the way TRU files expect
c     it to be.
c
      do i= 1, MP.nfrm
        gait_events(i)= 0
      end do
      do i= 1, MP.num_rhs
        gait_events(MP.rhs_frm(i))= 1
      end do
      do i= 1, MP.num_lhs
        gait_events(MP.lhs_frm(i))= 2
      end do
      do i= 1, MP.num_rto
        gait_events(MP.rto_frm(i))= 3
      end do
      do i= 1, MP.num_lto
        gait_events(MP.lto_frm(i))= 4
      end do
      if ( (MP.num_rhs .eq. 0) .or. (MP.num_lhs .eq. 0) .or.
     &     (MP.num_rto .eq. 0) .or. (MP.num_lto .eq. 0)   ) then
        write(6,200)
        if ( OF.ToLog ) write(3,200)
200     format(' Warning- The gait events have been set improperly in the'/
     &         '          TRU file and as a result there will be compatibility'/
     &         '          with earlier software.  Use CYC EVE to set the'
     &         '          gait events.')
      end if
c
c     Write marker position and joint angle data.  Each frame contains first
c     the marker position data and then the joint angle data.  NOTE that
c     the x and y marker positions are swapped here so that the TRU file
c     will be compatible with the coord systems used in OUT.
c             
      junk= 0
      write(6,201)
201   format(' ')
      do frm= 1, MP.nfrm
        if ( OF.UpDate .and. (mod(frm,10) .eq. 0) ) 
     &    write(6,202) frm,MP.nfrm
202     format('+Writing frame # ',i3,'/',i3,'.')
        ptr= frm1_ptr+(frm-1)*word_size_of_frm
        write(2,rec=ptr,err=1010,iostat=error) junk                 
        write(2,rec=ptr+1,err=1010,iostat=error) gait_events(frm)
        ptr= ptr+2
        do mrk= 1, 27
          value= int( (-10.*MrkData(y,frm,mrk)+0.5)/meter - 190 )
          write(2,rec=ptr,err=1010,iostat=error) value
          value= int( (10.*MrkData(x,frm,mrk)+0.5)/meter - 190 )
          write(2,rec=ptr+1,err=1010,iostat=error) value
          value= int( (10.*MrkData(z,frm,mrk)+0.5)/meter - 190 )
          write(2,rec=ptr+2,err=1010,iostat=error) value   
          ptr= ptr + 3
        end do ! mrk
c                       
c       in writing out the joint angles, swap the obliquity and rotation
c       angles of the pelvis so that they are the same what programs using
c       tru files expect.  Also, invert the sign of the pelvic flexion.
c       option to remove intial rotation in non-saggital plane angles to
c       correct initial misalignment of markers with the body segments which
c       induces a significant static initial angle in these angles but affects
c       the saggital plane angles minimally
c                                                
        if ( save_EUL ) then      
          do j= 1, 7
            if ( jnt_num(j) .eq. pvlb ) then  ! pelvis - correct angles
              value1=  int( JangEUL(1,frm,jnt_num(j))*10. + 0.5 )
              value2=  int( JangEUL(2,frm,jnt_num(j))*10. + 0.5 )
              value3=  int( JangEUL(3,frm,jnt_num(j))*10. + 0.5 )
              write(2,rec=ptr,err=1010,iostat=error)   value1
              write(2,rec=ptr+1,err=1010,iostat=error) value2
              write(2,rec=ptr+2,err=1010,iostat=error) value3
              ptr= ptr+3
            else if ( (jnt_num(j) .eq. rthp) .or. 
     &                (jnt_num(j) .eq. lfhp)      ) then ! rt & lf hips
              value1=  int( JangEUL(1,frm,jnt_num(j))*10. + 0.5 )
              value2=  int( JangEUL(2,frm,jnt_num(j))*10. + 0.5 )
              value3= -int( JangEUL(3,frm,jnt_num(j))*10. + 0.5 )
              write(2,rec=ptr,err=1010,iostat=error)   value1
              write(2,rec=ptr+1,err=1010,iostat=error) value2
              write(2,rec=ptr+2,err=1010,iostat=error) value3
              ptr= ptr+3
            else if ( (jnt_num(j) .eq. rtkn) .or.          
     &                (jnt_num(j) .eq. lfkn)      ) then ! rt & lf knees
              value1=  int( JangEUL(1,frm,jnt_num(j))*10. + 0.5 )
              value2= -int( JangEUL(2,frm,jnt_num(j))*10. + 0.5 )
              value3=  int( JangEUL(3,frm,jnt_num(j))*10. + 0.5 )
              write(2,rec=ptr,err=1010,iostat=error)   value1
              write(2,rec=ptr+1,err=1010,iostat=error) value2
              write(2,rec=ptr+2,err=1010,iostat=error) value3
              ptr= ptr+3
            else
              do axis= x, z
                value= int( JangEUL(axis,frm,jnt_num(j))*10. + 0.5 )
                write(2,rec=ptr,err=1010,iostat=error) value
                ptr= ptr+1
              end do ! axis                                  
            end if ! jnt = 1 : pelvis                      
          end do ! jnt                                                  
        else if ( save_JCS ) then          
          do j= 1, 7   
            if ( j .eq. 1 ) then  ! pelvis - correct so that angles behave
              value1= -int( JangJCS(1,frm,jnt_num(j))*10. + 0.5 )
              value2=  int( JangJCS(3,frm,jnt_num(j))*10. + 0.5 )
              value3=  int( JangJCS(2,frm,jnt_num(j))*10. + 0.5 )
              write(2,rec=ptr,err=1010,iostat=error) value1
              write(2,rec=ptr+1,err=1010,iostat=error) value2
              write(2,rec=ptr+2,err=1010,iostat=error) value3
              ptr= ptr+3
            else if ( (j .eq. 2) .or. (j .eq. 5) ) then ! rt & lf hips
              value1=  int( JangJCS(1,frm,jnt_num(j))*10. + 0.5 )
              value2= -int( JangJCS(3,frm,jnt_num(j))*10. + 0.5 )
              value3=  int( JangJCS(2,frm,jnt_num(j))*10. + 0.5 )
              write(2,rec=ptr,err=1010,iostat=error) value1
              write(2,rec=ptr+1,err=1010,iostat=error) value2
              write(2,rec=ptr+2,err=1010,iostat=error) value3
              ptr= ptr+3
            else          
              do axis= x, z
                value= int( JangJCS(axis,frm,jnt_num(j))*10. + 0.5 )
                write(2,rec=ptr,err=1010,iostat=error) value
                ptr= ptr+1               
              end do ! axis   
            end if ! jnt = 1 : pelvis    
          end do ! j
        end if ! save joint angles
      end do ! frm
      close( unit=2 )                                       
c                             
c     Now save the 'b' version with the initial joint angles subtracted
c                                     
      if ( (save_EUL .and. AF.angle_EUL) .or.
     &     (save_JCS .and. AF.angle_JCS)      ) then ! save two versions of TRU file-
        i= 60                     ! one with normal angles and version
        do while ( filename(i:i) .ne. '.' ) ! 'b' with the initial joint
          i= i - 1                    ! angle when standing subtracted.
        end do      
        i= i-1
        filename2= filename(1:i)//'b'//filename(i+1:59)
        open( unit=4,file=filename2,status='unknown',access='direct',
     &        recl=2,carriagecontrol='none',
     &        form='formatted',recordtype='fixed',iostat=open_status,err=1050 )
        write(4,125,rec=1,err=1060,iostat=error) num_frm
        write(4,125,rec=2,err=1060,iostat=error) interval
        write(4,125,rec=3,err=1060,iostat=error) frm_rate
        write(4,125,rec=4,err=1060,iostat=error) word_size_of_frm
        write(4,125,rec=5,err=1060,iostat=error) side_frm_size
        write(4,125,rec=6,err=1060,iostat=error) anat_name_ptr                
        write(4,125,rec=7,err=1060,iostat=error) frm1_ptr
        write(4,125,rec=8,err=1060,iostat=error) end_ptr
        ptr= anat_name_ptr
        write(4,125,rec=ptr,err=1060,iostat=error) num_mrk
        do mrk= 1, num_mrk
          do i= 1, 7, 2
            ptr= ptr+1
            write(4,125,rec=ptr,err=1060,iostat=error) mrk_names(mrk)(i:i+1)
          end do ! i                                        
        end do ! mrk
        do frm= 1, MP.nfrm
          ptr= frm1_ptr+(frm-1)*word_size_of_frm
          write(4,125,rec=ptr,err=1060,iostat=error) junk
          write(4,125,rec=ptr+1,err=1060,iostat=error) gait_events(frm)
          ptr= ptr+2
          do mrk= 1, 27
            value= int( (-10.*MrkData(y,frm,mrk)+0.5)/meter )
            write(4,125,rec=ptr,err=1060,iostat=error) value
            value= int( (10.*MrkData(x,frm,mrk)+0.5)/meter )
            write(4,125,rec=ptr+1,err=1060,iostat=error) value
            value= int( (10.*MrkData(z,frm,mrk)+0.5)/meter )
            write(4,125,rec=ptr+2,err=1060,iostat=error) value   
            ptr= ptr + 3
          end do ! mrk      
          if ( save_EUL ) then
            do j= 1, 7
              if ( j .eq. 1 ) then  ! pelvis - correct so that angles behave
                value1= -int( ( JangEUL(1,frm,jnt_num(j)) - 
     &                           JangAvgEUL(1,jnt_num(j)) )*10. + 0.5 )
                value2= int( ( JangEUL(2,frm,jnt_num(j)) - 
     &                          JangAvgEUL(3,jnt_num(j))  )*10. + 0.5 )
                value3= int( ( JangEUL(3,frm,jnt_num(j)) - 
     &                          JangAvgEUL(2,jnt_num(j))  )*10. + 0.5 )
                write(4,125,rec=ptr,err=1060,iostat=error) value1
                write(4,125,rec=ptr+1,err=1060,iostat=error) value2
                write(4,125,rec=ptr+2,err=1060,iostat=error) value3
                ptr= ptr+3
              else if ( (j .eq. 2) .or. (j .eq. 5) ) then ! rt & lf hips
                value1= int( ( JangEUL(1,frm,jnt_num(j)) - 
     &                           JangAvgEUL(1,jnt_num(j)) )*10. + 0.5 )
                value2= int( ( JangEUL(2,frm,jnt_num(j)) - 
     &                          JangAvgEUL(3,jnt_num(j))  )*10. + 0.5 )
                value3= -int( ( JangEUL(3,frm,jnt_num(j)) - 
     &                          JangAvgEUL(2,jnt_num(j))  )*10. + 0.5 )
                write(4,125,rec=ptr,err=1060,iostat=error) value1
                write(4,125,rec=ptr+1,err=1060,iostat=error) value2
                write(4,125,rec=ptr+2,err=1060,iostat=error) value3
                ptr= ptr+3
              else   
                do axis= x, z
                  value= int( ( JangEUL(axis,frm,jnt_num(j)) - 
     &                          JangAvgEUL(axis,jnt_num(j)))*10. + 0.5 )
                  write(4,125,rec=ptr,err=1060,iostat=error) value
                  ptr= ptr+1                                      
                end do ! axis   
              end if ! jnt = 1 : pelvis
            end do ! j
          else ! save_JCS angle
            do j= 1, 7
              if ( j .eq. 1 ) then  ! pelvis - correct so that angles behave
                value1= -int( ( JangJCS(1,frm,jnt_num(j)) - 
     &                           JangAvgJCS(1,jnt_num(j)) )*10. + 0.5 )
                value2= int( ( JangJCS(3,frm,jnt_num(j)) - 
     &                          JangAvgJCS(3,jnt_num(j))  )*10. + 0.5 )
                value3= int( ( JangJCS(2,frm,jnt_num(j)) - 
     &                          JangAvgJCS(2,jnt_num(j))  )*10. + 0.5 )
                write(4,125,rec=ptr,err=1060,iostat=error) value1
                write(4,125,rec=ptr+1,err=1060,iostat=error) value2
                write(4,125,rec=ptr+2,err=1060,iostat=error) value3
                ptr= ptr+3
              else if ( (j .eq. 2) .or. (j .eq. 5) ) then ! rt & lf hips
                value1= int( ( JangJCS(1,frm,jnt_num(j)) - 
     &                           JangAvgJCS(1,jnt_num(j)) )*10. + 0.5 )
                value2= int( ( JangJCS(3,frm,jnt_num(j)) - 
     &                          JangAvgJCS(3,jnt_num(j))  )*10. + 0.5 )
                value3= int( ( JangJCS(2,frm,jnt_num(j)) - 
     &                          JangAvgJCS(2,jnt_num(j))  )*10. + 0.5 )
                write(4,125,rec=ptr,err=1060,iostat=error) value1
                write(4,125,rec=ptr+1,err=1060,iostat=error) value2
                write(4,125,rec=ptr+2,err=1060,iostat=error) value3
                ptr= ptr+3
              else   
                do axis= x, z
                  value= int( ( JangJCS(axis,frm,jnt_num(j)) - 
     &                          JangAvgJCS(axis,jnt_num(j)))*10. + 0.5 )
                  write(4,125,rec=ptr,err=1060,iostat=error) value
                  ptr= ptr+1                                      
                end do ! axis   
              end if ! jnt = 1 : pelvis
            end do ! j
          end if ! euler or JCS angles
        end do ! frm
        close( unit=4 )
      end if ! standing position joint angles have been calculated
      if ( save_EUL ) then
        write(6,203)
203     format(' NOTE- Euler angles have been saved in the true file')
      else
        write(6,204)
204     format(' NOTE- JCS angles have been saved in the true file')
      end if
      data_saved= .true.                    
      return

1000  write(6,1005) open_status,filename
      if ( OF.ToLog ) write(3,1005) open_status,filename
1005  format(' Error- Error # ',i2,' occured while opening file: ',a60)
      return                         

1010  write(6,1015) error,filename
      if ( OF.ToLog ) write(3,1015) error,filename
1015  format(' Error- Error # ',i2,' occurred while writing to ',a60)
      return

1020  write(6,1025)
      if ( OF.ToLog ) write(3,1025) 
1025  format(' Error- Only one angle type may be specified (EUL or JCS)...')
      return

1030  write(6,1035)
      if ( OF.ToLog ) write(3,1035) 
1035  format(' Error- Euler angles are specified to be saved,'/
     &       '        but they have not been calculated.')
      return

1040  write(6,1045)
      if ( OF.ToLog ) write(3,1045)
1045  format(' Error- JCS angles are specified to be saved,'/
     &       '        but they have not been calculated.')
      return

1050  write(6,1055) open_status,filename2
      if ( OF.ToLog ) write(3,1055) open_status,filename2
1055  format(' Error- Error # ',i2,' while opening file: ',a60)
      return

1060  write(6,1065) error,filename2
      if ( OF.ToLog ) write(3,1065) error,filename2
1065  format(' Error- Error # ',i2,' occurred while writing to ',a60)
      return
      end
c
c
c      
c
c     Copyright by Dwight Meglan 1990
c                          
c     Purpose: Read in information from a text file and place it in specific data
c              arrays. This routine is intended for importing special data sets such
c              as those averaged in other programs or extracted from the literature.
c              As an example, the data tables in the back of David Winter's book were
c              scanned and imported with this routine to create comparison files that
c              could be read by TELIO and plotted with OSU's data. The following
c              quantities can be imported:
c               JAN - Joint angles
c               JMO - Joint moments
c               JPW - Joint powers
c               GRF - Ground reaction forces
c               EMG - Muscle EMG signals
c               SPN - Segment position/orientation
c
c             This routine is not well error trapped, so use it carefully. If the 
c             text being read in is not in the proper format, it should crash out
c             with a hopefully informative error message. But you never know...
c
c     Routines called:
c       GetJntNum
c       MatMult
c       OpenTextFile
c       ParseLine
c       VecChangeCS
c       zero_force_flags
c       zero_motion_flags
c
      subroutine ReadTextFile( filename,CMND,MF,SF,JF,FF,
     &                         EF,CF,OF )
      include 'a_const.inc'
                                                         
      include 'a_struct.inc'
      include 'a_prm.inc'
      include 'a_mrk.inc'
      include 'a_seg.inc'
      include 'a_gls2.inc'
      include 'a_frc.inc'
      include 'a_enr.inc'
      include 'a_emg.inc'
      include 'a_names.inc'
      include 'a_jnt.inc'
                                    
      record /CmndLine/      CMND                                    
      record /marker_flag/   MF     
      record /segment_flag/  SF
      record /joint_flag/    JF
      record /force_flag/    FF
      record /emg_flag/      EF
      record /cycle_flag/    CF             
      record /option_flag/   OF
      character filename*60

      record /CmndLine/      FileCmnd
 
      real*4    ChMat(3,3),TmpSegPos(3,4)
      integer*4 NumReadVar,NumPerLine(6),Start,side,JntNum,chnl,i,j,NumFrm,
     &          ReadVarNum,ierr,frm,seg,ent1
      character ReadVarName(6)*3,FileText*200,JntText*3
      logical*4 LegVar,GetJntNum,OpenTextFile

      data NumReadVar  / 6 /
      data ReadVarName / 'JAN','JMO','JPW','GRF','EMG','SPN' /
      data NumPerLine  / 3,3,3,2,1,14 /
      data ChMat / 0.0,1.0,0.0,0.0,0.0,1.0,-1.0,0.0,0.0 /
c
c     First check if there are enough parameters on command line
c     Then, check whether quantity to be read is valid
c                                      
c      if ( CMND.NumCmnd .lt. 4 ) goto 1000
      ReadVarNum= 0
      do i= 1, NumReadVar
        if ( CMND.CmndText(3) .eq. ReadVarName(i) ) ReadVarNum= i
      end do
      if ( ReadVarNum .eq. 0 ) goto 1010
c                 
c     Determine component to be read, if needed...
c
      if ( ReadVarNum .lt. 4 ) then ! Joint variable...
        JntText= CMND.CmndText(4)
        LegVar= .false.
        if ( .not. GetJntNum( JntText,JntNum ) ) then
          if ( (ReadVarNum .eq. 2) .or. (ReadVarNum .eq. 3) ) then
            if ( JntText .eq. 'RLG' ) then
              LegVar= .true.
              side= rt
            else if ( JntText .eq. 'LLG' ) then
              LegVar= .true.
              side= lf
            end if
          else                          
            goto 1020 ! error
          end if
        end if
      else if ( ReadVarNum .eq. 4 ) then ! GRF
        if ( CMND.CmndText(4)(1:2) .eq. 'RT' ) then
          side= rt           
        else if ( CMND.CmndText(4)(1:2) .eq. 'LF' ) then
          side= lf
        else
          goto 1030 ! error
        end if
      else if ( ReadVarNum .eq. 5 ) then ! EMG
        if ( .not. CMND.CmndNumOK(4) ) goto 1040 ! error
        if ( (CMND.CmndIntVal(4) .lt. 1) .or. 
     &       (CMND.CmndIntVal(4) .gt. MAX_EMG) ) goto 1040 ! error
        Chnl= CMND.CmndIntVal(4)
      end if
c
c     Made it this far now open the text file
c                             
      FileText= CMND.Text(1)                
      Start= CMND.CmndStart(2)
      if ( .not. OpenTextFile(FileText,Start,.false.) ) goto 1060 ! error
c
c     Read the first line and see if the right number of numbers are there
c
      NumFrm= 1
      read(2,100,iostat=ierr,err=1090) FileText
100   format(a200)                    
      call ParseLine( FileText,FileCmnd )
      ent1= 0
      if ( FileCmnd.NumCmnd .ne. NumPerLine(ReadVarNum) ) then
        if ( (ReadVarNum .eq. 6) .and. (FileCmnd.NumCmnd .eq. 15) ) then
          ent1= 1
        else
          goto 1070 ! error
        end if
      end if
      do i= 1, NumPerLine(ReadVarNum)
        if ( .not. FileCmnd.CmndNumOK(ent1+i) ) goto 1080 ! error
      end do
c
c     File seems to be OK now proceed to read in the numbers...
c
      ierr= 0   
      do while ( ierr .eq. 0 ) 
        if ( ReadVarNum .eq. 1 ) then ! assign Joint angle
          do i= 1, NumPerLine(ReadVarNum)
            JangJCS(i,NumFrm,JntNum)= FileCmnd.CmndRealVal(i)
          end do
        else if ( ReadVarNum .eq. 2 ) then ! assign Joint Moments
          if ( .not. LegVar ) then
            do i= 1, NumPerLine(ReadVarNum)
              JrlJCS(i+3,NumFrm,JntNum)= FileCmnd.CmndRealVal(i)
            end do
            do i= 1, 3    
              JrlJCS(i,NumFrm,JntNum)= 0.0
            end do
            do i= 1, 6
              JrlGCS(i,NumFrm,JntNum)= 0.0
              JrlDCS(i,NumFrm,JntNum)= 0.0
              JrlPCS(i,NumFrm,JntNum)= 0.0
            end do
          else
            do i= 1, NumPerLine(ReadVarNum)
              JRLegJCS(i+3,NumFrm,side)= FileCmnd.CmndRealVal(i)
            end do
            do i= 1, 3
              JRLegJCS(i,NumFrm,side)= 0.0
            end do
            do i= 1, 6
              JRLegGCS(i,NumFrm,side)= 0.0
            end do
          end if
        else if ( ReadVarNum .eq. 3 ) then ! assign Joint Powers
          if ( .not. LegVar ) then
            do i= 1, NumPerLine(ReadVarNum)
              JntPower(i,NumFrm,JntNum)= FileCmnd.CmndRealVal(i)
            end do
            JntPower(4,NumFrm,JntNum)= 0.0
          else
            do i= 1, NumPerLine(ReadVarNum)
              JntPowerLeg(i,NumFrm,side)= FileCmnd.CmndRealVal(i)
            end do
            JntPowerLeg(4,NumFrm,side)= 0.0
          end if
        else if ( ReadVarNum .eq. 4 ) then ! assign Ground Reaction Force
          do i= 1, 6
            FtGrl(i,NumFrm,side)= 0.0
          end do
          FtGrl(1,NumFrm,side)=  FileCmnd.CmndRealVal(1)
          FtGrl(3,NumFrm,side)= -FileCmnd.CmndRealVal(2)
        else if ( ReadVarNum .eq. 5 ) then ! assign EMG
          EMGData(NumFrm,chnl)= FileCmnd.CmndRealVal(1)
        else if ( ReadVarNum .eq. 6 ) then ! assign segment position from animation
          frm= FileCmnd.CmndIntVal(ent1+1)
          if ( frm .gt. MAX_MTN ) then
            write(6,500) MAX_MTN
500         format(' Error- More than ',i4,' frames of motion data in the file'/
     &             '        Only this many frames have been read')
            goto 300
          end if
          seg= FileCmnd.CmndIntVal(ent1+2)
          if ( (seg .lt. 1) .or. (seg .gt. MAX_SEG) ) then
            write(6,510) seg,MAX_SEG
510         format(' Error- The current segment # ',i4,' is not valid'/
     &             '        Only segments from 1 to ',i2,' are valid')
            if ( frm .gt. 1 ) goto 300
            goto 999
          end if
          TmpSegPos(x,x)= FileCmnd.CmndRealVal(ent1+3)
          TmpSegPos(y,x)= FileCmnd.CmndRealVal(ent1+4)
          TmpSegPos(z,x)= FileCmnd.CmndRealVal(ent1+5)
          TmpSegPos(x,y)= FileCmnd.CmndRealVal(ent1+6)
          TmpSegPos(y,y)= FileCmnd.CmndRealVal(ent1+7)
          TmpSegPos(z,y)= FileCmnd.CmndRealVal(ent1+8)
          TmpSegPos(x,z)= FileCmnd.CmndRealVal(ent1+9)
          TmpSegPos(y,z)= FileCmnd.CmndRealVal(ent1+10)
          TmpSegPos(z,z)= FileCmnd.CmndRealVal(ent1+11)
          TmpSegPos(x,cntr)= FileCmnd.CmndRealVal(ent1+12)
          TmpSegPos(y,cntr)= FileCmnd.CmndRealVal(ent1+13)
          TmpSegPos(z,cntr)= FileCmnd.CmndRealVal(ent1+14)
          call MatMult( ChMat,TmpSegPos,SgpGCS(x,x,frm,seg) )
          call VecChangeCS( ChMat,TmpSegPos(x,cntr),SgpGCS(x,cntr,frm,seg) )
          NumFrm= frm
        end if              
        read(2,100,iostat=ierr,err=1090,end=300) FileText
        if ( ierr .eq. 0 ) then
          call ParseLine( FileText,FileCmnd )
          if ( (FileCmnd.NumCmnd .ne. NumPerLine(ReadVarNum)) .and.
     &         (ent1 .eq. 0) ) goto 1070 ! error
          do i= 1, NumPerLine(ReadVarNum)
            if ( .not. FileCmnd.CmndNumOK(ent1+i) ) goto 1080 ! error
          end do
          NumFrm= NumFrm + 1
        end if
      end do  
300   close(unit=2)              
      if ( ReadVarNum .lt. 4 ) then
        MP.nfrm= NumFrm
        MP.frm_rate= 51.0
        MP.num_cycle= 1
        MP.first_frm= 1
        MP.cycle_first_frm(1)= 1
        MP.cycle_last_frm(1)= NumFrm
        MP.cycle_length(1)= NumFrm
      end if
      if ( (ReadVarNum .eq. 2) .or. (ReadVarNum .eq. 3) ) then
        if ( JntText .eq. 'RLG' ) then
          do i= 1, NumFrm
            do j= rtak, rthp
              JntCalcOK(i,j)= .true.
              JrlCalcType(i)= -1
            end do
          end do ! j
        else if ( JntText .eq. 'LLG' ) then
          do i= 1, NumFrm
            do j= lfak, lfhp
              JntCalcOK(i,j)= .true.
              JrlCalcType(i)= -1
            end do
          end do ! j      
        else
          do i= 1, NumFrm
            JntCalcOK(i,JntNum)= .true.
            JrlCalcType(i)= -1
          end do
        end if
      end if
      if ( ReadVarNum .eq. 1 ) then
        JF.angle_JCS= .true.   
      else if ( ReadVarNum .eq. 2 ) then
        JF.Load= .true.
        if ( (JntText .eq. 'RLG') .or. (JntText .eq. 'LLG') )
     &    JF.legsummed= .true.
      else if ( ReadVarNum .eq. 3 ) then
        JF.Power= .true.
        if ( (JntText .eq. 'RLG') .or. (JntText .eq. 'LLG') )
     &    JF.legsummed= .true.
      else if ( ReadVarNum .eq. 4 ) then
        FF.read= .true.
        FF.individual_feet= .true.
        FP.nfrm= NumFrm
        do i= 1, NumFrm
          FtGrlOK(i,side)= .true.
        end do
      else if ( ReadVarNum .eq. 5 ) then
        EF.Read= .true.
        EP.NumSamples= NumFrm
        EP.SampleRate= 1.0
        EP.Name(chnl)= 'Imported'
        if ( chnl .gt. EP.NumChnl ) EP.NumChnl= chnl
      else if ( ReadVarNum .eq. 6 ) then
        call zero_motion_flags( MF,SF,JF,AF,CF )
        call zero_force_flags( FF,EF )
        MP.nfrm= NumFrm
        MF.read= .true.
        MF.adjusted= .true.
        SF.param= .true.
        SF.position= .true.
      end if                 
      write(6,400) NumFrm
400   format(' ',i5,' frames of data read from file')
999   return          
                
1000  write(6,1005) CMND.NumCmnd
      if ( OF.ToLog ) write(3,1005) CMND.NumCmnd
1005  format(' Error- There are ',i2,' entries in the command line but a'/
     &       '        minimum of 4 are required')
      return

1010  write(6,1015) CMND.CmndText(3),(ReadVarName(i),i=1,NumReadVar)
      if ( OF.ToLog ) write(3,1015) CMND.CmndText(3),
     &                                      (ReadVarName(i),i=1,NumReadVar)
1015  format(' Error- ',a3,' is not a valid text import quantity'/
     &       '        Valid quantities are: ',6(1x,a3))
      return

1020  write(6,1025) JntText,(jnt_names(i),i=1,MAX_JNT)
      if ( OF.ToLog ) write(3,1025) JntText,(jnt_names(i),i=1,MAX_JNT)
1025  format(' Error- ',a3,' is not a valid joint name'/
     &       '        Valid names are: RLG LLG '/3(23x,5(a4,1x)/))
      return

1030  write(6,1035) CMND.CmndText(4)
      if ( OF.ToLog ) write(3,1035) CMND.CmndText(4)
1035  format(' Error- ',a3,' is not a valid side'/
     &       '        Valid side names are: RT LF')
      return

1040  write(6,1045) CMND.CmndText(4),MAX_EMG
      if ( OF.ToLog ) write(3,1045) CMND.CmndText(4),MAX_EMG
1045  format(' Error- ',a3,' is not a valid emg channel number'/
     &       '        Valid channels are 1 to ',i2)
      return

1060  write(6,1065)
      if ( OF.ToLog ) write(3,1065)
1065  format(' Error- Text file reading not successfull')
      return                                                    

1070  write(6,1075) ReadVarName(ReadVarNum),NumPerLine(ReadVarNum),NumFrm,
     &              FileCmnd.NumCmnd
      if ( OF.ToLog ) write(3,1075) ReadVarName(ReadVarNum),NumFrm,
     &                        NumPerLine(ReadVarNum),FileCmnd.NumCmnd
1075  format(' Error- Reading in ',a3,' requires ',i2,' numbers/line'/ 
     &       '        But line #',i4,'of the file has only ',i2,' numbers')
      close(unit=2)
      return

1080  write(6,1085) i,FileCmnd.CmndText(i),NumFrm
      if ( OF.ToLog ) write(3,1085) i,FileCmnd.CmndText(i),NumFrm
1085  format(' Error- Entry #',i2,' (',a3,') on line #',i4,' of the file'/
     &       '        is not a valid number') 
      close(unit=2)
      return

1090  write(6,1095) ierr,NumFrm
      if ( OF.ToLog ) write(3,1095) ierr,NumFrm
1095  format(' Error- Error #,'i4,' occured while reading line # ',i4/
     &       '        Text file reading not successfull')
      close(unit=2)
      return
      end
