// ******************************************************************
//  Benthos.c	 : 17Jan2010  LAR
//  17Jan10 -- Use 2010-05-31 Benthos.c for TT8 Spray as a basis to
//                 create a version for CF2 controller.  
//                 The Benthos Modem uses the TPU port
//                 used by ADP in normal (non-Acoustic modem) Spray.
//                 Power to the modem is controlled by the 
//                 Aux power switch normally used by the Altimeter
//  15Jun10 -- Replaced calls to tget with calls to CF2 tgets
//  23AUG10 -- BDJ Added the AT$BS command to the return buffer (sent 
//                 back to groundstation) in XferSubBlk()
//  10FEB11 -- BDJ check to make sure we dont already have the msg 
//                 before starting wait in XferSubBlk()
//  30Nov15 -- LAR Removed btmsk from struct ACM. No bit masking is now 
//                 available to get individual 246 byte sub-blocks
//  30Aug16 -- LAR Added service-wdog() to function tgetac.  Dog gets 
//                 tickled on every command or read of string from REMOTE
//                 Hopefully this will stop the ABORTs during ACM operations.
//  31Aug16 -- JTS incorporated in LAR changes, +added more service-wdog.

// ******************************************************************

/*
#include  <TT8.h>		
#include  <tat332.h>	
#include  <sim332.h>	
#include  <qsm332.h>	
#include  <tpu332.h>	
#include  <dio332.h>	
#include  <tt8pic.h>	
#include  <tt8lib.h>
#include  <userio.h>
#include  "lowpow.h"
#include  "cf1.h"
#include  "eeprom.h"
*/	
#include  <cfxbios.h>		// Persistor BIOS and I/O Definitions
#include  <cfxpico.h>		// Persistor PicoDOS Definitions
#include  <stdio.h>
#include  <stdlib.h>
#include  <string.h>
#include  "gpio_mux.h"
#include  "setup.h"
#include  "sbd.h"
#include  "spi_cmd.h"
#include  "BenthCF2.h"
#include  "lifo.h"
#include  "miss_fnx.h"

//acoustic modem uses same ports as ALT/ADP 
#if BENTHOS==1 || NPS==1
// PROCEED ...#endif is at the end of this file

//external declarations
extern short acm_xmits;             //number of commands sent to the remote
extern union CS CS0, CS1, CS2;
extern short TSerInUse;  //GLOBAL defined in start.c for Benthos
                  // =1 if TSerial port of TPU is in use, else 0
                  //Used to prevent code from changing System clock or turning off
                  //the oscillator or TPU in low power states.To do so messes up 
                  //TSerial interrupt servicing and baud rates.
extern struct acm_command modem_cmd_str; //command string for remote modem (start.c)
extern struct all_param all;  //defined in cfxmain.c
extern char   Verbose;  // switch for displaying more info
extern short  iparam [ MAX_PARAM ]; // global parameter settings
long ReadCharsFromConsole(char *buffer, long n);//CFxPicoPreMain.c
extern short TSerInUse;  //GLOBAL defined in start.c for Benthos
                  // =1 if TSerial port of TPU is in use, else 0
                  //Used to prevent code from changing System clock or turning off
                  //the oscillator or TPU in low power states.To do so messes up 

//internal declarations
short acm_is_on;  //1 if acm is on and ready to talk
short REM_TO=14; //time out secs for 1st char of remote modem response
                //14 secs allows one-way range=7875m
                //The value is adjusted in acm_Power_Up to be compatible with
                //LOCAL setting of Sreg7
#define MAXREPLY 500
char REMrep[MAXREPLY+1];//buffer used by cmd_acm to recv reply from REMOTE

void menu_acm(void);
//short GetTSdata( struct acm_param *acm );  //Read TSer interrupt buffer to Spray RAM
  
char s_NoLOCresp[]= "No LOCAL resp to %s:\n";  //a string used often in diagnostics 

short acm_off() // turn off acoustic modem
{  //ALWAYS returns 0 (assumes success)
   short istat = 0;
   struct spi_param *ss;
   ss = all.sspi; // points to the spi comms struct
   setup_spi ( CS_GPIO, ss );
   
   istat = spi_aux_pwr( -AUX_ON_ALT, ss );  //turn off power
   if (Verbose) cprintf("Err Stat off_acm = %d\n", istat);
   DelayMilliSecs(10);  // wait 
  
   istat = spi_mux( 0, SER_ADP, ss ); // turn off rs232, returns TRUE if ok
   if (!istat) cprintf("Err Stat off_mux\n");
   //istat==TRUE if command was accepted ok.
   //return ( !istat );  // returns 0 if spi_mux cmd was OK
   return ( 0 );   //But per TT8 version, this is ignored
   
} // end acm_off


short start_acm(void) // power up and initialize acm
{  //On success returns 
   //         0 = success
   //On errors returns:  
   //        -2 = no response from LOCAL modem
   short i=0,nread;
   char reply[200];
   short istat; 
   short found;
   char s[]="\n err on ACM %s chan = %d \n";
  
   struct spi_param *ss;
   TUPort    *tup;

   tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP
     
   ss = all.sspi; // points to the spi comms struct
   setup_spi ( CS_GPIO, ss );
   
   //ACM uses ADP subchannel of serial MUX1 with Baud=BAUD_MUX1
   istat = spi_mux( 1, SER_ADP, ss ); // turn on rs232 & configure mux
   if ( !istat )
     cprintf("Err Stat mux_adp\n" );
     
   istat = spi_aux_pwr( AUX_ON_ALT, ss ); // modem now has power
   if ( istat != SPI_CRC_OK )
   {  // error sending the 'ON' command
      cprintf("Err Stat on_acm = %d\n", istat);
      return ( -2 );//error=no response from Local
   } 
   acm_is_on=1;  //the acm is on and set up for communication

   DelayMilliSecs(10000); // 10 second delay to let settle
   
   //Give up to 15 seconds for Benthos to send its wakeup display:
      /*
     
      Benthos ATM-88X
      LF Frequency Band
      Standard version 5.6
      Warning battery low
      >
      */
   //12Feb09  nread = tgetac( ACM_FROM,reply,198,15 );
   //15Jun10  nread = tget( ACM_FROM,reply,198,15 );
   nread = tgets( tup, reply,198,15,NO_EOL,&found); 
  
   if( nread==0 )
     return(-2);  //no wakeup banner received
   else
     {printf("%s\n",reply);
      return(0);   //got something so assume awake
     }
}  // end start_acm

short acm_Power_Up( void )
 {// Turn on power, initialize LOCAL acoustic modem, and get a good response
  // return 0=OK, 
  //       -1=error in starting -- leaves acm off

  short i=0, err=1,nread;
  uchar dbg[80];
	    
  while ( (i++<3) && (err!=0) )
  { // try up to 3x to power up modem & get an ACK
    err = start_acm(); // turn on acm and initialize
    if (err <0) 
    { //debug printf("start_acm fail--try again\n");
      acm_off(); 
      DelayMilliSecs(5000); 
    }  //try power reset
  } // end while trying to start
  if ( err != 0 )return(-1); //unable to start LOCAL

  //make 2 attempts to get ACK from modem        
  while ( (i++<2) && (err!=0) )
  { // try to initialize and get correct response
    err = ack_acm();  //see if it's awake
    if (err<0) // if still no response, try once more
    { DelayMilliSecs(500); err= ack_acm(); 
    }
  }
  if(err != 0)
  { //no contact with LOCAL is fatal
    acm_off();  //Power down & release serial port
    return(-1); //return error
  }
    
  //initialize the state of ac modem
  // 09July2009  Ken Scussel of Benthos suggests the values of   debug
  //    Sreg2, Sreg7, Sreg10, Sreg13, Sreg15, Sreg16 on each dive   debug
  // Setting of Sreg2 on LOCAL has no effect on auto-retry of blocks so don't care 
  //10Jul09 removed cmd_acm("ATS11=1\0",REMrep,MAXREPLY,2,1); //enable RTS handshake
  cmd_acm("ATS7=35\0",REMrep,MAXREPLY,2,1); //acoustic response timeout  debug get default from EEPROM???
  cmd_acm("ATS10=255\0",REMrep,MAXREPLY,2,1); //Idle time=255 Don't go to low power
                                              //since Spray switches the power.
  cmd_acm("ATS12=04\0",REMrep,MAXREPLY,2,1); //ATX uses 512 bit test message
  cmd_acm("ATS13=03\0",REMrep,MAXREPLY,2,1); //code needs maximum verbosity on messages
  cmd_acm("ATS15=00\0",REMrep,MAXREPLY,2,1); //powerup in command mode
  cmd_acm("ATS16=00\0",REMrep,MAXREPLY,2,1); //display data packets containing errors
  SetSreg(-1,6,(short)iparam[IA_acm_lxmt],1 );  //set xmit power level of Local per EEPROM default
  //assume that commands were accepted   debug
  
  //read Sreg7 = acoustic time out LSB=.5 second & set global REM_TO
  if( GetSreg( -1, 7, &nread,1) == 0)
  { REM_TO=nread/2+5;  //set REM_TO to be 5 second longer
    if (REM_TO < 10)REM_TO=10; //lower bounds
    if (REM_TO > 30)REM_TO=30; //upper bounds
  }
  sprintf(dbg,"acm_Power_Up       setting REM_TO=%d\n",REM_TO);
  printf("%s",dbg);//print diagnostic
  buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1 
  return(0);  //success
 } // end acm_Power_Up




short  tgetac ( TUPort  *tup,  char *str,  short n, short tmo, short gap_s) 
{ // get string from TPU Port *tup : Returns when either:
  // n characters are read in 
  // tmo = #sec to wait for 1st char
  // gap_s = time out gap between characters
  
  // returns # of bytes actually read
  // the string is in *str
  
   char c=0, *cs = str; 	//* ch points to str buffer
   short i=0;  	//* i=# characters read in so far
   short ms, ok=1, reply;
   ulong tnow, tstart; 
   long  dt;
   
   if (tmo>30) tmo=30; // limit time-out
   if (gap_s >30) gap_s=30; // limit time-out
   ms = tmo*1000; // #ms for the first char
   //if (Verbose) cprintf("tgets wait = %d ms\n",ms);
   
   //== aug16, with 30s tmo, up to 500 char, this could take awhile!
   service_wdog(); // make sure the wdog has been serviced.
   tstart   = RTCGetTime(0,0);// present UX time

   while ( ok ) //=====================================================
   { //keep reading while ok

	   reply = TURxGetByteWithTimeout( tup, ms ); //get a byte
	   // cprintf("%02x ",reply); //value>0 unless not rcv'd, then=-1
	   ok = ( reply>=0 ); // good char, store
	   if (ok) //------------------------------------------------
	   {  
	      c = reply;  // store as char
	      *cs++ = c;  // store the byte
	      i++;        // increment the counter
	      ms = gap_s*1000;    // time out on gap_s seconds gap
	      ok = (i<n); // make sure still room for next
	   } //------------------------------------------------------
	   tnow = RTCGetTime(0,0);// present UX time
	   dt   = tnow - tstart;
	   if ( dt > 300 ) //........
	   { // aug16 service the wdog
	     tstart = tnow;
	     service_wdog();
	   } //......................

   } 
   //==================================================================

   
   *cs = '\0';  //* tack on the end of string marker
   //if (Verbose) cprintf("tgets #bytes=%d, found=%d\n", i, *found);
   
   return (  i );
} //end tgetac

short cmd_acm(char s_cmd[],char s_rply[],short maxlen,short to_secs,short gap_s) //send s_cmd to remote modem
 {//Send s_cmd string (Null terminated) to an acoustic modem.  This routine works with
  //sending s_cmd to either a LOCAL or REMOTE modem; the difference is only in the
  //contents of the s_cmd string.
  //The local modem sets RTS=LO, ie ModemBusy()=1, while local is waiting for response to
  //the remote.
  //Wait for up to to_secs seconds for a response and put up to maxlen bytes into s_rply
  //Be sure to include Round trip sound transit time in to_secs if the command is 
  //going to a REMOTE modem
  //Quit when the gap between successive bytes exceeds gap_s seconds
  
  //The error message parsing assumes that error messages
  //are displayed (LOCAL register S13=3).
  //On success returns actual length stored in REMrep (>0)
  //         0 = success but zero length response
  //On errors returns:  
  //        -1 = illegal command
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response

  short nread,tleft;
  ulong tstrt,tend;
  TUPort    *tup;

  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP

  s_rply[0] = '\0';nread=0; // set reply to NULL str

  if ( !acm_is_on ) return(-2);  //exit immediately if acm not ON
  
  TURxFlush  ( tup );   // flush the tpu port input buffer
  tputs( tup, s_cmd); // send command string 
  if(  strstr(s_cmd,"$")  !=NULL || 
       strstr(s_cmd,":")  !=NULL ||
       strstr(s_cmd,"ATR")!=NULL ||
       strstr(s_cmd,"ATX")!=NULL ||
       strstr(s_cmd,"ATY")!=NULL    ) //if is a command for the remote
  {acm_xmits++;} //increment the query
  
  tstrt = RTCGetTime(0,0);  //time when command was sent in seconds
  tend = tstrt+to_secs;  //allow to_secs seconds for 1st char

  nread = tgetac( tup,s_rply,maxlen,to_secs,1 ); 
  //12Feb09nread = tget( ACM_FROM,s_rply,maxlen,to_secs ); //look for \r\n
  //debug  printf("cmd_rem1\n");debug_string( s_rply, nread);  
  
  if ( nread <= 0 )
  { //LOCAL didn't respond and neither did REMOTE
    printf(s_NoLOCresp,s_cmd);  //"No LOCAL resp to %s:\n"
    return(-2);
  }
  
  //We got something from the LOCAL 
  if ( strstr(s_rply,"Invalid Command") != NULL )
  { printf("Invalid command: %s\n",s_cmd);
    return(-1);
  }
  
  //Normal response is "\r\n" from the LOCAL to ack receiving the command from serial port;
  //Usually this happens in about 1 second, but we have seen the LOCAL not respond for at
  //least a few seconds if the xducer is hearing a lot of noise.  
  //In any case there will be more response following.
  //The LOCAL returns prompt "\r\n>" at the end if the response is complete
  tleft = (short)( tend - RTCGetTime(0,0) );  //seconds left until timeout expires since command sent
  if ( tleft > 0 && strncmp(&s_rply[nread-3],"\r\n>",3)!=0 )
  { //wait for more that will be coming
    nread +=  tgetac( tup,&s_rply[nread],maxlen-nread,tleft,gap_s ); 
    //debug  printf("cmd_rem2\n");debug_string( s_rply, nread);
  }
  
  //At this point the buffer should have either an error message from the local, an error
  //message from the REMOTE, or the response from the REMOTE.
  if ( strstr(s_rply,"Error in header") != NULL )
  { //LOCAL detected bad header from the REMOTE.  The REMOTE thinks everything is OK
    //so more might be coming in but the LOCAL will send"Response" err and will ignore it
    tleft = (short)( tend - RTCGetTime(0,0) );  //secs left until timeout expires since command sent
    nread +=  tgetac( tup,&s_rply[nread],maxlen-nread,tleft,gap_s ); 
    //debug  printf("cmd_rem3\n");debug_string( s_rply, nread);
  }

  if ( strstr(s_rply,"Response Not Received") != NULL )
  { //LOCAL got nothing from the REMOTE.  Either the REMOTE didn't get the command
    //or the LOCAL didn't hear its response.
    printf("\nNo REM resp to %s:\n",s_cmd);
    //if the REMOTE has retry enabled and this was a data transfer, REMOTE could
    //try again
    return(-3);   //remote not responding
  }
  
  return ( nread );  //return bytes stored into s_rply
 }  // end cmd_acm
 
short ack_acm(void) //send AT to modem & look for '\r\nOK\r\n>'
 {//On success returns
  //         0 = success 
  //On errors returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem or LOCAL not ON
  //        -4 = unexpected,illegal, or unparsable response
  short nread,to=1;
  nread=cmd_acm("AT",REMrep,MAXREPLY,to,1); //send"AT" to acoustic modem
  
  if ( nread<0 )
   {return(nread);  //return negative error code on failure
   }
  else
   {//printf("\n");debug_string( sget, nread);  //debug display response 
    REMrep[19]='\0';//just in case too long or no NULL

    if( strncmp(REMrep,"\r\nOK\r\n>",7)==0 )
      return(0);  //success
    else
      return(-4);  //unexpected response
   }
 }  // end ack_acm

void Transparent_ACM( void )  //Added special for updating Benthos firmware
{ //A Transparent path between Spray's serial COMM port and the
  //Benthos modem.  Quits when both ends are idle for COM_to seconds.
  //Assumes the LOCAL modem is powered up and leaves it on when done.
  short COM_to, ifecho;
  char c;
  unsigned long now,tl_com;
  TUPort    *tup;

  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP
  
  /*acm_off();
  printf("Power Up ");fflush(stdout);
  err=acm_Power_Up(); //make sure LOC is powered up
  if (err>=0)
    printf("OK\n");
  else 
  { printf("Err=%d\n",err);
    return;
  }*/
 
  SerInFlush();  //flush Spray COMM input buffer
  printf("Idle channel timeout (seconds)? ");fflush(stdout);
  scanf("%d",&COM_to);
  printf("Local echo of Spray output (y/n)? ");fflush(stdout);
  ifecho=YesNo();

 
  TURxFlush  ( tup );   // flush the tpu port input buffer
  SerInFlush();  //flush Spray COMM input buffer
  
  //transfer bytes in both directions until neither end has transmitted
  //for tl_com seconds
  now=tl_com=RTCGetTime(0,0);
  while( (short)(now-tl_com) <= COM_to )
  { if( SCIRxQueuedCount() )
    { //xfer byte waiting at Spray COMM port to AC modem
      c = (char)SCIRxGetChar();
      TUTxPutByte ( tup, c, 0);   
      if(ifecho)SCITxPutChar(c);  //provide local echo
      tl_com=RTCGetTime(0,0);  //secs of last char from COMM
    }
   
    if( TURxQueuedCount(tup) )
    { //xferbyte waiting at AC modem to Spray COMM port
	    c = (char)TURxGetByte(tup,FALSE);  	//read in character
	    SCITxPutChar(c);
      tl_com=RTCGetTime(0,0);  //secs of last char from modem
    }
    
    now=RTCGetTime(0,0); 
  }
  printf("Channel has been Idle for %d seconds\n",COM_to);
  
  return;
}  //end Transparent
 
