1
0
forked from M-Labs/kirdy

280 Commits

Author SHA1 Message Date
26f12dbf77 add draft for thermostat usage guide 2024-10-18 12:58:20 +08:00
388e6725ef thermostat: Clear alarm when settings are loaded 2024-10-17 17:36:17 +08:00
f4c761c5ca pid: Scale PID Parameters with Sampling Rate
- Breaking changes
2024-10-17 17:36:06 +08:00
e560d8f1eb thermostat, gui: Set max v range according to datasheet 2024-10-16 16:21:11 +08:00
0437c6e76e thermostat, gui: Relax the max settable TEC Current Limit
- Improper settings can cause PoE power failure and over heating
- See hardware repo Issue #51
2024-10-16 16:14:58 +08:00
bad21806f8 gui: Add new form for temp adc filter cfg and settable polling rate
List of Changes:
1. Get report via polling instead of active report mode
2. Allow user to set a custom report polling rate while settings polling rate is fixed to 10Hz
- it is necessary for pid autotune to function correctly
3. Add a form for configuring temperature adc filter
4. Use two different timer for polling report and settings to optimize performance

Known Issue:
1. CPU utilization increases with the report polling rate as for each report recv-ed, gui renders and plots one frame of the 4 graphs
2024-10-10 17:25:33 +08:00
cb2bc505c9 driver: expose _odr_type for filter cfgs 2024-10-10 17:25:29 +08:00
4f19d2c2be driver: Expose the upper and lower limit settings for Sinc3WithFineODR Filter Cfg 2024-10-10 15:23:57 +08:00
e8d3858fc9 driver: return whether task queue is full in task_dispatcher() 2024-10-10 15:20:58 +08:00
9bec56ed6c driver: Add support for GetPollInterval cmd 2024-10-10 15:19:43 +08:00
51b82e0447 cmd_handler: Add cmd to get temp adc polling interval 2024-10-10 13:41:15 +08:00
31e108a4b5 thermostat: settings obj now contains the exact filter type and val 2024-10-10 13:39:37 +08:00
253f4410ee sys_timer: Add fn to retrieve ts in us resolution 2024-10-10 13:31:20 +08:00
bd72c382b0 thermostat: Fix incorrect Dac Calibration Algo
- Thermostat Firmware repo PR #133
2024-10-08 15:57:18 +08:00
a512614de5 net: add option not to open sock when it is polled
- fix a bug, which mcu can get stuck in State::HardReset indefinitely
2024-09-11 17:54:23 +08:00
76f7875e3a gui: await for hard rst and dfu action 2024-09-11 17:54:23 +08:00
0d64d62fb0 gui: Add pd_mon config form
- force user to follow a sequence of steps for safely configuring pd_mon
2024-09-11 17:54:18 +08:00
0aeffdbf7b ld_pwr_exc_protector: disable irq after pwr off
-setting 0V threshold during power off might trigger IRQ
2024-09-11 17:05:38 +08:00
89a1270c02 driver: send cmd err msg to a pyqt sig 2024-09-11 17:05:38 +08:00
c2e78e6f17 driver: Support ApplyPdParams cmd
- Include handling mechanism for "InvalidSettings"b response msg
2024-09-11 17:05:38 +08:00
86e6d3764e laser: check pd_mon params & pwr limit before set
- Design to prevent Issue #47 on hardware kirdy repo from happening
- responsitivity, i_dark & transconductance determine the pwr limit range
- when "ApplyPdParams" and "SetLdPwrLimit" are called, pwr limit range
    is checked before applying new parameter
- Send out a "InvalidSettings" response with error message if settings
    cannot be applied
- Add settable power range to settings json object
2024-09-11 17:05:34 +08:00
1111967a7b gui: add arg for saving log to a file 2024-09-04 17:21:13 +08:00
1896e2534b main: Add PrepareForDfu state 2024-09-04 16:12:06 +08:00
6d4b1b0574 driver: end_session if ConnClose & Dfu is recv-ed 2024-09-04 16:10:42 +08:00
0b5fda2cd9 net: close oldest sock if available socks run out
- By default, smoltcp by default reset any new connections
    if there is no available socket
2024-09-04 15:46:43 +08:00
b763350a8b net: disable IRQ when data is being enqueued
- This prevents broken json to be sent out due to IRQ
2024-09-04 15:46:43 +08:00
838592c812 gui: Display hw_rev when connected 2024-09-04 15:46:43 +08:00
e632cbbfdd driver: use readuntil to get response msg
- The driver may recv partial ctrl msg, which causes a json decode error
2024-09-04 15:46:43 +08:00
27bf573010 driver: retrieve hw_rev after conn is established 2024-09-04 15:45:27 +08:00
c267c30b89 driver: add get_hw_rev() fn 2024-09-04 15:39:27 +08:00
c5826876a6 firmware: Add cmd to get hw_rev 2024-09-04 15:39:27 +08:00
6782cda790 Update pid autotuner code 2024-09-04 15:39:27 +08:00
2fe2ef531b Update example code 2024-09-04 15:39:27 +08:00
f2ad06ecae gui: patch the GUI code 2024-09-04 15:39:27 +08:00
5166bb7ba8 driver: rm thread use & make it asyncio callable
- make all cmds asyncio callable
- control specific cmds can be enqueued to the handler synchronously
2024-09-04 15:39:21 +08:00
9c611fc861 main: poll iface for dfu & hard rst ctrl msgs 2024-09-03 12:15:03 +08:00
51913f2e2f net: fix incorrect poll_iface() calls
- poll_iface might be called by interrupt while poll_iface was being called in main loop
- Disable interrupt when poll_iface is being processed
- Only use IfacePollTimer to poll_iface
2024-08-27 18:49:43 +08:00
c86d67b15c firmware: do not display smoltcp log 2024-08-27 18:49:43 +08:00
3d2294a90c driver: fix incorrect recv_response behavior 2024-08-27 18:49:13 +08:00
3ba8b99084 Use degree celsius as temperature base unit
- Breaking: Erase settings before flashing
- except beta in Steinhart-Hart equation
- to keep the temperature value set consistent with the value returned
- f32 floating point calculation is inaccurate
2024-08-07 17:36:12 +08:00
af73ac8127 gui: Add text for LD & Thermostat status 2024-08-07 12:48:12 +08:00
c214e4182d temp_mon: fix limits not effective at stable temp
- previously, temp is stable, temp limits are not checked
2024-08-07 12:12:07 +08:00
fb69ae3306 main: Don't change temp ctrl mode at otp 2024-08-07 11:26:47 +08:00
8c8ed12522 gui: Disable param tree compact height
- The displayed number was clipped by the pyqtgraph spinbox
2024-08-06 13:01:57 +08:00
572e2dbc5d gui: update for the new driver code
- Default to use active report mode
- Connection will be retried upon abnormal disconnection
- Remove poll every _s and apply btn
2024-08-06 13:01:57 +08:00
82c46e04d0 Update PID Autotune Code for Driver Code 2024-08-06 13:01:57 +08:00
28f8c3c497 Update Example Code for Driver Code 2024-08-06 13:01:57 +08:00
47bf166ecb driver: kirdy driver rewrite
- Start a new thread to handle connections and schedule FIFO task execution
- Make all the cmd accessible without using async
- Connections automatically retry upon disconnection
- Add support for PyQt6 Signal
2024-08-06 13:01:54 +08:00
1dcac25574 gui: Use a custom regex to handle the suffix with temperature characters 2024-07-31 11:35:04 +08:00
3f9a4bf140 ld: Block pwr on request if alarm is asserted
- Align the alarm and pwr up behavior with thermostat temp_mon
2024-07-26 14:29:46 +08:00
201148fb21 main: allow loading flash without device settings
- Fix bugs: ld & thermostat settings cannot be loaded if device settings
    are not present. But, device settings can be left not configured.
2024-07-26 11:10:32 +08:00
a90031dd6c ld_pwr_exc_protector: clear irq bit at pwr up 2024-07-26 11:10:32 +08:00
7b52072617 ld: cfg hardware when pd_mon params changes 2024-07-26 11:10:32 +08:00
c241d34434 gui: fix default units and mv to params' titles
- siPrefix(eg. m, u, k) is no longer editable and fixed
- the displayed unit is relocated to ctrl panel params' titles
2024-07-26 11:10:28 +08:00
20c3d42dd7 rm ld soft current limit 2024-07-25 15:53:00 +08:00
aae89256c3 gui: fix ctrl_panel value getting overwritten bug 2024-07-24 12:19:26 +08:00
3344e7ab19 rm debug comments 2024-07-23 10:39:33 +08:00
a76b8de994 thermostat: Save the user config-ed value directly
- the firmware recorded the actual value set with quantization errors or
    other errors
- this caused confusion for the user when they read back the settings
2024-07-22 17:37:25 +08:00
3410a271fd thermostat: rst pid_state at pwr_up 2024-07-22 10:54:01 +08:00
8f38acd0f2 thermostat: do not rst tec_i_out to 0 at pwr down 2024-07-22 10:52:32 +08:00
b95b9dcefa gui: Display -273.15 if thermistor is not detected
- if tec_temp == None -> Hide temp_plot graph and display -273.15 on ctrl panel
2024-07-19 18:37:52 +08:00
5e6a4c0646 gui: Do not turn on report mode for PID Autotune 2024-07-19 18:24:14 +08:00
ec5bf1d6b6 gui, ld: term_status -> term_50ohm
- term_status (Is50Ohm / Not50Ohm) -> term_50ohm (On/Off)
2024-07-19 18:10:38 +08:00
3737c2ed59 gui: increase the connection timeout value
- 0.1s is too short. Min settable sampling rate is 1.25Sps
2024-07-19 18:10:38 +08:00
4cd328d98c ld: apply pwr limit setting to analog watchdog
- This fixes a bug which power limit is not applied to pd_mon at startup
2024-07-19 18:10:38 +08:00
af95de0f16 thermostat: cal gain error at TEC DAC output 2024-07-19 18:10:12 +08:00
db2f76771a Update Kirdy Driver Example Code 2024-07-19 18:10:08 +08:00
6f87cba9a6 drive: use aenum for cmd 2024-06-21 16:54:24 +08:00
80d94270a2 fix typo 2024-06-18 15:43:36 +08:00
456691f79f gui: import .ui file and rm auto generated py file 2024-06-17 14:49:35 +08:00
0f3db7fec5 gui: Fix typos 2024-06-17 14:49:35 +08:00
9002ca6992 gui: Fix a bug which GUI may recv two msgs at once 2024-06-17 14:49:31 +08:00
92f6b83b16 gui: Fix a bug which dfu cmd can cause connrst err
- Kirdy may close and reset itself before host computer ends its session
2024-06-17 13:16:52 +08:00
da50f372d9 gui: plot ld_pwr is zero if responsitivity not set 2024-06-17 13:16:52 +08:00
57bfd6d83c gui: Relocate LD Power limit params in ctrl panel 2024-06-17 13:16:52 +08:00
dacf9f0fa5 gui: Add pid autotune to ctrl panel 2024-06-17 13:16:52 +08:00
bfd8c0e43a pid_autotune: Update pid autotune main fn 2024-06-17 13:16:52 +08:00
dc97f42bd0 boot: Do not pwr up LD & TEC at boot stage
- This fix LD current spike at boot up
2024-06-17 13:16:52 +08:00
3813b79623 driver: Fix IP Settings not being able to update 2024-06-17 13:16:52 +08:00
83a764889c ld_pwr_exc_protector: set smpr to be slowest
- reduce noise on the analog watchdog
2024-06-17 13:16:52 +08:00
c09da0db98 ld_pwr_exc_protector: Apply 32-pt avg to pd_v
- Reduce noise amplitude from STM32f4 ADC
2024-06-17 13:16:52 +08:00
d5a620c76b pd_mon: make transconductance user-configurable
- hw change: different boards now have different gain settings in order
    to set a pd current range close to the ld being used
- transconductance and fin_gain params are added to flash with "Device"
    as key
2024-06-17 13:16:48 +08:00
d1660c6090 ld_pwr_exc_protector: cal volt offset for pd mon
- hw change: A voltage offset is added onto the pd mon pin
2024-06-13 15:43:44 +08:00
f35546b070 gui: lock param tree value when user inputs a new value
- prevent user input from getting overwritten from incoming data
2024-06-11 15:03:09 +08:00
0dceb8c3c6 correct typo 2024-05-10 12:27:34 +08:00
266110ba14 pid: rm setpoint change kick
- See Thermostat Firmware PR#107
2024-05-10 11:34:58 +08:00
e8aaf5f66b net: Implement iface polling timer
- iface needs to be polled with ref to iface.poll_at time to ensure quality of service
- iface.poll() also services TCP related Timers
2024-05-02 13:13:47 +08:00
70fed23c51 main: Add checks before sock send/recv are called 2024-05-02 13:05:10 +08:00
b8241d1f27 cargo fmt
- rustfmt.toml is ported from artiq-zynq repo
2024-04-23 17:09:26 +08:00
ded7dd7694 gui: add waitingspinnerwidget 2024-04-23 15:55:14 +08:00
7fa4330797 gitignore: ignore python .pyc files 2024-04-23 15:52:32 +08:00
7be35fe7f0 thermostat: Reduce DAC calibration time
- 13 out of 18 bit of DAC is used to calibrate the output with 12bit ADC
- Calibration with more than 13 bit does not improve the accuracy
2024-04-23 15:45:37 +08:00
c5efc6ca57 MAX1968: Improve STM32 ADC calibration consistency 2024-04-23 15:35:23 +08:00
b330f45260 net: Increase ARP Cache Size from 4 to 16 2024-04-23 11:43:27 +08:00
554f292cab net: Disable nagle algo & Set keep_alive duration 2024-04-22 17:06:07 +08:00
f303ab639a net: Reset tcp socks when new eth link is detected 2024-04-22 17:06:02 +08:00
ba30575406 GUI: Improve abnormal disconnection handling 2024-04-22 17:05:56 +08:00
bfdb1f5066 gui: move tool, save & load flash btns to menu bar
- Add a new form to update IP address
2024-04-19 15:56:48 +08:00
5b35f32a42 driver: Handle CancelledError for send_cmd handler 2024-04-19 15:56:48 +08:00
2df22d5dcb driver: Use inet_aton to check IPV4 address format 2024-04-19 15:56:48 +08:00
b73eacd234 driver: Wait for a sec after hard rst cmd is sent 2024-04-19 15:56:43 +08:00
4a06a7579f gui: handle hard reset in _on_connection_changed()
- Ensure correct gui disconnection seq during Kirdy hard reset
2024-04-19 15:56:36 +08:00
9524601cb6 gui: Improve DataWatcher operation
- Ensure there is only one instance of signal_emitter() task
- calling stop_watching should stop report_task
2024-04-19 13:25:00 +08:00
4116962175 firmware: rst active_rpt if eth link is not active 2024-04-18 17:33:09 +08:00
0e087c6992 ld: ld_soft_i_limit now limits ld_i_out being set 2024-04-18 17:04:52 +08:00
6b250aa1fd gui: Add global QT Style Sheet
- Gray out the objects if they are disabled
2024-04-18 16:45:35 +08:00
de80aedafc gui: separate connection_menu into Qdiag obj 2024-04-11 16:58:48 +08:00
31a97bdeb4 gui: GUI Initial Commit
- Adapted from Thermostat GUI PR #101
2024-04-11 16:33:35 +08:00
784b8a357b driver: Update send_cmd fn to use msg_type params 2024-04-10 16:57:43 +08:00
ceb003e07e cmd_handler: Add msg_type params to report & settings 2024-04-10 16:57:43 +08:00
10873f4791 firmware: Load default pwr_on settings from flash 2024-04-10 16:57:43 +08:00
e160316bfc ld: add ld_terms_short in settings report 2024-04-10 16:57:43 +08:00
0a992c1dc8 ld: pd_responsitivity -> pd_mon_params 2024-04-10 16:57:43 +08:00
ae74455f94 driver: Separate setting pd_config into two fns 2024-04-10 16:56:20 +08:00
6bccbceb81 main: Add a state to only save network settings 2024-04-10 16:56:20 +08:00
eaebdb390d cmd_handler: change mW to W in SetLdPwrLimit cmd 2024-04-10 16:56:20 +08:00
489333c43d ld: save pwr_limit into settings 2024-04-10 16:56:20 +08:00
4288d465c9 thermostat: Report temp_setpoint in degree celsius 2024-04-10 16:56:20 +08:00
09863353cd driver: Add lock for tcp writer & reader stream 2024-04-10 16:56:20 +08:00
6d107d55cf driver: Add StoppedConnecting exception 2024-04-10 16:56:20 +08:00
d435dc06d8 fix typos 2024-03-25 15:43:20 +08:00
898358f4e6 pykirdy: Add reporting interval in report_mode 2024-03-25 15:43:20 +08:00
a13c6fa86c pykirdy: Update handling of json objects received
- Accept new line delimited json in read_response()
2024-03-25 15:43:20 +08:00
b1a1173075 cmd_handler: Add newline at the end of json sent 2024-03-25 15:43:20 +08:00
af68b0e90a Update rust, stm32-eth, hal, smoltcp versions 2024-03-25 15:43:12 +08:00
f8abfd4300 Update nixpkgs version 2024-03-22 12:48:53 +08:00
7d41edef20 ld: Increase the delay before turning on LD_PWR
- Delay is tested and verified with oscilloscope
- Tested with 0.3A LD output current
2024-03-21 15:21:24 +08:00
607da8a822 ld: Add a delay before turning on LD PWR
- DAC output might not drop to 0V before PWR is being turned on
- Thus, it might drive nondeterministic current into the laser diode during pwr up
2024-03-20 17:24:47 +08:00
7f06fc06fd async driver: fix incorrect timeout handling 2024-03-20 15:35:12 +08:00
67f9e65df8 Start tec_readings_conversion after report is sent
- Before this fix, tec_readings only converts when eth_is_pending
2024-03-20 15:35:12 +08:00
7d2e14ef2f eth: Poll Iface In ETH IRQ
- Improve responsiveness of TCP packets handling
- Fix a bug when one client is connected and active report is ON, the other client's cmds do not receive Ack from Kirdy
2024-03-20 15:32:28 +08:00
e525a3f354 fix typo 2024-03-20 12:06:05 +08:00
8431e9f43d Update README 2024-03-20 11:55:21 +08:00
e9d5f4598a Fix wrong USB Product Description 2024-03-20 11:45:09 +08:00
1867935047 Send report to connected socket
- Remove active report in flash settings
2024-03-19 17:38:29 +08:00
99cf17f7e4 temp_mon: Add support for constant current mode 2024-03-19 14:52:26 +08:00
048245f674 set_tec_max_i_pos/neg->...cooling_i/heating_i 2024-03-19 14:52:26 +08:00
0a01d299bc Fix wrong reporting of Thermostat Pwr_on status 2024-03-19 14:52:26 +08:00
dbbd438e92 ld: Fix unable to save & load ld_i flash settings 2024-03-19 14:48:25 +08:00
3ad7c417f6 Fix a ld current ramp up bug
- During ld_i ramp up, if pwr up cmd is called, ld_i is set incorrectly
2024-03-18 16:41:55 +08:00
c06491a8b2 Update PID Autotune Code 2024-03-18 15:55:05 +08:00
0380c8d30b Unify naming and unit in firmware and driver
- mA, uA -> A
- K -> Degree Celsius
- tec -> thermostat
2024-03-18 15:55:05 +08:00
ed5cda6364 Correct wrong comment in driver 2024-03-18 15:54:25 +08:00
61624b0bd6 Implement Kirdy AsyncIo Driver and example code 2024-03-15 17:14:22 +08:00
1480305c16 max1968: Fix ADC+DMA does not start converting bug 2024-03-15 17:12:42 +08:00
74c465d16f fix typos 2024-03-12 11:47:58 +08:00
574abc2c2f Add instructions to erase flash settings with DFU 2024-03-12 11:26:45 +08:00
a9dbff8250 Send Response Back to The Client after Recv Cmd 2024-03-08 16:38:52 +08:00
8cd3f70721 Add Instructions for Erasing Flash Settings 2024-03-07 16:20:51 +08:00
b7231f48ff Fix Recv Dfu cmd doesn't hard reset bug 2024-03-07 10:26:32 +08:00
7f6a385e1d Support controls from multiple TCP Socket Clients 2024-03-06 16:30:31 +08:00
eff8adc184 Update Eth Link Speed with the detected Speed
- adds support to Half/FullDuplexBase10T, HalfDuplexBase100Tx Eth Speed
- fix a bug which Kirdy requires link to up to enter main loop
2024-03-06 12:15:08 +08:00
a579e5c5d5 Add default gateway into IpSettings 2024-03-06 10:34:57 +08:00
65b757ac3c Add cmd to Store IP Settings in Flash
- Cmds Setup Flow: Set IP -> Save Settings to Flash -> Issue HardRst
2024-03-05 16:44:28 +08:00
911d9a7bc9 Load & Config IP Settings from Flash at StartUp 2024-03-05 16:44:03 +08:00
111d9a4226 cleanup 2024-03-05 16:33:49 +08:00
a3885c365e Implement Save & Load Flash Settings in FSM & cmd 2024-03-05 16:33:45 +08:00
a58b0954ec Add Set Default PWR On cmd for ld & tec 2024-03-05 10:35:05 +08:00
92c6cf12d4 Add fns to apply ld, tec settings summary configs 2024-03-05 10:35:05 +08:00
ef2410f441 Add fns, struct items for getting ld, tec settings 2024-03-05 10:35:05 +08:00
adfa4f5fa3 Move TempAdcFilter struct def to Thermostat 2024-03-05 10:35:05 +08:00
df939eb9a3 Use FSM to manage device state in main loop 2024-03-05 10:35:01 +08:00
3aca712e1d Fix typos 2024-03-04 10:32:33 +08:00
4526536a48 Ignore packets of 0 bytes size 2024-03-01 11:31:06 +08:00
f488786e1c Use DMA to read Tec_I & Tec_V
- Remove the single conversion polling time with DMA
2024-03-01 11:31:01 +08:00
3528d8a68f Update Python Test Scripts 2024-02-28 13:33:51 +08:00
46393bacdb Add comments on TCP client development 2024-02-28 13:00:02 +08:00
6fc800e562 net: Set stm32 link speed with the Phy Speed read 2024-02-28 13:00:02 +08:00
76477065be Change f64 to f32 for SI Unit related computation
- Save computational time
- Improve the processing speed of TecSetI cmd
2024-02-28 12:59:58 +08:00
b1fa0e51c8 Add a separate tec_set_i cmd to reduce overhead
- miniconf is slow at validating complex struct with large enum lists
2024-02-28 12:46:21 +08:00
9f82fa58f4 cmd: Add temp adc filter config cmd 2024-02-28 12:23:09 +08:00
19341672a9 Correct SPI Clock Freq and Set them to Max
- Set the SPI frequency to maximum to reduce data transaction time
- SPI1 SCLK is divided from PCLK2
- SPI2, SPI2 SCLK are divided from PCLK1
2024-02-28 12:23:09 +08:00
86a9fb039e boot: Correct PCLK1, PCLK2 freq value
- PCLK is divided from HCLK in power of two
- abs max pclk1 = 42MHz
- abs max pclk2 = 84MHz
2024-02-28 12:23:09 +08:00
a2bb390ae2 ad7172: Add filter configs for single channel mode 2024-02-28 12:23:09 +08:00
18dd0a7963 Save Thermostat and Laser Diode PWR On/Off status 2024-02-28 12:22:40 +08:00
d1cc677668 thermostat: split poll_adc & update_pid into 2 fns 2024-02-28 12:21:37 +08:00
c09ccc29cd cmd: Add cmd to get all device settings 2024-02-23 12:45:29 +08:00
c4135f6ac3 cmd: Add cmds to config pd_mon params 2024-02-22 16:44:45 +08:00
6ee45b4814 thermostat: Limit Pid Temp SetPt to TempMon limits 2024-02-21 17:37:56 +08:00
a8787430b1 Add Temperature Monitor to Thermostat
- Issue an alarm when temperature goes out of user-defined operating range during Pid Controller startup
    or reading is outside of +-0.5 Degree from temperature set point after Pid Controller becomes stable
