尽管设备NTP启动后等指令秒抵消使用率 [英] Device starting instruction seconds after other despite NTP offset usage

查看:142
本文介绍了尽管设备NTP启动后等指令秒抵消使用率的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

我有两个物理设备,银河S3(电话)和华硕700T(平板电脑),我想在精确的同一时间执行相同的指令集。因此,我使用Android的<一个href=\"https://github.com/android/platform_frameworks_base/blob/master/core/java/android/net/SntpClient.java\"相对=nofollow>平台框架基本SNTP客户端code 来实例化一个SNTP客户端,获取原子时,计算偏移基于该系统时间,并增加了正/负偏移指令执行时间戳使得它运行在跨所有设备完全相同的时间(几毫秒之内)。我在做开/关一组相机手电筒在一秒钟的时间间隔开始对全值,例如12:47:00.000分,因为这是明显的和相对简单的,看看我的过程是正确的。

问题:

一设备趋向(用秒表一个非常明显的3-5秒),开始在另一个后面的方式

案例:S3〜背后原子时间0.640秒,背后原子时间700T〜1.100秒; 700T明显开始〜S3之后3.7秒。

方法用来解决这一问题:

有是一个Android应用程序, ClockSync 这台设备,以原子时并声称20ms内有准确度。我比较我的右偏移计算的运行我的应用程序和之前的区别除了其偏移和矿山偏离不超过20ms的〜(即Clocksync失调可能是0.620,我的将是不超过0.640上无论是S3或700T )。

我生成时间戳闪光手电筒模式被接通后右/关闭,和东西结账,设备之间的唯一区别是,人们可能会有点领先于其他的,因为它是打印系统时间和一个设备可能是大约半秒比其他慢

*请注意NTP偏移的大部分都被过滤掉了,由于他们的绝对数量减少的可读性

S3明显开始第一和700T根据物理秒表我手头上开始后约2.130秒。

700T:

运行我的应用程序之前,根据Clocksync应用偏移:1264

  D / NTP偏移:1254
D / NTP偏移:1242
D / NTP偏移:1203
NTPOffset之前D / instrCal:2014年8月15日15:17:1.0
NTPOffset经过D / instrCal:2014年8月15日15:17:2.203
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:02.217
NTPOffset之前D / instrCal:2014年8月15日15:17:2.0
NTPOffset经过D / instrCal:2014年8月15日15:17:3.245
D / dalvikvm:GC_CONCURRENT释放399K,13%免费3930K / 4496K,暂停14毫秒+ 1毫秒,总46ms
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:03.253
NTPOffset之前D / instrCal:2014年8月15日15:17:3.0
NTPOffset经过D / instrCal:2014年8月15日15:17:4.231
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:04.236
NTPOffset之前D / instrCal:2014年8月15日15:17:4.0
NTPOffset经过D / instrCal:2014年8月15日15:17:5.248
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:05.254
NTPOffset之前D / instrCal:2014年8月15日15:17:5.0
NTPOffset经过D / instrCal:2014年8月15日15:17:6.237
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:06.242
NTPOffset之前D / instrCal:2014年8月15日15:17:6.0
NTPOffset经过D / instrCal:2014年8月15日15:17:7.243
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:07.255
NTPOffset之前D / instrCal:2014年8月15日15:17:7.0
NTPOffset经过D / instrCal:2014年8月15日15:17:8.240
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:08.246
D / dalvikvm:GC_FOR_ALLOC释放366K,15%的可用3910K / 4552K,暂停28ms,总28ms
NTPOffset之前D / instrCal:2014年8月15日15:17:8.0
NTPOffset经过D / instrCal:2014年8月15日15:17:9.221
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:09.227
NTPOffset之前D / instrCal:2014年8月15日15:17:9.0
NTPOffset经过D / instrCal:2014年8月15日15:17:10.245
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:10.251

S3:

运行我的应用程序之前,根据Clocksync应用偏移:1141

  D / NTP偏移:1136
