[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