====== EMSCOPE WEBSOCKET PROTOCOL DOCUMENTATION ====== The Emscope software consists in a //WebSocket// server listening on //port 8010// of the machine and a //Web Server// listening on the standard HTTP //port 80// that serves the front end html and related files. To configure, parametrize the measurements and manage the standards, a //JSON// style command set is used through a //WebSocket// connection. ===== - First connection ===== Upon connection, the server does not send any data automatically. The client must first send a //session_UUID// message to activate itself. The server responds to this with device info: { “SN”: “123456789”, “measurement_uncertainty”: "0.5 dB", “num_points”: 8192 } After that, the client must send //trace_type// to enable measurement data streaming. See section 8 for details on session_UUID. ===== - Default values ===== The default values for measurements are: * Channel: LG * Detector: PK * Tracer: Clear/write * Units: dbuv * RWB: 9 * Subranges: 10 * Margin: 10 * Active: true * Mode: circuit * input_attenuator: auto * reference_level: 100 (dbuV) * average: 10 ===== - Commands and responses ===== The following parameters can be sent to configure the device: ^ Name ^ Parameter ^ Type ^ Default ^ | Channel | measure_channel | "lg", "ng", "cm", "dm" (RX2) / "l1", "l2", "l3", "n" (RX4) | “lg” | | Detector type | detector_type | “pk”, “qp”, “av” | “pk” | | Trace | trace_type | “clearwrite”, “maxhold”, “minhold”, “freeze”, "average" | “clearwrite” | | RBW | rbw | "200","9","120" (CISPR), "1", "10" (MIL), "200_9", "1_10" (dual band) | "9" | | Average (for trace average) | average | integer (10 - 20) | 10 | | Mode | mode | “circuit”, “modal” | “circuit” | | Reference level| reference_level | integer | 100 | | Input attenuator| input_attenuator | {0..78} (integer), “auto” | "auto" | | Amplitude units| amp_units | “dbm”, “dbmv”, “dbuv”, “watts”, “volts” | "dbuv" | | Serial number | SN | string | | | Sweep time | sweep_time | {1..15} (float) | 1 | | External loss attenuator | external_loss | External loss name (string) | none | ===== - Changing RBW ===== After setting the RBW we shouldn't send any commands until the device it sends back the RBW value. This is so because a hot-change of firmware, that typically takes around 3.5 seconds, is required. To set a new RBW, send a **rbw** field with the string corresponding to the RBW: { "rbw": "9", "threephase": false } RBW table: ^ Value ^ RBW ^ Frequency ^ Standard ^ | 200 | 200 Hz | 9 kHz - 150 kHz | CISPR 16-1-1 | | 9 | 9 kHz | 150 kHz - 30 MHz | CISPR 16-1-1 | | 120 | 120 kHz | 30 MHz - 110 MHz | CISPR 16-1-1 | | 1 | 1 kHz | 10 kHz - 150 kHz | MIL-STD-461 | | 10 | 10 kHz | 150 kHz - 30 MHz | MIL-STD-461 | | 200_9 | 200 Hz / 9 kHz | 9 kHz - 30 MHz | Dual-band | | 1_10 | 1 kHz / 10 kHz | 10 kHz - 30 MHz | Dual-band | threephase (boolean, default false) — set to true for three-phase (RX4) instruments and EMI_3P. Must be sent together with rbw in the same message. Sending rbw without threephase defaults to false. ===== - Additional configuration fields ===== The following fields can be sent as individual JSON messages at any time after the RBW has been set. ==== display_range ==== Restricts the frequency window of the data sent by the server to a sub-range of the active band. Send: { "display_range": [from_hz, to_hz] } Both values are absolute frequencies in Hz and must lie within the band of the currently active RBW. Example — receive only the 150 kHz – 30 MHz portion of the 9 kHz band: { "display_range": [150000, 30000000] } To restore the full band, send the full frequency limits of the active band. ==== visible ==== Controls whether the trace is shown on the instrument's front panel display. Does not affect data transmission to WebSocket clients. Send: { "visible": true } { "visible": false } ==== freeze (trace_type) ==== In addition to the trace types listed in section 3, the value "freeze" is also accepted: { "trace_type": "freeze" } The instrument continues sweeping internally but stops updating the displayed trace. Sending a different trace_type unfreezes it. ===== - Initial configuration ===== A typical initial configuration parameters example would be: { "detector_type": "pk", "measure_channel": "lg", "trace_type": "clearwrite", "amp_units": "dbmv", "rbw": "9", "reference_level": 70, "input_attenuator": "auto", "sweep_time": "1" } ===== - Receiving measurements ===== To start receiving measurements, we should at least send the **trace_type**. The measurements are sent in a **values** object immediately after the measurement is done. The result contains an array of arrays recodesenting the frequency and the read value ( **[frequency_Hz, value]** ). The first element is the absolute frequency in Hz (not an index). The amplitude is in the units configured via "amp_units". Another key, **overload**, is true if the ADC saturated during this sweep. When overload occurs, reduce "reference_level" or set "input_attenuator" to a higher fixed value. Example: { "values": [ [ 0, 0.024687 ], [ 7326, 0.014846 ], [ 10000, 0.00344 ], ... ], "overload": false } When "input_attenuator" is set to "auto", the server echoes the resolved hardware attenuation value back to the client in each measurement message under the key "input_attenuator": { "values": [...], "overload": false, "input_attenuator": 30 } This allows clients to log or display the actual attenuation applied. ===== - Locking the device to the current session ===== Sending //session_UUID// is required — the server will not activate the client or send any data until it receives this message. It also serves as a session lock: if another client already holds the lock with a different UUID, the server closes the connection with close code 4003. Use the same UUID for subsequent reconnections to the same session. { "session_UUID": "12345qwerty" } The server responds immediately with device info (SN, MAC, SFP_SN). Once this exchange is complete, the client is active and can send configuration messages. ===== - Standards (or Limits) ===== ==== - Getting the standards list ==== Send the field **get_standards** with a value of "true": { "get_standards": true } It will return a **standards** object with an array of standards with the following format: "Standard name": [ { "rbw": value, "data": [ From frequency (Mhz), To frequency (Mhz), From Quasi-peak (dBuV), To Quasi-peak (dBuV), Average from Quasi-peak (dBuV), Average to Quasi-peak (dBuV) ], ... } ] For example: { "standards": [ { "Standard One": { "rbw": "9", "data": [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], ... ] } }, { "Standard Two": { "rbw": "200", "data": [ [21, 22, 23, 24, 25, 26], [27, 28, 29, 210, 211, 212], ... ] } }, ... ] } ==== - Creating a standard ==== To create a standard, send a **name** field with the name of the new standard, a **modify** field set to "false" and a **values** array in the same format we described before when receiving the standards list. { "name":"New standard", "modify":false, "standard_rbw": "10", "values":[ ["1","2","3","4","5","6"], ["11","12","13","14","15","16"], ... ] } ==== - Editing a standard ==== Send the same values as creating it, but with the field //modify// set to //true// and an additional field, //original_name//, just in case we wanted to rename the original //name//, i.e: { "original_name": "New standard", "name":"Modified standard", "modify":true, "standard_rbw": "1", "values":[ ["21","22","23","24","25","26"], ["11","12","13","14","15","16"], ... ] } ==== - Deleting a standard ==== Simply send a **delete_standard** with the name of the standard, i.e: { "delete_standard": "Standard One" } ==== - Resetting standards to factory defaults ==== Simply send a **reset_standards** with a value of "true": { "reset_standards": true } ===== - Sessions (or Presets) ===== ==== - Getting the sessions list ==== Send the field **get_sessions** with a value of "true": { "get_sessions": true } It will return a **sessions** object with an array of sessions with the following format: [ "Session name": [BLOB DATA], ... ] For example: { "sessions": [ { "Session One": "{\"PROGRAM_VERSION\":1.25,\"id_counter\":2,\"current_center_freq_units\":\"mhz\",\"current_start_freq_units\":\"mhz\",\"current_stop_freq_units\":\"mhz\",\"current_span_units\":\"mhz\",\"current_center_frequency\":15000000,\"current_start_frequency\":0,\"current_stop_frequency\":30000000,\"current_span\":30000000,\"measure_mode\":\"circuit\",\"current_display_scale\":0,\"aDetectors\":[{\"type\":\"normal\",\"measure_channel\":\"lg\",\"trace_type\":\"clearwrite\",\"detector_type\":\"pk\",\"amp_units\":\"dbuv\",\"color\":\"red\",\"hidden\":false,\"standard\":null,\"id\":0,\"sweep_time\":1},{\"type\":\"normal\",\"measure_channel\":\"lg\",\"trace_type\":\"clearwrite\",\"detector_type\":\"pk\",\"amp_units\":\"dbuv\",\"color\":\"red\",\"hidden\":false,\"standard\":null,\"id\":1}],\"reference_level\":100,\"reference_level_volts\":0.1,\"amplitude_step\":10,\"aCurrentFreq\":[150000,30000000],\"aCurrentSpan\":[0,100],\"aMarkers\":[[],[]],\"aMMarkers\":[],\"aStandards\":{\"CISPR 22 CLASS A\":[[\"0.15\",\"0.5\",\"79\",\"79\",\"66\",\"66\"],[\"0.5\",\"30\",\"73\",\"73\",\"60\",\"60\"]],\"CISPR 22 CLASS B\":[[\"0.15\",\"0.5\",\"66\",\"56\",\"56\",\"46\"],[\"0.5\",\"5\",\"56\",\"56\",\"46\",\"46\"],[\"5\",\"30\",\"60\",\"60\",\"50\",\"50\"]]},\"aStandardsQuasiPeak\":{},\"aStandardsAverage\":{},\"active_tab\":1,\"ZERO_SPAN_MODE\":false,\"amp_units\":\"dbuv\",\"rbw\":\"9\",\"input_attenuator\":\"auto\",\"sweep_time\":1,\"current_color_theme\":\"dark\"}", ... ] }, { "Session Two": "......", ... ] }, ... ] } ==== - Creating a session ==== To create a session, send a **session_name** field with the name of the new session, a **modify** field set to "false" and a **session_values** array in the same format we described before when receiving the sessions list. { "session_name":"New session", "modify":false, "session_values": ".....", ... ] } ==== - Editing a session ==== Send the same values as creating it, but with the field //modify// set to //true// and an additional field, //original_session//, just in case we wanted to rename the original //name//, i.e: { "original_session": "New session", "session_name":"Modified session", "modify":true, "values": "......", ... ] } ==== - Deleting a session ==== Simply send a **delete_session** with the name of the session, i.e: { "delete_session":"Session One" } ==== - Deleting all sessions ==== Simply send a **reset_sessions** with a value of "true": { "reset_sessions": true } ===== - External loss attenuators ===== ==== - Getting the external loss attenuators list ==== Send the field **get_external_losses** with a value of "true": { "get_external_losses": true } It will return a **external_losses** object with an array of external loss attenuators with the following format: "External loss attenuator name": [ [ Frequency (Mhz), Line (dB), Neutral (dB) ], [ Frequency (Mhz), Line (dB), Neutral (dB) ], ... ] For example: { "external_losses": [ { "External loss attenuator 1": [ [ 1, 2, 3 ], [ 2, 3, 4 ], ... ] }, { "External loss attenuator 2": [ [ 10, 20, 30 ], [ 20, 30, 40 ], ... ] }, ... ] } ==== - Creating an external loss attenuator ==== To create an external loss attenuator, send an **external_loss_name** field with the name of the new external loss attenuator, a **modify** field set to "false" and a **values** array in the same format we described before when receiving the external loss attenuators list. { "external_loss_name":"New External loss attenuator", "modify":false, "values":[ ["1","2","3"], ["10","20","30"], ... ] } ==== - Editing an external loss attenuator ==== Send the same values as creating it, but with the field //modify// set to //true// and an additional field, //original_external_loss_name//, just in case we wanted to rename the original //external_loss_name//, i.e: { "original_external_loss_name": "New External loss attenuator", "external_loss_name":"Modified External loss attenuator", "modify":true, "values":[ ["11","22","33"], ["21","22","23"], ... ] } ==== - Deleting an external loss attenuator ==== Simply send a **delete_external_loss** with the name of the external loss attenuator, i.e: { "delete_external_loss": "External loss attenuator 1" } ===== - Report lists ===== To generate a report list, we must send 3 values: //standard// (the standard name string), //subranges// (number of subranges, an integer) and //margin// (the margin, an integer), i.e: { "standard": "One standard", "subranges": 10, "margin": 10 } The answer to the request will be a boolean //frase// and a //report// object with an array of arrays in the following order: [ Marker (integer), Frequency in Mhz (float), Peak level in dBuV (float), Quasi-Peak level in dBuV (float), Quasi-Peak limit in dBuV (float), Distance to Quasi-Peak limit [dB] (float), Average level in dBuv (float), Average limit in dBuV (float), Distance to Average limit [dB] (float), Channel (string), Compliance (PASS/FAIL) ] The //frase// value is **true** if at least one of the reported emissions is within margin dB of its applicable limit (i.e. the result is close to the pass/fail boundary). **false** otherwise. For example: { "report": [ [ 1, 5, 116.964216, 116.418941, 119.000000, 10.000000, 116.480995, 109.000000, 10.000000, "L", "PASS" ], [ 2, 6, 313.963336, 133.418211, 1123440000, 111220000, 334.440995, 239.020000, 30.000000, "N", "FAIL" ], ... ], "frase": true } ===== - Keepalive ===== The server sends periodic keepalive messages to detect disconnected clients: { "ping": true } The client MUST reply immediately with: { "pong": true } If the client does not respond within the timeout, the server will close the connection. Clients that use the official Python API (EmscopeConnection) have this handled automatically. ===== - Miscellaneous ===== ==== - Getting the activated licenses ==== Send the field **get_licenses** with a value of "true": { "get_licenses": true } As a response you will get a **licenses** object with an array with the name of each activated licenses: {"licenses": [ "emi", "osc" ]} ==== - Getting the current PCB and FPGA temperature ==== Send the field **get_temps** with a value of "true": { "get_temps": true } As a response you will get a **temperatures** object with an array with the celsius degrees. The first value is for the PCB, the second one for the FPGA: {"temperatures": [ 45.12345, 50.12345 ]}