D / NTP偏移:1136
D / NTP偏移:1137
NTPOffset之前D / instrCal:2014年8月15日15:17:1.0
NTPOffset经过D / instrCal:2014年8月15日15:17:2.137
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:02.156
NTPOffset之前D / instrCal:2014年8月15日15:17:2.0
NTPOffset经过D / instrCal:2014年8月15日15:17:3.135
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:03.145
NTPOffset之前D / instrCal:2014年8月15日15:17:3.0
NTPOffset经过D / instrCal:2014年8月15日15:17:4.134
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:04.143
NTPOffset之前D / instrCal:2014年8月15日15:17:4.0
NTPOffset经过D / instrCal:2014年8月15日15:17:5.135
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:05.144
NTPOffset之前D / instrCal:2014年8月15日15:17:5.0
NTPOffset经过D / instrCal:2014年8月15日15:17:6.133
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:06.141
NTPOffset之前D / instrCal:2014年8月15日15:17:6.0
NTPOffset经过D / instrCal:2014年8月15日15:17:7.135
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:07.145
NTPOffset之前D / instrCal:2014年8月15日15:17:7.0
NTPOffset经过D / instrCal:2014年8月15日15:17:8.133
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:08.142
NTPOffset之前D / instrCal:2014年8月15日15:17:8.0
NTPOffset经过D / instrCal:2014年8月15日15:17:9.136
D /闪光灯:闪光灯手电筒模式关闭呼叫击中2014年8月15日15:17:09.146
NTPOffset之前D / instrCal:2014年8月15日15:17:9.0
NTPOffset经过D / instrCal:2014年8月15日15:17:10.136
D /闪光:开来电闪手电筒模式击中2014年8月15日15:17:10.146

根据邮票花费不超过30毫秒为每个设备打开/关闭闪光灯,所以虽然它不可取的,因为它是30毫秒需要时以后,它没有那么大的差别,并不能解释启动装置上的巨大差异。

code:

在开始的时候,我宣布了一堆全局变量的活性生命周期方法,如外:

  PowerManager.WakeLock激活锁定;
私人相机拍照;
私人布尔isFlashOn;
私人布尔hasFlash;
私人SQLiteDbAdapter dbHelper;
私人SimpleCursorAdapter DataAdapter的;
私人处理程序instrHandler =新的处理程序();
私人INT arrayCounter = 0;
私人长期NTPOffset;
私人日历NTPcal = Calendar.getInstance();

OnStart方法

  @覆盖
    保护无效调用onStart(){
        super.onStart();
        //需要确保CPU运行,即使用户可能不是触摸屏保持
        电源管理PM =(电源管理)getSystemService(Context.POWER_SERVICE);
        激活锁定= pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                展wakelook);
        wakeLock.acquire();        新GetNTPServerTimeTask()执行();
        //在启动应用程序拿相机PARAMS
        getCamera的();        //准备好从数据库SQLite的说明拉
        dbHelper =新SQLiteDbAdapter(本);
        dbHelper.open();        //获取要使用说明
        最终名单&LT; D​​ynamoDBManager.EventInstruction&GT; instructionSet = setListFromInstructionQuery();        最终的Runnable runnableInstructions =新的Runnable(){
            @覆盖
            公共无效的run(){
                Log.d(runnableInstructions时间戳指令#顶+ arrayCounter,getCurrentTimeStamp());
                串instrType = instructionSet.get(arrayCounter).getInstructionType();
                串instrDetail = instructionSet.get(arrayCounter).getInstructionDetail();                如果(instrType.equals(闪)){
                    如果(instrDetail.equals(上)){
                        turnOnFlash();
                    }否则如果(instrDetail.equals(关)){
                        turnOffFlash();
                    }
                }                //获取下一个指令时间
                arrayCounter ++;                //循环,直到我们离开的指令
                如果(arrayCounter&下; instructionSet.size()){
                    字符串STARTTIME = instructionSet.get(arrayCounter).getInstructionStartTime();
                    日历instrCal = convertISO8601StringToCal(startTime时);
                    printYMDHMSM(instrCal NTPOffset之前,instrCal);
                    instrCal.add(Calendar.MILLISECOND,(INT)NTPOffset);
                    printYMDHMSM(instrCal NTPOffset后,instrCal);                    长差异= instrCal.getTimeInMillis() - System.currentTimeMillis的();
                    字符串=那么sdiff将String.valueOf(差异);
                    Log.d(时间戳的差值计算,getCurrentTimeStamp());
                    Log.d(差异,差,那么sdiff +);
                    instrHandler.postDelayed(这一点,差异);
                }
            }
        };        可运行runnableInstructionsDelay =新的Runnable(){
            @覆盖
            公共无效的run(){
                Log.d(时间戳在拿到第一个指令的时间,getCurrentTimeStamp());
                字符串STARTTIME = instructionSet.get(arrayCounter).getInstructionStartTime();
                日历instrCal = convertISO8601StringToCal(startTime时);
                printYMDHMSM(NTPOffset之前首先INSTR instrCal,instrCal);
                instrCal.add(Calendar.MILLISECOND,(INT)NTPOffset);
                printYMDHMSM(第一INSTR instrCal NTPOffset后,instrCal);                长差异= instrCal.getTimeInMillis() - System.currentTimeMillis的();
                instrHandler.postDelayed(runnableInstructions,差异);
            }
        };        //获取第一个指令时间
        如果(arrayCounter&下; instructionSet.size()及&放大器; arrayCounter == 0){
            //由于活动得到自动切换第一条指令时间戳之前30秒,我们要
            //只使用最新的NTP向右偏移启动指令集之前,
            instrHandler.postDelayed(runnableInstructionsDelay,25000);
        }
    }

