Logo Search packages:      
Sourcecode: xdemorse version File versions  Download package

main.c

/*
 * Initial main.c file generated by Glade. Edit as required.
 * Glade will not overwrite this file.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>
#include "interface.h"
#include "support.h"
#include "xdemorse.h"

/* xdemorse main window */
GtkWidget *main_window;

/* Global widgets */
GtkWidget
  *gbl_rx_scrolledwindow,
  *gbl_scope,
  *gbl_waterfall,
  *gbl_scope_label;

/* Speed and squelch spin button */
GtkSpinButton
  *gbl_speed,
  *gbl_squelch;

/* Text buffer for text viewer */
GtkTextBuffer *gbl_rx_text_buffer;

int
  dsp_fd, /* File descriptor of dsp device   */
  mix_fd; /* File descriptor of mixer device */

/* Signal handler */
static void sig_handler( int signal );

/* Runtime config data */
rc_data_t rc_data;

/* Waterfall window pixbuf */
GdkPixbuf *gbl_wfall_pixbuf = NULL;
guchar *gbl_wfall_pixels;
gint
  gbl_wfall_rowstride,
  gbl_wfall_n_channels,
  gbl_wfall_width,
  gbl_wfall_height;

/*------------------------------------------------------------------------*/

  int
main (int argc, char *argv[])
{
  /* Main window */
  GtkWidget *main_window;

  /* Command line option returned by getopt() */
  int option;

  /* getopt() variables */
  extern char *optarg;
  extern int optind, opterr, optopt;

  /* New and old actions for sigaction() */
  struct sigaction sa_new, sa_old;

  /* Initialize new actions */
  sa_new.sa_handler = sig_handler;
  sigemptyset( &sa_new.sa_mask );
  sa_new.sa_flags = 0;

  /* Register function to handle signals */
  sigaction( SIGINT,  &sa_new, &sa_old );
  sigaction( SIGSEGV, &sa_new, 0 );
  sigaction( SIGFPE,  &sa_new, 0 );
  sigaction( SIGTERM, &sa_new, 0 );
  sigaction( SIGABRT, &sa_new, 0 );

  gtk_set_locale ();
  gtk_init (&argc, &argv);

  Set_Flag( ADAPT_SPEED | DISPLAY_SIGNAL);
  rc_data.unit_elem = 15;

  /* Process command line options */
  while( (option = getopt(argc, argv, "hlv") ) != -1 )
      switch( option )
      {
        case 'h': /* Print usage and exit */
            Usage();
            exit( 0 );

        case 'l':  /* Print available mixer devices */
            {
              /* Sound device names from souncard.h */
              char *dev_name[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
              int device;
              puts("Printing out mixer device names" );
              for( device = 0; device < SOUND_MIXER_NRDEVICES; device++ )
                  puts( dev_name[device] );
              exit( 0 );
            }

        case 'v': /* Print version */
            printf( "%s %s\n", PACKAGE, VERSION );
            exit( 0 );

        default: /* Print usage and exit */
            Usage();
            exit( -1 );

      } /* End of switch( option ) */

  /*
   * The following code was added by Glade to create one of each component
   * (except popup menus), just so that you see something after building
   * the project. Delete any components that you don't want shown initially.
   */
  main_window = create_main_window ();
  gtk_window_set_title( GTK_WINDOW(main_window), PACKAGE"-"VERSION );

  /* Get Rx text buffer and scroller */
  gbl_rx_text_buffer = gtk_text_view_get_buffer
      ( GTK_TEXT_VIEW(lookup_widget(main_window, "rx_textview")) );
  gbl_rx_scrolledwindow = lookup_widget( main_window, "rx_scrolledwindow" );

  /* Get widgets and spin buttons */
  gbl_scope       = lookup_widget( main_window, "scope" );
  gbl_waterfall   = lookup_widget( main_window, "waterfall" );
  gbl_scope_label = lookup_widget( main_window, "label1" );
  gbl_speed       = GTK_SPIN_BUTTON( lookup_widget(main_window, "speed") );
  gbl_squelch     = GTK_SPIN_BUTTON( lookup_widget(main_window, "squelch") );
  rc_data.det_thr = (int)gtk_spin_button_get_value( gbl_squelch );
  rc_data.spd_wpm = (int)gtk_spin_button_get_value( gbl_speed );

  gtk_widget_show (main_window);

  /* Initialize FFT */
  Ifft_init( FFT_SIZE );

  /* Load runtime config file, abort on error */
  gtk_idle_add( Load_Config, NULL );

  gtk_main ();

  return 0;
}

/*------------------------------------------------------------------------*/

/*  Load_Config()
 *
 *  Loads the xdemorserc configuration file
 */

  gboolean
Load_Config( gpointer data )
{
  char
      rc_fpath[64], /* File path to xdemorserc */
      line[81];     /* Buffer for Load_Line  */
      
  /* Config file pointer */
  FILE *xdemorserc;


  /* Setup file path to xdemorserc */
  snprintf( rc_fpath, 64, "%s/.xdemorserc", getenv("HOME") );

  /* Open xdemorserc file */
  xdemorserc = fopen( rc_fpath, "r" );
  if( xdemorserc == NULL )
  {
      perror( rc_fpath );
      Error_Dialog(
            "Failed to open xdemorserc file\n"
            "Quit xdemorse and correct" );
      return( FALSE );
  }

  /*** Read runtime configuration data ***/
  /* Read mixer dev, abort if EOF */
  if( Load_Line(line, xdemorserc, "Mixer Device" ) != SUCCESS )
      return( FALSE );
  strncpy( rc_data.mix_dev, line, 20 );
  rc_data.mix_dev[20] = '\0';

  /* Read input DSP dev, abort if EOF */
  if( Load_Line(line, xdemorserc, "Input DSP Device") != SUCCESS )
      return( FALSE );
  strncpy( rc_data.dsp_dev, line, 20 );
  rc_data.dsp_dev[20] = '\0';

  /* Read DSP speed Samples/sec,  abort if EOF */
  if( Load_Line(line, xdemorserc, "DSP Speed" ) != SUCCESS )
      return( FALSE );
  rc_data.dsp_speed = atoi( line );

  /* Read recording source, abort if EOF */
  if( Load_Line(line, xdemorserc, "Recording Source") != SUCCESS )
      return( FALSE );
  strncpy( rc_data.rec_src, line, 20 );
  rc_data.rec_src[20] = '\0';

  /* Read Recording level device, abort if EOF */
  if( Load_Line(line, xdemorserc, "Recording Level Device") != SUCCESS )
      return( FALSE );
  strncpy( rc_data.inp_lev, line, 20 );
  rc_data.inp_lev[20] = '\0';

  /* Read Recording level, abort if EOF */
  if( Load_Line(line, xdemorserc, "Recording Level") != 0 )
      return( FALSE );
  rc_data.rec_lev = atoi( line );

  /* Read stero/mono mode, abort if EOF */
  if( Load_Line(line, xdemorserc, "Stero|Mono Mode") != SUCCESS )
      return( FALSE );
  line[6] = '\0';
  if( strcmp(line, "STEREO") == 0 )
      rc_data.num_chn = 2;
  else if( strcmp(line, "MONO") == 0 )
      rc_data.num_chn = 1;
  else
  {
      Error_Dialog(
            "Error reading Stereo or Mono mode\n"
            "Quit and correct xdemorserc" );
      return( FALSE );
  }

  /* Read channel used, abort if EOF */
  if( Load_Line(line, xdemorserc, "Left|Right Channel") != SUCCESS )
      return( FALSE );
  line[5] = '\0';
  if( strcmp(line, "LEFT") == 0 )
      rc_data.use_chn = 0;
  else if( strcmp(line, "RIGHT") == SUCCESS )
      rc_data.use_chn = 1;
  else
  {
      Error_Dialog(
            "Error reading Stereo channel to use\n"
            "Quit and correct xdemorserc" );
      return( FALSE );
  }

  /* Read max WPM, abort if EOF */
  if( Load_Line(line, xdemorserc, "Maximum WPM") != SUCCESS )
      return( FALSE );
  rc_data.min_unit = atoi( line );

  /* Read min WPM, abort if EOF */
  if( Load_Line(line, xdemorserc, "Minimum WPM") != SUCCESS )
      return( FALSE );
  rc_data.max_unit = atoi( line );

  /* Check range of Morse speeds. At this point max_unit
   * holds minimum wpm and min_unit holds maximum wpm */
  if( (rc_data.max_unit < MIN_SPEED) ||
        (rc_data.min_unit > MAX_SPEED) )
  {
      Error_Dialog(
            "Morse code speed (WPM)\n"
            "range is out of limits\n"
            "Quit and correct xdemorserc" );
      return( FALSE );
  }

  /* Set spinbutton range */
  gtk_spin_button_set_range( gbl_speed,
        (gdouble)rc_data.max_unit, (gdouble)rc_data.min_unit );
 
  /* Read and check initial WPM, abort if EOF */
  if( Load_Line(line, xdemorserc, "Initial WPM") != SUCCESS )
      return( FALSE );
  int wpm = atoi( line );

  /* Check initial Morse speed. At this point max_unit
   * holds minimum wpm and min_unit holds maximum wpm */
  if( (wpm > rc_data.min_unit) ||
        (wpm < rc_data.max_unit) )
  {
      Error_Dialog(
            "Initial Morse code speed (WPM)\n"
            "is out of specified range\n"
            "Quit and correct xdemorserc" );
      return( FALSE );
  }

  /* Read word wrap column, abort if EOF */
  if( Load_Line(line, xdemorserc, "Word Wrap Column") != SUCCESS )
      return( FALSE );
  rc_data.wrd_wrp = atoi( line );

  /* Read FT-847 serial port device, abort if EOF */
  if( Load_Line(line, xdemorserc, "FT-847 Serial Port") != SUCCESS )
      return( FALSE );
  strncpy( rc_data.ft847_serial, line, 31 );
  rc_data.ft847_serial[31] = '\0';

  /* Read FT-857 serial port device, abort if EOF */
  if( Load_Line(line, xdemorserc, "FT-857 Serial Port") != SUCCESS )
      return( FALSE );
  strncpy( rc_data.ft857_serial, line, 31 );
  rc_data.ft857_serial[31] = '\0';

  /* Read CAT enable flag, abort if EOF */
  if( Load_Line(line, xdemorserc, "CAT Enable") != SUCCESS )
      return( FALSE );
  if( strcmp(line, "FT847") == 0 )
      rc_data.tcvr_type = FT847;
  else if( strcmp(line, "FT857") == 0 )
      rc_data.tcvr_type = FT857;
  else if( strcmp(line, "NONE") == 0 )
      rc_data.tcvr_type = NONE;
  else
  {
      rc_data.tcvr_type = NONE;
      Error_Dialog(
            "Error reading Transceiver type\n"
            "Quit and correct xdemorserc" );
  }

  /* Read input tone (Rx BFO) frequency */
  if( Load_Line(line, xdemorserc, "Input Tone") != SUCCESS )
      return( FALSE );
  rc_data.tone_freq = atoi( line );

  /* The tone freq must be rounded so that the fft_stride
   * is an integer otherwise the waterfall is not accurate */
  rc_data.fft_stride = rc_data.dsp_speed/rc_data.tone_freq/4;
  rc_data.tone_freq  = rc_data.dsp_speed/rc_data.fft_stride/4;

  /* Calculate parameters that depend on above */
  rc_data.max_unit = (60*rc_data.tone_freq) /
      (50*CYCLES_PER_FRAG*rc_data.max_unit);
  rc_data.min_unit = (60*rc_data.tone_freq) /
      (50*CYCLES_PER_FRAG*rc_data.min_unit);
  gtk_spin_button_set_value( gbl_speed, (gdouble)wpm );
  rc_data.unit_elem =
      (60 * rc_data.tone_freq) / (50 * CYCLES_PER_FRAG * wpm);
  rc_data.red_line = FFT_SIZE / 4;

  /* Select Transceiver type */
  switch( rc_data.tcvr_type )
  {
      case FT847:
        Set_Flag( ENABLE_CAT | ENABLE_CAT_847 );
        Open_Tcvr_Serial();
        break;

      case FT857:
        Set_Flag( ENABLE_CAT | ENABLE_CAT_857 );
        Open_Tcvr_Serial();
        break;

      case NONE:
        Clear_Flag( ENABLE_CAT );
  }

  /* Allocate memory to recv samples buffer */
  rc_data.buffer_idx = rc_data.buffer_size = BUFFER_SIZE * rc_data.num_chn;
  rc_data.buffer = malloc( (size_t)rc_data.buffer_size );
  if( rc_data.buffer == NULL )
  {
      Error_Dialog(
            "Memory allocation for buffer failed\n"
            "Quit xdemorse and correct" );
      return( FALSE );
  }

  fclose( xdemorserc );

  /* Enable receiving Morse code */
  if( Setup_Sound_Card() && Initialize_Detector() )
      Set_Flag( ENABLE_RECEIVE );

  return( FALSE );

} /* End of Load_Config() */

/*------------------------------------------------------------------*/

/*  Load_Line()
 *
 *  Loads a line from a file, aborts on failure. Lines beginning
 *  with a '#' are ignored as comments. At the end of file EOF is
 *  returned. Lines assumed maximum 80 characters long.
 */

  int
Load_Line( char *buff, FILE *pfile, char *messg )
{
  int
      num_chr, /* Number of characters read, excluding lf/cr */
      chr;     /* Character read by getc() */
  char err_msg[128];

  /* Prepare error message */
  snprintf( err_msg, 128,
        "Error reading %s\n"
        "Premature EOF (End Of File)", messg );

  /* Clear buffer at start */
  buff[0] = '\0';
  num_chr = 0;

  /* Get next character, return error if chr = EOF */
  if( (chr = fgetc(pfile)) == EOF )
  {
      fprintf( stderr, "xdemorse: %s\n", err_msg );
      Error_Dialog( err_msg );
      return( EOF );
  }

  /* Ignore commented lines, white spaces and eol/cr */
  while(
        (chr == '#') ||
        (chr == ' ') ||
        (chr == HT ) ||
        (chr == CR ) ||
        (chr == LF ) )
  {
      /* Go to the end of line (look for LF or CR) */
      while( (chr != CR) && (chr != LF) )
        /* Get next character, return error if chr = EOF */
        if( (chr = fgetc(pfile)) == EOF )
        {
            fprintf( stderr, "xdemorse: %s\n", err_msg );
            Error_Dialog( err_msg );
            return( EOF );
        }

      /* Dump any CR/LF remaining */
      while( (chr == CR) || (chr == LF) )
        /* Get next character, return error if chr = EOF */
        if( (chr = fgetc(pfile)) == EOF )
        {
            fprintf( stderr, "xdemorse: %s\n", err_msg );
            Error_Dialog( err_msg );
            return( EOF );
        }

  } /* End of while( (chr == '#') || ... */

  /* Continue reading characters from file till
   * number of characters = 80 or EOF or CR/LF */
  while( num_chr < 80 )
  {
      /* If LF/CR reached before filling buffer, return line */
      if( (chr == LF) || (chr == CR) ) break;

      /* Enter new character to line buffer */
      buff[num_chr++] = chr;

      /* Get next character */
      if( (chr = fgetc(pfile)) == EOF )
      {
        /* Terminate buffer as a string if chr = EOF */
        buff[num_chr] = '\0';
        return( SUCCESS );
      }

      /* Abort if end of line not reached at 80 char. */
      if( (num_chr == 80) && (chr != LF) && (chr != CR) )
      {
        /* Terminate buffer as a string */
        buff[num_chr] = '\0';
        snprintf( err_msg, 128,
              "Error reading %s\n"
              "Line longer than 80 characters", messg );
        fprintf( stderr, "xdemorse: %s\n%s\n", err_msg, buff );
        Error_Dialog( err_msg );
        return( ERROR );
      }

  } /* End of while( num_chr < max_chr ) */

  /* Terminate buffer as a string */
  buff[num_chr] = '\0';

  return( SUCCESS );

} /* End of Load_Line() */

/*------------------------------------------------------------------*/

/*  Print_Character()
 *
 *  Prints a character to a text view port
 */

  gboolean
Print_Character( gpointer data )
{
  /* Text buffer marker */
  static GtkTextIter iter;

  GtkAdjustment *adjustment;

  char
      dec_char,     /* Decoded Morse character */
      char2text[2]; /* Convert char to string  */

  /* Number of chars printed on a line */
  static int line_idx = 0;

  /* Print decoded characters with word wrap */
  if( (dec_char = Get_Character()) )
  {
      /* Do word wrapping */
      if( (line_idx++ > rc_data.wrd_wrp) &&
            (dec_char == ' ') )
      {
        dec_char = '\n';
        line_idx = 0;
      }

      /* Convert char to 'text' */
      char2text[0] = dec_char;
      char2text[1] = '\0';

      /* Print character */
      gtk_text_buffer_get_iter_at_offset( gbl_rx_text_buffer, &iter,
            gtk_text_buffer_get_char_count
            (gbl_rx_text_buffer) );

      gtk_text_buffer_insert( gbl_rx_text_buffer, &iter,
            char2text, -1 );

      /* Scroll Text View to bottom on LF */
      if( (dec_char == '\n') )
      {
        adjustment = gtk_scrolled_window_get_vadjustment
            ( GTK_SCROLLED_WINDOW(gbl_rx_scrolledwindow) );

        gtk_adjustment_set_value( adjustment, adjustment->upper );
      }

  } /* if( dec_char = Get_Character() ) */

  if( isFlagClear(DSP_IO_ERROR) )
      return( TRUE );
  else
      return( FALSE );

} /* Print_Character() */

/*------------------------------------------------------------------------*/

/*  Usage()
 *
 *  Prints usage information
 */

  void
Usage( void )
{
  fprintf( stderr, "%s\n",
        "Usage: demorse [-hv]" );

  fprintf( stderr, "%s\n",
        "       -h: Print this usage information and exit");

  fprintf( stderr, "%s\n",
        "       -v: Print version number and exit");

} /* End of Usage() */

/*------------------------------------------------------------------------*/

/*  sig_handler()
 *
 *  Signal Action Handler function
 */

static void sig_handler( int signal )
{
  /* Wrap up and quit */
  Cleanup();
  fprintf( stderr, "\n" );
  switch( signal )
  {
      case SIGINT :
        fprintf( stderr, "%s\n",  "xdemorse: Exiting via User Interrupt" );
        exit( signal );

      case SIGSEGV :
        fprintf( stderr, "%s\n",  "xdemorse: Segmentation Fault" );
        exit( signal );

      case SIGFPE :
        fprintf( stderr, "%s\n",  "xdemorse: Floating Point Exception" );
        exit( signal );

      case SIGABRT :
        fprintf( stderr, "%s\n",  "xdemorse: Abort Signal received" );
        exit( signal );

      case SIGTERM :
        fprintf( stderr, "%s\n",  "xdemorse: Termination Request received" );
        exit( signal );
  }

} /* End of sig_handler() */

/*------------------------------------------------------------------------*/

/* Functions for testing and setting/clearing flow control flags
 *
 *  See xdemorse.h for definition of flow control flags
 */

/* An int variable holding the single-bit flags */
static int Flags = 0;

  int
isFlagSet( int flag )
{
  return( (Flags & flag) == flag );
}

  int
isFlagClear( int flag )
{
  return( (~Flags & flag) == flag );
}

  void
Set_Flag( int flag )
{
  Flags |= flag;
}

  void
Clear_Flag( int flag )
{
  Flags &= ~flag;
}

  void
Toggle_Flag( int flag )
{
  Flags ^= flag;
}

/*------------------------------------------------------------------------*/

/*  Cleanup()
 *
 *  Cleans up before quitting
 */

  void
Cleanup( void )
{
  /* Release sound card */
  if( dsp_fd > 0 )
  {
      close( dsp_fd );
      dsp_fd = 0;
  }
  if( mix_fd > 0 )
  {
      close( mix_fd );
      mix_fd = 0;
  }

  Close_Tcvr_Serial();

} /* Cleanup( void ) */

/*------------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index