Giter Site home page Giter Site logo

maia-sdr / maia-sdr Goto Github PK

View Code? Open in Web Editor NEW
185.0 9.0 20.0 1.63 MB

Maia SDR is an open-source FPGA-based SDR project focusing on the ADALM Pluto

Home Page: https://maia-sdr.org

Makefile 0.84% Tcl 4.82% Python 44.89% Verilog 1.87% Rust 43.96% C 1.90% HTML 0.85% JavaScript 0.03% CSS 0.71% GLSL 0.12%
adalm-pluto amaranth-hdl dsp fpga radio rust sdr signal-processing wasm webgl2

maia-sdr's Introduction

Maia SDR

Maia SDR is an open-source FPGA-based SDR project focusing on the ADALM Pluto.

It currently provides a firmware image for the Pluto with the following functionality:

  • Web-based interface that can be accessed from a smartphone, PC or other device.
  • Real-time waterfall display supporting up to 61.44 Msps (limit given by the AD936x RFIC of the Pluto).
  • IQ recording in SigMF format, at up to 61.44 Msps and with a 400 MiB maximum data size (limit given by the Pluto RAM size). Recordings can be downloaded to a smartphone or other device.

See maia-sdr.org for more details.

Installation and supported hardware

See the installation instructions.

Firmware images

Stable releases of firmware images for the ADALM Pluto and Pluto+ can be found in the plutosdr-fw repository. See also the installation instructions.

Development ADALM Pluto firmware images for each commit and pull request are built automatically with Github's actions system. To download the firmware image for the latest commit in the main branch (or for any other commit), click on the actions check, then go to the plutosdr-fw action details, click on the summary, and download the pluto-firmware artifact. This procedure is illustrated by the following screenshots.

List of actions

plutosdr-fw action details

plutosdr-fw action summary

For each pull request, a firmware image is also built. The bot adds a comment to the pull request with a link to the firmware when it is ready.

Support

Support for Maia SDR is handled through Github issues and Github discussions.

Project structure

The project is divided into the following components:

  • maia-hdl. The FPGA design. It is written in Amaranth. It can be used to build the Vivado project and bitstream for Maia SDR or as a library in other projects.
  • maia-httpd. The software application that runs on the Zynq ARM CPU. It is written in asynchronous Rust and spawns a web server.
  • maia-kmod. A kernel module used to control some RAM buffers that are used to exchange data between the FPGA and CPU.
  • maia-wasm. A web application that is the UI of Maia SDR. IT is written in Rust with WebAssembly and uses WebGL2 to render the waterfall.

License

maia-hdl is licensed under the MIT license. maia-httpd and maia-wasm are licensed under either of the Apache License, Version 2.0 or the MIT license at your option. maia-kmod is licensed under the GPL, version 2.

maia-sdr's People

Contributors

batchdrake avatar daniestevez avatar gavrilyak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maia-sdr's Issues

CPU usage in idle mode question

maia-httpd uses 30% of CPU when no clients connected (number_integrations = 16) and above 50% when number_integrations = 1. Pluto is warm.
I added the following condition and CPU usage drops to 5% on number_integrations = 1.

  if self.sender.receiver_count() > 0 {
    let _ = self.sender.send(Self::buffer_u64_to_f32(buffer, scale));
  }

