1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
/*
 * File: bessel_beam_gain.rs
 * Project: src
 * Created Date: 22/11/2019
 * Author: Shun Suzuki
 * -----
 * Last Modified: 22/05/2020
 * Modified By: Shun Suzuki (suzuki@hapis.k.u-tokyo.ac.jp)
 * -----
 * Copyright (c) 2019 Hapis Lab. All rights reserved.
 * -----
 * The following algorithm is originally developed by Keisuke Hasegawa et al.
 * K. Hasegawa, et al. "Electronically Steerable Ultrasound-Driven Long Narrow Air Stream," Applied Physics Letters, 111, 064104 (2017).
 *
 */

use autd_geometry::consts::NUM_TRANS_IN_UNIT;
use autd_geometry::Geometry;
use autd_geometry::Vector3;

use crate::consts::ULTRASOUND_WAVELENGTH;
use crate::convert_to_pwm_params;
use crate::Gain;

/// Gain to produce Bessel Beam
pub struct BesselBeamGain {
    point: Vector3,
    dir: Vector3,
    theta_z: f64,
    amp: u8,
    data: Option<Vec<u8>>,
}

impl BesselBeamGain {
    /// Generate BesselBeamGain.
    ///
    /// # Arguments
    ///
    /// * `point` - Start point of the beam.
    /// * `dir` - Direction of the beam.
    /// * `theta_z` - Angle between the conical wavefront of the beam and the direction.
    ///
    pub fn create(point: Vector3, dir: Vector3, theta_z: f64) -> Box<BesselBeamGain> {
        BesselBeamGain::create_with_amp(point, dir, theta_z, 0xff)
    }

    /// Generate BesselBeamGain.
    ///
    /// # Arguments
    ///
    /// * `point` - Start point of the beam.
    /// * `dir` - Direction of the beam.
    /// * `theta_z` - Angle between the conical wavefront of the beam and the direction.
    /// * `amp` - Amplitude of the beam.
    ///
    pub fn create_with_amp(
        point: Vector3,
        dir: Vector3,
        theta_z: f64,
        amp: u8,
    ) -> Box<BesselBeamGain> {
        Box::new(BesselBeamGain {
            point,
            dir,
            theta_z,
            amp,
            data: None,
        })
    }
}

impl Gain for BesselBeamGain {
    fn get_data(&self) -> &[u8] {
        assert!(self.data.is_some());
        match &self.data {
            Some(data) => data,
            None => panic!(),
        }
    }

    fn build(&mut self, geometry: &Geometry) {
        if self.data.is_some() {
            return;
        }

        let num_devices = geometry.num_devices();
        let num_trans = NUM_TRANS_IN_UNIT * num_devices;
        let mut data = Vec::with_capacity(num_trans * 2);

        let dir = self.dir.normalize();
        let v = Vector3::new(dir.y, -dir.x, 0.);
        let theta_w = v.norm().asin();
        let point = self.point;
        let theta_z = self.theta_z;
        for i in 0..num_trans {
            let trp = geometry.position(i);
            let r = trp - point;
            let xr = r.cross(&v);
            let r = r * theta_w.cos() + xr * theta_w.sin() + v * (v.dot(&r) * (1. - theta_w.cos()));
            let dist = theta_z.sin() * (r.x * r.x + r.y * r.y).sqrt() - theta_z.cos() * r.z;
            let phase = (dist % ULTRASOUND_WAVELENGTH) / ULTRASOUND_WAVELENGTH;
            let phase = (255.0 * (1.0 - phase)) as u8;
            let amp: u8 = self.amp;
            let (d, s) = convert_to_pwm_params(amp, phase);
            data.push(s);
            data.push(d);
        }
        self.data = Some(data);
    }
}