- If alarm is observed, power down laser and tec controller and disengage Pid Controller
- Add the corresponding cmd for configuring the temperature monitor
2024-02-21 17:32:11 +08:00
ed785b7c85 ld: correct timer and dac reset in pwr_up seq 2024-02-21 11:11:12 +08:00
30ab228b4b ld: Set default ld_current_limit in setup() fn 2024-02-20 16:22:13 +08:00
88cca12a60 ld: Use Timer IRQ to ramp up/down output current
- Fixes possible watchdog timeout
- Improves ethernet response time & thermostat Pid update interval consistency
2024-02-20 16:18:06 +08:00
8139ebdc1b ld_ctrl: set_dac() fn now returns the volt set
- before this fix, set_dac() just returns the inputted voltage
2024-02-20 12:30:00 +08:00
5f83b73011 cmd: Add active report mode cmd 2024-02-19 15:08:00 +08:00
0642640da7 dfu: Add eth cmd, fns to put stm32 into dfu mode 2024-02-16 17:23:47 +08:00
6dd1bf9301 hw_rev: Generate random mac address on rev0_3 2024-02-16 17:23:39 +08:00
d2efc02b6a eth_cmd_test: Reset ld current to 0A during SIGINT 2024-02-15 12:47:29 +08:00
89d415194d update eth_cmd_test code 2024-02-15 12:14:14 +08:00
09b3765877 pid_autotune: Add pid autotune script
- Port from thermostat repo
2024-02-15 12:14:14 +08:00
7a76325288 cmd: rm rev from cmd json
- not in used
2024-02-15 12:14:14 +08:00
ffa5f4e8ff pid: Use Thermostat's PID algo instead of IDSP
- Has lower steady state error upon testing with PID autotune
2024-02-15 12:14:14 +08:00
e9a396f001 thermostat: Add abs timestamp to StatusReport 2024-02-15 12:14:14 +08:00
412f5ec58b cmd: add cmds to poll tec temp, dis/enable pid 2024-02-15 12:14:14 +08:00
e29898f8f8 fix typos 2024-02-15 12:14:14 +08:00
60a79d1780 rename get_term_status -> get_lf_mod_in_impedance 2024-02-15 12:14:14 +08:00
b0edd3dba2 hw_rev: Wait for 5s b4 gpio init instead of 1.5s
- See Issue #32
2024-02-15 12:14:14 +08:00
4c331dd319 add BSD build instructions 2024-02-12 18:33:06 +08:00
6b05b2d851 Fix Power Up Hw Issue
- See Issue #32 on Kirdy Hw Repo
2024-02-07 12:46:13 +08:00
f22ab430b8 pid: Use IDSP impl of Pid, Add Eth Pid Ctrl Cmds 2024-02-07 11:56:38 +08:00
4cd650367d thermostat: cleanup pid_engaged fn 2024-02-05 15:14:51 +08:00
de262b849d eth: Add Thermostat related json cmd 2024-02-05 15:14:23 +08:00
4132bfacd3 boot: Set thermostat & LD init output current to 0 2024-02-02 17:37:37 +08:00
6096711d2c eth: Poll and Handle Json based Ld ctrl cmd
- Upgrade to miniconf 9.0
- Only non report related laser diode ctrl command is implemented
2024-02-02 17:35:58 +08:00
3d3d6f5cb5 ld: cleanup 2024-02-02 17:23:39 +08:00
172c3e1dfc cleanup boot.rs, thermostat.rs 2024-02-01 16:23:52 +08:00
ee0ed8ebe7 eth: add drivers for init, polling & sending data 2024-02-01 16:20:36 +08:00
771f2813f7 gpio: correct wrong eth pin definition 2024-01-31 17:15:32 +08:00
4e0d14def2 laser: Add driver and fn to get termination status 2024-01-30 13:01:33 +08:00
f6677d874c LD: Rst LD current at power_up for soft start 2024-01-26 16:56:55 +08:00
d391e3a1fb LD: Setting LD output current now ramps up slowly 2024-01-26 16:56:55 +08:00
edd30e94a0 laser_diode: Add fn to set pd_dark_i 2024-01-26 16:56:55 +08:00
ad731c2f15 rename pd_mon -> pd_responsitivity 2024-01-26 16:56:55 +08:00
9d8a553669 pd_mon: add fns to convert between pwr and current 2024-01-26 16:56:55 +08:00
e22f424531 rm unused cargo config target definition 2024-01-26 16:56:55 +08:00
6af0f992d5 LdPwrExcProtector: Cleanup 2024-01-26 16:56:55 +08:00
f50505feaf Rename Analog_Wdg -> LdPwrExcProtector 2024-01-26 16:56:55 +08:00
9ae867cd88 max1968: rm redundant adc config 2024-01-26 16:56:55 +08:00
85b50bf824 Analog_Wdg: Finish Power Excursion Protection fns
- Verified to be working
2024-01-26 16:56:39 +08:00
07ea733b34 thermostst: Patch hw related settings for rev0_3 2024-01-24 12:25:45 +08:00
5bea3f0e5f boot: init analog wdg & power up laser after cfg
- Verified to be boot-able on Rev0_3 board
2024-01-24 12:19:07 +08:00
af283b17ac laser_diode: use analog wdg fns 2024-01-24 12:18:42 +08:00
c3022e9db1 Analog_Wdg: Add GPIO init 2024-01-24 12:18:42 +08:00
c02181c80c laser diode: Add Analog Watchdog initial commit
- rm pid_state for laser diode
2024-01-24 12:18:42 +08:00
095fe8ea69 AD7172: Rm deprecated warning for Rev0_2 hw bug 2024-01-24 12:18:42 +08:00
a0b67cdb09 MAX1968: Patch DacVfb pinout for rev0_3 2024-01-24 12:18:42 +08:00
381f25f036 AD7172: correct wrong NSS pinout definition 2024-01-24 12:18:42 +08:00
c768bdc93a Thermostat: Add fn to calibrate ADC's VDDA
- ADC1 exclusive feature
- Calibrated VDDA val can be passed to other ADCs to adjust gain error
2024-01-24 12:18:42 +08:00
0d12c902fc pd_mon: Separate ld_power calculation to a file 2024-01-24 12:18:37 +08:00
3cfdee917a rename current_source -> ld_ctrl 2024-01-24 12:16:06 +08:00
5f582be143 rename ld_drive to laser_diode 2024-01-24 12:16:06 +08:00
2f7ca2a706 Thermostat: Add fns to report status & settings
- Report all system status, PID Settings, NTC parameter, TEC Settings
2024-01-24 12:16:06 +08:00
ff3d9b790a thermostat: pid_state rename variables 2024-01-24 12:16:06 +08:00
bc7bf9a6e7 rename channel_state -> pid_state 2024-01-24 12:16:02 +08:00
ca110962f7 Thermostat: Add pid_ctrl parms & its related fns
- setup: include the pid_ctrl
- poll_adc: Fetch Temperature and Update PID to set the current output
2024-01-24 12:16:02 +08:00
d0f226ce03 Thermostat: Add default to ChannelState 2024-01-24 12:16:02 +08:00
f49fd08c69 ad7172: impl default for ch_calibration 2024-01-24 12:16:02 +08:00
ccaf728c75 thermostat: Add channel_state mod for PID Control 2024-01-24 12:15:57 +08:00
3ac287ace2 steinhart_hart: Add Steinhart-Hart eq fns
- Port from thermostat firmware
2024-01-17 12:20:34 +08:00
cdf900a5b6 gpio: Add AD7172 phy gpio init
- Will not function on rev0_2 kirdy due to wrong hardware connection
2024-01-16 17:36:16 +08:00
af8d361b95 AD7172: Add AD7172 drivers fns
- Port from Thermostat Firmware
2024-01-16 16:19:01 +08:00
e355e83d28 memory.x: Add comment on the reserved flash sector 2024-01-16 15:27:43 +08:00
285fc9b5d6 memory.x: Reserved Flash Sector 11 for Config 2024-01-15 17:44:44 +08:00
8c612fc0c9 memory.x: RM CONFIG MEMORY area in linker script
- STM32f407 does not have second bank or flash/ Sector 12 unlike F427
2024-01-15 17:43:17 +08:00
42cd55645c main: Add demo fns to use on-board flash 2024-01-15 17:21:48 +08:00
d190b8b192 flash: Add init boot seq
- Max flash erase time for PSize = 8 and 128KiB Sector is 4s
- Watchdog period is changed accordingly
2024-01-15 17:21:06 +08:00
74325a3cee flash: Add fns to store data in flash
- Writing to the wrong flash sector can cause MCU to stall
- Only 16KiB out of 128KiB in the sector is used to save RAM for compacting
2024-01-15 17:14:58 +08:00
709eae8566 max5719: Slow down SPI clock
- setting it to 30MHz hang MCU core
2024-01-11 17:46:35 +08:00
d3e3451d37 Add README.md for flashing instructions 2024-01-11 17:17:38 +08:00
33ff0c3678 main: Add demo fns reading all hardware status 2024-01-11 17:17:38 +08:00
33d9cb45c4 ld_drive: Add soft current limit 2024-01-11 17:17:38 +08:00
8ae27725c6 Port PID Controller from thermostat firmware
- Add serde Cargo
2024-01-11 17:17:10 +08:00
20fc6d6fac rm photodiode folder and its mod.rs
- photodiode related fns are written inside laser diode folder
- as they are closely related
2024-01-10 17:51:27 +08:00
654964831a boot: Change thermostat boot seq for new setup seq
- Power Up -> Calibrate -> Set the TEC current to some value
2024-01-10 10:38:05 +08:00
27c7b5929e thermostat: change setup seq
- default power off
- with all params set to default val on hw
- rm calibration from setup
2024-01-10 10:38:05 +08:00
e48f1979f0 Clean up warnings 2024-01-10 10:38:05 +08:00
59f2385ecf ld_drive: make settings private & add set & get fn 2024-01-10 10:38:05 +08:00
d4e074cbd4 ld_drive: rebase to the point that declare set_i 2024-01-10 10:38:05 +08:00
277796d2a4 boot: Add ld_drive boot sequence 2024-01-10 10:38:05 +08:00
be8bf0a8b6 ld_drive: add setup fn 2024-01-10 10:38:05 +08:00
b1123047c7 ld_drive: Add fns to get pd current & cal ld pwr 2024-01-10 10:38:05 +08:00
707fac0775 ld_drive: add a param for setting to cal pd pwr
- Conversion ratio between pd current and ld output power
2024-01-10 10:38:05 +08:00
a99dde8b38 current_source: add driver to read pd voltage 2024-01-10 10:38:05 +08:00
d1f7a20c15 ld_drive: Add fns to set ld output current 2024-01-10 10:38:05 +08:00
58a8b15c40 ld_drive: add unit type and hw related constant 2024-01-10 10:38:05 +08:00
590ba8171c Add high level ld_drive mod
- Use current_source driver to interface with the board
2024-01-10 10:38:05 +08:00
0807f66b3c Add digital gpio related fns to Current source
- power_up
- power_down
- ld_short_enable
- ld_short_disable
    - Rev0_2 has hardware connection bug for LD_EN net.
    - laser driver power will always be enabled
2024-01-10 10:38:05 +08:00
2953d4edde Rewrite current_source obj and add gpio init 2024-01-10 10:38:05 +08:00
f2c026ffdd Rewrite MAX5719 Driver into a separated file 2024-01-10 10:38:05 +08:00
c7d36bc8d5 Reorganize variables and fns structure 2024-01-10 10:38:00 +08:00
784fc03957 cargo: add miniconf 2024-01-10 10:36:13 +08:00
6cc2bc32c5 thermostat:reorganize fns structure, add setup seq 2024-01-10 10:36:09 +08:00
23ee568ea7 Port TEC DAC calibration procedure from Thermostat
- Needs clean up
- To be evaluated and rethink the calibration procedure
2024-01-10 10:36:05 +08:00
d3f3608136 DacVFb is changed to PC3 for rev0_2 prototype
- This requires a circuit change to connect it to the PoE_PWR_SRC
    through a 1.6k Ohm and 100nF LPF
2023-12-21 16:39:09 +08:00
4cf7b7fdf9 Rewrite MAX1968 Driver Generically
- Set variable to proper types(Rate, ElectricalPotential, etc)
- Declare const MAX_I_POS, MAX_I_NEG, MAX_V conversion
2023-12-21 13:13:06 +08:00
0179e7641a Add MAX1968 Startup Sequence 2023-12-20 14:51:34 +08:00
f6767b147b Fix typo in MAX1968 driver power_down fn 2023-12-20 14:48:47 +08:00
475fe28604 Initial Commit for AD5680 and MAX1968 Drivers
- GPIO Initializations for AD5680, MAX1968 drivers
- CTLI voltage of  MAX1968 can be set with AD5680
- All features of MAX1968 can be controlled
2023-12-20 12:08:48 +08:00
764a203dd8 Update stm32-eth cargo crate to 0.15.2
- stm32-eth crate requires stm32f4xx-hal 0.14.0 and smoltcp 0.10.0
- its dependency packages requires rustc 1.67 or above (1.67.1 is chosen)
2023-12-15 16:40:55 +08:00
f2b419f8d0 Fix nix build to compilation error at Check Phase
- "panic_handler" should not be declared during test
- "main" and "std" are needed during test
2023-12-15 13:21:19 +08:00
62 changed files with 13045 additions and 349 deletions

View File

@@ -1,12 +0,0 @@
[target.thumbv7em-none-eabihf]
runner = "gdb -q -x openocd.gdb"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

8
.cargo/config.toml Normal file
View File

@@ -0,0 +1,8 @@
[target.thumbv7em-none-eabihf]
runner = "gdb -q -x openocd.gdb"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
target/
result
*.pyc
*.jdebug*

60
BSD.md Normal file
View File

@@ -0,0 +1,60 @@
# Building Rust embedded firmware on BSD
Using the Rust stable compiler from the BSD ports is mostly a matter of removing the arbitrary limitations that have been built into the Rust toolchain.
Get rid of the "nightly toolchain" check in cargo-xbuild:
```
diff --git a/src/lib.rs b/src/lib.rs
index 204ad8f..9c61e39 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -157,28 +157,11 @@ pub fn build(args: Args, command_name: &str, crate_config: Option<Config>) -> Re
})
})?;
- // We can't build sysroot with stable or beta due to unstable features
let sysroot = rustc::sysroot(verbose)?;
- let src = match meta.channel {
- Channel::Dev => rustc::Src::from_env().ok_or(anyhow!(
+ let src = rustc::Src::from_env().ok_or(anyhow!(
"The XARGO_RUST_SRC env variable must be set and point to the \
- Rust source directory when working with the 'dev' channel",
- ))?,
- Channel::Nightly => {
- if let Some(src) = rustc::Src::from_env() {
- src
- } else {
- sysroot.src()?
- }
- }
- Channel::Stable | Channel::Beta => {
- bail!(
- "The sysroot can't be built for the {:?} channel. \
- Switch to nightly.",
- meta.channel
- );
- }
- };
+ Rust source directory",
+ ))?;
let cmode = if let Some(triple) = args.target() {
if triple == meta.host {
```
Run ``cargo install --path .`` to install the modified cargo-xbuild
Get a copy of the Rust sources that corresponds to the installed compiler binary, and set up the environment accordingly:
```
> rustc --version
rustc 1.72.1 (d5c2e9c34 2023-09-13) (built from a source tarball)
> git clone --depth=1 --recurse-submodules --shallow-submodules https://github.com/rust-lang/rust --branch 1.72.1
> export XARGO_RUST_SRC=`pwd`/rust/library
```
Disable other arbitrary checks in the Rust compiler:
```
> export RUSTC_BOOTSTRAP=1
```
And you can now simply run ``cargo xbuild`` to build the firmware.