short all_Sregs( short remID ) //Fill string with S-register status
 {//Asks remote modem remID for contents of all Sregisters.
  //stores up to MAXREPLY chars into global REMrep, terminates with NULL char
  //If remID <0, asks the local modem
  //On success returns actual length stored in sreg (>0)
  //         0 = success but zero length response
  //On errors returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response
  //        -4 = unexpected,illegal, or unparsable response

  short nread,to=1,n;
  char *r_ptr,cmd[8];

  if ( remID < 0 )
   {//ask the local modem for its Sregisters
    if( (nread=cmd_acm("ATS?",REMrep,MAXREPLY,to,1))>0 ) //"ATS?" gets Sreg 0 thru 20
     {n=cmd_acm("ATS22?",&REMrep[nread],MAXREPLY-nread,to,1); //"ATS22?" gets 22 thru 40
      if(n>0)
        nread += n;  //total bytes read in S00 through S40
     }
    else
     {return(nread);
     }
   }
  else
   {//ask the remote modem for its Sregisters
    sprintf(cmd,"AT$S%02d\0",remID);  //create the command string
    nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO,5); //send"AT$Snn" to remote modem gets 0 thru 20
   }

  if ( nread <0 )
   {return(nread);  //return failure code
   }
  else
   {//compact the response
    r_ptr=strstr(REMrep,"S00=");  //look for S00= in reply
    if(r_ptr == NULL )
     {//bad response
      return(-4);
     }
    else
     {//legal Sreg reply--shuffle "S00=" to char[0]
      nread=(short)(&REMrep[nread]-r_ptr);  //chars to shuffle
      memmove(REMrep,r_ptr,nread);
      REMrep[nread-3]='\0';  //terminate with NULL char
      return ( strlen(REMrep) );
     } 
   }
 }  // end all_Sregs

short GetSreg( short remID, short regno , short *value , short Ntry )  //Read specified S register
{ //Asks remote modem remID for contents of Sregister regno and fills value.
  //If remID <0, asks the local modem
  //Make Ntry attempts before returning for REMOTE modem.  One try for LOCAL
  // return 0=ok
  //On errors returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem
  //        -3 = REMOTE no response
  //        -4 = can't parse response
  //14Jul09 version only asks for the single register desired rather that
  //        asking for all registers and parsing out the desired one
  short nread,temp,err,n;
  char cmd[14],*ptr;

  if( regno < 0 )
  { //illegal register number
    printf("Ill. Sreg:%d\n",regno);
    return (-1);
  }

  if( remID == -1 )
  { //get Sreg from LOCAL modem
    sprintf(cmd,"ATS%02d?\0",regno);
    nread=cmd_acm(cmd,REMrep,MAXREPLY,2,1); //send cmd to local modem
    if ( nread < 0 )
    { //unable to read Sregs
      return(nread);  //return error code
    }
    else
    { //printf("\ndebug GetSreg\n");debug_string( REMrep, nread);  //debug display response
      nread=sscanf(REMrep,"%d",&temp);  //parse the value into temporary value
      if(nread==1)
      { *value=temp;//success in extracting value, return the value
        return(0);
      }
      else
        return(-4);//failed extracting the value, leaves value unchanged
    }
  }
  else
  { //get Sreg from REMOTE modem
    
    for(n=0;n<Ntry;n++)  //make Ntry attempts
    {
      sprintf(cmd,"ATEC%02d,ER%02d?\0",remID,regno);
      nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO,5); //send cmd to remote modem
      if ( nread < 0 )
      { //unable to read Sregs
        err=nread;  //return error code
      }
      else
      { //printf("\ndebug GetSreg\n");debug_string( REMrep, nread);  //debug display response
        ptr=strstr(REMrep,"Sreg");  //Response is ...SregXXXX:nnnnn  XXXX=regno,nnnnn=value
        if(ptr == NULL)err=-4;//can't parse response
        nread=sscanf(ptr+9,"%d",&temp);  //parse the value into temporary value 
        if(nread==1)
        { *value=temp;//success in extracting value, return the value
          err=0;//success
          break;
        }
        else
          err=-4;//failed extracting the value
      }
    }
    return(err);
  }
  
}  // end GetSreg

short GetBaud( short remID, short*baud,short Ntry )  //Get acoustic baud rate
 {//Asks remote modem remID for current baud rate and fills baud.
  //If remID <0, asks the local modem
  //REMOTE modems are attempted Ntry times before giving up.  LOCAL is asked once
  // return 0=ok
  //On errors returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem
  //        -3 = REMOTE no response
  //        -4 = can't parse response
  short reg4,nerr,mtry;
  
  if(remID<0)
    mtry=1;//single attempt to read LOCAL baud
  else
    mtry=Ntry;  //Make Ntry attempts
    
  if ( (nerr=GetSreg(remID,4,&reg4,mtry))==0 )  //remID<0 ==> LOCAL modem
   {//reg4 = contents of Sreg 4
    //convert to baud rate
    switch ( reg4 )
     {case 2:*baud= 140;break;
      case 3:*baud= 300;break;
      case 4:*baud= 600;break;
      case 5:*baud= 800;break;
      case 6:*baud=1066;break;
      case 7:*baud=1200;break;
      case 8:*baud=2400;break;
      default: return(-4);  //unknown baud rate
     }
    return (0);  //success
   }
  else
   {return(nerr);  //failed reading the register
   }
 } // end GetBaud 

//Commands and requests to remote modems

                      
short RemRange( short remID , short *meters , short Ntries )  //range to remote modem
 {//legal remote_IDs= 0,..,99
  //On success returns 
  //         0 = success
  //On errors returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response
  //        -4 = unexpected,illegal, or unparsable response

  short nread,err,n;
  char cmd[11],*ptr;
  float frnge=0.;//floating range
  //uchar dbg[200];

  //generate command string
  sprintf(cmd,"ATR%02d\0",remID);
   
  for(n=0;n<Ntries;n++)   //make Ntries attempts at range
  {
    nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO+5,5); //send command.Allow 5 secs of remote latency
  
    if ( nread < 0 )
    { err=nread;  //neg error code
    }
    else
    {
      frnge=-1;
      ptr=strstr(REMrep,"Range=");
      if(ptr != NULL)
      { nread=sscanf(ptr+6,"%f",&frnge);  //read the value,returns 1 if parsed OK
        if( nread==1 )
        { *meters= (short)(frnge+.5);  //round to nearest meter
          err=0;
          break;  //succeeded
        }
      }
      else
      { //printf("\n");debug_string( REMrep, nread);  //debug display response
         err=-4;  //couldn't parse the response
      }
    }
  }
  //sprintf(dbg,"RemRange %6ld %s err=%d\n",RTCGetTime(0,0),cmd,err);
  //printf("%s",dbg);
  //buf_to_cf1V( strlen(dbg),dbg);
  return(err);
 }  //end RemRange
 
short DataBy(short remID , long *nbytes , short Ntry )  //# data bytes in remote modem
 {//legal remote_IDs= 0,..,99
  //If remID <0, asks the local modem
  //On success returns
  //         0 = success
  //On errors returns *nbytes=4096 and:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response
  //        -4 = unexpected,illegal, or unparsable response
  short nread,err,n;
  char cmd[11],*ptr;
  float frnge=0.;//floating range
  //uchar dbg[200];

  *nbytes=4096L;  //fills in a value in case we can't get an actual value
  
  if( remID < 0 )
  {//LOCAL modem
    nread=cmd_acm("ATBN\0",REMrep,MAXREPLY,5,1); //send command.Allow 5 secs of latency
  }
  else
  { //REMOTE modem
    //generate command string
    sprintf(cmd,"AT$BN%02d\0",remID);
    
    for(n=0;n<Ntry;n++)  //Ntry attempts with REMOTE
    {
      nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO+5,5); //send command.Allow 5 secs of remote latency
  
      if ( nread < 0 )
      { err=nread;  //negative error code
      }
      else
      { //printf("\n");debug_string( REMrep, nread);  //debug display response
        *nbytes=-1;
        ptr=strstr(REMrep,"#bytes stored = ");
        ptr +=15;  //advance to start of byte count
        nread=sscanf(ptr,"%ld",nbytes);  //read the value 
                                        //returns 1 if parsed OK
        if( nread==1 )
        { err=0;  //response parsed
          break;
        }
        else
          err=-4;  //couldn't parse the response
      }
    }
  }
  //sprintf(dbg,"DataBy   %6ld %s err=%d\n",RTCGetTime(0,0),cmd,err);
  //printf("%s",dbg);
  //buf_to_cf1V( strlen(dbg),dbg);
  return(err);
 }  //end DataBy

short GetBatt(short remID ,float *xmitV,short Ntry) //read battery of remote modem
 {//Asks remote modem remID for battery voltage
  //If remID <0, asks the local modem
  //Makes Ntry attempts for a REMOTE modem, one for LOCAL
  //On success returns
  //         0 = success
  //On errors returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response
  //        -4 = unexpected,illegal, or unparsable response

  //*xmitV = (float)transmit battery in VDC
  short nread,err,n;
  char cmd[11];
  char *r_ptr;
  //uchar dbg[200];

  *xmitV=99.99;  //set a flag value until the real voltage is read successfully
  
  if ( remID < 0 )
  { //LOCAL modem battery voltage
    nread=cmd_acm("ATV",REMrep,MAXREPLY,5,2); //send"ATV" to acoustic modem
    if ( nread < 0 )
    { err=nread;  //return negative error code
    }
    else
    { //printf("\n");debug_string( REMrep, nread);  //debug display response 
      //check for format and parse out the voltages
      r_ptr=strstr(REMrep,"Xmit Bat = ");  //look for Bat line
      if ( r_ptr != NULL )
      { //found Xmit voltage
        if( sscanf((char*)(r_ptr+10),"%fV",xmitV) == 1 )
        { err=0;  //OK
        }
        else
        { err=-4; //Xmit read failed
        }
      }
      else
      { err=-4; //Xmit read failed
      }    
    }
  }// end local modem batts
  else
  { //remote modem battery voltage
    sprintf(cmd,"AT$V%02d\0",remID);  //generate command string
    for(n=0;n<Ntry;n++)  //Ntry attempts
    { nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO+5,10); //send"AT$V" to remote
                                       //send command.Allow 5 secs of remote latency
      if ( nread < 0 )
      { err=nread;  //return negative error code
      }
      else
      { //printf("\n");debug_string( REMrep, nread);  //debug display response 
        //check for format and parse out the voltages
        r_ptr=strstr(REMrep,"Xmit Bat = ");  //look for Bat line
        if ( r_ptr != NULL )
        { //found Xmit voltage
          if( sscanf((char*)(r_ptr+10),"%fV",xmitV) == 1 )
          { err=0;  //OK
            break;
          }
          else
          { err=-4; //Xmit read failed
          }
        }
        else
        { err=-4; //Xmit read failed
        }
      }
    }
  }  //end remote modem batts
    
  //sprintf(dbg,"GetBatt  %6ld %s err=%d\n",RTCGetTime(0,0),cmd,err);
  //printf("%s",dbg);
  //buf_to_cf1V( strlen(dbg),dbg);
  return(err);
 } // end GetBatt 
 
short AmbAGC(void)    //Ambient AGC level
{ //15Jul08 returns LOCAL AGC before talk to remote to estimate ambient noise
  short n,nread,AGCavg,agc;
  uchar dbg[60],*ptr,trs[40];
  
  nread=cmd_acm("ATTN1\0",dbg,40,5,5); //ATTN1 starts AGC display
    //40 chars = 16 char header + 6 readings (4chars each) at one second intervals
    //1st reading from Benthos could be old so trash it
 
  n=cmd_acm("ATTN0\0",trs,39,2,2); //ATTN0 stops AGC display
    
  ptr=strstr(dbg,">");  //look for ">" just before 1st AGC reading
  AGCavg=0;
  if ( ptr !=NULL)
  { //found the AGC readings
    ptr++;  //advance to 1st reading
    printf("AGC: %s\n",ptr); //display raw AGC readings
    ptr+=4; //skip 1st reading in the average as it may be old
    for(n=0 ; n<5  ;n++ )
    { nread=sscanf(ptr,"%d",&agc);
      if(nread == 0 )break;
      AGCavg += agc; 
      ptr +=4;//advance to next reading
    }
    if(n>0) 
      AGCavg=(float)AGCavg/n+.5;
    else
      AGCavg=-1.;
  }
  return(AGCavg);
}  //end AGCamb
   
short LinkQ(short remID,short *nbytes,struct acm_param *acm)  //get data from remote modem
 {//On success returns
  //         0 = success
  //On errors returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response
  //        -4 = unexpected,illegal, or unparsable response

  //Get the quality status of the acoustic link to remote modem remID.
  //Put up to MAXREPLY bytes of response into global array REMrep.  The response string
  //is NULL terminated ASCII. Maxby must allow room for the NULL.
  //nbytes returns length of the reply
  //Example of interrogation response(161 bytes):
  //  00140 bits/sec
  //  Acquisition stats SNR:-05.1 MPD:01.5 SPD:+00.0
  //  Header stats CRC:Pass SNR:23.5 CCERR:013
  //  MOD:02 ERR:000 SNR:23.0 AGC:16 SPD:+00.0 CCERR:013
  //  
  //The baud rate is determined by S4 of the remote and is encoded in MOD.
  //The length of the test message is determined by S12 of the local modem .

  //fills acm->baud with the baud rate of the remote modem & other test results
    
  short nread,tlen;
  short found;
  char cmd[7], *ptr;
  float f_inp;
  TUPort    *tup;

  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP

  *nbytes=0;//nothing has been read yet
  //Clear the >acm structure so won't confuse new and old contents
  acm->baud = acm->tst_bytes = 0;
  acm->SourceID = acm->CRC = acm->mpd = acm->snr =999;
  acm->agc = acm->spd = acm->ccerr=999;
  acm->ber  = 9999;
       
  //The local Sreg12 determines the length of the test message
  if( GetSreg(-1, 12, &tlen ,1 )==0 )
  { //success reading S12, parse the coded value
    switch (tlen)   //get number of bits in test message 
    { case 0: case 128: tlen=8;break;  //8 bytes
      case 1: case 129: tlen=32;break; //32 bytes
      case 2: case 130: tlen=128;break; 
      case 3: case 131: tlen=256;break; 
      case 4: case 132: tlen=512;break; 
      case 5: case 133: tlen=1024;break; 
      case 6: case 134: tlen=2048;break; 
      case 7: case 135: tlen=4096;break; 
      default: tlen=1;
    }
  }
  else
  { //failed to get S12
    tlen=1;//default value
  }
  acm->tst_bytes=tlen;  //number of bytes in test message
  
  //generate command string
  sprintf(cmd,"ATX%02d\0",remID);

  nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO+16,5); //send"ATXn" to remote
  if (nread < 0)
   {return(nread);//return neg error code
   }
 //The local Spray modem returns "\r\n" when it accepts the command. When it
  //receives the header for the test message from the remote, it returns the baud rate.Then 
  //there is a delay since the local modem then doesn't return anything until the test 
  //message has been completely received.  For an 8 byte test (S12=0) this takes
  //0.5 second @ 140 baud.  For an 128 byte test (S12=2) this takes 7.5 second @ 140 baud.

  //got something from remote
  //The first 16 bytes should be "\r\nBBBBB bits/sec" where BBBBB=remote baud rate
  //We can use the baud rate to estimate how long the remote will take to send the
  //test message of acm->tst_bytes bytes
  ptr=REMrep; //start at beginning of link status reply
  ptr=strstr(ptr,"bits/sec");  //look for "bits/sec"
  if(ptr == NULL )return(-4);//bad format
  sscanf(ptr-6,"%d",&acm->baud);  //baud rate of remote
  tlen=(short)((8L*acm->tst_bytes)/acm->baud)+3;  //seconds to xmit test message
  //listen for up to tlen secs for message to quit (in case it hasn't)
  //12Feb09  nread += tgetac( ACM_FROM,&REMrep[nread],(short)(MAXREPLY-nread),tlen,1);
  //15Jun10  nread += tget( ACM_FROM,&REMrep[nread],(short)(MAXREPLY-nread),tlen);
  nread += tgets( tup,&REMrep[nread],(short)(MAXREPLY-nread),tlen,NO_EOL,&found);
  //the Spray Benthos modem prepends an \r\n to start and appends an \r\n> to the
  //reply from the remote modem.  Remove these 5 extra bytes so buffer has
  //only the data stream from the remote
  if ( !strncmp(REMrep,"\r\n",2) )
   {memmove(REMrep,&REMrep[2],nread-2);//trim initial \r\n
    nread -= 2;//correct the length
    if ( !strncmp(&REMrep[nread-3],"\r\n>",3) )
     {//\r\n> as expected at end--trim them by
      nread -= 3;//correcting the length and
      REMrep[nread]='\0';  //terminating with NULL char
      *nbytes=nread;  //number of bytes in REMrep
        
      //parse the response and fill LinkQ area in ACM structure with
      //baud,errct,snr,agc,spd, and ccerr
      //Example of interrogation response(161 bytes):
      //  00140 bits/sec
      //  Acquisition stats SNR:-05.1 MPD:01.5 SPD:+00.0
      //  Header stats CRC:Pass SNR:23.5 CCERR:013
      //  MOD:02 ERR:000 SNR:23.0 AGC:16 SPD:+00.0 CCERR:013
      //  
      ptr=strstr(ptr,"MPD:");  //look for "MPD:" in message stats
      if(ptr == NULL )return(-4);//bad format
      sscanf(ptr+4,"%f",&f_inp);  //multipath delay in floating millisecs
      acm->mpd=(short)(10*f_inp);  //LSB = 0.1 millisec
       
      ptr=strstr(ptr," ERR:");  //look for " ERR:" in message stats
      if(ptr == NULL )return(-4);//bad format
      sscanf(ptr+5,"%d",&acm->ber);  //count of bit errors  25Jul09 Prior to this ber
                                     //was the bit error rate computed via
                                     //acm->ber=(short)((1000L*n_inp)/(8L*acm->tst_bytes));
        
      ptr=strstr(ptr,"SNR:");  //look for "SNR:" in message stats
      if(ptr == NULL )return(-4);//bad format
      sscanf(ptr+4,"%f",&f_inp);  //SNR in floating db
      acm->snr=(short)(10*f_inp);  //LSB = 0.1 dB
        
      ptr=strstr(ptr,"AGC:");  //look for "AGC:" in message stats
      if(ptr == NULL )return(-4);//bad format
      sscanf(ptr+4,"%d",&acm->agc);  //AGC  0-95
        
      ptr=strstr(ptr,"SPD:");  //look for "SPD:" in message stats
      if(ptr == NULL )return(-4);//bad format
      sscanf(ptr+4,"%f",&f_inp);  //SPD in floating kts
      acm->spd=(short)(10*f_inp);  //LSB = 0.1 kt
        
      ptr=strstr(ptr,"CCERR:");  //look for "CCERR:" in message stats
      if(ptr == NULL )
       {//if REMOTE is 2400 baud it doesn't send CCERR so ignore
        return(0);// the bad format and leave CCERR unchanged
       }
      else
        sscanf(ptr+6,"%d",&acm->ccerr);  //CCERR 0-14
                
      return (0) ;// success
     }
    else
     {//no \r\n at the end
      return(-4);
     }        
   }
  else
   {//something wrong with format--no initial \r\n so don't trim
    //but still try to trim end
    if ( !strncmp(&REMrep[nread-3],"\r\n>",3) )
     {//\r\n> as expected at end--trim them by
      nread -= 3;//correcting the length and
      REMrep[nread]='\0';  //terminating with NULL char
     }        
    *nbytes=nread;  //number of bytes in REMrep
    return (-4) ;//only a partial success--
   }

 }  //end LinkQ
 
