在手机上绘制根轨迹(2)

另一种方式实现手机上绘制根轨迹。并且在手机上绘制螺旋桨性能曲线。


在手机上绘制根轨迹

上一次写的easyAuto,好倒是挺好的,但是有一个问题:仅仅有安卓的版本,没有iOS版本。这样一来,有很多用户无法得到方便。因此,要么自己买一套iOS的设备,要么换一种方式:网页实现。

网页绘制的好处有很多,可以实现真正的跨平台,每一次代码的更新用户并不需要任何的升级这些,因此好处太多了。没能开发iOS程序一直是我的遗憾,这样也算是一种弥补。现在的前端技术很火,bootcdn上面一大堆js库,都很有意思,没事浏览浏览上面的目录,说不定能发现宝贝。

在上次的文章里我们详细的讨论了算法,又是多项式又是矩阵什么的,比较麻烦。网上找到的轮子有

  • math.js库,比较强大,但是矩阵不能求特征值
  • numerical.js库,很久没有更新了,矩阵能够求特征值,还有样条插值函数
  • polynomial.js,比较简陋,就是用来表示多项式的 即使有这些低层次的轮子,要移植我曾经的一大堆专门的数值代码也是非常麻烦的。手写就不说了,自己想想都觉得多。我在网上看到了几种方案:
  • emscripten-qt直接移植Qt程序,这个项目已经没有维护了,不可能能移植QtCharts模块
  • Qt+WebAssembly,看了几个demo,效果很差
  • 在网页里面直接运行apk,也没有看见理想的方案
  • 用emscripten移植数值算法的代码,但是自己本来就完全不会javascript,这样自己也会更麻烦

那也没有实现的办法呢?我想到了WolframAlpha,试了试,可以画根轨迹,当然这个问题可以结束了,所有手机要想画根轨迹直接访问WolframAlpha不就完了?!但是还是要自己写程序,看了看有没有WolframAlpha的API,还真有,那就好说。通过API访问https://api.wolframalpha.com/v2/query?input=root+locus+for+transfer+function+(s%5E2%2B3)%2F(s%5E3%2B5)&format=image&output=JSON&appid=DEMO可以得到一个这样的json:

{
    "queryresult": {
        "success": true,
        "error": false,
        "numpods": 2,
        "datatypes": "ControlSystem",
        "timedout": "",
        "timedoutpods": "",
        "timing": 2.244,
        "parsetiming": 1.397,
        "parsetimedout": false,
        "recalculate": "",
        "id": "MSPa3194185g424ef9ic5i8b000031903e0de369bii3",
        "host": "http://www4d.wolframalpha.com",
        "server": "15",
        "related": "http://www4d.wolframalpha.com/api/v2/relatedQueries.jsp?id=MSPa3195185g424ef9ic5i8b00002hd488g9fggigd5f3827527191664667052",
        "version": "2.6",
        "pods": [
            {
                "title": "Input interpretation",
                "scanner": "Identity",
                "id": "Input",
                "position": 100,
                "error": false,
                "numsubpods": 1,
                "subpods": [
                    {
                        "title": "",
                        "img": {
                            "src": "http://www4d.wolframalpha.com/Calculate/MSP/MSP3196185g424ef9ic5i8b00000e5afg304gfd51fb?MSPStoreType=image/gif&s=15",
                            "alt": "root locus plot | transfer function  (3 + s^2)/(5 + s^3)",
                            "title": "root locus plot | transfer function  (3 + s^2)/(5 + s^3)",
                            "width": 314,
                            "height": 55
                        }
                    }
                ]
            },
            {
                "title": "Root locus plot",
                "scanner": "ControlSystems",
                "id": "RootLocusPlot",
                "position": 200,
                "error": false,
                "numsubpods": 1,
                "subpods": [
                    {
                        "title": "",
                        "img": {
                            "src": "http://www4d.wolframalpha.com/Calculate/MSP/MSP3197185g424ef9ic5i8b0000445b174i6b48h4ea?MSPStoreType=image/gif&s=15",
                            "alt": "\n(shown for gain between 0 and 10)",
                            "title": "\n(shown for gain between 0 and 10)",
                            "width": 300,
                            "height": 318
                        }
                    }
                ],
                "states": [
                    {
                        "name": "Increase range",
                        "input": "RootLocusPlot__Increase range"
                    },
                    {
                        "name": "Decrease range",
                        "input": "RootLocusPlot__Decrease range"
                    }
                ]
            }
        ]
    }
}

