Search code examples
pythonpython-3.xmatplotlibpygmt

Python plot signals based on the azimuth of arrival?


I'm trying to make a plot of a signal, where the orientation of the signal on the plot is the azimuth the signal originated from.

My initial thought was to use a polar projection, but I don't actually want the signal itself to be converted into polar coordinates.

I tried to remedy the polar conversion by using a polar projected image as a background to a subplot grid, rotating the subplots based on the azimuth, similar to what's found in this link: Rotate transformation on matplotlib axis in subplot

Here's the plot that I created using matplotlib following this workflow, showing signals of azimuths 45 degrees apart.enter image description here

This plot is kinda what I want, but not exactly. It plots the signals by their azimuth, and still preserves their amplitude and frequency without an actual polar conversion. However, this approach is incredibly hackish and not scalable -- it would be very difficult to insert a signal at 30 degrees, for example, and the signals at 135 & 225 degrees don't show up at all.

I think my problem is that I'm conceptualizing the problem incorrectly. I've tagged PyGMT here because I know there is a way to create the plot I want using it, but finding a springboard example with that library has proved difficult for me. Any insights are greatly appreciated.


Solution

  • In the off chance anyone ever needs something similar, here's what I was able to cobble together. I used PyGMT to create the figure I needed -- if you're not already familiar with Generic Mapping Tools (GMT) syntax, it can be difficult to learn. Below is the script I created to generate a random sequence of signals of various amplitudes and frequencies with corresponding "azimuths" that simulate their propagation direction or arrival direction.

    Cheers!

    import numpy as np
    import matplotlib.pyplot as plt
    import pygmt
    import pandas as pd
    
    azimuths = np.arange(-180, 180, 45) # range of azimuths
    time = np.linspace(0.5, 2*np.pi, 1000) # time vector (same for each)
    
    # Amplitudes just calculated from random amplitude & frequency shifts 
    amplitudes = [np.random.randn()*np.sin(np.random.randint(5,10)*time) for i in range(len(azimuths))]
    
    # Create PyGMT Figure
    fig = pygmt.Figure()
    
    # Add a basemap to the figure from 0-360 degrees & a radius of 0-4
    # projection='P20c+a10' means Polar projections 20 cm wide; +a means to set North as 0 & increase clockwise
    # frame="pa30f15|sa15f5g45" sets the ticks around the outside of the polar plot
    # pa30f15 --> primary axes mark major ticks 30 degrees apart and minor ticks every 15 degrees
    # sa15f5g45 --> secondary major ticks every 15 degrees, secondary minor ticks every 5 degrees
    #               with gridlines every 45 degrees
    fig.basemap(region=[0,360,0,4], projection='P20c+a', frame="pa30f15|sa15f5g45")
    
    # Loop over each azimuth and append to the plot each time
    for i, az in enumerate(azimuths):
        if az <=180:
            # Each time we use wiggle to plot the data
            
            # Scale adjusts the height scale --> inverse? Decimal scales increase
            # amplitude on the plot
            fig.wiggle(
                       scale=1,
                       # x is the direction, x, y, z need the same shape
                       x=np.repeat(az,len(time)),
                       # Distance along our azimuth (follows the radius)
                       y=time,
    
                       # Actual amplitude of our wave
                       z=amplitudes[i],
                       # 0.5 point red pen
                       pen='0.5p,red'
                       )
        else:
    
            # If the azimuth is greater than 180, flip the sign of amplitude
            fig.wiggle(
                       scale=0.25,
                       x=np.repeat(az,len(time)),
                       y=time,
                       z=-amplitudes[i],
                       pen='0.5p,red'
                       )
             
    # Shows the GMT plot using default pdf viewer
    fig.show(method='external')
    

    enter image description here