short SetBaud( short RemID , short baud,struct acm_param *acm ,short Ntry )  //Set acoustic baud rate on remote
 {// return 0=ok
  //        -1 = illegal remote ID
  //        -2 = no response from local modem
  //        -3 = REMOTE no response
  //        -4 = can't parse REMOTE's response
  //Sets acoustic baud rate of RemID
  //If RemID == -1, sets ac baud of local modem
  //Make Ntry attempts for REMOTE modem.  One try for LOCAL
  
  short reg4,err,nread,n;
  char cmd[24];
  uchar dbg[200];

  //lookup Sreg 4 setting to get desired baud 
  switch ( baud )
   {case 140:
      reg4=2;
      break;
    case 300:
      reg4=3;
      break;
    case 600:
      reg4=4;
      break;
    case 800:
      reg4=5;
      break;
    case 1066:
      reg4=6;
      break;
    case 1200:
      reg4=7;
      break;
    case 2400:
      reg4=8;
      break;
    default:
      return(-2);  //unknown baud
   }
   
  //send the command
  if( RemID == -1 )
   {//set acoustic baud of local modem
    err=SetSreg(-1, 4 , reg4 ,1 ); 
    return(err);
   }
  else
  { //remote RemID
    for(n=0;n<Ntry;n++)
    { //make Ntry attempts
      sprintf(cmd,"AT$A%02d,%02d\0",RemID,reg4);//create the string
      nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO+5,5); //send command with REM_TO sec timeout
                                          //allow 5 secs for remote latency
      if (nread < 0 )
      { err=nread;//return neg error code
      }
      else if ( (nread>0) && (strncmp(&REMrep[nread-7],"\r\nOK\r\n>",7) == 0) )
      { //printf("\n");debug_string( REMrep, nread);  //debug display response 
        //remote baud was changed OK. The remote determines data xfer rate so
        acm->baud=baud;//set baud rate in ACM struct 
        err=0;
        break;//success
      }
      else
      { err=-4;  //return -4 on failure to parse remote's response
      }
    }
    sprintf(dbg,"SetBaud  %6ld %s err=%d\n",RTCGetTime(0,0),cmd,err);
    printf("%s",dbg);
    buf_to_cf1V( strlen(dbg),dbg);
    return(err);
  }
 } // end SetBaud  
 
short SetPower( short RemID , short level , short Ntry )  //Set acoustic output level on remote
{ // return 0=ok
  //        -1 = illegal remote ID
  //        -2 = no response from local modem
  //        -3 = REMOTE no response
  //        -4 = can't parse REMOTE's response
  //Sets output level of RemID
  //If RemID == -1, sets level of local modem
  //If setting power on a REMOTE modem then make Ntry attempts before returning.
  //Changes of LOCAL power are always a single try.
  
  short err,nread,n;
  char cmd[24],dbg[80];

  //legal levels are 1 (-21 dB) thru 8 (0 dB=max output power)
  if( (level < 1) || (level > 8) ) return(-2);
   
  //send the command
  if( RemID == -1 )
  { //set level of local modem
    err=SetSreg(-1, 6 , level ,1 ); //single try for LOCAL
    sprintf(dbg,"CHPOW    %6ld ID=%d Pow=%d err=%d\n",RTCGetTime(0,0),RemID,level,err); 
    printf("%s",dbg);
    buf_to_cf1V( strlen(dbg),dbg);
    return(err);
  }
  else
  { //remote RemID

    for(n=0;n<Ntry;n++)
    { sprintf(cmd,"AT$P%02d,%02d\0",RemID,level);//create the string
      nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO+5,5); //send command with REM_TO sec timeout
                                          //allow 53 secs for remote latency
      if (nread < 0 )
      { err=nread;  //return negative error code
      }
      else if ( (nread>0) && (strncmp(&REMrep[nread-7],"\r\nOK\r\n>",7) == 0) )
      { err=0;  //remote power was changed OK.
        break;
      }
      else
      { //printf("\n");debug_string( REMrep, nread);  //debug display response
        err=-4;//set negative error code
      }
    }
    sprintf(dbg,"CHPOW    %6ld ID=%d Pow=%d err=%d\n",RTCGetTime(0,0),RemID,level,err); 
    printf("%s",dbg);
    buf_to_cf1V( strlen(dbg),dbg);
    return(err);
  }
} // end SetPower  

short SetSreg( short RemID ,short regno , short value , short Ntry )  //Set specified S register on remote
 {//put value into Sregister regno of remote RemID
  //If RemID == -1, sets Sreg of local modem
  //Make Ntry attempts for REMOTE modem, single attempt for LOCAL
  //On success returns 
  //         0 = success
  //On errors returns:  
  //        -1 = illegal remote ID, register, or value
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response
  //        -4 = unexpected,illegal, or unparsable response
  short nread,n,err;
  char cmd[20];

  if( regno < 0 )
   {//illegal register number
    printf("Ill. Sreg:%d\n",regno);
    return (-1);
   }

  if( RemID == -1 )
  { //set register of local modem
    if( regno<=20 )
    { sprintf(cmd,"ATS%02d=%03d\0",regno,value);
    }
    else
    { //undocumented registers >20 have special form of command
      sprintf(cmd,"ATS%02d*%03d\0",regno,value);
    }
   
    nread=cmd_acm(cmd,REMrep,MAXREPLY,1,1); //send command
    if ( nread<0 )
    { return(nread);  //return neg error code on failure
    }
    else
    { if( strncmp(&REMrep[nread-7],"\r\nOK\r\n>",7)==0 )
        return(0);  //success
      else
      { //printf("\n");debug_string( REMrep, nread);  //debug display response 
        return(-4);//unexpected response
      }
    }
  }   //End setting LOCAL register
  else
  { //not local modem--set REMOTE register using ATEC,EW command.  Note that this is a
    //nonvolatile setting of the register.
    for(n=0;n<Ntry;n++)
    { //make Ntry attempts
      sprintf(cmd,"ATEC%02d,EW%02d=%03d\0",RemID,regno,value);
      nread=cmd_acm(cmd,REMrep,MAXREPLY,REM_TO,5); //send command
      //printf("\n");debug_string( REMrep, nread);  //debug display response 
      if ( nread<0 ) err=nread;  //return neg error code on failure
    
      if( strstr(REMrep,"All values written and stored.") != NULL ) 
      { err=0;  //success
        break;
      }
      else
      { //printf("\n");debug_string( REMrep, nread);  //debug display response 
        err=-4;//unexpected response
      }
    }
    return(err);
  } 
   
 } // end SetSreg  
 
short LOCmode(void)  
{  //use undocumented "ATC" command to see what the LOCAL modem is doing
   //returns 0 if LOCAL is in acquisition mode (waiting for??)
   //        1 if LOCAL is receiving a packet from REMOTE
   //             or if the LOCAL doesn't respond(because it is busy listening)
   //        2 if LOCAL is transmitting
   //      < 0 on error
   //*****************WARNING*******************************************************
   //This function sends a command to the LOCAL MODEM using function               *
   //cmd_acm. cmd_acm flushes the TSerial input buffer at the start and then       *
   //sucks all of its output out of the TSerial buffer.  This leaves the TSerial   *
   //input buffer empty.  Anything that was in the buffer when LOCmode is called   *
   //has been lost                                                                 *
   //*******************************************************************************
   short nread;
   
   nread=cmd_acm("ATC",REMrep,MAXREPLY,5,1); //send"ATC" to acoustic modem
  
   if( nread == -2 )
   { //LOC didn't reply
     return(1);  //assume this means it is busy listening to a remote
   }
   else if ( nread<0 )
   {  return(nread);  //return negative error code on failure
   }
   else
   {  //printf("\n");debug_string( REMrep, nread);  //debug display response 
      REMrep[nread]='\0';
      if( strstr(REMrep,"Acquistion mode")!=NULL )
         return(0);  //Aquisition mode (yep, they misspelled it)
      else if( strstr(REMrep,"Receiving Packet")!=NULL )
         return(1);  //receiving
      else if( strstr(REMrep,"Transmitting")!=NULL )
         return(2);  //transmitting
      else
         return(-4);  //unknown response
   }
}  // end LOCmode

short WhatBlock( struct acm_param *ACM )
{ //Fills ACM->this_blk with the next 4K block to get from the REMOTE
  //Obtains info from the ACM struct
  //and manipulates values of  
  //new_blk, old_blk,xfer_mode,this_blk in the ACM struct
  //Returns 0 if there is data to get
  //        -1 if there is no more data blocks to get from this REMOTE
  //NEW_BLK transfers blocks incrementing by blk_inc(set from ee_acm_binc value)
  //OLD_BLK transfers blocks icrement by 1 regardless of blk_inc (ee_acm_binc)value
   
  if( ACM->new_blk <= ACM->lnew_blk )
  { ACM->xfer_mode= NEW_BLK;  //there is new data to get
    ACM->this_blk = ACM->new_blk; //from this block
    ACM->new_blk += ACM->blk_inc;  //now it points to next NEW block
  }
  else if( ACM->old_blk <= ACM->lold_blk )
  { //there is old data that GS wants us to repeat
    ACM->xfer_mode= OLD_BLK;  //there is new data to get
    ACM->this_blk = ACM->old_blk; //from this block
    ACM->old_blk++;  //now it points to next OLD block( Increments by 1)
  }
  else
  { return(-1); //neither new nor old data to get
  }
  
  return(0);  //there is data to get
}  //end WhatBlock

void Adjust4NxtDive(struct acm_param *ACM)
{ //15Feb09 -- Once this_blk block is xferred successfully we don't need to look
  //           for it on the the next dive.If this block had 4Kbytes
  //           then on the next dive start at this_blk+1.  
  //           If we didn't read a full 4K bytes,
  //           then we have to read it again later.  
  //           NOTE:  The memory of the last dive successfully transferred is 
  //                  lost when Spray is set to interrogate another modem by 
  //                  changing the settings in ACM->modem struct.
  //09Dec09 -- Whether or not the transfer was successful,this_blk is incremented by
  //                  ee_acm_binc for the next dive.  Let the ground station handle the
  //                  logic for repeating invalid data
  //30May2010 -- Neither NEW_BLK nor OLD_BLK transfers alter the modem database so the transfer
  //                  will repeat on the next dive until Ground Station reprograms the
  //                  desired block settings.  Adjust4NxtDive does nothing and immediately
  //                  returns.
  
  /*
  need lots of work in Adjust4NxtDive. Error messages make #of bytes rec'd a poor indicator
  of whether block was completely xferred. Must parse the DATA(xxxx) to see how many
  bytes were transferred and need to add total of sub-block hunks.  Need to check that CRC was
  good on all hunks of the block.  Ddon't want to advance
  to next block on next dive if only a partial block was read or if CRC was bad on any part of the
  block*/
 
 return;  //Until we figure auto-increment out, Adjust4NxtDive does nothing
 
}  //end Adjust4NxtDive