这样只用通过javascript进行http的get请求就能行了。理论很美满,显示很骨感,试了大半天,总是会遇到Access-Control-Allow-Origin的问题。上谷歌去查各种方案,都说WolframAlpha的API不能通过javascript访问,要么用服务端代码,要么用proxy,好像没有解决方案。但是,在网上查javascript跨域的问题,也是得到了一大堆奇奇怪怪的解决方法,我最终实现了跨域访问:

var str1 = document.getElementById("num").value;
var str2 = document.getElementById("den").value;
var querystr = "https://api.wolframalpha.com/v2/query?input=root+locus+for+transfer+function+(" +
    encodeURIComponent(str1) + encodeURIComponent(")/(") + encodeURIComponent(str2) +
    ")&format=image&output=JSON&appid=XXXX&callback=?";
$.ajax({ 
  type: 'GET', 
  url: querystr , 
  //是否使用缓存 
  cache:false, 
  //数据类型,这里我用的是json 
  dataType: "json", 
  crossDomain: true, 
  //必要的时候需要用JSON.stringify() 将JSON对象转换成字符串 
  //data: JSON.strigify({key:value}), 
  //data : ""
  //请求成功的回调函数 success: 
  success : function(data){  
    document.getElementById("res1").src = data.queryresult.pods[0].subpods[0].img.src;
    document.getElementById("res2").src = data.queryresult.pods[1].subpods[0].img.src;
  }
});

不知道实现了跨域访问的原因到底是在URL里面加了”&callback=?”还是设置了crossDomain为true的原因:cold_sweat:。这样就实现了在网页上绘制根轨迹。不过这个网页的使用体验肯定没有安卓版的easyAuto好,一个是输入格式更为严格,第二个是速度太慢。就这个访问速度,我相信即使是javascript代码也能做到更快把图画出来,不过我是真的不想再去用javascript写数值的代码了。

螺旋桨性能查询网页

做这个的原因和上一个一样,想实现彻底的跨平台。之前把demoProp的所有数据全部导入到了一个sqlite文件中,加上索引一共40+MB,正好在网上看到了sql.js项目,这个是emscripten把sqlite直接翻译过来的结果,想试一试,于是把数据库和网页挂在网上,试了试发现访问速度太慢了。而且一不注意,手机用了200+MB的流量,血淋淋的教训啊。我明白这个的意思了:用http请求把url的数据库先下载下来,然后再在内存中运行sql语句。这个代价相比PHP+MySQL的代价的确太大了。我把索引全部删除,数据库的大小还是20+MB,没办法,想了想一般在无人机我们关注的是静拉力,于是删除了所有动拉力数据,仅保留静拉力数据,这下只有600+KB了。再把一些无关的字段删去,sqlite对sql的支持太差,删除字段还需要这样写:

create table temp as select propName,RPM,PWR,Thrust from propellers;  
drop table propellers;  
alter table temp rename to propellers;  

这样数据库只剩300KB了。当然,也可以把所有数据以文本形式保存在网站上,具体对于某个螺旋桨,只需要读取具体的一个数据就行,不过处理文件肯定没有sql语句来得方便。

关于图表的绘制,我使用的是ECharts3.而UI的绘制则是使用jQuery Mobile,都是直接挂的bootcdn,希望服务器不要宕机:stuck_out_tongue_winking_eye:。jQuery Mobile的空间要想更好地适应移动端,需要加上如下代码:

<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">

最后放上链接: 在线绘制根轨迹 在线查询螺旋桨数据