灯光博物馆

  得知海山世界近期弄了个国际灯光节,也给本来就很赞的夜景更添几许华丽。作为光影爱好者,自然不能错过,吃过晚餐,带上EOS M3便去瞧了下,顺便叫上朋友叙叙旧。不过户外的展出是在少的可怜,不细看都没发现新设的灯光摆放在那。

  当然既然是用上了“国际”两个字的主题,怎能少的了从国外重金搬过来的展品,这不都圈起来放在了海上世界B区桥下的展厅,没错就是要收费,祭出了即将到期的深大学生证半价进去看了一圈。挺好使的,考虑还是别申请毕业了,再多玩玩~~

  接下来就是剧透了,里面一共就7幅作品,说实话第一眼却是觉得无聊,弄几个霓虹灯,几何图形就来糊弄人。后来多次观察与解说的攀谈中才知会,原来重点是在这些作品的表现形式,均带有起源性和里程碑意义,不然怎么叫灯光博物馆呢。

  James Turrell的三幅全息作品,最为印象深刻,大概是科因为幻情节吧,一听到全息这两个词就已经激动不已了。不同观察角度有不同的效果,仿佛是在一个有限的平面上孕育着无限的信息。比如蓝色的这幅,正面看是蓝色的一条斜线,在向右移动时候,线段会逐渐拉伸成为一个面,这幅作品仅在自然光下便可观测到。
IMG_3671
  中间黄色这幅起初并没有发现任何内容,原来是要低角度才能看得到,里面是一个金黄色的直角三角体,左右移动均能明显看到三角体的左右两侧,随着视野而变化。上下角度的改变也会出现以绿色为主渐变效果,当然我更愿意相信是光源在介质中折射时碰到临界值处失真的表现。
IMG_3665
  第三幅是镇馆之宝了,造价也是不菲。两个三角形呈一定角度,再用一个三角形从中间插入,最重要的是这些都是立体的,向外凸出,仿佛就在眼前,裸眼3D来着。在头顶的一个渐变光照射下格外清晰,立体感非常强烈,可惜照片没办法捕捉。
IMG_3664
  目前看到的全息作品都是一些简单的几何图形,要是有更为复杂的自然场景那会是如何?或者将摄影照片全息化又会是如何?不可思议,妙不可言,期待。剩下几个作品就不做介绍了,叙述的故事大过于表现形势,一言难尽。

一些扩展连接:
http://www.seaworldchina.com/news.aspx?id=10000611&page=7
http://jamesturrell.com/work/type/holograms/
https://zh.wikipedia.org/wiki/%E5%85%A8%E6%81%AF%E6%91%84%E5%BD%B1