short XferBlk(struct acm_param *ACM)  //get data from remote modem
{ //Makes ee_acm_mxtry attempts to start transfer of 4K data block this_blk from REMOTE
  //ACM->remID = REMOTE modem mID
  //ACM->this_blk = index of the REMOTE 4K byte block to get
  //Watches the TS buffer and collect responses in ACM->buf.  ACM->by_read = bytes
  //read (also points to where next byte will be stored in ACM->buf)

  //Uses the TPU TS UART serial port.  If the start is successful, TSerInUse is
  //set =1 to prevent lpwait and Snooze functions from changing Sys clock or stopping
  //TPU or CPU oscillator.  Any of these make interrupt-driven TSerial fail.
  //This version written 02Mar09 based on StartXfer, does not use the RTS 
  //output line from the Benthos modem.
  
  //On success returns 
  //         0 = success  data xfer without error
  //On errors returns:  
  //        -2 = no response from LOC modem or invalid command
  //        -3 = REMOTE no response
  //        -4 = unparseable, unknown, or illegal response
  //        -5 = remote data block is empty
  //        -6 = data xferred but had CRC error
  //        -7 = bad data header
  //        -8 = can't start the REMOTE because it is transmitting
  //The return status is also put in ACM->Xfer_err

  char cmd[13];
  short nread,i;   
  short found;
  ulong secs,tend,nbytes;
  uchar dbg[80];
  TUPort    *tup;

  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP
  
  TSerInUse=1;  //assume REMOTE is going to respond so make sure don't change
                //CPU clock rate or stop it
  
  //Set Transfer status values to clearly illegal
  ACM->SourceID = ACM->CRC = ACM->mpd = ACM->snr =  999;
  ACM->agc = ACM->spd = ACM->ccerr = ACM->rem_pkt = 999;

  ACM->StX_tries=0;  //haven't yet tried to start the transfer
  do
  { ACM->StX_tries++;
    
    //20Feb09--make sure the REMOTE isn't talking 
    tend = RTCGetTime(0,0)+2*REM_TO;  //for twice the round-trip time
    sprintf(dbg,"XferBlk  %6ld blk %d try#%d wait for idle REM\n",
                 RTCGetTime(0,0),ACM->this_blk,ACM->StX_tries);
    printf("%s",dbg);//print diagnostic
    buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
    i=0;
    while( RTCGetTime(0,0)<tend  )
    { if(LOCmode()==1)i=1;//LOCAL is receiving from REMOTE
      DelayMilliSecs(1000);//check once a second 
    }
    sprintf(dbg,"XferBlk  %6ld Done with wait\n",RTCGetTime(0,0));
    printf("%s",dbg);//print diagnostic
    buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1

    if(i != 0 )
    {  ACM->Xfer_err = -8; 
       sprintf(dbg,"XferBlk  %6ld REMOTE is talking\n",RTCGetTime(0,0));
       printf("%s",dbg);//print diagnostic
       buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
       //REMOTE is talking so delay before next try
       if( ACM->StX_tries<iparam[IA_acm_mxtry] )
       { //delay gets longer with each attempt
         secs=RTCGetTime(0,0)+(long)(ACM->StX_tries)*iparam[IA_acm_rmdly]; //time to quit
         while( RTCGetTime(0,0) < secs)
         {  DelayMilliSecs(1000);//1 second delays (not low power) and doesn't
                                 //change CPU clock rate
         }
       }              
       continue;  //20Feb09----go to next try
    }
  
    //We haven't heard from the REMOTE for two RT travel times so lets start Xfer
    //of the desired block
    sprintf(cmd,"AT$BB%02d,%03d\0",ACM->remID,ACM->this_blk);  //generate command string
    TURxFlush  ( tup );   // flush the tpu port input buffer
    tputs( tup, cmd); // send command string
    acm_xmits++;
    
    ACM->buf[0] = '\0';ACM->by_read=0; // set reply to NULL str
    //15Jun10  ACM->by_read = tget(ACM_FROM,ACM->buf,40,1);//Local sends \r\n when it gets command and starts it
    ACM->by_read = tgets(tup,ACM->buf,40,1,NO_EOL,&found);//Local sends \r\n when it gets command and starts it
                                      //but there could be other LOCAL error messages also
    ACM->buf[ACM->by_read]='\0'; //NULL terminate the reply
    if ( ACM->by_read<2 )
    { //didn't get the expected "\r\n" from the LOCAL
      ACM->Xfer_err = -2;
      sprintf(dbg,"XferBlk  %6ld No LOC resp to %s\n",RTCGetTime(0,0),cmd);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      strcpy(&ACM->buf[ACM->by_read],dbg); //05Dec09 put error message in buf
      ACM->by_read +=strlen(dbg);
      continue;  //try again
    }
    else if ( strstr(ACM->buf,"Invalid") != NULL )
    { ACM->Xfer_err = -2;  //invalid command
      sprintf(dbg,"XferBlk  %6ld Invalid cmd:%s\n",RTCGetTime(0,0),cmd);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      ACM->by_read +=nread; //02Dec09 error message is preserved in buf
      continue;  //try again
    }

    //update ACM->by_read so the LOCAL response is appended to the ACM buf
    ACM->by_read += nread; 
    ACM->p_wrking = &ACM->buf[ACM->by_read];  //save the start of the message being worked on (for use by ParseBuf)    
    //Listen for REMOTE's response, which could be an error message or nothing
    //But if REM is close and data block is short, it could be real data message
    //15Jun10  nread = tget(ACM_FROM,&ACM->buf[ACM->by_read],MAXREPLY,REM_TO); //allow time for REM to respond
    nread = tgets(tup,&ACM->buf[ACM->by_read],MAXREPLY,REM_TO,NO_EOL,&found); //allow time for REM to respond
    ACM->buf[ACM->by_read+nread]='\0';  //NULL terminate
    
    if ( strstr(ACM->p_wrking,"Response Not Received") != NULL )
    { //no response from REMOTE modem
      ACM->Xfer_err = -3;   //remote not responding
      sprintf(dbg,"XferBlk  %6ld No REM resp to cmd:%s\n",RTCGetTime(0,0),cmd);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      ACM->by_read +=nread; //08Nov09 error message is preserved in buf
      continue;  //try again
    }
    else if ( strstr(ACM->p_wrking,"Block Empty") != NULL )
    { //REMOTE modem no data in this block
      ACM->Xfer_err = -5;
      sprintf(dbg,"XferBlk  %6ld Empty REM block %d\n",
                          RTCGetTime(0,0),ACM->this_blk);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      return(ACM->Xfer_err);  //don't try again--this is not really an error
    }
    else
    { //We haven't received any known error message.  
      //If nothing was received (ACM->by_read==0) then we assume REM is started
      //If something was received (ACM->by_read>0) then we it was real REM data 
      //and is stored at start of ACM->buf
      //In either case, we stop trying to start the transfer and start
      //looking for the rest of the DATA
      ACM->by_read +=nread;
      ACM->Xfer_err = 0;
      break;  
    } 
  } while (ACM->StX_tries<iparam[IA_acm_mxtry] && ACM->Xfer_err<0 ); //debug do we really want to try more than once?
    
  if( ACM->Xfer_err <0 ) return(ACM->Xfer_err);  //quit if unable to start the Xfer

  //At this point we have detected and responded to error codes
  // -2,-3,-5,-8. Remain to check for: 0=data is all OK,-6=data has bad CRC,
  // -4=bad format, -7=bad header   
  
  secs=RTCGetTime(0,0);  //save time that xmit was started
  //Fill ACM->endXfer with expected time that last byte will be received at LOCAL
  //calc size of block to be xmitted
  if(ACM->rem_bytes > 0 )
  { //LOCAL obtained number of bytes stored in REMOTE in OpenAcLink
    nbytes= ACM->rem_bytes - 4096L*ACM->this_blk; //# of bytes after start of desired REM block
    if(nbytes>4096L)nbytes=4096L;  //bytes that will be in the block
  }
  else
    nbytes=4096L; //assume have to send a whole block if don't know REMOTE byte count
  if(ACM->baud > 0 )
  { //LOCAL obtained the baud rate at REMOTE in OpenAcLink
    ACM->blk_secs = (nbytes*8L)/ACM->baud+5;  //seconds to send the block, 5 sec REMOTE latency
  }
  else
  { //assume 140 baud if don't know the REMOTE setting
    ACM->blk_secs = (nbytes*8L)/140+5;    
  }
        
  if( ACM->Sreg2==0 )
  { //Retry isn't enabled so REMOTE will send only once
    ACM->endXfer=secs +ACM->blk_secs+REM_TO;
  }
  else
  { //Retry is enabled on REMOTE so it will re-try twice for total of 3 times
    ACM->endXfer=secs + 3*(ACM->blk_secs + REM_TO); //allow for two way travel of NACK
  }
  sprintf(dbg,"XferBlk  %6ld Blk:%d Expect end at %ld\n",
              secs,ACM->this_blk,ACM->endXfer);
  printf("%s",dbg);//print diagnostic
  buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
    
  while( RTCGetTime(0,0) < ACM->endXfer )
  { //periodically suck whatever is in TS buffer and append to ACM buffer
    //15Jun10  nread = tget(ACM_FROM,&ACM->buf[ACM->by_read],(ACM->bufsize-ACM->by_read),10);
    nread = tgets(tup,&ACM->buf[ACM->by_read],(ACM->bufsize-ACM->by_read),10,NO_EOL,&found);
    if( nread > 0 )
    { //something was waiting
      ACM->by_read += nread; //bump storage pointer
      //Look for command termination string "\r\n>"
      if( strncmp(&ACM->buf[ACM->by_read-3],"\r\n>",3)==0 )
      { //termination string at end of message indicates LOCAL thinks it's done
        //with the REMOTE.  Either the message received is the end of the DATA or
        //it is an error message generated by LOCAL, e.g. Bad header.
        //If if is a "Bad header" message, the REMOTE will still be sending the
        //data message because it doesn't know the LOCAL didn't get the header.
        
        if( strncmp(&ACM->buf[ACM->by_read-10],"<EOP>\r\n",7)==0 )
        { //"<EOP>\r\n\r\n>" terminates the reception of data buffer
          break;  //so can leave now to check the data's validity
        }
        else
        { //LOCAL quit with an error message of some sort
          continue;  //keep listening until timeout or see end of data
                     //this is a punt that should be expanded in future
        }
      }
      //LOCAL thinks more is coming yet
    }
    //wait for more until time expires or hit end of message
  }
  ACM->buf[ACM->by_read]='\0';  //NULL terminate
  
  //Examine ACM->buf to extract data if possible and ascertain its validity
  ACM->p_wrking = ACM->buf;  //start parsing at beginning of buf
  ACM->Xfer_err = ParseBuf(ACM);
  //sprintf(dbg,"XferBlk  %6ld Err=%d  Blk:%d Try:%d\n",
  //             RTCGetTime(0,0),ACM->Xfer_err,ACM->this_blk,ACM->RdB_tries);
  //buf_to_cf1V( strlen(dbg),dbg); 
  //printf("%s",dbg);
  
  return(ACM->Xfer_err);
}   //end XferBlk

short XferSubBlk(short subindex, struct acm_param *ACM)  //get data from remote modem
{ //Makes ee_acm_mxtry attempts to start transfer of ACM->modem.nsub sub-blocks of 
  //256bytes using AT$BS starting at sub-block index subindex.
  //NOTE that subindex is not the same as the 4K block index but they are related:
  //        subindex of 1st sub-blok of this_blk = 16*ACM->this_blk 
  //ACM->remID = REMOTE modem ID
  //Watches the TS buffer and collect responses in ACM->buf.  ACM->by_read = bytes
  //read (also points to where next byte will be stored in ACM->buf).
  //XferSubBlk appends to this buffer and ASSUMES ACM->by_read has been initialized previously
  //24Jul09  The transfer of each 4K block is broken into transfers of modem.nsub 
  //         sub-blocks.  Each sub-block contains 256 bytes; nsub={1,2,4,8,16}
  //         The value of nsub is sent to REMOTE Sreg 57 which actually controls the size
  //         of the transfer.
  //         If nsub<16 then the AT$BSn,ISUB command is used
  //            where    n = REM ID
  //                  ISUB = index in REM Flash of the starting sub-block.  The sub-block
  //                         index of 1st sub-block in 4K block BLOK is ISUB = 16*BLOK.
  //                         Sub-blocks Isub, Isub+1,...,Isub+15 are all in BLOK
  //         
  //         If nsub=16 then the AT$BBn,BLOK command is used in XferBlk
  //            where    n = REM ID
  //                  BLOK = index in REM Flash of the 4K block.
 
  //Uses the TPU TS UART serial port.  If the start is successful, TSerInUse is
  //set =1 to prevent lpwait and Snooze functions from changing Sys clock or stopping
  //TPU or CPU oscillator.  Any of these make interrupt-driven TSerial fail.
  //This version written 02Mar09 based on StartXfer, does not use the RTS 
  //output line from the Benthos modem.
  
  //On success returns 
  //         0 = success  data xfer without error
  //On errors returns:  
  //        -2 = no response from LOC modem or invalid command
  //        -3 = REMOTE no response
  //        -4 = unparseable, unknown, or illegal response
  //        -5 = remote data block is empty
  //        -6 = data xferred but had CRC error
  //        -7 = bad data header
  //        -8 = can't start the REMOTE because it is transmitting
  //The return status is also put in ACM->Xfer_err

  char cmd[13];
  short nread,i;
  short found;
  ulong secs,tend,nbytes,end_sub;
  uchar dbg[80];
  TUPort    *tup;

  tup = all.tu_port[TU_ADP].tup;  // tpu uart port pointer for ACM/ADP
  
  TSerInUse=1;  //assume REMOTE is going to respond so make sure don't change
                //CPU clock rate or stop it
  
  //Set Transfer status values to clearly illegal
  ACM->SourceID = ACM->CRC = ACM->mpd = ACM->snr =  999;
  ACM->agc = ACM->spd = ACM->ccerr = ACM->rem_pkt = 999;
  
  //01feb11 move this to do_acm function
  /*if( subindex%16 == 0 ) 
  { //this is 1st sub-block of a 4K block
    ACM->buf[0] = '\0';ACM->by_read=0; //initialize the concatenating buffer to receive
                                       //1st sub-block at beginning of the buf.
                                       //Subsequent sub-blocks are appended to the first hunk
  }*/ 

  ACM->StX_tries=0;  //haven't yet tried to start the transfer
  do
  { ACM->StX_tries++;
    
    //02Aug09--make sure the REMOTE isn't talking
    tend = RTCGetTime(0,0)+2*REM_TO;  //for twice the round-trip time
    sprintf(dbg,"XfSbBlk  %6ld Blk:%d SbIndx:%d try#%d wait for idle REM\n",
                 RTCGetTime(0,0),ACM->this_blk,subindex,ACM->StX_tries);
    printf("%s",dbg);//print diagnostic
    buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
    i=0;
    while( RTCGetTime(0,0)<tend  )
    { if(LOCmode()==1)i=1;//LOCAL is receiving from REMOTE
      DelayMilliSecs(1000);//check once a second 
    }
    sprintf(dbg,"XfSbBlk  %6ld Done with wait\n",RTCGetTime(0,0));
    printf("%s",dbg);//print diagnostic
    buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      
    if(i != 0 )
    { ACM->Xfer_err = -8; 
      sprintf(dbg,"XfSbBlk  %6ld REMOTE is talking\n",RTCGetTime(0,0));
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      //REMOTE is talking so delay before next try
      if( ACM->StX_tries<iparam[IA_acm_mxtry] )
      { //delay gets longer with each attempt
        secs=RTCGetTime(0,0)+(long)(ACM->StX_tries)*iparam[IA_acm_rmdly]; //time to quit
        while( RTCGetTime(0,0) < secs)
        {  DelayMilliSecs(1000);//1 second delays (not low power) and doesn't
                                //change CPU clock rate
        }
      }              
      continue;  //20Feb09----go to next try
    } 
      
    //We haven't heard from the REMOTE for two RT travel times so lets start Xfer
    
    //Start Xfer of the desired sub-block     
    sprintf(cmd,"AT$BS%02d,%04d\0",ACM->remID,subindex);  //generate command string
    strcpy(&ACM->buf[ACM->by_read],cmd); //+++BDJ added for gs parsing
    ACM->by_read += strlen(cmd);  //+++BDJ added for gs parsing    
    TURxFlush  ( tup );   // flush the tpu port input buffer
    tputs( tup, cmd); // send command string
    acm_xmits++;
    
    //15Jun10  nread = tget(ACM_FROM,&ACM->buf[ACM->by_read],40,1);//Local sends \r\n when it gets command and starts it
    nread = tgets(tup,&ACM->buf[ACM->by_read],40,1,NO_EOL,&found);//Local sends \r\n when it gets command and starts it
                                        //but there could be other LOCAL error messages also
    ACM->buf[ACM->by_read+nread]='\0'; //NULL terminate the reply
    if ( nread<2 )
    { //didn't get the expected "\r\n" from the LOCAL
      ACM->Xfer_err = -2;
      sprintf(dbg,"XfSbBlk  %6ld No LOC resp to %s\n",RTCGetTime(0,0),cmd);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      strcpy(&ACM->buf[ACM->by_read],dbg); //05Dec09 put error message in buf
      ACM->by_read +=strlen(dbg);
      continue;  //try again
    }
    else if ( strstr(&ACM->buf[ACM->by_read],"Invalid") != NULL )
    { ACM->Xfer_err = -2;  //invalid command
      sprintf(dbg,"XfSbBlk  %6ld Invalid cmd:%s\n",RTCGetTime(0,0),cmd);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      ACM->by_read +=nread; //08Nov09 error message is preserved in buf
      continue;  //try again
    }
    
    //update ACM->by_read so the LOCAL response is appended to the ACM buf
    ACM->by_read += nread; 
    ACM->p_wrking = &ACM->buf[ACM->by_read];  //save the start of the message being worked on (for use by ParseBuf)    
    //Listen for REMOTE's response, which could be an error message or nothing
    //But if REM is close and data block is short, it could be real data message
    //15Jun10  nread = tget(ACM_FROM,ACM->p_wrking,MAXREPLY,REM_TO); //allow time for REM to respond
    nread = tgets(tup,ACM->p_wrking,MAXREPLY,REM_TO,NO_EOL,&found); //allow time for REM to respond
    ACM->buf[ACM->by_read+nread]='\0';  //NULL terminate
    if ( strstr(ACM->p_wrking,"Response Not Received") != NULL )  //fixed 05Nov09
    { //no response from REMOTE modem
      ACM->Xfer_err = -3;   //remote not responding
      sprintf(dbg,"XfSbBlk  %6ld No REM resp to cmd:%s\n",RTCGetTime(0,0),cmd);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      ACM->by_read +=nread; //08Nov09 error message is preserved in buf
      continue;  //try again
    }
    else if ( strstr(ACM->p_wrking,"Block Empty") != NULL )
    { //REMOTE modem no data in this block
      ACM->Xfer_err = -5;
      sprintf(dbg,"XfSbBlk  %6ld Empty REM block %d subindex=%d\n",
                          RTCGetTime(0,0),ACM->this_blk,subindex);
      printf("%s",dbg);//print diagnostic
      buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
      ACM->by_read +=nread;
      return(ACM->Xfer_err);  //don't try again--this is not really an error
    }
    else
    { //We haven't received any known error message.  
      //If nothing was received (ACM->by_read==0) then we assume REM is started
      //If something was received (ACM->by_read>0) then we it was real REM data 
      //and is stored at start of ACM->buf
      //In either case, we stop trying to start the transfer and start
      //looking for the rest of the DATA
      ACM->by_read +=nread;
      ACM->Xfer_err = 0;
      break;  
    }
       

  } while (ACM->StX_tries<iparam[IA_acm_mxtry] && ACM->Xfer_err<0 ); //debug do we really want to try more than once?
    
  if( ACM->Xfer_err == -3 )
  { //Remote failed to respond--put annotation in Iridium data stream
    //GapInDATA(xxxx) where xxxx=number of bytes in the hunk that failed to start
    //hunk has ACM->modem.nsub sub-blocks of 256 bytes each
    sprintf(&ACM->buf[ACM->by_read],"GapInDATA(%04d)",256*ACM->modem.nsub); 
    ACM->by_read += 15;
    ACM->buf[ACM->by_read]='\0';  //NULL terminate    
  }
    
  if( ACM->Xfer_err <0 ) return(ACM->Xfer_err);  //quit if unable to start the Xfer

  //At this point we have detected and responded to error codes
  // -2,-3,-5,-8. Remain to check for: 0=data is all OK,-6=data has bad CRC,
  // -4=bad format, -7=bad header   
  
  secs=RTCGetTime(0,0);  //save time that xmit was started
  //Fill ACM->endXfer with expected time that last byte will be received at LOCAL
  //calc size of hunk to be xmitted
  nbytes=ACM->modem.nsub*256;  //hunk has nsub sub-blocks of 256 byte
  if(ACM->baud > 0 )
  { //LOCAL obtained the baud rate at REMOTE in OpenAcLink
    ACM->blk_secs = (nbytes*8L)/ACM->baud+5;  //seconds to send the block, 5 sec REMOTE latency
  }
  else
  { //assume 140 baud if don't know the REMOTE setting
    ACM->blk_secs = (nbytes*8L)/140+5;    
  }
        
  if( ACM->Sreg2==0 )
  { //Retry isn't enabled so REMOTE will send only once
    ACM->endXfer=secs +ACM->blk_secs+REM_TO;
  }
  else
  { //Retry is enabled on REMOTE so it will re-try twice for total of 3 times
    ACM->endXfer=secs + 3*(ACM->blk_secs + REM_TO); //allow for two way travel of NACK
  }
  sprintf(dbg,"XfSbBlk  %6ld Blk:%d SbIndx:%d Expect end at %ld\n",
              secs,ACM->this_blk,subindex,ACM->endXfer);
  printf("%s",dbg);//print diagnostic
  buf_to_cf1V( strlen(dbg),dbg);  //and write to CF1
    
  //10feb11 BDJ check to make sure we dont already have the msg before starting wait
  if( !strncmp(&ACM->buf[ACM->by_read-10],"<EOP>\r\n",7)==0 )
   { //"<EOP>\r\n\r\n>" terminates the reception of data buffer 
    while(  RTCGetTime(0,0) < ACM->endXfer )
     { //periodically suck whatever is in TS buffer and append to ACM buffer
      nread = tgets(tup,&ACM->buf[ACM->by_read],(ACM->bufsize-ACM->by_read),10,NO_EOL,&found);
      if( nread > 0 )
       { //something was waiting
        ACM->by_read += nread; //bump storage pointer
        //Look for command termination string "\r\n>"
        if( strncmp(&ACM->buf[ACM->by_read-3],"\r\n>",3)==0 )
         { //termination string at end of message indicates LOCAL thinks it's done
           //with the REMOTE.  Either the message received is the end of the DATA or
           //it is an error message generated by LOCAL, e.g. Bad header.
           //If if is a "Bad header" message, the REMOTE will still be sending the
           //data message because it doesn't know the LOCAL didn't get the header.
        
          if( strncmp(&ACM->buf[ACM->by_read-10],"<EOP>\r\n",7)==0 )
           { //"<EOP>\r\n\r\n>" terminates the reception of data buffer
            break;  //so can leave while loop now to check the data's validity
           }
          else
           { //LOCAL quit with an error message of some sort
            continue;  //keep listening until timeout or see end of data
                     //this is a punt that should be expanded in future
           }
         }//end if strncmp
        //LOCAL thinks more is coming yet
       }//end if read something
     //wait for more until time expires or hit end of message
     }//end while loop
   } //end if !strncmp
  ACM->buf[ACM->by_read]='\0';  //NULL terminate
  
  end_sub= RTCGetTime(0,0) - ACM->endLinks;  //seconds since end of OpenAcLink
  if(end_sub>32767)end_sub=32767;
  else if (end_sub<0) end_sub=0;
  ACM->eb_secs = (short)end_sub;  //secs from end of OpenAcLink to end of
                                  //acoustic xfer of this block.  Granularity
                                  //depends how frequently CheckXfer is called.
 
  //Examine ACM->buf to extract data if possible and ascertain its validity
  ACM->Xfer_err = ParseBuf(ACM);  //starts parsing at ACM->p_wrking
  //sprintf(dbg,"XfSbBlk  %6ld Err=%d  Blk:%d Try:%d\n",
  //             RTCGetTime(0,0),ACM->Xfer_err,ACM->this_blk,ACM->RdB_tries);
  //buf_to_cf1V( strlen(dbg),dbg); 
  //printf("%s",dbg);
  
  return(ACM->Xfer_err);
}   //end XferSubBlk

