Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
SimTX Wiki
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
SimTX API v24/Quick start
(section)
Page
Discussion
English
Read
Edit
Edit source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
Edit source
View history
General
What links here
Related changes
Special pages
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
== Transmitting == Transmitting requires an account with the <code>game.transmit</code> [[SimTX API v24/Permissions|permission]]. For a script, the simplest credential is a personal credential created on the account website. Put it in the <code>SIMTX_TOKEN</code> environment variable. Interactive software should use [[SimTX API v24/Device login|device login]] instead. The program below obtains a [[SimTX API v24/Game tickets|game ticket]], opens a transmitter session on 14.230 MHz USB at 100 W, waits for admission, transmits a 1 kHz tone for ten seconds while printing telemetry, and goes off air. <syntaxhighlight lang="python" line> import json import math import os import socket import ssl import struct import threading import time import urllib.request AUTH = "https://auth.simtx.net" TOKEN = os.environ["SIMTX_TOKEN"] # a personal credential (stx_pc1_...) SERVER_ID = "argon" HOST, PORT = "argon.simtx.net", 5556 RATE = 48_000 def post_json(path, body): request = urllib.request.Request( AUTH + path, data=json.dumps(body).encode(), headers={"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"}, ) with urllib.request.urlopen(request, timeout=10) as response: return json.load(response) def read_exact(sock, n): buf = bytearray() while len(buf) < n: chunk = sock.recv(n - len(buf)) if not chunk: raise ConnectionError("server closed the connection") buf += chunk return bytes(buf) def write_utf(s): data = s.encode("utf-8") return struct.pack(">H", len(data)) + data def read_utf(sock): (n,) = struct.unpack(">H", read_exact(sock, 2)) return read_exact(sock, n).decode("utf-8", "replace") def field(field_id, payload): return struct.pack(">Bi", field_id, len(payload)) + payload def f32(value): return struct.pack(">f", value) def transmitter_request(): antenna = write_utf("DIPOLE") + struct.pack(">ffffi", 10.0, 0.0, 0.0, 20.0, 0) fields = [ field(1, b"N0CALL"), # callsign field(2, struct.pack(">ddd", 47.4979, 19.0402, 120.0)), # location field(3, antenna), # antenna field(4, b"USB"), # modulation field(5, f32(14_230_000.0)), # frequencyHz field(6, f32(100.0)), # powerWatts field(7, b"AUDIO_STREAM"), # sourceType field(8, struct.pack(">i", RATE)), # sampleRate field(9, f32(1.0)), # audioGain field(10, f32(2_700.0)), # txBandwidthHz field(11, f32(300.0)), # audioLowCutHz field(12, f32(0.0)), # toneHz field(13, f32(1.0)), # dutyCycle ] return b"\x02" + struct.pack(">B", len(fields)) + b"".join(fields) ticket = post_json("/api/v1/game-tickets", {"serverId": SERVER_ID, "role": "tx-audio"})["ticket"] raw = socket.create_connection((HOST, PORT), timeout=10) raw.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock = ssl.create_default_context().wrap_socket(raw, server_hostname=HOST) # Handshake: role 2 (TX_AUDIO), authKind 1 (GAME_TICKET) sock.sendall(struct.pack(">iibb", 0x53494D54, 24, 2, 1) + write_utf(ticket)) packet_id, status = struct.unpack(">BB", read_exact(sock, 2)) message = read_utf(sock) if read_exact(sock, 1)[0]: read_exact(sock, 16) handle = read_utf(sock) read_utf(sock) permissions = [read_utf(sock) for _ in range(read_exact(sock, 1)[0])] read_exact(sock, 16) if status != 0: raise SystemExit(f"handshake rejected ({status}): {message}") print("signed in as", handle, "with", permissions) sock.sendall(transmitter_request()) # Admission: TxQueue (4) until TxAccept (3), or TxReject (5) while True: packet_id = read_exact(sock, 1)[0] if packet_id == 3: print("on air") break if packet_id == 4: ahead, total = struct.unpack(">ii", read_exact(sock, 8)) print(f"queued: {ahead} ahead of {total}") elif packet_id == 5: raise SystemExit("rejected: " + read_utf(sock)) else: raise SystemExit(f"unexpected packet {packet_id}") sock.settimeout(None) def read_telemetry(): try: while True: packet_id = read_exact(sock, 1)[0] if packet_id == 7: values = struct.unpack(">qq16f?", read_exact(sock, 8 + 8 + 16 * 4 + 1)) forward, vswr, buffer_s = values[4], values[7], values[12] print(f"forward {forward:6.1f} W SWR {vswr:4.2f} buffer {buffer_s:4.2f} s") elif packet_id == 6: print("kicked:", read_utf(sock)) return else: print("unexpected packet", packet_id) return except (ConnectionError, OSError): return threading.Thread(target=read_telemetry, daemon=True).start() # Ten seconds of a 1 kHz tone, sent in real time in 20 ms blocks block = RATE // 50 start = time.monotonic() for n in range(500): samples = [0.5 * math.sin(2 * math.pi * 1000 * (n * block + i) / RATE) for i in range(block)] sock.sendall(b"\x03" + struct.pack(">i%df" % block, block, *samples)) time.sleep(max(0.0, start + (n + 1) * 0.02 - time.monotonic())) sock.shutdown(socket.SHUT_WR) sock.close() print("off air") </syntaxhighlight> Things to notice: * Only the 13 required fields of the [[SimTX API v24/Packets/TransmitterSessionRequest|TransmitterSessionRequest]] are sent. The server uses defaults for the transmitter imperfections (fields 14 to 28). * The sender paces itself to real time with a deadline per block. Sending as fast as possible would fill the server's buffer, and anything beyond the allowed burst would be discarded. The <code>buffer</code> value in the telemetry should stay roughly constant. * Telemetry is read on a separate thread. A client that stops reading is disconnected after five seconds. * There is no "stop transmitting" packet. Closing the socket takes the station off air.
Summary:
Please note that all contributions to SimTX Wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
SimTX Wiki:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)