So my question is: is this a proper fix? Maybe it will break something or maybe it could be done better (e.g. don't read buffers at all when no client is connected) .

Thanks in advance.

maia-hdl: spectrum integrator counter reset warning

The spectrum integrator is giving a warning:

/hdl/maia-sdr/maia-hdl/maia_hdl/spectrum_integrator.py:137: SyntaxWarning: Reset value -5 is signed, but the signal shape is unsigned(12)
  write_counter = Signal(self.order_log2, reset=write_counter_rst)

HDL to allow for sampling time alignment between multiple plutos

Hi

i wanted to ask if there was any plan to introduce a HDL code to the Zynq to allow for sampling time alignment between mutiple pluto SDR's, as that would certainly be helpful for phase synchronization experiments between multiple pluto sdr's. From my limited understanding there is an AXI pin in the Zynq which allows for sending out a signal that could be used for master slave configuration with another zynq in a second pluto to trigger it in order to sample simulataneously. This is something i've been stuck with for a long time and i've been dying to find a solution for this for the ADALM pluto.

Sincerely,
AG

null fields in PATCH JSON requests

Currently PATCH requests are sending a bunch of fields set to null instead of completely omitting these fields. The reason is that this is how serde_json serializes the Option fields in patch structs like this one. The serde_json documentation indicates how to skip option fields that are None, by using

#[serde(skip_serializing_if = "Option::is_none")]

We should use this.

Recording enhancements

It would be really nice if it was possible to define a set number of samples that would be captured. (such as 100e6 samples, which at 40e6 sample rate results in a 2.5 second file) Another way could be to just define the time of a capture.

It would also be super nice if the filename could default to the timestamp with a keyword that is set, the sigmf-recorder utility that is on Gitlab will output something like this:

2022-07-23-23-20-25_keyword_F440000000_G30.sigmf

Spectrometer: peak detect mode

For short packets (for example, Bluetooth), the spectrometer is not very sensitive because it averages FFTs of contiguous samples to achieve the required waterfall update rate. For instance, with a waterfall update rate of 30 Hz, each waterfall line is an average of 33.3 ms worth of samples.

A peak detect mode would be useful in these cases. In this mode, instead of adding each FFT to the accumulator, we would take the maximum between the current FFT and the accumulator value, and store that in the accumulator.

maia-wasm: waterfall frequency ticks not showing for some particular sample rates

I have just noticed that at 45 Msps the frequency ticks in the waterfall are not being displayed. The Javascript console in Firefox shows the following messages:

WebGL warning: drawElementsInstanced: Index buffer too small. 32
After reporting 32, no further warnings will be reported for this WebGL context. 

Probably the logic handling the drawing of the ticks is wrong in some corner cases.

maia-wasm waterfall panics when the horizontal resolution is large

After installing maia-sdr firmware from release as instruction describes i cannot record spectrogram. Running ./maia-httpd from /root directory returns this error

# ./maia-httpd
Error: failed to open maia-sdr-spectrometer DMA buffer

Caused by:
    0: failed to open rxbuffer DMA buffer
    1: mmap rxbuffer failed

I did not change my default IP address as described in "Configure the Pluto USB Ethernet". So i opened connection to Pluto on 192.168.3.1 IP-address from Mozilla Firefox on Windows 10. Opening http://192.168.3.1:8000/ shows spectrogram screen with black screen as this

image

I've flashed pluto using pluto.frm file from release archive. I am using ADALM-PLUTO REV B. fw_printenv command outputs this

# fw_printenv
adi_loadvals=fdt addr ${fit_load_address} && fdt get value fdt_choosen /configurations/${fit_config}/ fdt && fdt get addr fdtaddr /images/${fdt_choosen} data && fdt addr ${fdtaddr}; if test -n ${ad936x_ext_refclk}  && test ! -n ${ad936x_skip_ext_refclk}; then fdt set /clocks/clock@0 clock-frequency ${ad936x_ext_refclk}; fi; if test -n ${model}; then fdt set / model ${model}; fi; if test -n ${attr_name} && test -n ${attr_val}; then fdt set /amba/spi@e0006000/ad9361-phy@0 ${attr_name} ${attr_val}; fi
baudrate=115200
bitstream_image=system.bit.bin
bitstream_size=0x400000
boot_image=BOOT.bin
boot_size=0xF00000
bootcmd=run $modeboot
bootdelay=0
bootenv=uEnv.txt
clear_reset_cause=mw f8000008 df0d && mw f8000258 00400000 && mw f8000004 767b
devicetree_image=devicetree.dtb
devicetree_load_address=0x2000000
devicetree_size=0x20000
dfu_alt_info=boot.dfu raw 0x0 0x100000;firmware.dfu raw 0x200000 0x1E00000;uboot-extra-env.dfu raw 0xFF000 0x1000;uboot-env.dfu raw 0x100000 0x20000;spare.dfu raw 0x120000 0xE0000
dfu_ram=echo Entering DFU RAM mode ... && run dfu_ram_info && dfu 0 ram 0
dfu_ram_info=set dfu_alt_info dummy.dfu ram 0 0\\;firmware.dfu ram ${fit_load_address} 0x1E00000
dfu_sf=gpio set 15;set stdout serial@e0001000;echo Entering DFU SF mode ... && run dfu_sf_info && dfu 0 sf 0:0:40000000:0 && if test -n ${dfu_alt_num} && test ${dfu_alt_num} = 1; then set fit_size ${filesize} && set dfu_alt_num && env save; fi;gpio clear 15;
dfu_sf_info=set dfu_alt_info boot.dfu raw 0x0 0x100000\\;firmware.dfu raw 0x200000 0x1E00000\\;uboot-extra-env.dfu raw 0xFF000 0x1000\\;uboot-env.dfu raw 0x100000 0x20000\\;spare.dfu raw 0x120000 0xE0000
ethaddr=00:0a:35:00:01:22
extraenv_load_address=0x207E000
fdt_high=0x20000000
filesize=9a620b
fit_config=config@0
fit_load_address=0x2080000
importbootenv=echo Importing environment from SD ...; env import -t ${loadbootenv_addr} $filesize
initrd_high=0x20000000
jtagboot=env default -a;sf probe && sf protect unlock 0 100000 && run dfu_sf;
kernel_image=uImage
loadbit_addr=0x100000
loadbootenv=load mmc 0 ${loadbootenv_addr} ${bootenv}
loadbootenv_addr=0x2000000
maxcpus=1
preboot=if test $modeboot = sdboot && env run sd_uEnvtxt_existence_test; then if env run loadbootenv; then env run importbootenv; fi; fi;
qspiboot_extraenv=sf read ${extraenv_load_address} 0xFF000 0x1000 && env import -c ${extraenv_load_address} 0x1000 || true
ramdisk_image=uramdisk.image.gz
ramdisk_load_address=0x4000000
ramdisk_size=0x400000
read_sf=sf probe 0:0 50000000 0 && run qspiboot_extraenv &&sf read ${fit_load_address} 0x200000 ${fit_size} && iminfo ${fit_load_address} || sf read ${fit_load_address} 0x200000  0x1E00000;
sd_uEnvtxt_existence_test=test -e mmc 0 /uEnv.txt
stdout=serial@e0001000
thor_ram=run dfu_ram_info && thordown 0 ram 0
uenvboot=if run loadbootenv; then echo Loaded environment from ${bootenv}; run importbootenv; fi; if test -n $uenvcmd; then echo Running uenvcmd ...; run uenvcmd; fi
usbboot=if usb start; then run uenvboot; echo Copying Linux from USB to RAM... && load usb 0 ${fit_load_address} ${kernel_image} && load usb 0 ${devicetree_load_address} ${devicetree_image} && load usb 0 ${ramdisk_load_address} ${ramdisk_image} && bootm ${fit_load_address} ${ramdisk_load_address} ${devicetree_load_address}; fi
hostname=pluto
ipaddr=192.168.3.1
ipaddr_host=192.168.3.10
netmask=255.255.255.0
udc_handle_suspend=0
netmask_eth=255.255.255.0
ramboot_verbose=adi_hwref;echo Copying Linux from DFU to RAM... && run dfu_ram;if run adi_loadvals; then echo Loaded AD936x refclk frequency and model into devicetree; fi; envversion;setenv bootargs console=ttyPS0,115200 maxcpus=${maxcpus} rootfstype=ramfs root=/dev/ram0 rw earlyprintk clk_ignore_unused uio_pdrv_genirq.of_id=uio_pdrv_genirq uboot="${uboot-version}" && bootm ${fit_load_address}#${fit_config}
qspiboot_verbose=adi_hwref;echo Copying Linux from QSPI flash to RAM... && run read_sf && if run adi_loadvals; then echo Loaded AD936x refclk frequency and model into devicetree; fi; envversion;setenv bootargs console=ttyPS0,115200 maxcpus=${maxcpus} rootfstype=ramfs root=/dev/ram0 rw earlyprintk clk_ignore_unused uio_pdrv_genirq.of_id=uio_pdrv_genirq uboot="${uboot-version}" && bootm ${fit_load_address}#${fit_config} || echo BOOT failed entering DFU mode ... && run dfu_sf
qspiboot=set stdout nulldev;adi_hwref;test -n $PlutoRevA || gpio input 14 && set stdout serial@e0001000 && sf probe && sf protect lock 0 100000 && run dfu_sf;  set stdout serial@e0001000;itest *f8000258 == 480003 && run clear_reset_cause && run dfu_sf; itest *f8000258 == 480007 && run clear_reset_cause && run ramboot_verbose; itest *f8000258 == 480006 && run clear_reset_cause && run qspiboot_verbose; itest *f8000258 == 480002 && run clear_reset_cause && exit; echo Booting silently && set stdout nulldev; run read_sf && run adi_loadvals; envversion;setenv bootargs console=ttyPS0,115200 maxcpus=${maxcpus} rootfstype=ramfs root=/dev/ram0 rw quiet loglevel=4 clk_ignore_unused uio_pdrv_genirq.of_id=uio_pdrv_genirq uboot="${uboot-version}" && bootm ${fit_load_address}#${fit_config} || set stdout serial@e0001000;echo BOOT failed entering DFU mode ... && sf protect lock 0 100000 && run dfu_sf
attr_name=compatible
attr_val=ad9364
mode=1r1t
fit_size=C695A7

How to solve this? I am an inexperienced user of Pluto.

About the usage of spectrum client

This repo is definitely a great piece of engineering. I would like to ask about the usage of spectrum client python code provided in httpd part. Even though I can reach the server in the browser perfectly on http://192.168.2.1:8000/ I can not get the python code connect by calling python spectrum_client.py ws://192.168.2.1:8000 and I get the error as follows
raise InvalidStatusCode(status_code, response_headers) websockets.exceptions.InvalidStatusCode: server rejected WebSocket connection: HTTP 200

Maybe I misunderstood what I should provide as the websocket adress . I somehow could not get the WebSocket connection.

Best,

maia-wasm: disable RX gain field in AGC modes

When the RX AGC is set to some value different from "Manual", the RX gain field should be grayed out or disabled, since that value is intended as read-only (it shows the current gain applied by the AGC), and the values that get entered by the user there do not get applied at all.

Firefox Uncaught in promise

Ran into this issue trying to use Firefox with a Linux host.

This was a fatal error on FF93 (really old I know :) ) where the spectrum would not display. Updating to FF 109 (most recent version) it is no longer fatal and the spectrum displays but throws a slightly different error. I'm including the FF93 output as it might be a bit more helpful, the FF 109 doesn't output very much.

