Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

超声波传输(一)——集成QuietModemKit #91

Open
soapgu opened this issue Dec 14, 2021 · 0 comments
Open

超声波传输(一)——集成QuietModemKit #91

soapgu opened this issue Dec 14, 2021 · 0 comments
Labels
Demo Demo objctive c Improvements or additions to documentation

Comments

@soapgu
Copy link
Owner

soapgu commented Dec 14, 2021

  • QuietModemKit简介

    QuietModemKit是一个可以通过调整可听声或者近似超声波传输数据,可以编码解码数据的针对IOS平台的第三方库。内核库quiet是使用C编写的,原则上套上不同的“壳”,可以在各个平台原型。
    通过可听声传播信息历史以及非常悠久。用超声波传输信息,一听就很fashion(zhuangbi)
    我第一反应是这货😄
    图片
    这货😂
    图片

  • 基础原理

言归正传,输入流程为

  1. 输入信息(字符串)
  2. DSP编码,通过扬声器发出
  3. 通过麦克风拾音,
  4. DSP解码,还原信息

经过DSP编码后的一个frame信号
图片

具体深层次原理就有点“深水炸弹”了。
发送一个超声波正弦波并不难,但是要能编解码这真的有些复杂

前面一直学习的swift,对Objective C没有一点概念。而这个库是用的Objective C,使用oc语言去写对接起来更便利一点。如果用swift去写还有一个适配过程,和C#调用VC一样。
反正Objective C早晚要碰的,不如先会会。

图片

语言有点差异,但是结构至少是一样的,storyboard 和ViewController都有,就是有了.m 和 .h 两个文件分开了,先不管,反正我要实现的功能不复杂。
  • 初识Carthage

Carthage是一个类似CocoaPods的依赖管理工具,github上推荐用Carthage集成

  1. 首先安装Carthage,官网上有pkg,release
  2. 创建文件名Cartfile,并输入内容 github "Quiet/QuietModemKit"
  3. QuietModemKit本身需要安装cmake,我头铁后面就报错了。具体操作看资料
  1. 运行 carthage update --use-xcframeworks
    漫长的等待,注意看好控制台,不要有出错信息
  2. 拖QuietModemKit.xcframework从Carthage/Build 到 XCode的 Targets -> Project-> General-> "Frameworks and Libraries"

问题点:
由于carthage默认拉代码的方式是https,而https方式拉github不太容易成功,即使翻墙都没啥用~~~
经常出错信息 fatal: unable to access 'https://github.com。
解决方式:配置ssh 的key后改成默认ssh方式拉取github代码,控制台执行 git config --global url.git://github.com/.insteadOf https://github.com/,注意这代码有副作用,用好以后最好改回来!

基本上玩过CocoaPods以后觉得差不多

  • 集成QuietModemKit

github上只有控制台实现要iOS上实现还要改写一点。

只需import就可以使用相关库
#import <QuietModemKit/QuietModemKit.h>
StoryBoard的IDE还是一样,区别就是
Action需要往 @implementation 的代码块里面拖
IBOutlet 需要往@interface代码块拖

  1. 编码调解并发声音实现
- (IBAction)ButtonClick:(id)sender {
    
    QMTransmitterConfig *txConf = [[QMTransmitterConfig alloc] initWithKey:self.labelSelect.text];

    QMFrameTransmitter *tx = [[QMFrameTransmitter alloc] initWithConfig:txConf];

    NSString *frame_str =  self.txtSend.text; //@"Hello, World!";
    NSData *frame = [frame_str dataUsingEncoding:NSUTF8StringEncoding];
    [tx send:frame];
    //CFRunLoopRun();
    [tx close];
    self.label.text = [NSString stringWithFormat:@"send message:%@", frame_str ];
}
  1. 接收声音并解码实现
    录音需要app声明权限,否则运行会报错

图片

需要设置info.plist,具体操作如截图 和安卓的manifest.xml的用法相似,只是觉得iOS的info.plist更有年代感

下面是接受音乐解码实现

static QMFrameReceiver *rx;
NSString *current =@"audible";
NSString *receive =@"";

- (IBAction)recordClick:(id)sender {
    [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted){
        QMReceiverConfig *rxConf = [[QMReceiverConfig alloc] initWithKey:current];
        if( rx != nil ){
            [rx close];
        }
        rx = [[QMFrameReceiver alloc] initWithConfig:rxConf];
        [rx setReceiveCallback:^(NSData *frame){
            receive =  [[NSString alloc] initWithData:frame encoding:NSUTF8StringEncoding];
            //printf("%s\n", [frame bytes]);
            NSLog(@"收到:%@", receive);
            self.label.text = [NSString stringWithFormat:@"receive:%@", receive ];
        }];
    }];
}

这里的requestRecordPermission和setReceiveCallback里面写了两个内嵌callback。
不能完全抄demo例子,否则不能访问当前类的成员。

  1. picker方式选择编解码类型
    为了能让app随时切换编码。
@interface ViewController ()<UIPickerViewDataSource,UIPickerViewDelegate>

NSArray *profileArray;

- (void)viewDidLoad {
    [super viewDidLoad];
    profileArray = [NSArray arrayWithObjects:@"audible",@"ultrasonic-fsk",@"ultrasonic-fsk-fast", nil];
    // Do any additional setup after loading the view.
}

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return profileArray.count;
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
    return [profileArray objectAtIndex:row];
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
    self.labelSelect.text = [profileArray objectAtIndex:row];
    current = self.labelSelect.text;
}

需要在ide里设置dataSource和delegate
图片

dataSource和delegate需要实现UIPickerViewDataSource,UIPickerViewDelegate 两个protocol,这个类型Swift里面也有
实现4个方法

参考

  1. 定时发送声音实现
    UIKit里面的Objctive C里Timer实现叫NSTimer,定期执行代码和前面录音的方式差不多。有点类似闭包
NSTimer *timer;

- (IBAction)repeatSend:(id)sender {
    if( timer != nil && [timer isValid ] ){
        [timer invalidate];
        [self.repeatButton setTitle:@"Repeat Send" forState:UIControlStateNormal];
    }
    else{
        [self.repeatButton setTitle:@"Repeat Stop" forState:UIControlStateNormal];
        timer = [NSTimer scheduledTimerWithTimeInterval:3 repeats:YES block:^(NSTimer * _Nonnull timer) {
            QMTransmitterConfig *txConf = [[QMTransmitterConfig alloc] initWithKey:self.labelSelect.text];

            QMFrameTransmitter *tx = [[QMFrameTransmitter alloc] initWithConfig:txConf];

            NSString *frame_str =  self.txtSend.text; //@"Hello, World!";
            NSData *frame = [frame_str dataUsingEncoding:NSUTF8StringEncoding];
            [tx send:frame];
            [tx close];
        }]; 
    }
}

实现了3秒发送一次声音;
可以停止循环,重新开始;
修改按钮名称

注意:
原来

self.repeatButton.titleLabel.text = "XXXXXX";`

执行没有任何效果(也不报错)无力吐槽
一定要

[self.repeatButton setTitle:@"Repeat Send" forState:UIControlStateNormal];

完工效果图
图片

Carthage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Demo Demo objctive c Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

1 participant