Sony Xperia™ Z5 Premium 扶不起的安卓

  有点标题党了,之所以想到“扶不起”大概是看到了太多老人假摔讹钱的新闻。确实安卓这个生态也是在野蛮生长,本以为旗舰机会好一点,却也是无用。

  就在几个月前的手机掉水之后,一直在寻找一款主力机,首选当然是Apple新机6s系列,但实在不喜欢凸出的摄像头,于是不再作为首选考虑。期间了解到 Sony Xperia™ Z5 Premium Dual 国行会在10月底预售开启,素有机皇之称。了解了硬件后都还比较满意,尤其防水功能更是实用,于是第一时间订购。

  满怀期待的等到了11月10号统一发货,第二天下午便收到了,简单说下优缺点:说实话4K屏却没那么的吸引人,大部分时候是渲染在1080p分辨率被拉升到4K,整个就一个朦胧美,一点都不精致。目前仅打开相册和视频会切换成4K,确实细节多了不少,非常惊艳,忍不住凑近仔细看。接下来是2300w像素的摄像头,或许也是期待过于高了点,官方宣传夜景效果好,正好当天晚上在欢乐海岸和努比亚团队在拍摄光绘,着重试了下拍夜景的效果,依旧很失望。自带的相机下始终没能达到ISO 12800,人像黑乎乎的,平均ISO仅1000左右,或许是为了减少噪点吧。实体的拍摄键还是很好用的,可以一只手端着横排。商场内暖光场合依旧有白平衡不稳,画面时冷时热。自带的相机默认并没有选择最大像素,开始不知为何,后来才知道,4K屏下渲染一张全尺寸的照片竟然要两次才能完成,而且是纵向间隔,不仔细看看不出,就当是卡个一秒样子。

  以上还不是最糟糕的,糟糕的还是在安卓系统上,众所周知的原因,国行系统是没有谷歌服务框架 Google Services Framework 的,这意味着没有一个可靠的App下载超市,仅有一个“索尼精选”的超市,内容少不说,大部分内容还是百度提供的,搞不好给你来个百度全家桶也不是不可能。所以以前下载App的习惯是去官网下载,现在很多都不提供apk下载,仅留一个Google Play的链接。由于国内第三方超市极其强势,几乎都是定制版,比如下载新浪微博,官网的下载链接就是360的服务器,而且启动界面也打上了360 Logo,一些常用App权限项也是多的惊人,虽然国行系统带了一个应用权限管理,但似乎无效,比如关闭了支付宝的地理位置访问,会导致无法启动,而关闭联系人却依然能取到。更有甚者如大众点评在安装后,应用权限管理都崩溃了,更别说拦截。实在是难以理解。而我在iPhone玩的部落战争也由于没有GSF,无法转移到这台手机上来。

  这个手机充电也是个大麻烦,公司台式机和Macbook的USB 3.0都无法满足充电需求,只能用自带的充电头冲,这个充电头仅仅输出1.5A,边冲边玩也是不行的,几次看到充满还需要1天之久的提示,也是醉了。官方送的抗蓝光贴膜和蓝牙运动耳机以及有线耳机就不吐槽了,简直就想直接丢掉。

  这还没结束,我手头这台手机居然还掉信号,自从收到手机打的三个超过5分钟的都断掉过,或者是卡上几秒没有声音。之前有朋友提醒过Sony手机向来信号不好,尤其是水货。看来国行也不过如此,但希望这只是个例。

  说完了缺点也说下优点:1. 侧面的指纹识别还是很好用的,比较顺手,口袋里拿出来握姿就能解锁,识别速度和准确度都还不错。2. 双击屏幕点亮也是很不错的设计,方面在上班时候放在桌子上,看消息,免去了拿起手机按解锁键。3. 背面的镜面设计也很好看,我选的是黑色版,整个后背给人感觉特别正式,气派,市面上也很少有雷同设计。4. 立体声外放绝对好评,横过来听歌,看视频超级享受。5. 独立快门按钮,对焦快,白天拍摄画质不错。

  总之,依然是缺点多余优点的一款产品,仅仅在我手上用了一天,现在已经联系售后在走退货流程了,实在没有想到安卓阵营发展到5.1了居然还是这个样子。大法的黑科技如何牛叉,硬件工艺如何先进,却栽倒在安卓这个烂系统上。

iPhone 5 进水换电池纪录

  国庆期间很不幸的把手机掉水盆里了,完完整整的泡了几秒钟。赶紧关机后置于米袋中大半天后,发现屏幕里面还是有水迹,随后拿到就近的手机维修店开机烘烤了个把小时,总算把水迹弄干了,没过多检查就先拿回家了。

  用了一段时间后发现电池出现了异常,电量指示的百分比在开机后就不会再变了,每次都是开机时的电量,且在电量低于40%左右,会频繁的重启,几乎无法使用。于是想到用iTools的“电池专家”来看看有无头绪,果然已经检测不到电池容量了,电压,温度,充放电循环次数都无法显示,仅剩一个序列号。出于职业习惯,打开了系统实时日志,发现大量的Error是在获取电池信息,具体为:

CLTM[23] <Error>: CLTM: Could not get event from service (gas gauge battery)
CLTM[23] <Error>: CLTM: Could net get value for gas gauge battery
...

  至此有七成把握是电池上的芯片出问题了(这块的电路无法完全断电),当然也有可能是主板的电源接口附近的某个部件短路而损坏,但几率小得多。这也是为什么一定要尽快关机的原因,如果能直接扣电池则最好。更具体来讲可以参考这篇博客,详细解释了iPhone电池的四个连接点用途。可见正是因为第三根SWI信号线失去了作用,导致的报错。

  后来在万能的淘宝上购买了一块副产电池,抱着试一试的心态,发现果然正常了,看来这台iPhone 5还能多用一段时间。之所以没有选择原厂Sony电池是因为毕竟机器已经停产很久了,即便有也是放置了很久的,还不如副产出产较新的好点。换电池过程比较简单,卸下底部的两个螺丝,用吸盘吸住底部屏幕,用点力气就可以将屏幕抠出来。这里要注意顶部的排线,屏幕抬起角度不要超过90度。实际上iPhone 6以后都采取了防水设计,可以有效的阻止水侵入内部。