782
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,23 +15,29 @@ default-target = "thumbv7em-none-eabihf"
[dependencies]
panic-halt = "0.2.0"
cortex-m = "0.7.6"
cortex-m-rt = { version = "0.7.1", features = ["device"] }
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = { version = "0.7.3", features = ["device"] }
cortex-m-semihosting = "0.5.0"
log = "0.4.17"
bare-metal = "1"
nb = "1"
cortex-m-log = { version = "0.7.0", features = ["log-integration", "semihosting"] }
stm32f4xx-hal = { version = "0.13.2", features = ["rt", "stm32f407", "usb_fs"] }
stm32-eth = { version = "0.3.0", features = ["stm32f407"] }
smoltcp = { version = "0.8.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log", "medium-ethernet"] }
stm32f4xx-hal = { version = "0.20.0", features = ["stm32f407", "otg-fs", "usb_fs"] }
stm32-eth = { version = "0.6.0", features = ["stm32f407", "smoltcp-phy", "smoltcp"] }
ieee802_3_miim = "0.8.0"
smoltcp = { version = "0.11.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "medium-ethernet", "iface-neighbor-cache-count-16"] }
uom = { version = "0.30", default-features = false, features = ["autoconvert", "si", "f32", "use_serde"] }
num-traits = { version = "0.2.15", default-features = false, features = ["libm"] }
usb-device = "0.2.9"
usbd-serial = "0.1.1"
usb-device = "0.3.2"
usbd-serial = "0.2.1"
fugit = "0.3.6"
rtt-target = { version = "0.3.1", features = ["cortex-m"] }
miniconf = "0.9.0"
serde = { version = "1.0.158", features = ["derive"], default-features = false }
sfkv = "0.1"
bit_field = "0.10"
crc = "3.0.1"
byteorder = { version = "1", default-features = false }
[features]
semihosting = ["cortex-m-log/semihosting"]
RTT = []

85
README.md Normal file
View File

@@ -0,0 +1,85 @@
# Testing Firmware for the Sinara 1550 Kirdy
- This repo is for testing only. Not intended for production use.
## Building
### Reproducible build with Nix
kirdy firmware is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
Once you have Flakes enabled, you can use ``nix build`` to build the firmware.
### Development environment
Clone this repository and with Nix Flakes enabled, use the following commands:
```shell
nix develop
cargo build
```
The resulting ELF file will be located under `target/thumbv7em-none-eabihf/release/kirdy`.
Alternatively, you can install the Rust toolchain without Nix using rustup; see the Rust manifest file pulled in `flake.nix` to determine which Rust version to use.
For building on FreeBSD or OpenBSD, see BSD.md.
## Debugging
Connect SWDIO/SWCLK/RST/GND to a programmer such as ST-Link v2.1. Run OpenOCD:
```shell
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
```
You may need to power up the programmer before powering the device.
Leave OpenOCD running. Run the GNU debugger:
```shell
gdb target/thumbv7em-none-eabihf/release/kirdy
(gdb) source openocd.gdb
```
## Flashing
If the firmware to be flashed involves an update on the flash settings, it is required to erase the flash settings before flashing the new firmware to avoid unexpected hardware behavior. There are several options for flashing kirdy. DFU requires only a USB-C cable or RJ45 cable, whereas OpenOCD needs a JTAG/SWD adapter.
### dfu-util on Linux
* Install the DFU USB tool (dfu-util).
* Convert firmware from ELF to BIN: `arm-none-eabi-objcopy -O binary kirdy kirdy.bin` (you can skip this step if using the BIN from Hydra)
* Put STM32 into DFU Mode. You can either
* Connect to the USB Type C cable to kirdy next to the RJ45 Jack. After that, add BOOT0 jumper to kirdy near programming headers and then cycle board power to put it in DFU mode. OR
* Plug in RJ45 cable, which connect to a network that is accessible by your computer and send the corresponding dfu json command via TCP Socket to kirdy. Please see the python test script for the command.
* Push firmware to flash: `dfu-util -a 0 -s 0x08000000:leave -D kirdy.bin`
* If you plugged in the BOOT0 jumper, you will need to
1. Remove BOOT0 jumper
2. Cycle power to leave DFU update mode
* If you plugged in the RJ45 cable, the MCU would start its application code automatically. No power cycle is needed.
### st.com DfuSe tool on Windows
On a Windows machine install [st.com](https://st.com) DfuSe USB device firmware upgrade (DFU) software. [link](https://www.st.com/en/development-tools/stsw-stm32080.html).
- add jumper to kirdy across 2-pin jumper adjacent to JTAG connector
- cycle board power to put it in DFU update mode
- connect USB Type C to PC
- use st.com software to upload firmware
- remove jumper
- cycle power to leave DFU update mode
### OpenOCD
```shell
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program target/thumbv7em-none-eabihf/debug/kirdy verify reset; exit"
```
## Erasing Flash Settings
The flash settings are stored in the last flash sector(ID: 11) of bank 0 of stm32f405. You can erase it with JTAG/SWD adapter or by putting the device in Dfu mode. You may find it useful if you have set network settings incorrectly.
With JTAG/SWD adapter connected, issue the following command:
```shell
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "flash init; init; halt; flash erase_sector 0 11 last; reset; exit"
```
With STM32 in DFU Mode, connect the USB Type C cable and then issue the following command:
```
dfu-util -a 0 -s 0x080E0000:leave -D erase_flash_settings.bin
```

View File

@@ -1,7 +1,4 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs::File, io::Write, path::PathBuf};
fn main() {
// Put the linker script somewhere the linker can find it

56
docs/thermostat.md Normal file
View File

@@ -0,0 +1,56 @@
# Thermostat
Kirdy incorporates a single channel thermostat circuitry like Sinara-hw [Thermostat](https://github.com/sinara-hw/Thermostat) with improved noise. It can deliver more than 9W of cooling/heating power with better than +-1mK temperature stability.
## Power Input
If you use Kirdy with a PoE (IEEE 802.3af Class 3) PSE, Kirdy may experience power failure due to power limit. If you need more power on Thermostat, you can plug in additional Power supply on the 12V power Jack(Recommend: 12V 2A). Although the TEC Driver has a very high 165 degree thermal shutdown threshold, it is highly recommended to provide active cooling to the TEC Driving Circuitry.
## Control Mode
Like Sinara-hw [Thermostat](https://github.com/sinara-hw/Thermostat), they both share the same control mode and algorithm, namely constant Current Control Mode
### Constant Current Control Mode
You can drive a constant current to the load. The end user to connect an external control loop. If end user wants to use it with an external control loop, it is recommended to retrieve reports via the ActiveReportMode instead of polling status report for better consistency and higher maximum update rate.
### PID Control Mode
Thermostat uses the [PID algorithm](https://hackmd.io/IACbwcOTSt6Adj3_F9bKuw) derived by Quartiq. The included Pid Autotune Algorithm can already achieve the specified +-1mK stability.
During autotuning, Theremstat drives a user-specified step current into the load to calculate the pid parameters. Improper usage can damage the connected device.
#### Important Notes
1. Configure all the protection mechanisms correctly before running the pid autotune algorithm.
2. Supervise the PID autotune process.
3. PID Autotune algorithm sometimes can produce unstable PID parameters. Do evaluate the PID performance after autotune is completed
4. For best performance, you should do the autotune with Kirdy installed in the environment that it is gonna be used.
5. If you are using the GUI, you need to set a high enough polling rate for PID autotune to function correctly.
#### Usage Guide
You can run pid autotune via the standalone auto
1. Target Temperature
- Set it to the intended operating temperature for the connected device to operate in. You can still change temperature setpoint afterwards.
2. Test Current
- Set it to the maximum recommended operating current specified on the TEC/Resistive load.
3. Update Rate
- This can be set by configuring the filter settings in the Temperature ADC. High Update Rate can yield faster loop response with temperature regulation noise as the trade-off. Higher update rate can be useful if the load is actively cooled.
4. Temperature Swing
- Lower value can damp the control loop response.
5. Lookback Period
- Larger value can damp the control loop response. Do note that the larger lookback period also increases the autotune completion time.
You can give your system a try with the following parameters.
1. Target Temperature: (User-Specified)
2. Test Current: (User-Specified)
3. Update Rate: 16.67Hz (Sinc5Sinc1 Filter with 50Hz and 60Hz Rejection)
4. Temperature Swing: 0 Degree
5. Lookback Period: 5s
Once autotune is successful, you can improve its stability by increasing the lookback period.
#### Troubleshooting
If you obtain unstable PID parameters,
1. Check if the polling rate is close to the PID update rate. If not, increase the polling rate
2. Increase the lookback period and try again.
If the autotune does not finish running or faikls, you should
1. Check if the thermostat has a hard time heating or cooling the connected device to the targe temperature. If yes, you should consider adjust the target temperature accordingly.
2. Check if the polling rate is close to the PID update rate. If not, increase the polling rate.

BIN
erase_flash_settings.bin Normal file

Binary file not shown.

14
flake.lock generated
View File

@@ -3,11 +3,11 @@
"mozilla-overlay": {
"flake": false,
"locked": {
"lastModified": 1664789696,
"narHash": "sha256-UGWJHQShiwLCr4/DysMVFrYdYYHcOqAOVsWNUu+l6YU=",
"lastModified": 1704373101,
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "80627b282705101e7b38e19ca6e8df105031b072",
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
"type": "github"
},
"original": {
@@ -18,16 +18,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1666164185,
"narHash": "sha256-5v+YB4ijeUfg5LCz9ck4gIpCPhIS+qn02OyPJO48bCE=",
"lastModified": 1713725259,
"narHash": "sha256-9ZR/Rbx5/Z/JZf5ehVNMoz/s5xjpP0a22tL6qNvLt5E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c5203abb1329f7ea084c04acda330ca75d5b9fb5",
"rev": "a5e4bbcb4780c63c79c87d29ea409abf097de3f7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}

View File

@@ -1,15 +1,15 @@
{
description = "Firmware for kirdy";
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-22.05;
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-23.11;
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
outputs = { self, nixpkgs, mozilla-overlay }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
rustManifest = pkgs.fetchurl {
url = "https://static.rust-lang.org/dist/2022-09-22/channel-rust-stable.toml";
sha256 = "f257a7de2f284f025238964ca2c1865c704be4156e1909d6393d92b796005055";
url = "https://static.rust-lang.org/dist/2024-03-21/channel-rust-nightly.toml";
sha256 = "1c7db6ab80d20682b5cc5bda7360c63311d7188c0c082902d3790820527cd4e0";
};
targets = [
@@ -22,7 +22,7 @@
inherit targets;
extensions = ["rust-src"];
};
rust = rustChannelOfTargets "stable" null targets;
rust = rustChannelOfTargets "nightly" null targets;
rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
rustc = rust;
cargo = rust;
@@ -51,6 +51,34 @@
'';
dontFixup = true;
auditable = false;
};
pglive = pkgs.python3Packages.buildPythonPackage rec {
pname = "pglive";
version = "0.7.2";
format = "pyproject";
src = pkgs.fetchPypi {
inherit pname version;
hash = "sha256-jqj8X6H1N5mJQ4OrY5ANqRB0YJByqg/bNneEALWmH1A=";
};
buildInputs = [ pkgs.python3Packages.poetry-core ];
propagatedBuildInputs = with pkgs.python3Packages; [ pyqtgraph numpy ];
};
kirdy_gui = pkgs.python3Packages.buildPythonPackage {
pname = "kirdy_gui";
version = "0.0.0";
format = "pyproject";
src = "${self}/pykirdy";
nativeBuildInputs = [ pkgs.qt6.wrapQtAppsHook ];
propagatedBuildInputs = [ pkgs.qt6.qtbase ] ++ (with pkgs.python3Packages; [ pyqtgraph pyqt6 qasync pglive aenum sipyco]);
dontWrapQtApps = true;
postFixup = ''
wrapQtApp "$out/bin/tec_qt"
'';
};
in {
packages.x86_64-linux = {
@@ -64,16 +92,14 @@
devShell.x86_64-linux = pkgs.mkShell {
name = "kirdy-dev-shell";
buildInputs = with pkgs; [
rustPlatform.rust.rustc
rustPlatform.rust.cargo
openocd dfu-util glibc
rust openocd dfu-util glibc
] ++ (with python3Packages; [
numpy matplotlib pyqtgraph
numpy matplotlib pyqtgraph setuptools pyqt6 qasync pglive aenum sipyco
]);
shellHook=
''
export QT_PLUGIN_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.dev.qtPluginPrefix}
export QML2_IMPORT_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.dev.qtQmlPrefix}
export QT_PLUGIN_PATH=${pkgs.qt6.qtbase}/${pkgs.qt6.qtbase.dev.qtPluginPrefix}
export QML2_IMPORT_PATH=${pkgs.qt6.qtbase}/${pkgs.qt6.qtbase.dev.qtQmlPrefix}
'';
};
defaultPackage.x86_64-linux = kirdy;

View File

@@ -2,9 +2,8 @@
MEMORY
{
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
/* reserved for config data */
CONFIG (rx) : ORIGIN = 0x8100000, LENGTH = 16K
/* The last flash sector is reserved for config */
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 896K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K - 4
/* reserved for DFU trigger message */
DFU_MSG (wrx) : ORIGIN = 0x2001BFFC, LENGTH = 4
@@ -14,6 +13,5 @@ MEMORY
}
_flash_start = ORIGIN(FLASH);
_config_start = ORIGIN(CONFIG);
_dfu_msg = ORIGIN(DFU_MSG);
_stack_start = ORIGIN(CCMRAM) + LENGTH(CCMRAM);

137
pykirdy/asyncio_exmaple.py Normal file
View File

@@ -0,0 +1,137 @@
from pprint import pp
from driver.kirdy import Kirdy, FilterConfig
import signal
import time
import asyncio
async def enter_dfu_mode(kirdy: Kirdy):
"""
Enter Device Firmware Upgrade(Dfu) mode
Please refer to README.md for firmware update instructions.
"""
await kirdy.device.dfu()
async def active_report(kirdy: Kirdy):
"""
Configure Kirdy to actively report status to connected socket
Press Ctrl + C to exit active report mode
"""
async for data in kirdy.report_mode():
pp(data)
async def device_cfg(kirdy: Kirdy):
"""
Configure Kirdy's network and board specific transconductance settings.
These configs are saved to flash immediately after command is processed.
"""
# Kirdy rev0_3's gain and transconductance varies between boards to maximize the
# PD current range resolution.
await kirdy.device.set_pd_mon_fin_gain(1.0)
await kirdy.device.set_pd_mon_transconductance(1/1000.0)
# Network Settings will be updated on next reboot.
await kirdy.device.set_ip_settings(
addr="192.168.1.128",
port=1337,
prefix_len=24,
gateway="192.168.1.1"
)
# Hard reset Kirdy.
await kirdy.device.hard_reset()
async def ld_thermostat_cfg(kirdy: Kirdy):
"""
Control and config laser diode and thermostat parameters.
"""
# Load the laser diode and thermostat settings from flash memory.
await kirdy.device.restore_settings_from_flash()
# Power off the laser diode & thermostat and clear any asserted alarm
await kirdy.laser.set_power_on(False)
await kirdy.laser.clear_alarm()
await kirdy.thermostat.set_power_on(False)
await kirdy.thermostat.clear_alarm()
# Set the laser diode terminals not to be shorted
await kirdy.laser.set_ld_terms_short(False)
# Do not power up the laser & thermostat during initial startup
await kirdy.laser.set_default_pwr_on(False)
await kirdy.thermostat.set_default_pwr_on(False)
await kirdy.laser.set_i(0)
# Configure the laser diode output power limit and photodiode parameters
# Exceeding the power limit triggers overpower protection alarm.
# The laser diode power will be cut off upon alarm assertion while the thermostat power remains unchanged.
await kirdy.laser.set_ld_pwr_limit(0.0)
await kirdy.laser.set_pd_mon_dark_current(0.0)
await kirdy.laser.set_pd_mon_responsitivity(0.0)
# Configure the thermostat NTC thermistor parameters.
await kirdy.thermostat.set_sh_r0(10.0 * 1000)
await kirdy.thermostat.set_sh_t0(25)
await kirdy.thermostat.set_sh_beta(3900)
# Configure the thermostat TEC settings.
# The actual output current is limited by value set below.
await kirdy.thermostat.set_tec_max_cooling_i(1.0)
await kirdy.thermostat.set_tec_max_heating_i(1.0)
await kirdy.thermostat.set_tec_max_v(4.0)
# Configure the thermostat temperature monitor limits.
# Exceeding the temperature limits trigger over temperature protection alarm.
# Both laser diode and thermostat power will be cut off upon alarm assertion.
await kirdy.thermostat.set_temp_mon_upper_limit(70)
await kirdy.thermostat.set_temp_mon_lower_limit(0)
# Configure the thermostat ADC Filter Setting / PID Update Rate / Report Rate.
# The ADC sampling rate determines the report and pid update rate.
# The chosen filter and sampling rate affects the noise of the readings.
# For details, please refer to the AD7172 datasheet.
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
# Configure the thermostat PID parameters.
# You can configure the PID parameter by the included autotune script.
# You should configure the filter sampling rate first before applying pid parameters.
await kirdy.thermostat.set_temperature_setpoint(25)
await kirdy.thermostat.set_pid_kp(0.15668282198105507)
await kirdy.thermostat.set_pid_ki(0.0001281321)
await kirdy.thermostat.set_pid_kd(13.82367)
await kirdy.thermostat.set_pid_output_max(1.0)
await kirdy.thermostat.set_pid_output_min(-1.0)
# Configure thermostat to run in PID control mode
await kirdy.thermostat.set_pid_control_mode()
# When control mode is switched from PID to constant current(CC) control mode,
# thermostat keeps its instantaneous output current unchanged.
# Thermostat output current should only be set if it is in CC control mode
# or the value set will be overwritten by PID output.
await kirdy.thermostat.set_constant_current_control_mode()
await kirdy.thermostat.set_tec_i_out(0.0)
# Save the current settings to flash memory
await kirdy.device.save_current_settings_to_flash()
# Power on the laser diode and thermostat
await kirdy.laser.set_power_on(True)
await kirdy.thermostat.set_power_on(True)
pp(await kirdy.device.get_settings_summary())
pp(await kirdy.device.get_status_report())
async def main():
kirdy = Kirdy()
kirdy.start_session(host='192.168.1.128', port=1337)
await kirdy.wait_until_connected()
await ld_thermostat_cfg(kirdy)
# await active_report(kirdy)
# await device_cfg(kirdy)
# await enter_dfu_mode(kirdy)
await kirdy.end_session()
if __name__ == "__main__":
asyncio.run(main())

981
pykirdy/driver/kirdy.py Normal file
View File

@@ -0,0 +1,981 @@
import types
import socket
import json
import logging
import time
from threading import Thread
from aenum import StrEnum, NoAlias
import queue
import asyncio
class _dt(StrEnum):
ip_settings = "ip_settings"
temp_adc_filter = "temp_adc_filter"
f32 = "data_f32"
bool = "data_bool"
none = "None"
class State(StrEnum):
disconnected = "disconnected"
connected = "connected"
class CmdList:
class device(StrEnum, settings=NoAlias):
_target = "device_cmd"
SetIPSettings = _dt.ip_settings
SetPdFinGain = _dt.f32
SetPdTransconductance = _dt.f32
SetActiveReportMode = _dt.bool
GetHwRev = _dt.none
GetStatusReport = _dt.none
GetSettingsSummary = _dt.none
Dfu = _dt.none
SaveFlashSettings = _dt.none
LoadFlashSettings = _dt.none
HardReset = _dt.none
class ld(StrEnum, settings=NoAlias):
_target = "laser_diode_cmd"
SetDefaultPowerOn = _dt.bool
PowerUp = _dt.none
PowerDown = _dt.none
LdTermsShort = _dt.none
LdTermsOpen = _dt.none
SetI = _dt.f32
SetPdResponsitivity = _dt.f32
SetPdDarkCurrent = _dt.f32
ApplyPdParams = _dt.none
SetLdPwrLimit = _dt.f32
ClearAlarm = _dt.none
class thermostat(StrEnum, settings=NoAlias):
_target = "thermostat_cmd"
SetDefaultPowerOn = _dt.bool,
PowerUp = _dt.f32,
PowerDown = _dt.f32,
SetTecMaxV = _dt.f32,
SetTecMaxIPos = _dt.f32,
SetTecMaxINeg = _dt.f32,
SetTecIOut = _dt.f32,
SetTemperatureSetpoint = _dt.f32,
SetPidEngage = _dt.none,
SetPidDisEngage = _dt.none,
SetPidKp = _dt.f32,
SetPidKi = _dt.f32,
SetPidKd = _dt.f32,
SetPidOutMin = _dt.f32,
SetPidOutMax = _dt.f32,
ConfigTempAdcFilter = _dt.temp_adc_filter,
GetPollInterval = _dt.none,
SetTempMonUpperLimit = _dt.f32,
SetTempMonLowerLimit = _dt.f32,
ClearAlarm = _dt.none,
SetShT0 = _dt.f32,
SetShR0 = _dt.f32,
SetShBeta = _dt.f32,
class FilterConfig:
class Sinc5Sinc1With50hz60HzRejection(StrEnum):
f27sps = "F27SPS"
f21sps = "F21SPS"
f20sps = "F20SPS"
f16sps = "F16SPS"
_odr_type = "sinc5sinc1postfilter"
def _filter_type(self):
return "Sinc5Sinc1With50hz60HzRejection"
@classmethod
def get_list_of_settings(cls):
ret = []
for e in cls:
if e not in [cls._odr_type]:
ret.append(e)
return ret
class Sinc5Sinc1(StrEnum):
f31250_0sps = "F31250_0SPS"
f15625_0sps = "F15625_0SPS"
f10417_0sps = "F10417_0SPS"
f5208_0sps = "F5208_0SPS"
f2597_0sps = "F2597_0SPS"
f1007_0sps = "F1007_0SPS"
f503_8sps = "F503_8SPS"
f381_0sps = "F381_0SPS"
f200_3sps = "F200_3SPS"
f100_2sps = "F100_2SPS"
f59_52sps = "F59_52SPS"
f49_68sps = "F49_68SPS"
f20_01sps = "F20_01SPS"
f16_63sps = "F16_63SPS"
f10_0sps = "F10_0SPS"
f5_0sps = "F5_0SPS"
f2_5sps = "F2_5SPS"
f1_25sps = "F1_25SPS"
_odr_type = "sinc5sinc1odr"
def _filter_type(self):
return "Sinc5Sinc1"
@classmethod
def get_list_of_settings(cls):
ret = []
for e in cls:
if e not in [cls._odr_type]:
ret.append(e)
return ret
class Sinc3(StrEnum):
f31250_0sps = "F31250_0SPS"
f15625_0sps = "F15625_0SPS"
f10417_0sps = "F10417_0SPS"
f5208_0sps = "F5208_0SPS"
f2597_0sps = "F2597_0SPS"
f1007_0sps = "F1007_0SPS"
f503_8sps = "F503_8SPS"
f381_0sps = "F381_0SPS"
f200_3sps = "F200_3SPS"
f100_2sps = "F100_2SPS"
f59_52sps = "F59_52SPS"
f49_68sps = "F49_68SPS"
f20_01sps = "F20_01SPS"
f16_63sps = "F16_63SPS"
f10_0sps = "F10_0SPS"
f5_0sps = "F5_0SPS"
f2_5sps = "F2_5SPS"
f1_25sps = "F1_25SPS"
_odr_type = "sinc3odr"
def _filter_type(self):
return "Sinc3"
@classmethod
def get_list_of_settings(cls):
ret = []
for e in cls:
if e not in [cls._odr_type]:
ret.append(e)
return ret
class Sinc3WithFineODR():
upper_limit = 31250
lower_limit = 1.907465
_odr_type = "sinc3fineodr"
def __init__(self, rate):
assert rate >= self.lower_limit and rate <= self.upper_limit
self.rate = float(rate)
def _filter_type(self):
return "Sinc3WithFineODR"
class InvalidDataType(Exception):
pass
class InvalidCmd(Exception):
pass
class Device:
def __init__(self, send_cmd_handler, send_raw_cmd_handler):
self._cmd = CmdList.device
self._send_cmd = send_cmd_handler
self._send_raw_cmd = send_raw_cmd_handler
async def set_ip_settings(self, addr="192.168.1.128", port=1337, prefix_len=24, gateway="192.168.1.1"):
"""
Upon command execution, the ip settings are saved into flash and are effective upon next reboot.
"""
try:
socket.inet_aton(addr)
socket.inet_aton(gateway)
except OSError:
raise InvalidDataType
addr = addr.split(".")
gateway = gateway.split(".")
if not(isinstance(port, int) and isinstance(prefix_len, int)):
raise InvalidDataType
return await self._send_raw_cmd(
{
"device_cmd": "SetIPSettings",
"ip_settings": {
"addr": [int(x) for x in addr],
"port": port,
"prefix_len": prefix_len,
"gateway": [int(x) for x in gateway],
}
}
)
async def set_active_report_mode(self, on):
"""
Set active report to be on. If it is on, Kirdy will send status report
to the client socket according to the temperature polling rate set.
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetActiveReportMode, on)
async def set_pd_mon_fin_gain(self, gain):
"""
Configure the photodiode monitor final analog front-end stage gain.
- gain: unitless
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPdFinGain, gain)
async def set_pd_mon_transconductance(self, transconductance):
"""
Configure the photodiode monitor transconductance value.
- transconductance: 1/Ohm
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPdTransconductance, transconductance)
async def get_hw_rev(self):
"""
Get hardware revision of the connected Kirdy
{
'msg_type': 'HwRev',
'hw_rev': {
'major': 0,
'minor': 3
}
}
"""
return await self._send_cmd(self._cmd._target, self._cmd.GetHwRev, msg_type="HwRev")
async def get_status_report(self, sig=None):
"""
Get status of all peripherals in a json object.
{
'ts': 227657, # Relative Timestamp (ms)
'msg_type': 'Report' # Indicate it is a 'Report' json object
'laser': {
'pwr_on': False, # Laser Power is On (True/False)
'pwr_excursion': False, # Was Laser experienced an Overpowered Condition? (True/False)
'ld_i_set': 0.0, # Laser Diode Output Current (A)
'pd_i': 2.0000002e-06, # Internal Photodiode Monitor current (A)
'pd_pwr': None, # Power Readings from Internal Photodiode (W). Return None if pd_mon parameter(s) are not defined.
'term_50ohm': 'On' # Is the Low Frequency Modulation Input's Impedance 50 Ohm? ("On"/"Off")
},
'thermostat': {
'pwr_on': False, # Tec Power is On (True/False)
'pid_engaged': False, # Is Pid_Engaged. If False, it is in Constant Current Mode (True/False)
'temp_mon_status': { # Temperature Monitor:
'status': 'Off', # "Off": Power is Off
# "ConstantCurrentMode": Thermostat is regulated in CC mode
# "PidStartUp": PID Regulation is not stable
# "PidStable": PID Regulation is stable and the temperature is within +-1mK to the setpoint
# "OverTempAlarm": Overtemperature Alarm is triggered
'over_temp_alarm': False # Was Laser Diode experienced an Overtemperature condition (True/False)
},
'temperature': 25.03344, # Temperature Readings (Degree Celsius)
'i_set': 0.0, # Tec Current Set by User/PID Controller(A)
'tec_i': 0.0024998188, # Tec Current Readings (A)
'tec_v': -0.00399971 # Tec Voltage Readings (V)
}
}
"""
return await self._send_cmd(self._cmd._target, self._cmd.GetStatusReport, msg_type="Report", sig=sig)
async def get_settings_summary(self, sig=None):
"""
Get the current settings of laser and thermostat in a json object.
{
'msg_type': 'Settings', # Indicate it is a 'Settings' json object
'laser': {
'default_pwr_on': False, # Power On Laser Diode at Startup
'ld_drive_current': { # Laser Diode Output Current (A)
'value': 0.0, # Value Set
'max': 0.3 # Max Value Settable
},
'pd_mon_params': { # Photodiode Parameters
'transconductance': 0.000115258765 # Board Specific Transconductance (1/ohm)
'responsitivity': 0.0141, # Responsitivity (A/W)
'i_dark': 0.0 # Max Value Settable (A)
},
'ld_pwr_limit': { # Laser Diode Power Limit (W)
'value': 0.00975, # Value Set
'max': 0.023321507 # Max Value Settable
},
'ld_terms_short: False, # Is Laser Diode Terminals short? (True/False)
},
'thermostat': {
'default_pwr_on': True, # Power on Thermostat at Startup
'pid_engaged': True, # True: PID Control Mode | False Constant Current Mode
'temperature_setpoint': 25.0, # Temperature Setpoint (Degree Celsius)
'tec_settings': {
'i_set': { # Current TEC Current Set by PID Controller/User (A)
'value': 0.04330516, # Value Set
'max': 3.0 # Max Value Settable
},
'max_v': { # Max Voltage Across Tec Terminals (V)
'value': 4.00000000, # Value Set
'max': 4.3 # Max Value Settable
},
'max_i_pos': { # Max Cooling Current Across Tec Terminals (A)
'value': 0.99628574, # Value Set
'max': 3.0 # Max Value Settable
},
'max_i_neg': { # Max Heating Current Across Tec Terminals (A)
'value': 0.99628574, # Value Set
'max': 3.0 # Max Value Settable
}
},
'pid_params': { # PID Controller Parameters
'kp': 0.15668282, # Proportional Gain
'ki': 0.0021359625, # Integral Gain
'kd': 0.8292545, # Derivative Gain
'output_min': -1.0, # Minimum Current Output (A)
'output_max': 1.0 # Maximum Current Output (A)
},
'temp_adc_settings': { # Temperature ADC Settings (Please read AD7172-2 Documentation)
'filter_type': 'Sinc5Sinc1With50hz60HzRejection', # Filter Types
'sinc5sinc1odr': None, # (Unused)
'sinc3odr': None, # (Unused)
'sinc5sinc1postfilter': None, # (Unused)
'sinc3fineodr': None, # (Unused)
'rate': 16.67 # ADC Sampling Rate (Hz)
},
'temp_mon_settings': { # Temperature Monitor Settings
'upper_limit': 40.0, # Temperature Upper Limit (Degree Celsius)
'lower_limit': 10.0 # Temperature Lower Limit (Degree Celsius)
},
'thermistor_params': { # Thermistor Steinhart-Hart equation parameters
't0': 25.0, # t0: Degree Celsius
'r0': 10000.0, # r0: Ohm
'b': 3900.0 # b: (unitless)
}
}
}
"""
return await self._send_cmd(self._cmd._target, self._cmd.GetSettingsSummary, msg_type="Settings", sig=sig)
async def dfu(self):
"""
Hard reset and put the connected Kirdy into the Dfu mode for firmware update.
"""
return await self._send_cmd(self._cmd._target, self._cmd.Dfu)
async def save_current_settings_to_flash(self):
"""
Save the current laser diode and thermostat configurations into flash.
"""
return await self._send_cmd(self._cmd._target, self._cmd.SaveFlashSettings)
async def restore_settings_from_flash(self):
"""
Restore the laser diode and thermostat settings from flash.
"""
return await self._send_cmd(self._cmd._target, self._cmd.LoadFlashSettings)
async def hard_reset(self):
"""
Hard Reset Kirdy. The socket connection will be closed by Kirdy.
Laser diode power and Tec power will be turned off.
Kirdy will send out a json({'msg_type': 'HardReset'}) to all sockets before hard reset take place.
"""
return await self._send_cmd(self._cmd._target, self._cmd.HardReset)
class Laser:
def __init__(self, send_cmd_handler):
self._cmd = CmdList.ld
self._send_cmd = send_cmd_handler
async def set_power_on(self, on):
"""
Power Up or Power Down laser diode. Powering up the Laser Diode resets the pwr_excursion status
- on (True/False)
"""
if on:
return await self._send_cmd(self._cmd._target, self._cmd.PowerUp, None)
else:
return await self._send_cmd(self._cmd._target, self._cmd.PowerDown, None)
async def set_default_pwr_on(self, on):
"""
Set whether laser diode is powered up at Startup.
- on (True/False)
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetDefaultPowerOn, on)
async def set_ld_terms_short(self, short):
"""
Open/Short laser diode terminals.
- on (True/False)
"""
if short:
return await self._send_cmd(self._cmd._target, self._cmd.LdTermsShort, None)
else:
return await self._send_cmd(self._cmd._target, self._cmd.LdTermsOpen, None)
async def set_i(self, i):
"""
Set laser diode output current: Max(0, Min(i_set, 300mA)).
- i: A
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetI, i)
async def set_pd_mon_responsitivity(self, responsitivity):
"""
Configure the photodiode monitor responsitivity parameter.
The value is only effective if ApplyPdParams cmd is issued.
- responsitivity: A/W
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPdResponsitivity, responsitivity)
async def set_pd_mon_dark_current(self, dark_current):
"""
Configure the photodiode monitor dark current parameter.
The value is only effective if ApplyPdParams cmd is issued.
- dark_current: A
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPdDarkCurrent, dark_current)
async def apply_pd_params(self):
"""
Evaluate and apply photodiode monitor parameters that are set with SetPdDarkCurrent and SetPdResponsitivity cmd.
After Kirdy receives the cmd, it will check if the current power limit is within the newly calculated
power limit range. If it is out of range, the photodiode monitor parameters remains unchanged and Kirdy
sends out a "InvalidSettings" message along with an error message.
"""
return await self._send_cmd(self._cmd._target, self._cmd.ApplyPdParams)
async def set_ld_pwr_limit(self, pwr_limit):
"""
Set the power limit for the power excursion monitor.
If the power limit settings is out of range, power limit remains unchanged and Kirdy
sends out a "InvalidSettings" message along with an error message.
If the calculated power with the params of pd_mon > pwr_limit,
overpower protection is triggered.
- pwr_limit: W
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetLdPwrLimit, pwr_limit)
async def clear_alarm(self):
"""
Clear the power excursion monitor alarm.
"""
return await self._send_cmd(self._cmd._target, self._cmd.ClearAlarm)
class Thermostat:
def __init__(self, send_cmd_handler, send_raw_cmd_handler):
self._cmd = CmdList.thermostat
self._send_cmd = send_cmd_handler
self._send_raw_cmd = send_raw_cmd_handler
async def set_power_on(self, on):
"""
Power up or power down thermostat.
- Powering up the thermostat resets the pwr_excursion status
"""
if on:
return await self._send_cmd(self._cmd._target, self._cmd.PowerUp, None)
else:
return await self._send_cmd(self._cmd._target, self._cmd.PowerDown, None)
async def set_default_pwr_on(self, on):
"""
Set whether thermostat is powered up at Startup.
- on: (True/False)
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetDefaultPowerOn, on)
async def set_tec_max_v(self, max_v):
"""
Set Tec Maximum Voltage Across the TEC Terminals.
- max_v: V
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetTecMaxV, max_v)
async def set_tec_max_cooling_i(self, max_i_pos):
"""
Set Tec maximum cooling current (Settable Range: 0.0 - 3.0)
- max_i_pos: A
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetTecMaxIPos, max_i_pos)
async def set_tec_max_heating_i(self, max_i_neg):
"""
Set Tec maximum heating current (Settable Range: 0.0 - 3.0)
- max_i_neg: A
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetTecMaxINeg, max_i_neg)
async def set_tec_i_out(self, i_out):
"""
Set Tec Output Current (Settable Range: 0.0 - 3.0)
This cmd is only effective in constant current control mode
or your newly set value will be overwritten by PID Controller Output
- i_out: A
"""
if isinstance(i_out, float):
return await self._send_raw_cmd({"tec_set_i": i_out})
elif isinstance(i_out, int):
return await self._send_raw_cmd({"tec_set_i": float(i_out)})
else:
raise InvalidDataType
async def set_constant_current_control_mode(self):
"""
Disable PID Controller and output current can be controlled with set_tec_i_out() cmd.
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPidDisEngage, None)
async def set_temperature_setpoint(self, temperature):
"""
Set Temperature Setpoint for PID Controller. This parameter is not active in constant current control mode
- temperature: Degree Celsius
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetTemperatureSetpoint, temperature)
async def set_pid_control_mode(self):
"""
Enable PID Controller. Its PID Update Interval is controlled by the Temperature ADC polling rate.
Please refer to config_temp_adc_filter for the possible polling rate options
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPidEngage, None)
async def set_pid_kp(self, kp):
"""
Set Kp parameter for PID Controller
kp: (unitless)
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPidKp, kp)
async def set_pid_ki(self, ki):
"""
Set Ki parameter for PID Controller
ki: (unitless)
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPidKi, ki)
async def set_pid_kd(self, kd):
"""
Set Kd parameter for PID Controller
kd: (unitless)
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPidKd, kd)
async def set_pid_output_max(self, out_max):
"""
Set max output limit at the PID Output
- out_max: A
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPidOutMax, out_max)
async def set_pid_output_min(self, out_min):
"""
Set min output limit at the PID Output
- out_min: A
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetPidOutMin, out_min)
async def set_temp_mon_upper_limit(self, upper_limit):
"""
Set Temperature Monitor Upper Limit Threshold. Exceeding the limit for too long
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
- upper_limit: Degree Celsius
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetTempMonUpperLimit, upper_limit)
async def set_temp_mon_lower_limit(self, lower_limit):
"""
Set Temperature Monitor Lower Limit Threshold. Exceeding the limit for too long
will force the TEC Controller, PID Controller and Laser Diode Power to Shutdown
- lower_limit: Degree Celsius
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetTempMonLowerLimit, lower_limit)
async def clear_alarm(self):
"""
Clear the temperature monitor alarm
"""
return await self._send_cmd(self._cmd._target, self._cmd.ClearAlarm)
async def set_sh_t0(self, t0):
"""
Set t0 Steinhart-Hart parameter for the laser diode NTC
- t0: Degree Celsius
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetShT0, t0)
async def set_sh_r0(self, r0):
"""
Set r0 Steinhart-Hart parameter for the laser diode NTC
- r0: Ohm
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetShR0, r0)
async def set_sh_beta(self, beta):
"""
Set beta Steinhart-Hart parameter for the laser diode NTC
- beta: (unitless)
"""
return await self._send_cmd(self._cmd._target, self._cmd.SetShBeta, beta)
async def config_temp_adc_filter(self, filter_config):
"""
Configure the temperature adc filter type and sampling rate.
Please refer to AD7172 datasheet for the usage of various types of filter.
The actual temperature polling rate is bottlenecked by the processing speed of the MCU and
performs differently under different kinds of workload. Please verify the polling rate with
the timestamp.
"""
cmd = {}
cmd[self._cmd._target] = self._cmd.ConfigTempAdcFilter.name
if hasattr(filter_config, 'rate'):
cmd[self._cmd.ConfigTempAdcFilter] = {
"filter_type": filter_config._filter_type(),
filter_config._odr_type: filter_config.rate,
}
else:
cmd[self._cmd.ConfigTempAdcFilter] = {
"filter_type": filter_config._filter_type(),
filter_config._odr_type: filter_config,
}
return await self._send_raw_cmd(cmd)
async def get_poll_interval(self):
return await self._send_cmd(self._cmd._target, self._cmd.GetPollInterval, msg_type="Interval")
class Kirdy:
def __init__(self):
self.device = Device(self._send_cmd, self._send_raw_cmd)
self.laser = Laser(self._send_cmd)
self.thermostat = Thermostat(self._send_cmd, self._send_raw_cmd)
self.hw_rev = None
self._task_queue, self._int_msg_queue, self._report_queue = None, None, None
self._timeout = 5.0
self._writer, self._reader = None, None
self._event_loop = None
self._msg_queue_get_report = False
self._report_mode_on = False
self._state = State.disconnected
self.read_response_task, self.handler_task = None, None
self._lock = asyncio.Lock()
# PyQt Signal
self._report_sig = None # Dict
self._connected_sig = None # Bool
self._err_msg_sig = None # Str
self.connected_event = None
def get_hw_rev(self):
return self.hw_rev
def set_report_sig(self, sig):
"""
Connect a PyQt Signal to the active report output(dict). This should be configured before the session is started.
"""
self._report_sig = sig
def set_connected_sig(self, sig):
"""
Connect a PyQt Signal to connection status(bool)
- True: Connection is established
- False: Connection is dropped
"""
self._connected_sig = sig
def set_err_msg_sig(self, sig):
"""
Emit a error message to a PyQt Signal(str) when a cmd fails to execute
"""
self._err_msg_sig = sig
def start_session(self, host='192.168.1.128', port=1337):
"""
Start Kirdy Connection Session.
In case of disconnection, all the queued tasks are cleared and the handler task retries TCP connection indefinitely.
- host: Kirdy's IP Address
- port: Kirdy's Port Number
"""
self._host, self._ctrl_port = host, port
if self._event_loop is None:
try:
self._event_loop = asyncio.get_running_loop()
except:
self._event_loop = asyncio.new_event_loop()
self._event_loop.run_forever()
self.connected_event = asyncio.Event()
self.handler_task = asyncio.create_task(self._handler())
async def end_session(self, block=False):
"""
Stop Kirdy's TCP connection and its associated thread.
"""
if self._event_loop is not None:
if block:
await self._task_queue.join()
if self.read_response_task is not None:
self.read_response_task.cancel()
await self.read_response_task
self.read_response_task = None
if self.handler_task is not None:
self.handler_task.cancel()
await self.handler_task
self.handler_task = None
self._writer = None
if self._connected_sig is not None:
self._connected_sig.emit(False)
def connecting(self):
"""
Return True if client is connecting
"""
return self._state == State.disconnected and self.read_response_task is not None
def connected(self):
"""
Returns True if client is connected.
"""
return self._writer is not None
async def wait_until_connected(self):
if not(self.connected()):
await self.connected_event.wait()
async def report_mode(self):
"""
Enable and retrieve active report from Kirdy
"""
if self.connected():
self._report_mode_on = True
await self.device.set_active_report_mode(True)
report = None
while self._report_mode_on:
report = await self._report_queue.get()
if not(isinstance(report, dict)):
self.stop_active_report()
else:
yield report
if isinstance(report, dict):
await self.device.set_active_report_mode(False)
else:
raise ConnectionError
def stop_report_mode(self):
self._report_mode_on = False
def task_dispatcher(self, awaitable_fn):
"""
Enqueue a task to be handled by the handler.
"""
if self.connected():
try:
self._task_queue.put_nowait(lambda: awaitable_fn)
return True
except asyncio.queues.QueueFull:
return False
else:
raise ConnectionError
async def _sock_disconnection_handling(self):
# Reader needn't be closed
try:
self._writer.close()
await self._writer.wait_closed()
except:
# In Hard Reset/DFU cmd, Kirdy may close its socket first
pass
self._reader = None
self._writer = None
for i in range(self._report_queue.maxsize):
if self._report_queue.full():
self._report_queue.get_nowait()
self._report_queue.put_nowait(None)
if self._connected_sig is not None:
self._connected_sig.emit(False)
async def _handler(self):
try:
self._state = State.disconnected
first_con = True
task = None
while True:
if self._state == State.disconnected:
try:
self.hw_rev = None
await self.__coninit(self._timeout)
self.read_response_task = asyncio.create_task(self._read_response_handler())
task = None
logging.info("Connected to %s:%d", self._host, self._ctrl_port)
hw_rev = await self.device.get_hw_rev()
self.hw_rev = hw_rev["hw_rev"]
if self._connected_sig is not None:
self._connected_sig.emit(True)
self.connected_event.set()
# State Transition
self._state = State.connected
except (OSError, TimeoutError):
if first_con:
first_con = False
logging.warning("Cannot connect to %s:%d. Retrying in the background.", self._host, self._ctrl_port)
await asyncio.sleep(5.0)
elif self._state == State.connected:
try:
task = await self._task_queue.get()
if isinstance(task, Exception):
raise task
await task()
self._task_queue.task_done()
except (TimeoutError, ConnectionResetError, ConnectionError):
logging.warning("Connection to Kirdy is dropped.")
first_con = True
self.read_response_task.cancel()
# State Transition
self._state = State.disconnected
await self._sock_disconnection_handling()
except asyncio.exceptions.CancelledError:
pass
except:
logging.warning("Handler experienced an error.", exc_info=True)
await self._sock_disconnection_handling()
async def _read_response_handler(self):
try:
while True:
if self._report_mode_on:
response = await asyncio.wait_for(self._read_response(), self._timeout)
else:
response = await self._read_response()
if response["msg_type"] == 'HardReset':
logging.warn("Kirdy is being hard reset.")
raise asyncio.exceptions.CancelledError
if response["msg_type"] == 'Dfu':
logging.warn("Kirdy enters Dfu Mode.")
asyncio.create_task(self.end_session())
if response["msg_type"] == 'ConnectionClose':
logging.warn("Kirdy runs out of TCP sockets and closes this connected socket.")
asyncio.create_task(self.end_session())
if response["msg_type"] == 'Report' and not self._msg_queue_get_report:
if self._report_sig is None:
self._report_queue.put_nowait_overwrite(response)
else:
self._report_sig.emit(response)
else:
if self._msg_queue_get_report:
self._msg_queue_get_report = False
self._int_msg_queue.put_nowait_overwrite(response)
except asyncio.exceptions.CancelledError:
pass
except (TimeoutError, ConnectionResetError, ConnectionError) as exec:
self._task_queue.put_nowait_overwrite(exec)
self._int_msg_queue.put_nowait_overwrite(exec)
except Exception as exec:
logging.warn("Read Response Handler experienced an error. Exiting.", exc_info=True)
self._task_queue.put_nowait_overwrite(exec)
self._int_msg_queue.put_nowait_overwrite(exec)
if self._report_mode_on:
self._report_mode_on = False
self._report_queue.put_nowait_overwrite(TimeoutError)
async def _stop_handler(self):
for task in asyncio.all_tasks():
task.cancel()
await asyncio.gather(*asyncio.all_tasks(), loop=self._event_loop)
async def __coninit(self, timeout):
def _put_nowait_overwrite(self, item):
if self.full():
self.get_nowait()
self.put_nowait(item)
asyncio.Queue.put_nowait_overwrite = _put_nowait_overwrite
if self._task_queue is not None:
while not(self._task_queue.empty()):
task = self._task_queue.get_nowait()
if isinstance(task, types.FunctionType):
task().close()
else:
self._task_queue = asyncio.Queue(maxsize=16)
self._int_msg_queue = asyncio.Queue(maxsize=4)
self._report_queue = asyncio.Queue(maxsize=16)
self._reader, self._writer = await asyncio.wait_for(asyncio.open_connection(self._host, self._ctrl_port), timeout)
writer_sock = self._writer.get_extra_info("socket")
writer_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
async def _read_response(self):
raw_response = b''
while len(raw_response) == 0:
# Ignore 0 size packet
raw_response = await self._reader.readuntil()
response = raw_response.decode('utf-8', errors='ignore').split("\n")
return json.loads(response[0])
def _response_handling(self, msg, msg_type, sig=None):
if msg["msg_type"] in ["InvalidCmd", "InvalidDatatype"]:
raise InvalidCmd
elif msg["msg_type"] == msg_type:
if sig is not None:
sig.emit(msg)
else:
logging.warn(f"Commands fail to execute. {msg['msg_type']}:{msg['msg']}")
if self._err_msg_sig is not None and msg['msg'] is not None:
self._err_msg_sig.emit(msg['msg'])
return msg
async def _send_raw_cmd(self, cmd, msg_type="Acknowledge", sig=None):
if self.connected():
async with self._lock:
self._writer.write(bytes(json.dumps(cmd), "UTF-8"))
await self._writer.drain()
msg = await asyncio.wait_for(self._int_msg_queue.get(), self._timeout)
return self._response_handling(msg, msg_type, sig)
else:
raise ConnectionError
async def _send_cmd(self, target, cmd, data=None, msg_type="Acknowledge", sig=None):
cmd_dict = {}
cmd_dict[target] = cmd.name
if cmd == _dt.f32:
if isinstance(data, float):
cmd_dict[cmd] = data
elif isinstance(data, int):
cmd_dict[cmd] = float(data)
elif cmd == _dt.bool:
if isinstance(data, bool):
cmd_dict[cmd] = data
else:
raise InvalidDataType
elif cmd == "None":
pass
if msg_type == 'Report':
self._msg_queue_get_report = True
async with self._lock:
self._writer.write(bytes(json.dumps(cmd_dict), "UTF-8"))
await self._writer.drain()
msg = await asyncio.wait_for(self._int_msg_queue.get(), self._timeout)
if isinstance(msg, Exception):
raise msg
return self._response_handling(msg, msg_type, sig)

