Матрица светодиодной ленты для БПФ с Python и Raspberry Pi

Я застрял с Python и светодиодной лентой. Светодиодная лента с чипом WS2801 и адресацией через SPI организована в виде такой матрицы:

     ----      ----      ----      ----
140 |    |    |    |    |    |    |    | 15
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
    |    |    |    |    |    |    |    |
155 |    |    |    |    |    |    |    | 0
          ----      ----      ----

Каждая черточка представляет собой один светодиод (один пиксель). В столбце 16 пикселей, что составляет 8 строк.

Нумерация начинается снизу справа. Таким образом, самый правый столбец начинается с индекса 0 и заканчивается индексом 15. Четыре пикселя между строками не будут освещены. Таким образом, второй самый правый столбец идет сверху вниз с индексом 20 вниз с индексом 35. Самый левый столбец находится в диапазоне от 140 вверху до 155 внизу.

Что я хочу сделать, так это визуализировать спектр песни во время ее исполнения. Низкие частоты должны отображаться в левых столбцах, более высокие частоты в правых столбцах.

Мой код основан на PixelPi (https://github.com/scottjgibson/PixelPi). Я опускаю часть с БПФ, потому что проблема не в ней.

# Each pixel consumes 3 bytes
PIXEL_SIZE = 3 

# Lots of colors
BLACK = bytearray(b'\x00\x00\x00')
AQUA = bytearray(b'\x00\xff\xff')
AQUAMARINE = bytearray(b'\x7f\xff\xd4')

def filter_pixel(input_pixel, brightness):
    output_pixel = bytearray(PIXEL_SIZE)

    input_pixel[0] = int(brightness * input_pixel[0])
    input_pixel[1] = int(brightness * input_pixel[1])
    input_pixel[2] = int(brightness * input_pixel[2])

    output_pixel[0] = gamma[input_pixel[0]]
    output_pixel[1] = gamma[input_pixel[1]]
    output_pixel[2] = gamma[input_pixel[2]]
    return output_pixel

# Initialize LED strip matrix
height  = 16
base    = [155, 120, 115, 80, 75, 40, 35, 0]
# Do the indexes of this column go from bottom to top?
up      = [False, True, False, True, False, True, False, True]
color   = [NAVY, RED, MAROON, DARKBLUE, DARKCYAN, PALEGREEN, YELLOWGREEN, YELLOW]

# Output matrix, filled with black pixels
empty_output = bytearray(args.num_leds * PIXEL_SIZE + 3)
for led in range(args.num_leds):
    empty_output[led * PIXEL_SIZE:] = filter_pixel(BLACK, 1)

current_color = bytearray(PIXEL_SIZE)
corrected_color = bytearray(PIXEL_SIZE)

while True: # (Actually while song is playing)

    # Returns an array of length 8 with values between 0 and 4095
    matrix = calculate_levels(matrix, weighting, data, CHUNK_SIZE, sample_rate)

    # Copy the matrix with only black pixels. Copying seems to be faster than resetting all not needed pixels to black
    pixel_output[:] = empty_output


    for col in range(len(base)):
        current_color[:] = color[col][:]
            # Do some gamma correction
        corrected_color[:] = filter_pixel(current_color[:], 1)

            # Each column is 16 pixels high. The maximum value of the FFT to be returned for each column is 4095. 4096 / 256 = 16
        lighted_height = round(matrix[col]/float(1 << 8), 2)

        for row in range(max(16, int(lighted_height) + 1)):
            pixel_index = base[col] + row if up[col] == True else base[col] - row
            pixel_index = pixel_index * PIXEL_SIZE

            if (row < int(lighted_height)):
                # Pixel's brightness in 100%
                pixel_output[pixel_index:] = corrected_color[:]
            elif (row <= int(lighted_height) and row + 1 > int(lighted_height)):
                # Pixel's brightness is between 0 and 1
                pixel_output[pixel_index:] = filter_pixel(current_color[:], lighted_height - int(lighted_height))

            #print "[col:", col, ", row:", row, "] : ", pixel_index, "lighted_height:", lighted_height, "int(lighted_height)", int(lighted_height), "lighted:", lighted

            # As I uncomment these two lines, at least all pixels on the other columns are displayed.
            #spidev.write(pixel_output)
            #spidev.flush()

    spidev.write(pixel_output)
    spidev.flush()

Проблема в том, что этот код освещает только самый правый столбец (от 0 до 15). Все остальные столбцы кажутся черными.

Когда я помещаю spidev.write(pixel_output) и spidev.flush() в цикл col таким образом, что pixel_output записывается для каждого столбца, по крайней мере некоторые индикаторы в других столбцах загораются. Однако они каким-то образом появляются довольно случайным образом, и звук больше не является гладким.

Кстати, светодиодная лента отлично справляется с такими примерами PixelPi, как замирание и погоня. Может ли это быть связано с какими-то свойствами микросхемы WS2801, о которых я не знаю? Или я не правильно рассчитал матрицу pixel_output?

Обновление: еще одна странность:

i = 0
x = 0
while x < 160:
    if i != 0 and i % 16 == 0:
    x = x + 4

    pixel_index = x * PIXEL_SIZE
    pixel_output[pixel_index:] = filter_pixel(WHITE, 1)

    i = i + 1
    x = x + 1

    print "i, x", i, x

    time.sleep(0.1)
    spidev.write(pixel_output)
    spidev.flush()

Это должно на самом деле подсветить пиксель 0 до последнего и оставить 4 пикселя после 16 выполнений цикла. Однако он не пропускает ни одного пикселя и, таким образом, останавливается до того, как будет достигнут последний пиксель.


person Explicat    schedule 03.06.2014    source источник
comment
Вам нужно сделать шаг назад и упростить свой тестовый пример. Избавьтесь от зависимости от музыкальных файлов и просто создайте тестовый образец с известными значениями. Так вы найдете свою ошибку.   -  person Mark Lakata    schedule 04.06.2014


Ответы (1)


Догадаться!

pixel_output[pixel_index:] = filter_pixel(WHITE, 1)

копирует не только 3 элемента массива, как я ожидал. Он копирует значения, возвращаемые filter_pixel из pixel_index, по всему буферу до конца.

Установка верхнего предела для копирования решила мою проблему.

pixel_output[pixel_index:(pixel_index + PIXEL_SIZE)] = filter_pixel(WHITE, 1)
person Explicat    schedule 04.06.2014