为花图相册开启全站https

是否为花图相册开启全站https其实考虑已久:一来是发现目前越来越多的网站都走向了https,即便不是电子商务的网站,例如百度。二是服务器计算能力越来越强大,似乎也不必要在乎加解密,握手所消耗的这点资源。三是出于程序员思维,好东西自然要折腾一番,且不说在Chrome地址栏中绿色的scheme是多么的诱人。

1. SSL证书分类

SSL证书大致可以分为Class 1~5这五种,级别依次递增,数字越高,信用级别越高,自然收费也更高。
Class 1(DV),只验证域名真实性,即保证所访问的域名是真实有效的,这个级别的证书申请也是最简单的,只需提供域名管理者邮箱或者以域名为后缀的几个特定邮箱即可。通常用于个人或邮件。
Class 2(IV)验证域名和个人身份证明。
Class 3(OV)验证域名和组织机构信息。
Class 4(EV)电子商务,网上交易等。
Class 5私有组织或政府部门,比较少见。

这里虽然有等级之分,其实也就是准入门槛越来越高而已,增加了造假难度,但底层加密方式可以都是一样的。

2. 证书申请

这里不得不提到两家免费证书提供商,国内的Wosign和国外的StartSSL,这两家都有试用过,这里简单记录下使用过程:

Wosign的免费证书申请地址隐藏比较深,官网拉倒最下面才有个注册入口。由于是国内的,注册就容易多了,基本上顺着向导很容易就能拿到证书,而且提供的证书非常全面,涵盖了常用的几个服务器,包括Apache,Nginx,Tomcat等,可以说非常便捷。

StartSSL就麻烦多了,首先注册时候填写的资料必须是个人的真实的,或者说看起来要真实。其二需要人工审核,一般也很快,十分钟左右就能收到邮件。而这家最该死的设计就是以后的登录必须靠首次登陆所安装的证书来识别,因此需要备份好证书。可惜的是我这里Chrome下首次安装失败了,建议用IE。意味着我只要退出登录了就再也进不来了。不过并不妨碍继续申请域名证书,首先是验证域名,再申请证书,得到的私钥需要用openssl解密,或者在toolbox中在线解密。然后再下载他们家的sub class1证书附加到域名证书中,才可以正常使用。

在实际测试中,Wosign的免费证书顶级CA居然是StartSSL签发的,Wosign作为一个二级CA,有点奇怪,证书链比StartSSL多一层,理论上验证也要花费更多时间。

3. Nginx服务器配置

主要是增加443端口监听,别忘了在防火墙上也要允许通过443端口。开启ssl,很简单。ssl_ciphers可能还需要优化,目前来讲RC4算法已经不太安全了,可以禁用。由于加密所需要的握手次数和时间都增加不少,因此很有必要调节下ssl_session的缓存大小和超时时间。官方资料是1m cache大概能存储4000个会话,timeout 10m是十分钟。部分代码如下:

listen       80;
listen       443 ssl;
server_name  snapast.com www.snapast.com;

ssl			on;
ssl_certificate		/etc/nginx/ssl/ssl.crt;
ssl_certificate_key	/etc/nginx/ssl/ssl.key;
ssl_protocols		TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 		EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers	on;
ssl_session_cache	shared:SSL:10m;
ssl_session_timeout	10m;

开启后,还需要将非https请求从定向到https,这里我做了两步处理:首先host不等于snapast.com的转为snapast.com,即将带www的转为不带www。scheme不是https的转到带https,同时带上上下文和参数。

if ($host != 'snapast.com') {
rewrite ^/(.*)$ https://snapast.com/$1 permanent;
}

if ($scheme != "https") {
rewrite ^/(.*)$ https://snapast.com/$1 permanent;
}