1192
pykirdy/kirdy_qt.py Normal file

File diff suppressed because it is too large Load Diff

358
pykirdy/pid_autotune.py Normal file
View File

@@ -0,0 +1,358 @@
import math
import logging
from collections import deque, namedtuple
from enum import Enum
import socket
import json
import time
import signal
from driver.kirdy import Kirdy, FilterConfig
import asyncio
from sipyco.asyncio_tools import SignalHandler
# Based on hirshmann pid-autotune libiary
# See https://github.com/hirschmann/pid-autotune
# Which is in turn based on a fork of Arduino PID AutoTune Library
# See https://github.com/t0mpr1c3/Arduino-PID-AutoTune-Library
class PIDAutotuneState(Enum):
STATE_OFF = 'off'
STATE_RELAY_STEP_UP = 'relay step up'
STATE_RELAY_STEP_DOWN = 'relay step down'
STATE_SUCCEEDED = 'succeeded'
STATE_FAILED = 'failed'
STATE_READY = 'ready'
class PIDAutotune:
PIDParams = namedtuple('PIDParams', ['Kp', 'Ki', 'Kd'])
PEAK_AMPLITUDE_TOLERANCE = 0.05
_tuning_rules = {
"ziegler-nichols": [0.6, 1.2, 0.075],
"tyreus-luyben": [0.4545, 0.2066, 0.07214],
"ciancone-marlin": [0.303, 0.1364, 0.0481],
"pessen-integral": [0.7, 1.75, 0.105],
"some-overshoot": [0.333, 0.667, 0.111],
"no-overshoot": [0.2, 0.4, 0.0667]
}
def __init__(self, setpoint, out_step=10, lookback=60,
noiseband=0.5, sampletime=1.2):
if setpoint is None:
raise ValueError('setpoint must be specified')
self._inputs = deque(maxlen=round(lookback / sampletime))
self._sampletime = sampletime
self._setpoint = setpoint
self._outputstep = out_step
self._noiseband = noiseband
self._out_min = -out_step
self._out_max = out_step
self._state = PIDAutotuneState.STATE_OFF
self._peak_timestamps = deque(maxlen=5)
self._peaks = deque(maxlen=5)
self._output = 0
self._last_run_timestamp = 0
self._peak_type = 0
self._peak_count = 0
self._initial_output = 0
self._induced_amplitude = 0
self._Ku = 0
self._Pu = 0
def setParam(self, target, step, noiseband, sampletime, lookback):
self._setpoint = target
self._outputstep = step
self._out_max = step
self._out_min = -step
self._noiseband = noiseband
self._inputs = deque(maxlen=round(lookback / sampletime))
self._sampletime = sampletime
def setReady(self):
self._state = PIDAutotuneState.STATE_READY
self._peak_count = 0
def setOff(self):
self._state = PIDAutotuneState.STATE_OFF
def setFailed(self):
self._state = PIDAutotuneState.STATE_FAILED
self._peak_count = 30
def state(self):
"""Get the current state."""
return self._state
def output(self):
"""Get the last output value."""
return self._output
def tuning_rules(self):
"""Get a list of all available tuning rules."""
return self._tuning_rules.keys()
def get_tec_pid (self):
divisors = self._tuning_rules["tyreus-luyben"]
kp = self._Ku * divisors[0]
ki = divisors[1] * self._Ku / self._Pu / (1 / self._sampletime)
kd = divisors[2] * self._Ku * self._Pu * (1 / self._sampletime)
return kp, ki, kd
def get_pid_parameters(self, tuning_rule='ziegler-nichols'):
"""Get PID parameters.
Args:
tuning_rule (str): Sets the rule which should be used to calculate
the parameters.
"""
divisors = self._tuning_rules[tuning_rule]
kp = self._Ku * divisors[0]
ki = divisors[1] * self._Ku / self._Pu / (1 / self._sampletime)
kd = divisors[2] * self._Ku * self._Pu * (1 / self._sampletime)
return PIDAutotune.PIDParams(kp, ki, kd)
def run(self, input_val, time_input):
"""To autotune a system, this method must be called periodically.
Args:
input_val (float): The temperature input value.
time_input (float): Current time in seconds.
Returns:
`true` if tuning is finished, otherwise `false`.
"""
now = time_input * 1000
if (self._state == PIDAutotuneState.STATE_OFF
or self._state == PIDAutotuneState.STATE_SUCCEEDED
or self._state == PIDAutotuneState.STATE_FAILED
or self._state == PIDAutotuneState.STATE_READY):
self._state = PIDAutotuneState.STATE_RELAY_STEP_UP
self._last_run_timestamp = now
# check input and change relay state if necessary
if (self._state == PIDAutotuneState.STATE_RELAY_STEP_UP
and input_val > self._setpoint + self._noiseband):
self._state = PIDAutotuneState.STATE_RELAY_STEP_DOWN
logging.debug('switched state: {0}'.format(self._state))
logging.debug('input: {0}'.format(input_val))
elif (self._state == PIDAutotuneState.STATE_RELAY_STEP_DOWN
and input_val < self._setpoint - self._noiseband):
self._state = PIDAutotuneState.STATE_RELAY_STEP_UP
logging.debug('switched state: {0}'.format(self._state))
logging.debug('input: {0}'.format(input_val))
# set output
if (self._state == PIDAutotuneState.STATE_RELAY_STEP_UP):
self._output = self._initial_output - self._outputstep
elif self._state == PIDAutotuneState.STATE_RELAY_STEP_DOWN:
self._output = self._initial_output + self._outputstep
# respect output limits
self._output = min(self._output, self._out_max)
self._output = max(self._output, self._out_min)
# identify peaks
is_max = True
is_min = True
for val in self._inputs:
is_max = is_max and (input_val >= val)
is_min = is_min and (input_val <= val)
self._inputs.append(input_val)
# we don't trust the maxes or mins until the input array is full
if len(self._inputs) < self._inputs.maxlen:
return False
# increment peak count and record peak time for maxima and minima
inflection = False
# peak types:
# -1: minimum
# +1: maximum
if is_max:
if self._peak_type == -1:
inflection = True
self._peak_type = 1
elif is_min:
if self._peak_type == 1:
inflection = True
self._peak_type = -1
# update peak times and values
if inflection:
self._peak_count += 1
self._peaks.append(input_val)
self._peak_timestamps.append(now)
logging.debug('found peak: {0}'.format(input_val))
logging.debug('peak count: {0}'.format(self._peak_count))
# check for convergence of induced oscillation
# convergence of amplitude assessed on last 4 peaks (1.5 cycles)
self._induced_amplitude = 0
if inflection and (self._peak_count > 4):
abs_max = self._peaks[-2]
abs_min = self._peaks[-2]
for i in range(0, len(self._peaks) - 2):
self._induced_amplitude += abs(self._peaks[i]
- self._peaks[i+1])
abs_max = max(self._peaks[i], abs_max)
abs_min = min(self._peaks[i], abs_min)
self._induced_amplitude /= 6.0
# check convergence criterion for amplitude of induced oscillation
amplitude_dev = ((0.5 * (abs_max - abs_min)
- self._induced_amplitude)
/ self._induced_amplitude)
logging.debug('amplitude: {0}'.format(self._induced_amplitude))
logging.debug('amplitude deviation: {0}'.format(amplitude_dev))
if amplitude_dev < PIDAutotune.PEAK_AMPLITUDE_TOLERANCE:
self._state = PIDAutotuneState.STATE_SUCCEEDED
# if the autotune has not already converged
# terminate after 10 cycles
if self._peak_count >= 20:
self._output = 0
self._state = PIDAutotuneState.STATE_FAILED
return True
if self._state == PIDAutotuneState.STATE_SUCCEEDED:
self._output = 0
logging.debug('peak finding successful')
# calculate ultimate gain
self._Ku = 4.0 * self._outputstep / \
(self._induced_amplitude * math.pi)
print('Ku: {0}'.format(self._Ku))
# calculate ultimate period in seconds
period1 = self._peak_timestamps[3] - self._peak_timestamps[1]
period2 = self._peak_timestamps[4] - self._peak_timestamps[2]
self._Pu = 0.5 * (period1 + period2) / 1000.0
print('Pu: {0}'.format(self._Pu))
for rule in self._tuning_rules:
params = self.get_pid_parameters(rule)
print('rule: {0}'.format(rule))
print('Kp: {0}'.format(params.Kp))
print('Ki: {0}'.format(params.Ki))
print('Kd: {0}'.format(params.Kd))
return True
return False
async def main():
"""
PID AutoTune Tools for Kirdy
The obtained temperature works best at the target temperature specified.
Before running PID AutoTune, please
1. Secure the laser diode onto the LD adapter and copper heat sink
2. Make sure Kirdy has warmed up and reached thermal equilibrium state
In case of PID Autotune Failure, you can
1. Run the PID Autotune again
2. Or increase the lookback period
3. Or increase the sampling rate
"""
# Target temperature of the autotune routine, celsius
target_temperature = 20
# Value by which output will be increased/decreased from zero, amps
output_step = 1
# Reference period for local minima/maxima, seconds
lookback = 2.0
# Determines by how much the input value must
# overshoot/undershoot the setpoint, celsius
noiseband = 2.0
kirdy = Kirdy()
kirdy.start_session(host='192.168.1.126', port=1337)
await kirdy.wait_until_connected()
while not(kirdy.connected()):
pass
await kirdy.laser.set_power_on(False)
await kirdy.laser.set_i(0)
await kirdy.thermostat.set_power_on(False)
await kirdy.thermostat.set_constant_current_control_mode()
await kirdy.thermostat.set_tec_i_out(0)
await kirdy.thermostat.clear_alarm()
signal_handler = SignalHandler()
signal_handler.setup()
async def sig_handling():
await signal_handler.wait_terminate()
tuner.setFailed()
asyncio.create_task(sig_handling())
await kirdy.device.set_active_report_mode(False)
# Configure the Thermistor Parameters
await kirdy.thermostat.set_sh_beta(3950)
await kirdy.thermostat.set_sh_r0(10.0 * 1000)
await kirdy.thermostat.set_sh_t0(25)
# Set a large enough temperature range so that it won't trigger overtemperature protection
await kirdy.thermostat.set_temp_mon_upper_limit(target_temperature + 20)
await kirdy.thermostat.set_temp_mon_lower_limit(target_temperature - 20)
await kirdy.thermostat.set_tec_max_cooling_i(output_step)
await kirdy.thermostat.set_tec_max_heating_i(output_step)
# The Polling Rate of Temperature Adc is equal to the PID Update Interval
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
settings = await kirdy.device.get_settings_summary()
sampling_rate = settings["thermostat"]["temp_adc_settings"]["rate"]
print("Settings: {0}".format(settings))
tuner = PIDAutotune(target_temperature, output_step,
lookback, noiseband, 1/sampling_rate)
await kirdy.thermostat.set_power_on(True)
while True:
status_report = await kirdy.device.get_status_report()
temperature = status_report["thermostat"]["temperature"]
ts = status_report['ts']
print("Ts: {0} Current Temperature: {1} degree".format(ts, temperature))
if (tuner.run(temperature, ts / 1000.0)):
print(tuner._state)
break
tuner_out = tuner.output()
await kirdy.thermostat.set_tec_i_out(float(tuner_out))
await kirdy.thermostat.set_tec_i_out(0)
await kirdy.thermostat.set_power_on(False)
if tuner.state() == PIDAutotuneState.STATE_SUCCEEDED:
pid_params = tuner.get_pid_parameters(tuning_rule="tyreus-luyben")
await kirdy.thermostat.set_pid_kp(pid_params.Kp)
await kirdy.thermostat.set_pid_ki(pid_params.Ki)
await kirdy.thermostat.set_pid_kd(pid_params.Kd)
await kirdy.thermostat.set_pid_output_max(1.0)
await kirdy.thermostat.set_pid_output_min(1.0)
await kirdy.end_session()
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,210 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Cfg_Adc_Filter_Form</class>
<widget class="QDialog" name="Cfg_Adc_Filter_Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>786</width>
<height>303</height>
</rect>
</property>
<property name="windowTitle">
<string>Config Temperature ADC Filter</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>731</width>
<height>251</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3,4,4">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Value</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Readings</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="filter_type_layout" stretch="3,4,4">
<item>
<widget class="QLabel" name="filter_type_lbl">
<property name="text">
<string>Filter Type</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="filter_type_cbox">
<property name="editable">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filter_type_reading_lbl">
<property name="text">
<string>Sinc5Sinc1With50hz60HzRejection</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="filter_sampling_rate_layout" stretch="3,4,4,4">
<item>
<widget class="QLabel" name="filter_sampling_rate_lbl">
<property name="text">
<string>Filter Sampling Rate</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="filter_sampling_rate_cbox"/>
</item>
<item>
<widget class="QDoubleSpinBox" name="fine_filter_sampling_rate_spinbox">
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>16.670000000000002</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filter_sampling_rate_reading_lbl">
<property name="text">
<string>F16SPS</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="recorded_sampling_rate_layout" stretch="3,4,4">
<item>
<widget class="QLabel" name="recorded_sampling_rate_lbl">
<property name="text">
<string>Recorded Sampling Rate</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="recorded_sampling_rate_reading_lbl">
<property name="text">
<string>16.67</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="apply_btn_layout" stretch="3,2,3,2">
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="apply_btn">
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="close_btn">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>close_btn</sender>
<signal>clicked()</signal>
<receiver>Cfg_Adc_Filter_Form</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>677</x>
<y>246</y>
</hint>
<hint type="destinationlabel">
<x>392</x>
<y>151</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,488 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Cfg_Pd_Mon_Form</class>
<widget class="QDialog" name="Cfg_Pd_Mon_Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>520</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>520</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>520</height>
</size>
</property>
<property name="windowTitle">
<string>config_pd_mon_form</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget_2">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>481</width>
<height>500</height>
</rect>
</property>
<layout class="QVBoxLayout" name="cfg_pd_mon_form_layout" stretch="0,2,2,2,2,2,1">
<item>
<widget class="QLabel" name="title_lbl">
<property name="font">
<font>
<pointsize>22</pointsize>
</font>
</property>
<property name="text">
<string>Configure Photodiode Monitor</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="pwr_off_layout" stretch="5,4">
<property name="spacing">
<number>12</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="pwr_off_lbl">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string> Step 1: Turn off Laser Power</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pwr_off_btn">
<property name="text">
<string>Power Off</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="rst_ld_pwr_limit_layout" stretch="5,4">
<item>
<widget class="QLabel" name="rst_ld_pwr_limit_lbl">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string> Step 2: Reset Ld Pwr Limit to 0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rst_ld_pwr_limit_btn">
<property name="text">
<string>Reset Ld Pwr Limit</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="cfg_pd_params_layout">
<item>
<widget class="QLabel" name="cfg_pd_params_lbl">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string> Step 3: Configure Photodiode Parameters</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="5,2,2">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string> Value</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Reading</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="cfg_responsitivity_layout" stretch="5,2,2">
<item>
<widget class="QLabel" name="cfg_responsitivity_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Responsitivity: </string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="cfg_responsitivity_spinbox">
<property name="suffix">
<string/>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="cfg_responsitivity_reading">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>0.0000</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="cfg_dark_current_layout" stretch="5,2,2">
<item>
<widget class="QLabel" name="cfg_dark_current_lbl">
<property name="text">
<string>Dark Current: </string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="cfg_dark_current_spinbox">
<property name="decimals">
<number>4</number>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="cfg_dark_current_reading">
<property name="text">
<string>0.0000</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="apply_pd_params_layout" stretch="5,4">
<property name="spacing">
<number>12</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="apply_pd_params_btn">
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="cfg_pwr_limit_layout">
<item>
<widget class="QLabel" name="cfg_pd_pwr_limit_lbl">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string> Step 4: Configure Laser Diode Power Limit</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="5,2,2">
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string> Value</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Reading</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="pwr_limit_layout" stretch="5,2,2">
<item>
<widget class="QLabel" name="cfg_pwr_limit_lbl">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Power Limit:</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="cfg_pwr_limit_spinbox">
<property name="decimals">
<number>4</number>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="cfg_pwr_limit_reading">
<property name="text">
<string>0.0000</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="settable_pwr_range_layout" stretch="5,4">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QLabel" name="settable_pwr_range_lbl">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Settable Power Range:</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="settable_pwr_range_display_lbl">
<property name="minimumSize">
<size>
<width>0</width>
<height>28</height>
</size>
</property>
<property name="text">
<string>( Power Range )</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="apply_pwr_limit_layout" stretch="2,3,4">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="apply_pwr_limit_max_btn">
<property name="text">
<string>Apply Max</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="apply_pwr_limit_btn">
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="pwr_on_layout" stretch="5,4">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QLabel" name="pwr_on_lbl">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string> Step 5: Turn On Laser Power</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pwr_on_btn">
<property name="text">
<string>Clear Alarm and Power On</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="close_btn_layout" stretch="5,4">
<property name="spacing">
<number>12</number>
</property>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="close_btn">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>close_btn</sender>
<signal>clicked()</signal>
<receiver>Cfg_Pd_Mon_Form</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>141</x>
<y>456</y>
</hint>
<hint type="destinationlabel">
<x>281</x>
<y>355</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,376 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Conn_Settings_Form</class>
<widget class="QDialog" name="Conn_Settings_Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>415</width>
<height>145</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>415</width>
<height>145</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>415</width>
<height>145</height>
</size>
</property>
<property name="windowTitle">
<string>Connection Settings</string>
</property>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="ip_addr_layout">
<item>
<widget class="QLabel" name="ip_addr_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>IP Address:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_0">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>192</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_0_label">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_1">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>168</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_1_label">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_2">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_2_label">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_3">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>128</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_2">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="port_layout" stretch="3,0,6">
<item>
<widget class="QLabel" name="port_no_label">
<property name="maximumSize">
<size>
<width>97</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Port:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="port_in">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1337</string>
</property>
<property name="maxLength">
<number>5</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="port_layout_spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_4">
<property name="geometry">
<rect>
<x>20</x>
<y>90</y>
<width>371</width>
<height>47</height>
</rect>
</property>
<layout class="QHBoxLayout" name="buttons_layout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="connect_btn">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel_btn">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>addr_in_0</tabstop>
<tabstop>addr_in_1</tabstop>
<tabstop>addr_in_2</tabstop>
<tabstop>addr_in_3</tabstop>
<tabstop>port_in</tabstop>
<tabstop>connect_btn</tabstop>
<tabstop>cancel_btn</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>cancel_btn</sender>
<signal>clicked()</signal>
<receiver>Conn_Settings_Form</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>340</x>
<y>140</y>
</hint>
<hint type="destinationlabel">
<x>364</x>
<y>96</y>
</hint>
</hints>
</connection>
<connection>
<sender>connect_btn</sender>
<signal>clicked()</signal>
<receiver>Conn_Settings_Form</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>258</x>
<y>147</y>
</hint>
<hint type="destinationlabel">
<x>237</x>
<y>99</y>
</hint>
</hints>
</connection>
</connections>
</ui>

