Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
LoRaPHY.h
00001 /** 00002 * @file LoRaPHY.h 00003 * 00004 * @brief An abstract class providing radio object to children and 00005 * provide base for implementing LoRa PHY layer 00006 * 00007 * \code 00008 * ______ _ 00009 * / _____) _ | | 00010 * ( (____ _____ ____ _| |_ _____ ____| |__ 00011 * \____ \| ___ | (_ _) ___ |/ ___) _ \ 00012 * _____) ) ____| | | || |_| ____( (___| | | | 00013 * (______/|_____)_|_|_| \__)_____)\____)_| |_| 00014 * (C)2013 Semtech 00015 * ___ _____ _ ___ _ _____ ___ ___ ___ ___ 00016 * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| 00017 * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| 00018 * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| 00019 * embedded.connectivity.solutions=============== 00020 * 00021 * \endcode 00022 * 00023 * Description: LoRa PHY layer 00024 * 00025 * License: Revised BSD License, see LICENSE.TXT file include in the project 00026 * 00027 * Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) 00028 * 00029 * Copyright (c) 2017, Arm Limited and affiliates. 00030 * SPDX-License-Identifier: BSD-3-Clause 00031 * 00032 */ 00033 00034 #ifndef MBED_OS_LORAPHY_BASE_ 00035 #define MBED_OS_LORAPHY_BASE_ 00036 00037 #include "platform/NonCopyable.h" 00038 00039 #include "system/LoRaWANTimer.h" 00040 #include "LoRaRadio.h" 00041 #include "lora_phy_ds.h" 00042 00043 class LoRaPHY : private mbed::NonCopyable<LoRaPHY> { 00044 00045 public: 00046 virtual ~LoRaPHY(); 00047 00048 /** Stores a reference to Radio object. 00049 * 00050 * Application is responsible for constructing a 'LoRaRadio' object 00051 * which is passed down to the PHY layer. 00052 * 00053 * @param radio a reference to radio driver object 00054 */ 00055 void set_radio_instance(LoRaRadio& radio); 00056 00057 /** Puts radio in sleep mode. 00058 * 00059 * Requests the radio driver to enter sleep mode. 00060 */ 00061 void put_radio_to_sleep(void); 00062 00063 /** Puts radio in standby mode. 00064 * 00065 * Requests the radio driver to enter standby mode. 00066 */ 00067 void put_radio_to_standby(void); 00068 00069 /** Puts radio in receive mode. 00070 * 00071 * Requests the radio driver to enter receive mode for a given time or to 00072 * enter continuous reception mode. 00073 * 00074 * @param is_rx_continuous if true, sets the radio to enter continuous 00075 * reception mode. 00076 * 00077 * @param max_rx_window duration of receive window 00078 */ 00079 void setup_rx_window(bool is_rx_continuous, uint32_t max_rx_window); 00080 00081 /** Delegates MAC layer request to transmit packet. 00082 * 00083 * @param buf a pointer to the data which needs to be transmitted 00084 * 00085 * @param size size of the data in bytes 00086 */ 00087 void handle_send(uint8_t *buf, uint8_t size); 00088 00089 /** Enables/Disables public network mode. 00090 * 00091 * Public and private LoRaWAN network constitute different preambles and 00092 * Net IDs. This API isused to tell the radio which network mode is in use. 00093 * 00094 * @param set true or false 00095 */ 00096 void setup_public_network_mode(bool set); 00097 00098 /** Provides a random number from radio. 00099 * 00100 * Returns a 32-bit random unsigned integer value based upon RSSI 00101 * measurements. 00102 * 00103 * @return a 32-bit long random number 00104 * 00105 */ 00106 uint32_t get_radio_rng(); 00107 00108 /** 00109 * @brief calculate_backoff Calculates and applies duty cycle back-off time. 00110 * Explicitly updates the band time-off. 00111 * 00112 * @param joined Set to true, if the node has already joined a network, otherwise false. 00113 * @param last_tx_was_join_req Set to true, if the last uplink was a join request. 00114 * @param dc_enabled Set to true, if the duty cycle is enabled, otherwise false. 00115 * @param channel The current channel index. 00116 * @param elapsed_time Elapsed time since the start of the node. 00117 * @param tx_toa Time-on-air of the last transmission. 00118 */ 00119 void calculate_backoff(bool joined, bool last_tx_was_join_req, bool dc_enabled, uint8_t channel, 00120 lorawan_time_t elapsed_time, lorawan_time_t tx_toa); 00121 00122 /** 00123 * Tests if a channel is on or off in the channel mask 00124 */ 00125 bool mask_bit_test(const uint16_t *mask, unsigned bit); 00126 00127 /** 00128 * Tests if a channel is on or off in the channel mask 00129 */ 00130 void mask_bit_set(uint16_t *mask, unsigned bit); 00131 00132 /** 00133 * Tests if a channel is on or off in the channel mask 00134 */ 00135 void mask_bit_clear(uint16_t *mask, unsigned bit); 00136 00137 /** Entertain a new channel request MAC command. 00138 * 00139 * MAC command subsystem processes the new channel request coming form 00140 * the network server and then MAC layer asks the PHY layer to entertain 00141 * the request. 00142 * 00143 * @param channel_id The channel ID. 00144 * @param new_channel A pointer to the new channel's parameters. 00145 * 00146 * @return bit mask, according to the LoRaWAN spec 1.0.2. 00147 */ 00148 virtual uint8_t request_new_channel(int8_t channel_id, channel_params_t* new_channel); 00149 00150 /** Process PHY layer state after a successful transmission. 00151 * @brief set_last_tx_done Updates times of the last transmission for the particular channel and 00152 * band upon which last transmission took place. 00153 * @param channel The channel in use. 00154 * @param joined Boolean telling if node has joined the network. 00155 * @param last_tx_done_time The last TX done time. 00156 */ 00157 virtual void set_last_tx_done(uint8_t channel, bool joined, lorawan_time_t last_tx_done_time); 00158 00159 /** Enables default channels only. 00160 * 00161 * Falls back to a channel mask where only default channels are enabled, all 00162 * other channels are disabled. 00163 */ 00164 virtual void restore_default_channels(); 00165 00166 /** Processes the incoming CF-list. 00167 * 00168 * Handles the payload containing CF-list and enables channels defined 00169 * therein. 00170 * 00171 * @param payload Payload to process. 00172 * @param size Size of the payload. 00173 * 00174 */ 00175 virtual void apply_cf_list(const uint8_t* payload, uint8_t size); 00176 00177 /** Calculates the next datarate to set, when ADR is on or off. 00178 * 00179 * @param restore_channel_mask A boolean set restore channel mask in case 00180 * of failure. 00181 * 00182 * @param dr_out The calculated datarate for the next TX. 00183 * 00184 * @param tx_power_out The TX power for the next TX. 00185 * 00186 * @param adr_ack_counter The calculated ADR acknowledgement counter. 00187 * 00188 * @return True, if an ADR request should be performed. 00189 */ 00190 bool get_next_ADR(bool restore_channel_mask, int8_t& dr_out, 00191 int8_t& tx_power_out, uint32_t& adr_ack_counter); 00192 00193 /** Configure radio reception. 00194 * 00195 * @param [in] config A pointer to the RX configuration. 00196 * 00197 * @return True, if the configuration was applied successfully. 00198 */ 00199 virtual bool rx_config(rx_config_params_t * config); 00200 00201 /** Computing Receive Windows 00202 * 00203 * For more details please consult the following document, chapter 3.1.2. 00204 * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf 00205 * or 00206 * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf 00207 * 00208 * Downlink start: T = Tx + 1s (+/- 20 us) 00209 * | 00210 * TRxEarly | TRxLate 00211 * | | | 00212 * | | +---+---+---+---+---+---+---+---+ 00213 * | | | Latest Rx window | 00214 * | | +---+---+---+---+---+---+---+---+ 00215 * | | | 00216 * +---+---+---+---+---+---+---+---+ 00217 * | Earliest Rx window | 00218 * +---+---+---+---+---+---+---+---+ 00219 * | 00220 * +---+---+---+---+---+---+---+---+ 00221 *Downlink preamble 8 symbols | | | | | | | | | 00222 * +---+---+---+---+---+---+---+---+ 00223 * 00224 * Worst case Rx window timings 00225 * 00226 * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME 00227 * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME 00228 * 00229 * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR 00230 * 00231 * RxOffset = ( TRxLate + TRxEarly ) / 2 00232 * 00233 * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR 00234 * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME 00235 * 00236 * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol. 00237 */ 00238 /*! 00239 * Computes the RX window timeout and offset. 00240 * 00241 * @param [in] datarate The RX window datarate index to be used. 00242 * 00243 * @param [in] min_rx_symbols The minimum number of symbols required to 00244 * detect an RX frame. 00245 * 00246 * @param [in] rx_error The maximum timing error of the receiver 00247 * in milliseconds. The receiver will turn on 00248 * in a [-rxError : +rxError] ms interval around 00249 * RxOffset. 00250 * 00251 * @param [out] rx_conf_params Pointer to the structure that needs to be 00252 * filled with receive window parameters. 00253 * 00254 */ 00255 virtual void compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols, 00256 uint32_t rx_error, 00257 rx_config_params_t *rx_conf_params); 00258 00259 /** Configure radio transmission. 00260 * 00261 * @param [in] tx_config Structure containing tx parameters. 00262 * 00263 * @param [out] tx_power The TX power which will be set. 00264 * 00265 * @param [out] tx_toa The time-on-air of the frame. 00266 * 00267 * @return True, if the configuration was applied successfully. 00268 */ 00269 virtual bool tx_config(tx_config_params_t* tx_config, int8_t* tx_power, 00270 lorawan_time_t* tx_toa); 00271 00272 /** Processes a Link ADR Request. 00273 * 00274 * @param [in] params A pointer ADR request parameters. 00275 * 00276 * @param [out] dr_out The datarate applied. 00277 * 00278 * @param [out] tx_power_out The TX power applied. 00279 * 00280 * @param [out] nb_rep_out The number of repetitions to apply. 00281 * 00282 * @param [out] nb_bytes_parsed The number of bytes parsed. 00283 * 00284 * @return The status of the operation, according to the LoRaMAC specification. 00285 */ 00286 virtual uint8_t link_ADR_request(adr_req_params_t* params, 00287 int8_t* dr_out, int8_t* tx_power_out, 00288 uint8_t* nb_rep_out, 00289 uint8_t* nb_bytes_parsed); 00290 00291 /** Accept or rejects RxParamSetupReq MAC command 00292 * 00293 * The function processes a RX parameter setup request in response to 00294 * server MAC command for RX setup. 00295 * 00296 * @param [in] params A pointer to rx parameter setup request. 00297 * 00298 * @return The status of the operation, according to the LoRaWAN specification. 00299 */ 00300 virtual uint8_t accept_rx_param_setup_req(rx_param_setup_req_t* params); 00301 00302 /** 00303 * @brief accept_tx_param_setup_req Makes decision whether to accept or reject TxParamSetupReq MAC command. 00304 * 00305 * @param ul_dwell_time The uplink dwell time. 00306 * @param dl_dwell_time The downlink dwell time. 00307 * 00308 * @return True to let the MAC know that the request is 00309 * accepted and MAC can apply TX parameters received 00310 * form Network Server. Otherwise false is returned. 00311 */ 00312 virtual bool accept_tx_param_setup_req(uint8_t ul_dwell_time, uint8_t dl_dwell_time); 00313 00314 /** Processes a DlChannelReq MAC command. 00315 * 00316 * @param channel_id The channel ID to add the frequency. 00317 * @param rx1_frequency The alternative frequency for the Rx1 window. 00318 * 00319 * @return The status of the operation, according to the LoRaWAN specification. 00320 */ 00321 virtual uint8_t dl_channel_request(uint8_t channel_id, uint32_t rx1_frequency); 00322 00323 /** Alternates the datarate of the channel for the join request. 00324 * 00325 * @param nb_trials Number of trials to be made on one given data rate. 00326 * 00327 * @return The datarate to apply . 00328 */ 00329 virtual int8_t get_alternate_DR(uint8_t nb_trials); 00330 00331 /** Searches and sets the next available channel. 00332 * 00333 * If there are multiple channels found available, one of them is selected 00334 * randomly. 00335 * 00336 * @param [in] nextChanParams Parameters for the next channel. 00337 * 00338 * @param [out] channel The next channel to use for TX. 00339 * 00340 * @param [out] time The time to wait for the next transmission according to the duty cycle. 00341 * 00342 * @param [out] aggregatedTimeOff Updates the aggregated time off. 00343 * 00344 * @return Function status [1: OK, 0: Unable to find a channel on the current datarate]. 00345 */ 00346 virtual lorawan_status_t set_next_channel(channel_selection_params_t* nextChanParams, 00347 uint8_t* channel, lorawan_time_t* time, 00348 lorawan_time_t* aggregatedTimeOff); 00349 00350 /** Adds a channel to the channel list. 00351 * 00352 * Verifies the channel parameters and if everything is found legitimate, 00353 * adds that particular channel to the channel list and updates the channel 00354 * mask. 00355 * 00356 * @param [in] new_channel A pointer to the parameters for the new channel. 00357 * @param [in] id Channel ID 00358 * 00359 * @return LORAWAN_STATUS_OK if everything goes fine, negative error code 00360 * otherwise. 00361 */ 00362 virtual lorawan_status_t add_channel(const channel_params_t* new_channel, uint8_t id); 00363 00364 /** Removes a channel from the channel list. 00365 * 00366 * @param [in] channel_id Index of the channel to be removed 00367 * 00368 * @return True, if the channel was removed successfully. 00369 */ 00370 virtual bool remove_channel(uint8_t channel_id); 00371 00372 /** Puts the radio into continuous wave mode. 00373 * 00374 * @param [in] continuous_wave A pointer to the function parameters. 00375 * 00376 * @param [in] frequency Frequency to transmit at 00377 */ 00378 virtual void set_tx_cont_mode(cw_mode_params_t * continuous_wave, 00379 uint32_t frequency = 0); 00380 00381 /** Computes new data rate according to the given offset 00382 * 00383 * @param [in] dr The current datarate. 00384 * 00385 * @param [in] dr_offset The offset to be applied. 00386 * 00387 * @return The computed datarate. 00388 */ 00389 virtual uint8_t apply_DR_offset(int8_t dr, int8_t dr_offset); 00390 00391 /** 00392 * @brief reset_to_default_values resets some parameters to default values 00393 * @param params Pointer to MAC protocol parameters which will be reset 00394 * @param init If true, most of the values will be modified 00395 */ 00396 void reset_to_default_values(loramac_protocol_params *params, bool init = false); 00397 00398 public: 00399 /** 00400 * @brief get_next_lower_tx_datarate Gets the next lower datarate 00401 * @param datarate Current TX datarate 00402 * @return Lower datarate than given one or minimum if lower cannot be found anymore 00403 */ 00404 int8_t get_next_lower_tx_datarate(int8_t datarate); 00405 00406 /** 00407 * @brief get_minimum_rx_datarate Gets the minimum RX datarate supported by a device 00408 * @return Minimum RX datarate 00409 */ 00410 uint8_t get_minimum_rx_datarate(); 00411 00412 /** 00413 * @brief get_minimum_tx_datarate Gets the minimum TX datarate supported by a device 00414 * @return Minimum TX datarate 00415 */ 00416 uint8_t get_minimum_tx_datarate(); 00417 00418 /** 00419 * @brief get_default_tx_datarate Gets the default TX datarate 00420 * @return default TX datarate 00421 */ 00422 uint8_t get_default_tx_datarate(); 00423 00424 /** 00425 * @brief get_default_tx_power Gets the default TX power 00426 * @return Default TX power 00427 */ 00428 uint8_t get_default_tx_power(); 00429 00430 /** 00431 * @brief get_max_payload Gets maximum amount in bytes which device can send 00432 * @param datarate A datarate to use 00433 * @param use_repeater If true repeater table is used, otherwise payloads table is used 00434 * @return Maximum number of bytes for payload 00435 */ 00436 uint8_t get_max_payload(uint8_t datarate, bool use_repeater = false); 00437 00438 /** 00439 * @brief get_maximum_frame_counter_gap Gets maximum frame counter gap 00440 * @return Maximum frame counter gap 00441 */ 00442 uint16_t get_maximum_frame_counter_gap(); 00443 00444 /** 00445 * @brief get_ack_timeout Gets timeout value for ACK to be received 00446 * @return ACK timeout 00447 */ 00448 uint32_t get_ack_timeout(); 00449 00450 /** 00451 * @brief get_default_rx2_frequency Gets default RX2 frequency 00452 * @return RX2 frequency 00453 */ 00454 uint32_t get_default_rx2_frequency(); 00455 00456 /** 00457 * @brief get_default_rx2_datarate Gets default RX2 datarate 00458 * @return RX2 datarate 00459 */ 00460 uint8_t get_default_rx2_datarate(); 00461 00462 /** 00463 * @brief get_channel_mask Gets the channel mask 00464 * @param get_default If true the default mask is returned, otherwise the current mask is returned 00465 * @return A channel mask 00466 */ 00467 uint16_t* get_channel_mask(bool get_default = false); 00468 00469 /** 00470 * @brief get_max_nb_channels Gets maximum number of channels supported 00471 * @return Number of channels 00472 */ 00473 uint8_t get_max_nb_channels(); 00474 00475 /** 00476 * @brief get_phy_channels Gets PHY channels 00477 * @return PHY channels 00478 */ 00479 channel_params_t* get_phy_channels(); 00480 00481 /** 00482 * @brief is_custom_channel_plan_supported Checks if custom channel plan is supported 00483 * @return True if custom channel plan is supported, false otherwise 00484 */ 00485 bool is_custom_channel_plan_supported(); 00486 00487 public: //Verifiers 00488 00489 /** 00490 * @brief verify_rx_datarate Verifies that given RX datarate is valid 00491 * @param datarate Datarate to check 00492 * @return true if given datarate is valid, false otherwise 00493 */ 00494 bool verify_rx_datarate(uint8_t datarate); 00495 00496 /** 00497 * @brief verify_tx_datarate Verifies that given TX datarate is valid 00498 * @param datarate Datarate to check 00499 * @param use_default If true validation is done against default value 00500 * @return true if given datarate is valid, false otherwise 00501 */ 00502 bool verify_tx_datarate(uint8_t datarate, bool use_default = false); 00503 00504 /** 00505 * @brief verify_tx_power Verifies that given TX power is valid 00506 * @param tx_power Power to check 00507 * @return True if valid, false otherwise 00508 */ 00509 bool verify_tx_power(uint8_t tx_power); 00510 00511 /** 00512 * @brief verify_duty_cycle Verifies that given cycle is valid 00513 * @param cycle Cycle to check 00514 * @return True if valid, false otherwise 00515 */ 00516 bool verify_duty_cycle(bool cycle); 00517 00518 /** 00519 * @brief verify_nb_join_trials Verifies that given number of trials is valid 00520 * @param nb_join_trials Number to check 00521 * @return True if valid, false otherwise 00522 */ 00523 bool verify_nb_join_trials(uint8_t nb_join_trials); 00524 00525 protected: 00526 LoRaPHY(LoRaWANTimeHandler &lora_time); 00527 00528 /** 00529 * Looks up corresponding band for a frequency. Returns -1 if not in any band. 00530 */ 00531 int lookup_band_for_frequency(uint32_t freq) const; 00532 00533 /** 00534 * Verifies, if a frequency is within a given band. 00535 */ 00536 virtual bool verify_frequency_for_band(uint32_t freq, uint8_t band) const; 00537 00538 /** 00539 * Verifies, if a value is in a given range. 00540 */ 00541 bool val_in_range(int8_t value, int8_t min, int8_t max); 00542 00543 /** 00544 * Verifies, if a datarate is available on an active channel. 00545 */ 00546 bool verify_channel_DR(uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, 00547 int8_t minDr, int8_t maxDr, channel_params_t* channels); 00548 00549 /** 00550 * Disables a channel in a given channels mask. 00551 */ 00552 bool disable_channel(uint16_t* channel_mask, uint8_t id, uint8_t max_channels); 00553 00554 /** 00555 * Counts number of bits on in a given mask 00556 */ 00557 uint8_t count_bits(uint16_t mask, uint8_t nb_bits); 00558 00559 /** 00560 * Counts the number of active channels in a given channels mask. 00561 */ 00562 uint8_t num_active_channels(uint16_t* channel_mask, uint8_t start_idx, 00563 uint8_t stop_idx); 00564 00565 /** 00566 * Copy channel masks. 00567 */ 00568 void copy_channel_mask(uint16_t* dest_mask, uint16_t* src_mask, uint8_t len); 00569 00570 /** 00571 * Updates the time-offs of the bands. 00572 */ 00573 lorawan_time_t update_band_timeoff(bool joined, bool dutyCycle, band_t * bands, 00574 uint8_t nb_bands); 00575 00576 /** 00577 * Parses the parameter of an LinkAdrRequest. 00578 */ 00579 uint8_t parse_link_ADR_req(const uint8_t* payload, link_adr_params_t* adr_params); 00580 00581 /** 00582 * Verifies and updates the datarate, the TX power and the number of repetitions 00583 * of a LinkAdrRequest. 00584 */ 00585 uint8_t verify_link_ADR_req(verify_adr_params_t* verify_params, int8_t* dr, 00586 int8_t* tx_pow, uint8_t* nb_rep); 00587 00588 /** 00589 * Computes the symbol time for LoRa modulation. 00590 */ 00591 double compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth ); 00592 00593 /** 00594 * Computes the symbol time for FSK modulation. 00595 */ 00596 double compute_symb_timeout_fsk(uint8_t phy_dr); 00597 00598 /** 00599 * Computes the RX window timeout and the RX window offset. 00600 */ 00601 void get_rx_window_params(double t_symbol, uint8_t min_rx_symbols, 00602 uint32_t rx_error, uint32_t wakeup_time, 00603 uint32_t* window_timeout, int32_t* window_offset); 00604 00605 /** 00606 * Computes the txPower, based on the max EIRP and the antenna gain. 00607 */ 00608 int8_t compute_tx_power(int8_t txPowerIndex, float maxEirp, float antennaGain); 00609 00610 /** 00611 * Provides a random number in the range provided. 00612 */ 00613 int32_t get_random(int32_t min, int32_t max); 00614 00615 /** 00616 * Get next lower data rate 00617 */ 00618 int8_t get_next_lower_dr(int8_t dr, int8_t min_dr); 00619 00620 /** 00621 * Get channel bandwidth depending upon data rate table index 00622 */ 00623 uint8_t get_bandwidth(uint8_t dr_index); 00624 00625 uint8_t enabled_channel_count(bool joined, uint8_t datarate, 00626 const uint16_t *mask, uint8_t* enabledChannels, 00627 uint8_t* delayTx); 00628 00629 bool is_datarate_supported(const int8_t datarate) const; 00630 00631 protected: 00632 LoRaRadio *_radio; 00633 LoRaWANTimeHandler &_lora_time; 00634 loraphy_params_t phy_params; 00635 }; 00636 00637 #endif /* MBED_OS_LORAPHY_BASE_ */
Generated on Tue Jul 12 2022 12:44:31 by
