root/trunk/libffado/src/libutil/TimeSource.cpp

Revision 445, 8.8 kB (checked in by pieterpalmers, 17 years ago)

* name change from FreeBoB to FFADO
* replaced tabs by 4 spaces
* got rid of end-of-line spaces
* made all license and copyrights conform

library becomes LGPL, apps become GPL
explicitly state LGPL v2.1 and GPL v2 (don't like v3 draft)

copyrights are 2005-2007 Daniel & Pieter
except for the MotU stuff (C) Jonathan, Pieter

Line 
1 /*
2  * Copyright (C) 2005-2007 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License version 2.1, as published by the Free Software Foundation;
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA
22  */
23
24 #include "TimeSource.h"
25
26 #include <time.h>
27
28 #include <assert.h>
29
30 #include "libutil/Time.h"
31
32 namespace Util {
33
34 IMPL_DEBUG_MODULE( TimeSource, TimeSource, DEBUG_LEVEL_NORMAL );
35
36 TimeSource::TimeSource() :
37     m_Master(NULL), m_last_master_time(0), m_last_time(0),
38     m_slave_rate(0.0), m_slave_offset(0), m_last_err(0.0)
39 {
40
41 }
42
43 TimeSource::~TimeSource() {
44
45 }
46 /**
47  * (re)Initializes the TimeSource
48  *
49  * @return true if successful
50  */
51 void TimeSource::initSlaveTimeSource() {
52     ffado_microsecs_t my_time;
53     ffado_microsecs_t master_time;
54     ffado_microsecs_t my_time2;
55     ffado_microsecs_t master_time2;
56
57     if (m_Master) {
58         my_time=getCurrentTime();
59         master_time=m_Master->getCurrentTime();
60
61         struct timespec ts;
62
63         // sleep for ten milliseconds
64         ts.tv_sec=0;
65         ts.tv_nsec=10000000L;
66
67         nanosleep(&ts,NULL);
68
69         my_time2=getCurrentTime();
70         master_time2=m_Master->getCurrentTime();
71
72         float diff_slave=my_time2-my_time;
73         float diff_master=master_time2-master_time;
74
75         m_slave_rate=diff_slave/diff_master;
76
77         // average of the two offset estimates
78         m_slave_offset  = my_time-wrapTime((ffado_microsecs_t)(master_time*m_slave_rate));
79         m_slave_offset += my_time2-wrapTime((ffado_microsecs_t)(master_time2*m_slave_rate));
80         m_slave_offset /= 2;
81
82         m_last_master_time=master_time2;
83         m_last_time=my_time2;
84
85         debugOutput(DEBUG_LEVEL_NORMAL,"init slave: master=%lu, master2=%lu, diff=%f\n",
86             master_time, master_time2, diff_master);
87         debugOutput(DEBUG_LEVEL_NORMAL,"init slave: slave =%lu, slave2 =%lu, diff=%f\n",
88             my_time, my_time2, diff_slave);
89         debugOutput(DEBUG_LEVEL_NORMAL,"init slave: slave rate=%f, slave_offset=%lu\n",
90             m_slave_rate, m_slave_offset
91             );
92     }
93
94
95 }
96
97 /**
98  * Maps a time point of the master to a time point
99  * on it's own timeline
100  *
101  * @return the mapped time point
102  */
103 ffado_microsecs_t TimeSource::mapMasterTime(ffado_microsecs_t master_time) {
104     if(m_Master) {
105         // calculate the slave based upon the master
106         // and the estimated rate
107
108         // linear interpolation
109         int delta_master=master_time-m_last_master_time;
110
111         float offset=m_slave_rate * ((float)delta_master);
112
113         ffado_microsecs_t mapped=m_last_time+(int)offset;
114
115         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"map time: master=%d, offset=%f, slave_base=%lu, pred_ticks=%lu\n",
116             master_time, offset, m_last_time,mapped
117             );
118
119         return wrapTime(mapped);
120
121     } else {
122         debugOutput( DEBUG_LEVEL_VERBOSE, "Requested map for non-slave TimeSource\n");
123
124         return master_time;
125     }
126 }
127
128 /**
129  * Update the internal state of the TimeSource
130  *
131  */
132 bool TimeSource::updateTimeSource() {
133     // update all slaves
134     for ( TimeSourceVectorIterator it = m_Slaves.begin();
135           it != m_Slaves.end(); ++it ) {
136
137         // update the slave with the current
138         // master time
139         if (!(*it)->updateTimeSource()) return false;
140     }
141
142     // this TimeSource has a master
143     if(m_Master) {
144         ffado_microsecs_t my_time=getCurrentTime();
145         ffado_microsecs_t master_time=m_Master->getCurrentTime();
146
147         // we assume that the master and slave time are
148         // measured at the same time, but that of course is
149         // not really true. The DLL will have to filter this
150         // out.
151
152         // the difference in master time
153         int64_t delta_master;
154         if (master_time > m_last_master_time) {
155             delta_master=master_time-m_last_master_time;
156         } else { // wraparound
157             delta_master=m_Master->unWrapTime(master_time)-m_last_master_time;
158         }
159
160         // the difference in slave time
161         int64_t delta_slave;
162         if (my_time > m_last_time) {
163             delta_slave=my_time-m_last_time;
164         } else { // wraparound
165             delta_slave=unWrapTime(my_time)-m_last_time;
166         }
167
168         // the estimated slave difference
169         int delta_slave_est=(int)(m_slave_rate * ((double)delta_master));
170
171         // the measured & estimated rate
172         double rate_meas=((double)delta_slave/(double)delta_master);
173         double rate_est=((double)m_slave_rate);
174
175         m_last_err=(rate_meas-rate_est);
176
177         m_slave_rate += 0.01*m_last_err;
178
179         debugOutput(DEBUG_LEVEL_VERBOSE,"update slave: master=%llu, master2=%llu, diff=%lld\n",
180             master_time, m_last_master_time, delta_master);
181         debugOutput(DEBUG_LEVEL_VERBOSE,"update slave: slave =%llu, slave2 =%llu, diff=%lld, diff_est=%d\n",
182             my_time, m_last_time, delta_slave, delta_slave_est);
183         debugOutput(DEBUG_LEVEL_VERBOSE,"update slave: slave rate meas=%f, slave rate est=%f, err=%f, slave rate=%f\n",
184             rate_meas, rate_est, m_last_err, m_slave_rate
185             );
186
187
188         m_last_master_time=master_time;
189
190         int64_t tmp = delta_slave_est;
191         tmp += m_last_time;
192
193         m_last_time = tmp;
194
195
196
197     }
198
199     return true;
200 }
201
202 /**
203  * Sets the master TimeSource for this timesource.
204  * This TimeSource will sync to the master TimeSource,
205  * making that it will be able to map a time point of
206  * the master to a time point on it's own timeline
207  *
208  * @param ts master TimeSource
209  * @return true if successful
210  */
211 bool TimeSource::setMaster(TimeSource *ts) {
212     if (m_Master==NULL) {
213         m_Master=ts;
214
215         // initialize ourselves.
216         initSlaveTimeSource();
217
218         return true;
219     } else return false;
220 }
221
222 /**
223  * Clears the master TimeSource for this timesource.
224  *
225  * @return true if successful
226  */
227 void TimeSource::clearMaster() {
228     m_Master=NULL;
229 }
230
231 /**
232  * Registers a slave timesource to this master.
233  * A slave TimeSource will sync to this TimeSource,
234  * making that it will be able to map a time point of
235  * the master (this) TimeSource to a time point on
236  * it's own timeline
237  *
238  * @param ts slave TimeSource to register
239  * @return true if successful
240  */
241 bool TimeSource::registerSlave(TimeSource *ts) {
242     // TODO: we should check for circular master-slave relationships.
243
244     debugOutput( DEBUG_LEVEL_VERBOSE, "Registering slave (%p)\n", ts);
245     assert(ts);
246
247     // inherit debug level
248     ts->setVerboseLevel(getDebugLevel());
249
250     if(ts->setMaster(this)) {
251         m_Slaves.push_back(ts);
252         return true;
253     } else {
254         return false;
255     }
256 }
257
258 /**
259  * Unregisters a slave TimeSource
260  *
261  * @param ts slave TimeSource to unregister
262  * @return true if successful
263  */
264 bool TimeSource::unregisterSlave(TimeSource *ts) {
265     debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering TimeSource (%p)\n", ts);
266     assert(ts);
267
268     for ( TimeSourceVectorIterator it = m_Slaves.begin();
269           it != m_Slaves.end(); ++it ) {
270
271         if ( *it == ts ) {
272             m_Slaves.erase(it);
273             ts->clearMaster();
274             return true;
275         }
276     }
277
278     debugOutput( DEBUG_LEVEL_VERBOSE, " TimeSource (%p) not found\n", ts);
279
280     return false;
281 }
282
283 /**
284  * Set verbosity level.
285  * All slave timesources get the same verbosity level
286  *
287  * @param l verbosity level
288  */
289 void TimeSource::setVerboseLevel(int l) {
290     setDebugLevel(l);
291
292     for ( TimeSourceVectorIterator it = m_Slaves.begin();
293           it != m_Slaves.end(); ++it ) {
294
295         (*it)->setVerboseLevel(l);
296     }
297
298 }
299
300 void TimeSource::printTimeSourceInfo() {
301     debugOutputShort( DEBUG_LEVEL_NORMAL, "TimeSource (%p) info\n", this);
302     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Master           : %p\n", m_Master);
303     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Slave rate       : %f\n", m_slave_rate);
304     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Slave offset     : %lld\n", m_slave_offset);
305     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Last error       : %f\n", m_last_err);
306     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Last master time : %llu\n",m_last_master_time );
307     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Last slave time  : %llu\n",m_last_time );
308
309
310     for ( TimeSourceVectorIterator it = m_Slaves.begin();
311           it != m_Slaves.end(); ++it ) {
312
313         (*it)->printTimeSourceInfo();
314     }
315 }
316
317 } // end of namespace Util
Note: See TracBrowser for help on using the browser.