696
pykirdy/ui/kirdy_qt.ui Normal file
View File

@@ -0,0 +1,696 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1280</width>
<height>720</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>1280</width>
<height>720</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>3840</width>
<height>2160</height>
</size>
</property>
<property name="windowTitle">
<string>Kirdy Control Panel</string>
</property>
<property name="windowIcon">
<iconset theme="application-x-executable"/>
</property>
<widget class="QWidget" name="main_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="main_layout">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,2">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QVBoxLayout" name="ctrl_vertical_layout" stretch="1,1,10,1,1,1,10,1">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetMaximumSize</enum>
</property>
<item>
<widget class="QLabel" name="ld_section_label">
<property name="font">
<font>
<pointsize>14</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string> Laser Diode</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="ParameterTree" name="ld_status" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>57</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="ParameterTree" name="ld_tree" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="ld_btns_layout">
<property name="spacing">
<number>30</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QPushButton" name="ld_pwr_on_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER ON</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ld_pwr_off_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER OFF</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ld_clear_alarm_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>CLEAR ALARM</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="tec_section_label">
<property name="font">
<font>
<pointsize>14</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string> Thermostat</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="ParameterTree" name="tec_status" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>57</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="ParameterTree" name="tec_tree" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="tec_btns_layout">
<property name="spacing">
<number>30</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QPushButton" name="tec_pwr_on_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER ON</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tec_pwr_off_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER OFF</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tec_clear_alarm_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>CLEAR ALARM</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="graphgroup">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetNoConstraint</enum>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="LivePlotWidget" name="tec_temp_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="LivePlotWidget" name="tec_i_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="LivePlotWidget" name="pd_mon_pwr_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="LivePlotWidget" name="ld_i_set_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="bottom_settings_group">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="settings_layout">
<item>
<widget class="QPushButton" name="connect_btn">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="status_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>480</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>480</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>120</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>Disconnected</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="plot_settings">
<property name="toolTip">
<string>Plot Settings</string>
</property>
<property name="text">
<string>📉</string>
</property>
<property name="popupMode">
<enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="limits_warning">
<property name="toolTipDuration">
<number>1000000000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="background_task_lbl">
<property name="text">
<string>Ready.</string>
</property>
</widget>
</item>
<item>
<widget class="QtWaitingSpinner" name="loading_spinner" native="true"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="polling_rate_lbl">
<property name="text">
<string>Polling Rate (Hz): </string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="report_group" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QDoubleSpinBox" name="polling_rate_spinbox">
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>20.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="polling_rate_apply_btn">
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1280</width>
<height>23</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QMenu" name="menuKirdy">
<property name="enabled">
<bool>true</bool>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="title">
<string>Kirdy</string>
</property>
<addaction name="menu_action_about_kirdy"/>
<addaction name="menu_action_update_net_settings"/>
<addaction name="menu_action_save"/>
<addaction name="menu_action_load"/>
<addaction name="menu_action_dfu_mode"/>
<addaction name="menu_action_hard_reset"/>
</widget>
<widget class="QMenu" name="menuAbout">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="title">
<string>About</string>
</property>
<addaction name="menu_action_about_gui"/>
</widget>
<addaction name="menuKirdy"/>
<addaction name="menuAbout"/>
</widget>
<action name="actionReset">
<property name="text">
<string>Reset</string>
</property>
<property name="toolTip">
<string>Reset the Kirdy</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionEnter_DFU_Mode">
<property name="text">
<string>Enter DFU Mode</string>
</property>
<property name="toolTip">
<string>Reset kirdy and enter USB device firmware update (DFU) mode</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionNetwork_Settings">
<property name="text">
<string>Network Settings</string>
</property>
<property name="toolTip">
<string>Configure IPv4 address, netmask length, and optional default gateway</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionAbout_Kirdy">
<property name="text">
<string>About Kirdy</string>
</property>
<property name="toolTip">
<string>Show Kirdy hardware revision, and settings related to i</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionLoad_all_configs">
<property name="text">
<string>Load all channel configs from flash</string>
</property>
<property name="toolTip">
<string>Restore configuration for all channels from flash</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionSave_all_configs">
<property name="text">
<string>Save all channel configs to flash</string>
</property>
<property name="toolTip">
<string>Save configuration for all channels to flash</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionConnection_Settings">
<property name="text">
<string>Connect</string>
</property>
</action>
<action name="menu_action_update_net_settings">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Update Network Settings</string>
</property>
</action>
<action name="menu_action_dfu_mode">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Enter DFU Mode</string>
</property>
</action>
<action name="menu_action_save">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save Settings to Flash</string>
</property>
</action>
<action name="menu_action_hard_reset">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Hard Reset</string>
</property>
</action>
<action name="menu_action_connect">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Connect</string>
</property>
</action>
<action name="menu_action_disconnect">
<property name="text">
<string>Disconnect</string>
</property>
</action>
<action name="menu_action_about_gui">
<property name="text">
<string>About GUI</string>
</property>
</action>
<action name="action">
<property name="text">
<string>test</string>
</property>
</action>
<action name="menu_action_about_kirdy">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>About Kirdy</string>
</property>
</action>
<action name="menu_action_load">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Load Settings from Flash</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>ParameterTree</class>
<extends>QWidget</extends>
<header>pyqtgraph.parametertree</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LivePlotWidget</class>
<extends>QWidget</extends>
<header>pglive.sources.live_plot_widget</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QtWaitingSpinner</class>
<extends>QWidget</extends>
<header>waitingspinnerwidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,6 @@
QPushButton:disabled { color: gray }
ParameterTree:disabled { color: gray }
QToolButton:disabled { color: gray }
QDoubleSpinBox:disabled { color: gray }
QCheckBox:disabled { color: gray }
QMenu:disabled { color: gray }

View File