NTP抵消异步任务的循环,并设置一个全局变量NTPoffset

 公共类GetNTPServerTimeTask扩展
            AsyncTask的&LT;太虚,太虚,太虚&GT; {        长NTPnow = 0;        @覆盖
        保护无效doInBackground(虚空......空隙
        ){            SntpClient客户端=新SntpClient();
            如果(client.requestTime(0.north-america.pool.ntp.org,10000)){
                NTPnow = client.getNtpTime()+ SystemClock.elapsedRealtime() - client.getNtpTimeReference();
                NTPcal.setTime(新日期(NTPnow));
                //如果NTPCal是未来,我们希望值是正的,所以我们可以增加价值的系统时钟相匹配
                NTPOffset = NTPcal.getTimeInMillis() - System.currentTimeMillis的();                //调试时间
                Log.d(NTP现在,将String.valueOf(NTPnow));
                Log.d(NTP SYSTEMTIME,将String.valueOf(System.currentTimeMillis的()));
                Log.d(NTP偏移,将String.valueOf(NTPOffset));
                printYMDHMSM(日历实例,Calendar.getInstance());
                printYMDHMSM(NTPCal值,NTPcal);
            }
            返回null;
        }
    @覆盖
    保护无效onPostExecute(虚空避免){
        super.onPostExecute(避免);
        新GetNTPServerTimeTask()执行();
    }
}

闪光灯开/关方式:

 私人无效turnOnFlash(){
    如果(!isFlashOn){
        如果(相机== NULL || PARAMS == NULL){
            返回;
        }        PARAMS = camera.getParameters();
        params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        Log.d(闪光,+ getCurrentTimeStamp()呼叫在打闪光灯手电筒模式);
        camera.setParameters(PARAMS);
        camera.start preVIEW();
        isFlashOn = TRUE;
    }}私人无效turnOffFlash(){
    如果(isFlashOn){
        如果(相机== NULL || PARAMS == NULL){
            返回;
        }        PARAMS = camera.getParameters();
        params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
        Log.d(闪光,闪手电筒模式关闭呼叫击中+ getCurrentTimeStamp());
        camera.setParameters(PARAMS);
        camera.stop preVIEW();
        isFlashOn = FALSE;
    }
}

时间戳方法,我写道:

 公共静态字符串getCurrentTimeStamp(){
    尝试{        SimpleDateFormat的日期格式=新的SimpleDateFormat(YYYY-MM-DD HH:MM:SS.SSS);
        字符串currentTimeStamp = dateFormat.format(新的Date()); //查找今天的日期        返回currentTimeStamp;
    }赶上(例外五){
        e.printStackTrace();        返回null;
    }
}


解决方案

您说,您正在使用的相机闪光灯只是作为一个测试,看看你的方法是工作,但我觉得你的测试用例的选择是什么让你陷入困境。除非它真的是你的最终目标有在同一时间的照相机闪光灯火,尝试选择不同的东西来测试它。你可以让他们玩一个声音,但有可能在音频子系统的一些未predictable延迟 - 一个更好的测试将是,你必须更明确的控制的东西,比如通过用户界面框架闪烁的屏幕上的内容,或更好的是,通过GLSurfaceView,在那里你有超过帧速率非常精细的控制,并确切地知道等待时间应该是什么在屏幕上闪烁的东西。