short ParseBuf( struct acm_param *ACM )
{ //parses ACM->buf, extracts parameters of interest, detemines validity
  //of data message.  Fills values into ACM diagnostic structure
  //The parsing starts at byte pointed to by ACM->p_wrking which is set by XferSubBlk and
  //XferBlock to the start of the current area being filled and worked on.
  //This value is set to 0 for the XferBlk of a full 4K block or points to the current
  //hunk for XferSubBlk of sub-blocks
  //Returns 
  //         0 = success  data xfer without error
  //On errors returns:  
  //        -2 = no response from LOC modem or invalid command
  //        -3 = REMOTE no response
  //        -4 = unparseable, unknown, or illegal response
  //        -5 = remote data block is empty
  //        -6 = data xferred but had CRC error
  //        -7 = bad data header
  //        -8 = can't start the REMOTE because it is transmitting
 
  
  short s_dum,err,CRCmap;
  uchar *ptr,*mess_start;
  char strng[5],dbg[80];
  float f_inp;
    
  
  //look for a REMOTE packet ID in whatever we received
  //Do this for all returns, even those with errors
  if ( (ptr=strstr(ACM->p_wrking,"sequence #")) != NULL )//start at beg. of raw buffer
  { //Found the packet ID
    ptr += 10; sscanf(ptr,"%d",&ACM->rem_pkt);  //Remote packet ID
                                                //999 if no packet ID found
    printf("rem_pkt=%d\n",ACM->rem_pkt);
  }
  
  //05Nov09  There can be more than one instance of CRC failure if REM has retries enabled.
  //Find the hex map of sub-blocks with bad CRC
  ptr=ACM->p_wrking; //XferBlock starts at ACM->buf[0], XferSubBlk at start of current hunk
  CRCmap=0; //no bad sub-blocks identified yet
  while ( ptr != NULL)
  { ptr=strstr(ptr,"CRC Fail (");  //look for hex map of sub-blocks with bad CRC
    if( ptr==NULL )
    { break;  //no CRC error maps found
    }
    else
    { //parse this CRC error map 
      ptr += 10; //points to the hex rep of bad sub-blocks.  LSB=sub-block0
      sscanf(ptr,"%04x",&CRCmap);
      sprintf(dbg,"ParseBuf %6ld CRCmap=0x%04x\n",
                  RTCGetTime(0,0),CRCmap);
      printf("%s",dbg);
      buf_to_cf1V( strlen(dbg),dbg);     //header line with source, time, CRCmap
    }
  }
  //debug working here 06Nov09
  //need some way to determine and indicate that the DATA was corrected finally or if still
  //has an error.I guess if there are 3 CRCmaps we know the DATA is still bad?
  
  ptr=strstr(ACM->p_wrking,"DATA(");  //look for "DATA("  to get message length
  if( ptr==NULL )
  { //no "DATA(" found in the buffer.
    if ( strstr(ACM->p_wrking,"Error in header") != NULL )
    { //The header had some error so the LOCAL thinks we're done but the 
      //REMOTE could be transmitting away thinking all is well with the transfer.
      //But at least we know the REMOTE is there and talking.
      err = -7;
    }
    else
    { err = -4;  //bad format
    }
  }
  else
  { //Found DATA--extract params of interest
    ptr += 5; s_dum=4096; sscanf(ptr,"%d",&s_dum);  //bytes in message sent by remote
                                                    //01Mar09 assume full block 
    sprintf(dbg,"ParseBuf %6ld %4d bytes in DATA mess\n",
                RTCGetTime(0,0),s_dum);
    printf("%s",dbg);
    buf_to_cf1V( strlen(dbg),dbg);     //header line with source, time, # bytes
    ptr=strstr(ptr,"):");  //look for "):"  at end of message length
    if( ptr==NULL )
    { err = -4;  //bad format
    }
    mess_start=ptr+2;  //points to 1st byte of the actual message
    ptr = mess_start + s_dum;  //1st byte past message
    
    ptr=strstr(ptr,"Source:");  //look for "Source:"
    if( ptr!=NULL )
    { ptr += 7; sscanf(ptr,"%d",&ACM->SourceID);   //ID read from message
    }
    
    ptr=strstr(ptr,"CRC:"); 
    if( ptr!=NULL )
    { //found CRC--is it OK?
      ptr += 4; sscanf(ptr,"%s",&strng);   //CRC Fail or Pass?
      if( !strncmp(strng,"Pass",4) )
      { ACM->CRC = 0 ; //CRC passed
        err = 0;  //DATA present with good CRC
      }
      else
      { ACM->CRC = 1 ; //CRC failure
        err = -6;  //DATA present with bad CRC
      }
    }
     
    ptr=strstr(ptr,"MPD:");  //look for "MPD:" in message stats
    if( ptr!=NULL )
    { ptr += 4; sscanf(ptr,"%f",&f_inp);  //multipath delay in floating millisecs
      ACM->mpd=(short)(10*f_inp);  //LSB = 0.1 millisec
    }
     
    ptr=strstr(ptr,"SNR:");  //look for "SNR:" in message stats
    if( ptr!=NULL )
    { ptr += 4; sscanf(ptr,"%f",&f_inp);  //SNR in floating dB
      ACM->snr=(short)(10*f_inp);  //LSB = 0.1 dB
    }
     
    ptr=strstr(ptr,"AGC:");  //look for "AGC:" in message stats
    if( ptr!=NULL )
    {  ptr += 4; sscanf(ptr,"%d",&ACM->agc);  //AGC 0-65
    }
     
    ptr=strstr(ptr,"SPD:");  //look for "SPD:" in message stats
    if( ptr!=NULL )
    {  ptr += 4; sscanf(ptr,"%f",&f_inp);  //SPD in knots
       ACM->spd=(short)(10*f_inp);  //LSB = 0.1 knot
    }
     
    ptr=strstr(ptr,"CCERR:");  //look for "CCERR:" in message stats
    if( ptr!=NULL )
    { ptr += 6; sscanf(ptr,"%d",&ACM->ccerr);  //error metric
    }
    
    //Index in REMOTE memory of last byte of the data message just rec'd
    ACM->indx_recd=(long)ACM->this_blk*4096L +s_dum -1;  //16Mar09
    
  }
  
  return(err);
}  //end ParseBuf

// remove OpenAcLink 22Jul09 to save 3312 bytes of ROM