@@ -0,0 +1,131 @@
# Form implementation generated from reading ui file 'config_adc_filter_form.ui'
#
# Created by: PyQt6 UI code generator 6.6.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_Cfg_Adc_Filter_Form(object):
def setupUi(self, Cfg_Adc_Filter_Form):
Cfg_Adc_Filter_Form.setObjectName("Cfg_Adc_Filter_Form")
Cfg_Adc_Filter_Form.resize(786, 303)
self.verticalLayoutWidget = QtWidgets.QWidget(parent=Cfg_Adc_Filter_Form)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 20, 731, 251))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.label_4 = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.label_4.setObjectName("label_4")
self.horizontalLayout.addWidget(self.label_4)
self.label_5 = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.label_5.setObjectName("label_5")
self.horizontalLayout.addWidget(self.label_5)
self.horizontalLayout.setStretch(0, 3)
self.horizontalLayout.setStretch(1, 4)
self.horizontalLayout.setStretch(2, 4)
self.verticalLayout.addLayout(self.horizontalLayout)
self.filter_type_layout = QtWidgets.QHBoxLayout()
self.filter_type_layout.setObjectName("filter_type_layout")
self.filter_type_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.filter_type_lbl.setObjectName("filter_type_lbl")
self.filter_type_layout.addWidget(self.filter_type_lbl)
self.filter_type_cbox = QtWidgets.QComboBox(parent=self.verticalLayoutWidget)
self.filter_type_cbox.setEditable(False)
self.filter_type_cbox.setObjectName("filter_type_cbox")
self.filter_type_layout.addWidget(self.filter_type_cbox)
self.filter_type_reading_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.filter_type_reading_lbl.setObjectName("filter_type_reading_lbl")
self.filter_type_layout.addWidget(self.filter_type_reading_lbl)
self.filter_type_layout.setStretch(0, 3)
self.filter_type_layout.setStretch(1, 4)
self.filter_type_layout.setStretch(2, 4)
self.verticalLayout.addLayout(self.filter_type_layout)
self.filter_sampling_rate_layout = QtWidgets.QHBoxLayout()
self.filter_sampling_rate_layout.setObjectName("filter_sampling_rate_layout")
self.filter_sampling_rate_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.filter_sampling_rate_lbl.setObjectName("filter_sampling_rate_lbl")
self.filter_sampling_rate_layout.addWidget(self.filter_sampling_rate_lbl)
self.filter_sampling_rate_cbox = QtWidgets.QComboBox(parent=self.verticalLayoutWidget)
self.filter_sampling_rate_cbox.setObjectName("filter_sampling_rate_cbox")
self.filter_sampling_rate_layout.addWidget(self.filter_sampling_rate_cbox)
self.fine_filter_sampling_rate_spinbox = QtWidgets.QDoubleSpinBox(parent=self.verticalLayoutWidget)
self.fine_filter_sampling_rate_spinbox.setMaximum(1000.0)
self.fine_filter_sampling_rate_spinbox.setProperty("value", 16.67)
self.fine_filter_sampling_rate_spinbox.setObjectName("fine_filter_sampling_rate_spinbox")
self.filter_sampling_rate_layout.addWidget(self.fine_filter_sampling_rate_spinbox)
self.filter_sampling_rate_reading_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.filter_sampling_rate_reading_lbl.setObjectName("filter_sampling_rate_reading_lbl")
self.filter_sampling_rate_layout.addWidget(self.filter_sampling_rate_reading_lbl)
self.filter_sampling_rate_layout.setStretch(0, 3)
self.filter_sampling_rate_layout.setStretch(1, 4)
self.filter_sampling_rate_layout.setStretch(2, 4)
self.filter_sampling_rate_layout.setStretch(3, 4)
self.verticalLayout.addLayout(self.filter_sampling_rate_layout)
self.recorded_sampling_rate_layout = QtWidgets.QHBoxLayout()
self.recorded_sampling_rate_layout.setObjectName("recorded_sampling_rate_layout")
self.recorded_sampling_rate_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.recorded_sampling_rate_lbl.setObjectName("recorded_sampling_rate_lbl")
self.recorded_sampling_rate_layout.addWidget(self.recorded_sampling_rate_lbl)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.recorded_sampling_rate_layout.addItem(spacerItem1)
self.recorded_sampling_rate_reading_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget)
self.recorded_sampling_rate_reading_lbl.setObjectName("recorded_sampling_rate_reading_lbl")
self.recorded_sampling_rate_layout.addWidget(self.recorded_sampling_rate_reading_lbl)
self.recorded_sampling_rate_layout.setStretch(0, 3)
self.recorded_sampling_rate_layout.setStretch(1, 4)
self.recorded_sampling_rate_layout.setStretch(2, 4)
self.verticalLayout.addLayout(self.recorded_sampling_rate_layout)
self.apply_btn_layout = QtWidgets.QHBoxLayout()
self.apply_btn_layout.setObjectName("apply_btn_layout")
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.apply_btn_layout.addItem(spacerItem2)
self.apply_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget)
self.apply_btn.setObjectName("apply_btn")
self.apply_btn_layout.addWidget(self.apply_btn)
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.apply_btn_layout.addItem(spacerItem3)
self.close_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget)
self.close_btn.setObjectName("close_btn")
self.apply_btn_layout.addWidget(self.close_btn)
self.apply_btn_layout.setStretch(0, 3)
self.apply_btn_layout.setStretch(1, 2)
self.apply_btn_layout.setStretch(2, 3)
self.apply_btn_layout.setStretch(3, 2)
self.verticalLayout.addLayout(self.apply_btn_layout)
self.retranslateUi(Cfg_Adc_Filter_Form)
self.close_btn.clicked.connect(Cfg_Adc_Filter_Form.accept) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Cfg_Adc_Filter_Form)
def retranslateUi(self, Cfg_Adc_Filter_Form):
_translate = QtCore.QCoreApplication.translate
Cfg_Adc_Filter_Form.setWindowTitle(_translate("Cfg_Adc_Filter_Form", "Config Temperature ADC Filter"))
self.label_4.setText(_translate("Cfg_Adc_Filter_Form", "Value"))
self.label_5.setText(_translate("Cfg_Adc_Filter_Form", "Readings"))
self.filter_type_lbl.setText(_translate("Cfg_Adc_Filter_Form", "Filter Type"))
self.filter_type_reading_lbl.setText(_translate("Cfg_Adc_Filter_Form", "Sinc5Sinc1With50hz60HzRejection"))
self.filter_sampling_rate_lbl.setText(_translate("Cfg_Adc_Filter_Form", "Filter Sampling Rate"))
self.filter_sampling_rate_reading_lbl.setText(_translate("Cfg_Adc_Filter_Form", "F16SPS"))
self.recorded_sampling_rate_lbl.setText(_translate("Cfg_Adc_Filter_Form", "Recorded Sampling Rate"))
self.recorded_sampling_rate_reading_lbl.setText(_translate("Cfg_Adc_Filter_Form", "16.67"))
self.apply_btn.setText(_translate("Cfg_Adc_Filter_Form", "Apply"))
self.close_btn.setText(_translate("Cfg_Adc_Filter_Form", "Close"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Cfg_Adc_Filter_Form = QtWidgets.QDialog()
ui = Ui_Cfg_Adc_Filter_Form()
ui.setupUi(Cfg_Adc_Filter_Form)
Cfg_Adc_Filter_Form.show()
sys.exit(app.exec())

View File

@@ -0,0 +1,295 @@
# Form implementation generated from reading ui file 'config_pd_mon_form.ui'
#
# Created by: PyQt6 UI code generator 6.6.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_Cfg_Pd_Mon_Form(object):
def setupUi(self, Cfg_Pd_Mon_Form):
Cfg_Pd_Mon_Form.setObjectName("Cfg_Pd_Mon_Form")
Cfg_Pd_Mon_Form.resize(500, 520)
Cfg_Pd_Mon_Form.setMinimumSize(QtCore.QSize(500, 520))
Cfg_Pd_Mon_Form.setMaximumSize(QtCore.QSize(500, 520))
self.verticalLayoutWidget_2 = QtWidgets.QWidget(parent=Cfg_Pd_Mon_Form)
self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(10, 10, 481, 500))
self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
self.cfg_pd_mon_form_layout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
self.cfg_pd_mon_form_layout.setContentsMargins(0, 0, 0, 0)
self.cfg_pd_mon_form_layout.setObjectName("cfg_pd_mon_form_layout")
self.title_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(22)
self.title_lbl.setFont(font)
self.title_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.title_lbl.setObjectName("title_lbl")
self.cfg_pd_mon_form_layout.addWidget(self.title_lbl)
self.pwr_off_layout = QtWidgets.QHBoxLayout()
self.pwr_off_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint)
self.pwr_off_layout.setSpacing(12)
self.pwr_off_layout.setObjectName("pwr_off_layout")
self.pwr_off_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(14)
self.pwr_off_lbl.setFont(font)
self.pwr_off_lbl.setObjectName("pwr_off_lbl")
self.pwr_off_layout.addWidget(self.pwr_off_lbl)
self.pwr_off_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2)
self.pwr_off_btn.setObjectName("pwr_off_btn")
self.pwr_off_layout.addWidget(self.pwr_off_btn)
self.pwr_off_layout.setStretch(0, 5)
self.pwr_off_layout.setStretch(1, 4)
self.cfg_pd_mon_form_layout.addLayout(self.pwr_off_layout)
self.rst_ld_pwr_limit_layout = QtWidgets.QHBoxLayout()
self.rst_ld_pwr_limit_layout.setObjectName("rst_ld_pwr_limit_layout")
self.rst_ld_pwr_limit_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(14)
self.rst_ld_pwr_limit_lbl.setFont(font)
self.rst_ld_pwr_limit_lbl.setObjectName("rst_ld_pwr_limit_lbl")
self.rst_ld_pwr_limit_layout.addWidget(self.rst_ld_pwr_limit_lbl)
self.rst_ld_pwr_limit_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2)
self.rst_ld_pwr_limit_btn.setObjectName("rst_ld_pwr_limit_btn")
self.rst_ld_pwr_limit_layout.addWidget(self.rst_ld_pwr_limit_btn)
self.rst_ld_pwr_limit_layout.setStretch(0, 5)
self.rst_ld_pwr_limit_layout.setStretch(1, 4)
self.cfg_pd_mon_form_layout.addLayout(self.rst_ld_pwr_limit_layout)
self.cfg_pd_params_layout = QtWidgets.QVBoxLayout()
self.cfg_pd_params_layout.setObjectName("cfg_pd_params_layout")
self.cfg_pd_params_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(14)
self.cfg_pd_params_lbl.setFont(font)
self.cfg_pd_params_lbl.setObjectName("cfg_pd_params_lbl")
self.cfg_pd_params_layout.addWidget(self.cfg_pd_params_lbl)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.label_2 = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
self.label = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.horizontalLayout.setStretch(0, 5)
self.horizontalLayout.setStretch(1, 2)
self.horizontalLayout.setStretch(2, 2)
self.cfg_pd_params_layout.addLayout(self.horizontalLayout)
self.cfg_responsitivity_layout = QtWidgets.QHBoxLayout()
self.cfg_responsitivity_layout.setObjectName("cfg_responsitivity_layout")
self.cfg_responsitivity_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.cfg_responsitivity_lbl.sizePolicy().hasHeightForWidth())
self.cfg_responsitivity_lbl.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setPointSize(12)
self.cfg_responsitivity_lbl.setFont(font)
self.cfg_responsitivity_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.cfg_responsitivity_lbl.setObjectName("cfg_responsitivity_lbl")
self.cfg_responsitivity_layout.addWidget(self.cfg_responsitivity_lbl)
self.cfg_responsitivity_spinbox = QtWidgets.QDoubleSpinBox(parent=self.verticalLayoutWidget_2)
self.cfg_responsitivity_spinbox.setSuffix("")
self.cfg_responsitivity_spinbox.setDecimals(4)
self.cfg_responsitivity_spinbox.setMaximum(1000.0)
self.cfg_responsitivity_spinbox.setSingleStep(0.001)
self.cfg_responsitivity_spinbox.setObjectName("cfg_responsitivity_spinbox")
self.cfg_responsitivity_layout.addWidget(self.cfg_responsitivity_spinbox)
self.cfg_responsitivity_reading = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.cfg_responsitivity_reading.sizePolicy().hasHeightForWidth())
self.cfg_responsitivity_reading.setSizePolicy(sizePolicy)
self.cfg_responsitivity_reading.setObjectName("cfg_responsitivity_reading")
self.cfg_responsitivity_layout.addWidget(self.cfg_responsitivity_reading)
self.cfg_responsitivity_layout.setStretch(0, 5)
self.cfg_responsitivity_layout.setStretch(1, 2)
self.cfg_responsitivity_layout.setStretch(2, 2)
self.cfg_pd_params_layout.addLayout(self.cfg_responsitivity_layout)
self.cfg_dark_current_layout = QtWidgets.QHBoxLayout()
self.cfg_dark_current_layout.setObjectName("cfg_dark_current_layout")
self.cfg_dark_current_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.cfg_dark_current_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.cfg_dark_current_lbl.setObjectName("cfg_dark_current_lbl")
self.cfg_dark_current_layout.addWidget(self.cfg_dark_current_lbl)
self.cfg_dark_current_spinbox = QtWidgets.QDoubleSpinBox(parent=self.verticalLayoutWidget_2)
self.cfg_dark_current_spinbox.setDecimals(4)
self.cfg_dark_current_spinbox.setSingleStep(0.001)
self.cfg_dark_current_spinbox.setObjectName("cfg_dark_current_spinbox")
self.cfg_dark_current_layout.addWidget(self.cfg_dark_current_spinbox)
self.cfg_dark_current_reading = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.cfg_dark_current_reading.setObjectName("cfg_dark_current_reading")
self.cfg_dark_current_layout.addWidget(self.cfg_dark_current_reading)
self.cfg_dark_current_layout.setStretch(0, 5)
self.cfg_dark_current_layout.setStretch(1, 2)
self.cfg_dark_current_layout.setStretch(2, 2)
self.cfg_pd_params_layout.addLayout(self.cfg_dark_current_layout)
self.apply_pd_params_layout = QtWidgets.QHBoxLayout()
self.apply_pd_params_layout.setSpacing(12)
self.apply_pd_params_layout.setObjectName("apply_pd_params_layout")
spacerItem1 = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.apply_pd_params_layout.addItem(spacerItem1)
self.apply_pd_params_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2)
self.apply_pd_params_btn.setObjectName("apply_pd_params_btn")
self.apply_pd_params_layout.addWidget(self.apply_pd_params_btn)
self.apply_pd_params_layout.setStretch(0, 5)
self.apply_pd_params_layout.setStretch(1, 4)
self.cfg_pd_params_layout.addLayout(self.apply_pd_params_layout)
self.cfg_pd_mon_form_layout.addLayout(self.cfg_pd_params_layout)
self.cfg_pwr_limit_layout = QtWidgets.QVBoxLayout()
self.cfg_pwr_limit_layout.setObjectName("cfg_pwr_limit_layout")
self.cfg_pd_pwr_limit_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(14)
self.cfg_pd_pwr_limit_lbl.setFont(font)
self.cfg_pd_pwr_limit_lbl.setObjectName("cfg_pd_pwr_limit_lbl")
self.cfg_pwr_limit_layout.addWidget(self.cfg_pd_pwr_limit_lbl)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_2.addItem(spacerItem2)
self.label_3 = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.label_3.setObjectName("label_3")
self.horizontalLayout_2.addWidget(self.label_3)
self.label_4 = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.label_4.setObjectName("label_4")
self.horizontalLayout_2.addWidget(self.label_4)
self.horizontalLayout_2.setStretch(0, 5)
self.horizontalLayout_2.setStretch(1, 2)
self.horizontalLayout_2.setStretch(2, 2)
self.cfg_pwr_limit_layout.addLayout(self.horizontalLayout_2)
self.pwr_limit_layout = QtWidgets.QHBoxLayout()
self.pwr_limit_layout.setObjectName("pwr_limit_layout")
self.cfg_pwr_limit_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(12)
self.cfg_pwr_limit_lbl.setFont(font)
self.cfg_pwr_limit_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.cfg_pwr_limit_lbl.setObjectName("cfg_pwr_limit_lbl")
self.pwr_limit_layout.addWidget(self.cfg_pwr_limit_lbl)
self.cfg_pwr_limit_spinbox = QtWidgets.QDoubleSpinBox(parent=self.verticalLayoutWidget_2)
self.cfg_pwr_limit_spinbox.setDecimals(4)
self.cfg_pwr_limit_spinbox.setSingleStep(0.001)
self.cfg_pwr_limit_spinbox.setObjectName("cfg_pwr_limit_spinbox")
self.pwr_limit_layout.addWidget(self.cfg_pwr_limit_spinbox)
self.cfg_pwr_limit_reading = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.cfg_pwr_limit_reading.setObjectName("cfg_pwr_limit_reading")
self.pwr_limit_layout.addWidget(self.cfg_pwr_limit_reading)
self.pwr_limit_layout.setStretch(0, 5)
self.pwr_limit_layout.setStretch(1, 2)
self.pwr_limit_layout.setStretch(2, 2)
self.cfg_pwr_limit_layout.addLayout(self.pwr_limit_layout)
self.settable_pwr_range_layout = QtWidgets.QHBoxLayout()
self.settable_pwr_range_layout.setSpacing(12)
self.settable_pwr_range_layout.setObjectName("settable_pwr_range_layout")
self.settable_pwr_range_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(12)
self.settable_pwr_range_lbl.setFont(font)
self.settable_pwr_range_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.settable_pwr_range_lbl.setObjectName("settable_pwr_range_lbl")
self.settable_pwr_range_layout.addWidget(self.settable_pwr_range_lbl)
self.settable_pwr_range_display_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
self.settable_pwr_range_display_lbl.setMinimumSize(QtCore.QSize(0, 28))
self.settable_pwr_range_display_lbl.setObjectName("settable_pwr_range_display_lbl")
self.settable_pwr_range_layout.addWidget(self.settable_pwr_range_display_lbl)
self.settable_pwr_range_layout.setStretch(0, 5)
self.settable_pwr_range_layout.setStretch(1, 4)
self.cfg_pwr_limit_layout.addLayout(self.settable_pwr_range_layout)
self.apply_pwr_limit_layout = QtWidgets.QHBoxLayout()
self.apply_pwr_limit_layout.setObjectName("apply_pwr_limit_layout")
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.apply_pwr_limit_layout.addItem(spacerItem3)
self.apply_pwr_limit_max_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2)
self.apply_pwr_limit_max_btn.setObjectName("apply_pwr_limit_max_btn")
self.apply_pwr_limit_layout.addWidget(self.apply_pwr_limit_max_btn)
self.apply_pwr_limit_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2)
self.apply_pwr_limit_btn.setObjectName("apply_pwr_limit_btn")
self.apply_pwr_limit_layout.addWidget(self.apply_pwr_limit_btn)
self.apply_pwr_limit_layout.setStretch(0, 2)
self.apply_pwr_limit_layout.setStretch(1, 3)
self.apply_pwr_limit_layout.setStretch(2, 4)
self.cfg_pwr_limit_layout.addLayout(self.apply_pwr_limit_layout)
self.cfg_pd_mon_form_layout.addLayout(self.cfg_pwr_limit_layout)
self.pwr_on_layout = QtWidgets.QHBoxLayout()
self.pwr_on_layout.setSpacing(12)
self.pwr_on_layout.setObjectName("pwr_on_layout")
self.pwr_on_lbl = QtWidgets.QLabel(parent=self.verticalLayoutWidget_2)
font = QtGui.QFont()
font.setPointSize(14)
self.pwr_on_lbl.setFont(font)
self.pwr_on_lbl.setObjectName("pwr_on_lbl")
self.pwr_on_layout.addWidget(self.pwr_on_lbl)
self.pwr_on_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2)
self.pwr_on_btn.setObjectName("pwr_on_btn")
self.pwr_on_layout.addWidget(self.pwr_on_btn)
self.pwr_on_layout.setStretch(0, 5)
self.pwr_on_layout.setStretch(1, 4)
self.cfg_pd_mon_form_layout.addLayout(self.pwr_on_layout)
self.close_btn_layout = QtWidgets.QHBoxLayout()
self.close_btn_layout.setSpacing(12)
self.close_btn_layout.setObjectName("close_btn_layout")
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.close_btn_layout.addItem(spacerItem4)
self.close_btn = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2)
self.close_btn.setObjectName("close_btn")
self.close_btn_layout.addWidget(self.close_btn)
self.close_btn_layout.setStretch(0, 5)
self.close_btn_layout.setStretch(1, 4)
self.cfg_pd_mon_form_layout.addLayout(self.close_btn_layout)
self.cfg_pd_mon_form_layout.setStretch(1, 2)
self.cfg_pd_mon_form_layout.setStretch(2, 2)
self.cfg_pd_mon_form_layout.setStretch(3, 2)
self.cfg_pd_mon_form_layout.setStretch(4, 2)
self.cfg_pd_mon_form_layout.setStretch(5, 2)
self.cfg_pd_mon_form_layout.setStretch(6, 1)
self.retranslateUi(Cfg_Pd_Mon_Form)
self.close_btn.clicked.connect(Cfg_Pd_Mon_Form.accept) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Cfg_Pd_Mon_Form)
def retranslateUi(self, Cfg_Pd_Mon_Form):
_translate = QtCore.QCoreApplication.translate
Cfg_Pd_Mon_Form.setWindowTitle(_translate("Cfg_Pd_Mon_Form", "config_pd_mon_form"))
self.title_lbl.setText(_translate("Cfg_Pd_Mon_Form", "Configure Photodiode Monitor"))
self.pwr_off_lbl.setText(_translate("Cfg_Pd_Mon_Form", " Step 1: Turn off Laser Power"))
self.pwr_off_btn.setText(_translate("Cfg_Pd_Mon_Form", "Power Off"))
self.rst_ld_pwr_limit_lbl.setText(_translate("Cfg_Pd_Mon_Form", " Step 2: Reset Ld Pwr Limit to 0"))
self.rst_ld_pwr_limit_btn.setText(_translate("Cfg_Pd_Mon_Form", "Reset Ld Pwr Limit"))
self.cfg_pd_params_lbl.setText(_translate("Cfg_Pd_Mon_Form", " Step 3: Configure Photodiode Parameters"))
self.label_2.setText(_translate("Cfg_Pd_Mon_Form", " Value"))
self.label.setText(_translate("Cfg_Pd_Mon_Form", "Reading"))
self.cfg_responsitivity_lbl.setText(_translate("Cfg_Pd_Mon_Form", "Responsitivity: "))
self.cfg_responsitivity_reading.setText(_translate("Cfg_Pd_Mon_Form", "0.0000"))
self.cfg_dark_current_lbl.setText(_translate("Cfg_Pd_Mon_Form", "Dark Current: "))
self.cfg_dark_current_reading.setText(_translate("Cfg_Pd_Mon_Form", "0.0000"))
self.apply_pd_params_btn.setText(_translate("Cfg_Pd_Mon_Form", "Apply"))
self.cfg_pd_pwr_limit_lbl.setText(_translate("Cfg_Pd_Mon_Form", " Step 4: Configure Laser Diode Power Limit"))
self.label_3.setText(_translate("Cfg_Pd_Mon_Form", " Value"))
self.label_4.setText(_translate("Cfg_Pd_Mon_Form", "Reading"))
self.cfg_pwr_limit_lbl.setText(_translate("Cfg_Pd_Mon_Form", "Power Limit:"))
self.cfg_pwr_limit_reading.setText(_translate("Cfg_Pd_Mon_Form", "0.0000"))
self.settable_pwr_range_lbl.setText(_translate("Cfg_Pd_Mon_Form", "Settable Power Range:"))
self.settable_pwr_range_display_lbl.setText(_translate("Cfg_Pd_Mon_Form", "( Power Range )"))
self.apply_pwr_limit_max_btn.setText(_translate("Cfg_Pd_Mon_Form", "Apply Max"))
self.apply_pwr_limit_btn.setText(_translate("Cfg_Pd_Mon_Form", "Apply"))
self.pwr_on_lbl.setText(_translate("Cfg_Pd_Mon_Form", " Step 5: Turn On Laser Power"))
self.pwr_on_btn.setText(_translate("Cfg_Pd_Mon_Form", "Clear Alarm and Power On"))
self.close_btn.setText(_translate("Cfg_Pd_Mon_Form", "Close"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Cfg_Pd_Mon_Form = QtWidgets.QDialog()
ui = Ui_Cfg_Pd_Mon_Form()
ui.setupUi(Cfg_Pd_Mon_Form)
Cfg_Pd_Mon_Form.show()
sys.exit(app.exec())

View File

@@ -0,0 +1,161 @@
# Form implementation generated from reading ui file 'conn_settings_form.ui'
#
# Created by: PyQt6 UI code generator 6.6.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_Conn_Settings_Form(object):
def setupUi(self, Conn_Settings_Form):
Conn_Settings_Form.setObjectName("Conn_Settings_Form")
Conn_Settings_Form.resize(415, 145)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(Conn_Settings_Form.sizePolicy().hasHeightForWidth())
Conn_Settings_Form.setSizePolicy(sizePolicy)
Conn_Settings_Form.setMinimumSize(QtCore.QSize(415, 145))
Conn_Settings_Form.setMaximumSize(QtCore.QSize(415, 145))
self.horizontalLayoutWidget = QtWidgets.QWidget(parent=Conn_Settings_Form)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(20, 10, 371, 41))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.ip_addr_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.ip_addr_layout.setContentsMargins(0, 0, 0, 0)
self.ip_addr_layout.setObjectName("ip_addr_layout")
self.ip_addr_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.ip_addr_label.sizePolicy().hasHeightForWidth())
self.ip_addr_label.setSizePolicy(sizePolicy)
self.ip_addr_label.setMaximumSize(QtCore.QSize(120, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.ip_addr_label.setFont(font)
self.ip_addr_label.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.ip_addr_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.ip_addr_label.setObjectName("ip_addr_label")
self.ip_addr_layout.addWidget(self.ip_addr_label)
self.addr_in_0 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_0.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_0.setMaxLength(3)
self.addr_in_0.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_0.setObjectName("addr_in_0")
self.ip_addr_layout.addWidget(self.addr_in_0)
self.dot_0_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_0_label.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_0_label.setFont(font)
self.dot_0_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_0_label.setObjectName("dot_0_label")
self.ip_addr_layout.addWidget(self.dot_0_label)
self.addr_in_1 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_1.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_1.setMaxLength(3)
self.addr_in_1.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_1.setObjectName("addr_in_1")
self.ip_addr_layout.addWidget(self.addr_in_1)
self.dot_1_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_1_label.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_1_label.setFont(font)
self.dot_1_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_1_label.setObjectName("dot_1_label")
self.ip_addr_layout.addWidget(self.dot_1_label)
self.addr_in_2 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_2.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_2.setMaxLength(3)
self.addr_in_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_2.setObjectName("addr_in_2")
self.ip_addr_layout.addWidget(self.addr_in_2)
self.dot_2_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_2_label.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_2_label.setFont(font)
self.dot_2_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_2_label.setObjectName("dot_2_label")
self.ip_addr_layout.addWidget(self.dot_2_label)
self.addr_in_3 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_3.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_3.setMaxLength(3)
self.addr_in_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_3.setObjectName("addr_in_3")
self.ip_addr_layout.addWidget(self.addr_in_3)
self.horizontalLayoutWidget_2 = QtWidgets.QWidget(parent=Conn_Settings_Form)
self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(20, 50, 371, 41))
self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
self.port_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
self.port_layout.setContentsMargins(0, 0, 0, 0)
self.port_layout.setObjectName("port_layout")
self.port_no_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_2)
self.port_no_label.setMaximumSize(QtCore.QSize(97, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.port_no_label.setFont(font)
self.port_no_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.port_no_label.setObjectName("port_no_label")
self.port_layout.addWidget(self.port_no_label)
self.port_in = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_2)
self.port_in.setMaximumSize(QtCore.QSize(50, 16777215))
self.port_in.setMaxLength(5)
self.port_in.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.port_in.setObjectName("port_in")
self.port_layout.addWidget(self.port_in)
spacerItem = QtWidgets.QSpacerItem(50, 20, QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Minimum)
self.port_layout.addItem(spacerItem)
self.port_layout.setStretch(0, 3)
self.port_layout.setStretch(2, 6)
self.horizontalLayoutWidget_4 = QtWidgets.QWidget(parent=Conn_Settings_Form)
self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(20, 90, 371, 47))
self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4")
self.buttons_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_4)
self.buttons_layout.setContentsMargins(0, 0, 0, 0)
self.buttons_layout.setObjectName("buttons_layout")
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.buttons_layout.addItem(spacerItem1)
self.connect_btn = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_4)
self.connect_btn.setObjectName("connect_btn")
self.buttons_layout.addWidget(self.connect_btn)
self.cancel_btn = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_4)
self.cancel_btn.setObjectName("cancel_btn")
self.buttons_layout.addWidget(self.cancel_btn)
self.retranslateUi(Conn_Settings_Form)
self.cancel_btn.clicked.connect(Conn_Settings_Form.reject) # type: ignore
self.connect_btn.clicked.connect(Conn_Settings_Form.accept) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Conn_Settings_Form)
Conn_Settings_Form.setTabOrder(self.addr_in_0, self.addr_in_1)
Conn_Settings_Form.setTabOrder(self.addr_in_1, self.addr_in_2)
Conn_Settings_Form.setTabOrder(self.addr_in_2, self.addr_in_3)
Conn_Settings_Form.setTabOrder(self.addr_in_3, self.port_in)
Conn_Settings_Form.setTabOrder(self.port_in, self.connect_btn)
Conn_Settings_Form.setTabOrder(self.connect_btn, self.cancel_btn)
def retranslateUi(self, Conn_Settings_Form):
_translate = QtCore.QCoreApplication.translate
Conn_Settings_Form.setWindowTitle(_translate("Conn_Settings_Form", "Connection Settings"))
self.ip_addr_label.setText(_translate("Conn_Settings_Form", "IP Address:"))
self.addr_in_0.setText(_translate("Conn_Settings_Form", "192"))
self.dot_0_label.setText(_translate("Conn_Settings_Form", "."))
self.addr_in_1.setText(_translate("Conn_Settings_Form", "168"))
self.dot_1_label.setText(_translate("Conn_Settings_Form", "."))
self.addr_in_2.setText(_translate("Conn_Settings_Form", "1"))
self.dot_2_label.setText(_translate("Conn_Settings_Form", "."))
self.addr_in_3.setText(_translate("Conn_Settings_Form", "128"))
self.port_no_label.setText(_translate("Conn_Settings_Form", "Port:"))
self.port_in.setText(_translate("Conn_Settings_Form", "1337"))
self.connect_btn.setText(_translate("Conn_Settings_Form", "Connect"))
self.cancel_btn.setText(_translate("Conn_Settings_Form", "Cancel"))

View File