我觉得这里发生了什么是你必须从两个不同的供应商的两个完全不同的设备。我不知道肯定,但我猜三星拥有三星提供的摄像头实现,并很可能于低启动延迟进行优化,这样可以拉动手机从口袋里拿出来和拍照速度非常快。华硕使得不同的权衡(这是一个平板电脑,摄影是不那么重要)。该器件还肯定要用不同的相机硬件和有不同的驱动程序。因此,即使你正在做的调用相机子系统几乎同时在两台设备上,但实际上这一号召有不同的反应。它可能要火了另一个进程,把它的意图,获取实时相机preVIEW去,走出去,把一杯咖啡,诸如此类的事情。另外,如果你想有两个相同的设备上运行相同的操作系统运行测试,你可能会得到更好的结果。

作为一个更普遍的评论,我不知道你的总体目标是什么,但不要让您的希望为能够过紧的公差范围内实现同时性 - 很多东西都对你的工作。 Android是没有设计成一个实时操作系统,甚至在延迟就像是现场音频重要的地方,它有一些追赶工作要做。操作系统可能不会给你想要与你的过程中,调度延迟,除非你是在错误的时间非常小心内存分配和这样的(垃圾收集,一切都是Java的Andr​​oid上可以是一个小联合国predictable出窗口)。你的进程可以在任何时间,如果别的东西发生碰撞得到周围。即使在一开始你NTP同步可有点忧虑,特别是如果你在同步移动网络连接,并没有的WiFi(虽然话说回来,我不知道该怎么好该协议是在处理这一点)。把事情到半秒之内应该是可行的,也许,我想,把事情要小于10ms很可能是极其艰难,而在介于将...介于两者之间。

更新1

您正在使用 android.os.Handler 类做你的时间。我没有采取梳理出你的日志,看看如何接近同时你当两个设备唤醒了越来越并试图发出信号给外界的时间,但处理程序可能无法在这做了很好的工作。如果问题的一部分是,这种设备甚至不认为他们已经非常接近对方,其内部时钟+ NTP时间戳衡量,那么你可以尝试不同的东西,像 android.app .AlarmManager 。不知道这是否会是好还是坏,但它会有所不同。

Background:

I have two physical devices, a Galaxy S3 (phone) and an Asus 700T (tablet) that I want to execute the same set of instructions at the exact same time. As such, I'm using Android's Platform Frameworks Base SNTP client code to instantiate a SNTP client which gets the atomic time, calculates an offset based off the system time, and adds the positive/negative offset to the instruction execution timestamp so that it runs at the exact same time (within a few milliseconds) across all devices. I'm doing a set of camera flashlight on/off in one second intervals starting on whole values eg 12:47:00.000 pm because it's noticeable and relatively simple to see if my process is correct.

Issue:

One device tends to start way behind the other (a very noticeable 3-5 seconds using a stopwatch).

Case Example: S3 ~.640 seconds behind atomic time, 700T ~1.100 seconds behind atomic time; 700T visibly starts ~3.7 seconds after S3.

Methods Employed to Solve the Issue:

There is an Android app, ClockSync which sets a device to atomic time and claims to have accuracy within 20ms. I have compared my calculated offsets with its right before running my app and the difference its offset and mine strays no further than ~20ms apart (i.e. Clocksync's offset might be .620, mine would be no further than .640 on either the S3 or 700T).

I generate timestamps right after the flash torch mode gets turned off/on, and things check out, the only difference between devices is that one might be a little ahead of the other because it's printing system time and one device might be about half a second slower than the other.

*Note the bulk of the NTP Offsets have been filtered out due the sheer number of them reducing readability

S3 visibly started first and the 700T started about 2.130 seconds after according to a physical stopwatch I had on hand.

700T:

Offset according to Clocksync app before running my app: 1264

D/NTP Offset﹕ 1254
D/NTP Offset﹕ 1242
D/NTP Offset﹕ 1203
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:1.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:2.203
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:02.217
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:2.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:3.245
D/dalvikvm﹕ GC_CONCURRENT freed 399K, 13% free 3930K/4496K, paused 14ms+1ms, total 46ms
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:03.253
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:3.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:4.231
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:04.236
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:4.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:5.248
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:05.254
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:5.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:6.237
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:06.242
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:6.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:7.243
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:07.255
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:7.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:8.240
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:08.246
D/dalvikvm﹕ GC_FOR_ALLOC freed 366K, 15% free 3910K/4552K, paused 28ms, total 28ms
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:8.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:9.221
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:09.227
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:9.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:10.245
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:10.251

S3:

Offset according to Clocksync app before running my app: 1141

D/NTP Offset﹕ 1136
D/NTP Offset﹕ 1136
D/NTP Offset﹕ 1137
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:1.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:2.137
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:02.156
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:2.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:3.135
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:03.145
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:3.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:4.134
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:04.143
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:4.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:5.135
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:05.144
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:5.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:6.133
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:06.141
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:6.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:7.135
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:07.145
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:7.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:8.133
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:08.142
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:8.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:9.136
D/Flash﹕ Flash torch mode off call hit at 2014-08-15 15:17:09.146
D/instrCal before NTPOffset﹕ 2014-8-15 15:17:9.0
D/instrCal after NTPOffset﹕ 2014-8-15 15:17:10.136
D/Flash﹕ Flash torch mode on call hit at 2014-08-15 15:17:10.146

Based on the stamps it takes no more than 30 ms for each device to turn flash on/off, so while it's not desirable in that it's 30ms after when desired, it's not that big of a difference and can't account for the immense difference between starting on the devices.

Code:

At the beginning I declare a bunch of global variables outside the activity lifecycle methods such as:

PowerManager.WakeLock wakeLock;
private Camera camera;
private boolean isFlashOn;
private boolean hasFlash;
private SQLiteDbAdapter dbHelper;
private SimpleCursorAdapter dataAdapter;
private Handler instrHandler = new Handler();
private int arrayCounter = 0;
private long NTPOffset;
private Calendar NTPcal = Calendar.getInstance();

onStart method

   @Override
    protected void onStart() {
        super.onStart();
        // Needed to ensure CPU keeps running even though user might not touch screen
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                "Show wakelook");
        wakeLock.acquire();

        new GetNTPServerTimeTask().execute();


        // On starting the app get the camera params
        getCamera();

        // Get ready to pull instructions from SQLite DB
        dbHelper = new SQLiteDbAdapter(this);
        dbHelper.open();

        // Fetch instructions to be used
        final List<DynamoDBManager.EventInstruction> instructionSet = setListFromInstructionQuery();

        final Runnable runnableInstructions = new Runnable() {
            @Override
            public void run() {
                Log.d("top of runnableInstructions timestamp for instruction #" + arrayCounter, getCurrentTimeStamp());
                String instrType = instructionSet.get(arrayCounter).getInstructionType();
                String instrDetail = instructionSet.get(arrayCounter).getInstructionDetail();

                if (instrType.equals("flash")) {
                    if (instrDetail.equals("on")) {
                        turnOnFlash();
                    } else if (instrDetail.equals("off")) {
                        turnOffFlash();
                    }
                }

                // Get the next instruction time
                arrayCounter++;

                // Loop until we're out of instructions
                if (arrayCounter < instructionSet.size()) {
                    String startTime = instructionSet.get(arrayCounter).getInstructionStartTime();
                    Calendar instrCal = convertISO8601StringToCal(startTime);
                    printYMDHMSM("instrCal before NTPOffset", instrCal);
                    instrCal.add(Calendar.MILLISECOND, (int) NTPOffset);
                    printYMDHMSM("instrCal after NTPOffset", instrCal);

                    long diff = instrCal.getTimeInMillis() - System.currentTimeMillis();
                    String sDiff = String.valueOf(diff);
                    Log.d("Timestamp at difference calculation", getCurrentTimeStamp());
                    Log.d("Difference", "Difference " + sDiff);
                    instrHandler.postDelayed(this, diff);
                }
            }
        };

        Runnable runnableInstructionsDelay = new Runnable() {
            @Override
            public void run() {
                Log.d("Timestamp at get first instruction time", getCurrentTimeStamp());
                String startTime = instructionSet.get(arrayCounter).getInstructionStartTime();
                Calendar instrCal = convertISO8601StringToCal(startTime);
                printYMDHMSM("First instr instrCal before NTPOffset", instrCal);
                instrCal.add(Calendar.MILLISECOND, (int) NTPOffset);
                printYMDHMSM("First instr instrCal after NTPOffset", instrCal);

                long diff = instrCal.getTimeInMillis() - System.currentTimeMillis();
                instrHandler.postDelayed(runnableInstructions, diff);
            }
        };

        // Get the first instruction time
        if (arrayCounter < instructionSet.size() && arrayCounter == 0) {
            // Since activity gets auto-switched to 30 seconds before first instruction timestamp we want to 
            // use only the most recent NTP offset right before launching the instruction set
            instrHandler.postDelayed(runnableInstructionsDelay, 25000);
        }
    }

NTP offset Async Task that loops and sets a global NTPoffset variable