4. 其他说明

Tomcat也可以开启http所访问,但由于都是内网,因此没必要再开启https了,但需要注意的是,在Tomcat看来还是http的请求,所以通过request.getScheme()得到的依然是http,凡有用到的需要注意一下,容易出现在拼接basePath的地方。

出于安全规范,全站https后,若有脚本等资源还是http引入的浏览器都会拒绝加载,而我之前用的51.la统计工具便不支持https,试过腾讯统计也不支持,目前发现百度统计可以用,因为他的脚本没有限定scheme。

到此就算完成了,欢迎访问花图相册看效果:https://snapast.com:cool:

使用Java控制路由器获取公网IP

  不知道是公网IP不够用了,还是什么鬼原因,近期我这的联通ADSL拨号很大程度上获取的是一个10.开头的内网IP。虽说通常情况下无需关心,但跑PT,VPN等速度上大打折扣。投诉无果后只能自己写个脚本来自动更换IP。

  其原理很简单,模拟登录到路由器上检查WANIP是否是10.或0.开头,如是则断开重连,以此循环。代码是Java编写,无任何依赖,运行在树莓派上,24小时监视,在运营商完全分配内网IP之前还可以挣扎一阵子。有需要的朋友可以参考下。

  我这用的是TP-LINK WR720N路由器,设置了局域网IP为192.168.30.1 端口88,通过Chrome登录到路由器,可以在开发者工具中查看到Basic加密的Key,替换相应的位置即可。

查询IP的链接

http://192.168.30.1:88/userRpm/StatusRpm.htm

断开拨号的链接

http://192.168.30.1:88/userRpm/StatusRpm.htm?Disconnect=%B6%CF%20%CF%DF&wan=1

重新拨号的链接

http://192.168.30.1:88/userRpm/StatusRpm.htm?Connect=%C1%AC%20%BD%D3&wan=1

代码如下:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CheckIP {
	private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
			"yyyy-MM-dd HH:mm:ss");

	public static void main(String[] args) throws Exception {
		do {
			String currentIP = getIP();
			if (currentIP.startsWith("10.") || currentIP.startsWith("0.")) {
				System.out.println(simpleDateFormat.format(new Date())
						+ " 检测到异常:" + currentIP);
				getHtml("http://192.168.30.1:88/userRpm/StatusRpm.htm?Disconnect=%B6%CF%20%CF%DF&wan=1");
				Thread.sleep(1000 * 1);
				getHtml("http://192.168.30.1:88/userRpm/StatusRpm.htm?Connect=%C1%AC%20%BD%D3&wan=1");
				Thread.sleep(1000 * 3);
			}
			Thread.sleep(1000 * 3);
		} while (true);
	}

	private static String getIP() throws Exception {
		String wanPara = getHtml("http://192.168.30.1:88/userRpm/StatusRpm.htm");
		if (null != wanPara) {
			wanPara = wanPara.substring(wanPara.indexOf("var wanPara"),
					wanPara.length());
			wanPara = wanPara.substring(0, wanPara.indexOf(");") + 2);
		}
		return getFirstIp(wanPara);
	}

	private static String getHtml(String address) throws Exception {
		URL url = new URL(address);
		URLConnection connection = url.openConnection();
		connection.setRequestProperty("Authorization", "Basic YWRtaW46d3Npa3Nr");
		connection.connect();
		InputStream inputStream = null;
		StringBuffer stringBuffer = new StringBuffer();
		inputStream = connection.getInputStream();
		BufferedReader bufferedReader = new BufferedReader(
				new InputStreamReader(inputStream));
		String line;
		while ((line = bufferedReader.readLine()) != null) {
			stringBuffer.append(line);
		}
		bufferedReader.close();
		inputStream.close();
		return stringBuffer.toString();
	}

	private static String getFirstIp(String packet) {
		Pattern p = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+");
		Matcher m = p.matcher(packet);
		if (m.find()) {
			return m.group();
		} else {
			return null;
		}
	}
}

编译

pi@raspberrypi ~ $ javac CheckIP.java

后台运行

pi@raspberrypi ~ $ nohup java CheckIP &

日志

pi@raspberrypi ~ $ tail -f nohup.out