short OpenAcLnk3( void )   //Try to open Acoustic Link to ACM->modem
{ //Based on control parameters stored in ACM->modem structure, try to communicate
  //with a remote modem.
  //This version has a different sequence to establishing a link, going from commands
  //with short responses to increasingly long responses.
  //24Jul09 changed the power adjust method 
  //  1) Set REMOTE to min power spec by ACM->modem.minpwr and use 
  //     REMOTE's response to verify it's there
  //     The REMOTE responds to a power level change at the new setting.
  //  2) Adjust REMOTE power upwards until a response is received.
  //  3) Set REMOTE power to (highest_level_with_no_response + ACM->modem.pmargin)
  //     Each step up increases output by 3 dB.
  //  4) Get range reading
  //  5) Get number of bytes of data available at REM
  //        Unlike original version (OpenAcLink), the inability to receive the 
  //        number of data byes available is not fatal and does not turn off the LOCAL.
  //  6) Get REM battery voltage
  //  7) Check the link and adjust to lower baud rates as needed.
  //  8) If ACM->modemlist.nsub = 0 then adjust ACM->modem.nsub to agree with baud
  //        and use AT$BS transfer of 256-byte sub-blocks
  //     If ACM->modemlist.nsub ={1,2,4,8} then use AT$BS transfer of nsub sub-blocks
  //     If ACM->modemlist.nsub == 16 then always use AT$BB transfer of 4K block
  //If all this succeeds, send any commands in the modinfo struct to the REMOTE
  
  //Obtain control and config info from struct modinfo filled from GS via Iridium cmd_6.
  //  Uses  struct ACM->modem.ib_new to compute what remote blocks 
  //  contain new data.
  //  Uses  struct ACM->modem.ib_rpt and nb_rpt to compute what remote blocks 
  //  contain old data that the GS wants to repeat.
  //Results are placed in the ACM struct BUT THE XFER IS NOT STARTED
  //Returns  0 = Link is established,all queries answered
  //        -3 = No response heard from REMOTE when it xmitted at max power
  //        -1 = unable to power up LOCAL or no response to LinkQ query
  //                Does NOT turn off power to LOCAL

  short n,stat,nread,newbaud,bstat;
  short Ntries=3; //number of retries for max power,get REM Sreg6,set REM baud,getRange
  long l;
  uchar dbg[200];
  float xmitV;
  
  struct acm_param *ACM; 
  ACM=all.acm;

  ACM->remID=ACM->modem.id;   //Copy the ID of the modem being sought
  ACM->depth=(short)(10*(all.dat->dbar));//depth in 0.1 dBar      
    
  //clear misc modem info in ACM struct that won't be cleared or filled by LinkQ
  //Clear the >acm structure so won't confuse new and old contents
  ACM->baud = ACM->tst_bytes = 0;
  ACM->SourceID = ACM->CRC = ACM->mpd = ACM->snr =999;
  ACM->agc = ACM->spd = ACM->ccerr=999;
  ACM->ber  = 9999;
  ACM->rem_bytes=ACM->indx_recd=0L;
  ACM->by_read = 0;  //no bytes have yet been read from this REMOTE
  ACM->new_blk= ACM->lnew_blk =0;  //first,last blocks with new data in REMOTE flash
  ACM->endLinks = 0;  //time in seconds when successful OpenAcLink ended
  ACM->battV=9999;  //Remote battery voltage in .01 V
  ACM->LocBatt=9999;//LOCAL battery voltage in .01V
  ACM->Sreg2=0xff;  //Remote Sreg2 setting (only legal values are 0 and 1)
  ACM->AGCamb = 0;//ambient noise from LOCAL AGC before start talking
  ACM->Lnk_tries=0;
  ACM->rempwr=9;  //Power setting of the remote hasn't been set yet. 
  ACM->CRC=999;  //CRC=FAIL until see valid block
  ACM->range=ACM->rem_pkt=ACM->StX_tries=ACM->RdB_tries=0;

 //Seen if LOCAL modem is already ON and active
  if( ack_acm() != 0 )  //send AT to modem & look for '\r\nOK\r\n>'
  { //It isn't powered up
    //This isn't really a problem as normal dive activities would not yet
    //have turned on the modem so don't generate an error message  10Jan09

    nread=acm_Power_Up(); //Try to power the modem
    sprintf(dbg,"PwrUpLOC %6ld err=%d\n",RTCGetTime(0,0),nread);
    printf("%s",dbg);
    buf_to_cf1V( strlen(dbg),dbg);
    if ( nread < 0 ) 
    {  return(-1);  //set error flag but leave LOCAL ON
    } 
  }
  
  //8Dec09  now that LOCAL is awake send modem_cmd_str if it is LOCAL command 
  //        If the command is to a REMOTE or expects a response from a remote the
  //        sending of the command is deferred until a link to ANY remote has been
  //        established
  if( modem_cmd_str.nbytes>0 )
  { //there is a command waiting to be sent.
    //All Commands with either $ or : are to REMOTE.  Other commands 
    //which look for response from a remote are ATRn,ATXn,ATYn
    modem_cmd_str.command[modem_cmd_str.nbytes]='\0'; //null terminate for sure
    if( strstr(modem_cmd_str.command,"$")==NULL && 
        strstr(modem_cmd_str.command,":")==NULL &&
        strstr(modem_cmd_str.command,"ATR")==NULL &&
        strstr(modem_cmd_str.command,"ATX")==NULL &&
        strstr(modem_cmd_str.command,"ATY")==NULL    )
    { //This command is to LOCAL and expects no reply from a REMOTE
      printf("Command to LOCAL\n");  //debug
      debug_string( modem_cmd_str.command, modem_cmd_str.nbytes); //debug 
      stat=cmd_acm(modem_cmd_str.command,REMrep,MAXREPLY,REM_TO,10); //send string and get response
      printf("%d bytes:\n%s\n",stat,REMrep);  //debug output
      //queue command string and results to Iridium (also annotates to CF1)
      lifo_accmd(modem_cmd_str.command, modem_cmd_str.nbytes,REMrep,stat);         
      if( stat >= 0 )modem_cmd_str.nbytes=0;  //only send this command once
    }
  }

  stat= GetBatt(-1 ,&xmitV,1); //read battery of LOCAL modem
  sprintf(dbg,"OpAcLnk3 %6ld LOCBatt err=%d  %5.2f V\n",RTCGetTime(0,0),stat,xmitV);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  if ( stat >= 0 )
  { ACM->LocBatt=(short)(100*xmitV);  //update batt voltage in ACM struct LSB=.01V
  }
  else
  { ACM->LocBatt=9999;  //illegal batt voltage in ACM structe
  }
  
  ACM->AGCamb=AmbAGC();  //15Jul08 use LOCAL AGC to estimate ambient noise
  sprintf(dbg,"AGCamb   %6ld AGC=%d\n",RTCGetTime(0,0),ACM->AGCamb);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  
  //Make Ntries tries at max power to see if the modem is there
  stat=SetPower(ACM->remID,8,2*Ntries);
  if(stat==0)
  {  //Spray heard response when REMOTE xmitted at max power
     sprintf(dbg,"OpAcLnk3 %6ld REMid %d Heard @ max pwr \n",RTCGetTime(0,0),ACM->remID);
     printf("%s",dbg);
     buf_to_cf1V( strlen(dbg),dbg);
  }
  else
  {  //never heard from REM
     sprintf(dbg,"OpAcLnk3 %6ld REMid %d no reply\n",RTCGetTime(0,0),ACM->remID);
     printf("%s",dbg);
     buf_to_cf1V( strlen(dbg),dbg);
     return(-3); //failure here returns with ACM->rempwr=9
  }
  //REMOTE was heard and is at max power  ACM->rempwr hasn't been changed from rempwr=9
    
  //Send any command string which is queued for delivery to a REMOTE
  //As of 08Dec2009 no check to see if command is for ACM->remID
  if( modem_cmd_str.nbytes>0 )
  { //there is a command waiting to be sent.
    //All Commands with either $ or : are to REMOTE.  Other commands 
    //which look for response from a remote are ATRn,ATXn,ATYn
    modem_cmd_str.command[modem_cmd_str.nbytes]='\0'; //null terminate for sure
    if( strstr(modem_cmd_str.command,"$")!=NULL || 
        strstr(modem_cmd_str.command,":")!=NULL ||
        strstr(modem_cmd_str.command,"ATR")!=NULL ||
        strstr(modem_cmd_str.command,"ATX")!=NULL ||
        strstr(modem_cmd_str.command,"ATY")!=NULL    )
    { //This command is to a REMOTE
      printf("Command to REMOTE\n");  //debug
      debug_string( modem_cmd_str.command, modem_cmd_str.nbytes); //debug 
      stat=cmd_acm(modem_cmd_str.command,REMrep,MAXREPLY,REM_TO,10); //send string and get response
      printf("%d bytes:\n%s\n",stat,REMrep);  //debug output
      //queue command string and results to Iridium (also annotates to CF1)
      lifo_accmd(modem_cmd_str.command, modem_cmd_str.nbytes,REMrep,stat);         
      if( stat >= 0 )modem_cmd_str.nbytes=0;  //only send this command once
    }
  }
  
//debug working here 24Jul09  Get REMOTE AGC history via AT$Gx  after delay for silence

  //Adjust REMOTE power to save energy and minimize multipath problems
  //Start at ACM->modem.minpwr and increase power until REMOTE responds
  ACM->rempwr=ACM->modem.minpwr;  //Note if minpwr=8 then no search is done 
  while( ACM->rempwr < 8 )
  {  if( (stat=SetPower(ACM->remID,ACM->rempwr,Ntries)) != 0 )
     {  ACM->rempwr++;  //no response,bump up one level
        if( ACM->rempwr == 8 )
        {  //level 7 didn't work
           //We already know 8 worked but REMOTE is at lev 7
           stat=SetPower(ACM->remID,ACM->rempwr,Ntries);  //Set REM back to 8
        }
        else if (ACM->rempwr > 8 )
           ACM->rempwr=8; //keep it legal & try again
     }
     else
     {  //heard it @ ACM->rempwr--add power margin
        ACM->rempwr += ACM->modem.pmargn;;
        if( ACM->rempwr>8 ) ACM->rempwr=8; //keep it legal
        stat=SetPower(ACM->remID,ACM->rempwr,Ntries);
        break;
     }
  }
  //SetPower annotates success/fail of each call to CF1
  //Proceed regardless of success of last change since we know we heard
  //REM at max power
    
  //Read the actual setting from the REMOTE, Ntries tries
  stat=1;ACM->rempwr=-ACM->rempwr;  //If we can't read the REMOTE Sreg6 then
                                    //ACM->rempwr is negative of last setting
                                    //that was sent to the REMOTE
  stat=GetSreg(ACM->remID,6,&ACM->rempwr,Ntries);
  sprintf(dbg,"OpAcLnk3 %6ld REMid %d Sreg6=%d err=%d\n",
                 RTCGetTime(0,0),ACM->remID,ACM->rempwr,stat);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  
  stat=RemRange(ACM->remID ,&n,Ntries);  //get range from remote modem
  sprintf(dbg,"OpAcLnk3 %6ld Range err=%d  %d m\n",RTCGetTime(0,0),stat,n);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  if ( stat >= 0 )
  {  ACM->range=n;  //update range in ACM struct
  }
  else
  {  ACM->range=0;  //update range in ACM structe
  }

  stat=DataBy(ACM->remID ,&l,Ntries);  //l=(long) # bytes stored in remote
  sprintf(dbg,"OpAcLnk3 %6ld DataBy err=%d  %ld REM bytes\n",RTCGetTime(0,0),stat,l);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  if ( stat < 0 )
  {  ACM->rem_bytes=-1L;  //DataBy failed--set error bit
  }
  else
  {  ACM->rem_bytes=l;  //save bytes in remote Flash in ACM struct
  }
  
  stat= GetBatt(ACM->remID ,&xmitV,Ntries); //read battery of remote modem
  sprintf(dbg,"OpAcLnk3 %6ld REMBatt err=%d  %5.2f V\n",RTCGetTime(0,0),stat,xmitV);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  if ( stat >= 0 )
  { ACM->battV=(short)(100*xmitV);  //update batt voltage in ACM struct LSB=.01V
  }
  else
  { ACM->battV=9999;  //illegal batt voltage in ACM structe
  }

  stat=LinkQ(ACM->remID,&nread,ACM);//check link quality
                                   //fills ACM->baud if successful
  //On errors LinkQ returns:  
  //        -1 = illegal remote ID
  //        -2 = no response from local modem or LOCAL not ON
  //        -3 = REMOTE no response
  //        -4 = unexpected,illegal, or unparsable response
  //         0 = success and sets ACM->baud to REMOTE's acoustic baud

  sprintf(dbg,"OpAcLnk3 %6ld LinkQ err=%d\n",RTCGetTime(0,0),stat);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  //debug working here--several Jan09 dives had 55 minute gap between the time of
  //                    this message and the next diagnostic print.  This was seen with both
  //                    the "OpAcLink" diagnostic after baud changes below or the "Start Xfer"
  //                    diagnostic in StartXfer
  //                    Need more diagnostics here to find what it is doing for that 55 min 
  if ( (stat==-1) || (stat==-2) )
  {  return(-1);  //illegal remID or no local response is fatal.Leave ON
  }
  
  if( ACM->baud != ACM->modem.baud )
  { //the REMOTE isn't at the desired baud so change it
    //Will also get here if the initial LinkQ is not successful
    if( ACM->modem.baud==140 || ACM->modem.baud==300  || ACM->modem.baud==600 ||
        ACM->modem.baud==800 || ACM->modem.baud==1066 || ACM->modem.baud==1200 ||
        ACM->modem.baud==2400)
    { //try to set legal baud on remote
      bstat=SetBaud( ACM->remID , ACM->modem.baud, ACM,Ntries  );  //Set REM ac baud rate
      sprintf(dbg,"OpAcLnk3 %6ld StBd(1) to %d err=%d\n",RTCGetTime(0,0),ACM->modem.baud,bstat);
      printf("%s",dbg);
      buf_to_cf1V( strlen(dbg),dbg);
      //ACM->baud is changed if SetBaud succeeds
      stat=LinkQ(ACM->remID,&nread,ACM);//check link quality at new baud
      sprintf(dbg,"OpAcLnk3 %6ld LinkQ aft StBd(1) err=%d\n",RTCGetTime(0,0),stat);
      printf("%s",dbg);
      buf_to_cf1V( strlen(dbg),dbg);
    }
  }
  for(ACM->Lnk_tries=1;ACM->Lnk_tries<3 ;ACM->Lnk_tries++)
  { //make up to 3 tries to get LinkQ while trying lower baud rates
    if (stat <0 || ACM->ber>2 )  //25Jul09 added check on BER
    { //LinkQ failed or had more than 2 bit errors in 256-byte test message
      if( ACM->modem.baud == 0 )
      { continue;//don't change the remote baud before try again
      }
      else
      { //current baud failed.Drop baud and try again if either autoselect or
        //specified a legal baud rate.
        newbaud=ACM->baud;
        switch ( ACM->baud )
        { case 300: newbaud=140;break;
          case 600: newbaud=300;break;
          case 800: newbaud=600;break;
          case 1200: newbaud=800;break;
        }
        if(newbaud < ACM->baud)
        { //change remote baud rate
          bstat=SetBaud( ACM->remID , newbaud, ACM ,Ntries );  //Set acoustic baud rate on remote
          sprintf(dbg,"OpAcLnk3 %6ld StBd(2) to %d BER=%d err=%d\n",
                      RTCGetTime(0,0),newbaud,ACM->ber,bstat);
          printf("%s",dbg);
          buf_to_cf1V( strlen(dbg),dbg);
          //ACM->baud is changed if SetBaud succeeds
          if(newbaud ==ACM->baud)
          { //Remote is at new baud
            printf("Rem %d baud set to %d\n",ACM->remID,ACM->baud);
          }
        }
        stat=LinkQ(ACM->remID,&nread,ACM);//try again
                   //14Jul09 Note that stat only reflects whether the ATX call resulted
                   //        in a response from the REMOTE that could be parsed.  It does
                   //        not look at the value of ERR,MPD, CCERR which indicate the
                   //        QUALITY of the link.  debug working on this
                         
        sprintf(dbg,"OpAcLnk3 %6ld LinkQ aft StBd(2) BER=%d err=%d\n",
                    RTCGetTime(0,0),ACM->ber,stat);
        printf("%s",dbg);
        buf_to_cf1V( strlen(dbg),dbg);
      }
    }

    //auto-increase of baud would go here if we can figure out how to do it reliably
    else
    {break;//LinkQ succeeded
    }
  }
    
  if( stat <0 || ACM->ber>2 )
  {//bad or no response to LinkQ
   //make a final try at nextlower baud
     newbaud=ACM->baud;
     switch ( ACM->baud )
     { case 300: newbaud=140;break;
       case 600: newbaud=300;break;
       case 800: newbaud=600;break;
       case 1200: newbaud=800;break;
     }
     if(newbaud < ACM->baud)
     {//change remote baud rate
      bstat=SetBaud( ACM->remID , newbaud, ACM ,Ntries );  //Set acoustic baud rate on remote
      sprintf(dbg,"OpAcLnk3 %6ld StBd(3) to %d err=%d\n",RTCGetTime(0,0),newbaud,bstat);
      printf("%s",dbg);
      buf_to_cf1V( strlen(dbg),dbg);
      //ACM->baud is changed if SetBaud succeeds
      if(newbaud ==ACM->baud)
      {//Remote is at new baud
        printf("Rem %d baud set to %d\n",ACM->remID,ACM->baud);
        ACM->Lnk_tries++;
        stat=LinkQ(ACM->remID,&nread,ACM);//try again
        sprintf(dbg,"OpAcLnk3 %6ld LinkQ aft StBd(3) BER=%d err=%d\n",
                    RTCGetTime(0,0),ACM->ber,stat);
        printf("%s",dbg);
        buf_to_cf1V( strlen(dbg),dbg);
      }
     }
  } 
  
  if( stat <0 )
  {  //still bad or no response to LinkQ
     return(-1);  //illegal remID or no local response is fatal.Leave ON
  }
     
  //LinkQ succeeded and filled baud,mpd,ber,snr,agc,spd,ccerr in ACM struct
  sprintf(dbg,"OpAcLnk3 %6ld REM baud=%d BER=%d\n",RTCGetTime(0,0),ACM->baud,ACM->ber);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg); 
  
  if( ACM->modem.nsub == 0 )
  { //Adjust size of data transfer according to baud rate
    //The transfer will be done using AT$BS to transfer in 256-byte blocks
    switch (ACM->baud)
    {  case 140:
         ACM->modem.nsub=4;  //xfer in 1k sub-blocks
         break;
       case 300:
         ACM->modem.nsub=8;  //xfer in 2k sub-blocks
         break;
       case 600:
       case 800:
       default:
         ACM->modem.nsub=16;  //xfer in full 4K Blocks
         break;
    }
  }
  else   
  { //Use the value unchanged.
    //If modem.nsub == 16 then the transfer will be in 4K block using AT$BB regardless 
    //   of baud rate.
    //If modem.nsub == (1,2,4,8} the xfer will use AT$BS in hunks of {256,512,1024,2048}
    //   bytes
  }
  sprintf(dbg,"OpAcLnk3 %6ld Xfer chunksize=%d bytes\n",RTCGetTime(0,0),256*ACM->modem.nsub);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg); 


  //15Jul08 read Remote Sreg2 and save status of retry_enable in ACM->Sreg2
  GetSreg( ACM->remID, 2 , &ACM->Sreg2,Ntries );
  sprintf(dbg,"OpAcLnk3 %6ld REM S2=%d\n",RTCGetTime(0,0),ACM->Sreg2);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);

  if( iparam[IA_acm_binc] == 0)
  { //auto incrementing of the starting block between dives is not wanted but we
    ACM->blk_inc=1; //still want to read each block within a dive
  }
  else
  { //auto-incrementing is enabled between blocks.  
    ACM->blk_inc=iparam[IA_acm_binc];  //there will be decimation
                                           //within a dive if blk_inc > 1
  }
  
  //compute which blocks in the remote flash contain data that Spray hasn't yet seen
  ACM->new_blk= ACM->modem.ib_new;  //start at 1st byte of 4K byte block ib_new
                                    //Value is set via Iridium cmd_6.As of 15Feb09 each
                                    //time a new block is successfully read acoustically,
                                    //ib_new is advanced by ee_acm_binc.  If binc>0 next dive
                                    //won't repeat asking for blocks that have already been sent.
  //Compute the index of last block to ask for
  //send up to nb_new blocks of new data, incrementing index by blk_inc each time
  ACM->lnew_blk=ACM->new_blk + (ACM->modem.nb_new*ACM->blk_inc) -1; 

  if( ACM->rem_bytes > -1L )
  { //Remote responded to LinkQ and we know how much data is available
    //so we can refine lnew_blk a little
    //ACM->rem_bytes   = # of bytes stored at remote
    //ACM->rem_bytes-1 = index of newest byte at remote
    n = (short)((ACM->rem_bytes-1L) / 4096L);  //n=index of block with
                                               //last byte written to flash
    if ( ACM->lnew_blk > n )ACM->lnew_blk=n;  //only ask for data if it's available
  }

  //which blocks in the remote flash contain old data that GS wants to repeat
  ACM->old_blk  = ACM->modem.ib_rpt;  //ib_rpt=1st old block to repeat (from GS cmd_6)
                                    //Value is set via Iridium cmd_6. 
                                    //In contrast to new data, old data always asks for each
                                    //of nb_rpt blocks starting with old_blk.
                                    //blk_inc (derived from ee_acm_binc) is ignored for OLD blks
  ACM->lold_blk = ACM->old_blk + ACM->modem.nb_rpt -1 ;//send nb_rpt old blks (0 is OK)
    
  ACM->endLinks = RTCGetTime(0,0);  //time in seconds when OpenAcLink ends
  ACM->eb_secs=0;              //elapsed secs since endLinks=0
    
  return(0);  //success
}  //end OpenAcLnk3
 
