Documentation ¶
Overview ¶
Package sdr contains go fundamental types and helpers to allow for reading from and writing to software defined radios.
The interfaces and functions exposed here are designed to mirror and behave in a way that is expected and not supprising to a Go developer. A lot of the design here is taken from the Go io package. A new set of interfaces are required in order to provide a set of tools to work with reading and writing IQ samples.
Since conversion between IQ formats is very expensive, this package operates on a generic "sdr.Samples" type, which is a vector of IQ samples, in some format. Reading and writing time sensitive IQ data should usually be kept in its native format, and allow for a non-real-time conversion on a separate thread to take place.
Most code designed to create and consume IQ data likely wants data in the complex64 format, so an explicit cast or conversion should be made before doing signal processing.
Index ¶
- Variables
- func ConvertBuffer(dst, src Samples) (int, error)
- func Copy(dst Writer, src Reader) (int64, error)
- func CopyBuffer(dst Writer, src Reader, buf Samples) (int64, error)
- func CopySamples(dst, src Samples) (int, error)
- func Duration(s Samples, sampleRate uint) time.Duration
- func LookupTableIndexI8(v [2]int8) uint16
- func LookupTableIndexU8(v [2]uint8) uint16
- func MustUnsafeSamplesAsBytes(buf Samples) []byte
- func Pipe(samplesPerSecond uint, format SampleFormat) (PipeReader, PipeWriter)
- func PipeWithContext(ctx context.Context, samplesPerSecond uint, format SampleFormat) (PipeReader, PipeWriter)
- func ReadAtLeast(r Reader, buf Samples, min int) (int, error)
- func ReadFull(r Reader, buf Samples) (int, error)
- func SetGainStages(device Sdr, gainSettings map[string]float32) error
- func UnsafeSamplesAsBytes(buf Samples) ([]byte, error)
- type Closer
- type GainStage
- type GainStageType
- type GainStages
- type HardwareInfo
- type LookupTable
- type PipeReadWriter
- type PipeReader
- type PipeWriter
- type ReadCloser
- type ReadClosers
- type ReadWriteCloser
- type Reader
- type Readers
- type Receiver
- type SampleFormat
- type Samples
- type SamplesC64
- func (s SamplesC64) Add(c []complex64) error
- func (s SamplesC64) Format() SampleFormat
- func (s SamplesC64) Length() int
- func (s SamplesC64) Multiply(c complex64)
- func (s SamplesC64) Scale(r float32)
- func (s SamplesC64) Size() int
- func (s SamplesC64) Slice(start, end int) Samples
- func (s SamplesC64) ToI16(out SamplesI16) (int, error)
- func (s SamplesC64) ToI8(out SamplesI8) (int, error)
- func (s SamplesC64) ToU8(out SamplesU8) (int, error)
- type SamplesI16
- func (s SamplesI16) Format() SampleFormat
- func (s SamplesI16) Length() int
- func (s SamplesI16) ShiftLSBToMSBBits(bits int)
- func (s SamplesI16) Size() int
- func (s SamplesI16) Slice(start, end int) Samples
- func (s SamplesI16) ToC64(out SamplesC64) (int, error)
- func (s SamplesI16) ToI8(out SamplesI8) (int, error)
- func (s SamplesI16) ToU8(out SamplesU8) (int, error)
- type SamplesI8
- func (s SamplesI8) Format() SampleFormat
- func (s SamplesI8) Length() int
- func (s SamplesI8) Size() int
- func (s SamplesI8) Slice(start, end int) Samples
- func (s SamplesI8) ToC64(out SamplesC64) (int, error)
- func (s SamplesI8) ToI16(out SamplesI16) (int, error)
- func (s SamplesI8) ToU8(out SamplesU8) (int, error)
- type SamplesPool
- type SamplesU8
- func (s SamplesU8) Format() SampleFormat
- func (s SamplesU8) Length() int
- func (s SamplesU8) Size() int
- func (s SamplesU8) Slice(start, end int) Samples
- func (s SamplesU8) ToC64(out SamplesC64) (int, error)
- func (s SamplesU8) ToI16(out SamplesI16) (int, error)
- func (s SamplesU8) ToI8(out SamplesI8) (int, error)
- type Sdr
- type Transceiver
- type Transmitter
- type WriteCloser
- type Writer
Constants ¶
This section is empty.
Variables ¶
var ( // ErrSampleFormatMismatch will be returned when there's a mismatch between // sample formats. ErrSampleFormatMismatch = fmt.Errorf("sdr: iq sample formats do not match") // ErrSampleFormatUnknown will be returned when a specific iq format is not // implemented. ErrSampleFormatUnknown = fmt.Errorf("sdr: iq sample format is not understood") // ErrDstTooSmall will be returned when attempting to perform an operation // and the target buffer is too small to use. ErrDstTooSmall = fmt.Errorf("sdr: destination sample buffer is too small") )
var ( // ErrShortBuffer will return if the number of bytes read was less than the // minimum required by the callee. ErrShortBuffer = fmt.Errorf("sdr: short read") // ErrUnexpectedEOF will return if the EOF was reached before parsing was // completed. ErrUnexpectedEOF = fmt.Errorf("sdr: expected EOF") )
var ( // ErrConversionNotImplemented will be returned if the Sample type is // unable to be converted into the desired target format. ErrConversionNotImplemented = fmt.Errorf("sdr: unknown format conversion") )
var ErrNotSupported = fmt.Errorf("sdr: feature not supported by this device")
ErrNotSupported will be returned when an SDR does not support the feature requested.
var ( // ErrPipeClosed will be returned when the Pipe is closed. ErrPipeClosed = fmt.Errorf("sdr: pipe is closed") )
var ErrShortWrite = errors.New("sdr: short write")
ErrShortWrite will be returned when a write was aborted halfway through.
Functions ¶
func ConvertBuffer ¶
ConvertBuffer the provided Samples to the desired output format.
The conversion will happen in CPU, and this format can be a little slow, but it will get the job done, and beats the heck out of having to worry about the underlying data format.
In the event that the desired format is the same as the provided format this function will copy the source samples to the target buffer.
func Copy ¶
Copy will copy samples from the src sdr.Reader to the dst sdr.Writer.
The Reader and Writer must be of the same SampleFormat. If not, that will return an error, and the caller should explicitly define how and where to convert the two formats.
func CopyBuffer ¶
CopyBuffer will copy samples from the src sdr.Reader to the dst sdr.Writer using the provided Buffer.
func CopySamples ¶
CopySamples is the interface version of `copy`, which is type-aware.
This is used when you want to copy samples between two buffers of the same type. This can't be used for conversion.
func LookupTableIndexI8 ¶
LookupTableIndexI8 will return the index into the LookupTable for an int8 iq sample.
func LookupTableIndexU8 ¶
LookupTableIndexU8 will return the index into the LookupTable for an uint8 iq sample.
func MustUnsafeSamplesAsBytes ¶
MustUnsafeSamplesAsBytes will call the very dangerous UnsafeSamplesAsBytes function, and add even more unsafe and dangerous behavior on top -- namely, a panic if the Samples type can not be represented as a byte slice.
Use of the function should be *seriously* discouraged and not used to every extent possible.
func Pipe ¶
func Pipe(samplesPerSecond uint, format SampleFormat) (PipeReader, PipeWriter)
Pipe will create a new sdr.Reader and sdr.Writer that will allow writes to pass through and show up to a reader. This allows "patching" a Write endpoint into a "Read" endpoint.
func PipeWithContext ¶
func PipeWithContext( ctx context.Context, samplesPerSecond uint, format SampleFormat, ) (PipeReader, PipeWriter)
PipeWithContext will create a new sdr.Reader and sdr.Writer as returned by the Pipe call, but with a custom Context. This is purely used for external control of the lifecycle of the Pipe.
func ReadAtLeast ¶
ReadAtLeast reads from r into buf until it has read at least min bytes.
func SetGainStages ¶
SetGainStages will set the GainStages on the device by their Name as returned by .String().
func UnsafeSamplesAsBytes ¶
UnsafeSamplesAsBytes is a very dangerous function.
Use of the function should be *seriously* discouraged and not used to every extent possible. This is only to be used at carefully controlled boundaries where safe high-level primitives can't be used for a serious technical purpose.
Types ¶
type Closer ¶
type Closer interface {
Close() error
}
Closer is the interface that wraps the basic Close method.
type GainStage ¶
type GainStage interface { // GainRange expresses the max and minimum values that this Gain stage // can be set to. The value may be negative in the case of attenuation, // or positive in the case of amplification. Range() [2]float32 // Type will return what type of GainStage this is, usually what part // of the chain from antenna to USB the gain stage exists within. Type() GainStageType // String will return a human readable name to be used when referencing this // stage to a user. This string should match the format "%s Gain Stage" in // a sensible way, something like "IF", "LNA" or "Baseband" are all good // examples. String() string }
GainStage is a step at which an adjustment can be made to the values that flow through that stage.
type GainStageType ¶
type GainStageType uint16
GainStageType describes what type the GainStage is.
This is mostly centered around where in the chain from antenna to USB this particular GainStage is, as well as if it's on the rx or tx side.
const ( // GainStageTypeUnknown is provided when the GainStageType is not known. GainStageTypeUnknown GainStageType = 0x0000 // GainStageTypeIF represents a GainStage where Gain is applied to the // signal in its Intermediate Frequency stage. GainStageTypeIF GainStageType = 0x0001 // GainStageTypeBB represents a GainStage where the Gain is applied at // the Baseband. GainStageTypeBB GainStageType = 0x0002 // GainStageTypeFE represents a GainStage before the baseband/if, in // the radio Frontend GainStageTypeFE GainStageType = 0x0004 // GainStageTypeAmp represents a GainStage before the baseband/if, in // the radio Frontend GainStageTypeAmp GainStageType = 0x0008 // GainStageTypeAttenuator represents a GainStage which reduces the power // passing through rather than amplifying it. GainStageTypeAttenuator GainStageType = 0x0010 // GainStageTypeRecieve represents a GainStage on the receive path. GainStageTypeRecieve GainStageType = 0x0100 // GainStageTypeTransmit represents a GainStage on the transmit path. GainStageTypeTransmit GainStageType = 0x0200 )
func (GainStageType) Is ¶
func (gst GainStageType) Is(gainStageType GainStageType) bool
Is will check to see if the GainStageType is another GainStageType. Direct comparison won't work, since we bitmask the two together, so this is a helpful function to do an XOR and ==.
func (GainStageType) String ¶
func (gst GainStageType) String() string
String will return a short human-readable list of type names.
type GainStages ¶
type GainStages []GainStage
GainStages is a list of GainStage objects.
func (GainStages) Filter ¶
func (gs GainStages) Filter(gainStageType GainStageType) GainStages
Filter will return the GainStages that match a specific criteria.
func (GainStages) First ¶
func (gs GainStages) First(gainStageType GainStageType) GainStage
First will return the first GainStage that matches the criteria, returning nil if no such gain stage exists.
func (GainStages) Map ¶
func (gs GainStages) Map() map[string]GainStage
Map will return the GainStages in a map, where the key is the value returned by .String().
type HardwareInfo ¶
type HardwareInfo struct { // Manufacturer is the person, company or group that created this SDR. Manufacturer string // Product is the name of the specific SDR product connected. Product string // Serial is an identifier that is unique to the connected SDR. Serial string }
HardwareInfo contains information about the connected SDR.
Some subset of this information may be populated, none of it is a hard requirement if it does not exist. Not all SDRs will have a Serial, for example.
type LookupTable ¶
type LookupTable interface { // Lookup - uncreatively - looks up the values from the source ('src') // IQ buffer, and writes the precomputed value to the destination ('dst') // buffer. // // 'dst' and 'src' MUST match the configured sample format(s). Lookup(dst, src Samples) (int, error) // SourceSampleFormat is the sample format of the precomputed table keys. // this must be one of SampleFormatI8 or SampleFormatU8, depending on the // configuration of the LookupTable. SourceSampleFormat() SampleFormat // DestinationSampleFormat is the sample format of the precomputed table // values. This can be any IQ type. DestinationSampleFormat() SampleFormat }
LookupTable or "iq table" is a micro-optimization for extremely hotpath code where you make a memory / one-time CPU tradeoff for many expensive operations on an int8 or uint8. Since both are [2]int8 or [2]uint8, it's very possible to pre-compute all possible input IQ samples, since both could "just" be thought of as an int16, which is less than a fraction of a second to precompute (in IQ terms - 65535 (int16 max) is only 0.03 of a second at 2Msps. That being said -- this isn't free and shouldn't be overused.
func NewLookupTable ¶
func NewLookupTable(inputFormat SampleFormat, lookup Samples) (LookupTable, error)
NewLookupTable will create a new LookupTable. The 'inputFormat' is the format of input IQ samples. This must be either SampleFormatI8 or SampleFormatU8.
On the other end, the 'lookup' buffer is the data to place into the output buffer depending on the input samples. The 'lookup' buffer must be exactly 65536 samples long.
type PipeReadWriter ¶
type PipeReadWriter interface { PipeReader PipeWriter }
PipeReadWriter is the Read/Write interface exposed by a Pipe.
type PipeReader ¶
type PipeReader interface { ReadCloser // CloseWithError will Close the pipe with a specific Error rather than // the default ErrPipeClosed. This can be useful if code is expecting an // io.EOF, for instance. CloseWithError(error) error }
PipeReader is the Read interface exposed by the Pipe.
type PipeWriter ¶
type PipeWriter interface { WriteCloser // CloseWithError will Close the pipe with a specific Error rather than // the default ErrPipeClosed. This can be useful if code is expecting an // io.EOF, for instance. CloseWithError(error) error }
PipeWriter is the Write interface exposed by the Pipe.
type ReadCloser ¶
ReadCloser is the interface that groups the basic Read and Close methods.
func ReaderWithCloser ¶
func ReaderWithCloser(r Reader, c func() error) ReadCloser
ReaderWithCloser will add a closer to a reader to make an sdr.ReadCloser
type ReadClosers ¶
type ReadClosers []ReadCloser
ReadClosers is a collection of ReadCloser objects.
func (ReadClosers) Close ¶
func (rcs ReadClosers) Close() error
Close will close all the ReadClosers.
func (ReadClosers) Readers ¶
func (rcs ReadClosers) Readers() Readers
Readers will return the ReadClosers as a Reader slice.
func (ReadClosers) SampleFormat ¶
func (rcs ReadClosers) SampleFormat() SampleFormat
SampleFormat returns the IQ format of the Readers.
func (ReadClosers) SampleRate ¶
func (rcs ReadClosers) SampleRate() uint
SampleRate returns the number of IQ samples per second.
type ReadWriteCloser ¶
ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type Reader ¶
type Reader interface { // Read IQ Samples into the target Samples buffer. There are two return // values, an int representing the **IQ** samples (not bytes) read by this // function, and any error conditions encountered. Read(Samples) (int, error) // Get the sdr.SampleFormat SampleFormat() SampleFormat // SampleRate will get the number of samples per second that this // stream is communicating at. SampleRate() uint }
Reader is the interface that wraps the basic Read method.
func ByteReader ¶
func ByteReader( r io.Reader, byteOrder binary.ByteOrder, samplesPerSecond uint, sf SampleFormat, ) Reader
ByteReader will wrap an io.Reader, and decode encoded IQ data from raw bytes into an sdr.Samples object.
func MultiReader ¶
MultiReader will act like `cat`, passing Reads through from one reader to the next until the end of the streams.
An io.EOF will be returned if they all return EOF, otherwise the first error to be hit will be returned.
type Readers ¶
type Readers []Reader
Readers represents a collection of Readers.
func (Readers) SampleFormat ¶
func (rs Readers) SampleFormat() SampleFormat
SampleFormat returns the IQ Format of the Readers.
func (Readers) SampleRate ¶
SampleRate returns the number of samples per second in the stream.
type Receiver ¶
type Receiver interface { Sdr // StartRx will listen on the configured frequency and start to stream iq // samples to be read out of the provided Reader. It's absolutely // imperative that the consuming code will actively consume from the // Reader, or backlogged samples can result in dropped samples or other // error conditions. Those error conditions are not defined at this time, // but may break in wildly unpredictable ways, since the time sensitive // SDR code may hang waiting for reads. StartRx() (ReadCloser, error) }
Receiver is an "extension" of the SDR Interface, it contains all the common control methods, plus additional bits to receive iq data from the airwaves.
This can either be used as part of a function signature if your code really only needs to receive, or as part of a type-cast to determine if the SDR is capable of receiving.
type SampleFormat ¶
type SampleFormat uint8
SampleFormat is an ID used throughout go-rf to uniquely identify what type the IQ samples are in. This allows code to quickly compare to see if two types are talking about the same type or not, without resoring to expensive read operations and type casting.
const ( // SampleFormatC64 indicates that SamplesC64 will be handled. See // sdr.SamplesC64 for more information. SampleFormatC64 SampleFormat = 1 // SampleFormatU8 indicates that SamplesU8 will be handled. See // sdr.SamplesU8 for more information. SampleFormatU8 SampleFormat = 2 // SampleFormatI16 indicates that SamplesI16 will be handled. See // sdr.SamplesI16 for more information. SampleFormatI16 SampleFormat = 3 // SampleFormatI8 indicates that SamplesI8 will be handled. See // sdr.SamplesI8 for more information. SampleFormatI8 SampleFormat = 4 )
func (SampleFormat) Size ¶
func (sf SampleFormat) Size() int
Size will return the number of bytes that are needed to represent a single phasor, both real and imaginary.
func (SampleFormat) String ¶
func (sf SampleFormat) String() string
String returns the format name as a human readable String.
type Samples ¶
type Samples interface { // Format returns the type of this vector, as exported by the SampleFormat // enum. Format() SampleFormat // Size will return the size of this sdr.Samples in *bytes*. This is used // when your code needs to be aware of the underlying storage size. This // should usually only be used at i/o boundaries. Size() int // Length will return the number of IQ samples in this vector of Samples. // // This is the count of real and imaginary pairs, so in the case // of the U8 type, this will be half the size of the vector. // // This function is usually the correct one to use when processing // sample information. Length() int // Slice will return a slice of the sample buffer from the provided // starting position until the ending position. The returned value is // assumed to be a slice, which is to say, mutations of the returned // Samples will modify the slice from whence it came. // // samples.Slice(0, 10) is assumed to be the same as samples[:10], except // it does not require the typecast to the concrete type implementing // this interface. Slice(int, int) Samples }
Samples represents a vector of IQ data.
This type is an interface_ and not a struct or typedef to complex64 because this allows the generic IQ helpers in this package to operate on the native format of the SDR without requiring expensive conversions to other types.
This package contains 4 Samples implementations:
- SamplesU8 - interleaved uint8 values
- SamplesI8 - interleaved int8 values
- SamplesI16 - interleaved int16 values
- SamplesC64 - vector of complex64 values (interleaved float32 values)
This should cover most common SDRs, but if you're handing a type of IQ data that is not supported, you may either implement the Samples type yourself along with the required interface points, convert to a format this library supports, or send a PR to this library adding support to this format.
func MakeSamples ¶
func MakeSamples(sampleFormat SampleFormat, sampleSize int) (Samples, error)
MakeSamples will create a buffer of a specified size and type. This will return a newly allocated slice of Samples. This function is used when the code processing Samples is fairly generic, to avoid switches on type, and to reduce the cost of adding new sample formats.
If your code is specific to a given Sample format, for instance if your code only supports SamplesC64, it's fine to not use this function at all.
type SamplesC64 ¶
type SamplesC64 []complex64
SamplesC64 indicates that the samples are in a complex64 number, which is itself two interleaved float32 numbers, the i and q value. In memory, this is the same thing as interleaving i and q values in a float array.
This is the format that is most useful to process iq data in from a mathematical perspective, and is going to be the most common type to work with when writing signal processing code.
func (SamplesC64) Add ¶
func (s SamplesC64) Add(c []complex64) error
Add will conduct a complex addition of each phasor in this buffer by a provided complex number 'c', writing results to 'dst'.
func (SamplesC64) Format ¶
func (s SamplesC64) Format() SampleFormat
Format returns the type of this vector, as exported by the SampleFormat enum.
func (SamplesC64) Length ¶
func (s SamplesC64) Length() int
Length will return the number of IQ samples in this vector of Samples.
This is the count of real and imaginary pairs, so in the case of the U8 type, this will be half the size of the vector.
This function is usually the correct one to use when processing sample information.
func (SamplesC64) Multiply ¶
func (s SamplesC64) Multiply(c complex64)
Multiply will conduct a complex multiplication of each phasor in this buffer by a provided complex number 'c'.
func (SamplesC64) Scale ¶
func (s SamplesC64) Scale(r float32)
Scale will multiply each I and Q value by the provided real value 'r'. This will *not* do a complex multiplication, this is the same as if each phasor had their real and imag parts multiplied by the provided real value 'r'.
func (SamplesC64) Size ¶
func (s SamplesC64) Size() int
Size will return the size of this sdr.Samples in *bytes*. This is used when your code needs to be aware of the underlying storage size. This should usually only be used at i/o boundaries.
func (SamplesC64) Slice ¶
func (s SamplesC64) Slice(start, end int) Samples
Slice will return a slice of the sample buffer from the provided starting position until the ending position. The returned value is assumed to be a slice, which is to say, mutations of the returned Samples will modify the slice from whence it came.
samples.Slice(0, 10) is assumed to be the same as samples[:10], except it does not require the typecast to the concrete type implementing this interface.
func (SamplesC64) ToI16 ¶
func (s SamplesC64) ToI16(out SamplesI16) (int, error)
ToI16 will convert the complex64 data to int16 data.
type SamplesI16 ¶
type SamplesI16 [][2]int16
SamplesI16 indicates that the samples are being sent as a vector of interleaved int16 integers. The values range from +32767 to -32768. 0 remains, well 0.
There are a few hazards for people working with this type at an IO boundary with an SDR (if you're consuming this data, you don't really have to care much - it's a number! numbers are great! Enjoy!).
Firstly, this type isn't really any particular device's native format, which may cause a bit of confusion, but it's close enough to be useful when reading from ADCs that have 12 or 14 bits of precision, since you don't want to align to non-8-bit boundaries while also maintaining sanity. As a result, working with this type can be a bit awkward when doing IO with an SDR, since you either have to align to the MSB or LSB. Some SDRs (I'm looking at you, PlutoSDR) expect their samples to be in both formats depending on direction.
If you are at the IO boundary with an SDR the correct format for this type is MSB aligned data. As soon as you convert the byte stream into this type, be sure it's immediately MSB aligned. If your SDR is giving you LSB aligned data, you may need to call ShiftLSBToMSBBits with the number of bits your data is in (for instance, `12` for a 12 bit ADC) to MSB align.
func (SamplesI16) Format ¶
func (s SamplesI16) Format() SampleFormat
Format returns the type of this vector, as exported by the SampleFormat enum.
func (SamplesI16) Length ¶
func (s SamplesI16) Length() int
Length will return the number of IQ samples in this vector of Samples.
This is the count of real and imaginary pairs, so in the case of the U8 type, this will be half the size of the vector.
This function is usually the correct one to use when processing sample information.
func (SamplesI16) ShiftLSBToMSBBits ¶
func (s SamplesI16) ShiftLSBToMSBBits(bits int)
ShiftLSBToMSBBits is a helper function to be used when the input data is not actually 16 bits. This is usually fine if the data is MSB aligned, since the range is still roughly the int16 max / min (just like, 8 off). However, if the data is LSB aligned, this is a major shitshow, since the max is no longer 2**16, the max is 2**12 or 2**14. Rather than make multiple sdr.Sample types for each ADC bit count, the SDR code should call ShiftLSBToMSBBits at the boundary to shift the data from LSB to MSB aligned to get full-range values.
The value `bits` is the number of bits the ADC sends. This will result in a bitshift of 16 - bits. So, if you have a 12 bit ADC, and this is invoked, each I and Q sample will be shifted left 4 bits, bringing the range from +/- 2047 to +/-32768.
This will mutate the buffer in place.
func (SamplesI16) Size ¶
func (s SamplesI16) Size() int
Size will return the size of this sdr.Samples in *bytes*. This is used when your code needs to be aware of the underlying storage size. This should usually only be used at i/o boundaries.
func (SamplesI16) Slice ¶
func (s SamplesI16) Slice(start, end int) Samples
Slice will return a slice of the sample buffer from the provided starting position until the ending position. The returned value is assumed to be a slice, which is to say, mutations of the returned Samples will modify the slice from whence it came.
samples.Slice(0, 10) is assumed to be the same as samples[:10], except it does not require the typecast to the concrete type implementing this interface.
func (SamplesI16) ToC64 ¶
func (s SamplesI16) ToC64(out SamplesC64) (int, error)
ToC64 will convert the int16 data to a vector of complex64 numbers.
type SamplesI8 ¶
type SamplesI8 [][2]int8
SamplesI8 indicates that the samples are being sent as a vector of interleaved int8 numbers, where -128 is -1, and 1 is 127.
This is the native format of the HackRF
func LookupTableIdentityI8 ¶
func LookupTableIdentityI8() SamplesI8
LookupTableIdentityI8 will return an identity SamplesI8 table, from 0 to max in index order. This can be used to apply a transform to (like Add, or Multiply).
func (SamplesI8) Format ¶
func (s SamplesI8) Format() SampleFormat
Format returns the type of this vector, as exported by the SampleFormat enum.
func (SamplesI8) Length ¶
Length will return the number of IQ samples in this vector of Samples.
This is the count of real and imaginary pairs, so in the case of the I8 type, this will be half the size of the vector.
This function is usually the correct one to use when processing sample information.
func (SamplesI8) Size ¶
Size will return the size of this sdr.Samples in *bytes*. This is used when your code needs to be aware of the underlying storage size. This should usually only be used at i/o boundaries.
func (SamplesI8) Slice ¶
Slice will return a slice of the sample buffer from the provided starting position until the ending position. The returned value is assumed to be a slice, which is to say, mutations of the returned Samples will modify the slice from whence it came.
samples.Slice(0, 10) is assumed to be the same as samples[:10], except it does not require the typecast to the concrete type implementing this interface.
func (SamplesI8) ToC64 ¶
func (s SamplesI8) ToC64(out SamplesC64) (int, error)
ToC64 will convert the int8 data to a vector of complex64 numbers.
type SamplesPool ¶
type SamplesPool struct {
// contains filtered or unexported fields
}
SamplesPool creates a dynamically sized buffer pool of a set size and sample format. This allows code to reuse buffers, and avoid allocations if a buffer is ready for use.
Under the hood this is a sync.Pool, but with some type-safe (well, as type safe as you can get by returning another interface type...) hooks to make this a bit more ergonomic to use.
func NewSamplesPool ¶
func NewSamplesPool(format SampleFormat, length int) (*SamplesPool, error)
NewSamplesPool will create a new SamplesPool that creates buffers of the provided sample format and length.
Under the hood this is a wrapped sync.Pool.
func (SamplesPool) Get ¶
func (sp SamplesPool) Get() Samples
Get will either return an unused buffer, or allocate a new one for your use.
The smallest size of a buffer returned will be the length passed to the NewSamplesPool constructor, of the provided SampleFormat.
func (SamplesPool) Put ¶
func (sp SamplesPool) Put(s Samples)
Put will return a buffer to the pool. In the future this is likely to panic if the buffer returned is not the same size as the buffer that was taken out, but since the size won't /shrink/, it's actually not that bad to deal with here.
type SamplesU8 ¶
type SamplesU8 [][2]uint8
SamplesU8 indicates that the samples are being sent as a vector of interleaved uint8 numbers, where 0 is -1, and 1 is 0xFF.
This type is very hard to process, since the 0 value is 127.5, which is not representable, but it's *very* effective to send data over a connection, since it's the most compact representation.
This is the native format of the rtl-sdr.
func LookupTableIdentityU8 ¶
func LookupTableIdentityU8() SamplesU8
LookupTableIdentityU8 will return an identity SamplesU8 table, from 0 to max in index order. This can be used to apply a transform to (like Add, or Multiply).
func (SamplesU8) Format ¶
func (s SamplesU8) Format() SampleFormat
Format returns the type of this vector, as exported by the SampleFormat enum.
func (SamplesU8) Length ¶
Length will return the number of IQ samples in this vector of Samples.
This is the count of real and imaginary pairs, so in the case of the U8 type, this will be half the size of the vector.
This function is usually the correct one to use when processing sample information.
func (SamplesU8) Size ¶
Size will return the size of this sdr.Samples in *bytes*. This is used when your code needs to be aware of the underlying storage size. This should usually only be used at i/o boundaries.
func (SamplesU8) Slice ¶
Slice will return a slice of the sample buffer from the provided starting position until the ending position. The returned value is assumed to be a slice, which is to say, mutations of the returned Samples will modify the slice from whence it came.
samples.Slice(0, 10) is assumed to be the same as samples[:10], except it does not require the typecast to the concrete type implementing this interface.
func (SamplesU8) ToC64 ¶
func (s SamplesU8) ToC64(out SamplesC64) (int, error)
ToC64 will convert the uint8 data to a vector of complex64 numbers.
type Sdr ¶
type Sdr interface { // Close will free any resources held by the SDR object, and disconnect // from the hardware, if applicable. After this call, it's assumed // that any further function calls become very undefined behavior. Close() error // SetCenterFrequency will set the center of the hardware frequency to a // specific frequency in Hz. SetCenterFrequency(rf.Hz) error // GetCenterFrequency will get the centered hardware frequency, in Hz. GetCenterFrequency() (rf.Hz, error) // SetAutomaticGain will let the SDR take care of setting the gain as // required. SetAutomaticGain(bool) error // GetGainStages will return all gain stages that are supported by this // SDR, sorted in order from the antenna backwards to the USB port. GetGainStages() (GainStages, error) // GetGain will return the Gain set for the specific Gain stage. GetGain(GainStage) (float32, error) // SetGain will set the Gain for a specific Gain stage. SetGain(GainStage, float32) error // SetSampleRate will set the number of samples per second that this // device should be sending back to us. A lower number usually gives us less // RF bandwidth, and a higher number may result in corruption (in the case // of the rtl-sdr) or dropped samples (in the case of the Pluto and friends). SetSampleRate(uint) error // GetSampleRate will get the number of samples per second that this // device is configured to be sending back to us. GetSampleRate() (uint, error) // SampleFormat returns the type of this vector, as exported by the // SampleFormat enum. SampleFormat() SampleFormat // HardwareInfo will return information about the connected SDR. HardwareInfo() HardwareInfo }
Sdr is the generic interface that all SDRs will expose. Since this covers an extensive amount of functionality, it's expected some devices will not support a given function. If that happens, the error that must be returned is an ErrNotSupported.
A specific SDR may support additional functionality, so be sure to check the documentation of the underlying SDR implementation as well!
type Transceiver ¶
type Transceiver interface { Sdr Receiver Transmitter }
Transceiver is an "extension" of the SDR Interface, it contains all the common control methods, plus additional bits to both transmit and receive iq data.
This can either be used as part of a function signature if your code really only needs to both receive and transmit, or as part of a type-cast to determine if the SDR is capable of both receiving and transmitting.
type Transmitter ¶
type Transmitter interface { Sdr // StartTx will begin to transmit on the configured frequency, and start to // stream iq samples written to the underlying hardware to be sent over // the air. // // It's absolutely imperative that the producing code feed iq samples into // the transmitter at the specified rate, or bad things may happen and // cause wildly unpredictable things. StartTx() (WriteCloser, error) }
Transmitter is an "extension" of the SDR Interface, it contains all the common control methods, plus additional bits to transmit iq data over the airwaves.
This can either be used as part of a function signature if your code really only needs to transmit, or as part of a type-cast to determine if the SDR is capable of transmitting.
type WriteCloser ¶
WriteCloser is the interface that groups the basic Read and Close methods.
func WriterWithCloser ¶
func WriterWithCloser(w Writer, c func() error) WriteCloser
WriterWithCloser will add a closer to a writer to make an sdr.WriteCloser
type Writer ¶
type Writer interface { // Write IQ Samples into the target Samples buffer. There are two return // values, an int representing the **IQ** samples (not bytes) read by this // function, and any error conditions encountered. Write(Samples) (int, error) // Get the sdr.SampleFormat SampleFormat() SampleFormat // SampleRate will get the number of samples per second that this // stream is communicating at. SampleRate() uint }
Writer is the interface that wraps the basic Write method.
func ByteWriter ¶
func ByteWriter( w io.Writer, byteOrder binary.ByteOrder, samplesPerSecond uint, sf SampleFormat, ) Writer
ByteWriter will wrap an io.Writer, and write encoded IQ data as a series of raw bytes out.
func Discard ¶
func Discard(sampleRate uint, sampleFormat SampleFormat) Writer
Discard will accept writes, and store them safely... nowhere. This is a highly optimized and very fast writer. Just don't expect to get your data back.
func MultiWriter ¶
MultiWriter creates a writer that duplicates its writes to all the provided writers, similar to the Unix tee(1) command, or io.MultiWriter.
This has the same behavior as an io.MultiWriter, but will copy between IQ streams.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package airspyhf contains an sdr.Sdr implementation for Airspy SDRs
|
Package airspyhf contains an sdr.Sdr implementation for Airspy SDRs |
Package debug contains helpers which are useful when trying to track down errors or current state from the hz.tools/sdr package and package implementations/drivers.
|
Package debug contains helpers which are useful when trying to track down errors or current state from the hz.tools/sdr package and package implementations/drivers. |
Package fft contains a common interface to perform FFTs between frequency and time-Series complex data.
|
Package fft contains a common interface to perform FFTs between frequency and time-Series complex data. |
Package hackrf contains the HackRF driver for hz.tools/sdr
|
Package hackrf contains the HackRF driver for hz.tools/sdr |
Package internal contains some pointy knobs which are explicitly not part of the external API.
|
Package internal contains some pointy knobs which are explicitly not part of the external API. |
simd
Package simd contains some IQ specific helpers which have both a native go as well as tuned implementation depending on the platform.
|
Package simd contains some IQ specific helpers which have both a native go as well as tuned implementation depending on the platform. |
warning
Package warning contains helpers to mark functions as unsafe, experimental or deprecated.
|
Package warning contains helpers to mark functions as unsafe, experimental or deprecated. |
Package mock contains a sdr.Sdr implementation that is suitable for testing.
|
Package mock contains a sdr.Sdr implementation that is suitable for testing. |
Package pluto contains an sdr.Sdr implementation for the Analog Devices ADALM-PLUTO SDR.
|
Package pluto contains an sdr.Sdr implementation for the Analog Devices ADALM-PLUTO SDR. |
iio
Package iio contains a Go wrapper around libiio, to talk with devices such as the Analog Devices ADALM-PLUTO.
|
Package iio contains a Go wrapper around libiio, to talk with devices such as the Analog Devices ADALM-PLUTO. |
Package rtl contains an sdr.Sdr implementation for rtlsdr based SDRs.
|
Package rtl contains an sdr.Sdr implementation for rtlsdr based SDRs. |
e4k
Package e4k contains code to translate gains in dB to values to be sent to the e4k's 6-stage IF gain controls.
|
Package e4k contains code to translate gains in dB to values to be sent to the e4k's 6-stage IF gain controls. |
kerberos
Package kerberos contains the KerberosSDR driver for hz.tools/sdr
|
Package kerberos contains the KerberosSDR driver for hz.tools/sdr |
kerberos/internal
Package internal contains kerberos specific internal bits of API.
|
Package internal contains kerberos specific internal bits of API. |
Package rtltcp contains an sdr.Sdr implementation to talk to an rtl_tcp server.
|
Package rtltcp contains an sdr.Sdr implementation to talk to an rtl_tcp server. |
Package stream contains a number of helpers to process sdr.Reader or sdr.Writers.
|
Package stream contains a number of helpers to process sdr.Reader or sdr.Writers. |
Package testutils contains some helpers for testing SDR code and other implementations.
|
Package testutils contains some helpers for testing SDR code and other implementations. |
Package uhd contains the UHD USRP driver for hz.tools/sdr
|
Package uhd contains the UHD USRP driver for hz.tools/sdr |
Package yikes contains helpers which *are* exported for other packages, but the use of these helpers is *strongly* discouraged, and is very unsafe.
|
Package yikes contains helpers which *are* exported for other packages, but the use of these helpers is *strongly* discouraged, and is very unsafe. |