@@ -0,0 +1,264 @@
# Form implementation generated from reading ui file 'update_network_settings_form.ui'
#
# Created by: PyQt6 UI code generator 6.6.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_Update_Network_Settings_Form(object):
def setupUi(self, Update_Network_Settings_Form):
Update_Network_Settings_Form.setObjectName("Update_Network_Settings_Form")
Update_Network_Settings_Form.resize(415, 180)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(Update_Network_Settings_Form.sizePolicy().hasHeightForWidth())
Update_Network_Settings_Form.setSizePolicy(sizePolicy)
Update_Network_Settings_Form.setMinimumSize(QtCore.QSize(415, 180))
Update_Network_Settings_Form.setMaximumSize(QtCore.QSize(415, 180))
self.horizontalLayoutWidget = QtWidgets.QWidget(parent=Update_Network_Settings_Form)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(20, 10, 371, 41))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.ip_addr_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.ip_addr_layout.setContentsMargins(0, 0, 0, 0)
self.ip_addr_layout.setObjectName("ip_addr_layout")
self.ip_addr_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.ip_addr_label.sizePolicy().hasHeightForWidth())
self.ip_addr_label.setSizePolicy(sizePolicy)
self.ip_addr_label.setMaximumSize(QtCore.QSize(120, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.ip_addr_label.setFont(font)
self.ip_addr_label.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.ip_addr_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.ip_addr_label.setObjectName("ip_addr_label")
self.ip_addr_layout.addWidget(self.ip_addr_label)
self.addr_in_0 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_0.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_0.setMaxLength(3)
self.addr_in_0.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_0.setObjectName("addr_in_0")
self.ip_addr_layout.addWidget(self.addr_in_0)
self.dot_0_lbl = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_0_lbl.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_0_lbl.setFont(font)
self.dot_0_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_0_lbl.setObjectName("dot_0_lbl")
self.ip_addr_layout.addWidget(self.dot_0_lbl)
self.addr_in_1 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_1.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_1.setMaxLength(3)
self.addr_in_1.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_1.setObjectName("addr_in_1")
self.ip_addr_layout.addWidget(self.addr_in_1)
self.dot_1_lbl = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_1_lbl.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_1_lbl.setFont(font)
self.dot_1_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_1_lbl.setObjectName("dot_1_lbl")
self.ip_addr_layout.addWidget(self.dot_1_lbl)
self.addr_in_2 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_2.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_2.setMaxLength(3)
self.addr_in_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_2.setObjectName("addr_in_2")
self.ip_addr_layout.addWidget(self.addr_in_2)
self.dot_2_lbl = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_2_lbl.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_2_lbl.setFont(font)
self.dot_2_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_2_lbl.setObjectName("dot_2_lbl")
self.ip_addr_layout.addWidget(self.dot_2_lbl)
self.addr_in_3 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_3.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_3.setMaxLength(3)
self.addr_in_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_3.setObjectName("addr_in_3")
self.ip_addr_layout.addWidget(self.addr_in_3)
self.horizontalLayoutWidget_4 = QtWidgets.QWidget(parent=Update_Network_Settings_Form)
self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(20, 130, 371, 47))
self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4")
self.buttons_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_4)
self.buttons_layout.setContentsMargins(0, 0, 0, 0)
self.buttons_layout.setObjectName("buttons_layout")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.buttons_layout.addItem(spacerItem)
self.update_btn = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_4)
self.update_btn.setObjectName("update_btn")
self.buttons_layout.addWidget(self.update_btn)
self.cancel_btn = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_4)
self.cancel_btn.setObjectName("cancel_btn")
self.buttons_layout.addWidget(self.cancel_btn)
self.horizontalLayoutWidget_3 = QtWidgets.QWidget(parent=Update_Network_Settings_Form)
self.horizontalLayoutWidget_3.setGeometry(QtCore.QRect(20, 90, 371, 41))
self.horizontalLayoutWidget_3.setObjectName("horizontalLayoutWidget_3")
self.port_layout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_3)
self.port_layout_2.setContentsMargins(0, 0, 0, 0)
self.port_layout_2.setSpacing(6)
self.port_layout_2.setObjectName("port_layout_2")
self.prefix_len_lbl = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_3)
self.prefix_len_lbl.setMaximumSize(QtCore.QSize(98, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.prefix_len_lbl.setFont(font)
self.prefix_len_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.prefix_len_lbl.setObjectName("prefix_len_lbl")
self.port_layout_2.addWidget(self.prefix_len_lbl)
self.prefix_len_in = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_3)
self.prefix_len_in.setMaximumSize(QtCore.QSize(50, 16777215))
self.prefix_len_in.setMaxLength(2)
self.prefix_len_in.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.prefix_len_in.setObjectName("prefix_len_in")
self.port_layout_2.addWidget(self.prefix_len_in)
self.port_no_lbl = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_3)
self.port_no_lbl.setMinimumSize(QtCore.QSize(82, 0))
self.port_no_lbl.setMaximumSize(QtCore.QSize(50, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.port_no_lbl.setFont(font)
self.port_no_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.port_no_lbl.setObjectName("port_no_lbl")
self.port_layout_2.addWidget(self.port_no_lbl)
self.port_in = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_3)
self.port_in.setMaximumSize(QtCore.QSize(50, 16777215))
self.port_in.setMaxLength(5)
self.port_in.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.port_in.setObjectName("port_in")
self.port_layout_2.addWidget(self.port_in)
spacerItem1 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.port_layout_2.addItem(spacerItem1)
self.horizontalLayoutWidget_5 = QtWidgets.QWidget(parent=Update_Network_Settings_Form)
self.horizontalLayoutWidget_5.setGeometry(QtCore.QRect(20, 50, 371, 41))
self.horizontalLayoutWidget_5.setObjectName("horizontalLayoutWidget_5")
self.gateway_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_5)
self.gateway_layout.setContentsMargins(0, 0, 0, 0)
self.gateway_layout.setObjectName("gateway_layout")
self.gateway_lbl = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_5)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.gateway_lbl.sizePolicy().hasHeightForWidth())
self.gateway_lbl.setSizePolicy(sizePolicy)
self.gateway_lbl.setMaximumSize(QtCore.QSize(120, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.gateway_lbl.setFont(font)
self.gateway_lbl.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.gateway_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.gateway_lbl.setObjectName("gateway_lbl")
self.gateway_layout.addWidget(self.gateway_lbl)
self.gateway_in_0 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_5)
self.gateway_in_0.setMaximumSize(QtCore.QSize(50, 16777215))
self.gateway_in_0.setMaxLength(3)
self.gateway_in_0.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.gateway_in_0.setObjectName("gateway_in_0")
self.gateway_layout.addWidget(self.gateway_in_0)
self.dot_0_lbl_2 = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_5)
self.dot_0_lbl_2.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_0_lbl_2.setFont(font)
self.dot_0_lbl_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_0_lbl_2.setObjectName("dot_0_lbl_2")
self.gateway_layout.addWidget(self.dot_0_lbl_2)
self.gateway_in_1 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_5)
self.gateway_in_1.setMaximumSize(QtCore.QSize(50, 16777215))
self.gateway_in_1.setMaxLength(3)
self.gateway_in_1.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.gateway_in_1.setObjectName("gateway_in_1")
self.gateway_layout.addWidget(self.gateway_in_1)
self.dot_1_lbl_2 = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_5)
self.dot_1_lbl_2.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_1_lbl_2.setFont(font)
self.dot_1_lbl_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_1_lbl_2.setObjectName("dot_1_lbl_2")
self.gateway_layout.addWidget(self.dot_1_lbl_2)
self.gateway_in_2 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_5)
self.gateway_in_2.setMaximumSize(QtCore.QSize(50, 16777215))
self.gateway_in_2.setMaxLength(3)
self.gateway_in_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.gateway_in_2.setObjectName("gateway_in_2")
self.gateway_layout.addWidget(self.gateway_in_2)
self.dot_2_lbl_2 = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_5)
self.dot_2_lbl_2.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_2_lbl_2.setFont(font)
self.dot_2_lbl_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_2_lbl_2.setObjectName("dot_2_lbl_2")
self.gateway_layout.addWidget(self.dot_2_lbl_2)
self.gateway_in_3 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_5)
self.gateway_in_3.setMaximumSize(QtCore.QSize(50, 16777215))
self.gateway_in_3.setMaxLength(3)
self.gateway_in_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.gateway_in_3.setObjectName("gateway_in_3")
self.gateway_layout.addWidget(self.gateway_in_3)
self.retranslateUi(Update_Network_Settings_Form)
self.cancel_btn.clicked.connect(Update_Network_Settings_Form.reject) # type: ignore
self.update_btn.clicked.connect(Update_Network_Settings_Form.accept) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Update_Network_Settings_Form)
Update_Network_Settings_Form.setTabOrder(self.addr_in_0, self.addr_in_1)
Update_Network_Settings_Form.setTabOrder(self.addr_in_1, self.addr_in_2)
Update_Network_Settings_Form.setTabOrder(self.addr_in_2, self.addr_in_3)
Update_Network_Settings_Form.setTabOrder(self.addr_in_3, self.gateway_in_0)
Update_Network_Settings_Form.setTabOrder(self.gateway_in_0, self.gateway_in_1)
Update_Network_Settings_Form.setTabOrder(self.gateway_in_1, self.gateway_in_2)
Update_Network_Settings_Form.setTabOrder(self.gateway_in_2, self.gateway_in_3)
Update_Network_Settings_Form.setTabOrder(self.gateway_in_3, self.prefix_len_in)
Update_Network_Settings_Form.setTabOrder(self.prefix_len_in, self.port_in)
Update_Network_Settings_Form.setTabOrder(self.port_in, self.update_btn)
Update_Network_Settings_Form.setTabOrder(self.update_btn, self.cancel_btn)
def retranslateUi(self, Update_Network_Settings_Form):
_translate = QtCore.QCoreApplication.translate
Update_Network_Settings_Form.setWindowTitle(_translate("Update_Network_Settings_Form", "Update Network Settings"))
self.ip_addr_label.setText(_translate("Update_Network_Settings_Form", "IP Address:"))
self.addr_in_0.setText(_translate("Update_Network_Settings_Form", "192"))
self.dot_0_lbl.setText(_translate("Update_Network_Settings_Form", "."))
self.addr_in_1.setText(_translate("Update_Network_Settings_Form", "168"))
self.dot_1_lbl.setText(_translate("Update_Network_Settings_Form", "."))
self.addr_in_2.setText(_translate("Update_Network_Settings_Form", "1"))
self.dot_2_lbl.setText(_translate("Update_Network_Settings_Form", "."))
self.addr_in_3.setText(_translate("Update_Network_Settings_Form", "128"))
self.update_btn.setText(_translate("Update_Network_Settings_Form", "Update"))
self.cancel_btn.setText(_translate("Update_Network_Settings_Form", "Cancel"))
self.prefix_len_lbl.setText(_translate("Update_Network_Settings_Form", "Prefix Length:"))
self.prefix_len_in.setText(_translate("Update_Network_Settings_Form", "24"))
self.port_no_lbl.setText(_translate("Update_Network_Settings_Form", "Port:"))
self.port_in.setText(_translate("Update_Network_Settings_Form", "1337"))
self.gateway_lbl.setText(_translate("Update_Network_Settings_Form", "Gateway:"))
self.gateway_in_0.setText(_translate("Update_Network_Settings_Form", "192"))
self.dot_0_lbl_2.setText(_translate("Update_Network_Settings_Form", "."))
self.gateway_in_1.setText(_translate("Update_Network_Settings_Form", "168"))
self.dot_1_lbl_2.setText(_translate("Update_Network_Settings_Form", "."))
self.gateway_in_2.setText(_translate("Update_Network_Settings_Form", "1"))
self.dot_2_lbl_2.setText(_translate("Update_Network_Settings_Form", "."))
self.gateway_in_3.setText(_translate("Update_Network_Settings_Form", "1"))

View File