public class GetNTPServerTimeTask extends
            AsyncTask<Void, Void, Void> {

        long NTPnow = 0;

        @Override
        protected Void doInBackground(Void... voids
        ) {

            SntpClient client = new SntpClient();
            if (client.requestTime("0.north-america.pool.ntp.org", 10000)) {
                NTPnow = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference();
                NTPcal.setTime(new Date(NTPnow));
                // If NTPCal is ahead, we want the value to be positive so we can add value to system clock to match
                NTPOffset = NTPcal.getTimeInMillis() - System.currentTimeMillis();

                // Time debugging
                Log.d("NTP Now", String.valueOf(NTPnow));
                Log.d("NTP SystemTime", String.valueOf(System.currentTimeMillis()));
                Log.d("NTP Offset", String.valueOf(NTPOffset));
                printYMDHMSM("Calendar Instance", Calendar.getInstance());
                printYMDHMSM("NTPCal Value", NTPcal);
            }
            return null;
        }
    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        new GetNTPServerTimeTask().execute();
    }
}

Flash on/off methods:

private void turnOnFlash() {
    if (!isFlashOn) {
        if (camera == null || params == null) {
            return;
        }

        params = camera.getParameters();
        params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        Log.d("Flash", "Flash torch mode on call hit at " + getCurrentTimeStamp());
        camera.setParameters(params);
        camera.startPreview();
        isFlashOn = true;
    }

}

private void turnOffFlash() {
    if (isFlashOn) {
        if (camera == null || params == null) {
            return;
        }

        params = camera.getParameters();
        params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
        Log.d("Flash", "Flash torch mode off call hit at " + getCurrentTimeStamp());
        camera.setParameters(params);
        camera.stopPreview();
        isFlashOn = false;
    }
}

Timestamp method I wrote:

public static String getCurrentTimeStamp() {
    try {

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String currentTimeStamp = dateFormat.format(new Date()); // Find todays date

        return currentTimeStamp;
    } catch (Exception e) {
        e.printStackTrace();

        return null;
    }
}

解决方案

You said that you were using the camera flash just as a test to see if your approach is working, but I think your choice of a test case is what's getting you into trouble. Unless it really is your end goal to have the camera flashes fire at the same time, try choosing something different to test it. You could have them play a sound, but there may be some unpredictable latency in the audio subsystem -- a better test would be something where you have more explicit control, like flashing something on the screen via the UI framework, or better yet, flashing something on the screen via GLSurfaceView, where you have very fine-grained control over the frame rate and know exactly what the latency should be.

I think what's happening here is that you have two completely different devices from two different vendors. I don't know for certain, but I'm guessing the Samsung has a Samsung-supplied camera implementation, and it's likely to be optimized for low startup latency, so you can pull the phone out of your pocket and take a picture very quickly. The Asus makes different tradeoffs (it's a tablet, photography is less important). The devices also certainly use different camera hardware and have different drivers. So even though you're making the call to the camera subsystem nearly simultaneously on both devices, they actually respond to that call differently. It may have to fire up another process, send it an intent, get the live camera preview going, go out and make a cup of coffee, that sort of thing. Alternately, if you would run the test with two of the same device running the same OS, you would probably get better results.

As a more general comment, I don't know what your overall goals are, but don't get your hopes up for being able to achieve simultaneity within too tight a tolerance -- a lot of things are working against you. Android isn't designed as a real-time OS, and even in places where latency is important like live audio, it's got some catching up to do. The OS may not give you the scheduling latency you want with your process, and Java on Android can be a little unpredictable unless you're very careful about memory allocation and such (a garbage collection at the wrong time and everything is out the window). Your process can get bumped around at any time if something else occurs. Even your NTP synchronization at the beginning can be a little fraught, especially if you're synchronizing over a mobile network connection and not WiFi (though having said that, I don't know how good the protocol is at dealing with that). Getting things to within a half-second should be feasible, maybe, I think, getting things to less than 10ms is likely to be extremely tough, and somewhere in between will be...somewhere in between.

UPDATE 1

You're using the android.os.Handler class to do your timing. I haven't taken the time to tease out your logs and see how close to simultaneous you're getting with when both devices are woken up and attempt to signal to the outside world, but Handler may not do a very good job at it. If part of the problem is that the devices don't even think they're getting very close to each other, measured by their internal clock + NTP timestamp, then you could try something different, like android.app.AlarmManager. Don't know if that will be better or worse, but it will be different.

这篇关于尽管设备NTP启动后等指令秒抵消使用率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