/************************************************************************** * * $Id: gpsxmple.c 1.10.1.11 2017/04/11 13:55:50 martin TEST $ * $Name: $ * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * * Description: * Sample program demonstrating how to access Meinberg devices * using the binary data protocol. * * Depending on the target operating system this program works * either via serial port (the default) or via a network socket * connection, or via USB. * * Supported target environments: * Windows / VC6 (serial and socket) * Linux / gcc or clang (serial, socket, USB) * QNX 6.x / gcc (socket only) * DOS / BC3.1 (serial only) * * For makefiles and build environment setups check the * corresponding subdirectories. * * Please send changes required for other operating systems * or build to * * ----------------------------------------------------------------------- * $Log: gpsxmple.c $ * Revision 1.10.1.11 2017/04/11 13:55:50 martin * New version code 2.7. * Made do_connect_serial() more fault tolerant. * Fixed build under Windows. * Removed obsolete code. * Cleanup. * Revision 1.10.1.10 2017/04/10 13:31:06Z martin * Fixed some compiler warnings. * Revision 1.10.1.9 2017/04/05 16:08:24Z martin * Adapted to new library functions. * Display PTP state, if supported. * Revision 1.10.1.8 2015/07/25 15:28:12 martin * Revision 1.10.1.7 2015/07/24 13:47:32Z martin * Revision 1.10.1.6 2015/07/14 13:01:02 martin * Revision 1.10.1.5 2014/11/04 11:32:25 martin * Started to support XBP addressing. * Revision 1.10.1.4 2014/10/30 13:49:38 martin * Revision 1.10.1.3 2013/02/01 16:11:59 martin * Revision 1.10.1.2 2012/03/13 16:27:44 martin * Revision 1.10.1.1 2012/03/13 11:48:35Z martin * Revision 1.10 2012/01/12 09:57:04 martin * Support selection of SCU port. * Preliminary code to send PCPS_TIME via binary protocol. * Debug code to test TX flushing problems under Windows. * GPSXMPLE code disabled by default to build mbgevlog. * Revision 1.9 2011/12/15 14:35:49Z martin * Started to migrate to opaque stuctures. * Made more functions static. * Added code to show / clear GPS event log. * Used this module for the mbgevlog program and disabled * some GPSXMPLE code using preprocessor symbol. * Revision 1.8 2011/04/15 13:04:14 martin * Optionally poll for user capture events. * Under Unix catch signals to terminate properly. * Revision 1.7 2009/10/02 14:18:08Z martin * Changes due to renamed library functions. * Optionally force connection. * Read global receiver_info after startup. * Read string type info and port info, if supported. * Fixed a typo for passwd parameter in usage(). * Show SVs and DAC. * Optionally loop and wait for automatic messages. * Revision 1.6 2006/10/25 12:31:54Z martin * Support serial I/O under Windows. * Check return codes of API functions and print associated * messages in case of error. * Revision 1.5 2006/08/24 13:36:38Z martin * Conditional support for network socket I/O. * Serial I/O is now also conditional only. * Use unified API calls from mbgextio module. * Account for DAC bias. * Revision 1.4 2006/05/17 10:20:29 martin * Account for renamed structure. * Revision 1.3 2005/09/08 14:28:34 martin * Cleaned up display of synth settings. * Revision 1.2 2005/04/26 11:29:16 martin * Initial revision under RCS. * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #if defined( MBG_TGT_UNIX ) #include #endif #if defined( MBG_TGT_DOS ) #include #define done kbhit() #else static int done; #endif const char pname[] = "gpsxmple"; const char pversion[] = "2.7"; const char pdescr[] = "Example Program Accessing a Meinberg Device via Binary Protocol"; // the variables below are required for communication MBG_MSG_CTL *msg_ctl; #define _DO_SET_TIME 0 // not yet ported // Define the minimum number of some resource types // required by the software #define N_COM_MIN 1 // minimum number of COM ports #define N_SCU_PORT 2 #define IDLE_SLEEP_MS 200 #define _log_msg_0( _lvl, _fmt ) \ printf( _fmt ); printf( "\n" ) #define _log_msg_1( _lvl, _fmt, _v1 ) \ printf( _fmt, _v1 ); printf( "\n" ) #define _log_msg_2( _lvl, _fmt, _v1, _v2 ) \ printf( _fmt, _v1, _v2 ); printf( "\n" ) #define _log_msg_3( _lvl, _fmt, _v1, _v2, _v3 ) \ printf( _fmt, _v1, _v2, _v3 ); printf( "\n" ) static const char *target; static int is_socket; static BAUD_RATE baudrate; static const char *framing = MBG_DEFAULT_FRAMING; static const char *str_new_date; static const char *str_new_time; static int must_send_auto; static int must_poll_ucap; static int must_clear_event_log; static int must_set_synth; static int must_set_pout_mode; static int must_force_connection; static int verbose; static const char *default_target = DEFAULT_SERIAL_DEVICE_NAME; #if _USE_SOCKET_IO static const char *password; static int scu_port; static int must_set_scu_port; #endif static char ws[256]; static const char *fmt = "%-20s "; static const char *dow_str[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; #if defined( DEBUG ) static /*HDR*/ void print_original_serial_settings( FILE *fp, const MBGSERIO_DEV *p ) { #if defined( MBG_TGT_WIN32 ) const DCB *dcb = &p->org_dcb; const COMMTIMEOUTS *tmo = &p->org_commtimeouts; const COMMPROP *prop = &p->comm_prop; fprintf( fp, "Comm Properties:\n" ); fprintf( fp, " wPacketLength: %u\n", prop->wPacketLength ); fprintf( fp, " wPacketVersion: %u\n", prop->wPacketVersion ); fprintf( fp, " dwServiceMask: %u (%u)\n", prop->dwServiceMask, SP_SERIALCOMM ); fprintf( fp, " dwReserved1: %u\n", prop->dwReserved1 ); fprintf( fp, " dwMaxTxQueue: %u\n", prop->dwMaxTxQueue ); fprintf( fp, " dwMaxRxQueue: %u\n", prop->dwMaxRxQueue ); fprintf( fp, " dwMaxBaud: %u\n", prop->dwMaxBaud ); fprintf( fp, " dwProvSubType: %u\n", prop->dwProvSubType ); fprintf( fp, " dwProvCapabilities: %u\n", prop->dwProvCapabilities ); fprintf( fp, " dwSettableParams: 0x%2X\n", prop->dwSettableParams ); fprintf( fp, " dwSettableBaud: 0x%2X\n", prop->dwSettableBaud ); fprintf( fp, " wSettableData: 0x%2X\n", prop->wSettableData ); fprintf( fp, " wSettableStopParity: 0x%2X\n", prop->wSettableStopParity ); fprintf( fp, " dwCurrentTxQueue: %u\n", prop->dwCurrentTxQueue ); fprintf( fp, " dwCurrentRxQueue: %u\n", prop->dwCurrentRxQueue ); fprintf( fp, " dwProvSpec1: %u\n", prop->dwProvSpec1 ); fprintf( fp, " dwProvSpec2: %u\n", prop->dwProvSpec2 ); // fprintf( fp, " wcProvChar: %c\n", prop->wcProvChar[0] ); // wchar!! fprintf( fp, "\n" ); fprintf( fp, "Original serial DCB settings:\n" ); fprintf( fp, " DCBlength: %u\n", dcb->DCBlength ); fprintf( fp, " BaudRate: %u\n", dcb->BaudRate ); fprintf( fp, " fBinary: %u\n", dcb->fBinary ); fprintf( fp, " fParity: %u\n", dcb->fParity ); fprintf( fp, " fOutxCtsFlow: %u\n", dcb->fOutxCtsFlow ); fprintf( fp, " fOutxDsrFlow: %u\n", dcb->fOutxDsrFlow ); fprintf( fp, " fDtrControl: %u\n", dcb->fDtrControl ); fprintf( fp, " fDsrSensitivity: %u\n", dcb->fDsrSensitivity ); fprintf( fp, " fTXContinueOnXoff: %u\n", dcb->fTXContinueOnXoff ); fprintf( fp, " fOutX: %u\n", dcb->fOutX ); fprintf( fp, " fInX: %u\n", dcb->fInX ); fprintf( fp, " fErrorChar: %u\n", dcb->fErrorChar ); fprintf( fp, " fNull: %u\n", dcb->fNull ); fprintf( fp, " fRtsControl: %u\n", dcb->fRtsControl ); fprintf( fp, " fAbortOnError: %u\n", dcb->fAbortOnError ); fprintf( fp, " fDummy2: %u\n", dcb->fDummy2 ); fprintf( fp, " wReserved: %u\n", dcb->wReserved ); fprintf( fp, " XonLim: %u\n", dcb->XonLim ); fprintf( fp, " XoffLim: %u\n", dcb->XoffLim ); fprintf( fp, " ByteSize: %u\n", dcb->ByteSize ); fprintf( fp, " Parity: %u\n", dcb->Parity ); fprintf( fp, " StopBits: %u\n", dcb->StopBits ); fprintf( fp, " XonChar: %u\n", dcb->XonChar ); fprintf( fp, " XoffChar: %u\n", dcb->XoffChar ); fprintf( fp, " ErrorChar: %u\n", dcb->ErrorChar ); fprintf( fp, " EofChar: %u\n", dcb->EofChar ); fprintf( fp, " EvtChar: %u\n", dcb->EvtChar ); fprintf( fp, " wReserved1: %u\n", dcb->wReserved1 ); fprintf( fp, "\n" ); fprintf( fp, "Original serial comm timeout settings:\n" ); fprintf( fp, " ReadIntervalTimeout: %u\n", tmo->ReadIntervalTimeout ); fprintf( fp, " ReadTotalTimeoutMultiplier: %u\n", tmo->ReadTotalTimeoutMultiplier ); fprintf( fp, " ReadTotalTimeoutConstant: %u\n", tmo->ReadTotalTimeoutConstant ); fprintf( fp, " WriteTotalTimeoutMultiplier: %u\n", tmo->WriteTotalTimeoutMultiplier ); fprintf( fp, " WriteTotalTimeoutConstant: %u\n", tmo->WriteTotalTimeoutConstant ); fprintf( fp, "\n" ); #endif } // print_original_serial_settings #endif // defined( DEBUG ) static /*HDR*/ void mbg_sleep_msec( long msec ) { #if defined( MBG_TGT_UNIX ) usleep( msec * 1000 ); #elif defined( MBG_TGT_WIN32 ) Sleep( msec ); #elif defined( MBG_TGT_DOS ) delay( (unsigned int) msec ); #endif } // mbg_sleep_msec static /*HDR*/ int set_scu_port( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr, int port ) { // Yet this is preliminary/untested. SCU_STAT_SETTINGS scu_stat_settings = { 0 }; int rc; scu_stat_settings.epld_control_mask = MSK_EPLD_CNTL_ENA_SNMP | MSK_EPLD_CNTL_SEL_SNMP; scu_stat_settings.epld_control_value = MSK_EPLD_CNTL_ENA_SNMP; if ( port ) // else leave 0 scu_stat_settings.epld_control_value |= MSK_EPLD_CNTL_SEL_SNMP; rc = mbgextio_xmt_msg( pmctl, p_addr, GPS_SCU_STAT, &scu_stat_settings, sizeof( scu_stat_settings ) ); return rc; } // set_scu_port static /*HDR*/ int check_rc( int rc ) { if ( done ) return 0; // we are going to exit anyway if ( mbg_rc_is_error( rc ) ) printf( "%s\n", mbg_strerror( rc ) ); return rc; } // check_rc static /*HDR*/ void snprint_tm( char *s, size_t max_len, TM_GPS *tm, int print_frac ) { size_t n = 0; int year = tm->year & ( DL_AUTO_FLAG - 1 ); n += snprintf_safe( &s[n], max_len - n, "%02i.%02i.%04i %02i:%02i:%02i", tm->mday, tm->month, year, tm->hour, tm->min, tm->sec ); if ( tm->year & DL_AUTO_FLAG ) strncpy_safe( &s[6], "****", 4 ); if ( print_frac ) n += snprintf_safe( &s[n], max_len - n, ".%07li", (long) tm->frac ); } /* snprint_tm */ static /*HDR*/ void snprint_lla( char *s, size_t max_len, LLA lla ) { static const double r2d = 180 / PI; snprintf_safe( s, max_len, "%c %.4f deg, %c %.4f deg, %.0fm", ( lla[LAT] < 0 ) ? 'S' : 'N', lla[LAT] * r2d, ( lla[LON] < 0 ) ? 'W' : 'E', lla[LON] * r2d, lla[ALT] ); } /* snprint_lla */ static /*HDR*/ void print_stat_info( STAT_INFO *p ) { const char *cp; switch ( p->mode ) { case TRACK: cp = "SINGLE SV MODE"; break; case AUTO_166: cp = "NORMAL OPERATION"; break; case WARM_166: cp = "WARM BOOT"; break; case COLD_166: cp = "COLD BOOT"; break; case AUTO_BC: cp = "REMOTE OPERATION"; break; case WARM_BC: cp = "REMOTE WARM"; break; case COLD_BC: cp = "REMOTE COLD"; break; case UPDA_166: cp = "UPDATE ALMANAC"; break; case UPDA_BC: cp = "REMOTE UPD. ALM."; break; default: cp = "INVALID MODE"; }; printf( "%s, %u/%u SVs, DAC: %+li/%+li\n", cp, p->good_svs, p->svs_in_view, (long) p->dac_val - OSC_DAC_BIAS, (long) p->dac_cal - OSC_DAC_BIAS ); } /* print_stat_info */ static /*HDR*/ void print_time( TTM *p ) { static const char *s[] = { "(current time)", "(capture 0)", "(capture 1)" }; snprint_tm( ws, sizeof( ws ), &p->tm, 1 ); printf( "\rCh: %2i %-14s %s Status: %04X\n", p->channel, s[p->channel + 1], ws, p->tm.status ); } /* print_time */ static /*HDR*/ void show_device_info( MBG_MSG_CTL *pmctl, int log ) { const char *fmt_min = "Number of %s from device (%u) is less than number required (%u)"; const char *fmt_max = "Number of %s from device (%u) exceeds number supported by the software (%u)"; size_t i; size_t l; RECEIVER_INFO *p = mbgextio_get_receiver_info_addr( pmctl ); // Check if numbers of resources provided by the device // don't exceed numbers of resources supported by the software #define _check_min( _n, _min, _info ) \ { \ if ( (_n) < (_min) ) \ { \ if ( log ) \ _log_msg_3( LOG_ERR, fmt_min, _info, _n, _min ); \ } \ } #define _check_max( _n, _max, _info ) \ { \ if ( (_n) > (_max) ) \ { \ if ( log ) \ _log_msg_3( LOG_WARNING, fmt_max, _info, _n, _max ); \ \ _n = _max; \ } \ } printf( fmt, "Device info:" ); printf( "%s v%X.%02X", p->model_name, p->sw_rev.code >> 8, p->sw_rev.code & 0xFF ); l = strlen( p->sw_rev.name ); if ( l ) { // skip trailing spaces for ( i = l - 1; i > 0; i-- ) if ( p->sw_rev.name[i] != ' ' ) break; if ( i != 0 ) printf( " \"%s\"", p->sw_rev.name ); #if 0 && defined( DEBUG ) // hex output for testing for ( i = 0; i < l; i++ ) printf( " %02X", p->sw_rev.name[i] ); #endif } printf( ", S/N: %s", p->sernum ); printf( "\n" ); if ( p->model_code == GPS_MODEL_UNKNOWN ) { _log_msg_0( LOG_ERR, "Refclock model_code not set: maybe receiver_info " "has not been received correctly" ); } else if ( log ) { if ( p->model_code >= N_GPS_MODEL ) { _log_msg_2( LOG_WARNING, "Unsupported refclock model_code %u (name: %s)", p->model_code, p->model_name ); } } _check_min( p->n_com_ports, N_COM_MIN, "COM ports" ); _check_max( p->n_com_ports, MAX_PARM_PORT, "COM ports" ); _check_max( p->n_str_type, MAX_PARM_STR_TYPE, "string types" ); _check_max( p->n_prg_out, MAX_PARM_POUT, "progr. outputs" ); } // show_device_info static /*HDR*/ void show_bvar_stat( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { BVAR_STAT bvar_stat; int rc; printf( fmt, "BVAR status:" ); rc = mbgextio_get_bvar_stat( pmctl, p_addr, &bvar_stat ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } printf( "%04X\n", bvar_stat ); } /* show_bvar_stat */ static /*HDR*/ void show_stat_info( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { STAT_INFO stat_info; int rc; printf( fmt, "Receiver status:" ); rc = mbgextio_get_gps_stat_info( pmctl, p_addr, &stat_info ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } print_stat_info( &stat_info ); } /* show_stat_info */ static /*HDR*/ void show_pos_lla( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { LLA lla; // Position as logitude, latitude, altitude int rc; printf( fmt, "Receiver Position:" ); rc = mbgextio_get_pos_lla( pmctl, p_addr, lla ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } snprint_lla( ws, sizeof( ws ), lla ); printf( "%s\n", ws ); } /* show_pos_lla */ static /*HDR*/ void show_tzdl( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { TZDL tzdl; int rc; printf( fmt, "Time Zone:" ); rc = mbgextio_get_tzdl( pmctl, p_addr, &tzdl ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } printf( "%-5s = UTC%+lisec\n", tzdl.name[0], (long) tzdl.offs ); printf( fmt, " " ); printf( "%-5s = UTC%+lisec\n", tzdl.name[1], (long) tzdl.offs + tzdl.offs_dl ); printf( fmt, "Daylight Saving" ); printf( "starts: " ); if ( tzdl.tm_on.year & DL_AUTO_FLAG ) { printf( "%s after ", dow_str[(int) tzdl.tm_on.wday] ); } snprint_tm( ws, sizeof( ws ), &tzdl.tm_on, 0 ); printf( "%sh\n", ws ); printf( fmt, " " ); printf( "ends: " ); if ( tzdl.tm_off.year & DL_AUTO_FLAG ) { printf( "%s after ", dow_str[(int) tzdl.tm_off.wday] ); } snprint_tm( ws, sizeof( ws ), &tzdl.tm_off, 0 ); printf( "%sh\n", ws ); } /* show_tzdl */ static /*HDR*/ void show_ptp_state( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { static const char *ptp_role_strs[N_PTP_ROLES] = PTP_ROLE_STRS; static const char *ptp_state_strs[N_PTP_PORT_STATE] = PTP_PORT_STATE_STRS; static const char *ptp_nw_prot_strs[N_PTP_NW_PROT] = PTP_NW_PROT_STRS; static const char *ptp_delay_mech_names[N_PTP_DELAY_MECH] = PTP_DELAY_MECH_NAMES; static const PTP_TABLE ptp_time_source_tbl[] = PTP_TIME_SOURCE_TABLE; static const char *ptp_clock_accuracy_strs[] = PTP_CLOCK_ACCURACY_STRS; char ws[100]; const char *cp; int must_show_slave_mode_info; int must_show_master_mode_info; int slave_mode_active; int master_mode_active; int any_mode_active; int utc_offset_valid; PTP_STATE ptp_state = { 0 }; PTP_CFG_INFO ptp_info = { { 0 } }; int tmp; int rc; printf( "\n" ); printf( "PTP State:\n" ); rc = mbgextio_get_ptp_state( pmctl, p_addr, &ptp_state ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } rc = mbgextio_get_ptp_cfg_info( pmctl, p_addr, &ptp_info ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } // set up some flags controlling which information to be shown must_show_slave_mode_info = ( verbose > 1 ) || ( ( ( 1UL << ptp_info.settings.ptp_role ) & PTP_ROLE_MSK_SLAVES ) != 0 ); must_show_master_mode_info = ( verbose > 1 ) || ( ( ( 1UL << ptp_info.settings.ptp_role ) & PTP_ROLE_MSK_MASTERS ) != 0 ); slave_mode_active = ( ptp_state.port_state == PTP_PORT_STATE_UNCALIBRATED ) || ( ptp_state.port_state == PTP_PORT_STATE_SLAVE ); master_mode_active = ( ptp_state.port_state == PTP_PORT_STATE_PRE_MASTER ) || ( ptp_state.port_state == PTP_PORT_STATE_MASTER ) || ( ptp_state.port_state == PTP_PORT_STATE_PASSIVE ); any_mode_active = slave_mode_active || master_mode_active; // PTP role and port state printf( " Port mode: " ); if ( any_mode_active ) printf( "%s", ( ptp_state.flags & PTP_FLAG_MSK_IS_UNICAST ) ? "Unicast " : "Multicast " ); if ( ptp_state.port_state < N_PTP_PORT_STATE ) printf( "%s", ptp_state_strs[ptp_state.port_state] ); else printf( "%s, code %i", str_undefined, ptp_state.port_state ); if ( !any_mode_active || ( verbose > 1 ) ) printf( " in %s role", ( ptp_info.settings.ptp_role < N_PTP_ROLES ) ? ptp_role_strs[ptp_info.settings.ptp_role] : str_undefined ); printf( "\n" ); if ( !any_mode_active || verbose ) { printf( " PTP protocol: " ); // If not fully synchronized then not all fields of the PTP_STATE // structure may have been filled, so we show configuration settings // in this case. if ( ptp_state.ptp_prot_version ) printf( "v%i, ", ptp_state.ptp_prot_version ); tmp = any_mode_active ? ptp_state.nw_prot : ptp_info.settings.nw_prot; printf( "%s", ( tmp < N_PTP_NW_PROT ) ? ptp_nw_prot_strs[tmp] : str_undefined ); printf( ", %s step", ( ptp_state.flags & PTP_FLAG_MSK_ONE_STEP ) ? "one" : "two" ); tmp = any_mode_active ? ptp_state.domain_number : ptp_info.settings.domain_number; printf( ", domain %i", tmp ); tmp = any_mode_active ? ptp_state.delay_mech : ptp_info.settings.delay_mech; printf( ", delay mech. %s", ( tmp < N_PTP_DELAY_MECH ) ? ptp_delay_mech_names[tmp] : str_undefined ); tmp = any_mode_active ? ptp_state.log_delay_req_intv : ptp_info.settings.delay_req_intv; printf( ", dly req. intv. 2^%i s", tmp ); printf( "\n" ); } if ( must_show_slave_mode_info ) { cp = ( slave_mode_active || ( verbose > 1 ) ) ? ws : str_not_avail; snprint_octets( ws, sizeof( ws ), ptp_state.gm_id.b, sizeof( ptp_state.gm_id.b ), MAC_SEP_CHAR_ALT, NULL ); printf( " Grandmaster ID: %s\n", cp ); snprint_nano_time( ws, sizeof( ws ), &ptp_state.offset ); printf( " PTP time offset: %s\n", cp ); snprint_nano_time( ws, sizeof( ws ), &ptp_state.path_delay ); printf( " PTP path delay: %s\n", cp ); if ( verbose > 1 ) { snprint_nano_time( ws, sizeof( ws ), &ptp_state.mean_path_delay ); printf( " Mean path delay: %s\n", cp ); snprint_nano_time( ws, sizeof( ws ), &ptp_state.delay_asymmetry ); printf( " Delay asymmetry: %s\n", cp ); } } // PTP time scale cp = NULL; if ( ptp_state.flags & PTP_FLAG_MSK_TIMESCALE_IS_PTP ) // this is the default { if ( must_show_master_mode_info || verbose ) cp = "TAI (standard)"; } else // unusual case, print info if available if ( any_mode_active ) cp = "arbitrary (non-standard)"; if ( cp ) printf( " PTP time scale: %s\n", cp ); // UTC offset and leap second status utc_offset_valid = ( ptp_state.flags & PTP_FLAG_MSK_UTC_VALID ) != 0; printf( " PTP UTC offset: " ); if ( !utc_offset_valid ) printf( " %s", str_unknown ); // UTC offset not valid if ( utc_offset_valid || ( verbose > 1 ) ) { printf( utc_offset_valid ? " " : " (" ); printf( "%+i s", ptp_state.utc_offset ); if ( ptp_state.flags & PTP_FLAG_MSK_LS_ANN ) // leap second is announced { // distinguish between leap second insertion and deletion printf( ", leap second %s scheduled", ( ptp_state.flags & PTP_FLAG_MSK_LS_ANN_NEG ) ? "deletion" : "insertion" ); } if ( !utc_offset_valid ) printf( ")" ); } printf( "\n" ); if ( verbose > 1 ) { const PTP_TABLE *p_tbl; printf( "PTP clock state:\n" ); for ( p_tbl = ptp_time_source_tbl; p_tbl->name; p_tbl++ ) if ( p_tbl->value == ptp_state.time_source ) break; printf( " Time source: %s\n", p_tbl->name ? p_tbl->name : str_unknown ); printf( " Clock class: %i\n", ptp_state.clock_class ); tmp = N_PTP_CLOCK_ACCURACY - PTP_CLOCK_ACCURACY_NUM_BIAS; if ( ( ptp_state.clock_accuracy >= PTP_CLOCK_ACCURACY_NUM_BIAS ) && ( ptp_state.clock_accuracy < N_PTP_CLOCK_ACCURACY ) ) cp = ptp_clock_accuracy_strs[ptp_state.clock_accuracy - PTP_CLOCK_ACCURACY_NUM_BIAS]; else cp = str_undefined; printf( " Clock accuracy: %s\n", cp ); #if 0 time_source clock_class clock_accuracy #endif printf( " Offs sc. log var: %u", ptp_state.clock_offset_scaled_log_variance ); printf( "\n" ); } } // show_ptp_state static /*HDR*/ void show_synth( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { SYNTH synth; double f; int rc; printf( fmt, "Synthesizer:" ); rc = mbgextio_get_synth( pmctl, p_addr, &synth ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } if ( synth.freq == 0 ) { printf( "disabled\n" ); return; } if ( synth.range == 0 ) { // freq field is in 0.1 Hz units, so if // the range is 0 we must divide by 10 // to yield the correct result f = (double) synth.freq / 10.0; } else { ulong scale = 1; int i; for ( i = 1; i < synth.range; i++ ) scale *= 10L; f = synth.freq * (double) scale; } printf( "%.1f Hz, Phase: %+.1f deg", (double) f, (double) synth.phase / 10.0 ); printf( ( f < SYNTH_PHASE_SYNC_LIMIT ) ? "\n" : " (phase ignored)\n" ); } /* show_synth */ static /*HDR*/ void set_synth( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { SYNTH synth = { 0 }; int rc; if ( mbg_rc_is_error( mbgextio_dev_has_synth( pmctl ) ) ) { printf( "The device doesn't provide a synthesizer.\n" ); return; } // In this example we set the synthesizer frequency to // 1 kHz, and the phase to 180 degrees. // The effective frequency is: (freq/10)*(10^^range) synth.freq = 1000; // base frequency value in 1/10 Hz Units synth.range = 1; // multiplier 10^^range synth.phase = 1800; // phase in 1/10 degrees rc = mbgextio_set_synth( pmctl, p_addr, &synth ); if ( mbg_rc_is_error( rc ) ) printf( "Failed to set synthesizer output: %s (rc: %i)\n", mbg_strerror( rc ), rc ); else printf( "Synthesizer has been modified.\n" ); } // set_synth // set programmable output mode static /*HDR*/ void set_pout_mode( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { RECEIVER_INFO *p_ri = mbgextio_get_receiver_info_addr( pmctl ); POUT_INFO_IDX pout_info_idx; POUT_SETTINGS pout_settings = { 0 }; // new settings, init'd to 0 uint16_t pout_idx = 0; // index of the programmable output int rc; if ( p_ri->n_prg_out == 0 ) { printf( "The device doesn't support programmable outputs.\n" ); return; } // valid pout_idx numbers include 0..receiver_info.n_prg_out-1 if ( pout_idx >= p_ri->n_prg_out ) { printf( "Programmable output index %u out of range (%u..%u)\n", pout_idx, 0, p_ri->n_prg_out - 1 ); return; } // OK, now get the current settings, and see which modes are // supported by the selected programmable pulse output. rc = mbgextio_get_pout_info_idx( pmctl, p_addr, &pout_info_idx, pout_idx ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } // As an example we want to set POUT_CYCLIC_PULSE mode. // First see if that mode is supported for that output. if ( !( pout_info_idx.pout_info.supp_modes & MSK_POUT_CYCLIC_PULSE ) ) { printf( "The selected mode is not supported by the selected output.\n" ); return; } // Generate a pulse every pout_settings.tm[0].on.t interval. // In this example we want an interval of 20 minutes, and a pulse length // of 300 milliseconds pout_settings.mode = POUT_CYCLIC_PULSE; pout_settings.pout_data.tm[0].on.t.hour = 0; pout_settings.pout_data.tm[0].on.t.min = 20; pout_settings.pout_data.tm[0].on.t.sec = 0; // pout_settings.pulse_len = 30; // 10 millisecond units rc = mbgextio_set_pout_settings_idx( pmctl, p_addr, &pout_settings, pout_idx ); if ( rc < 0 ) printf( "Failed to set programmable pulse output mode, code: %i\n", rc ); else printf( "Programmable output mode has been changed.\n" ); } // set_pout_mode #if 0 //##++++++++++++++++ static /*HDR*/ void show_port_parm( MBG_MSG_CTL *pmctl ) { PORT_PARM port_parm; int rc; int i; rc = mbgextio_get_port_parm( pmctl, &port_parm ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } for ( i = 0; ; ) { printf( "COM%i: %5lu Baud, %s, ", i, (ulong) port_parm.com[i].baud_rate, port_parm.com[i].framing ); switch ( port_parm.mode[i] ) { case 0: printf( "Time string on request only\n" ); break; case 1: printf( "Time string once per second\n" ); break; case 2: printf( "Time string once per minute\n" ); break; case 3: printf( "Capture Events\n" ); break; default: printf( "Unknown mode\n" ); } if ( ++i >= 2 ) break; printf( fmt, "" ); } } /* show_port_parm */ #endif static /*HDR*/ void show_serial_settings( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { const char *mode_names[N_STR_MODE] = DEFAULT_ENG_MODE_NAMES; RECEIVER_INFO *p_ri = mbgextio_get_receiver_info_addr( pmctl ); RECEIVER_PORT_CFG rpcfg; int rc; int i; printf( fmt, "Serial ports:" ); rc = mbgextio_get_serial_settings( pmctl, p_addr, &rpcfg ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); return; } // The number of serial ports and time string formats supported // by this device is specified in the receiver info structure. #if 0 //##++ TODO: possibly print supported string types for ( i = 0; i < p_ri->n_str_type; i++ ) { sti[i] = stii.str_type_info; } #endif // print port settings for ( i = 0; ; ) { PORT_SETTINGS *p = &rpcfg.pii[i].port_info.port_settings; printf( "COM%i: %5lu Baud, %s", i, (ulong) p->parm.baud_rate, p->parm.framing ); // Make sure indices received from the device do not exceed // maximum numbers supported by this program. if ( p->str_type >= MAX_PARM_STR_TYPE ) printf( " (string type exceeds max)" ); else if ( p->mode >= N_STR_MODE ) printf( " (string mode exceeds max)" ); else printf( ", \"%s\" string %s", rpcfg.stii[p->str_type].str_type_info.long_name, mode_names[p->mode] ); printf( "\n" ); if ( ++i >= p_ri->n_com_ports ) break; printf( fmt, "" ); } } /* show_serial_settings */ static /*HDR*/ void show_event_log( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { static const char *evt_id_names[N_MBG_EVT_ID] = MBG_EVT_ID_NAMES_ENG; MBG_NUM_EVT_LOG_ENTRIES log_entries; MBG_EVT_LOG_ENTRY evt_log_entry; time_t t; struct tm tm = { 0 }; int n_evt = 0; int event_id = -1; unsigned int i; int rc = mbgextio_get_num_evt_log_entries( pmctl, p_addr, &log_entries ); if ( mbg_rc_is_error( rc ) ) { fprintf( stderr, "** ERROR: failed to read event log: %s\n", mbg_strerror( rc ) ); return; } printf( "\nEvent Log (%i/%i entries used):\n", log_entries.used, log_entries.max ); for ( i = 0; i < log_entries.max; i++ ) { rc = ( i == 0 ) ? mbgextio_get_first_evt_log_entry( pmctl, p_addr, &evt_log_entry ) : mbgextio_get_next_evt_log_entry( pmctl, p_addr, &evt_log_entry ); if ( mbg_rc_is_error( rc ) ) { fprintf( stderr, "Failed to read event log entry %i: %s\n", i, mbg_strerror( rc ) ); break; } event_id = _mbg_get_evt_id( evt_log_entry.code ); if ( event_id == 0 ) break; n_evt++; t = evt_log_entry.time; rc = mbg_gmtime( &tm, &t ); if ( mbg_rc_is_success( rc ) ) printf( "%04i-%02i-%02i %02i:%02i:%02i ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ); else printf( "(inv. time) " ); if ( event_id < N_MBG_EVT_ID ) printf( "%s\n", evt_id_names[event_id] ); else printf( "Unknown event ID %u", event_id ); } if ( n_evt == 0 ) fprintf( stderr, "event log is empty\n" ); printf( "\n" ); } // show_event_log /*HDR*/ static int check_ucap_poll( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { int rc; printf( "Polling for user capture events:\n" ); do { TTM ttm; memset( &ttm, 0, sizeof( ttm ) ); rc = mbgextio_get_ucap( pmctl, p_addr, &ttm ); if ( mbg_rc_is_success( rc ) && _ttm_time_is_avail( &ttm ) ) { printf( "CAP%i: %04u-%02u-%02u %02u:%02u:%02u.%07u", ttm.channel, ttm.tm.year, ttm.tm.month, ttm.tm.mday, ttm.tm.hour,ttm.tm.min, ttm.tm.sec, ttm.tm.frac ); printf( ", Status: %04X (hex)", ttm.tm.status ); printf( "\n" ); } else { // No capture event available. We wait some time // and then retry. mbg_sleep_msec( IDLE_SLEEP_MS ); } } while ( !done ); return 0; } // check_ucap_poll static /*HDR*/ int set_date_time( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, const char *str_new_date, const char *str_new_time ) { #if _DO_SET_TIME PCPS_TIME_UNION u = { { 0 } }; #endif unsigned int year = 0; unsigned int month = 0; unsigned int mday = 0; unsigned int hour = 0; unsigned int min = 0; unsigned int sec = 0; unsigned int sec100 = 0; int rc; // Either a date string, a time string, or both // may have been passed to this function. // If either of them is NULL read the current date/time // as default values. if ( str_new_date == NULL || str_new_time == NULL ) { #if 0 && _DO_SET_TIME rc = mbg_get_time( dh, &u.t ); if ( mbg_ioctl_err( rc, "mbg_get_time" ) ) return rc; #else return -1; #endif } if ( str_new_date ) { rc = sscanf( str_new_date, "%u-%u-%u", &year, &month, &mday ); if ( ( rc < 3 ) || ( month < 1 ) || ( month > 12 ) || ( mday < 1 ) || ( mday > 31 ) ) { printf( "** Invalid date: %04u-%02u-%02u\n", year, month, mday ); return MBG_ERR_CFG; } } if ( str_new_time ) { rc = sscanf( str_new_time, "%u:%u:%u.%u", &hour, &min, &sec, &sec100 ); if ( ( rc < 2 ) // at least hours and minutes are required || ( hour > 23 ) || ( min > 59 ) || ( sec > 60 ) || ( sec100 > 99 ) ) { printf( "** Invalid time: %02u:%02u:%02u.%02u\n", hour, min, sec, sec100 ); return MBG_ERR_CFG; } } #if 0 //##++++++++++++++++ // GPS and non-GPS cards require different API calls which use // different structures to set the on-board date and time, // so we have to distinguish. if ( _pcps_is_gps( p_dev ) ) // is a GPS card { TTM ttm = { 0 }; ttm.channel = -1; if ( str_new_date ) // new date { ttm.tm.year = year; ttm.tm.month = month; ttm.tm.mday = mday; } else // copy current date as default { ttm.tm.year = u.t.year + 2000; ttm.tm.month = u.t.month; ttm.tm.mday = u.t.mday; } if ( str_new_time ) // new time { ttm.tm.hour = hour; ttm.tm.min = min; ttm.tm.sec = sec; ttm.tm.frac = sec100; } else // copy current time as default { ttm.tm.hour = u.t.hour; ttm.tm.min = u.t.min; ttm.tm.sec = u.t.sec; ttm.tm.frac = u.t.sec100; } ttm.tm.frac *= 100000; // fracs are in 100 ns units #if 0 // Existing versions of the GPS cards just take // TTM.tm as local time without accounting for // the status flags, or UTC offset. // Instead, the TTM::tm time is converted on-board // to UTC depending on the local TZDL configuration. // This works if the system time and the ttm.tm.offs_from_utc = 7200; ttm.tm.status |= TM_UTC | TM_LOCAL; ttm.tm.status |= TM_DL_ENB; #endif rc = mbg_set_gps_time( dh, &ttm ); if ( mbg_ioctl_err( rc, "mbg_set_gps_time" ) ) return rc; } else // is not a GPS card { if ( str_new_date ) // new date { // determine the day-of-week for the given date struct tm tm = { 0 }; tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = mday; tm.tm_hour = 12; tm.tm_isdst = -1; mktime( &tm ); u.stime.year = year % 100; u.stime.month = month; u.stime.mday = mday; u.stime.wday = _wday_sun06_to_mon17( tm.tm_wday ); } if ( str_new_time ) // new time { u.stime.hour = hour; u.stime.min = min; u.stime.sec = sec; u.stime.sec100 = sec100; } if ( u.stime.wday == 0 ) u.stime.wday = 1; // dummy rc = mbg_set_time( dh, &u.stime ); if ( mbg_ioctl_err( rc, "mbg_set_time" ) ) return rc; } #else { // PCPS_TIME t = { 0 }; // determine the day-of-week for the given date struct tm tm = { 0 }; tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = mday; tm.tm_hour = 12; tm.tm_isdst = -1; mktime( &tm ); t.year = year % 100; t.month = month; t.mday = mday; t.wday = ( tm.tm_wday == 0 ) ? 7 : tm.tm_wday; t.hour = hour; t.min = min; t.sec = sec; t.sec100 = sec100; #if 0 //##+++++++++++++++++ { MBG_PORT_HANDLE port_handle = pmctl->st.serio.port_handle; const char tmp[] = "\xFF\xFF\xFF\xFF"; // Note: encrypted msgs over serial are not yet supported. // _mbgserio_write( port_handle, &tmp, strlen( tmp ) ); // _swab_pcps_time( &t ); rc = mbgextio_xmt_msg( pmctl, PZF_PCPS_TIME, &t, sizeof( t ) ); // _mbgserio_write( port_handle, &tmp, strlen( tmp ) ); } #else // _swab_pcps_time( &t ); rc = mbgextio_xmt_msg( pmctl, p_addr, PZF_PCPS_TIME, &t, sizeof( t ) ); #endif if ( rc < 0 ) printf( "Failed to set date/time, rc: %i\n", rc ); } #endif printf( "\n\nDevice date/time have been set to %04u-%02u-%02u %02u:%02u:%02u.%02u\n\n", year, month, mday, hour, min, sec, sec100 ); return MBG_SUCCESS; } // set_date_time #if _USE_SERIAL_IO static /*HDR*/ BAUD_RATE do_connect_serial( const char *port_name, BAUD_RATE baudrate, bool force_connection ) { int rc; if ( force_connection ) { // Try to force the device to some useful settings, and return the baud rate // that can be used for communication. If the returned number is negative then // it's one of the Meinberg error codes. printf( "Trying to force connection via %s ... ", port_name ); baudrate = mbgextio_force_conn_serial( port_name ); if ( baudrate < 0 ) { printf( "FAILED: %s (rc:%li)\n", mbg_strerror( (int) baudrate ), (long) baudrate ); goto out; } printf( "using %li/%s\n", (long) baudrate, framing ); } else { if ( baudrate == 0 ) // no specific value specified { // Some newer devices support MBG_DEFAULT_BAUDRATE_HS for the binary protocol, // which is faster, but older devices support only MBG_DEFAULT_BAUDRATE, so we // may want to try both. baudrate = MBG_DEFAULT_BAUDRATE_HS; rc = mbgextio_open_serial( port_name, &msg_ctl, baudrate, framing ); if ( mbg_rc_is_success( rc ) ) goto opened_successfully; baudrate = MBG_DEFAULT_BAUDRATE; // next try this one } } // Now try to connect to the device with whichever baudrate has been determined rc = mbgextio_open_serial( port_name, &msg_ctl, baudrate, framing ); if ( mbg_rc_is_error( rc ) ) { baudrate = rc; goto out; } opened_successfully: #if defined( DEBUG ) print_original_serial_settings( stdout, msg_ctl->st.p_serio ); #endif printf( "Using %s with %li baud, %s\n\n", port_name, (long) baudrate, framing ); out: return baudrate; } // do_connect_serial #endif /*HDR*/ static void exit_close_connection( void ) { mbgextio_close_connection( &msg_ctl ); printf( "\nConnection closed.\n\n" ); } /* exit_close_connection */ /*HDR*/ static void exit_auto_off( void ) { mbgextio_xmt_cmd( msg_ctl, NULL, GPS_AUTO_OFF ); //##++++++ TODO: NULL? #if defined( DEBUG ) printf( "AUTO mode turned off.\n" ); #endif } /* exit_auto_off */ /*HDR*/ static void set_auto_mode( MBG_MSG_CTL *pmctl ) { mbgextio_xmt_cmd( pmctl, NULL, GPS_AUTO_ON ); //##++++++ TODO: NULL? #if defined( DEBUG ) printf( "AUTO mode turned on.\n" ); #endif atexit( exit_auto_off ); } // set_auto_mode #if defined( MBG_TGT_UNIX ) static /*HDR*/ void sighandler( int sig ) { #if defined( DEBUG ) printf( "Caught signal %i, exiting.\n", sig ); #endif done = 1; } // sighandler #endif // Do a very simple processing of command line parameters: static /*HDR*/ int check_command_line( int argc, char *argv[] ) { int must_print_usage = 0; int i; for ( i = 1; i < argc; i++ ) { if ( strcmp( argv[i], "-a" ) == 0 ) { must_send_auto = 1; continue; } if ( strcmp( argv[i], "-u" ) == 0 ) { must_poll_ucap = 1; continue; } if ( strcmp( argv[i], "-v" ) == 0 ) { verbose++; continue; } if ( strcmp( argv[i], "-?" ) == 0 ) { must_print_usage = 1; continue; } if ( strcmp( argv[i], "-h" ) == 0 ) { must_print_usage = 1; continue; } #if _USE_SOCKET_IO if ( strcmp( argv[i], "-n" ) == 0 ) { is_socket = 1; continue; } #endif #if _USE_SOCKET_IO if ( strcmp( argv[i], "-p" ) == 0 ) { if ( ++i < argc ) password = argv[i]; else must_print_usage = 1; continue; } #endif #if _USE_SERIAL_IO if ( strcmp( argv[i], "-b" ) == 0 ) { if ( ++i < argc ) baudrate = strtoul( argv[i], NULL, 10 ); else must_print_usage = 1; continue; } #endif #if _USE_SERIAL_IO if ( strcmp( argv[i], "-f" ) == 0 ) { if ( ++i < argc ) framing = argv[i]; else must_print_usage = 1; continue; } #endif #if _USE_SERIAL_IO if ( strcmp( argv[i], "-F" ) == 0 ) { must_force_connection = 1; continue; } #endif if ( strcmp( argv[i], "-c" ) == 0 ) { must_clear_event_log = 1; continue; } #if _USE_SOCKET_IO if ( strcmp( argv[i], "-P" ) == 0 ) { if ( ++i < argc ) { scu_port = atoi( argv[i] ); if ( ( scu_port >= 0 ) && ( scu_port < N_SCU_PORT ) ) { must_set_scu_port = 1; continue; } } fprintf( stderr, "** invalid parameter for \"-P\"\n" ); must_print_usage = 1; continue; } #endif if ( strcmp( argv[i], "-d" ) == 0 ) { if ( ++i < argc ) str_new_date = argv[i]; continue; } if ( strcmp( argv[i], "-t" ) == 0 ) { if ( ++i < argc ) str_new_time = argv[i]; continue; } if ( argv[i][0] != '-' ) target = argv[i]; else must_print_usage = 1; } if ( target == NULL ) { if ( is_socket ) must_print_usage = 1; else target = default_target; } if ( must_print_usage ) { printf( "Usage: %s [options] target\n", argv[0] ); printf( "\n" ); #if _USE_SERIAL_IO printf( " target can specify a serial port\n" ); #endif #if _USE_SOCKET_IO printf( " target can specify a network host name or IP address\n" ); #endif printf( "\n" ); printf( "Options\n" ); printf( " -? or -h Print this usage\n" ); printf( " -v Increase verbosity\n" ); printf( " -a Set auto mode\n" ); printf( " -u Poll user capture events\n" ); #if _USE_SOCKET_IO printf( " -n Connect to network target, not serial port\n" ); printf( " -p passwd Use specified password to connect\n" ); #endif #if _USE_SERIAL_IO printf( " -b speed Serial port baud rate, default: %lu or %lu\n", (long) MBG_DEFAULT_BAUDRATE_HS, (long) MBG_DEFAULT_BAUDRATE ); printf( " -f xxx Serial port framing, default: %s\n", framing ); printf( " -F Force serial connection\n" ); #endif printf( " -c clear event log (if supported by device)\n" ); #if _USE_SOCKET_IO printf( " -P scu_port Select clock at SCU port 0 or 1 (only with SCU over LAN)\n" ); #endif printf( "\n" ); printf( "Example:\n" ); #if _USE_SERIAL_IO printf( " %s Connect to serial port %s using %li/%s or %li/%s\n", argv[0], default_target, (long) MBG_DEFAULT_BAUDRATE_HS, framing, (long) MBG_DEFAULT_BAUDRATE, framing ); #endif #if _USE_SOCKET_IO printf( " %s -n -p secret 172.16.1.1 Connect to the specified network host\n", argv[0] ); #endif printf( "\n" ); return -1; } return 0; } // check_command_line static /*HDR*/ void exit_with_error_serial( int rc, const char *target ) { printf( "Failed to connect to device at %s: %s (rc: %i)\n", target, mbg_strerror( rc ), rc ); exit( 1 ); } // exit_with_error_serial int main( int argc, char *argv[] ) { XBP_ADDR *p_addr = NULL; int rc; fprintf( stderr, "\n\n\n%s v%s %s\n\n", pname, pversion, pdescr ); if ( check_command_line( argc, argv ) < 0 ) return 1; #if defined( MBG_TGT_UNIX ) signal( SIGTERM, sighandler ); signal( SIGINT, sighandler ); #endif #if _USE_SOCKET_IO if ( is_socket ) { rc = mbgextio_open_socket( target, &msg_ctl, password ); if ( mbg_rc_is_error( rc ) ) exit_with_error_serial( rc, target ); goto doit; } #endif #if _USE_SERIAL_IO baudrate = do_connect_serial( target, baudrate, must_force_connection ); if ( baudrate <= 0 ) exit_with_error_serial( (int) baudrate, target ); #endif #if !_USE_SOCKET_IO && !_USE_SERIAL_IO return 1; #endif doit: atexit( exit_close_connection ); // now start communication with whichever device has been opened above: #if 0 // TODO: make sure xbp is supported. mbgextio_setup_xbp_node_list( msg_ctl, p_addr ); #endif #if _USE_SOCKET_IO if ( must_set_scu_port ) { int rc = set_scu_port( msg_ctl, p_addr, scu_port ); if ( mbg_rc_is_success( rc ) ) printf( "\nSCU switched to port %i (clock #%i)\n\n", scu_port, scu_port + 1 ); else printf( "\nFailed to switch SCU to port %i (clock #%i)\n\n", scu_port, scu_port + 1 ); } #endif if ( must_send_auto ) set_auto_mode( msg_ctl ); if ( str_new_date && str_new_time ) { set_date_time( msg_ctl, p_addr, str_new_date, str_new_time ); return 0; } show_device_info( msg_ctl, 1 ); /* display system specific values */ if ( mbg_rc_is_success( mbgextio_dev_has_bvar_stat( msg_ctl ) ) ) show_bvar_stat( msg_ctl, p_addr ); if ( mbg_rc_is_success( mbgextio_dev_has_gps_stat_info( msg_ctl ) ) ) show_stat_info( msg_ctl, p_addr ); if ( mbg_rc_is_success( mbgextio_dev_has_pos_lla( msg_ctl ) ) ) show_pos_lla( msg_ctl, p_addr ); if ( mbg_rc_is_success( mbgextio_dev_has_tzdl( msg_ctl ) ) ) show_tzdl( msg_ctl, p_addr ); if ( mbg_rc_is_success( mbgextio_dev_has_serouts( msg_ctl ) ) ) show_serial_settings( msg_ctl, p_addr ); if ( mbg_rc_is_success( mbgextio_dev_has_synth( msg_ctl ) ) ) show_synth( msg_ctl, p_addr ); if ( mbg_rc_is_success( mbgextio_dev_has_ptp( msg_ctl ) ) ) show_ptp_state( msg_ctl, p_addr ); if ( mbg_rc_is_success( mbgextio_dev_has_evt_log( msg_ctl ) ) ) show_event_log( msg_ctl, p_addr ); if ( must_clear_event_log ) { if ( mbg_rc_is_success( mbgextio_dev_has_evt_log( msg_ctl ) ) ) { rc = mbgextio_clr_evt_log( msg_ctl, p_addr ); if ( mbg_rc_is_error( rc ) ) fprintf( stderr, "Failed to clear event log: %s (rc: %i)\n", mbg_strerror( rc ), rc ); else fprintf( stderr, "Event log has been cleared\n" ); } else fprintf( stderr, "This device does not support an event log\n" ); } if ( must_set_synth ) { if ( mbg_rc_is_success( mbgextio_dev_has_synth( msg_ctl ) ) ) set_synth( msg_ctl, p_addr ); else fprintf( stderr, "Can't set synthesizer output: not supported by device\n" ); } if ( must_set_pout_mode ) { if ( mbg_rc_is_success( mbgextio_dev_has_prog_pulses( msg_ctl ) ) ) set_pout_mode(msg_ctl, p_addr ); else fprintf( stderr, "Can't set programmable output mode: not supported by device\n" ); } if ( must_poll_ucap ) return check_ucap_poll( msg_ctl, p_addr ); if ( !must_send_auto ) return 0; printf( "\n" ); // We will start to wait for automatically transmitted messages // which require a longer timeout than we have used by default // to wait for replies to dedicated request messages. mbgextio_set_dev_poll_timeout( msg_ctl, 2000 ); // [msec] mbgextio_set_msg_rcv_timeout( msg_ctl, 2000 ); // [msec] do { MBG_MSG_BUFF *pmb; MSG_DATA *p; rc = mbgextio_rcv_msg( msg_ctl, p_addr, GPS_WILDCARD, NULL, 0 ); if ( mbg_rc_is_error( rc ) ) { check_rc( rc ); break; } mbgextio_xmt_cmd( msg_ctl, p_addr, GPS_AUTO_ON ); pmb = msg_ctl->rcv.pmb; p = &pmb->u.msg_data; switch ( pmb->hdr.cmd ) { case GPS_TIME: print_time( &p->ttm ); if ( p->ttm.channel == -1 ) { mbgextio_xmt_cmd( msg_ctl, p_addr, GPS_AUTO_ON ); mbgextio_xmt_cmd( msg_ctl, p_addr, GPS_STAT_INFO ); mbgextio_xmt_cmd( msg_ctl, p_addr, GPS_UCAP ); } break; case GPS_STAT_INFO: print_stat_info( &p->stat_info ); break; } /* switch */ } while ( !done ); return 0; } /* main */