2019年1月29日星期二

ICOM IC-R30使用心得

最初见到ICOM IC-R30是在日本业余无线电盛会HamFair 2017的现场图片中。我一直对这种便携式的宽频接收机情有独钟,所以当时心中就种了草。但不知道多久才能上市,也不知道多久国内能购买到,也就渐渐忘记这件事了。直到2018年的一天,我在电波之旅微信群中见到有朋友晒了这个接收机的图片,淘宝上一查,国内竟然可以买到了,但是价格很贵,我想等过些年降价了再说。没想到双十二的时候,北京某代理商降价900多元,还赠送礼品,感觉这个机会不错,于是出手买了一台。

这类对讲机大小的宽频接收机,我之前已经有了八重洲FT-1D和建武TH-F6A.这类产品受制于射频器件小型化的限制,接收性能上不会有惊人的表现,尽管他们出身于世界顶级的无线电设备厂商且身价不菲,因此我对IC-R30也没有抱太高的期望。事实证明的确如此。就拿最普通的FM广播接收来说,可以说在收音机中也只能算中等偏下的水平,尽管IC-R30是三次变频。在北京西三环公主坟附近接收灶君庙发射塔发射的100.6MHz, 会受到玉渊潭中央电视塔发射的99.6MHz的干扰,八重洲FT-1D也存在这个问题,而大多数收音机都不受这个影响。而在林萃桥南侧,恰好101.8MHz的信号比较弱,IC-R30甚至听不到声音,而其他很多收音机噪声较大,但可以接收到声音。此外本机FM接收不支持立体声,也不支持RDS. 中波也表现不佳,主要是底噪偏大,我都没怎么听。短波AM噪声很小,声音清晰,但我把玩时间不长,尚未进行DX实力的对比。430MHz业余无线电通联接收效果感觉和对讲机没什么差别。模拟电视伴音感觉有些模糊不清,需要聚精会神才能听清里面说什么,特别是刚刚从别的频率转过来,这种感觉特别明显。短波单边带、数字模式、航空波段受限于精力和条件,尚未体验。该机的GPS是一大亮点。冷启动搜星,大约一分钟左右就可以定位到准确的经纬度。在没有任何网络辅助手段的情况下,这个速度已经很快了,而且在室内距离窗口1米左右的位置也可以搜星定位,在我用过的GPS里算是很出色的了。
该机的一大特点是支持的频段超宽,从100kHz~3.3GHz,在业余宽频接收机中据我所知是最宽的。然而,500MHz频段以上除了模拟电视伴音,其实就没有什么可听的了。频率下限100kHz, 与大多数宽频接收机和多波段收音机一致。我一直想听日本的长波授时台并获取QSL卡,该机仍无法覆盖。如此宽的频率范围,其实还是用户心里的感受更好,实际并无太大价值。
该机的另一大特点是支持几乎所有模拟解调制式和多种数字制式。然而这些数字制式主要是ICOM公司主推的制式和日本的数字广播制式,并不支持世界范围广泛使用的数字制式APRS和DRM、竞争对手主推的C4FM、欧洲广泛使用的DAB数字广播等,在我国并没有太大用途。
该机的操作界面为英文、日语两种语言,说明书是纯英文的,随发货带一本简单的说明书,厂商官网还有一本电子版的详细的说明书供用户下载阅读。国内有些无线电爱好者英语水平有限,使用和阅读起来可能比较吃力。
综上所述,该机从实用角度看,并无太多有价值的亮点。然而,该机有很多非常人性化的设计,值得其他厂商,包括收音机厂商学习。这里一一列举下。
先说天线。我常用的收音机在听FM广播时有拉杆天线和耳机线作天线两种方式。使用拉杆天线的收音机,出门携带特别是乘坐公交车收听时就很不方便。不把天线拉出来信号不好,拉出天线来又怕扎了周围的人,也怕天线折断,还怕周围人的目光。IC-R30有个菜单项,可以选择使用拉杆天线或者耳机线作天线。按屏幕显示,1.3GHz以下都可以使用耳机线作为天线。这样就解决了我说的这个矛盾。方便的时候使用拉杆天线,追求最好的信号;不方便的时候切换成耳机线,避免了拉杆天线的不方便。从我个人的使用感受上看,在听FM广播时,选择哪种天线都没有感觉到性能的差异。其他手持宽频接收机,一般都是附带手台那种橡胶天线,这种天线接收U/VHF段性能还过得去,但不适合短波接收。很多爱好者都要自己配置拉杆天线用于短波接收。而本机直接附带了一根可以弯折360度的三段式拉杆天线,这样就不需要用户另行配备拉杆天线了。平时不用的时候,可以把天线弯折到机身背面方便携带,不用把天线拧下来,保护了SMA接口。
手台上的频率和音量调节令厂商们纠结。有些手台上保留了一个调节频率的旋扭,把音量改为侧按键,因此被用户诟病;有些手台上保留了两个旋扭,比较占地方,并且占用了GPS天线的黄金位置;有些手台上用双层旋扭分别调节音量和频率,可能也有它的缺点。IC-R30用了一种全新的设计方式。它在机身顶部保留了一个旋扭,侧按键保留了两个按键,然后用键盘上的一个键切换它们的功能。默认是顶部调频率,侧面调音量;按下键盘上的切换键,就变成顶部调音量,侧面调频率,较好的解决了这个难以兼顾的问题。值得一提的是,顶部的那个旋扭是金属的,手感非常好,尽管此处用塑料旋扭也完全可以实现相同的功能。由此可见厂家在细节上的用心。
该机的功耗远高于收音机,只打开单段接收,关闭GPS和蓝牙,收听一小时大约消耗10%的电量。该机发货附送了充电器和充电底座。用充电器充电的话,要放在底座上才能充。不过,本机可以使用USB接口进行充电(接口为Micro USB)。这样就方便多了,各种移动电源、车载充电器、太阳能充电器、笔记本电脑都可以给它充电,去野外使用再也不用担心电池不够用, 家里淘汰下来的手机充电器也派上了用场。说明书上特别强调了充电器的输出电流和USB线的负荷。实测用550mA电流输出的充电器,给几乎为空的电池充电。午夜12点充电开始,第二天早上睡醒的时候,电池早已充满。
该机带有通用性最强的3.5mm耳机接口,但不方便的是,立体声耳机插上去只有一个声道有声音。我很讨厌这个问题,我觉得一个耳朵响一个耳朵不响听起来不如两个耳朵都响舒服,即使没有立体声效果。八重洲、索尼(特别是面向日本市场的产品)的很多产品也都是这样设计的,有的朋友说可能是日本法律的强制规定,这点暂未得到证实。菜单中有个耳机模式,打开后,整体音量减小了,低音提高了,但仍然只有一个声道有声音。好在我之前做过一个转接板,可以把一个声道扩展到两个声道,解决了这个问题。IC-R30还支持蓝牙耳机,无需额外花钱购置昂贵的蓝牙模块。考虑到现在蓝牙耳机、蓝牙音箱都很普及,这也是一个方便用户的功能。这个功能我还没有尝试。
该机短暂按下开关机键,会有一个女声播报当前正在收听的频率,当然也是英语。这个功能很新颖,但我暂时没有体会到实用价值。IC-R30可以插SD卡,除了记录配置参数、GPS位置信息等,还有录制接收音频的功能。很多BCL、HAM都有录音的习惯,有了这个功能,就不需要额外连接和携带录音设备了,大大方便了户外使用。
以上是我把玩一个多月来的体会和心得。总的来说,该机在频率范围、解调方式等方面,在目前的便携式业余接收机中是最全面的。同时,该机针对当前手持无线电设备的痛点,做了很多易用性的优化,可以感受到设计师的用心。就像不少高档电子产品用过以后一样,你会感受到设计师的心和你的心是相通的,他知道你需要什么,你不满什么,这种感觉妙不可言。当然,不要指望它的接收性能有惊人的领先。

