[evla-sw-discuss] Code walkthrough

Barry Clark bclark at aoc.nrao.edu
Wed May 24 18:52:38 EDT 2006


----------
X-Sun-Data-Type: text
X-Sun-Data-Description: text
X-Sun-Data-Name: text
X-Sun-Charset: us-ascii
X-Sun-Content-Lines: 12

Code:  CMP timesetting - vxWorks part

Suggested date May 31 at 10:00 AM

Suggested reviewers - let me know - RSVP

Author:    Hichem Ben Frej
Chairman:  Barry Clark
Scribe:    Ken Sowinski
Lector:    Bill Sahr
Member:    Walter Brisken
Member:    Martin Pokorny
----------
X-Sun-Data-Type: default
X-Sun-Data-Description: default
X-Sun-Data-Name: slcTime.lis
X-Sun-Charset: us-ascii
X-Sun-Content-Lines: 513

     1	/* 
     2	  @(#)slcTime.c  version 0.1  created 05/03/29
     3	    %% VLA antennas time
     4	    LANGUAGE: C
     5	    ENVIRONMENT: EVLA Interim CMP real-time system running under vxWorks
     6	*/
       
     7	#include <stdio.h>
     8	#include <vxWorks.h>
     9	#include <sntpcLib.h>
    10	#include <in.h>
    11	#include <inetLib.h>
       
    12	#include <semLib.h>
    13	#include <taskLib.h>
    14	#include <tickLib.h>
    15	#include <timers.h>
       
    16	#include "cmp.h"
    17	#include "slc.h"
    18	#include "timedef.h"
       
    19	#define WG_NORMAL_RATE 192
    20	#define WG_24CYCLE_RATE 185
       
    21	#define MAX_BUFFERED_TIMESTAMPS 4ul /* must be an even power of 2 */
       
    22	/* time index calculator */
    23	#define TIMEIND(i) ((i+1) & (MAX_BUFFERED_TIMESTAMPS-1) )
       
    24	/* All times have units of days */
    25	double  msgtime[MAX_BUFFERED_TIMESTAMPS]; /* an array of the most 
    26						     recently arrived
    27						     times in timing messages */
    28	double  msgtick[MAX_BUFFERED_TIMESTAMPS]; /* the system timer readings 
    29						     when these messages 
    30						     arrived */
       
    31	int time_index = 0;          /* index into the above arrays */
       
    32	double msgTime;		/* time in fastest timing message */
       
    33	double msgTick;		/* system timer when that msg arrived */
       
       
    34	double msgDiff;		/* difference of the two above - 
    35				   effectively, this is taken as the real time that the
    36				   MIB rebooted, so it's the base time. */
       
    37	double intTime;		/* time at last heartbeat interrupt */
    38	double intDiff;		/* difference between known time and system
    39				   timer at last heartbeat interrupt */
    40	double intSTick;		/* system timer at last heartbeat interrupt */
       
    41	double heartInterval;    /* time between heartbeat interrupts */
    42	int heartRate;		/* heartbeat rate (multiple of 0.1 Hz) */
    43	int heartError;		/* number of times heartbeat interrupt has
    44				   arrived at the wrong interval */
    45	int heartReset;		/* number of times the heartbeat rate has been reset */
       
    46	double tick;             /* current clock tick time for interrupt being processed */
    47	/* end of protected variables */
       
    48	/* time debugging values */
    49	double msgAccumDiffs; /* accumulated differences of time messages; this 
    50				 is based on the ones chosen */
       
    51	double intAccumDiffs; /* accumulated variance between calculations of 
    52				 intDiff */
       
    53	/* semaphore to signal start of monitor cycle. Released in slcDriver*/
    54	extern SEM_ID semMonitorStart;
       
    55	/* semaphore to signal end of command cycle. Released in slcDriver*/
    56	extern SEM_ID semCommandEnd;
       
    57	/* semaphore to regulate access to timer */
    58	SEM_ID semTimeStamp;
       
    59	/* semaphore for the hearbeat */
    60	SEM_ID semHeartBeat;
       
       
    61	/* 
    62	 * Time server IP: 
    63	 * timehost.aoc.nrao.edu for AOC
    64	 * amos.evla.nrao.edu for VLA
    65	 */
       
    66	#ifdef _VLA_
    67	char *pServerAddr = "10.80.1.31";
    68	#else
    69	char *pServerAddr = "146.88.1.4";
    70	#endif
       
    71	extern int runSLC;
       
    72	double convert_to_mjd(struct timespec *);
       
       
    73	/* functions prototype */
    74	extern double getTimeStamp();
    75	double sysTick();
    76	void timeMsg(double);
    77	void cmpTime();
       
    78	double gtimeStamp;
       
    79	double getTimeStamp()
    80	{
    81		return gtimeStamp;
    82	}
       
    83	/*
    84	 * this task will run right after the access to time in shared memory.
    85	 */
    86	void setTimeStamp()
    87	{
    88		double sysTime = 0.0;
       
    89		while(runSLC)
    90		{
    91			/* wait fot the green light */
    92			semTake(semCommandEnd, WAIT_FOREVER);
       
    93			/* must synchronize access to the shared global data */
    94			semTake (semTimeStamp, WAIT_FOREVER);
       
    95			sysTime = sysTick();
       
    96	#ifdef _OLD_TIME_ /* changes to match the MIB timeStamp routine. */
       
    97			/* if we have had a heartbeat interrupt, and know what time it was then,
    98				 return that time plus the time since then */
       
    99			if(heartInterval != 0)
   100			{
   101				gtimeStamp = sysTime + intDiff;
   102			}
   103			/* We haven't had any heartbeat interrupts.  If we have had a timing
   104				 message, return the time in the best timing message plus the interval 
   105				 since it arrived. */
   106			else
   107	#endif
       
   108				if(msgDiff != 0)
   109				{
   110					gtimeStamp = sysTime + msgDiff;
   111				}
   112	#ifder _OLD_TIME_
   113			else
   114	{
   115		/* else we haven't the vaguest notion what the time is - return 0 */
       
   116		gtimeStamp = sysTime;
   117	}
   118	#endif
   119	semGive(semTimeStamp);
   120		}
   121	}
       
   122	/* routine to be called when a message containing the time is received */
   123	void timeMsg(double time)
   124	{
   125		int i,j;
   126		double approx_reboot, temp_diff;
   127		STATUS status;
       
   128		/* 
   129		   * this is a circular buffer with a head index (time_index) which 
   130		   * indicates the next position to write to.
   131		   */
       
   132		msgtime[time_index] = time;
       
   133		msgtick[time_index] = sysTick();
       
   134		/*
   135		   * Look through the stack of timing messages and find the one that
   136		   * apparently took the least time in transit.
   137		   * NOTE: changed from original code.  The test in the loop below will
   138		   * find the message with a time difference that is the largest amongst the
   139		   * messages we've revceived.  That message should be closer to the actual time
   140		   * the MIB rebooted; other lower valued messages will have taken more time to
   141		   * get to the MIB, since we're deducting a sysTick value which is from 
   142		   * the time the MIB rebooted.
   143		   * The result of the test should be an approximate base time for the MIB, i.e.,
   144		   * when the MIB got rebooted.
   145		   * - CFH
   146		   */
       
   147		approx_reboot = time - msgtick[time_index];
       
       
   148		j = time_index;
   149		for(i=TIMEIND(time_index); i != time_index; i=TIMEIND(i))
   150			if ((msgtime[i]!=0) && ((msgtime[i] - msgtick[i]) > approx_reboot))
   151			{
   152				approx_reboot = msgtime[i] - msgtick[i];
   153				j = i;
   154			}
   155		time_index=TIMEIND(time_index);
       
   156		temp_diff = msgDiff; /* save a copy */
       
   157		/* Synchronize access to the shared global data
   158		     This prevents reads to the double floats until we've written them.
   159		   */
       
   160		status = semTake (semTimeStamp, WAIT_FOREVER);
       
   161		msgTime = msgtime[j];
   162		msgTick = msgtick[j];
   163		msgDiff = msgTime - msgTick;
       
   164		status = semGive(semTimeStamp);
       
   165		if (temp_diff) /* don't do first time thru */
   166			msgAccumDiffs += (msgDiff-temp_diff); /* newer time if larger will result in + shift
   167							     in our time base; reflect that here */
   168		return;
       
   169	} /* timeMsg */
       
       
   170	/* 
   171	 * Routine that consults the system timer and returns the
   172	 * time, in fraction of a day, since the chip was powered up.
   173	 */
       
       
   174	double sysTick()
   175	{
   176		ULONG nTicks;
       
   177		double tick_mjd;
       
   178		nTicks = tickGet();
       
   179		tick_mjd = (double)nTicks*INV_PER_DAY_CLK_RATE;
       
   180		return tick_mjd;
       
   181	}
       
   182	/* debug counter */
   183	extern unsigned long TimeCnt;
       
   184	/* 
   185	 * timer high level ISR (HISR) to do more processing and signel global "heartbeat" event.
   186	 * This runs at the highest HISR priority.
   187	 */
       
       
   188	void cmpTime()
   189	{
       
   190		double interval,		/* current estimate of timer interval */
   191		tod,			/* time since 0h UTC today */
   192		temp_diff;
   193		int intRate,		/* interrupt rate (mult of 0.1 Hz) */
   194		day, 			/* integer MJD */
   195		intCount;		/* interrupt number since 0h UTC today */
       
   196		while(runSLC)
   197		{
   198			/* wait for the goahead from the start monitor ISR */
   199			if (ERROR == semTake(semMonitorStart, WAIT_FOREVER))
   200			{
   201				perror("semTake");
   202			}
       
   203			/* increament our debug cnt */
   204			TimeCnt++;
       
   205			/* record the time of this call (interrupt) */
   206			tick = sysTick();
       
   207			if(0.0 == msgTick)
   208				continue;
       
   209			/* if first interrupt we have received, just remember its time. */
   210			if(0.0 == intSTick)
   211			{
   212				intSTick = tick;
   213				continue;
   214			}
       
   215			semGive(semHeartBeat);
       
   216			/* estimate the heartbeat rate - the tick time is from the ISR */
   217			/* we also apply a condition that it be a multiple of 0.1 Hz */
       
   218			interval = tick - intSTick;
   219			if (0.0 == interval)
   220				intRate = WG_NORMAL_RATE;
   221				else
   222				intRate = 1.0/TEN_HZ_CYCLES_PER_DAY/interval + 0.5;
       
   223			interval = (double)1.0/TEN_HZ_CYCLES_PER_DAY/intRate;
   224			intSTick = tick;
       
   225			if (0.0 == heartInterval)
   226			{
   227				/* Here we do not have a known interrupt rate; set it to the 
   228					     rate calculated above */
   229				heartInterval = interval;
   230				heartRate = intRate;
   231			}
       
   232			/* we see if the interrupt rate calculated above agrees with previous
   233				 determination.  If not, we increment a counter */
       
   234			if(intRate == heartRate)
   235				heartError = 0;
   236				else
   237			{
   238				heartError++;
   239				/* printf("intRate: %5.12f -- %d\n", interval, intRate); */
       
   240				/* if too many disagreements, try setting up again */
   241				if(heartError > BADRATE)
   242				{
   243					heartInterval = 0;
   244					heartReset++;
   245				}
   246				continue;
   247			}
       
   248			/* Now we calculate the time of day of this interrupt.
   249				 First we estimate the time (fraction of a day) under the 
   250				 assumption that the timing message arrived in zero time 
   251			      */
       
   252			day = (int)msgTime;
   253			tod = msgDiff + tick - day;
       
   254			/* then we round that up to an integer number of interrupts */
   255			intCount = tod/heartInterval + 0.8;
       
   256	#ifdef _OLD_TIME_ /* this change to match the MIB new way of computing time: 
   257			     See note in setTimeStamp*/
   258			intTime = (double)day + intCount*heartInterval;
   259	#else
   260			intTime = (double)day + tod;
   261	#endif
       
   262			temp_diff = intDiff;
   263			intDiff = intTime - tick;
       
   264			if (temp_diff) /* don't calc first time thru */
   265				intAccumDiffs += (intDiff - temp_diff);
       
   266		} /* end (FOREVER loop) */
       
   267		return;
       
   268	} /* cmpTime */
       
   269	/*
   270	 * Convert from NTP time - an integer fraction and whole integer part, 
   271	 * representing seconds since 1-Jan-1900 00:00:00.00 - into a Modified 
   272	 * Julian Date (MJD) time, which is a double float representing days 
   273	 * since 17-Nov-1858 00:00:00.00.  Julian dates are actually 
   274	 * calculated from 1-Jan-4713 B.C. at 12:00:00.00 4700 B.C., 
   275	 * so the "Modified" part is to subtract 2,400,000.5 from 
   276	 * the Julian date.  QED.
   277	 */
   278	double convert_to_mjd(struct timespec *ntptime )
   279	{
   280		double mjd = MJD_BASE;
       
   281		if (ntptime->tv_sec < NTP_VX_BASE) /* something's wrong */
   282		{
   283			return mjd;
   284		}
       
   285		ntptime->tv_sec = ntptime->tv_sec - NTP_VX_BASE;
   286		mjd += ((double) ntptime->tv_sec/SECS_PER_DAY) + 
   287		    ((double) ntptime->tv_nsec/(ONE_SEC_NSEC*SECS_PER_DAY));
       
   288		return mjd;
       
       
   289	} /* convert_to_mjd */
       
       
   290	/* 
   291	 * ntp time in mjd computed by the Linux board ans stored
   292	 * in shared memory.
   293	 */
   294	extern double ntpTimeInMjd;
       
   295	/*
   296	 * The time client task requests time from a time server on the network.
   297	 * Once time acquired, the network driver is turned off and from then
   298	 * on the time is acquired from the shared memory.
   299	 * This task runs at the end of every command cycle.
   300	 */
   301	void ntpTask()
   302	{
       
   303		double mjd;
   304		u_int timeout = 10 * CLOCK_RATE;
   305		struct timespec CurrTime;
       
   306		int netTaskId;
       
   307		printf( "Time_client task starting\n" );
       
   308		/* get the taskId for the tNetTask */
   309		netTaskId = taskNameToId("tNetTask");
       
   310		while(1) /* try till we get an answer */
   311		{
   312			/* get the first ntp time from the server */
   313			if (OK == sntpcTimeGet(pServerAddr, timeout, &CurrTime))
   314			{
   315				/* now convert to MJD */
   316				mjd = convert_to_mjd(&CurrTime);
       
   317				/* process the MJD */
   318				timeMsg(mjd);
       
   319				/* delete the tNetTask; it won't be needed after this */
   320				taskDelete(netTaskId);
       
   321				/* bring down the network driver */
   322				ifFlagChange("ei0", 0x1, FALSE);
   323				break;
   324			}
   325		}
   326		/* bump this task priority */
   327		taskPrioritySet(taskIdSelf(), 3);
       
   328		/* from now on get it from the shared memory every WGC at the end of command cycle */
   329		while(runSLC)
   330		{
   331			/* wait fot the green light */
   332			semTake(semCommandEnd, WAIT_FOREVER);
       
   333			/* 
   334			       * Skip previously acquired time. 
   335			       * Since we are running much faster rate than the rate 
   336			       * at which the NTP time is acquired
   337			       */
   338			if (ntpTimeInMjd > mjd)
   339			{
   340				/* keep a copy for next cycle */
   341				mjd = ntpTimeInMjd;
       
   342				/* process the MJD */
   343				timeMsg(mjd);
   344			}
   345		}
       
   346	} /* ntpTask */
       
   347	double getHeartbeatInterval()
   348	{
   349		return heartInterval;
   350	} /* getHeartbeatInterval */
       
   351	/*
   352	 * Global helper routine, used by sequencer and anyone else 
   353	 * internally who needs to get the last heartbeat clock time 
   354	 * without going thru the logical points table.
   355	 */
   356	double getHeartbeatTime()
   357	{
   358		return intTime;
   359	} /* getHeartbeatInterval */
       
       
   360	void initSlcTime()
   361	{
   362		printf("initSlcTime starting ...\n");
       
   363		printf("creating binary semaphore for time stamp\n");
   364		semTimeStamp = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
       
   365		printf("creating binary sempahore for heartbeat\n");
   366		semHeartBeat = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
       
   367	#if 1
   368		taskSpawn("cmpTime",
   369		    4,
   370		    0x8,
   371		    DEF_STACK_SIZE,
   372		    (FUNCPTR)cmpTime, 
   373		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   374	#endif
       
   375	#if 1
   376		taskSpawn("setTimeStamp",
   377		    4,
   378		    0x8,
   379		    DEF_STACK_SIZE,
   380		    (FUNCPTR)setTimeStamp, 
   381		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   382	#endif
       
   383		/* Make sure this one has a lower priority than tNetTask */
   384		taskSpawn("NtpClient",
   385		    60,
   386		    0x8,
   387		    DEF_STACK_SIZE,
   388		    (FUNCPTR)ntpTask, 
   389		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
       
       
   390		printf("initSlcTime done...\n");
   391	}



More information about the evla-sw-discuss mailing list