FF 109 non-fatal error:

Uncaught (in promise) unable to parse maia_json::Ad9361 JSON 

FF 93 error:

Uncaught (in promise) 
<dialog id="recording_dialog" class="ui">
​
accessKey: ""
​
accessKeyLabel: ""
​
assignedSlot: null
​
attributes: NamedNodeMap [ class="ui", id="recording_dialog" ]
​
baseURI: "http://192.168.2.1:8000/"
​
childElementCount: 1
​
childNodes: NodeList(3) [ #text, form#recording_form, #text ]
​
children: HTMLCollection { 0: form#recording_form, length: 1, … }
​
classList: DOMTokenList [ "ui" ]
​
className: "ui"
​
clientHeight: 0
​
clientLeft: 0
​
clientTop: 0
​
clientWidth: 0
​
contentEditable: "inherit"
​
dataset: DOMStringMap(0)
​
dir: ""
​
draggable: false
​
firstChild: #text "
      "
​
firstElementChild: <form id="recording_form" method="dialog">
​
hidden: false
​
id: "recording_dialog"
​
innerHTML: "\n      <form method=\"dialog\" id=\"recording_form\">\n        <label for=\"recording_metadata_file\">Filename</label>\n        <input type=\"text\" id=\"recording_metadata_filename\">\n        <label for=\"recording_metadata_description\">Description</label>\n        <input type=\"text\" id=\"recording_metadata_description\">\n        <label for=\"recording_metadata_author\">Author</label>\n        <input type=\"text\" id=\"recording_metadata_author\">\n        <label for=\"recorder_mode\">Mode</label>\n        <select id=\"recorder_mode\">\n          <option>8 bit IQ</option>\n          <option>12 bit IQ</option>\n        </select>\n        <a id=\"download_recording\" href=\"/recording\" download=\"\">Download recording</a>\n        <button id=\"close_recording_dialog\" value=\"close\" autofocus=\"\">Close</button>\n      </form>\n    "
​
innerText: "\n      \n        Filename\n        \n        Description\n        \n        Author\n        \n        Mode\n        \n          8 bit IQ\n          12 bit IQ\n        \n        Download recording\n        Close\n      \n    "
​
isConnected: true
​
isContentEditable: false
​
lang: ""
​
lastChild: #text "
    "
​
lastElementChild: <form id="recording_form" method="dialog">
​
localName: "dialog"
​
namespaceURI: "http://www.w3.org/1999/xhtml"
​
nextElementSibling: <canvas id="canvas" style="cursor: crosshair;">
​
nextSibling: #text "

    "
​
nodeName: "DIALOG"
​
nodeType: 1
​
nodeValue: null
​
nonce: ""
​
offsetHeight: 0
​
offsetLeft: 0
​
offsetParent: null
​
offsetTop: 0
​
offsetWidth: 0
​
onabort: null
​
onanimationcancel: null
​
onanimationend: null
​
onanimationiteration: null
​
onanimationstart: null
​
onauxclick: null
​
onbeforeinput: null
​
onblur: null
​
oncanplay: null
​
oncanplaythrough: null
​
onchange: null
​
onclick: null
​
onclose: null
​
oncontextmenu: null
​
oncopy: null
​
oncuechange: null
​
oncut: null
​
ondblclick: null
​
ondrag: null
​
ondragend: null
​
ondragenter: null
​
ondragexit: null
​
ondragleave: null
​
ondragover: null
​
ondragstart: null
​
ondrop: null
​
ondurationchange: null
​
onemptied: null
​
onended: null
​
onerror: null
​
onfocus: null
​
onformdata: null
​
onfullscreenchange: null
​
onfullscreenerror: null
​
ongotpointercapture: null
​
oninput: null
​
oninvalid: null
​
onkeydown: null
​
onkeypress: null
​
onkeyup: null
​
onload: null
​
onloadeddata: null
​
onloadedmetadata: null
​
onloadend: null
​
onloadstart: null
​
onlostpointercapture: null
​
onmousedown: null
​
onmouseenter: null
​
onmouseleave: null
​
onmousemove: null
​
onmouseout: null
​
onmouseover: null
​
onmouseup: null
​
onmozfullscreenchange: null
​
onmozfullscreenerror: null
​
onpaste: null
​
onpause: null
​
onplay: null
​
onplaying: null
​
onpointercancel: null
​
onpointerdown: null
​
onpointerenter: null
​
onpointerleave: null
​
onpointermove: null
​
onpointerout: null
​
onpointerover: null
​
onpointerup: null
​
onprogress: null
​
onratechange: null
​
onreset: null
​
onresize: null
​
onscroll: null
​
onsecuritypolicyviolation: null
​
onseeked: null
​
onseeking: null
​
onselect: null
​
onselectionchange: null
​
onselectstart: null
​
onslotchange: null
​
onstalled: null
​
onsubmit: null
​
onsuspend: null
​
ontimeupdate: null
​
ontoggle: null
​
ontransitioncancel: null
​
ontransitionend: null
​
ontransitionrun: null
​
ontransitionstart: null
​
onvolumechange: null
​
onwaiting: null
​
onwebkitanimationend: null
​
onwebkitanimationiteration: null
​
onwebkitanimationstart: null
​
onwebkittransitionend: null
​
onwheel: null
​
outerHTML: "<dialog class=\"ui\" id=\"recording_dialog\">\n      <form method=\"dialog\" id=\"recording_form\">\n        <label for=\"recording_metadata_file\">Filename</label>\n        <input type=\"text\" id=\"recording_metadata_filename\">\n        <label for=\"recording_metadata_description\">Description</label>\n        <input type=\"text\" id=\"recording_metadata_description\">\n        <label for=\"recording_metadata_author\">Author</label>\n        <input type=\"text\" id=\"recording_metadata_author\">\n        <label for=\"recorder_mode\">Mode</label>\n        <select id=\"recorder_mode\">\n          <option>8 bit IQ</option>\n          <option>12 bit IQ</option>\n        </select>\n        <a id=\"download_recording\" href=\"/recording\" download=\"\">Download recording</a>\n        <button id=\"close_recording_dialog\" value=\"close\" autofocus=\"\">Close</button>\n      </form>\n    </dialog>"
​
ownerDocument: HTMLDocument http://192.168.2.1:8000/
​
parentElement: <body>
​
parentNode: <body>
​
part: DOMTokenList []
​
prefix: null
​
previousElementSibling: null
​
previousSibling: #text "

    "
​
scrollHeight: 0
​
scrollLeft: 0
​
scrollLeftMax: 0
​
scrollTop: 0
​
scrollTopMax: 0
​
scrollWidth: 0
​
shadowRoot: null
​
slot: ""
​
spellcheck: false
​
style: CSS2Properties(0)
​
tabIndex: -1
​
tagName: "DIALOG"
​
textContent: "\n      \n        Filename\n        \n        Description\n        \n        Author\n        \n        Mode\n        \n          8 bit IQ\n          12 bit IQ\n        \n        Download recording\n        Close\n      \n    "
​
title: ""
​
<prototype>: HTMLUnknownElementPrototype { … }


Tooltip for signal information and some kind of measuring possibilities

It would be cool if cursor on the waterfall canvas has (optional) tooltip which shows following information:

  • Frequency
  • Time delta from "now()"
  • Signal level?

Once there's some kind of tooltip mechanism, then in case there's also possibility to freeze/pause the view, then override the "panning" feature (change of X coord?) to allow measuring bandwidth and also timespan (Y coord?).

Maia-SDR is really an excellent piece of engineering :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.