@@ -0,0 +1,614 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Update_Network_Settings_Form</class>
<widget class="QDialog" name="Update_Network_Settings_Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>415</width>
<height>180</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>415</width>
<height>180</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>415</width>
<height>180</height>
</size>
</property>
<property name="windowTitle">
<string>Update Network Settings</string>
</property>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="ip_addr_layout">
<item>
<widget class="QLabel" name="ip_addr_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>IP Address:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_0">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>192</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_0_lbl">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_1">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>168</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_1_lbl">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_2">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_2_lbl">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_3">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>128</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_4">
<property name="geometry">
<rect>
<x>20</x>
<y>130</y>
<width>371</width>
<height>47</height>
</rect>
</property>
<layout class="QHBoxLayout" name="buttons_layout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="update_btn">
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel_btn">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_3">
<property name="geometry">
<rect>
<x>20</x>
<y>90</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="port_layout_2" stretch="0,0,0,0,0">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="prefix_len_lbl">
<property name="maximumSize">
<size>
<width>98</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Prefix Length:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="prefix_len_in">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>24</string>
</property>
<property name="maxLength">
<number>2</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="port_no_lbl">
<property name="minimumSize">
<size>
<width>82</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Port:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="port_in">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1337</string>
</property>
<property name="maxLength">
<number>5</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_5">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="gateway_layout">
<item>
<widget class="QLabel" name="gateway_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Gateway:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="gateway_in_0">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>192</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_0_lbl_2">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="gateway_in_1">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>168</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_1_lbl_2">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="gateway_in_2">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_2_lbl_2">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="gateway_in_3">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>addr_in_0</tabstop>
<tabstop>addr_in_1</tabstop>
<tabstop>addr_in_2</tabstop>
<tabstop>addr_in_3</tabstop>
<tabstop>gateway_in_0</tabstop>
<tabstop>gateway_in_1</tabstop>
<tabstop>gateway_in_2</tabstop>
<tabstop>gateway_in_3</tabstop>
<tabstop>prefix_len_in</tabstop>
<tabstop>port_in</tabstop>
<tabstop>update_btn</tabstop>
<tabstop>cancel_btn</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>cancel_btn</sender>
<signal>clicked()</signal>
<receiver>Update_Network_Settings_Form</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>340</x>
<y>140</y>
</hint>
<hint type="destinationlabel">
<x>364</x>
<y>96</y>
</hint>
</hints>
</connection>
<connection>
<sender>update_btn</sender>
<signal>clicked()</signal>
<receiver>Update_Network_Settings_Form</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>258</x>
<y>147</y>
</hint>
<hint type="destinationlabel">
<x>237</x>
<y>99</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,194 @@
"""
The MIT License (MIT)
Copyright (c) 2012-2014 Alexander Turkin
Copyright (c) 2014 William Hallatt
Copyright (c) 2015 Jacob Dawid
Copyright (c) 2016 Luca Weiss
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import math
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
class QtWaitingSpinner(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# WAS IN initialize()
self._color = QColor(Qt.GlobalColor.black)
self._roundness = 100.0
self._minimumTrailOpacity = 3.14159265358979323846
self._trailFadePercentage = 80.0
self._revolutionsPerSecond = 1.57079632679489661923
self._numberOfLines = 20
self._lineLength = 5
self._lineWidth = 2
self._innerRadius = 5
self._currentCounter = 0
self._timer = QTimer(self)
self._timer.timeout.connect(self.rotate)
self.updateSize()
self.updateTimer()
# END initialize()
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
def paintEvent(self, QPaintEvent):
painter = QPainter(self)
painter.fillRect(self.rect(), Qt.GlobalColor.transparent)
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
if self._currentCounter >= self._numberOfLines:
self._currentCounter = 0
painter.setPen(Qt.PenStyle.NoPen)
for i in range(0, self._numberOfLines):
painter.save()
painter.translate(self._innerRadius + self._lineLength, self._innerRadius + self._lineLength)
rotateAngle = float(360 * i) / float(self._numberOfLines)
painter.rotate(rotateAngle)
painter.translate(self._innerRadius, 0)
distance = self.lineCountDistanceFromPrimary(i, self._currentCounter, self._numberOfLines)
color = self.currentLineColor(distance, self._numberOfLines, self._trailFadePercentage,
self._minimumTrailOpacity, self._color)
painter.setBrush(color)
painter.drawRoundedRect(QRect(0, int(-self._lineWidth / 2), self._lineLength, self._lineWidth), self._roundness,
self._roundness, Qt.SizeMode.RelativeSize)
painter.restore()
def start(self):
if not self._timer.isActive():
self._timer.start()
self._currentCounter = 0
def stop(self):
if self._timer.isActive():
self._timer.stop()
self._currentCounter = 0
def setNumberOfLines(self, lines):
self._numberOfLines = lines
self._currentCounter = 0
self.updateTimer()
def setLineLength(self, length):
self._lineLength = length
self.updateSize()
def setLineWidth(self, width):
self._lineWidth = width
self.updateSize()
def setInnerRadius(self, radius):
self._innerRadius = radius
self.updateSize()
def color(self):
return self._color
def roundness(self):
return self._roundness
def minimumTrailOpacity(self):
return self._minimumTrailOpacity
def trailFadePercentage(self):
return self._trailFadePercentage
def revolutionsPersSecond(self):
return self._revolutionsPerSecond
def numberOfLines(self):
return self._numberOfLines
def lineLength(self):
return self._lineLength
def lineWidth(self):
return self._lineWidth
def innerRadius(self):
return self._innerRadius
def setRoundness(self, roundness):
self._roundness = max(0.0, min(100.0, roundness))
def setColor(self, color=Qt.GlobalColor.black):
self._color = QColor(color)
def setRevolutionsPerSecond(self, revolutionsPerSecond):
self._revolutionsPerSecond = revolutionsPerSecond
self.updateTimer()
def setTrailFadePercentage(self, trail):
self._trailFadePercentage = trail
def setMinimumTrailOpacity(self, minimumTrailOpacity):
self._minimumTrailOpacity = minimumTrailOpacity
def rotate(self):
self._currentCounter += 1
if self._currentCounter >= self._numberOfLines:
self._currentCounter = 0
self.update()
def updateSize(self):
self.size = (self._innerRadius + self._lineLength) * 2
self.setFixedSize(self.size, self.size)
def updateTimer(self):
self._timer.setInterval(int(1000 / (self._numberOfLines * self._revolutionsPerSecond)))
def lineCountDistanceFromPrimary(self, current, primary, totalNrOfLines):
distance = primary - current
if distance < 0:
distance += totalNrOfLines
return distance
def currentLineColor(self, countDistance, totalNrOfLines, trailFadePerc, minOpacity, colorinput):
color = QColor(colorinput)
if countDistance == 0:
return color
minAlphaF = minOpacity / 100.0
distanceThreshold = int(math.ceil((totalNrOfLines - 1) * trailFadePerc / 100.0))
if countDistance > distanceThreshold:
color.setAlphaF(minAlphaF)
else:
alphaDiff = color.alphaF() - minAlphaF
gradient = alphaDiff / float(distanceThreshold + 1)
resultAlpha = color.alphaF() - gradient * countDistance
# If alpha is out of bounds, clip it.
resultAlpha = min(1.0, max(0.0, resultAlpha))
color.setAlphaF(resultAlpha)
return color
if __name__ == '__main__':
app = QApplication([])
waiting_spinner = QtWaitingSpinner()
waiting_spinner.show()
waiting_spinner.start()
app.exec()

64
rustfmt.toml Normal file
View File

@@ -0,0 +1,64 @@
max_width = 120
hard_tabs = false
tab_spaces = 4
newline_style = "Auto"
use_small_heuristics = "Default"
indent_style = "Block"
wrap_comments = false
format_code_in_doc_comments = false
comment_width = 100
normalize_comments = false
normalize_doc_attributes = false
format_strings = true
format_macro_matchers = true
format_macro_bodies = true
empty_item_single_line = true
struct_lit_single_line = true
fn_single_line = false
where_single_line = true
imports_indent = "Visual"
imports_layout = "Mixed"
imports_granularity="Crate"
group_imports = "StdExternalCrate"
reorder_imports = true
reorder_modules = true
reorder_impl_items = false
type_punctuation_density = "Wide"
space_before_colon = false
space_after_colon = true
spaces_around_ranges = false
binop_separator = "Front"
remove_nested_parens = true
combine_control_expr = true
overflow_delimited_expr = false
struct_field_align_threshold = 0
enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
trailing_comma = "Vertical"
match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
edition = "2018"
version = "Two"
inline_attribute_width = 0
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
condense_wildcard_suffixes = false
color = "Auto"
unstable_features = false
disable_all_formatting = false
skip_children = false
hide_parse_errors = false
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
emit_mode = "Files"
make_backup = false

View File

@@ -1,20 +1,30 @@
use super::{gpio, sys_timer, usb};
use crate::laser_diode::current_sources::*;
use fugit::ExtU32;
use log::info;
use stm32f4xx_hal::{
pac::{CorePeripherals, Peripherals},
rcc::RccExt,
time::MegaHertz,
watchdog::IndependentWatchdog,
};
use log::{debug, info};
use stm32f4xx_hal::{pac::{CorePeripherals, Peripherals},
rcc::RccExt,
time::MegaHertz,
timer::TimerExt,
watchdog::IndependentWatchdog};
use uom::si::{electric_current::{ampere, milliampere},
f32::ElectricCurrent};
use super::{gpio, sys_timer, usb};
use crate::{device::{flash_store::{self, FlashStore},
hw_rev::HWRev},
laser_diode::{laser_diode::LdDrive, ld_ctrl::*},
net::net::{IpSettings, ServerHandle},
thermostat::{max1968::MAX1968, thermostat::Thermostat},
DeviceSettings};
#[cfg(not(feature = "semihosting"))]
const WATCHDOG_PERIOD: u32 = 1000;
const WATCHDOG_PERIOD: u32 = 4000;
#[cfg(feature = "semihosting")]
const WATCHDOG_PERIOD: u32 = 30000;
pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> IndependentWatchdog {
pub fn bootup(
mut core_perif: CorePeripherals,
perif: Peripherals,
) -> (IndependentWatchdog, FlashStore, HWRev, LdDrive, Thermostat) {
core_perif.SCB.enable_icache();
core_perif.SCB.enable_dcache(&mut core_perif.CPUID);
@@ -25,42 +35,90 @@ pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> Independen
.use_hse(MegaHertz::from_raw(8).convert())
.sysclk(MegaHertz::from_raw(168).convert())
.hclk(MegaHertz::from_raw(168).convert())
.pclk1(MegaHertz::from_raw(32).convert())
.pclk2(MegaHertz::from_raw(64).convert())
.pclk1(MegaHertz::from_raw(42).convert())
.pclk2(MegaHertz::from_raw(84).convert())
.freeze();
sys_timer::setup(core_perif.SYST, clocks);
let (_eth_pins, usb, current_source_phy) = gpio::setup(
clocks,
perif.GPIOA,
perif.GPIOB,
perif.GPIOC,
perif.GPIOD,
perif.GPIOG,
perif.SPI2,
perif.OTG_FS_GLOBAL,
perif.OTG_FS_DEVICE,
perif.OTG_FS_PWRCLK,
);
let (mut hw_rev, eth_pins, eth_mgmt_pins, usb, current_source_phy, ad7172_phy, max1968_phy, pd_mon_phy) =
gpio::setup(
clocks,
perif.TIM4,
perif.GPIOA,
perif.GPIOB,
perif.GPIOC,
perif.GPIOD,
perif.GPIOE,
perif.SPI1,
perif.SPI2,
perif.SPI3,
perif.OTG_FS_GLOBAL,
perif.OTG_FS_DEVICE,
perif.OTG_FS_PWRCLK,
);
usb::State::setup(usb);
let mut laser = CurrentSource {
phy: current_source_phy,
setting: CurrentSourceSettings {
output_current: 0.0,
},
};
debug!("Setting up TEC");
let tec_driver = MAX1968::new(max1968_phy, perif.ADC1, perif.ADC2, perif.DMA2);
let mut thermostat = Thermostat::new(tec_driver, ad7172_phy);
thermostat.setup();
thermostat.calibrate_dac_value();
thermostat.set_i(ElectricCurrent::new::<ampere>(0.0));
debug!("Setting up Laser Driver");
let current_source = LdCtrl::new(current_source_phy);
let mut laser = LdDrive::new(current_source, perif.ADC3, perif.TIM2.counter(&clocks), pd_mon_phy);
laser.setup();
laser.set_current(0.1).unwrap();
laser.ld_open();
laser.ld_set_i(ElectricCurrent::new::<ampere>(0.0));
laser.set_pd_i_limit(ElectricCurrent::new::<milliampere>(2.5));
laser.set_pd_mon_calibrated_vdda(thermostat.get_calibrated_vdda());
debug!("Setting up Internal Flash Driver");
let flash_store = flash_store::store(perif.FLASH);
let mut ip_settings: IpSettings = IpSettings::default();
let device_settings: DeviceSettings;
match flash_store.read_value("Device") {
Ok(Some(config)) => {
device_settings = config;
ip_settings = device_settings.ip_settings;
debug!("Found Device Settings");
}
Ok(None) => {
debug!("Flash does not have IP Settings");
}
Err(e) => {
debug!("Cannot Store Flash: {:?}", e);
}
}
debug!("Setting up ETH");
let mac_addr = hw_rev.get_mac_address();
let ethernet_parts_in = stm32_eth::PartsIn {
dma: perif.ETHERNET_DMA,
mac: perif.ETHERNET_MAC,
mmc: perif.ETHERNET_MMC,
ptp: perif.ETHERNET_PTP,
};
ServerHandle::new(
eth_pins,
eth_mgmt_pins,
ethernet_parts_in,
clocks,
perif.TIM5.counter(&clocks),
mac_addr,
ip_settings,
);
debug!("Setting Watchdog");
let mut wd = IndependentWatchdog::new(perif.IWDG);
wd.start(WATCHDOG_PERIOD.millis());
wd.feed();
info!("Kirdy setup complete");
wd
(wd, flash_store, hw_rev, laser, thermostat)
}

47
src/device/dfu.rs Normal file
View File

@@ -0,0 +1,47 @@
use core::arch::asm;
use cortex_m_rt::pre_init;
use stm32f4xx_hal::pac::{RCC, SYSCFG};
const DFU_TRIG_MSG: u32 = 0xDECAFBAD;
extern "C" {
// This symbol comes from memory.x
static mut _dfu_msg: u32;
}
pub unsafe fn set_dfu_trigger() {
_dfu_msg = DFU_TRIG_MSG;
}
/// Called by reset handler in lib.rs immediately after reset.
/// This function should not be called outside of reset handler as
/// bootloader expects MCU to be in reset state when called.
#[cfg(target_arch = "arm")]
#[pre_init]
unsafe fn __pre_init() {
if _dfu_msg == DFU_TRIG_MSG {
_dfu_msg = 0x00000000;
// Enable system config controller clock
let rcc = &*RCC::ptr();
rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit());
// Bypass BOOT pins and remap bootloader to 0x00000000
let syscfg = &*SYSCFG::ptr();
syscfg.memrm.write(|w| w.mem_mode().bits(0b01));
// Impose instruction and memory barriers
cortex_m::asm::isb();
cortex_m::asm::dsb();
asm!(
// Set stack pointer to bootloader location
"LDR R0, =0x1FFF0000",
"LDR SP,[R0, #0]",
// Jump to bootloader
"LDR R0,[R0, #4]",
"BX R0",
);
}
}

58
src/device/flash_store.rs Normal file
View File

@@ -0,0 +1,58 @@
use log::error;
use sfkv::{Store, StoreBackend};
use stm32f4xx_hal::{flash::{Error, FlashExt},
pac::FLASH};
// Last flash sector is used to avoid overwriting the code in flash.
pub const FLASH_SECTOR: u8 = 11;
pub const FLASH_SECTOR_11_OFFSET: u32 = 0xE0000;
/// Only 16 KiB out of 128KiB in the Sector is used to save RAM
pub const RESERVED_MEMORY: usize = 0x4000;
static mut BACKUP_SPACE: [u8; RESERVED_MEMORY] = [0; RESERVED_MEMORY];
pub struct FlashBackend {
flash: FLASH,
}
fn get_offset() -> usize {
FLASH_SECTOR_11_OFFSET as usize
}
impl StoreBackend for FlashBackend {
type Data = [u8];
fn data(&self) -> &Self::Data {
&self.flash.read()[get_offset()..(get_offset() + RESERVED_MEMORY)]
}
type Error = Error;
fn erase(&mut self) -> Result<(), Self::Error> {
self.flash.unlocked().erase(FLASH_SECTOR)
}
fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error> {
self.flash.unlocked().program(get_offset() + offset, payload.iter())
}
fn backup_space(&self) -> &'static mut [u8] {
unsafe { BACKUP_SPACE.as_mut() }
}
}
pub type FlashStore = Store<FlashBackend>;
pub fn store(flash: FLASH) -> FlashStore {
let backend = FlashBackend { flash };
let mut store = FlashStore::new(backend);
match store.get_bytes_used() {
Ok(_) => {}
Err(e) => {
error!("corrupt store, erasing. error: {:?}", e);
let _ = store.erase().map_err(|e| error!("flash erase failed: {:?}", e));
}
}
store
}

View File

@@ -1,48 +1,71 @@
use crate::laser_diode::current_sources::*;
use fugit::RateExtU32;
use stm32_eth::EthPins;
use stm32f4xx_hal::{
gpio::{gpioa::*, gpiob::*, gpioc::*, gpiog::*, GpioExt, Input},
otg_fs::USB,
pac::{GPIOA, GPIOB, GPIOC, GPIOD, GPIOG, OTG_FS_DEVICE, OTG_FS_GLOBAL, OTG_FS_PWRCLK, SPI2},
rcc::Clocks,
spi,
spi::{NoMiso, Spi},
};
use stm32f4xx_hal::{gpio::{alt::otg_fs::{Dm, Dp},
gpioa::*,
gpiob::*,
gpioc::*,
GpioExt, Input, Speed},
otg_fs::USB,
pac::{GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, OTG_FS_DEVICE, OTG_FS_GLOBAL, OTG_FS_PWRCLK, SPI1, SPI2,
SPI3, TIM4},
rcc::Clocks,
spi::{NoMiso, Spi},
timer::{pwm::PwmExt, Channel1, Channel2, Channel3}};
pub type EthernetPins =
EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PG13<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
use crate::{device::hw_rev::{HWRev, HwRevPins},
laser_diode::{ld_ctrl::{self, LdCtrlPhy},
ld_pwr_exc_protector::LdPwrExcProtectorPhy,
max5719},
net::net::EthernetMgmtPins,
thermostat::{ad5680, ad7172,
max1968::{self, MAX1968Phy, MAX1968PinSet, PWM_FREQ_KHZ}}};
pub type EthernetPins = EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
pub fn setup(
clocks: Clocks,
tim4: TIM4,
gpioa: GPIOA,
gpiob: GPIOB,
gpioc: GPIOC,
gpiod: GPIOD,
gpiog: GPIOG,
gpioe: GPIOE,
spi1: SPI1,
spi2: SPI2,
spi3: SPI3,
otg_fs_global: OTG_FS_GLOBAL,
otg_fs_device: OTG_FS_DEVICE,
otg_fs_pwrclk: OTG_FS_PWRCLK,
) -> (
HWRev,
EthernetPins,
EthernetMgmtPins,
USB,
CurrentSourcePhyConstruct<CurrentSourcePhyCh0>,
// photo_diode_phy,
// thermostat_phy
LdCtrlPhy<ld_ctrl::Channel0>,
ad7172::AdcPhy,
MAX1968Phy<max1968::Channel0>,
LdPwrExcProtectorPhy,
) {
let gpioa = gpioa.split();
let gpiob = gpiob.split();
let gpioc = gpioc.split();
let gpiod = gpiod.split();
let gpiog = gpiog.split();
let gpioe = gpioe.split();
let mut hw_rev = HWRev::detect_hw_rev(HwRevPins {
h0: gpioe.pe8.into_input(),
h1: gpioe.pe9.into_input(),
h2: gpioe.pe10.into_input(),
h3: gpioe.pe11.into_input(),
});
hw_rev.startup_delay_before_gpio_init();
let usb = USB {
usb_global: otg_fs_global,
usb_device: otg_fs_device,
usb_pwrclk: otg_fs_pwrclk,
pin_dm: gpioa.pa11.into_alternate(),
pin_dp: gpioa.pa12.into_alternate(),
pin_dm: Dm::PA11(gpioa.pa11.into_alternate()),
pin_dp: Dp::PA12(gpioa.pa12.into_alternate()),
hclk: clocks.hclk(),
};
@@ -50,32 +73,92 @@ pub fn setup(
ref_clk: gpioa.pa1,
crs: gpioa.pa7,
tx_en: gpiob.pb11,
tx_d0: gpiog.pg13,
tx_d0: gpiob.pb12,
tx_d1: gpiob.pb13,
rx_d0: gpioc.pc4,
rx_d1: gpioc.pc5,
};
let current_source_phy = CurrentSourcePhyConstruct {
max5719_spi: Spi::new(
spi2,
(
gpiob.pb10.into_alternate(),
NoMiso {},
gpiob.pb15.into_alternate(),
let mut eth_mgmt_pins = EthernetMgmtPins {
mdio: gpioa.pa2.into_alternate::<11>(),
mdc: gpioc.pc1.into_alternate::<11>(),
};
eth_mgmt_pins.mdio.set_speed(Speed::VeryHigh);
eth_mgmt_pins.mdc.set_speed(Speed::VeryHigh);
let current_source_phy = LdCtrlPhy {
dac: max5719::Dac::new(
Spi::new(
spi2,
(gpiob.pb10.into_alternate(), NoMiso::new(), gpiob.pb15.into_alternate()),
max5719::SPI_MODE,
max5719::SPI_CLOCK_MHZ.convert(),
&clocks,
),
spi::Mode {
polarity: spi::Polarity::IdleLow,
phase: spi::Phase::CaptureOnFirstTransition,
},
10_u32.MHz(),
&clocks,
gpiod.pd8.into_push_pull_output(),
gpiob.pb14.into_push_pull_output(),
),
max5719_load: gpiob.pb14.into_push_pull_output(),
max5719_cs: gpiod.pd8.into_push_pull_output(),
current_source_ldo_en: gpiod.pd9.into_push_pull_output(),
current_source_short: gpioa.pa4.into_push_pull_output(),
current_source_short_pin: gpioa.pa4.into_push_pull_output(),
termination_status_pin: gpiod.pd7.internal_pull_up(true),
};
(eth_pins, usb, current_source_phy)
let pd_mon_phy = LdPwrExcProtectorPhy {
_pd_mon_ch0: gpioa.pa3.into_analog(),
pwr_en_ch0: gpiod.pd9.into_push_pull_output(),
};
let pwm_chs = (
Channel1::new(gpiob.pb6),
Channel2::new(gpiob.pb7),
Channel3::new(gpiob.pb8),
);
let (max_i_neg0, max_v0, max_i_pos0) = tim4.pwm_hz(pwm_chs, PWM_FREQ_KHZ.convert(), &clocks).split();
let max1968_phy = MAX1968Phy::new(MAX1968PinSet {
dac: ad5680::Dac::new(
Spi::new(
spi1,
(gpiob.pb3.into_alternate(), NoMiso::new(), gpiob.pb5.into_alternate()),
ad5680::SPI_MODE,
ad5680::SPI_CLOCK_MHZ.convert(),
&clocks,
),
gpiob.pb4.into_push_pull_output(),
),
shdn: gpioa.pa5.into_push_pull_output(),
vref_pin: gpioa.pa6.into_analog(),
itec_pin: gpiob.pb1.into_analog(),
dac_feedback_pin: gpioc.pc0.into_analog(),
vtec_pin: gpiob.pb0.into_analog(),
max_v: max_v0,
max_i_pos: max_i_pos0,
max_i_neg: max_i_neg0,
});
let ad7172_phy = ad7172::Adc::new(
Spi::new(
spi3,
(
gpioc.pc10.into_alternate(),
gpioc.pc11.into_alternate(),
gpioc.pc12.into_alternate(),
),
ad7172::SPI_MODE,
ad7172::SPI_CLOCK_MHZ.convert(),
&clocks,
),
gpioa.pa15.into_push_pull_output(),
)
.unwrap();
(
hw_rev,
eth_pins,
eth_mgmt_pins,
usb,
current_source_phy,
ad7172_phy,
max1968_phy,
pd_mon_phy,
)
}

68
src/device/hw_rev.rs Normal file
View File

@@ -0,0 +1,68 @@
use crc::{Crc, CRC_24_BLE};
use miniconf::Tree;
use serde::{Deserialize, Serialize};
use stm32f4xx_hal::{gpio::{Input, PE10, PE11, PE8, PE9},
signature};
use crate::device::sys_timer::sleep;
pub struct HwRevPins {
pub h0: PE8<Input>,
pub h1: PE9<Input>,
pub h2: PE10<Input>,
pub h3: PE11<Input>,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct HWRev {
pub major: u8,
pub minor: u8,
}
impl HWRev {
pub fn detect_hw_rev(hwrev_pins: HwRevPins) -> Self {
let (h0, h1, h2, h3) = (
hwrev_pins.h0.is_high(),
hwrev_pins.h1.is_high(),
hwrev_pins.h2.is_high(),
hwrev_pins.h3.is_high(),
);
match (h0, h1, h2, h3) {
(true, true, true, true) => HWRev { major: 0, minor: 3 },
(_, _, _, _) => HWRev { major: 0, minor: 0 },
}
}
/// On Rev0_3, during power up, digital power rails are stabilized way before analog power rails
/// This causes improper initialization on any peripherals requiring calibrations
/// See Issue #32 on Kirdy Hw Repo
pub fn startup_delay_before_gpio_init(&mut self) {
if self.major == 0 && self.minor == 3 {
sleep(5000);
}
}
/// On Rev0_3, it lacks pre-allocated Mac Addresses accessible on PCB.
/// This functions generate a random Mac Address with 96bit unique UUID inside STM32
/// See Issue #36 on Kirdy Hw Repo
pub fn get_mac_address(&mut self) -> [u8; 6] {
if self.major == 0 && self.minor == 3 {
let uid = signature::Uid::get();
let mut uid_data: [u8; 12] = [0; 12];
uid_data[0] = uid.x() as u8;
uid_data[1] = (uid.x() >> 8) as u8;
uid_data[2] = uid.y() as u8;
uid_data[3] = (uid.y() >> 8) as u8;
uid_data[4..11].clone_from_slice(uid.lot_num().as_bytes());
let crc: Crc<u32> = Crc::<u32>::new(&CRC_24_BLE);
let mut digest = crc.digest();
digest.update(&uid_data);
let crc24 = digest.finalize();
[0x02, 0xE0, 0xD5, (crc24 >> 16) as u8, (crc24 >> 8) as u8, (crc24 as u8)]
} else {
unimplemented!()
}
}
}

View File

@@ -3,13 +3,15 @@ pub fn init_log() {
use super::usb;
static USB_LOGGER: usb::Logger = usb::Logger;
let _ = log::set_logger(&USB_LOGGER);
log::set_max_level(log::LevelFilter::Debug);
// log::set_max_level(log::LevelFilter::Debug);
log::set_max_level(log::LevelFilter::Trace);
}
#[cfg(feature = "RTT")]
pub fn init_log() {
use super::rtt_logger;
use rtt_target::rtt_init_print;
use super::rtt_logger;
static RTT_LOGGER: rtt_logger::Logger = rtt_logger::Logger;
rtt_init_print!();
let _ = log::set_logger(&RTT_LOGGER);
@@ -18,8 +20,8 @@ pub fn init_log() {
#[cfg(feature = "semihosting")]
pub fn init_log() {
use cortex_m_log::log::{init, Logger};
use cortex_m_log::printer::semihosting::{hio::HStdout, InterruptOk};
use cortex_m_log::{log::{init, Logger},
printer::semihosting::{hio::HStdout, InterruptOk}};
use log::LevelFilter;
static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None;
let logger = Logger {

View File

@@ -1,5 +1,8 @@
pub mod boot;
pub mod dfu;
pub mod flash_store;
pub mod gpio;
pub mod hw_rev;
pub mod log_setup;
pub mod rtt_logger;
pub mod sys_timer;

View File

@@ -1,9 +1,8 @@
use core::cell::RefCell;
use core::ops::Deref;
use cortex_m::interrupt::Mutex;
use cortex_m::peripheral::syst::SystClkSource;
use core::{cell::RefCell, ops::Deref};
use cortex_m::{interrupt::Mutex, peripheral::syst::SystClkSource};
use cortex_m_rt::exception;
use stm32f4xx_hal::{pac::SYST, rcc::Clocks};
use stm32f4xx_hal::{pac::{SYST, Peripherals}, rcc::Clocks};
/// Rate in Hz
const TIMER_RATE: u32 = 1000;
@@ -11,9 +10,13 @@ const TIMER_RATE: u32 = 1000;
const TIMER_DELTA: u32 = 1000 / TIMER_RATE;
/// Elapsed time in milliseconds
static TIMER_MS: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));
static mut US_COUNT: u32 = 168;
/// Setup SysTick exception
pub fn setup(mut syst: SYST, clocks: Clocks) {
unsafe {
US_COUNT = clocks.hclk().to_MHz()
}
syst.set_clock_source(SystClkSource::Core);
syst.set_reload(clocks.hclk().to_Hz() / TIMER_RATE - 1);
syst.enable_counter();
@@ -33,6 +36,15 @@ pub fn now() -> u32 {
cortex_m::interrupt::free(|cs| *TIMER_MS.borrow(cs).borrow().deref())
}
/// Obtain current time in milliseconds + microseconds
pub fn now_precise() -> (u32, u32) {
let ms = now();
unsafe {
let us = (Peripherals::steal().STK.load.read().bits() - Peripherals::steal().STK.val.read().bits()) / US_COUNT;
(ms, us)
}
}
/// block for `amount` milliseconds
pub fn sleep(amount: u32) {
if amount == 0 {

View File

@@ -1,17 +1,14 @@
use core::{
fmt::{self, Write},
mem::MaybeUninit,
};
use core::{fmt::{self, Write},
mem::MaybeUninit};
use cortex_m::interrupt::free;
use log::{Log, Metadata, Record};
use stm32f4xx_hal::{
otg_fs::{UsbBus as Bus, USB},
pac::{interrupt, Interrupt, NVIC},
};
use usb_device::{
class_prelude::UsbBusAllocator,
prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid},
};
use stm32f4xx_hal::{otg_fs::{UsbBus as Bus, USB},
pac::{interrupt, Interrupt, NVIC}};
use usb_device::{class_prelude::UsbBusAllocator,
descriptor::lang_id,
device::StringDescriptors,
prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid}};
use usbd_serial::SerialPort;
static mut EP_MEMORY: [u32; 1024] = [0; 1024];
@@ -27,16 +24,20 @@ pub struct State {
impl State {
pub fn setup(usb: USB) {
unsafe { BUS.write(Bus::new(usb, &mut EP_MEMORY)) };
unsafe { BUS.write(Bus::new(usb, EP_MEMORY.as_mut())) };
let str_descriptor = StringDescriptors::new(lang_id::LangID::EN)
.manufacturer("M-Labs")
.product("Kirdy");
let bus = unsafe { BUS.assume_init_ref() };
let serial = SerialPort::new(bus);
let dev = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("M-Labs")
.product("thermostat")
.device_release(0x20)
.self_powered(true)
.device_class(usbd_serial::USB_CLASS_CDC)
.strings(&[str_descriptor])
.unwrap()
.build();
free(|_| unsafe {

View File

@@ -1,71 +0,0 @@
use stm32f4xx_hal::{
gpio::{gpioa::*, gpiob::*, gpiod::*, Alternate, Output, PushPull, PD9},
hal::{blocking::spi::Write, digital::v2::OutputPin},
pac::SPI2,
spi::{NoMiso, Spi, TransferModeNormal},
};
use crate::device::sys_timer::sleep;
pub trait CurrentSourcePhy {
type CurrentSourceLdoEn: OutputPin;
type CurrentSourceShort: OutputPin;
type Max5719Load: OutputPin;
type Max5719Cs: OutputPin;
type Max5719Spi: Write<u8>;
}
pub struct CurrentSourcePhyConstruct<C: CurrentSourcePhy> {
pub max5719_spi: C::Max5719Spi,
pub max5719_load: C::Max5719Load,
pub max5719_cs: C::Max5719Cs,
pub current_source_ldo_en: C::CurrentSourceLdoEn,
pub current_source_short: C::CurrentSourceShort,
}
pub struct CurrentSourceSettings {
pub output_current: f32,
}
pub struct CurrentSource<C: CurrentSourcePhy> {
pub phy: CurrentSourcePhyConstruct<C>,
pub setting: CurrentSourceSettings,
}
pub struct CurrentSourcePhyCh0;
impl CurrentSourcePhy for CurrentSourcePhyCh0 {
type CurrentSourceLdoEn = PD9<Output<PushPull>>;
type CurrentSourceShort = PA4<Output<PushPull>>;
type Max5719Load = PB14<Output<PushPull>>;
type Max5719Cs = PD8<Output<PushPull>>;
type Max5719Spi =
Spi<SPI2, (PB10<Alternate<5>>, NoMiso, PB15<Alternate<5>>), TransferModeNormal>;
}
impl<C: CurrentSourcePhy> CurrentSource<C> {
pub fn setup(&mut self) {
let _ = self.phy.max5719_load.set_high();
let _ = self.phy.max5719_cs.set_high();
let _ = self.phy.current_source_ldo_en.set_high();
sleep(10_u32);
let _ = self.phy.current_source_short.set_high();
sleep(10_u32);
}
pub fn set_current(
&mut self,
current: f32,
) -> Result<(), <<C as CurrentSourcePhy>::Max5719Spi as Write<u8>>::Error> {
let _ = self.phy.max5719_load.set_high();
let _ = self.phy.max5719_cs.set_low();
self.setting.output_current = current * 10.0 / 0.75;
let word = (((self.setting.output_current / 4.096) * 1048576.0) as u32) << 4;
let mut buf = [
((word >> 16) & 0xFF) as u8,
((word >> 8) & 0xFF) as u8,
((word >> 0) & 0xFF) as u8,
];
self.phy.max5719_spi.write(&mut buf)?;
let _ = self.phy.max5719_cs.set_high();
let _ = self.phy.max5719_load.set_low();
Ok(())
}
}

View File

@@ -0,0 +1,311 @@
use core::marker::PhantomData;
use log::info;
use miniconf::Tree;
use num_traits::Zero;
use serde::{Deserialize, Serialize};
use stm32f4xx_hal::{pac::{ADC3, TIM2},
timer::CounterUs};
use uom::si::{electric_current::{ampere, milliampere},
f32::{ElectricCurrent, ElectricPotential, ElectricalConductance, ElectricalResistance, Power},
power::milliwatt};
use crate::{device::sys_timer::sleep,
laser_diode::{ld_ctrl::{Impedance50Ohm, LdCtrl},
ld_current_out_ctrl_timer::LdCurrentOutCtrlTimer,
ld_pwr_exc_protector::{self, LdPwrExcProtector},
pd_mon_params}};
impl Settings {
pub const LD_CURRENT_MAX: ElectricCurrent = ElectricCurrent {
dimension: PhantomData,
units: PhantomData,
value: 0.3,
};
pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
dimension: PhantomData,
units: PhantomData,
value: 4.096,
};
const LD_DRIVE_TRANSIMPEDANCE: ElectricalResistance = ElectricalResistance {
dimension: PhantomData,
units: PhantomData,
value: 10.0 / 0.75,
};
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Tree)]
struct Settings {
pwr_on: bool,
default_pwr_on: bool,
ld_drive_current: ElectricCurrent,
pd_mon_params: pd_mon_params::Parameters,
ld_pwr_limit: Power,
ld_terms_short: bool,
incoming_pd_mon_params: pd_mon_params::Parameters,
}
impl Default for Settings {
fn default() -> Self {
Self {
pwr_on: false,
default_pwr_on: false,
ld_drive_current: ElectricCurrent::new::<milliampere>(0.0),
pd_mon_params: pd_mon_params::Parameters::default(),
incoming_pd_mon_params: pd_mon_params::Parameters::default(),
ld_pwr_limit: Power::new::<milliwatt>(0.0),
ld_terms_short: false,
}
}
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct StatusReport {
pwr_on: bool,
pwr_excursion: bool,
ld_i_set: ElectricCurrent,
pd_i: ElectricCurrent,
pd_pwr: Power,
term_50ohm: Impedance50Ohm,
}
pub struct LdDrive {
ctrl: LdCtrl,
settings: Settings,
}
impl LdDrive {
pub fn new(
current_source: LdCtrl,
pins_adc: ADC3,
tim2: CounterUs<TIM2>,
phy: ld_pwr_exc_protector::LdPwrExcProtectorPhy,
) -> Self {
LdPwrExcProtector::setup(pins_adc, phy);
LdCurrentOutCtrlTimer::setup(tim2);
LdDrive {
ctrl: current_source,
settings: Settings::default(),
}
}
pub fn setup(&mut self) {
LdPwrExcProtector::pwr_off();
self.ctrl.set_i(
ElectricCurrent::new::<milliampere>(0.0),
Settings::LD_DRIVE_TRANSIMPEDANCE,
Settings::DAC_OUT_V_MAX,
);
LdCurrentOutCtrlTimer::reset();
self.ld_short();
}
pub fn ld_short(&mut self) {
self.ctrl.ld_short_enable();
self.settings.ld_terms_short = true;
}
pub fn ld_open(&mut self) {
self.ctrl.ld_short_disable();
self.settings.ld_terms_short = false;
}
pub fn power_up(&mut self) {
let prev_i_set = self.settings.ld_drive_current;
LdCurrentOutCtrlTimer::reset();
let _ = self.ctrl.set_i(
ElectricCurrent::new::<milliampere>(0.0),
Settings::LD_DRIVE_TRANSIMPEDANCE,
Settings::DAC_OUT_V_MAX,
);
// Wait for the DAC to reset its voltage back to 0V
sleep(35);
LdPwrExcProtector::pwr_on