Subsystem Search

substation.osc_sender

The API reference for OscEventSender.

OscEventSender

class OscEventSender(
    host: str | None = '127.0.0.1',
    port: int = 9000,
    sampler_host: str | None = None,
    sampler_port: int = 9002,
)

Send the scanner's events as OSC messages, to a sequencer and, optionally, a sampler.

osc = substation.osc_sender.OscEventSender(
        host="127.0.0.1", port=9000,
        sampler_host="127.0.0.1", sampler_port=9002,
)
osc.attach(scanner)

The messages it sends:

A sequencer receives nothing when host is None, and a sampler receives nothing unless sampler_host is set.

ctcss_hz and dcs_code carry any subaudible tone detected on the activation. OSC has no null, so 0.0 and 0 mean that no tone was detected: CTCSS tones start at 67 Hz, and no DCS code is 0. A DCS code is octal, and dcs_code is its integer value, so DCS 023 arrives as 19; format it in octal, as f"{dcs_code:03o}", to show it as a radio does. Tone detection has not yet been thoroughly tested with real radios.

Each message goes over UDP without waiting for a reply. A send that fails, such as one to a host that cannot be reached, is logged as a warning and never stops the scan.

A scanner builds a sender of its own when its configuration's osc settings turn sending on, so a script needs this class only to send somewhere those settings do not.

Set up the sender, opening a UDP client for each receiver.

Parameters

Raises

Members: attach, on_recording_saved, on_state_change

OscEventSender.on_state_change

OscEventSender.on_state_change(
    band_name: str,
    channel_index: int,
    is_active: bool,
    snr_db: float,
    ctcss_hz: float | None = None,
    dcs_code: int | None = None,
) -> None

Send /radio/state to the sequencer: the handler for channel_state that attach() subscribes.

The message's arguments are band_name, channel_index, is_active as 1 or 0, snr_db, ctcss_hz and dcs_code, with 0.0 and 0 for no tone. Nothing is sent when the sender has no sequencer.

It runs on the scanner's event loop, so it returns quickly. A failed send is caught and logged; anything else is a fault in Substation, and reaches the scanner, which logs it with its traceback.

Parameters

OscEventSender.on_recording_saved

OscEventSender.on_recording_saved(
    band_name: str,
    channel_index: int,
    file_path: str,
    ctcss_hz: float | None = None,
    dcs_code: int | None = None,
) -> None

Send /radio/recording to the sequencer, and /sample/import to the sampler, to each that the sender has: the handler for recording_saved that attach() subscribes.

/radio/recording's arguments are band_name, channel_index, file_path, ctcss_hz and dcs_code, with 0.0 and 0 for no tone. /sample/import carries only file_path; the sampler can read a tone from the recording's metadata.

It runs on the scanner's event loop.

Parameters

OscEventSender.attach

OscEventSender.attach(scanner: substation.scanner.RadioScanner) -> None

Subscribe this sender to a scanner's channel_state and recording_saved events, so each is sent as it happens.

Parameters