void test_acm( void )  // test above functions for ac modem
 {char ch='?';
  short nread,regno,value,nerr,tmo=0;
  char reply[450];
  float XMITv;
  TUPort    *tup;
  struct spi_param *ss;
   
  ss = all.sspi; // points to the spi comms struct
  setup_spi ( CS_GPIO, ss );

  tup = all.tu_port[TU_ADP].tup;  // correct tpu uart port pointer
    
  menu_acm();
  
  while ( ch !='Q')
  { SerInFlush(); // delete any old stuff 
      if (ch !=' ' && ch>0 ) cprintf("A_1>\n"); // display directory prompt
	  ch = -1; // illegal value - means it timed out
	  ch = SCIRxGetCharWithTimeout( 1000 ); // get a new character
	  tmo = check_tmo ( &ch, tmo );
	    // increments the tmo counter if ch<0, and
	    // goes to sleep mode after max_tmo.  Returns *ch as upper-case

	  switch (ch)
	  { // call the desired function
      case 'Q' : break;  // will quit the program
      case '?' : break;  // displays the directory prompt
      case  -1 :  break; // wait for another character
      case 'U' :   menu_acm(); break;

      case 'R' :  //remote modem functions
        acm_remote();
        break;
      case 'D' :  //do_acm
        dbg_ac_test();
        break;
      case 'W' : 
        acm_off();
        printf("Power Up ");fflush(stdout);
        nread=acm_Power_Up(); //make sure LOC is powered up
        if (nread >= 0)
          printf("OK\n");
        else 
          printf("Err=%d\n",nread);
        break;
      case 'T' :  //transparent pass Spray COMM to/from Modem
        Transparent_ACM();
        break;
      case 'P' :  //power up modem
        spi_aux_pwr( AUX_ON_ALT, ss ); // modem now has power
        acm_is_on=1;  //the acm is on and set up for communication
        printf("Modem is ON\n");
        break;
      case 'C' :  //power cycle testing
        //repeatedly turns Ac modem on and off to make sure it wakes
        { short secbet=5,secfail=20;
          char s_res[]="%s #fails=%d Wait %d secs\n";
          /*printf("Secs between tries,fails?\n");//debug fflush(stdout);
          scanf("%d %d",&secbet,&secfail);//debug */
          printf("Seconds ON , seconds OFF) ?\n");//debug fflush(stdout);
          scanf("%d %d",&secbet,&secfail);//debug 
          printf("Hit any key to stop\n");
          value=nerr=0;  //value=# of tries, nerr=# of fails
          do      
          { value++;  //count attempts
            printf("Try #%d  ",value);fflush(stdout);
            spi_aux_pwr( AUX_ON_ALT, ss ); // modem now has power
            acm_is_on=1;  //the acm is on and set up for communication
            DelayMilliSecs( (ulong)(1000*secbet) );  //ON for secbet secs
            acm_off(); //turn off LOCAL         
            DelayMilliSecs( (ulong)(1000*secfail) );  //OFF for secfailt secs
        /*  nread=acm_Power_Up();
            if (nread<0)
            { nerr++;  //count failures
              printf(s_res,"FAIL",nerr,secfail); 
              acm_off(); //turn off LOCAL         
              DelayMilliSecs( (ulong)(1000*secfail) );  //secfail secs between tries
            }
            else
            { printf(s_res,"GOOD",nerr,secbet); 
              acm_off(); //turn off LOCAL         
              DelayMilliSecs( (ulong)(1000*secbet) );  //secbet secs between tries
            }*/
          } while( !SCIRxQueuedCount() );
          printf("\n");
        }
        break;
      case 'O' : 
        acm_off(); 
        printf("Modem is OFF\n");
        break; 
      case 'M' :
        printf("LOCAL mode = %d\n",LOCmode());
        break;
      case 'G':  //Get specified S register
        printf("Sreg to get? ");reply[0]='\0';
        SerInFlush(); // delete any old stuff 
        ReadCharsFromConsole(reply,sizeof(reply));printf("\n");//response in temp buffer
        regno=-1;sscanf(reply,"%d %d",&regno,&value);
        if ( (nerr=GetSreg(-1,regno,&value,1 ))==0)
         {printf("S%02d=%d\n",regno,value);
         }
        else
         {printf("Err=%d\n",nerr);
         }
        break;
      case 'S':  //Set specified S register
        printf("Sreg, value_to_set? ");reply[0]='\0';
        SerInFlush(); // delete any old stuff 
        ReadCharsFromConsole(reply,sizeof(reply));printf("\n");//response in temp buffer
        comma2space(reply);//remove any commas
        regno=-1;sscanf(reply,"%d %d",&regno,&value);
        if ( (nerr=SetSreg(-1,regno ,value,1 ))==0)  //single try for LOCAL
         {printf("OK\n");
         }
        else
         {printf("Err=%d\n",nerr);
         }
        break;
      case 'A':  //display all Sregisters
        nread=all_Sregs(-1);  //local modem is remID=-1, read into REMrep
        if(nread>1)
         {printf("S-regs:\n");
          printf("%s\n",REMrep);
         }
        else
          printf("Err=%d\n",nread);
        break;
      case 'B':  //Get & change acoustic baud rate of LOCAL
        if ( (nerr=GetBaud(-1, &value,1))>=0)
        { //current baud is in value
          nerr=value; //save orig value & Get new value or /
          ChangeShort("Ac baud ",&value);
          if( value != nerr )
          { //a new value was entered
            nerr=SetBaud( -1 , value, all.acm,1 );
            printf("Ac baud change ");
            if(nerr==0)
              printf("OK\n");
            else printf("Err=%d\n",nerr);
          }
        }
        else
        { printf("Get Ac baud err=%d\n",nerr);
        }
        break;
      case 'V':  //read battery voltages
        printf("Local batts: ");
        nread=GetBatt(-1,&XMITv,1); //read local battery
        if (nread>=0)
          printf("OK  Xmit=%5.1fV\n",XMITv);
        else
          printf("FAIL  err=%d\n",nread);
        break;
      case 'N':  //Ambient AGC to estimate noise
        printf("Amb AGC = %d\n",AmbAGC());
        break;
      case 'L':  //Assign a new lifo
        all.klifo = assign_lifo(all.lifo, iparam [ EA_prof_num ],0);        
        break;        
      case 'F':  //debug phony fill of lifo
        { struct acm_param *ACM;
          ACM=all.acm;  //points to ACM struct for params.
          ACM->buf[0] = '\0';
          cprintf("how many bytes to load:");
          get_num(&nread);
          
          for (ACM->by_read=0;ACM->by_read<nread;ACM->by_read++){ACM->buf[ACM->by_read]='X';}
          all.klifo = assign_lifo ( all.lifo,  iparam [ EA_prof_num ], 0 );
          lifo_acraw();
          
        }
        break;
        //*/
      default  : printf(" ??\n");
 	} // end switch
  }  // ** end while 

  acm_off();  // make sure modem is off
  printf("Modem is off\n");

  return;
 }  // end test_acm

void menu_acm(void)
{ //print out menu selections 
  printf("Acoustic Modem\n"
         "Q.uit      : U.pdt menu : R.EMOTE\n"
         "W.ake LOC  : O.ff       : T.ransparent : M.ode?\n"
         "V.oltages  : B.aud      : N.oiseAGC    : C.ycle pwr\n"
         //"A.ll Sregs : S.et Sreg  : G.et Sreg    : F.ill phony\n");
         "A.ll Sregs : S.et Sreg  : G.et Sreg    : P.wr On\n"
         "D.o_acm    : L. Assign LIFO\n");
  return;
} // end menu_acm

void GetModemInfo( short Index )
{ //Copy info from database for modem Index into ACM->modem
  struct acm_param *ACM;
  ACM= all.acm;
  
  ACM->modem.id     =ACM->modlist[Index].id;
  ACM->modem.baud   =ACM->modlist[Index].baud;
  ACM->modem.ib_new =ACM->modlist[Index].ib_new;
  ACM->modem.nb_new =ACM->modlist[Index].nb_new;
  ACM->modem.ib_rpt =ACM->modlist[Index].ib_rpt;
  ACM->modem.nb_rpt =ACM->modlist[Index].nb_rpt;
  ACM->modem.dft_pit=iparam[IA_acm_ptch]; //28Mar10 use EEPROM val 
                                              //for all modems.Modlist.dft_pit
                                              //is no longer used or specified  
  ACM->modem.minpwr =ACM->modlist[Index].minpwr;
  ACM->modem.pmargn =ACM->modlist[Index].pmargn;
  ACM->modem.nsub   =ACM->modlist[Index].nsub; 
  return;
} //end GetModemInfo
 
void acm_dbase( void )  //manage modem database
{ char ch='?';
  short nmod,tmo=0;
  struct acm_param *ACM;
  
  ACM= all.acm;
  menu_acm_dbase();
 
  while ( ch !='Q')
  { SerInFlush(); // delete any old stuff 
      if (ch !=' ' && ch>0 ) cprintf("A_3>\n"); // display directory prompt
	  ch = -1; // illegal value - means it timed out
	  ch = SCIRxGetCharWithTimeout( 1000 ); // get a new character
	  tmo = check_tmo ( &ch, tmo );
	  // increments the tmo counter if ch<0, and
	  // goes to sleep mode after max_tmo.  Returns *ch as upper-case
	  
	  switch (ch)
	  { // call the desired function
        case 'Q': break;  // will quit the fn
        case '?':  break; // causes directory prompt to be displayed
        case  -1 :  break; // wait for another character
        case 'U':   menu_acm_dbase(); break;
        case 'I':  //initialize modem database
		  //initialize selected elements of the modem database
		  printf("Database has modem indices 0 thru %d\n",MAX_MODEMS-1);
		  printf("Index of modem to initialize (-1 = all) ?  ");fflush(stdout);
		  nmod=MAX_MODEMS+1;scanf("%d",&nmod); if(nmod>MAX_MODEMS||nmod<-1)break;
		  cprintf("\n");
		  if(nmod==-1)
		  { for(nmod=0;nmod<MAX_MODEMS;nmod++)  //clear all database
		    { ACM->modlist[nmod].id=-1;    
		      ACM->modlist[nmod].baud=600; 
		      ACM->modlist[nmod].ib_new=0;
		      ACM->modlist[nmod].nb_new=1;
		      ACM->modlist[nmod].ib_rpt=0;
		      ACM->modlist[nmod].nb_rpt=0;
		      ACM->modlist[nmod].dft_pit=0;	    
		      ACM->modlist[nmod].minpwr=8;
		      ACM->modlist[nmod].pmargn=2;
		      ACM->modlist[nmod].nsub=8; 
		    }
		  }
		  else
		  { ACM->modlist[nmod].id=-1;     //no modem at this waypoint
		    ACM->modlist[nmod].baud=600;  //600 baud is the default
		    ACM->modlist[nmod].ib_new=0;  //GS has no data from this REMOTE so start
	                                      //   with the first block
		    ACM->modlist[nmod].nb_new=1;  //but only send 1 block per dive
		    ACM->modlist[nmod].ib_rpt=0;  //block 0 is first old block but
		    ACM->modlist[nmod].nb_rpt=0;  //don't send any old data
		    ACM->modlist[nmod].dft_pit=0; //don't change pitch at start of ac comms		    
		    ACM->modlist[nmod].minpwr=8;  //Don't adjust remote power below 8
		    ACM->modlist[nmod].pmargn=2;  //Inc. REMOTE Xmit pwr by pmargn steps above lowest success
		    ACM->modlist[nmod].nsub=8;    //Xfer data in 2K blocks 
		  }
		  //Copy info into struct ACM->modem used for next modem to be accessed
		  GetModemInfo(0);
		  break;
    case 'D':  //display modem database
		  printf("Database has modem indices 0 thru %d\n",MAX_MODEMS-1);
		  printf("Index of modem to display?  ");fflush(stdout);
		  nmod=MAX_MODEMS+1;scanf("%d",&nmod); if(nmod>MAX_MODEMS||nmod<0)break;
          printf("\nid     =%d\n",ACM->modlist[nmod].id);
          printf("baud   =%d\n",ACM->modlist[nmod].baud);
          printf("ib_new =%d\n",ACM->modlist[nmod].ib_new);
          printf("nb_new =%d\n",ACM->modlist[nmod].nb_new);
          printf("ib_rpt =%d\n",ACM->modlist[nmod].ib_rpt);
          printf("nb_rpt =%d\n",ACM->modlist[nmod].nb_rpt);
          printf("minpwr =%d\n",ACM->modlist[nmod].minpwr);
          printf("pmargn =%d\n",ACM->modlist[nmod].pmargn);
          printf("nsub   =%d\n",ACM->modlist[nmod].nsub);
          break;
    case 'C':  //change modem info
		  printf("Database has modem indices 0 thru %d\n",MAX_MODEMS-1);
		  printf("Index of modem to change?  ");fflush(stdout);
		  nmod=MAX_MODEMS+1;scanf("%d",&nmod); if(nmod>MAX_MODEMS||nmod<0)break;
		  ChangeShort("\nModemID (-1 to terminate dbase)",&ACM->modlist[nmod].id);
		  ChangeShort("Baud",&ACM->modlist[nmod].baud);
		  ChangeShort("first new blk",&ACM->modlist[nmod].ib_new);
		  ChangeShort("# of new blks",&ACM->modlist[nmod].nb_new);
		  ChangeShort("first old blk",&ACM->modlist[nmod].ib_rpt);
		  ChangeShort("# of old blks",&ACM->modlist[nmod].nb_rpt);
		  ChangeShort("Min REM pwr",&ACM->modlist[nmod].minpwr);
		  ChangeShort("REM pwr margin",&ACM->modlist[nmod].pmargn);
		  ChangeShort("#sub-blks/4K block",&ACM->modlist[nmod].nsub);
		  break;
		case 'T':  //quick test setup
		  for(nmod=0;nmod<MAX_MODEMS;nmod++)  //clear all database
		    { ACM->modlist[nmod].id=-1;    
		      ACM->modlist[nmod].baud=600; 
		      ACM->modlist[nmod].ib_new=0;
		      ACM->modlist[nmod].nb_new=1;
		      ACM->modlist[nmod].ib_rpt=0;
		      ACM->modlist[nmod].nb_rpt=0;
		      ACM->modlist[nmod].dft_pit=0;	    
		      ACM->modlist[nmod].minpwr=8;
		      ACM->modlist[nmod].pmargn=2;
		      ACM->modlist[nmod].nsub=8; 
		    }
		    ACM->modlist[0].id=0;    
		    ACM->modlist[0].baud=600; 
		    ACM->modlist[0].ib_new=4;
		    ACM->modlist[0].nb_new=1;
		    ACM->modlist[0].ib_rpt=0;
            ACM->modlist[0].nb_rpt=0;
		    ACM->modlist[0].dft_pit=0;	    
		    ACM->modlist[0].minpwr=8;
		    ACM->modlist[0].pmargn=2;
		    ACM->modlist[0].nsub=8; 
		  break;		  
        default  : printf(" ??\n");
	  } // end switch
  }   // ** end while 

  return;
}   // end acm_dbase

void menu_acm_dbase(void)
{//print out menu selections 
  printf("Modem Config\n"
         "Q.uit : U.pdt menu \n"
         "I.nit : D.isplay   : C.hange\n");
  return;
} // end menu_acm_dbase

void acm_remote( void )  //functions for remote modem
{ char ch='?';
  short nread,nerr,value,regno,Ntry,tmo=0;
  long l;
  float XMITv;
  char reply[450];
  struct acm_param *ACM;

  ACM=all.acm;

  menu_acm_rem();
  ACM->remID=99;  //Deck Modem has reserved ID=99
  printf("\nremID = %d\n",ACM->remID);
 
  while ( ch !='Q')
  { SerInFlush(); // delete any old stuff 
      if (ch !=' ' && ch>0 ) cprintf("A_2>\n"); // display directory prompt
	  ch = -1; // illegal value - means it timed out
	  ch = SCIRxGetCharWithTimeout( 1000 ); // get a new character
	  tmo = check_tmo ( &ch, tmo );
	  // increments the tmo counter if ch<0, and
	  // goes to sleep mode after max_tmo.  Returns *ch as upper-case
	  
	  switch (ch)
	  { // call the desired function
      case 'Q': break;  // will quit the fn
      case 'U':   menu_acm_rem(); break;
      case '?' : break;  // displays the directory prompt
      case  -1 :  break; // wait for another character
      case 'C':  //change remID
        SerInFlush(); // delete any old stuff 
        printf("remID? ");  //which remote?
        ACM->remID=-1; scanf("%d",&ACM->remID);
        printf("remID=%d\n",ACM->remID);
        break;
      case 'M' :  //Manage modem data base
        acm_dbase();
        break;
      case 'L':  //Link quality
        value=LinkQ(ACM->remID,&nread,ACM);
        printf("LinkQ  ",ACM->remID);
        if ( value>=0 )
         {printf("OK\n");
         }
        else
         {printf("Err=%d\n",value);//unable to communicate
         }
        printf("%s\n",REMrep);
        break;
      case 'R':  //Range to remote
        printf("Number of tries? ");Ntry=1;scanf("%d",&Ntry);
        nread=RemRange(ACM->remID ,&value,Ntry);  //get range from remote modem
        printf("Rng  ");
        if( nread >=0 )  
         {printf("%d m\n",value);
         }
        else
         {printf("Err=%d\n",nread);
         }
        break;
      case 'V':  //read remote battery voltages
        printf("Number of tries? ");Ntry=1;scanf("%d",&Ntry);
        nread=GetBatt(ACM->remID,&XMITv,Ntry); //read remID battery
        printf("Batts: ");
        if (nread>=0)
          printf("OK  Xmit=%5.1fV\n",XMITv);
        else
          printf("Err=%d\n",nread);
        break;
      case 'B':  //Get & change acoustic baud rate of remote
        printf("Number of tries? ");Ntry=1;scanf("%d",&Ntry);
        if ( (nerr=GetBaud(ACM->remID, &value,Ntry))>=0)   //single try
        { //current baud is in value
          nerr=value; //save orig value & Get new value or /
          ChangeShort("Ac baud ",&value);
          if( value != nerr )
          { //a new value was entered
            nerr=SetBaud( ACM->remID , value, ACM,Ntry );
            printf("Ac baud change ");
            if(nerr==0)
              printf("OK\n");
            else printf("Err=%d\n",nerr);
          }
        }
        else
        { printf("Get Ac baud err=%d\n",nerr);
        }
        break;
      case 'P':  //Set acoustic power level of remote to max
        printf("Number of tries? ");Ntry=1;scanf("%d",&Ntry);
        nerr=SetPower( ACM->remID , 8 , Ntry ); //S6=8 is max power output, 0 is -21dB
        printf("MaxPwr: ");
        if(nerr>=0)
          printf("OK\n");
        else printf("FAIL err=%d\n",nerr);
        break;
      case 'F':  //get # bytes of data at remote  
        printf("Number of tries? ");Ntry=1;scanf("%d",&Ntry);
        nerr=DataBy(ACM->remID ,&l,Ntry);  //l=(long) # bytes stored in remote
        printf("%ld bytes  err=%d\n",l,nerr);
        break;
      case 'D':  //display of last Xfer
        printf("ID=%d  battV=%d  Sreg2=%d  REMpkt=%d  LOCbatt=%d\n"
               "remBy=%ld  indx_rec=%ld  blk_secs=%d AGCamb=%d\n"
               "blk=%d  by_read=%d\n"
               "Rd_try=%d  Rd_err=%d  Xtry=%d\n"
               "Xerr=%d  secs=%d  dpth=%d  rng=%d\n"
               "Lnktry=%d  baud=%d  ber=%d  snr=%d\n"
               "agc=%d  spd=%d  mpd=%d  CRC=%d\n"
               "n_blk=%d  ln_blk=%d  o_blk=%d  lo_blk=%d\n",
                     ACM->remID,ACM->battV,ACM->Sreg2,ACM->rem_pkt,ACM->LocBatt,
                     ACM->rem_bytes,ACM->indx_recd,ACM->blk_secs,ACM->AGCamb,
                     ACM->this_blk,ACM->by_read,
                     ACM->RdB_tries,ACM->RdB_err,ACM->StX_tries,
                     ACM->Xfer_err,ACM->eb_secs,ACM->depth,ACM->range,
                     ACM->Lnk_tries,ACM->baud,ACM->ber,ACM->snr,
                     ACM->agc,ACM->spd,ACM->mpd,ACM->CRC,
                     ACM->new_blk,ACM->lnew_blk,ACM->old_blk,ACM->lold_blk);
        break;
      case 'A':  //display all S registers
        nread=all_Sregs(ACM->remID);//read into REMrep
        printf("S-regs:");
        if(nread>1)
        {printf("OK\n%s\n",REMrep);
        }
        else
          printf("err=%d\n",nread);
        break;
      case 'G':  //Get specified S register
        printf("Number of tries? ");Ntry=1;scanf("%d",&Ntry);
        printf("Sreg to get? ");reply[0]='\0';
        SerInFlush(); // delete any old stuff 
        ReadCharsFromConsole(reply,sizeof(reply));printf("\n");//response in temp buffer
        regno=-1;sscanf(reply,"%d %d",&regno,&value);
        if ( (nerr=GetSreg(ACM->remID,regno,&value,Ntry ))==0)
         {printf("S%02d=%d\n",regno,value);
         }
        else
         {printf("Err=%d\n",nerr);
         }
        break;
      case 'S':  //set REMOTE Sreg
        printf("Number of tries? ");Ntry=1;scanf("%d",&Ntry);
        printf("Sreg, value_to_set? ");reply[0]='\0';
        SerInFlush(); // delete any old stuff 
        ReadCharsFromConsole(reply,sizeof(reply));printf("\n");//response in temp buffer
        comma2space(reply);//remove any commas
        regno=-1;sscanf(reply,"%d %d",&regno,&value);
        if ( (nerr=SetSreg(ACM->remID,regno ,value,Ntry ))==0)  //Make a single try
         {printf("OK\n");
         }
        else
         {printf("Err=%d\n",nerr);
         }
        break;
      case 'Z':  //write last data rec'd by GetTSdata to CF1 and to Iridium LIFO
        //writes acoustic data to Iridium Lifo in a new packet w/id=0
        all.klifo = assign_lifo(all.lifo,iparam[EA_surf_num],0);
        printf("LIFO index = %d\n",all.klifo);  
        lifo_acdiag();    
        lifo_acraw(); 
        break;
      case 'O':  //execute OpenAcLnk3
        nerr=OpenAcLnk3();
        printf("OpenAcLnk3 err=%d\n",nerr);
        break;
      default  : printf(" ??\n");
    } // end switch
  }  //end while 

  return;
}  // end acm_remote