2019年1月10日星期四

借用网上的Linux串口初始化代码要当心

入职新公司以来,我开发的几乎每个Linux程序都要用到串口。我开发第一个程序的时候,在网上阅读了大量的代码,并根据这些代码写了一个通用的初始化函数,以后的程序都调用这个函数进行初始化。在这期间,还发现了网上流传的代码存在的一个问题,详见《串口通信中的INPCK和ISTRIP标志位》。然而近期有人在使用我的程序时,还是发现了一个问题,所有流经我的程序的数据中的0x0D全部变成了0x0A.
网上一搜就知道了原因,原来是我初始化的时候没有关闭ICRNL标志位。我猜测Linux的串口架构沿用自Unix系统,那个年代串口主要用于远程登录和传输文本,设计上主要考虑的是方便人机交互;而现在在工业自动化领域,串口主要用来传输二进制数据,更希望数据准确传输。
这个问题也说明,网上找的代码,一定要自己研究明白再据为己有。现在有很多软件开发人员,随口就是去Github上搜代码,这样做未必是好事。
如下是我的初始化函数,已经修改了我上面提到的0x0D变成0x0A的问题,但不知道是否还隐藏着其他问题。供大家参考。
/*初始化串口*/
int init_serial(unsigned char com_sn, unsigned char spd, unsigned char data_bit, 
    unsigned char parity, unsigned char stop_bit)
{
    int  com_fd;              /*串口fd*/
    char com_device_name[16]; /*串口设备名*/
struct termios options;
    
    sprintf(com_device_name, "/dev/ttyS%d", com_sn);
#ifdef RS485_NONBLOCK
    com_fd = open(com_device_name, O_RDWR | O_NOCTTY | O_NDELAY); 
#else
    com_fd = open(com_device_name, O_RDWR | O_NOCTTY);
#endif 
    if (com_fd < 0) 
    {
    printf("Open the serial port %d error!\n", com_sn);
        control_led(LED_ERR, ON);
        return -1;
    }

tcgetattr(com_fd, &options);

    /*设置串口波特率*/
speed_t speed = B0;
    switch (spd)
    {
        case 0x96:
            speed = B9600;
            break;
        case 0x38:
            speed = B38400;
            break;
    }
cfsetispeed(&options, speed);
cfsetospeed(&options, speed);

/*
* Enable the receiver and set local mode
*/
options.c_cflag |= (CLOCAL | CREAD);

switch (parity)
    {
    case 'N':
        options.c_cflag &= ~PARENB;    //关闭校验  
        options.c_iflag &= ~INPCK;
        break;

    case 'E':
    options.c_cflag |= PARENB;       //使能校验
        options.c_iflag |= INPCK;
        options.c_cflag &= ~PARODD;      //关闭奇校验,也就相当于打开了偶校验
        break;

    case 'O':
        options.c_cflag |= PARENB;       //使能校验
        options.c_iflag |= INPCK;
        options.c_cflag |= PARODD;
        break;

    default:
        /*do nothing*/;
    }
    
    if (stop_bit == 1)
    {
        options.c_cflag &= ~CSTOPB;      //关闭第二个校验位,也就相当于只打开一个校验位
}

    options.c_cflag &= ~CSIZE;    
    if (data_bit == 8)
    {
    options.c_cflag |= CS8;          //8位数据位
    }

/*
* Disable hardware flow control
*/
options.c_cflag &= ~CRTSCTS;

/*
* Choosing raw input
*/
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

/*
* Disable software flow control
*/
options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);

/*
* Choosing raw output
*/
options.c_oflag &= ~OPOST;

    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 1;

    tcsetattr(com_fd, TCSANOW, &options);

    return com_fd;
}