# Ping360 discrepancy between pingviewer and brping python

**URL:** <https://discuss.bluerobotics.com/t/ping360-discrepancy-between-pingviewer-and-brping-python/16156>\
**Category:** Blue Robotics Products\
**Tags:** ping360\
**Created:** [February 28, 2024, 11:51pm UTC](https://discuss.bluerobotics.com/t/ping360-discrepancy-between-pingviewer-and-brping-python/16156 "2024-02-28T23:51:24Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![axppxsky](https://avatars.discourse-cdn.com/v4/letter/a/d2c977/32.png) [@axppxsky](https://discuss.bluerobotics.com/u/axppxsky)\
**Post date:** [February 28, 2024, 11:51pm UTC](https://discuss.bluerobotics.com/t/ping360-discrepancy-between-pingviewer-and-brping-python/16156/1 "2024-02-28T23:51:25Z")

</div>

i am trying to make an equivalent of ping viewer in python, but the cloud looks kinda off  
pingviewer looks like this

 ![Screenshot from 2024-02-28 18-08-38](https://us1.discourse-cdn.com/flex019/uploads/bluerobotics/original/2X/d/dc382ed23f7731574cac59d7c66be914c2d768ae.jpeg)

code outcome looks like this

 ![Screenshot from 2024-02-28 19-37-41](https://us1.discourse-cdn.com/flex019/uploads/bluerobotics/original/2X/5/537b7cd20ca44eb2678881da2b39ad2d6821c0ae.jpeg)

any help to debug would be appreciated

```auto

# Connect to and initialize device
p = Ping360()
p.connect_serial('/dev/ttyUSB0', 2000000)
print(f'{p.initialize() = }')

# Set some settings
print(p.set_number_of_samples(500))
print(p.set_gain_setting(1))
print(p.set_transmit_duration(27))
print(p.set_transmit_frequency(750))

def meters_per_sample(ping_message, v_sound=1500):
    """ Returns the target distance per sample, in meters. 
    
    'ping_message' is the message being analysed.
    'v_sound' is the operating speed of sound [m/s]. Default 1500.

    """
    # sample_period is in 25ns increments
    # time of flight includes there and back, so divide by 2
    return v_sound * ping_message.sample_period * 12.5e-9

```

using this logic and filtering intensities\>200

---

<div class="post-metadata">

**Author:** ![SirJansen](https://avatars.discourse-cdn.com/v4/letter/s/f08c70/32.png) [@SirJansen](https://discuss.bluerobotics.com/u/SirJansen)\
**Post date:** [February 29, 2024, 3:04pm UTC](https://discuss.bluerobotics.com/t/ping360-discrepancy-between-pingviewer-and-brping-python/16156/2 "2024-02-29T15:04:48Z")

</div>

As far as I recall there’s a bit more logic that goes into configuration. You need to select a scanning distance then base your calculations off that. At minimum we have this in our driver, now you’ll have to modify it as this was taken out of a class, but the logic is sound. You’ll also have to configure the transducer to use these calculated parameters.

```auto

def calculate_sample_period(self):
        self.sample_period = int((2 * self.range) / (self.number_of_samples * self.speed_of_sound * 25*(10**-9)))
        
def calculate_transmit_duration(self):
        """
        Per firmware engineer:
            1. Starting point is TxPulse in usec = ((one-way range in metres) * 8000) / (Velocity of sound in metres per second)
            2. Then check that TxPulse is wide enough for currently selected sample interval in usec, i.e.,
                if TxPulse < (2.5 * sample interval) then TxPulse = (2.5 * sample interval)
            3. Perform limit checking
        """
        inital_transmit = round((self.range * 8000) / self.speed_of_sound)
        duration = max((2.5 * self.sample_period) / 1000, inital_transmit)
        sonar_min_duration = 5
        transmit_max = min(self.sample_period*(64*10**6), 500)
        self.transmit_duration = max(sonar_min_duration, min(duration, transmit_max))

```

Calculating your meters per sample should be as simple as dividing your range, in meters of course, by number of samples. I hope this helps!

---

<div class="post-metadata">

**Author:** ![EliotBR](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.bluerobotics.com/eliotbr/32/6937_2.png) [@EliotBR](https://discuss.bluerobotics.com/u/EliotBR)\
**Post date:** [March 4, 2024, 1:41am UTC](https://discuss.bluerobotics.com/t/ping360-discrepancy-between-pingviewer-and-brping-python/16156/3 "2024-03-04T01:41:53Z")

</div>

Hi @axppxsky,

I’ve previously written this code to visualise Ping360 scans (where each scan is a single image), which may be worth referring to:

```python
import numpy as np
import colorcet as cc
import matplotlib.pyplot as plt

def visualise_scan(ping_messages=None, title=None,
                   ax=None, show=True, v_sound=1500, indices=None,
                   points=None, ignore_dist=None):
    ''' Plot a static image of a sonar scan.

    plot is independent of telemetry data, so includes no heading compensation.

    'ping_messages' is a list of ping-protocol (auto_/)device_data messages.
    'indices' is a list of indices to draw radial lines for, e.g. for displaying
      a region of interest.
    'points' is a numpy array of (x,y) points to add to the plot, where y points
      in the sonar's forwards direction.
    'ignore_dist' is a distance in meters to cover in black, generally to hide
      the sonar's internal-reflection region.
    '''
    if ax is None:
        fig = plt.figure()
        ax = fig.add_subplot(polar=True)

    if title:
        ax.set_title(title)

    settings = ping_messages[0]
    sample_sep = settings.sample_period * 12.5e-9 * v_sound
    dists = sample_sep * np.arange(len(settings.data))
    angles = []
    values = []
    for message in ping_messages:
        angles.append(message.angle)
        values.append(np.frombuffer(message.data, dtype=np.uint8))

    # convert ping360 gradians into plottable radians
    radians = np.unwrap(np.array(angles) * (2 * np.pi / 400))
    R, Theta = np.meshgrid(dists, radians)

    ax.pcolormesh(Theta, R, values, cmap=cc.m_rainbow, shading='nearest')

    if ignore_dist:
        theta = (radians[-1] + radians[0]) / 2
        width = abs(radians[-1] - radians[0])
        ax.bar(theta, ignore_dist, width=width, bottom=0, color='k', alpha=0.5)
    
    if indices:
        ax.vlines(radians[indices], 0, dists[-1], linewidth=1, color='k')
        
    if points is not None:
        # convert to radians, magnitudes for plotting on polar axes
        # subtract a half-turn to de-compensate for angular compensation in
        # points (ping360 0-angle is on cable side, 'front' is at 180 degrees)
        ax.scatter(np.arctan2(points[:,1], points[:,0]) - np.pi,
                   np.linalg.norm(points), 1, 'k')

    if show:
        plt.show()

    return ax, radians, dists

```