void menu_acm_rem(void)
{ //print out menu selections 
  printf("Remote Modem\n"
         "Q.uit       : U.pdt menu : C.hng ID : M.odemConf\n"
         "L.ink qual  : R.ange     : V.olts   : B.aud      : F.lash filld : G.et Sreg\n"
         "A.ll Sregs  : Z blk->LIFO: P.wr HI  : S.et Sreg  : D.isp stat   : O.pnAcLnk\n");
  return;
} // end menu_acm_rem

//  LIFOs special to BENTHOS version==============================================

uchar isubuf[1960];  //working buffer for Iridium message

//*24Mar2010--remove lifo_acdat since only use lifo_acraw

void lifo_acraw( void ) //** queue up raw acm data messages
{ //break the Ac modem buffer into Iridium messages <= 1944 bytes including segmentation info
  //and queue each to Iridium.
  //This is same function as lifo_acdat except the sensor ID code is 0xa1 and
  //the message contains all the header, trailer info.  Thus the byte index "aa"
  //is the byte index within the RAW message and not just the DATA portion
  //Each segment has the format:
  //     IDjjrbaa<modem data>;
	// where  ID	=	one-byte sensor ID code = 0xa0.
	//        jj	=	Number of bytes in the message.  
	//              The count includes ID, jj, the data, and the trailing ;
	//              The count is in 2 binary bytes with MSB first and LSB second.
	//              The maximum value that fits in an Iridium message is 
	//              1944 bytes(limited by append_lifo).
	//         r	=	Remote modem ID (0,..,99) in one byte. 
	//         b	=	index of 4K block in Remote modem's memory  (0,..,175) in one byte. 
	//        aa	=	index in that block of first byte of <modem data> (0,..,4K) in two bytes
	//  <modem data>	=	The modem data without editing. up to 1936 bytes.


  short j,n,istor;
  short blksiz=1936;  //bytes of ac data per block
  uchar *gptr;
  //uchar dbg[80];
  struct acm_param *ACM;
  ACM = all.acm;
  
  gptr=ACM->buf;  //initialize get pointer to start of buf
  
  n=0;  //index of current byte being moved to isubuf
  while ( n < ACM->by_read )
  { //still have bytes to queue up
    isubuf[0] = 0xa1;  //byte 0 = ID that this is RAW modem data
    //  bytes 1,2 = message length  (fill later)
    istor=3; //start storage pointer
    isubuf[istor++]= ACM->remID ;  //modem ID
    isubuf[istor++]= ACM->this_blk;  //current block index
    isubuf[istor++] = (uchar)(n >> 8);  // = MSB of index of 1st byte in this segment
    isubuf[istor++] = (uchar)(n & 0xff); //  LSB

    //move acm buffer into global buffer isubuf starting in byte 7
    for(j=0;j<blksiz;j++)
    { isubuf[istor++] = *gptr++;
      n++;  //count bytes moved from buffer so far (into any&all LIFOs)
      if(n == ACM->by_read)break;//hit the end of the buffer
    };
    
    isubuf[istor++] =';'; //append ';' to tag the end of message
    //now istor= # of bytes in this message
    
    isubuf[1] =  (uchar)(istor >> 8); // = MSB of number of bytes
    isubuf[2] =  (uchar)(istor & 0xff); // LSB
printf("\ndebug lifo_acraw\n");debug_string( isubuf, istor);  //debug display

    append_lifo(istor,isubuf); //queue message with istor bytes to LIFO
  }
  
  return;
}  //end lifo_acraw

void lifo_acdiag(void) //** queue up acm diag message
{ //move first part of ACM struct into LIFO.  It tracks current status

  uchar dat[255];  //temp area to receive ACM (24Feb08 ACM has 72 bytes)
  uchar dbg[80];
  uchar *ptr,*e_ptr; //ptrs used to transfer structure
  struct acm_param *ACM;
  short i;
  
  ACM = all.acm;

  dat[0] = 0xad; //Assign ID = 0xad = acoustic diagnostic
  i = 3; // dat[1], dat[2] = #bytes in the modem info message; set at end of fnx

  ptr=ACM;              //transfer from the beginning of ACM struct to,
  e_ptr=&ACM->end_diag; //but not including, end_diag
  
  while( ptr < e_ptr ) 
  { dat[i++]=*ptr++;
  }
  
  dat[i++] = ';'; // end-of-message char
  //Now i=# of bytes in this message : includes 0,1,2 bytes + ';'
  b2_store(1,dat,i);  //put length MSB in dat[1] and LSB in dat[2]
  
  append_lifo(i,dat); // append to present lifo
  
  //write the acdiag message to CF1 flash
  sprintf(dbg,"ACDIAG   %6ld %4d\n",RTCGetTime(0,0),i);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);  //header line with source, time, # bytes
  buf_to_cf1V( i, dat);  //copy the LIFO contents with tags
   
  return;
} //end lifo_acdiag

void lifo_acenergy(short REMid,short xmits) //** queue up acm energy usage
{
  uchar dat[255];  //temp area
  uchar dbg[80];
  struct acm_param *ACM;
  short i;
  ACM = all.acm;

  dat[0] = 0xae; //Assign ID = 0xad = acoustic diagnostic
  i=3;
  
  dat[i++]=REMid;
  dat[i++]=xmits;
  
  dat[i++] = ';'; // end-of-message char
  //Now i=# of bytes in this message : includes 0,1,2 bytes + ';'
  b2_store(1,dat,i);  //put length MSB in dat[1] and LSB in dat[2]
  
  append_lifo(i,dat); // append to present lifo
  
  //write the acdiag message to CF1 flash
  sprintf(dbg,"ACENERGY %6ld %4d\n",RTCGetTime(0,0),i);
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);  //header line with source, time, # bytes
  buf_to_cf1V( i, dat);  //copy the LIFO contents with tags
   
  return;  
  

}
void lifo_accmd(char cmd[],short ncmd,char rep[],short nrep) //** queue up acm command&reply message
{ //Each message has the format:
  //     IDjjkk<cmd str>mm<rep str>;
  //  where  ID	=	one-byte sensor ID code = 0xac.
  //         jj	=	Number of bytes in the message (inclusive from ID to trailing '';
  //         kk =   Number of bytes in the command string (max 40)   
  //  <cmd str> =  The command string
  //         mm =   error code if negative else = length of reply string
  //                  If negative, there is no reply string in the message
  //                  If positive,mm is reduced to keep jj <= 1944
  //  <rep str> =  The reply (or error) string from the modem
      
  uchar dbg[80];
  short istor,nc2stor,nr2stor;

  isubuf[0] = 0xac; //Assign ID = 0xac = acoustic command and reply message
  //  bytes 1,2 = message length  (fill later)
  istor = 3; // isubuf[1], isubuf[2] = #bytes in the modem info message; set at end of fnx
  
  nc2stor= ncmd<40 ? ncmd:40; //send at most 40 chars of command string
  isubuf[istor++] = (uchar)(nc2stor >> 8);  // = MSB of length of command string
  isubuf[istor++] = (uchar)(nc2stor & 0xff); //  LSB
  strncpy(&isubuf[istor],cmd,nc2stor);  //copy the command string (drops \0 at end)
  istor += nc2stor; //bump storage pointer
  
  //make sure things fit in total 1944 bytes from ID to ';' at the end
  nr2stor=1944 - 8 - nc2stor; //8 bytes in ID,jj,kk,mm,';' nr2stor=max left for reply
  if(nrep>0)
  { //there is a reply string--don't send back more than fits
    nr2stor = nrep<nr2stor ? nrep:nr2stor;
    isubuf[istor++] = (uchar)(nr2stor >> 8);  // = MSB of length of reply string
    isubuf[istor++] = (uchar)(nr2stor & 0xff); //  LSB
    strncpy(&isubuf[istor],rep,nr2stor);  //copy the reply string (drops \0 at end)
    istor += nr2stor; //bump storage pointer
  }
  else
  { //no reply string due to an error so only return the status
    isubuf[istor++] = (uchar)(nrep >> 8);  // = MSB of error status
    isubuf[istor++] = (uchar)(nrep & 0xff); //  LSB
  }
  
  isubuf[istor++] = ';'; // end-of-message char
  
  //Now istor=# of bytes in this message : includes 0,1,2 bytes + ';'
  isubuf[1] =  (uchar)(istor >> 8); // = MSB of number of bytes
  isubuf[2] =  (uchar)(istor & 0xff); // LSB
  
  append_lifo(istor,isubuf); //queue message with istor bytes to LIFO
  
  //write the accmd message to CF1 flash
  sprintf(dbg,"ACCMD    %6ld %4d\n",RTCGetTime(0,0),istor); // status & command string to CF1
  printf("%s",dbg);
  buf_to_cf1V( strlen(dbg),dbg);
  buf_to_cf1V( istor,isubuf );  //copy LIFO contents
  return;
} //end lifo_accmd

 void debug_string( char string[], short nchar)
  {//display  string of nchar bytes in ASCII and Hex
   short i;
   //ASCII display
   printf("%d by ASC:\n",nchar);
   for(i=0;i<nchar;i++)
     printf("%c",string[i]);  
   printf("\n%d by Hex:\n",nchar);
   for(i=0;i<nchar;i++)
     {printf("%02x ",(uchar)string[i]);
      if( i%20 == 19)printf("\n");
     }
   if(i%20 != 0)printf("\n");
   return;
  } //end debug_string
  
void buf_to_cf1V( long nbytes , uchar *bufptr)  //buffer to flash file
{ //send buffer pointed to by ptr to Flash file in blocks of 200 bytes
  long nblks,l;
  short blksiz=200,nlast;  //bytes in full,last (partial) block
  short j;
  uchar *ptr;
  
  ptr=bufptr;
  nblks=nbytes/blksiz;  //number of full blocks of blksiz bytes
  nlast=nbytes-nblks*blksiz;  //bytes in last partial block
  
  for(l=0;l<nblks;l++)
  { //write one block of 200 bytes (1st byte=# of bytes to follow)
    j=0; // counter of #tries writing to cf1
    flash_store( blksiz, ptr, CF_ACM ); // transfer to the flash
	ptr += blksiz;  //bump to next block
  }
  if(nlast>=0)
  { //finish last partial block (could have zero bytes of data)
    j=0; // counter of #tries writing to cf1
	flash_store( nlast, ptr,CF_ACM ); // transfer to the cf1
  }
}  //end buf_to_cf1V      

void comma2space( char * bptr )
 {//replace commas in bptr with spaces
  char *c_ptr;
  while( (c_ptr=strchr(bptr,',')) !=NULL )
   {//found a comma
    *c_ptr=' ';//change it to a space
   }
  return;
 }  //end comma2space

void ChangeShort(char label[], short *ptr)
{ //change the short value pointed to by ptr
  //combines calls to save code space by avoiding repetition
  char ans[80];
  printf("%s = %d  New value (/=no change) ?",label,*ptr);
  ans[0]='\0';
  SerInFlush(); // delete any old stuff 
  ReadCharsFromConsole(ans,sizeof(ans));printf("\n");
  comma2space(ans);//remove any commas from reply
  if( (strlen(ans)> 0) && (ans[0]!='/') ) sscanf(ans,"%d",ptr);
  return;
} //end ChangeShort

//*****************************************************************************
void dbg_ac_test( void )
{ //if ee_acm_mode=1 test acoustics at bottom of dive
  //if ee_acm_mode=2 test acoustics at ascend

  printf("Increasing profile number.....");
  incr_prof_num(); //for testing, just increment the dive number
  printf("DONE\n");
  //open a CF file
  printf("Opening a flash file....");
   flash_open ('D');
   printf("DONE\n");
  //assign a new lifo
  printf("Assigning a LIFO.....");
  all.klifo = assign_lifo(all.lifo, iparam [ EA_prof_num ],0);
  printf("DONE\n");
  printf("do_acm\n");
  do_acm(); 
  
  //close the CF file
   flash_close(); 
   
  //printf("Secs=%ld Ret from do_acm\n",RtcToCtm());
  
  /*
  uchar *gps_ptr;  // points to gps data
  uchar dbg[255];
  short ngps; //=#points in gps
  struct gps_param gps;  // used to give sizeof(gps) for cf1 xfr
  short p,mode;
  short stat;
  
  gps_ptr = (uchar*) all->gps;  // points to gps data
  ngps = sizeof(gps);  //#bytes in gps structure
  all->hyd->total=0; //clear total pump time in this debug test
  all->hyd->start=RtcToCtm()-10; //phony start time for pump is 10 seconds ago


  p = all->flt->pmin = 250;  // default value
  all->dat->i = 0;  //reset profile ram data buffer to 1st record
  all->dat->ie= 0;  // 0612: reset eng. ram data buffer to 1st record
  all->dat->isamp=0; // counter of #data samples for this dive
  all->dat->cf1= 1;  // 0901 enable cf1 writes
  all->hyd->total=0; //clear total pump time in this debug test
  
  all->klifo = assign_lifo(all->lifo,all->mis->idive,0);//lifo for data

  stat=cf1_fupdate();  //skip this update to rely on go_drift
  take_data(all);
  printf("Secs=%ld ret fm take_data Batt=%d\n",
          RtcToCtm(),all->dat->ad_bat);
  
  mode= UeeGetWord(ee_acm_mode);
     if (mode==1)
     { //we want to do AC comms while drifting in bottom_turn
       // descend(all); 
       if( PumpIsOn() )  //Is the pump running? 4Nov2009
       {  //Hydraulic pump is running--
          sprintf(dbg,"BOTTOM   %6ld Pump ON  PE=0x%04x hyd->total=%d\n",
                      RtcToCtm(),*PORTE,all->hyd->total);
          buf_to_cf1V( strlen(dbg),dbg);  
          printf("%s",dbg);
          Pump_Off(all->hyd); //kill pump to be quiet. this updates hyd->total
          sprintf(dbg,"BOTTOM   %6ld Pump is OFF PE=0x%04x hyd->total=%d\n",
                      RtcToCtm(),*PORTE,all->hyd->total);
          buf_to_cf1V( strlen(dbg),dbg);  
          printf("%s",dbg);
       } 
       else
       {  sprintf(dbg,"BOTTOM   %6ld Pump is OFF PE=0x%04x hyd->total=%d\n",
                      RtcToCtm(),*PORTE,all->hyd->total);
          buf_to_cf1V( strlen(dbg),dbg);  
          printf("%s",dbg);
       }
     
       do_acm(all);  
       printf("Secs=%ld Ret from do_acm\n",RtcToCtm());
     } 
     else if ( mode ==2 )
     {  //do ac comms in ascend
        ascend(all);
        printf("Secs=%ld Ret from ascend\n",RtcToCtm());
     }
	
  cf1_fupdate();
  //*/
  return;

}



#endif
//*****************************************************************************
//*** end file ****************************************************************
//*****************************************************************************