Compare commits

..

12 Commits

Author SHA1 Message Date
Alexander Chemeris
bd9c6215da bump version to 0.1.10~3 2016-06-18 16:11:12 +03:00
Alexander Chemeris
832b610cce bump version to 0.1.10~2 2016-06-18 15:58:49 +03:00
Kirill Zakharenko
032d72a1b7 bump version to 0.1.10~1 2016-06-18 15:58:49 +03:00
Kirill Zakharenko
e1501c24cd debian: compile for atom arch with SSE3 optimizations 2016-06-18 15:58:49 +03:00
Alexander Chemeris
737fc258ba transceiver: WIP: Set default max delay to 2 samples.
Default value of 0 may be too harsh, especially given random Rx/Tx delay
in 1 SPS receive mode.
2016-06-18 15:58:49 +03:00
Alexander Chemeris
58f5333c0a sigProcLib: Change number of head bits in detectRACHBurst() from 4 to 8.
To match GSM 05.02 Access Burst definition.
2016-06-18 15:58:49 +03:00
Alexander Chemeris
ceb417162a transceiver: Add an option to emulate a RACH delay in random filler mode. 2016-06-18 15:58:49 +03:00
Alexander Chemeris
5bf037bf33 transceiver: Log channel number in DEBUG output of demoded bursts. 2016-06-18 15:49:21 +03:00
Alexander Chemeris
a057c7d367 radioInterface: Initialize power scale with a meaningful default.
Right now if you forget to send "POWER" control command, osmo-trx
will transmitt zeros. This is counter-intuitive and I've spent several
hours debugging this "issue". The issue may happen easily, because
osmo-bts doesn't send "POWER" command if there is no "power" setting
in the configuration file. Given that "POWER" command actually sets
attenuation, it's percieved as optional and in absence of it should
default to "POWER 0" (no attenuation), which translates to power
scale being 1.0.
2016-06-18 15:49:21 +03:00
Alexander Chemeris
2ad7101d44 uhd: Fix comment.
It's osmo-trx, not OpenBTS anymore.
2016-06-18 15:49:21 +03:00
Alexander Chemeris
f7e0eb153d radioDevice: GSMRATE macro must have parentheses around its definition.
So we had the following define:
   #define GSMRATE       1625e3/6

Now, I wanted to use it in the following expression:
   3.0/GSMRATE
which turns into:
   3.0/1625e3/6
while what I really wanted is:
   3.0/(1625e3/6) = 3.0/1625e3*6

To avoid this, all macros with calculations must be enclosed in parentheses.
2016-06-18 15:49:21 +03:00
Alexander Chemeris
256ec63115 makefile: Fix build from an external path.
When you build from an external path, compiler can't find convert.h
include, because it was specified relative to the current directory.
Change this to specify the include dit relative to the Makefile
location.
2016-06-18 15:48:40 +03:00
266 changed files with 158456 additions and 26601 deletions

View File

@@ -1,4 +0,0 @@
--exclude osmocom-bb/.*
--exclude .*h
--exclude Transceiver52M/grgsm_vitac/.*
--ignore FUNCTION_WITHOUT_ARGS

View File

@@ -1,521 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 4.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
#AfterExternBlock: false # Unknown to clang-format-5.0
BeforeCatch: false
BeforeElse: false
IndentBraces: false
#SplitEmptyFunction: true # Unknown to clang-format-4.0
#SplitEmptyRecord: true # Unknown to clang-format-4.0
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
#FixNamespaceComments: false # Unknown to clang-format-4.0
# Taken from:
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
# | sort | uniq
ForEachMacros:
- 'apei_estatus_for_each_section'
- 'ata_for_each_dev'
- 'ata_for_each_link'
- '__ata_qc_for_each'
- 'ata_qc_for_each'
- 'ata_qc_for_each_raw'
- 'ata_qc_for_each_with_internal'
- 'ax25_for_each'
- 'ax25_uid_for_each'
- '__bio_for_each_bvec'
- 'bio_for_each_bvec'
- 'bio_for_each_integrity_vec'
- '__bio_for_each_segment'
- 'bio_for_each_segment'
- 'bio_for_each_segment_all'
- 'bio_list_for_each'
- 'bip_for_each_vec'
- 'bitmap_for_each_clear_region'
- 'bitmap_for_each_set_region'
- 'blkg_for_each_descendant_post'
- 'blkg_for_each_descendant_pre'
- 'blk_queue_for_each_rl'
- 'bond_for_each_slave'
- 'bond_for_each_slave_rcu'
- 'bpf_for_each_spilled_reg'
- 'btree_for_each_safe128'
- 'btree_for_each_safe32'
- 'btree_for_each_safe64'
- 'btree_for_each_safel'
- 'card_for_each_dev'
- 'cgroup_taskset_for_each'
- 'cgroup_taskset_for_each_leader'
- 'cpufreq_for_each_entry'
- 'cpufreq_for_each_entry_idx'
- 'cpufreq_for_each_valid_entry'
- 'cpufreq_for_each_valid_entry_idx'
- 'css_for_each_child'
- 'css_for_each_descendant_post'
- 'css_for_each_descendant_pre'
- 'device_for_each_child_node'
- 'dma_fence_chain_for_each'
- 'drm_atomic_crtc_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane_state'
- 'drm_atomic_for_each_plane_damage'
- 'drm_client_for_each_connector_iter'
- 'drm_client_for_each_modeset'
- 'drm_connector_for_each_possible_encoder'
- 'drm_for_each_bridge_in_chain'
- 'drm_for_each_connector_iter'
- 'drm_for_each_crtc'
- 'drm_for_each_encoder'
- 'drm_for_each_encoder_mask'
- 'drm_for_each_fb'
- 'drm_for_each_legacy_plane'
- 'drm_for_each_plane'
- 'drm_for_each_plane_mask'
- 'drm_for_each_privobj'
- 'drm_mm_for_each_hole'
- 'drm_mm_for_each_node'
- 'drm_mm_for_each_node_in_range'
- 'drm_mm_for_each_node_safe'
- 'flow_action_for_each'
- 'for_each_active_dev_scope'
- 'for_each_active_drhd_unit'
- 'for_each_active_iommu'
- 'for_each_available_child_of_node'
- 'for_each_bio'
- 'for_each_board_func_rsrc'
- 'for_each_bvec'
- 'for_each_card_auxs'
- 'for_each_card_auxs_safe'
- 'for_each_card_components'
- 'for_each_card_pre_auxs'
- 'for_each_card_prelinks'
- 'for_each_card_rtds'
- 'for_each_card_rtds_safe'
- 'for_each_cgroup_storage_type'
- 'for_each_child_of_node'
- 'for_each_clear_bit'
- 'for_each_clear_bit_from'
- 'for_each_cmsghdr'
- 'for_each_compatible_node'
- 'for_each_component_dais'
- 'for_each_component_dais_safe'
- 'for_each_comp_order'
- 'for_each_console'
- 'for_each_cpu'
- 'for_each_cpu_and'
- 'for_each_cpu_not'
- 'for_each_cpu_wrap'
- 'for_each_dev_addr'
- 'for_each_dev_scope'
- 'for_each_displayid_db'
- 'for_each_dma_cap_mask'
- 'for_each_dpcm_be'
- 'for_each_dpcm_be_rollback'
- 'for_each_dpcm_be_safe'
- 'for_each_dpcm_fe'
- 'for_each_drhd_unit'
- 'for_each_dss_dev'
- 'for_each_efi_handle'
- 'for_each_efi_memory_desc'
- 'for_each_efi_memory_desc_in_map'
- 'for_each_element'
- 'for_each_element_extid'
- 'for_each_element_id'
- 'for_each_endpoint_of_node'
- 'for_each_evictable_lru'
- 'for_each_fib6_node_rt_rcu'
- 'for_each_fib6_walker_rt'
- 'for_each_free_mem_pfn_range_in_zone'
- 'for_each_free_mem_pfn_range_in_zone_from'
- 'for_each_free_mem_range'
- 'for_each_free_mem_range_reverse'
- 'for_each_func_rsrc'
- 'for_each_hstate'
- 'for_each_if'
- 'for_each_iommu'
- 'for_each_ip_tunnel_rcu'
- 'for_each_irq_nr'
- 'for_each_link_codecs'
- 'for_each_link_platforms'
- 'for_each_lru'
- 'for_each_matching_node'
- 'for_each_matching_node_and_match'
- 'for_each_member'
- 'for_each_memblock'
- 'for_each_memblock_type'
- 'for_each_memcg_cache_index'
- 'for_each_mem_pfn_range'
- 'for_each_mem_range'
- 'for_each_mem_range_rev'
- 'for_each_migratetype_order'
- 'for_each_msi_entry'
- 'for_each_msi_entry_safe'
- 'for_each_net'
- 'for_each_net_continue_reverse'
- 'for_each_netdev'
- 'for_each_netdev_continue'
- 'for_each_netdev_continue_rcu'
- 'for_each_netdev_continue_reverse'
- 'for_each_netdev_feature'
- 'for_each_netdev_in_bond_rcu'
- 'for_each_netdev_rcu'
- 'for_each_netdev_reverse'
- 'for_each_netdev_safe'
- 'for_each_net_rcu'
- 'for_each_new_connector_in_state'
- 'for_each_new_crtc_in_state'
- 'for_each_new_mst_mgr_in_state'
- 'for_each_new_plane_in_state'
- 'for_each_new_private_obj_in_state'
- 'for_each_node'
- 'for_each_node_by_name'
- 'for_each_node_by_type'
- 'for_each_node_mask'
- 'for_each_node_state'
- 'for_each_node_with_cpus'
- 'for_each_node_with_property'
- 'for_each_of_allnodes'
- 'for_each_of_allnodes_from'
- 'for_each_of_cpu_node'
- 'for_each_of_pci_range'
- 'for_each_old_connector_in_state'
- 'for_each_old_crtc_in_state'
- 'for_each_old_mst_mgr_in_state'
- 'for_each_oldnew_connector_in_state'
- 'for_each_oldnew_crtc_in_state'
- 'for_each_oldnew_mst_mgr_in_state'
- 'for_each_oldnew_plane_in_state'
- 'for_each_oldnew_plane_in_state_reverse'
- 'for_each_oldnew_private_obj_in_state'
- 'for_each_old_plane_in_state'
- 'for_each_old_private_obj_in_state'
- 'for_each_online_cpu'
- 'for_each_online_node'
- 'for_each_online_pgdat'
- 'for_each_pci_bridge'
- 'for_each_pci_dev'
- 'for_each_pci_msi_entry'
- 'for_each_populated_zone'
- 'for_each_possible_cpu'
- 'for_each_present_cpu'
- 'for_each_prime_number'
- 'for_each_prime_number_from'
- 'for_each_process'
- 'for_each_process_thread'
- 'for_each_property_of_node'
- 'for_each_registered_fb'
- 'for_each_reserved_mem_region'
- 'for_each_rtd_codec_dai'
- 'for_each_rtd_codec_dai_rollback'
- 'for_each_rtd_components'
- 'for_each_set_bit'
- 'for_each_set_bit_from'
- 'for_each_set_clump8'
- 'for_each_sg'
- 'for_each_sg_dma_page'
- 'for_each_sg_page'
- 'for_each_sibling_event'
- 'for_each_subelement'
- 'for_each_subelement_extid'
- 'for_each_subelement_id'
- '__for_each_thread'
- 'for_each_thread'
- 'for_each_wakeup_source'
- 'for_each_zone'
- 'for_each_zone_zonelist'
- 'for_each_zone_zonelist_nodemask'
- 'fwnode_for_each_available_child_node'
- 'fwnode_for_each_child_node'
- 'fwnode_graph_for_each_endpoint'
- 'gadget_for_each_ep'
- 'genradix_for_each'
- 'genradix_for_each_from'
- 'hash_for_each'
- 'hash_for_each_possible'
- 'hash_for_each_possible_rcu'
- 'hash_for_each_possible_rcu_notrace'
- 'hash_for_each_possible_safe'
- 'hash_for_each_rcu'
- 'hash_for_each_safe'
- 'hctx_for_each_ctx'
- 'hlist_bl_for_each_entry'
- 'hlist_bl_for_each_entry_rcu'
- 'hlist_bl_for_each_entry_safe'
- 'hlist_for_each'
- 'hlist_for_each_entry'
- 'hlist_for_each_entry_continue'
- 'hlist_for_each_entry_continue_rcu'
- 'hlist_for_each_entry_continue_rcu_bh'
- 'hlist_for_each_entry_from'
- 'hlist_for_each_entry_from_rcu'
- 'hlist_for_each_entry_rcu'
- 'hlist_for_each_entry_rcu_bh'
- 'hlist_for_each_entry_rcu_notrace'
- 'hlist_for_each_entry_safe'
- '__hlist_for_each_rcu'
- 'hlist_for_each_safe'
- 'hlist_nulls_for_each_entry'
- 'hlist_nulls_for_each_entry_from'
- 'hlist_nulls_for_each_entry_rcu'
- 'hlist_nulls_for_each_entry_safe'
- 'i3c_bus_for_each_i2cdev'
- 'i3c_bus_for_each_i3cdev'
- 'ide_host_for_each_port'
- 'ide_port_for_each_dev'
- 'ide_port_for_each_present_dev'
- 'idr_for_each_entry'
- 'idr_for_each_entry_continue'
- 'idr_for_each_entry_continue_ul'
- 'idr_for_each_entry_ul'
- 'in_dev_for_each_ifa_rcu'
- 'in_dev_for_each_ifa_rtnl'
- 'inet_bind_bucket_for_each'
- 'inet_lhash2_for_each_icsk_rcu'
- 'key_for_each'
- 'key_for_each_safe'
- 'klp_for_each_func'
- 'klp_for_each_func_safe'
- 'klp_for_each_func_static'
- 'klp_for_each_object'
- 'klp_for_each_object_safe'
- 'klp_for_each_object_static'
- 'kvm_for_each_memslot'
- 'kvm_for_each_vcpu'
- 'list_for_each'
- 'list_for_each_codec'
- 'list_for_each_codec_safe'
- 'list_for_each_continue'
- 'list_for_each_entry'
- 'list_for_each_entry_continue'
- 'list_for_each_entry_continue_rcu'
- 'list_for_each_entry_continue_reverse'
- 'list_for_each_entry_from'
- 'list_for_each_entry_from_rcu'
- 'list_for_each_entry_from_reverse'
- 'list_for_each_entry_lockless'
- 'list_for_each_entry_rcu'
- 'list_for_each_entry_reverse'
- 'list_for_each_entry_safe'
- 'list_for_each_entry_safe_continue'
- 'list_for_each_entry_safe_from'
- 'list_for_each_entry_safe_reverse'
- 'list_for_each_prev'
- 'list_for_each_prev_safe'
- 'list_for_each_safe'
- 'llist_for_each'
- 'llist_for_each_entry'
- 'llist_for_each_entry_safe'
- 'llist_for_each_safe'
- 'mci_for_each_dimm'
- 'media_device_for_each_entity'
- 'media_device_for_each_intf'
- 'media_device_for_each_link'
- 'media_device_for_each_pad'
- 'nanddev_io_for_each_page'
- 'netdev_for_each_lower_dev'
- 'netdev_for_each_lower_private'
- 'netdev_for_each_lower_private_rcu'
- 'netdev_for_each_mc_addr'
- 'netdev_for_each_uc_addr'
- 'netdev_for_each_upper_dev_rcu'
- 'netdev_hw_addr_list_for_each'
- 'nft_rule_for_each_expr'
- 'nla_for_each_attr'
- 'nla_for_each_nested'
- 'nlmsg_for_each_attr'
- 'nlmsg_for_each_msg'
- 'nr_neigh_for_each'
- 'nr_neigh_for_each_safe'
- 'nr_node_for_each'
- 'nr_node_for_each_safe'
- 'of_for_each_phandle'
- 'of_property_for_each_string'
- 'of_property_for_each_u32'
- 'pci_bus_for_each_resource'
- 'ping_portaddr_for_each_entry'
- 'plist_for_each'
- 'plist_for_each_continue'
- 'plist_for_each_entry'
- 'plist_for_each_entry_continue'
- 'plist_for_each_entry_safe'
- 'plist_for_each_safe'
- 'pnp_for_each_card'
- 'pnp_for_each_dev'
- 'protocol_for_each_card'
- 'protocol_for_each_dev'
- 'queue_for_each_hw_ctx'
- 'radix_tree_for_each_slot'
- 'radix_tree_for_each_tagged'
- 'rbtree_postorder_for_each_entry_safe'
- 'rdma_for_each_block'
- 'rdma_for_each_port'
- 'resource_list_for_each_entry'
- 'resource_list_for_each_entry_safe'
- 'rhl_for_each_entry_rcu'
- 'rhl_for_each_rcu'
- 'rht_for_each'
- 'rht_for_each_entry'
- 'rht_for_each_entry_from'
- 'rht_for_each_entry_rcu'
- 'rht_for_each_entry_rcu_from'
- 'rht_for_each_entry_safe'
- 'rht_for_each_from'
- 'rht_for_each_rcu'
- 'rht_for_each_rcu_from'
- '__rq_for_each_bio'
- 'rq_for_each_bvec'
- 'rq_for_each_segment'
- 'scsi_for_each_prot_sg'
- 'scsi_for_each_sg'
- 'sctp_for_each_hentry'
- 'sctp_skb_for_each'
- 'shdma_for_each_chan'
- '__shost_for_each_device'
- 'shost_for_each_device'
- 'sk_for_each'
- 'sk_for_each_bound'
- 'sk_for_each_entry_offset_rcu'
- 'sk_for_each_from'
- 'sk_for_each_rcu'
- 'sk_for_each_safe'
- 'sk_nulls_for_each'
- 'sk_nulls_for_each_from'
- 'sk_nulls_for_each_rcu'
- 'snd_array_for_each'
- 'snd_pcm_group_for_each_entry'
- 'snd_soc_dapm_widget_for_each_path'
- 'snd_soc_dapm_widget_for_each_path_safe'
- 'snd_soc_dapm_widget_for_each_sink_path'
- 'snd_soc_dapm_widget_for_each_source_path'
- 'tb_property_for_each'
- 'tcf_exts_for_each_action'
- 'udp_portaddr_for_each_entry'
- 'udp_portaddr_for_each_entry_rcu'
- 'usb_hub_for_each_child'
- 'v4l2_device_for_each_subdev'
- 'v4l2_m2m_for_each_dst_buf'
- 'v4l2_m2m_for_each_dst_buf_safe'
- 'v4l2_m2m_for_each_src_buf'
- 'v4l2_m2m_for_each_src_buf_safe'
- 'virtio_device_for_each_vq'
- 'xa_for_each'
- 'xa_for_each_marked'
- 'xa_for_each_range'
- 'xa_for_each_start'
- 'xas_for_each'
- 'xas_for_each_conflict'
- 'xas_for_each_marked'
- 'xbc_array_for_each_value'
- 'xbc_for_each_key_value'
- 'xbc_node_for_each_array_value'
- 'xbc_node_for_each_child'
- 'xbc_node_for_each_key_value'
- 'zorro_for_each_dev'
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
#SortUsingDeclarations: false # Unknown to clang-format-4.0
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Always
...

88
.gitignore vendored
View File

@@ -1,88 +0,0 @@
# build results
*.o
*.lo
*.la
Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms
Transceiver52M/osmo-trx-ipc
Transceiver52M/osmo-trx-blade
Transceiver52M/osmo-trx-ipc2
Transceiver52M/osmo-trx-syncthing-blade
Transceiver52M/osmo-trx-syncthing-uhd
Transceiver52M/osmo-trx-syncthing-ipc
Transceiver52M/osmo-trx-ms-blade
Transceiver52M/osmo-trx-ms-uhd
Transceiver52M/osmo-trx-ms-ipc
Transceiver52M/device/ipc/uhddev_ipc.cpp
.clang-format
# tests
tests/CommonLibs/BitVectorTest
tests/CommonLibs/F16Test
tests/CommonLibs/InterthreadTest
tests/CommonLibs/LogTest
tests/CommonLibs/RegexpTest
tests/CommonLibs/SocketsTest
tests/CommonLibs/TimevalTest
tests/CommonLibs/URLEncodeTest
tests/CommonLibs/VectorTest
tests/CommonLibs/PRBSTest
tests/Transceiver52M/convolve_test
tests/Transceiver52M/LMSDeviceTest
Transceiver52M/device/ipc/ipc-driver-test
# automake/autoconf
*.in
.deps
.libs
.dirstamp
*~
Makefile
config.log
config.status
config.h
config.guess
config.sub
config/*
configure
compile
aclocal.m4
autom4te.cache
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
INSTALL
tests/package.m4
tests/testsuite
tests/atconfig
tests/testsuite.dir
tests/testsuite.log
# vim
*.sw?
# manuals
doc/manuals/*.html
doc/manuals/*.svg
doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
doc/manuals/vty/osmotrx-*-vty-reference.xml
doc/manuals/vty/osmotrx-*-vty-reference.xml.inc.gen
doc/manuals/vty/osmotrx-*-vty-reference.xml.inc.merged
doc/manuals/common
doc/manuals/build
contrib/osmo-trx.spec
!contrib/osmo-trx.spec.in
utils/osmo-prbs-tool
/.qtc_clangd/*
/.cache/*
/.vscode/*

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "osmocom-bb"]
path = osmocom-bb
url = https://gitea.osmocom.org/phone-side/osmocom-bb.git

View File

@@ -1,3 +0,0 @@
[gerrit]
host=gerrit.osmocom.org
project=osmo-trx

192
AUTHORS Normal file
View File

@@ -0,0 +1,192 @@
#
# Copyright 2008, 2009 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
David A. Burgess, dburgess@kestrelsp.com:
CLI/CLI.cpp
CLI/CLI.h
CommonLibs/Assert.h
CommonLibs/BitVector.cpp
CommonLibs/BitVectorTest.cpp
CommonLibs/Configuration.cpp
CommonLibs/Configuration.h
CommonLibs/ConfigurationTest.cpp
CommonLibs/Interthread.h
CommonLibs/InterthreadTest.cpp
CommonLibs/LinkedLists.cpp
CommonLibs/LinkedLists.h
CommonLibs/Regexp.h
CommonLibs/RegexpTest.cpp
CommonLibs/Sockets.cpp
CommonLibs/Sockets.h
CommonLibs/SocketsTest.cpp
CommonLibs/Threads.cpp
CommonLibs/Threads.h
CommonLibs/Timeval.cpp
CommonLibs/Timeval.h
CommonLibs/TimevalTest.cpp
CommonLibs/Vector.h
CommonLibs/VectorTest.cpp
Control/CallControl.cpp
Control/ControlCommon.cpp
Control/ControlCommon.h
Control/FACCHDispatch.cpp
Control/MobilityManagement.cpp
Control/PagerTest.cpp
Control/RadioResource.cpp
Control/SDCCHDispatch.cpp
GSM/GSM610Tables.cpp
GSM/GSM610Tables.h
GSM/GSMCommon.cpp
GSM/GSMCommon.h
GSM/GSMConfig.h
GSM/GSML1FEC.cpp
GSM/GSML1FEC.h
GSM/GSML2LAPDm.cpp
GSM/GSML2LAPDm.h
GSM/GSML3CCElements.cpp
GSM/GSML3CCElements.h
GSM/GSML3CCMessages.cpp
GSM/GSML3CCMessages.h
GSM/GSML3CommonElements.cpp
GSM/GSML3CommonElements.h
GSM/GSML3MMElements.cpp
GSM/GSML3MMElements.h
GSM/GSML3MMMessages.cpp
GSM/GSML3MMMessages.h
GSM/GSML3Message.cpp
GSM/GSML3Message.h
GSM/GSML3RRElements.cpp
GSM/GSML3RRElements.h
GSM/GSML3RRMessages.cpp
GSM/GSML3RRMessages.h
GSM/GSMLogicalChannel.h
GSM/GSMTDMA.cpp
GSM/GSMTDMA.h
GSM/GSMTransfer.cpp
GSM/GSMTransfer.h
LICENSEBLOCK
SIP/SIPEngine.h
SIP/SIPInterface.h
SMS/SMSMessages.cpp
SMS/SMSMessages.h
SMS/SMSTransfer.cpp
SMS/SMSTransfer.h
TRXManager/TRXManager.cpp
Transceiver/Complex.h
apps/OpenBTS900.cpp
apps/OpenBTS850.cpp
apps/OpenBTS25c3.cpp
tests/AGCHTest.cpp
tests/BeaconTest.cpp
tests/CallTest.cpp
tests/CallTest2.cpp
tests/LAPDmTest.cpp
tests/LoopbackTest.cpp
tests/RegistrationTest.cpp
tests/TRXSimulator.cpp
Harvind S. Samra, hssamra@kestrelsp.com:
Control/PagerTest.cpp
Control/RadioResource.cpp
GSM/GSMConfig.h
GSM/GSMTransfer.h
LICENSEBLOCK
Transceiver/ComplexTest.cpp
Transceiver/Transceiver.cpp
Transceiver/Transceiver.h
Transceiver/USRPDevice.cpp
Transceiver/USRPDevice.h
Transceiver/USRPping.cpp
Transceiver/radioInterface.cpp
Transceiver/radioInterface.h
Transceiver/rcvLPF_651.h
Transceiver/runTransceiver.cpp
Transceiver/sendLPF_961.h
Transceiver/sigProcLib.cpp
Transceiver/sigProcLib.h
Transceiver/sigProcLibTest.cpp
Transceiver/sweepGenerator.cpp
Transceiver/testRadio.cpp
Raffi Sevlian, raffisev@gmail.com:
Control/CallControl.cpp
Control/ControlCommon.cpp
Control/ControlCommon.h
Control/FACCHDispatch.cpp
Control/MobilityManagement.cpp
Control/PagerTest.cpp
Control/RadioResource.cpp
GSM/GSMCommon.h
GSM/GSMConfig.h
GSM/GSML1FEC.h
GSM/GSML3CCElements.cpp
GSM/GSML3CCElements.h
GSM/GSML3CCMessages.cpp
GSM/GSML3CCMessages.h
GSM/GSML3CommonElements.cpp
GSM/GSML3CommonElements.h
GSM/GSML3MMElements.cpp
GSM/GSML3MMElements.h
GSM/GSML3MMMessages.cpp
GSM/GSML3MMMessages.h
GSM/GSML3Message.cpp
GSM/GSML3Message.h
GSM/GSML3RRElements.cpp
GSM/GSML3RRElements.h
GSM/GSML3RRMessages.cpp
GSM/GSML3RRMessages.h
GSM/GSMLogicalChannel.h
GSM/GSMSAPMux.cpp
GSM/GSMSAPMux.h
GSM/GSMTransfer.h
LICENSEBLOCK
SIP/SIPEngine.cpp
SIP/SIPInterface.cpp
SIP/SIPInterface.h
SIP/SIPMessage.cpp
SIP/SIPMessage.h
SIP/SIPUtility.cpp
SIP/SIPUtility.h
SMS/CMMessage.cpp
SMS/CMMessage.h
SMS/CMProcessor.cpp
SMS/CMProcessor.h
SMS/CMTest.cpp
SMS/RLMessage.cpp
SMS/RLMessage.h
SMS/RLProcessor.cpp
SMS/RLProcessor.h
SMS/SMSMessages.cpp
SMS/SMSMessages.h
SMS/SMSProcessors.cpp
SMS/SMSProcessors.h
SMS/SMSTransfer.cpp
SMS/SMSTransfer.h
SMS/TLMessage.cpp
SMS/TLMessage.h
SMS/TLProcessor.cpp
SMS/TLProcessor.h
TRXManager/TRXManager.h
Alon Levy, alonlevy1@gmail.com
RRLPMessages.cpp
RRLPMessages.h
RRLPTest.cpp

30
COPYING
View File

@@ -666,23 +666,23 @@ For more information on this, and how to apply and follow the GNU AGPL, see
=========================================================================
This marks the end of the AGPLv3 text. The following text is appended to the
same file for convenience but constituting a distinct document, not part of the
same file for convience but constituting a distinct document, not part of the
actual AGPL text and not part of an attempt to create a deriviative work based
on the AGPLv3 text.
=========================================================================
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OsmoTRX
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
Permissive Terms Supplementing the License
1. Remote Interaction Through IP Networks.
OsmoTRX is an implementation of the GSM network cellular air interface,
OpenBTS includes an implementation of the GSM network cellular air interface,
as well as other interfaces to IP networks. The interaction of cellular
handsets with the OsmoTRX software is considered "remote network interaction"
handsets with the OpenBTS software is considered "remote network interaction"
for the purposes of the Affero General Public License and cellular users are
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
Network Interaction; Use with the GNU General Public License").
@@ -694,6 +694,17 @@ interfaces other than the GSM air interface from the requirements of Section 13
is an additional permission granted to you.
Non-Permissive Terms Supplementing The License
1. Trademarks.
"OpenBTS" is a trademark of Range Networks, Inc., registered with
the US Patent and Trademark Office. Your use of OpenBTS software under a GPL
license does not include the right to use the OpenBTS trademark in commerce.
This additional non-permissive term is consistent with Section 7 of the AGPLv3
license.
END OF ADDITIONAL TERMS
@@ -701,8 +712,13 @@ END OF ADDITIONAL TERMS
How to comply with Section 13 of the AGPLv3 license.
The recommended method for compliance with Section 13 of the AGPLv3 license is
to deliver a text message to each handset that attaches to the cellular
network which uses OsmoTRX. At a minimum, that text message should include the string
"OsmoTRX AGPLv3" and a URL that can be used to access the OsmoBTS source code. This
to deliver a text message to each handset that attaches to the OpenBTS cellular
network. At a minimum, that text message should include the string "OpenBTS
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
message need not be delivered to handsets that are denied registration with the
network, since those handsets have been denied service.
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
feature. See the OpenBTS.config.example file for more information on the use of
this feature for AGPLv3 compliance.

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -31,13 +30,12 @@
#include <iostream>
#include <stdio.h>
#include <sstream>
#include <math.h>
using namespace std;
/**
Apply a Galois polymonial to a binary sequence.
Apply a Galois polymonial to a binary seqeunce.
@param val The input sequence.
@param poly The polynomial.
@param order The order of the polynomial.
@@ -201,6 +199,49 @@ void BitVector::LSB8MSB()
uint64_t BitVector::syndrome(Generator& gen) const
{
gen.clear();
const char *dp = mStart;
while (dp<mEnd) gen.syndromeShift(*dp++);
return gen.state();
}
uint64_t BitVector::parity(Generator& gen) const
{
gen.clear();
const char *dp = mStart;
while (dp<mEnd) gen.encoderShift(*dp++);
return gen.state();
}
void BitVector::encode(const ViterbiR2O4& coder, BitVector& target)
{
size_t sz = size();
assert(sz*coder.iRate() == target.size());
// Build a "history" array where each element contains the full history.
uint32_t history[sz];
uint32_t accum = 0;
for (size_t i=0; i<sz; i++) {
accum = (accum<<1) | bit(i);
history[i] = accum;
}
// Look up histories in the pre-generated state table.
char *op = target.begin();
for (size_t i=0; i<sz; i++) {
unsigned index = coder.cMask() & history[i];
for (unsigned g=0; g<coder.iRate(); g++) {
*op++ = coder.stateTable(g,index);
}
}
}
unsigned BitVector::sum() const
{
unsigned sum = 0;
@@ -246,12 +287,148 @@ ostream& operator<<(ostream& os, const BitVector& hv)
ViterbiR2O4::ViterbiR2O4()
{
assert(mDeferral < 32);
mCoeffs[0] = 0x019;
mCoeffs[1] = 0x01b;
computeStateTables(0);
computeStateTables(1);
computeGeneratorTable();
}
void ViterbiR2O4::initializeStates()
{
for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]);
for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]);
}
void ViterbiR2O4::computeStateTables(unsigned g)
{
assert(g<mIRate);
for (unsigned state=0; state<mIStates; state++) {
// 0 input
uint32_t inputVal = state<<1;
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
// 1 input
inputVal |= 1;
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
}
}
void ViterbiR2O4::computeGeneratorTable()
{
for (unsigned index=0; index<mIStates*2; index++) {
mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index];
}
}
void ViterbiR2O4::branchCandidates()
{
// Branch to generate new input states.
const vCand *sp = mSurvivors;
for (unsigned i=0; i<mNumCands; i+=2) {
// extend and suffix
const uint32_t iState0 = (sp->iState) << 1; // input state for 0
const uint32_t iState1 = iState0 | 0x01; // input state for 1
const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output
const float cost = sp->cost;
sp++;
// 0 input extension
mCandidates[i].cost = cost;
mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask];
mCandidates[i].iState = iState0;
// 1 input extension
mCandidates[i+1].cost = cost;
mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask];
mCandidates[i+1].iState = iState1;
}
}
void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost)
{
const float *cTab[2] = {matchCost,mismatchCost};
for (unsigned i=0; i<mNumCands; i++) {
vCand& thisCand = mCandidates[i];
// We examine input bits 2 at a time for a rate 1/2 coder.
const unsigned mismatched = inSample ^ (thisCand.oState);
thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0];
}
}
void ViterbiR2O4::pruneCandidates()
{
const vCand* c1 = mCandidates; // 0-prefix
const vCand* c2 = mCandidates + mIStates; // 1-prefix
for (unsigned i=0; i<mIStates; i++) {
if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i];
else mSurvivors[i] = c2[i];
}
}
const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const
{
int minIndex = 0;
float minCost = mSurvivors[0].cost;
for (unsigned i=1; i<mIStates; i++) {
const float thisCost = mSurvivors[i].cost;
if (thisCost>=minCost) continue;
minCost = thisCost;
minIndex=i;
}
return mSurvivors[minIndex];
}
const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs)
{
branchCandidates();
getSoftCostMetrics(inSample,probs,iprobs);
pruneCandidates();
return minCost();
}
uint64_t Parity::syndrome(const BitVector& receivedCodeword)
{
return receivedCodeword.syndrome(*this);
}
void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert)
{
uint64_t pWord = data.parity(*this);
if (invert) pWord = ~pWord;
parityTarget.fillField(0,pWord,size());
}
SoftVector::SoftVector(const BitVector& source)
{
resize(source.size());
for (size_t i=0; i<size(); i++) {
if (source.bit(i)) mStart[i]=1.0F;
else mStart[i]=-1.0F;
else mStart[i]=0.0F;
}
}
@@ -261,20 +438,102 @@ BitVector SoftVector::sliced() const
size_t sz = size();
BitVector newSig(sz);
for (size_t i=0; i<sz; i++) {
if (mStart[i]>0.0F) newSig[i]=1;
if (mStart[i]>0.5F) newSig[i]=1;
else newSig[i] = 0;
}
return newSig;
}
void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
{
const size_t sz = size();
const unsigned deferral = decoder.deferral();
const size_t ctsz = sz + deferral*decoder.iRate();
assert(sz <= decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
uint32_t history[ctsz];
{
BitVector bits = sliced();
uint32_t accum = 0;
for (size_t i=0; i<sz; i++) {
accum = (accum<<1) | bits.bit(i);
history[i] = accum;
}
// Repeat last bit at the end.
for (size_t i=sz; i<ctsz; i++) {
accum = (accum<<1) | (accum & 0x01);
history[i] = accum;
}
}
// Precompute metric tables.
float matchCostTable[ctsz];
float mismatchCostTable[ctsz];
{
const float *dp = mStart;
for (size_t i=0; i<sz; i++) {
// pVal is the probability that a bit is correct.
// ipVal is the probability that a bit is incorrect.
float pVal = dp[i];
if (pVal>0.5F) pVal = 1.0F-pVal;
float ipVal = 1.0F-pVal;
// This is a cheap approximation to an ideal cost function.
if (pVal<0.01F) pVal = 0.01;
if (ipVal<0.01F) ipVal = 0.01;
matchCostTable[i] = 0.25F/ipVal;
mismatchCostTable[i] = 0.25F/pVal;
}
// pad end of table with unknowns
for (size_t i=sz; i<ctsz; i++) {
matchCostTable[i] = 0.5F;
mismatchCostTable[i] = 0.5F;
}
}
{
decoder.initializeStates();
// Each sample of history[] carries its history.
// So we only have to process every iRate-th sample.
const unsigned step = decoder.iRate();
// input pointer
const uint32_t *ip = history + step - 1;
// output pointers
char *op = target.begin();
const char *const opt = target.end();
// table pointers
const float* match = matchCostTable;
const float* mismatch = mismatchCostTable;
size_t oCount = 0;
while (op<opt) {
// Viterbi algorithm
assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1);
assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1);
const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
ip += step;
match += step;
mismatch += step;
// output
if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01;
oCount++;
}
}
}
// (pat) Added 6-22-2012
float SoftVector::getEnergy(float *plow) const
{
const SoftVector &vec = *this;
int len = vec.size();
float avg = 0; float low = 1;
for (int i = 0; i < len; i++) {
float energy = fabsf(vec[i]);
float bit = vec[i];
float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
if (energy < low) low = energy;
avg += energy/len;
}
@@ -286,12 +545,8 @@ float SoftVector::getEnergy(float *plow) const
ostream& operator<<(ostream& os, const SoftVector& sv)
{
for (size_t i=0; i<sv.size(); i++) {
if (sv[i]<-0.5) os << "0";
else if (sv[i]<-0.25) os << "o";
else if (sv[i]<0.0) os << ".";
else if (sv[i]>0.5) os << "1";
else if (sv[i]>0.25) os << "|";
else if (sv[i]>0.0) os << "'";
if (sv[i]<0.25) os << "0";
else if (sv[i]>0.75) os << "1";
else os << "-";
}
return os;

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -32,6 +30,201 @@
#include <stdint.h>
class BitVector;
class SoftVector;
/** Shift-register (LFSR) generator. */
class Generator {
private:
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
uint64_t mState; ///< shift register state. LSB is most recent.
uint64_t mMask; ///< mask for reading state
unsigned mLen; ///< number of bits used in shift register
unsigned mLen_1; ///< mLen - 1
public:
Generator(uint64_t wCoeff, unsigned wLen)
:mCoeff(wCoeff),mState(0),
mMask((1ULL<<wLen)-1),
mLen(wLen),mLen_1(wLen-1)
{ assert(wLen<64); }
void clear() { mState=0; }
/**@name Accessors */
//@{
uint64_t state() const { return mState & mMask; }
unsigned size() const { return mLen; }
//@}
/**
Calculate one bit of a syndrome.
This is in the .h for inlining.
*/
void syndromeShift(unsigned inBit)
{
const unsigned fb = (mState>>(mLen_1)) & 0x01;
mState = (mState<<1) ^ (inBit & 0x01);
if (fb) mState ^= mCoeff;
}
/**
Update the generator state by one cycle.
This is in the .h for inlining.
*/
void encoderShift(unsigned inBit)
{
const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
mState <<= 1;
if (fb) mState ^= mCoeff;
}
};
/** Parity (CRC-type) generator and checker based on a Generator. */
class Parity : public Generator {
protected:
unsigned mCodewordSize;
public:
Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
:Generator(wCoefficients, wParitySize),
mCodewordSize(wCodewordSize)
{ }
/** Compute the parity word and write it into the target segment. */
void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
/** Compute the syndrome of a received sequence. */
uint64_t syndrome(const BitVector& receivedCodeword);
};
/**
Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
This is the "workhorse" coder for most GSM channels.
*/
class ViterbiR2O4 {
private:
/**name Lots of precomputed elements so the compiler can optimize like hell. */
//@{
/**@name Core values. */
//@{
static const unsigned mIRate = 2; ///< reciprocal of rate
static const unsigned mOrder = 4; ///< memory length of generators
//@}
/**@name Derived values. */
//@{
static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
static const uint32_t mSMask = mIStates-1; ///< survivor mask
static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
static const uint32_t mOMask = (0x01<<mIRate)-1; ///< ouput mask, all iRate low bits set
static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
//@}
//@}
/** Precomputed tables. */
//@{
uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
//@}
public:
/**
A candidate sequence in a Viterbi decoder.
The 32-bit state register can support a deferral of 6 with a 4th-order coder.
*/
typedef struct candStruct {
uint32_t iState; ///< encoder input associated with this candidate
uint32_t oState; ///< encoder output associated with this candidate
float cost; ///< cost (metric value), float to support soft inputs
} vCand;
/** Clear a structure. */
void clear(vCand& v)
{
v.iState=0;
v.oState=0;
v.cost=0;
}
private:
/**@name Survivors and candidates. */
//@{
vCand mSurvivors[mIStates]; ///< current survivor pool
vCand mCandidates[2*mIStates]; ///< current candidate pool
//@}
public:
unsigned iRate() const { return mIRate; }
uint32_t cMask() const { return mCMask; }
uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
unsigned deferral() const { return mDeferral; }
ViterbiR2O4();
/** Set all cost metrics to zero. */
void initializeStates();
/**
Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
@return reference to minimum-cost candidate.
*/
const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
private:
/** Branch survivors into new candidates. */
void branchCandidates();
/** Compute cost metrics for soft-inputs. */
void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
/** Select survivors from the candidate set. */
void pruneCandidates();
/** Find the minimum cost survivor. */
const vCand& minCost() const;
/**
Precompute the state tables.
@param g Generator index 0..((1/rate)-1)
*/
void computeStateTables(unsigned g);
/**
Precompute the generator outputs.
mCoeffs must be defined first.
*/
void computeGeneratorTable();
};
class BitVector : public Vector<char> {
@@ -89,6 +282,16 @@ class BitVector : public Vector<char> {
void zero() { fill(0); }
/**@name FEC operations. */
//@{
/** Calculate the syndrome of the vector with the given Generator. */
uint64_t syndrome(Generator& gen) const;
/** Calculate the parity word for the vector with the given Generator. */
uint64_t parity(Generator& gen) const;
/** Encode the signal with the GSM rate 1/2 convolutional encoder. */
void encode(const ViterbiR2O4& encoder, BitVector& target);
//@}
/** Invert 0<->1. */
void invert();
@@ -224,20 +427,23 @@ class SoftVector: public Vector<float> {
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
//@}
// How good is the SoftVector in the sense of the bits being solid?
// Result of 1 is perfect and 0 means all the bits were 0.0
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
// (pat) How good is the SoftVector in the sense of the bits being solid?
// Result of 1 is perfect and 0 means all the bits were 0.5
// If plow is non-NULL, also return the lowest energy bit.
float getEnergy(float *low=0) const;
/** Fill with "unknown" values. */
void unknown() { fill(0.0F); }
void unknown() { fill(0.5F); }
/** Return a hard bit value from a given index by slicing. */
bool bit(size_t index) const
{
const float *dp = mStart+index;
assert(dp<mEnd);
return (*dp)>0.0F;
return (*dp)>0.5F;
}
/** Slice the whole signal into bits. */

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BitVector.h"
#include <iostream>
#include <cstdlib>
using namespace std;
int main(int argc, char *argv[])
{
BitVector v1("0000111100111100101011110000");
cout << v1 << endl;
v1.LSB8MSB();
cout << v1 << endl;
ViterbiR2O4 vCoder;
BitVector v2(v1.size()*2);
v1.encode(vCoder,v2);
cout << v2 << endl;
SoftVector sv2(v2);
cout << sv2 << endl;
for (unsigned i=0; i<sv2.size()/4; i++) sv2[random()%sv2.size()]=0.5;
cout << sv2 << endl;
BitVector v3(v1.size());
sv2.decode(vCoder,v3);
cout << v3 << endl;
cout << v3.segment(3,4) << endl;
BitVector v4(v3.segment(0,4),v3.segment(8,4));
cout << v4 << endl;
BitVector v5("000011110000");
int r1 = v5.peekField(0,8);
int r2 = v5.peekField(4,4);
int r3 = v5.peekField(4,8);
cout << r1 << ' ' << r2 << ' ' << r3 << endl;
cout << v5 << endl;
v5.fillField(0,0xa,4);
int r4 = v5.peekField(0,8);
cout << v5 << endl;
cout << r4 << endl;
v5.reverse8();
cout << v5 << endl;
BitVector mC = "000000000000111100000000000001110000011100001101000011000000000000000111000011110000100100001010000010100000101000001010000010100000010000000000000000000000000000000000000000000000001100001111000000000000000000000000000000000000000000000000000010010000101000001010000010100000101000001010000001000000000000000000000000110000111100000000000001110000101000001100000001000000000000";
SoftVector mCS(mC);
BitVector mU(mC.size()/2);
mCS.decode(vCoder,mU);
cout << "c=" << mCS << endl;
cout << "u=" << mU << endl;
unsigned char ts[9] = "abcdefgh";
BitVector tp(70);
cout << "ts=" << ts << endl;
tp.unpack(ts);
cout << "tp=" << tp << endl;
tp.pack(ts);
cout << "ts=" << ts << endl;
}

1154
CommonLibs/Configuration.cpp Normal file

File diff suppressed because it is too large Load Diff

422
CommonLibs/Configuration.h Normal file
View File

@@ -0,0 +1,422 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
* Copyright 2011, 2012 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include "sqlite3util.h"
#include <assert.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <regex.h>
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <Threads.h>
#include <stdint.h>
/** A class for configuration file errors. */
class ConfigurationTableError {};
extern char gCmdName[]; // Gotta be global, gotta be char*, gotta love it.
/** An exception thrown when a given config key isn't found. */
class ConfigurationTableKeyNotFound : public ConfigurationTableError {
private:
std::string mKey;
public:
ConfigurationTableKeyNotFound(const std::string& wKey)
:mKey(wKey)
{ }
const std::string& key() const { return mKey; }
};
class ConfigurationRecord {
private:
std::string mValue;
long mNumber;
bool mDefined;
public:
ConfigurationRecord(bool wDefined=true):
mDefined(wDefined)
{ }
ConfigurationRecord(const std::string& wValue):
mValue(wValue),
mNumber(strtol(wValue.c_str(),NULL,0)),
mDefined(true)
{ }
ConfigurationRecord(const char* wValue):
mValue(std::string(wValue)),
mNumber(strtol(wValue,NULL,0)),
mDefined(true)
{ }
const std::string& value() const { return mValue; }
long number() const { return mNumber; }
bool defined() const { return mDefined; }
float floatNumber() const;
};
/** A string class that uses a hash function for comparison. */
class HashString : public std::string {
protected:
uint64_t mHash;
void computeHash();
public:
HashString(const char* src)
:std::string(src)
{
computeHash();
}
HashString(const std::string& src)
:std::string(src)
{
computeHash();
}
HashString()
{
mHash=0;
}
HashString& operator=(std::string& src)
{
std::string::operator=(src);
computeHash();
return *this;
}
HashString& operator=(const char* src)
{
std::string::operator=(src);
computeHash();
return *this;
}
bool operator==(const HashString& other)
{
return mHash==other.mHash;
}
bool operator<(const HashString& other)
{
return mHash<other.mHash;
}
bool operator>(const HashString& other)
{
return mHash<other.mHash;
}
uint64_t hash() const { return mHash; }
};
typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap;
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
class ConfigurationKey;
typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap;
/**
A class for maintaining a configuration key-value table,
based on sqlite3 and a local map-based cache.
Thread-safe, too.
*/
class ConfigurationTable {
private:
sqlite3* mDB; ///< database connection
ConfigurationMap mCache; ///< cache of recently access configuration values
mutable Mutex mLock; ///< control for multithreaded access to the cache
std::vector<std::string> (*mCrossCheck)(const std::string&); ///< cross check callback pointer
public:
ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
/** Generate an up-to-date example sql file for new installs. */
std::string getDefaultSQL(const std::string& program, const std::string& version);
/** Generate an up-to-date TeX snippet. */
std::string getTeX(const std::string& program, const std::string& version);
/** Return true if the key is used in the table. */
bool defines(const std::string& key);
/** Return true if the application's schema knows about this key. */
bool keyDefinedInSchema(const std::string& name);
/** Return true if the provided value validates correctly against the defined schema. */
bool isValidValue(const std::string& name, const std::string& val);
/** Return true if the provided value validates correctly against the defined schema. */
bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); }
/** Return a map of all similar keys in the defined schema. */
ConfigurationKeyMap getSimilarKeys(const std::string& snippet);
/** Return true if this key is identified as static. */
bool isStatic(const std::string& key);
/**
Get a string parameter from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
std::string getStr(const std::string& key);
/**
Get a boolean from the table.
Return false if NULL or 0, true otherwise.
*/
bool getBool(const std::string& key);
/**
Get a numeric parameter from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
long getNum(const std::string& key);
/**
Get a vector of strings from the table.
*/
std::vector<std::string> getVectorOfStrings(const std::string& key);
/**
Get a float from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
float getFloat(const std::string& key);
/**
Get a numeric vector from the table.
*/
std::vector<unsigned> getVector(const std::string& key);
/** Get length of a vector */
unsigned getVectorLength(const std::string &key)
{ return getVector(key).size(); }
/** Set or change a value in the table. */
bool set(const std::string& key, const std::string& value);
/** Set or change a value in the table. */
bool set(const std::string& key, long value);
/** Create an entry in the table, no value though. */
bool set(const std::string& key);
/**
Remove an entry from the table.
Will not alter required values.
@param key The key of the item to be removed.
@return true if anything was actually removed.
*/
bool remove(const std::string& key);
/** Search the table, dumping to a stream. */
void find(const std::string& pattern, std::ostream&) const;
/** Return all key/value pairs stored in the ConfigurationTable */
ConfigurationRecordMap getAllPairs() const;
/** Define the callback to purge the cache whenever the database changes. */
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
/** Define the callback for cross checking. */
void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&));
/** Execute the application specific value cross checking logic. */
std::vector<std::string> crossCheck(const std::string& key);
/** purege cache if it exceeds a certain age */
void checkCacheAge();
/** Delete all records from the cache. */
void purge();
private:
/**
Attempt to lookup a record, cache if needed.
Throw ConfigurationTableKeyNotFound if not found.
Caller should hold mLock because the returned reference points into the cache.
*/
const ConfigurationRecord& lookup(const std::string& key);
};
typedef std::map<HashString, std::string> HashStringMap;
class SimpleKeyValue {
protected:
HashStringMap mMap;
public:
/** Take a C string "A=B" and set map["A"]="B". */
void addItem(const char*);
/** Take a C string "A=B C=D E=F ..." and add all of the pairs to the map. */
void addItems(const char*s);
/** Return a reference to the string at map["key"]. */
const char* get(const char*) const;
};
class ConfigurationKey {
public:
enum VisibilityLevel
{
CUSTOMER,
CUSTOMERSITE,
CUSTOMERTUNE,
CUSTOMERWARN,
DEVELOPER,
FACTORY
};
enum Type
{
BOOLEAN,
CHOICE_OPT,
CHOICE,
CIDR_OPT,
CIDR,
FILEPATH_OPT,
FILEPATH,
IPADDRESS_OPT,
IPADDRESS,
IPANDPORT,
MIPADDRESS_OPT,
MIPADDRESS,
PORT_OPT,
PORT,
REGEX_OPT,
REGEX,
STRING_OPT,
STRING,
VALRANGE
};
private:
std::string mName;
std::string mDefaultValue;
std::string mUnits;
VisibilityLevel mVisibility;
Type mType;
std::string mValidValues;
bool mIsStatic;
std::string mDescription;
public:
ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
mName(wName),
mDefaultValue(wDefaultValue),
mUnits(wUnits),
mVisibility(wVisibility),
mType(wType),
mValidValues(wValidValues),
mIsStatic(wIsStatic),
mDescription(wDescription)
{ }
ConfigurationKey()
{ }
const std::string& getName() const { return mName; }
const std::string& getDefaultValue() const { return mDefaultValue; }
void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
const std::string& getUnits() const { return mUnits; }
const VisibilityLevel& getVisibility() const { return mVisibility; }
const Type& getType() const { return mType; }
const std::string& getValidValues() const { return mValidValues; }
bool isStatic() const { return mIsStatic; }
const std::string& getDescription() const { return mDescription; }
static bool isValidIP(const std::string& ip);
static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger);
static const std::string visibilityLevelToString(const VisibilityLevel& visibility);
static const std::string typeToString(const ConfigurationKey::Type& type);
static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
static void printDescription(const ConfigurationKey &key, std::ostream& os);
static const std::string getARFCNsString();
};
#endif
// vim: ts=4 sw=4

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Configuration.h"
#include <iostream>
#include <string>
using namespace std;
ConfigurationKeyMap getConfigurationKeys();
ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys());
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
{
//cout << "update hook" << endl;
gConfig.purge();
}
int main(int argc, char *argv[])
{
gConfig.setUpdateHook(purgeConfig);
char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
for (int i=0; i<5; i++) {
gConfig.set(keys[i],i);
}
for (int i=0; i<5; i++) {
cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) << endl;
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
}
for (int i=0; i<5; i++) {
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
}
gConfig.set("key5","100 200 300 400 ");
std::vector<unsigned> vect = gConfig.getVector("key5");
cout << "vect length " << vect.size() << ": ";
for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i];
cout << endl;
std::vector<string> svect = gConfig.getVectorOfStrings("key5");
cout << "vect length " << svect.size() << ": ";
for (unsigned i=0; i<svect.size(); i++) cout << " " << svect[i] << ":";
cout << endl;
cout << "bool " << gConfig.getBool("booltest") << endl;
gConfig.set("booltest",1);
cout << "bool " << gConfig.getBool("booltest") << endl;
gConfig.set("booltest",0);
cout << "bool " << gConfig.getBool("booltest") << endl;
gConfig.getStr("newstring");
gConfig.getNum("numnumber");
SimpleKeyValue pairs;
pairs.addItems(" a=1 b=34 dd=143 ");
cout<< pairs.get("a") << endl;
cout<< pairs.get("b") << endl;
cout<< pairs.get("dd") << endl;
gConfig.set("fkey","123.456");
float fval = gConfig.getFloat("fkey");
cout << "fkey " << fval << endl;
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
gConfig.remove("fkey");
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
try {
gConfig.getNum("supposedtoabort");
} catch (ConfigurationTableKeyNotFound) {
cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl;
}
}
ConfigurationKeyMap getConfigurationKeys()
{
ConfigurationKeyMap map;
ConfigurationKey *tmp;
tmp = new ConfigurationKey("booltest","0",
"",
ConfigurationKey::DEVELOPER,
ConfigurationKey::BOOLEAN,
"",
false,
""
);
map[tmp->getName()] = *tmp;
free(tmp);
tmp = new ConfigurationKey("numnumber","42",
"",
ConfigurationKey::DEVELOPER,
ConfigurationKey::VALRANGE,
"0-100",
false,
""
);
map[tmp->getName()] = *tmp;
free(tmp);
tmp = new ConfigurationKey("newstring","new string value",
"",
ConfigurationKey::DEVELOPER,
ConfigurationKey::STRING,
"",
false,
""
);
map[tmp->getName()] = *tmp;
free(tmp);
return map;
}

210
CommonLibs/F16.h Normal file
View File

@@ -0,0 +1,210 @@
/*
* Copyright 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef F16_H
#define F16_H
#include <stdint.h>
#include <ostream>
/** Round a float to the appropriate F16 value. */
inline int32_t _f16_round(float f)
{
if (f>0.0F) return (int32_t)(f+0.5F);
if (f<0.0F) return (int32_t)(f-0.5F);
return 0;
}
/** A class for F15.16 fixed point arithmetic with saturation. */
class F16 {
private:
int32_t mV;
public:
F16() {}
F16(int i) { mV = i<<16; }
F16(float f) { mV = _f16_round(f*65536.0F); }
F16(double f) { mV = _f16_round((float)f*65536.0F); }
int32_t& raw() { return mV; }
const int32_t& raw() const { return mV; }
float f() const { return mV/65536.0F; }
//operator float() const { return mV/65536.0F; }
//operator int() const { return mV>>16; }
F16 operator=(float f)
{
mV = _f16_round(f*65536.0F);
return *this;
}
F16 operator=(int i)
{
mV = i<<16;
return *this;
}
F16 operator=(const F16& other)
{
mV = other.mV;
return mV;
}
F16 operator+(const F16& other) const
{
F16 retVal;
retVal.mV = mV + other.mV;
return retVal;
}
F16& operator+=(const F16& other)
{
mV += other.mV;
return *this;
}
F16 operator-(const F16& other) const
{
F16 retVal;
retVal.mV = mV - other.mV;
return retVal;
}
F16& operator-=(const F16& other)
{
mV -= other.mV;
return *this;
}
F16 operator*(const F16& other) const
{
F16 retVal;
int64_t p = (int64_t)mV * (int64_t)other.mV;
retVal.mV = p>>16;
return retVal;
}
F16& operator*=(const F16& other)
{
int64_t p = (int64_t)mV * (int64_t)other.mV;
mV = p>>16;
return *this;
}
F16 operator*(float f) const
{
F16 retVal;
retVal.mV = mV * f;
return retVal;
}
F16& operator*=(float f)
{
mV *= f;
return *this;
}
F16 operator/(const F16& other) const
{
F16 retVal;
int64_t pV = (int64_t)mV << 16;
retVal.mV = pV / other.mV;
return retVal;
}
F16& operator/=(const F16& other)
{
int64_t pV = (int64_t)mV << 16;
mV = pV / other.mV;
return *this;
}
F16 operator/(float f) const
{
F16 retVal;
retVal.mV = mV / f;
return retVal;
}
F16& operator/=(float f)
{
mV /= f;
return *this;
}
bool operator>(const F16& other) const
{
return mV>other.mV;
}
bool operator<(const F16& other) const
{
return mV<other.mV;
}
bool operator==(const F16& other) const
{
return mV==other.mV;
}
bool operator>(float f) const
{
return (mV/65536.0F) > f;
}
bool operator<(float f) const
{
return (mV/65536.0F) < f;
}
bool operator==(float f) const
{
return (mV/65536.0F) == f;
}
};
inline std::ostream& operator<<(std::ostream& os, const F16& v)
{
os << v.f();
return os;
}
#endif

55
CommonLibs/F16Test.cpp Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright 2009 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "F16.h"
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
F16 a = 2.5;
F16 b = 1.5;
F16 c = 2.5 * 1.5;
F16 d = c + a;
F16 e = 10;
cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl;
a *= 3;
b *= 0.3;
c *= e;
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
a /= 3;
b /= 0.3;
c = d * 0.05;
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
F16 f = a/d;
cout << f << ' ' << f+0.5 << endl;
}

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -47,7 +45,7 @@
// (pat) The elements in the queue are type T*, and
// the Fifo class implements the underlying queue.
// The default is class PointerFIFO, which does not place any restrictions on the type of T,
// and is implemented by allocating auxiliary structures for the queue,
// and is implemented by allocating auxilliary structures for the queue,
// or SingleLinkedList, which implements the queue using an internal pointer in type T,
// which must implement the functional interface of class SingleLinkListNode,
// namely: functions T*next() and void setNext(T*).
@@ -55,7 +53,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
protected:
Fifo mQ;
Fifo mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
@@ -162,7 +160,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
protected:
Fifo mQ;
Fifo mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
@@ -258,7 +256,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
// This recurs (and the InterthreadQueue fills up with data)
// until the read thread's accumulated temporary priority causes it to
// get a second pre-emptive activation over the writing thread,
// resulting in bursts of activity by the read thread.
// resulting in bursts of activity by the read thread.
{ ScopedLock lock(mLock);
mQ.put(val);
}
@@ -283,7 +281,7 @@ template <class T> class InterthreadQueueWithWait {
protected:
PointerFIFO mQ;
PointerFIFO mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
mutable Signal mReadSignal;
@@ -517,7 +515,7 @@ public:
@param timeout The blocking timeout in ms.
@return Pointer at key or NULL on timeout.
*/
D* read(const K &key, unsigned timeout)
D* read(const K &key, unsigned timeout) const
{
if (timeout==0) return readNoBlock(key);
ScopedLock lock(mLock);
@@ -537,7 +535,7 @@ public:
@param key The key to read from.
@return Pointer at key.
*/
D* read(const K &key)
D* read(const K &key) const
{
ScopedLock lock(mLock);
typename Map::const_iterator iter = mMap.find(key);

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -29,29 +28,21 @@
#include "Threads.h"
#include "Interthread.h"
#include <iostream>
#include <mutex>
std::mutex dbg_cout;
using namespace std;
InterthreadQueue<int> gQ;
InterthreadMap<int,int> gMap;
int q_last_read_val = -1;
int q_last_write_val;
int m_last_read_val;
int m_last_write_val;
#define CERR(text) { dbg_cout.lock() ; std::cerr << text; dbg_cout.unlock(); }
void* qWriter(void*)
{
int *p;
for (int i=0; i<20; i++) {
p = new int;
*p = i;
CERR("queue write " << *p);
COUT("queue write " << *p);
gQ.write(p);
q_last_write_val = i;
if (random()%2) sleep(1);
}
p = new int;
@@ -65,14 +56,8 @@ void* qReader(void*)
bool done = false;
while (!done) {
int *p = gQ.read();
CERR("queue read " << *p);
if (*p<0) {
assert(q_last_read_val == 19 && *p == -1);
done = true;
} else {
assert(q_last_read_val == *p - 1);
q_last_read_val = *p;
}
COUT("queue read " << *p);
if (*p<0) done=true;
delete p;
}
return NULL;
@@ -85,9 +70,8 @@ void* mapWriter(void*)
for (int i=0; i<20; i++) {
p = new int;
*p = i;
CERR("map write " << *p);
COUT("map write " << *p);
gMap.write(i,p);
m_last_write_val = i;
if (random()%2) sleep(1);
}
return NULL;
@@ -97,9 +81,7 @@ void* mapReader(void*)
{
for (int i=0; i<20; i++) {
int *p = gMap.read(i);
CERR("map read " << *p);
assert(*p == i);
m_last_read_val = *p;
COUT("map read " << *p);
// InterthreadMap will delete the pointers
// delete p;
}
@@ -127,13 +109,6 @@ int main(int argc, char *argv[])
qWriterThread.join();
mapReaderThread.join();
mapWriterThread.join();
assert(q_last_write_val == 19);
assert(q_last_read_val == 19);
assert(m_last_write_val == 19);
assert(m_last_read_val == 19);
printf("Done\n");
}

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -30,25 +29,6 @@
#include "LinkedLists.h"
PointerFIFO::~PointerFIFO()
{
ListNode *node, *next;
node = mHead;
while (node != NULL) {
next = node->next();
delete node;
node = next;
}
node = mFreeList;
while (node != NULL) {
next = node->next();
delete node;
node = next;
}
}
void PointerFIFO::push_front(void* val) // by pat
{
// Pat added this routine for completeness, but never used or tested.

View File

@@ -1,10 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -73,7 +70,6 @@ class PointerFIFO {
:mHead(NULL),mTail(NULL),mFreeList(NULL),
mSize(0)
{}
~PointerFIFO();
unsigned size() const { return mSize; }
unsigned totalSize() const { return 0; } // Not used in this version.

69
CommonLibs/LogTest.cpp Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright 2009 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <iterator>
#include "Logger.h"
#include "Configuration.h"
ConfigurationTable gConfig;
//ConfigurationTable gConfig("example.config");
void printAlarms()
{
std::ostream_iterator<std::string> output( std::cout, "\n" );
std::list<std::string> alarms = gGetLoggerAlarms();
std::cout << "# alarms = " << alarms.size() << std::endl;
std::copy( alarms.begin(), alarms.end(), output );
}
int main(int argc, char *argv[])
{
gLogInit("LogTest","NOTICE",LOG_LOCAL7);
LOG(EMERG) << " testing the logger.";
LOG(ALERT) << " testing the logger.";
LOG(CRIT) << " testing the logger.";
LOG(ERR) << " testing the logger.";
LOG(WARNING) << " testing the logger.";
LOG(NOTICE) << " testing the logger.";
LOG(INFO) << " testing the logger.";
LOG(DEBUG) << " testing the logger.";
std::cout << "\n\n\n";
std::cout << "testing Alarms\n";
std::cout << "you should see three lines:" << std::endl;
printAlarms();
std::cout << "----------- generating 20 alarms ----------" << std::endl;
for (int i = 0 ; i < 20 ; ++i) {
LOG(ALERT) << i;
}
std::cout << "you should see ten lines with the numbers 10..19:" << std::endl;
printAlarms();
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
* Copyright 2011, 2012 Range Networks, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -29,35 +30,276 @@
#include <fstream>
#include <string>
#include <stdarg.h>
#include <sys/time.h> // For gettimeofday
#include "Configuration.h"
#include "Logger.h"
#include "Threads.h" // pat added
using namespace std;
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
// Switches to enable/disable logging targets
// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
// Reference to a global config table, used all over the system.
extern ConfigurationTable gConfig;
/**@ The global alarms table. */
//@{
Mutex alarmsLock;
list<string> alarmsList;
void addAlarm(const string&);
//@}
// (pat) If Log messages are printed before the classes in this module are inited
// (which happens when static classes have constructors that do work)
// the OpenBTS just crashes.
// Prevent that by setting sLoggerInited to true when this module is inited.
static bool sLoggerInited = 0;
static struct CheckLoggerInitStatus {
CheckLoggerInitStatus() { sLoggerInited = 1; }
} sCheckloggerInitStatus;
/** Names of the logging levels. */
const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
int levelStringToInt(const string& name)
{
return os << ss.str();
// Reverse search, since the numerically larger levels are more common.
for (int i=numLevels-1; i>=0; i--) {
if (name == levelNames[i]) return i;
}
// Common substitutions.
if (name=="INFORMATION") return 6;
if (name=="WARN") return 4;
if (name=="ERROR") return 3;
if (name=="CRITICAL") return 2;
if (name=="EMERGENCY") return 0;
// Unknown level.
return -1;
}
/** Given a string, return the corresponding level name. */
int lookupLevel(const string& key)
{
string val = gConfig.getStr(key);
int level = levelStringToInt(val);
if (level == -1) {
string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
level = levelStringToInt(defaultLevel);
_LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
gConfig.set(key, defaultLevel);
}
return level;
}
int getLoggingLevel(const char* filename)
{
// Default level?
if (!filename) return lookupLevel("Log.Level");
// This can afford to be inefficient since it is not called that often.
const string keyName = string("Log.Level.") + string(filename);
if (gConfig.defines(keyName)) return lookupLevel(keyName);
return lookupLevel("Log.Level");
}
int gGetLoggingLevel(const char* filename)
{
// This is called a lot and needs to be efficient.
static Mutex sLogCacheLock;
static map<uint64_t,int> sLogCache;
static unsigned sCacheCount;
static const unsigned sCacheRefreshCount = 1000;
if (filename==NULL) return gGetLoggingLevel("");
HashString hs(filename);
uint64_t key = hs.hash();
sLogCacheLock.lock();
// Time for a cache flush?
if (sCacheCount>sCacheRefreshCount) {
sLogCache.clear();
sCacheCount=0;
}
// Is it cached already?
map<uint64_t,int>::const_iterator where = sLogCache.find(key);
sCacheCount++;
if (where!=sLogCache.end()) {
int retVal = where->second;
sLogCacheLock.unlock();
return retVal;
}
// Look it up in the config table and cache it.
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
// (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
sLogCacheLock.unlock();
int level = getLoggingLevel(filename);
sLogCacheLock.lock();
sLogCache.insert(pair<uint64_t,int>(key,level));
sLogCacheLock.unlock();
return level;
}
// copies the alarm list and returns it. list supposed to be small.
list<string> gGetLoggerAlarms()
{
alarmsLock.lock();
list<string> ret;
// excuse the "complexity", but to use std::copy with a list you need
// an insert_iterator - copy technically overwrites, doesn't insert.
insert_iterator< list<string> > ii(ret, ret.begin());
copy(alarmsList.begin(), alarmsList.end(), ii);
alarmsLock.unlock();
return ret;
}
/** Add an alarm to the alarm list. */
void addAlarm(const string& s)
{
alarmsLock.lock();
alarmsList.push_back(s);
unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
alarmsLock.unlock();
}
Log::~Log()
{
int old_state;
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
const char *fmt = neednl ? "%s\n" : "%s";
/* print related function called inside a C++ destructor, use pthread_setcancelstate() APIs.
See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
pthread_setcancelstate(old_state, NULL);
if (mDummyInit) return;
// Anything at or above LOG_CRIT is an "alarm".
// Save alarms in the local list and echo them to stderr.
if (mPriority <= LOG_ERR) {
if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
// Current logging level was already checked by the macro. So just log.
// Log to syslog
if (gLogToSyslog) {
syslog(mPriority, "%s", mStream.str().c_str());
}
// Log to file and console
if (gLogToConsole||gLogToFile) {
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
ScopedLock lock(gLogToLock);
if (gLogToConsole) {
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
std::cout << mStream.str();
if (neednl) std::cout<<"\n";
}
if (gLogToFile) {
fputs(mStream.str().c_str(),gLogToFile);
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
}
}
Log::Log(const char* name, const char* level, int facility)
{
mDummyInit = true;
gLogInit(name, level, facility);
}
ostringstream& Log::get()
{
assert(mPriority<numLevels);
mStream << levelNames[mPriority] << ' ';
return mStream;
}
void gLogInit(const char* name, const char* level, int facility)
{
// Set the level if one has been specified.
if (level) {
gConfig.set("Log.Level",level);
}
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
string str = gConfig.getStr("Log.File");
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
const char *fn = str.c_str();
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
gLogToFile = fopen(fn,"w"); // New log file each time we start.
if (gLogToFile) {
time_t now;
time(&now);
fprintf(gLogToFile,"Starting at %s",ctime(&now));
fflush(gLogToFile);
std::cout << "Logging to file: " << fn << "\n";
}
}
}
// Open the log connection.
openlog(name,0,facility);
}
void gLogEarly(int level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (gLogToSyslog) {
va_list args_copy;
va_copy(args_copy, args);
vsyslog(level | LOG_USER, fmt, args_copy);
va_end(args_copy);
}
if (gLogToConsole) {
va_list args_copy;
va_copy(args_copy, args);
vprintf(fmt, args_copy);
printf("\n");
va_end(args_copy);
}
if (gLogToFile) {
va_list args_copy;
va_copy(args_copy, args);
vfprintf(gLogToFile, fmt, args_copy);
fprintf(gLogToFile, "\n");
va_end(args_copy);
}
va_end(args);
}
// vim: ts=4 sw=4

View File

@@ -2,8 +2,6 @@
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -25,47 +23,72 @@
*/
// (pat) WARNING is stupidly defined in /usr/local/include/osipparser2/osip_const.h.
// This must be outside the #ifndef LOGGER_H to fix it as long as Logger.h included after the above file.
#ifdef WARNING
#undef WARNING
#endif
#ifndef LOGGER_H
#define LOGGER_H
#include <syslog.h>
#include <stdint.h>
#include <stdio.h>
#include <sstream>
#include <list>
#include <map>
#include <string>
extern "C" {
#include <osmocom/core/logging.h>
#include "debug.h"
}
#define _LOG(level) \
Log(LOG_##level).get() << pthread_self() \
<< timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
/* Translation for old log statements */
#ifndef LOGL_ALERT
#define LOGL_ALERT LOGL_FATAL
#endif
#ifndef LOGL_ERR
#define LOGL_ERR LOGL_ERROR
#endif
#ifndef LOGL_WARNING
#define LOGL_WARNING LOGL_NOTICE
#define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel)
#ifdef NDEBUG
#define LOG(wLevel) \
if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
#else
#define LOG(wLevel) \
if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
#endif
#define LOG(level) \
Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get()
// pat: And for your edification here are the 'levels' as defined in syslog.h:
// LOG_EMERG 0 system is unusable
// LOG_ALERT 1 action must be taken immediately
// LOG_CRIT 2 critical conditions
// LOG_ERR 3 error conditions
// LOG_WARNING 4 warning conditions
// LOG_NOTICE 5 normal, but significant, condition
// LOG_INFO 6 informational message
// LOG_DEBUG 7 debug-level message
#define LOGC(category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get()
// (pat) added - print out a var and its name.
// Use like this: int descriptive_name; LOG(INFO)<<LOGVAR(descriptive_name);
#define LOGVAR2(name,val) " " << name << "=" << (val)
#define LOGVAR(var) (" " #var "=") << var
#define LOGHEX(var) (" " #var "=0x") << hex << ((unsigned)var) << dec
#define LOGHEX2(name,val) " " << name << "=0x" << hex << ((unsigned)(val)) << dec
// These are kind of cheesy, but you can use for bitvector
#define LOGBV2(name,val) " " << name << "=(" << val<<" size:"<<val.size()<<")"
#define LOGBV(bv) LOGBV2(#bv,bv)
#define LOGVARRANGE(name,cur,lo,hi) " "<<name <<"=("<<(cur) << " range:"<<(lo) << " to "<<(hi) <<")"
#define LOGLV(category, level) \
Log(category, level, __BASE_FILE__, __LINE__).get()
#define LOGSRC(category, level, file, line) \
Log(category, level, file, line).get()
#define OBJLOG(wLevel) \
LOG(wLevel) << "obj: " << this << ' '
#define LOGCHAN(chan, category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[chan=" << chan << "] "
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
#include "Threads.h" // must be after defines above, if these files are to be allowed to use LOG()
#include "Utils.h"
/**
A C++ stream-based thread-safe logger.
Derived from Dr. Dobb's Sept. 2007 issue.
Updated to use syslog.
This object is NOT the global logger;
every log record is an object of this class.
*/
@@ -75,27 +98,42 @@ class Log {
protected:
std::ostringstream mStream; ///< This is where we buffer up the log entry.
int mCategory; ///< Priority of current report.
int mPriority; ///< Category of current report.
const char *filename; ///< Source File Name of current report.
int line; ///< Line number in source file of current report.
std::ostringstream mStream; ///< This is where we buffer up the log entry.
int mPriority; ///< Priority of current report.
bool mDummyInit;
public:
Log(int wCategory, int wPriority, const char* filename, int line)
: mCategory(wCategory), mPriority(wPriority),
filename(filename), line(line)
Log(int wPriority)
:mPriority(wPriority), mDummyInit(false)
{ }
Log(const char* name, const char* level=NULL, int facility=LOG_USER);
// Most of the work is in the destructor.
/** The destructor actually generates the log entry. */
~Log();
std::ostringstream& get();
};
extern bool gLogToConsole; // Output log messages to stdout
extern bool gLogToSyslog; // Output log messages to syslog
std::list<std::string> gGetLoggerAlarms(); ///< Get a copy of the recent alarm list.
/**@ Global control and initialization of the logging system. */
//@{
/** Initialize the global logging system. */
void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER);
/** Get the logging level associated with a given file. */
int gGetLoggingLevel(const char *filename=NULL);
/** Allow early logging when still in constructors */
void gLogEarly(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
//@}
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
#endif

View File

@@ -22,40 +22,92 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
EXTRA_DIST = \
example.config \
README.common
noinst_LTLIBRARIES = libcommon.la
libcommon_la_SOURCES = \
BitVector.cpp \
LinkedLists.cpp \
Sockets.cpp \
Threads.cpp \
Timeval.cpp \
Reporting.cpp \
Logger.cpp \
Utils.cpp \
trx_rate_ctr.cpp \
trx_vty.c \
debug.c
libcommon_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS) \
-lpthread \
$(NULL)
Configuration.cpp \
sqlite3util.cpp \
URLEncode.cpp \
Utils.cpp
noinst_PROGRAMS = \
BitVectorTest \
InterthreadTest \
SocketsTest \
TimevalTest \
RegexpTest \
VectorTest \
ConfigurationTest \
LogTest \
URLEncodeTest \
F16Test
# ReportingTest
noinst_HEADERS = \
BitVector.h \
PRBS.h \
Interthread.h \
LinkedLists.h \
Sockets.h \
Threads.h \
Timeval.h \
Regexp.h \
Vector.h \
Logger.h \
Configuration.h \
Reporting.h \
F16.h \
URLEncode.h \
Utils.h \
trx_rate_ctr.h \
trx_vty.h \
debug.h \
osmo_signal.h \
config_defs.h
Logger.h \
sqlite3util.h
URLEncodeTest_SOURCES = URLEncodeTest.cpp
URLEncodeTest_LDADD = libcommon.la
BitVectorTest_SOURCES = BitVectorTest.cpp
BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
InterthreadTest_SOURCES = InterthreadTest.cpp
InterthreadTest_LDADD = libcommon.la
InterthreadTest_LDFLAGS = -lpthread
SocketsTest_SOURCES = SocketsTest.cpp
SocketsTest_LDADD = libcommon.la
SocketsTest_LDFLAGS = -lpthread
TimevalTest_SOURCES = TimevalTest.cpp
TimevalTest_LDADD = libcommon.la
VectorTest_SOURCES = VectorTest.cpp
VectorTest_LDADD = libcommon.la $(SQLITE_LA)
RegexpTest_SOURCES = RegexpTest.cpp
RegexpTest_LDADD = libcommon.la
ConfigurationTest_SOURCES = ConfigurationTest.cpp
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
# ReportingTest_SOURCES = ReportingTest.cpp
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
LogTest_SOURCES = LogTest.cpp
LogTest_LDADD = libcommon.la $(SQLITE_LA)
F16Test_SOURCES = F16Test.cpp
MOSTLYCLEANFILES += testSource testDestination

111
CommonLibs/MemoryLeak.h Normal file
View File

@@ -0,0 +1,111 @@
/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef _MEMORYLEAK_
#define _MEMORYLEAK_ 1
#include <map>
#include "ScalarTypes.h"
#include "Logger.h"
namespace Utils {
struct MemStats {
// Enumerates the classes that are checked.
// Redundancies are ok, for example, we check BitVector and also
// several descendants of BitVector.
enum MemoryNames {
mZeroIsUnused,
mVector,
mVectorData,
mBitVector,
mByteVector,
mByteVectorData,
mRLCRawBlock,
mRLCUplinkDataBlock,
mRLCMessage,
mRLCMsgPacketDownlinkDummyControlBlock, // Redundant with RLCMessage
mTBF,
mLlcEngine,
mSgsnDownlinkMsg,
mRachInfo,
mPdpPdu,
mFECDispatchInfo,
mL3Frame,
msignalVector,
mSoftVector,
mScramblingCode,
mURlcDownSdu,
mURlcPdu,
// Must be last:
mMax,
};
int mMemTotal[mMax]; // In elements, not bytes.
int mMemNow[mMax];
const char *mMemName[mMax];
MemStats();
void memChkNew(MemoryNames memIndex, const char *id);
void memChkDel(MemoryNames memIndex, const char *id);
void text(std::ostream &os);
// We would prefer to use an unordered_map, but that requires special compile switches.
// What a super great language.
typedef std::map<std::string,Int_z> MemMapType;
MemMapType mMemMap;
};
extern struct MemStats gMemStats;
extern int gMemLeakDebug;
// This is a memory leak detector.
// Use by putting RN_MEMCHKNEW and RN_MEMCHKDEL in class constructors/destructors,
// or use the DEFINE_MEMORY_LEAK_DETECTOR class and add the defined class
// as an ancestor to the class to be memory leak checked.
struct MemLabel {
std::string mccKey;
virtual ~MemLabel() {
Int_z &tmp = Utils::gMemStats.mMemMap[mccKey]; tmp = tmp - 1;
}
};
#if RN_DISABLE_MEMORY_LEAK_TEST
#define RN_MEMCHKNEW(type)
#define RN_MEMCHKDEL(type)
#define RN_MEMLOG(type,ptr)
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
struct checkerClass {};
#else
#define RN_MEMCHKNEW(type) { Utils::gMemStats.memChkNew(Utils::MemStats::m##type,#type); }
#define RN_MEMCHKDEL(type) { Utils::gMemStats.memChkDel(Utils::MemStats::m##type,#type); }
#define RN_MEMLOG(type,ptr) { \
static std::string key = format("%s_%s:%d",#type,__FILE__,__LINE__); \
(ptr)->/* MemCheck##type:: */ mccKey = key; \
Utils::gMemStats.mMemMap[key]++; \
}
// TODO: The above assumes that checkclass is MemCheck ## subClass
#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
struct checkerClass : public virtual Utils::MemLabel { \
checkerClass() { RN_MEMCHKNEW(subClass); } \
virtual ~checkerClass() { \
RN_MEMCHKDEL(subClass); \
} \
};
#endif
} // namespace Utils
#endif

View File

@@ -1,108 +0,0 @@
/*
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifndef PRBS_H
#define PRBS_H
#include <stdint.h>
#include <assert.h>
/** Pseudo-random binary sequence (PRBS) generator (a Galois LFSR implementation). */
class PRBS {
public:
PRBS(unsigned wLen, uint64_t wCoeff, uint64_t wState = 0x01)
: mCoeff(wCoeff), mStartState(wState), mState(wState), mLen(wLen)
{ assert(wLen<=64); }
/**@name Accessors */
//@{
uint64_t coeff() const { return mCoeff; }
uint64_t state() const { return mState; }
void state(uint64_t state) { mState = state & mask(); }
unsigned size() const { return mLen; }
//@}
/**
Calculate one bit of a PRBS
*/
unsigned generateBit()
{
const unsigned result = mState & 0x01;
processBit(result);
return result;
}
/**
Update the generator state by one bit.
If you want to synchronize your PRBS to a known state, call this function
size() times passing your PRBS to it bit by bit.
*/
void processBit(unsigned inBit)
{
mState >>= 1;
if (inBit) mState ^= mCoeff;
}
/** Return true when PRBS is wrapping through initial state */
bool isFinished() const { return mStartState == mState; }
protected:
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
uint64_t mStartState; ///< initial shift register state.
uint64_t mState; ///< shift register state.
unsigned mLen; ///< number of bits used in shift register
/** Return mask for the state register */
uint64_t mask() const { return (mLen==64)?0xFFFFFFFFFFFFFFFFUL:((1<<mLen)-1); }
};
/**
A standard 9-bit based pseudorandom binary sequence (PRBS) generator.
Polynomial: x^9 + x^5 + 1
*/
class PRBS9 : public PRBS {
public:
PRBS9(uint64_t wState = 0x01)
: PRBS(9, 0x0110, wState)
{}
};
/**
A standard 15-bit based pseudorandom binary sequence (PRBS) generator.
Polynomial: x^15 + x^14 + 1
*/
class PRBS15 : public PRBS {
public:
PRBS15(uint64_t wState = 0x01)
: PRBS(15, 0x6000, wState)
{}
};
/**
A standard 64-bit based pseudorandom binary sequence (PRBS) generator.
Polynomial: x^64 + x^63 + x^61 + x^60 + 1
*/
class PRBS64 : public PRBS {
public:
PRBS64(uint64_t wState = 0x01)
: PRBS(64, 0xD800000000000000ULL, wState)
{}
};
#endif // PRBS_H

64
CommonLibs/Regexp.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REGEXPW_H
#define REGEXPW_H
#include <regex.h>
#include <iostream>
#include <stdlib.h>
class Regexp {
private:
regex_t mRegex;
public:
Regexp(const char* regexp, int flags=REG_EXTENDED)
{
int result = regcomp(&mRegex, regexp, flags);
if (result) {
char msg[256];
regerror(result,&mRegex,msg,255);
std::cerr << "Regexp compilation of " << regexp << " failed: " << msg << std::endl;
abort();
}
}
~Regexp()
{ regfree(&mRegex); }
bool match(const char *text, int flags=0) const
{ return regexec(&mRegex, text, 0, NULL, flags)==0; }
};
#endif

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -26,36 +25,24 @@
#include "BitVector.h"
#include "Regexp.h"
#include <iostream>
#include <cstdlib>
using namespace std;
int main(int argc, char *argv[])
{
BitVector v5("000011110000");
int r1 = v5.peekField(0,8);
int r2 = v5.peekField(4,4);
int r3 = v5.peekField(4,8);
cout << r1 << ' ' << r2 << ' ' << r3 << endl;
cout << v5 << endl;
v5.fillField(0,0xa,4);
int r4 = v5.peekField(0,8);
cout << v5 << endl;
cout << r4 << endl;
v5.reverse8();
cout << v5 << endl;
Regexp email("^[[:graph:]]+@[[:graph:]]+ ");
Regexp simple("^dburgess@");
const char text1[] = "dburgess@jcis.net test message";
const char text2[] = "no address text message";
unsigned char ts[9] = "abcdefgh";
BitVector tp(70);
cout << "ts=" << ts << endl;
tp.unpack(ts);
cout << "tp=" << tp << endl;
tp.pack(ts);
cout << "ts=" << ts << endl;
cout << email.match(text1) << " " << text1 << endl;
cout << email.match(text2) << " " << text2 << endl;
cout << simple.match(text1) << " " << text1 << endl;
cout << simple.match(text2) << " " << text2 << endl;
}

145
CommonLibs/Reporting.cpp Normal file
View File

@@ -0,0 +1,145 @@
/**@file Module for performance-reporting mechanisms. */
/*
* Copyright 2012 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Reporting.h"
#include "Logger.h"
#include <stdio.h>
#include <string.h>
static const char* createReportingTable = {
"CREATE TABLE IF NOT EXISTS REPORTING ("
"NAME TEXT UNIQUE NOT NULL, "
"VALUE INTEGER DEFAULT 0, "
"CLEAREDTIME INTEGER NOT NULL, "
"UPDATETIME INTEGER DEFAULT 0 "
")"
};
ReportingTable::ReportingTable(const char* filename)
{
gLogEarly(LOG_INFO | mFacility, "opening reporting table from path %s", filename);
// Connect to the database.
int rc = sqlite3_open(filename,&mDB);
if (rc) {
gLogEarly(LOG_EMERG | mFacility, "cannot open reporting database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
sqlite3_close(mDB);
mDB = NULL;
return;
}
// Create the table, if needed.
if (!sqlite3_command(mDB,createReportingTable)) {
gLogEarly(LOG_EMERG | mFacility, "cannot create reporting table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
}
}
bool ReportingTable::create(const char* paramName)
{
char cmd[200];
sprintf(cmd,"INSERT OR IGNORE INTO REPORTING (NAME,CLEAREDTIME) VALUES (\"%s\",%ld)", paramName, time(NULL));
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot create reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::incr(const char* paramName)
{
char cmd[200];
sprintf(cmd,"UPDATE REPORTING SET VALUE=VALUE+1, UPDATETIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot increment reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::max(const char* paramName, unsigned newVal)
{
char cmd[200];
sprintf(cmd,"UPDATE REPORTING SET VALUE=MAX(VALUE,%u), UPDATETIME=%ld WHERE NAME=\"%s\"", newVal, time(NULL), paramName);
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot maximize reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::clear(const char* paramName)
{
char cmd[200];
sprintf(cmd,"UPDATE REPORTING SET VALUE=0, UPDATETIME=0, CLEAREDTIME=%ld WHERE NAME=\"%s\"", time(NULL), paramName);
if (!sqlite3_command(mDB,cmd)) {
gLogEarly(LOG_CRIT|mFacility, "cannot clear reporting parameter %s, error message: %s", paramName, sqlite3_errmsg(mDB));
return false;
}
return true;
}
bool ReportingTable::create(const char* baseName, unsigned minIndex, unsigned maxIndex)
{
size_t sz = strlen(baseName);
for (unsigned i = minIndex; i<=maxIndex; i++) {
char name[sz+10];
sprintf(name,"%s.%u",baseName,i);
if (!create(name)) return false;
}
return true;
}
bool ReportingTable::incr(const char* baseName, unsigned index)
{
char name[strlen(baseName)+10];
sprintf(name,"%s.%u",baseName,index);
return incr(name);
}
bool ReportingTable::max(const char* baseName, unsigned index, unsigned newVal)
{
char name[strlen(baseName)+10];
sprintf(name,"%s.%u",baseName,index);
return max(name,newVal);
}
bool ReportingTable::clear(const char* baseName, unsigned index)
{
char name[strlen(baseName)+10];
sprintf(name,"%s.%u",baseName,index);
return clear(name);
}

86
CommonLibs/Reporting.h Normal file
View File

@@ -0,0 +1,86 @@
/**@file Module for performance-reporting mechanisms. */
/*
* Copyright 2012 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REPORTING_H
#define REPORTING_H
#include <sqlite3util.h>
#include <ostream>
/**
Collect performance statistics into a database.
Parameters are counters or max/min trackers, all integer.
*/
class ReportingTable {
private:
sqlite3* mDB; ///< database connection
int mFacility; ///< rsyslogd facility
public:
/**
Open the database connection;
create the table if it does not exist yet.
*/
ReportingTable(const char* filename);
/** Create a new parameter. */
bool create(const char* paramName);
/** Create an indexed parameter set. */
bool create(const char* baseBame, unsigned minIndex, unsigned maxIndex);
/** Increment a counter. */
bool incr(const char* paramName);
/** Increment an indexed counter. */
bool incr(const char* baseName, unsigned index);
/** Take a max of a parameter. */
bool max(const char* paramName, unsigned newVal);
/** Take a max of an indexed parameter. */
bool max(const char* paramName, unsigned index, unsigned newVal);
/** Clear a value. */
bool clear(const char* paramName);
/** Clear an indexed value. */
bool clear(const char* paramName, unsigned index);
/** Dump the database to a stream. */
void dump(std::ostream&) const;
};
#endif
// vim: ts=4 sw=4

136
CommonLibs/ScalarTypes.h Normal file
View File

@@ -0,0 +1,136 @@
/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef SCALARTYPES_H
#define SCALARTYPES_H
#include <iostream> // For size_t
#include <stdint.h>
//#include "GSMCommon.h" // Was included for Z100Timer
// We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b;
#define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
Classname() : value(Init) {} \
Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \
operator Basetype(void) const { return value; } /* Converts from basetype. */ \
Basetype operator=(Basetype wvalue) { return value = wvalue; } \
Basetype* operator&() { return &value; }
#define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \
Basetype operator++() { return ++value; } \
Basetype operator++(int) { return value++; } \
Basetype operator--() { return --value; } \
Basetype operator--(int) { return value--; } \
Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \
Basetype operator-=(Basetype wvalue) { return value = value - wvalue; }
#define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \
_INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
_INITIALIZED_SCALAR_ARITH_FUNCS(Basetype)
#define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \
template <Basetype Init> \
struct Classname_i { \
Basetype value; \
_INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \
}; \
typedef Classname_i<0> Classname_z;
// Usage:
// Where 'classname' is one of the types listed below, then:
// classname_z specifies a zero initialized type;
// classname_i<value> initializes the type to the specified value.
// We also define Float_z.
_DECLARE_SCALAR_TYPE(Int_i, Int_z, int)
_DECLARE_SCALAR_TYPE(Char_i, Char_z, signed char)
_DECLARE_SCALAR_TYPE(Int16_i, Int16_z, int16_t)
_DECLARE_SCALAR_TYPE(Int32_i, Int32_z, int32_t)
_DECLARE_SCALAR_TYPE(UInt_i, UInt_z, unsigned)
_DECLARE_SCALAR_TYPE(UChar_i, UChar_z, unsigned char)
_DECLARE_SCALAR_TYPE(UInt16_i, UInt16_z, uint16_t)
_DECLARE_SCALAR_TYPE(UInt32_i, UInt32_z, uint32_t)
_DECLARE_SCALAR_TYPE(Size_t_i, Size_t_z, size_t)
// Bool is special because it cannot accept some arithmetic funcs
//_DECLARE_SCALAR_TYPE(Bool_i, Bool_z, bool)
template <bool Init>
struct Bool_i {
bool value;
_INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init)
};
typedef Bool_i<0> Bool_z;
// float is special, because C++ does not permit the template initalization:
struct Float_z {
float value;
_INITIALIZED_SCALAR_FUNCS(Float_z,float,0)
};
struct Double_z {
double value;
_INITIALIZED_SCALAR_FUNCS(Double_z,double,0)
};
class ItemWithValueAndWidth {
public:
virtual unsigned getValue() const = 0;
virtual unsigned getWidth() const = 0;
};
// A Range Networks Field with a specified width.
// See RLCMessages.h for examples.
template <int Width=32, unsigned Init=0>
class Field_i : public ItemWithValueAndWidth
{
public:
unsigned value;
_INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init)
unsigned getWidth() const { return Width; }
unsigned getValue() const { return value; }
};
// Synonym for Field_i, but no way to do it.
template <int Width, unsigned Init=0>
class Field_z : public ItemWithValueAndWidth
{
public:
unsigned value;
_INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init)
unsigned getWidth() const { return Width; }
unsigned getValue() const { return value; }
};
// This is an uninitialized field.
template <int Width=32, unsigned Init=0>
class Field : public ItemWithValueAndWidth
{
public:
unsigned value;
_INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init)
unsigned getWidth() const { return Width; }
unsigned getValue() const { return value; }
};
// A Z100Timer with an initial value specified.
//template <int Init>
//class Z100Timer_i : public GSM::Z100Timer {
// public:
// Z100Timer_i() : GSM::Z100Timer(Init) {}
//};
#endif

333
CommonLibs/Sockets.cpp Normal file
View File

@@ -0,0 +1,333 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <sys/select.h>
#include "Threads.h"
#include "Sockets.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
{
assert(address);
assert(hostAndPort);
char *copy = strdup(hostAndPort);
char *colon = strchr(copy,':');
if (!colon) return false;
*colon = '\0';
char *host = copy;
unsigned port = strtol(colon+1,NULL,10);
bool retVal = resolveAddress(address,host,port);
free(copy);
return retVal;
}
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port)
{
assert(address);
assert(host);
// FIXME -- Need to ignore leading/trailing spaces in hostname.
struct hostent *hp;
int h_errno_local;
#ifdef HAVE_GETHOSTBYNAME2_R
struct hostent hostData;
char tmpBuffer[2048];
// There are different flavors of gethostbyname_r(), but
// latest Linux use the following form:
if (gethostbyname2_r(host, AF_INET, &hostData, tmpBuffer, sizeof(tmpBuffer), &hp, &h_errno_local)!=0) {
CERR("WARNING -- gethostbyname2_r() failed for " << host << ", " << hstrerror(h_errno_local));
return false;
}
#else
static Mutex sGethostbynameMutex;
// gethostbyname() is NOT thread-safe, so we should use a mutex here.
// Ideally it should be a global mutex for all non thread-safe socket
// operations and it should protect access to variables such as
// global h_errno.
sGethostbynameMutex.lock();
hp = gethostbyname(host);
h_errno_local = h_errno;
sGethostbynameMutex.unlock();
#endif
if (hp==NULL) {
CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno_local));
return false;
}
if (hp->h_addrtype != AF_INET) {
CERR("WARNING -- gethostbyname() resolved " << host << " to something other then AF_INET");
return false;
}
address->sin_family = hp->h_addrtype;
assert(sizeof(address->sin_addr) == hp->h_length);
memcpy(&(address->sin_addr), hp->h_addr_list[0], hp->h_length);
address->sin_port = htons(port);
return true;
}
DatagramSocket::DatagramSocket()
{
memset(mDestination, 0, sizeof(mDestination));
}
void DatagramSocket::nonblocking()
{
fcntl(mSocketFD,F_SETFL,O_NONBLOCK);
}
void DatagramSocket::blocking()
{
fcntl(mSocketFD,F_SETFL,0);
}
void DatagramSocket::close()
{
::close(mSocketFD);
}
DatagramSocket::~DatagramSocket()
{
close();
}
int DatagramSocket::write( const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0,
(struct sockaddr *)mDestination, addressSize());
if (retVal == -1 ) perror("DatagramSocket::write() failed");
return retVal;
}
int DatagramSocket::writeBack( const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0,
(struct sockaddr *)mSource, addressSize());
if (retVal == -1 ) perror("DatagramSocket::write() failed");
return retVal;
}
int DatagramSocket::write( const char * message)
{
size_t length=strlen(message)+1;
return write(message,length);
}
int DatagramSocket::writeBack( const char * message)
{
size_t length=strlen(message)+1;
return writeBack(message,length);
}
int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize());
if (retVal == -1 ) perror("DatagramSocket::send() failed");
return retVal;
}
int DatagramSocket::send(const struct sockaddr* dest, const char * message)
{
size_t length=strlen(message)+1;
return send(dest,message,length);
}
int DatagramSocket::read(char* buffer, size_t length)
{
socklen_t addr_len = sizeof(mSource);
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
(struct sockaddr*) &mSource, &addr_len);
if ((rd_length==-1) && (errno!=EAGAIN)) {
perror("DatagramSocket::read() failed");
throw SocketError();
}
return rd_length;
}
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(mSocketFD,&fds);
struct timeval tv;
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout%1000)*1000;
int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv);
if (sel<0) {
perror("DatagramSocket::read() select() failed");
throw SocketError();
}
if (sel==0) return -1;
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
return -1;
}
UDPSocket::UDPSocket(unsigned short wSrcPort)
:DatagramSocket()
{
open(wSrcPort);
}
UDPSocket::UDPSocket(unsigned short wSrcPort,
const char * wDestIP, unsigned short wDestPort )
:DatagramSocket()
{
open(wSrcPort);
destination(wDestPort, wDestIP);
}
void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
{
resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort );
}
void UDPSocket::open(unsigned short localPort)
{
// create
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
if (mSocketFD<0) {
perror("socket() failed");
throw SocketError();
}
// pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes.
int on = 1;
setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
// bind
struct sockaddr_in address;
size_t length = sizeof(address);
bzero(&address,length);
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_port = htons(localPort);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
throw SocketError();
}
}
unsigned short UDPSocket::port() const
{
struct sockaddr_in name;
socklen_t nameSize = sizeof(name);
int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize);
if (retVal==-1) throw SocketError();
return ntohs(name.sin_port);
}
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
:DatagramSocket()
{
if (localPath!=NULL) open(localPath);
if (remotePath!=NULL) destination(remotePath);
}
void UDDSocket::open(const char* localPath)
{
// create
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
if (mSocketFD<0) {
perror("socket() failed");
throw SocketError();
}
// bind
struct sockaddr_un address;
size_t length = sizeof(address);
bzero(&address,length);
address.sun_family = AF_UNIX;
strcpy(address.sun_path,localPath);
unlink(localPath);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
throw SocketError();
}
}
void UDDSocket::destination(const char* remotePath)
{
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
strcpy(unAddr->sun_path,remotePath);
}
// vim:ts=4:sw=4

193
CommonLibs/Sockets.h Normal file
View File

@@ -0,0 +1,193 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SOCKETS_H
#define SOCKETS_H
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <errno.h>
#include <list>
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#define MAX_UDP_LENGTH 1500
/** A function to resolve IP host names. */
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port);
/** Resolve an address of the form "<host>:<port>". */
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort);
/** An exception to throw when a critical socket operation fails. */
class SocketError {};
#define SOCKET_ERROR {throw SocketError(); }
/** Abstract class for connectionless sockets. */
class DatagramSocket {
protected:
int mSocketFD; ///< underlying file descriptor
char mDestination[256]; ///< address to which packets are sent
char mSource[256]; ///< return address of most recent received packet
public:
/** An almost-does-nothing constructor. */
DatagramSocket();
virtual ~DatagramSocket();
/** Return the address structure size for this socket type. */
virtual size_t addressSize() const = 0;
/**
Send a binary packet.
@param buffer The data bytes to send to mDestination.
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
@return number of bytes written, or -1 on error.
*/
int write( const char * buffer, size_t length);
/**
Send a C-style string packet.
@param buffer The data bytes to send to mDestination.
@return number of bytes written, or -1 on error.
*/
int write( const char * buffer);
/**
Send a binary packet.
@param buffer The data bytes to send to mSource.
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
@return number of bytes written, or -1 on error.
*/
int writeBack(const char * buffer, size_t length);
/**
Send a C-style string packet.
@param buffer The data bytes to send to mSource.
@return number of bytes written, or -1 on error.
*/
int writeBack(const char * buffer);
/**
Receive a packet.
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@return The number of bytes received or -1 on non-blocking pass.
*/
int read(char* buffer, size_t length);
/**
Receive a packet with a timeout.
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@param maximum wait time in milliseconds
@return The number of bytes received or -1 on timeout.
*/
int read(char* buffer, size_t length, unsigned timeout);
/** Send a packet to a given destination, other than the default. */
int send(const struct sockaddr *dest, const char * buffer, size_t length);
/** Send a C-style string to a given destination, other than the default. */
int send(const struct sockaddr *dest, const char * buffer);
/** Make the socket non-blocking. */
void nonblocking();
/** Make the socket blocking (the default). */
void blocking();
/** Close the socket. */
void close();
};
/** UDP/IP User Datagram Socket */
class UDPSocket : public DatagramSocket {
public:
/** Open a USP socket with an OS-assigned port and no default destination. */
UDPSocket( unsigned short localPort=0);
/** Given a full specification, open the socket and set the dest address. */
UDPSocket( unsigned short localPort,
const char * remoteIP, unsigned short remotePort);
/** Set the destination port. */
void destination( unsigned short wDestPort, const char * wDestIP );
/** Return the actual port number in use. */
unsigned short port() const;
/** Open and bind the UDP socket to a local port. */
void open(unsigned short localPort=0);
/** Give the return address of the most recently received packet. */
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
size_t addressSize() const { return sizeof(struct sockaddr_in); }
};
/** Unix Domain Datagram Socket */
class UDDSocket : public DatagramSocket {
public:
UDDSocket(const char* localPath=NULL, const char* remotePath=NULL);
void destination(const char* remotePath);
void open(const char* localPath);
/** Give the return address of the most recently received packet. */
const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; }
size_t addressSize() const { return sizeof(struct sockaddr_un); }
};
#endif
// vim:ts=4:sw=4

103
CommonLibs/SocketsTest.cpp Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Sockets.h"
#include "Threads.h"
#include <stdio.h>
#include <stdlib.h>
static const int gNumToSend = 10;
void *testReaderIP(void *)
{
UDPSocket readSocket(5934, "localhost", 5061);
readSocket.nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf, MAX_UDP_LENGTH);
if (count>0) {
COUT("read: " << buf);
rc++;
} else {
sleep(2);
}
}
return NULL;
}
void *testReaderUnix(void *)
{
UDDSocket readSocket("testDestination");
readSocket.nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf, MAX_UDP_LENGTH);
if (count>0) {
COUT("read: " << buf);
rc++;
} else {
sleep(2);
}
}
return NULL;
}
int main(int argc, char * argv[] )
{
Thread readerThreadIP;
readerThreadIP.start(testReaderIP,NULL);
Thread readerThreadUnix;
readerThreadUnix.start(testReaderUnix,NULL);
UDPSocket socket1(5061, "127.0.0.1",5934);
UDDSocket socket1U("testSource","testDestination");
COUT("socket1: " << socket1.port());
// give the readers time to open
sleep(1);
for (int i=0; i<gNumToSend; i++) {
socket1.write("Hello IP land");
socket1U.write("Hello Unix domain");
sleep(1);
}
readerThreadIP.join();
readerThreadUnix.join();
}
// vim: ts=4 sw=4

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -25,45 +24,85 @@
*/
#include <string.h>
#include <sys/types.h>
#include "Threads.h"
#include "Timeval.h"
#include "Logger.h"
extern "C" {
#include <osmocom/core/thread.h>
}
using namespace std;
#ifndef HAVE_ATOMIC_OPS
pthread_mutex_t atomic_ops_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
void set_selfthread_name(const char *name)
Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
void lockCout()
{
pthread_t selfid = pthread_self();
pid_t tid = osmo_gettid();
if (pthread_setname_np(selfid, name) == 0) {
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
} else {
char buf[256];
int err = errno;
char* err_str = strerror_r(err, buf, sizeof(buf));
LOG(NOTICE) << "Thread "<< selfid << " (task " << tid << ") set name \"" << name << "\" failed: (" << err << ") " << err_str;
}
gStreamLock.lock();
Timeval entryTime;
cout << entryTime << " " << pthread_self() << ": ";
}
void thread_enable_cancel(bool cancel)
void unlockCout()
{
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
cout << dec << endl << flush;
gStreamLock.unlock();
}
void lockCerr()
{
gStreamLock.lock();
Timeval entryTime;
cerr << entryTime << " " << pthread_self() << ": ";
}
void unlockCerr()
{
cerr << dec << endl << flush;
gStreamLock.unlock();
}
Mutex::Mutex()
{
bool res;
res = pthread_mutexattr_init(&mAttribs);
assert(!res);
res = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE);
assert(!res);
res = pthread_mutex_init(&mMutex,&mAttribs);
assert(!res);
}
Mutex::~Mutex()
{
pthread_mutex_destroy(&mMutex);
bool res = pthread_mutexattr_destroy(&mAttribs);
assert(!res);
}
/** Block for the signal up to the cancellation timeout. */
void Signal::wait(Mutex& wMutex, unsigned timeout) const
{
Timeval then(timeout);
struct timespec waitTime = then.timespec();
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
}
void Thread::start(void *(*task)(void*), void *arg)
{
assert(mThread==((pthread_t)0));
@@ -71,10 +110,8 @@ void Thread::start(void *(*task)(void*), void *arg)
// (pat) Moved initialization to constructor to avoid crash in destructor.
//res = pthread_attr_init(&mAttrib);
//assert(!res);
if (mStackSize != 0) {
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
}
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
res = pthread_create(&mThread, &mAttrib, task, arg);
assert(!res);
}

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -28,96 +26,138 @@
#ifndef THREADS_H
#define THREADS_H
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <pthread.h>
#include <iostream>
#include <cassert>
#include <assert.h>
#include <unistd.h>
#include "config.h"
#include "Timeval.h"
class Mutex;
/**@name Multithreaded access for standard streams. */
//@{
/**@name Functions for gStreamLock. */
//@{
extern Mutex gStreamLock; ///< global lock for cout and cerr
void lockCerr(); ///< call prior to writing cerr
void unlockCerr(); ///< call after writing cerr
void lockCout(); ///< call prior to writing cout
void unlockCout(); ///< call after writing cout
//@}
/**@name Macros for standard messages. */
//@{
#define COUT(text) { lockCout(); std::cout << text; unlockCout(); }
#define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); }
#ifdef NDEBUG
#define DCOUT(text) {}
#define OBJDCOUT(text) {}
#else
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
#endif
//@}
//@}
/**@defgroup C++ wrappers for pthread mechanisms. */
//@{
/** A class for recursive mutexes. */
/** A class for recursive mutexes based on pthread_mutex. */
class Mutex {
std::recursive_mutex m;
public:
private:
void lock() {
m.lock();
}
pthread_mutex_t mMutex;
pthread_mutexattr_t mAttribs;
bool trylock() {
return m.try_lock();
}
public:
void unlock() {
m.unlock();
}
Mutex();
~Mutex();
void lock() { pthread_mutex_lock(&mMutex); }
bool trylock() { return pthread_mutex_trylock(&mMutex)==0; }
void unlock() { pthread_mutex_unlock(&mMutex); }
friend class Signal;
};
class ScopedLock {
Mutex &mMutex;
public:
ScopedLock(Mutex &wMutex) : mMutex(wMutex) {
mMutex.lock();
}
~ScopedLock() {
mMutex.unlock();
}
private:
Mutex& mMutex;
public:
ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); }
~ScopedLock() { mMutex.unlock(); }
};
/** A C++ interthread signal. */
/** A C++ interthread signal based on pthread condition variables. */
class Signal {
/* any, because for some reason our mutex is recursive... */
std::condition_variable_any mSignal;
public:
private:
void wait(Mutex &wMutex, unsigned timeout) {
mSignal.wait_for(wMutex.m, std::chrono::milliseconds(timeout));
}
mutable pthread_cond_t mSignal;
void wait(Mutex &wMutex) {
mSignal.wait(wMutex.m);
}
public:
void signal() {
mSignal.notify_one();
}
Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); }
~Signal() { pthread_cond_destroy(&mSignal); }
/**
Block for the signal up to the cancellation timeout.
Under Linux, spurious returns are possible.
*/
void wait(Mutex& wMutex, unsigned timeout) const;
/**
Block for the signal.
Under Linux, spurious returns are possible.
*/
void wait(Mutex& wMutex) const
{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
void signal() { pthread_cond_signal(&mSignal); }
void broadcast() { pthread_cond_broadcast(&mSignal); }
void broadcast() {
mSignal.notify_all();
}
};
void set_selfthread_name(const char *name);
void thread_enable_cancel(bool cancel);
#define START_THREAD(thread,function,argument) \
thread.start((void *(*)(void*))function, (void*)argument);
/** A C++ wrapper for pthread threads. */
class Thread {
private:
private:
pthread_t mThread;
pthread_attr_t mAttrib;
// FIXME -- Can this be reduced now?
size_t mStackSize;
public:
public:
/** Create a thread in a non-running state. */
Thread(size_t wStackSize = 0) : mThread((pthread_t)0)
{
pthread_attr_init(&mAttrib); // (pat) moved this here.
mStackSize = wStackSize;
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
pthread_attr_init(&mAttrib); // (pat) moved this here.
mStackSize=wStackSize;
}
/**
@@ -125,54 +165,26 @@ class Thread {
It should be stopped and joined.
*/
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
~Thread()
{
pthread_attr_destroy(&mAttrib);
}
~Thread() { pthread_attr_destroy(&mAttrib); }
/** Start the thread on a task. */
void start(void *(*task)(void *), void *arg);
void start(void *(*task)(void*), void *arg);
/** Join a thread that will stop on its own. */
void join()
{
void join() {
if (mThread) {
int s = pthread_join(mThread, NULL);
assert(!s);
}
}
/** Send cancellation to thread */
void cancel()
{
pthread_cancel(mThread);
}
/** Send cancelation to thread */
void cancel() { pthread_cancel(mThread); }
};
#ifdef HAVE_ATOMIC_OPS
#define osmo_trx_sync_fetch_and_and(ptr, value) __sync_fetch_and_and((ptr), (value))
#define osmo_trx_sync_or_and_fetch(ptr, value) __sync_or_and_fetch((ptr), (value))
#else
extern pthread_mutex_t atomic_ops_mutex;
static inline int osmo_trx_sync_fetch_and_and(int *ptr, int value)
{
pthread_mutex_lock(&atomic_ops_mutex);
int tmp = *ptr;
*ptr &= value;
pthread_mutex_unlock(&atomic_ops_mutex);
return tmp;
}
static inline int osmo_trx_sync_or_and_fetch(int *ptr, int value)
{
int tmp;
pthread_mutex_lock(&atomic_ops_mutex);
*ptr |= value;
tmp = *ptr;
pthread_mutex_unlock(&atomic_ops_mutex);
return tmp;
}
#endif
#endif
// vim: ts=4 sw=4

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -28,49 +27,43 @@
#include "Timeval.h"
extern "C" {
#include <osmocom/core/timer.h>
}
using namespace std;
void Timeval::now()
{
osmo_clock_gettime(CLOCK_REALTIME, &mTimespec);
}
void Timeval::future(unsigned offset)
{
now();
unsigned sec = offset/1000;
unsigned msec = offset%1000;
mTimespec.tv_nsec += msec*1000*1000;
mTimespec.tv_sec += sec;
if (mTimespec.tv_nsec > 1000*1000*1000) {
mTimespec.tv_nsec -= 1000*1000*1000;
mTimespec.tv_sec += 1;
mTimeval.tv_usec += msec*1000;
mTimeval.tv_sec += sec;
if (mTimeval.tv_usec>1000000) {
mTimeval.tv_usec -= 1000000;
mTimeval.tv_sec += 1;
}
}
struct timespec Timeval::timespec() const
{
return mTimespec;
struct timespec retVal;
retVal.tv_sec = mTimeval.tv_sec;
retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
return retVal;
}
bool Timeval::passed() const
{
Timeval nowTime;
if (nowTime.mTimespec.tv_sec < mTimespec.tv_sec) return false;
if (nowTime.mTimespec.tv_sec > mTimespec.tv_sec) return true;
if (nowTime.mTimespec.tv_nsec >= mTimespec.tv_nsec) return true;
if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
return false;
}
double Timeval::seconds() const
{
return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec);
return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
}
@@ -79,25 +72,24 @@ long Timeval::delta(const Timeval& other) const
{
// 2^31 milliseconds is just over 4 years.
int32_t deltaS = other.sec() - sec();
int32_t deltaNs = other.nsec() - nsec();
return 1000*deltaS + deltaNs/1000000;
int32_t deltaUs = other.usec() - usec();
return 1000*deltaS + deltaUs/1000;
}
ostream& operator<<(ostream& os, const Timeval& tv)
{
ios_base::fmtflags flags_backup = os.setf( ios::fixed, ios::floatfield );
os.setf( ios::fixed, ios::floatfield );
os << tv.seconds();
os.flags( flags_backup );
return os;
}
ostream& operator<<(ostream& os, const struct timespec& ts)
{
os << ts.tv_sec << "," << ts.tv_nsec/1000;
os << ts.tv_sec << "," << ts.tv_nsec;
return os;
}

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -44,12 +42,12 @@ class Timeval {
private:
struct timespec mTimespec;
struct timeval mTimeval;
public:
/** Set the value to current time. */
void now();
/** Set the value to gettimeofday. */
void now() { gettimeofday(&mTimeval,NULL); }
/** Set the value to gettimeofday plus an offset. */
void future(unsigned ms);
@@ -57,18 +55,16 @@ class Timeval {
//@{
Timeval(unsigned sec, unsigned usec)
{
mTimespec.tv_sec = sec;
mTimespec.tv_nsec = usec*1000;
mTimeval.tv_sec = sec;
mTimeval.tv_usec = usec;
}
Timeval(const struct timeval& wTimeval)
{
mTimespec.tv_sec = wTimeval.tv_sec;
mTimespec.tv_nsec = wTimeval.tv_sec*1000;
}
:mTimeval(wTimeval)
{}
/**
Create a Timespec offset into the future.
Create a Timeval offset into the future.
@param offset milliseconds
*/
Timeval(unsigned offset=0) { future(offset); }
@@ -80,11 +76,10 @@ class Timeval {
/** Return total seconds. */
double seconds() const;
uint32_t sec() const { return mTimespec.tv_sec; }
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
uint32_t nsec() const { return mTimespec.tv_nsec; }
uint32_t sec() const { return mTimeval.tv_sec; }
uint32_t usec() const { return mTimeval.tv_usec; }
/** Return difference from other (other-self), in ms. */
/** Return differnce from other (other-self), in ms. */
long delta(const Timeval& other) const;
/** Elapsed time in ms. */
@@ -93,11 +88,11 @@ class Timeval {
/** Remaining time in ms. */
long remaining() const { return -elapsed(); }
/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */
/** Return true if the time has passed, as per gettimeofday. */
bool passed() const;
/** Add a given number of minutes to the time. */
void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; }
void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; }
};

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Timeval.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
Timeval then(10000);
cout << then.elapsed() << endl;
while (!then.passed()) {
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
usleep(500000);
}
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
}

28
CommonLibs/URLEncode.cpp Normal file
View File

@@ -0,0 +1,28 @@
/* Copyright 2011, Range Networks, Inc. */
#include <URLEncode.h>
#include <string>
#include <string.h>
#include <ctype.h>
using namespace std;
//based on javascript encodeURIComponent()
string URLEncode(const string &c)
{
static const char *digits = "01234567890ABCDEF";
string retVal="";
for (size_t i=0; i<c.length(); i++)
{
const char ch = c[i];
if (isalnum(ch) || strchr("-_.!~'()",ch)) {
retVal += ch;
} else {
retVal += '%';
retVal += digits[(ch>>4) & 0x0f];
retVal += digits[ch & 0x0f];
}
}
return retVal;
}

30
CommonLibs/URLEncode.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright 2011 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# include <string>
std::string URLEncode(const std::string&);

View File

@@ -0,0 +1,17 @@
#include "URLEncode.h"
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
string test = string("Testing: !@#$%^&*() " __DATE__ " " __TIME__);
cout << test << endl;
cout << URLEncode(test) << endl;
}

View File

@@ -1,34 +1,211 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
#include <vector>
#include <string>
#include <sstream>
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
std::vector<std::string> comma_delimited_to_vector(const char* opt)
#include <unistd.h> // For usleep
#include <sys/time.h> // For gettimeofday
#include <stdio.h> // For vsnprintf
#include <ostream> // For ostream
#include <sstream> // For ostringstream
#include <string.h> // For strcpy
//#include "GSMCommon.h"
#include "Utils.h"
#include "MemoryLeak.h"
namespace Utils {
MemStats gMemStats;
int gMemLeakDebug = 0;
MemStats::MemStats()
{
std::string str = std::string(opt);
std::vector<std::string> result;
std::stringstream ss(str);
while( ss.good() )
{
std::string substr;
getline(ss, substr, ',');
result.push_back(substr);
}
return result;
memset(mMemNow,0,sizeof(mMemNow));
memset(mMemTotal,0,sizeof(mMemTotal));
memset(mMemName,0,sizeof(mMemName));
}
void MemStats::text(std::ostream &os)
{
os << "Structs current total:\n";
for (int i = 0; i < mMax; i++) {
os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n";
}
}
void MemStats::memChkNew(MemoryNames memIndex, const char *id)
{
/*std::cout << "new " #type "\n";*/
mMemNow[memIndex]++;
mMemTotal[memIndex]++;
mMemName[memIndex] = id;
}
void MemStats::memChkDel(MemoryNames memIndex, const char *id)
{
/*std::cout << "del " #type "\n";*/
mMemNow[memIndex]--;
if (mMemNow[memIndex] < 0) {
LOG(ERR) << "Memory underflow on type "<<id;
if (gMemLeakDebug) assert(0);
mMemNow[memIndex] += 100; // Prevent another message for a while.
}
}
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
{
return os << ss.str();
}
std::ostream &osprintf(std::ostream &os, const char *fmt, ...)
{
va_list ap;
char buf[300];
va_start(ap,fmt);
int n = vsnprintf(buf,300,fmt,ap);
va_end(ap);
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
os << buf;
return os;
}
std::string format(const char *fmt, ...)
{
va_list ap;
char buf[300];
va_start(ap,fmt);
int n = vsnprintf(buf,300,fmt,ap);
va_end(ap);
if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
return std::string(buf);
}
// Return time in seconds with high resolution.
// Note: In the past I found this to be a surprisingly expensive system call in linux.
double timef()
{
struct timeval tv;
gettimeofday(&tv,NULL);
return tv.tv_usec / 1000000.0 + tv.tv_sec;
}
const std::string timestr()
{
struct timeval tv;
struct tm tm;
gettimeofday(&tv,NULL);
localtime_r(&tv.tv_sec,&tm);
unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
}
// High resolution sleep for the specified time.
// Return FALSE if time is already past.
void sleepf(double howlong)
{
if (howlong <= 0.00001) return; // Less than 10 usecs, forget it.
usleep((useconds_t) (1000000.0 * howlong));
}
//bool sleepuntil(double untilwhen)
//{
//double now = timef();
//double howlong = untilwhen - now; // Fractional time in seconds.
// We are not worrying about overflow because all times should be in the near future.
//if (howlong <= 0.00001) return false; // Less than 10 usecs, forget it.
//sleepf(sleeptime);
//}
std::string Text2Str::str() const
{
std::ostringstream ss;
text(ss);
return ss.str();
}
std::ostream& operator<<(std::ostream& os, const Text2Str *val)
{
std::ostringstream ss;
if (val) {
val->text(ss);
os << ss.str();
} else {
os << "(null)";
}
return os;
}
// Greatest Common Denominator.
// This is by Doug Brown.
int gcd(int x, int y)
{
if (x > y) {
return x % y == 0 ? y : gcd(y, x % y);
} else {
return y % x == 0 ? x : gcd(x, y % x);
}
}
// Split a C string into an argc,argv array in place; the input string is modified.
// Returns argc, and places results in argv, up to maxargc elements.
// The final argv receives the rest of the input string from maxargc on,
// even if it contains additional splitchars.
// The correct idiom for use is to make a copy of your string, like this:
// char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str());
// char *argv[2];
// int argc = cstrSplit(copy,argv,2,NULL);
// If you want to detect the error of too many arguments, add 1 to argv, like this:
// char *argv[3];
// int argc = cstrSplit(copy,argv,3,NULL);
// if (argc == 3) { error("too many arguments"; }
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars)
{
if (splitchars == NULL) { splitchars = " \t\r\n"; } // Default is any space.
int argc = 0;
while (argc < maxargc) {
while (*in && strchr(splitchars,*in)) {in++;} // scan past any splitchars
if (! *in) return argc; // return if finished.
pargv[argc++] = in; // save ptr to start of arg.
in = strpbrk(in,splitchars); // go to end of arg.
if (!in) return argc; // return if finished.
*in++ = 0; // zero terminate this arg.
}
return argc;
}
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat) { stat.text(os); return os; }
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat) { stat.text(os); return os; }
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat) { stat.text(os); return os; }
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat) { stat.text(os); return os; }
std::string replaceAll(const std::string input, const std::string search, const std::string replace)
{
std::string output = input;
int index = 0;
while (true) {
index = output.find(search, index);
if (index == std::string::npos) {
break;
}
output.replace(index, replace.length(), replace);
index += replace.length();
}
return output;
}
};

View File

@@ -1,22 +1,148 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
#pragma once
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <vector>
#ifndef GPRSUTILS_H
#define GPRSUTILS_H
#include <stdint.h>
#include <stdarg.h>
#include <string>
#include <string.h>
#include <math.h> // for sqrtf
#include "Logger.h"
std::vector<std::string> comma_delimited_to_vector(const char* opt);
namespace Utils {
extern double timef(); // high resolution time
extern const std::string timestr(); // A timestamp to print in messages.
extern void sleepf(double howlong); // high resolution sleep
extern int gcd(int x, int y);
// It is irritating to create a string just to interface to the brain-damaged
// C++ stream class, but this is only used for debug messages.
std::string format(const char *fmt, ...) __attribute__((format (printf,1,2)));
int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL);
// For classes with a text() function, provide a function to return a String,
// and also a standard << stream function that takes a pointer to the object.
// We dont provide the function that takes a reference to the object
// because it is too highly overloaded and generally doesnt work.
class Text2Str {
public:
virtual void text(std::ostream &os) const = 0;
std::string str() const;
};
std::ostream& operator<<(std::ostream& os, const Text2Str *val);
#if 0
// Generic Activity Timer. Lots of controls to make everybody happy.
class ATimer {
double mStart;
//bool mActive;
double mLimitTime;
public:
ATimer() : mStart(0), mLimitTime(0) { }
ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { }
void start() { mStart=timef(); }
void stop() { mStart=0; }
bool active() { return !!mStart; }
double elapsed() { return timef() - mStart; }
bool expired() { return elapsed() > mLimitTime; }
};
#endif
struct BitSet {
unsigned mBits;
void setBit(unsigned whichbit) { mBits |= 1<<whichbit; }
void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); }
unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); }
bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); }
unsigned bits() const { return mBits; }
operator int(void) const { return mBits; }
BitSet() { mBits = 0; }
};
// Store current, min, max and compute running average and standard deviation.
template<class Type> struct Statistic {
Type mCurrent, mMin, mMax; // min,max optional initialization so you can print before adding any values.
unsigned mCnt;
double mSum;
//double mSum2; // sum of squares.
// (Type) cast needed in case Type is an enum, stupid language.
Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {}
// Set the current value and add a statisical point.
void addPoint(Type val) {
mCurrent = val;
if (mCnt == 0 || val < mMin) {mMin = val;}
if (mCnt == 0 || val > mMax) {mMax = val;}
mCnt++;
mSum += val;
//mSum2 += val * val;
}
Type getCurrent() const { // Return current value.
return mCnt ? mCurrent : 0;
}
double getAvg() const { // Return average.
return mCnt==0 ? 0 : mSum/mCnt;
};
//float getSD() const { // Return standard deviation. Use low precision square root function.
// return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt;
//}
void text(std::ostream &os) const { // Print everything in parens.
os << "("<<mCurrent;
if (mMin != mMax) { // Not point in printing all this stuff if min == max.
os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg());
if (mCnt <= 999999) {
os <<LOGVAR2("N",mCnt);
} else { // Shorten this up:
char buf[10], *ep;
sprintf(buf,"%.3g",round(mCnt));
if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); }
os << LOGVAR2("N",buf);
}
// os<<LOGVAR2("sd",getSD()) standard deviation not interesting
}
os << ")";
// " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD());
}
// Not sure if this works:
//std::string statStr() const {
// return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD());
//}
};
// This I/O mechanism is so dumb:
std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat);
std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat);
std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat);
std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat);
// Yes, they botched and left this out:
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3)));
std::string replaceAll(const std::string input, const std::string search, const std::string replace);
}; // namespace
using namespace Utils;
#endif

View File

@@ -2,8 +2,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -34,19 +32,11 @@
#include <string.h>
#include <iostream>
#include <assert.h>
#include <stdlib.h>
#ifndef __OPTIMIZE__
#define assert_no_opt(x) assert(x)
#else
#define assert_no_opt(x)
#endif
// We can't use Logger.h in this file...
// We cant use Logger.h in this file...
extern int gVectorDebug;
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
typedef void (*vector_free_func)(void* wData);
typedef void *(*vector_alloc_func)(size_t newSize);
/**
A simplified Vector template with aliases.
@@ -70,8 +60,6 @@ template <class T> class Vector {
T* mData; ///< allocated data block, if any
T* mStart; ///< start of useful data
T* mEnd; ///< end of useful data + 1
vector_alloc_func mAllocFunc; ///< function used to alloc new mData during resize.
vector_free_func mFreeFunc; ///< function used to free mData.
public:
@@ -86,8 +74,8 @@ template <class T> class Vector {
/** Return the size of the Vector. */
size_t size() const
{
assert_no_opt(mStart>=mData);
assert_no_opt(mEnd>=mStart);
assert(mStart>=mData);
assert(mEnd>=mStart);
return mEnd - mStart;
}
@@ -97,30 +85,13 @@ template <class T> class Vector {
/** Change the size of the Vector, discarding content. */
void resize(size_t newSize)
{
if (mData!=NULL) {
if (mFreeFunc)
mFreeFunc(mData);
else
delete[] mData;
}
if (mData!=NULL) delete[] mData;
if (newSize==0) mData=NULL;
else {
if (mAllocFunc)
mData = (T*) mAllocFunc(newSize);
else
mData = new T[newSize];
}
else mData = new T[newSize];
mStart = mData;
mEnd = mStart + newSize;
}
/** Reduce addressable size of the Vector, keeping content. */
void shrink(size_t newSize)
{
assert_no_opt(newSize <= mEnd - mStart);
mEnd = mStart + newSize;
}
/** Release memory and clear pointers. */
void clear() { resize(0); }
@@ -129,7 +100,7 @@ template <class T> class Vector {
void clone(const Vector<T>& other)
{
resize(other.size());
other.copyTo(*this);
memcpy(mData,other.mStart,other.bytes());
}
@@ -138,31 +109,29 @@ template <class T> class Vector {
//@{
/** Build an empty Vector of a given size. */
Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
{ resize(wSize); }
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
/** Build a Vector by moving another. */
Vector(Vector<T>&& other)
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc)
/** Build a Vector by shifting the data block. */
Vector(Vector<T>& other)
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
{ other.mData=NULL; }
/** Build a Vector by copying another. */
Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); }
Vector(const Vector<T>& other):mData(NULL) { clone(other); }
/** Build a Vector with explicit values. */
Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
Vector(T* wData, T* wStart, T* wEnd)
:mData(wData),mStart(wStart),mEnd(wEnd)
{ }
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
Vector(T* wStart, size_t span, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
Vector(T* wStart, size_t span)
:mData(NULL),mStart(wStart),mEnd(wStart+span)
{ }
/** Build a Vector by concatenation. */
Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)
:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)
Vector(const Vector<T>& other1, const Vector<T>& other2)
:mData(NULL)
{
resize(other1.size()+other2.size());
memcpy(mStart, other1.mStart, other1.bytes());
@@ -186,8 +155,6 @@ template <class T> class Vector {
mData=other.mData;
mStart=other.mStart;
mEnd=other.mEnd;
mAllocFunc=other.mAllocFunc;
mFreeFunc=other.mFreeFunc;
other.mData=NULL;
}
@@ -204,7 +171,7 @@ template <class T> class Vector {
{
T* wStart = mStart + start;
T* wEnd = wStart + span;
assert_no_opt(wEnd<=mEnd);
assert(wEnd<=mEnd);
return Vector<T>(NULL,wStart,wEnd);
}
@@ -213,7 +180,7 @@ template <class T> class Vector {
{
T* wStart = mStart + start;
T* wEnd = wStart + span;
assert_no_opt(wEnd<=mEnd);
assert(wEnd<=mEnd);
return Vector<T>(NULL,wStart,wEnd);
}
@@ -230,15 +197,10 @@ template <class T> class Vector {
*/
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
{
unsigned int i;
T* dst = other.mStart + start;
T* src = mStart;
assert_no_opt(dst+span<=other.mEnd);
assert_no_opt(mStart+span<=mEnd);
for (i = 0; i < span; i++, src++, dst++)
*dst = *src;
/*TODO if not non-trivially copiable type class, optimize:
memcpy(dst,mStart,span*sizeof(T)); */
T* base = other.mStart + start;
assert(base+span<=other.mEnd);
assert(mStart+span<=mEnd);
memcpy(base,mStart,span*sizeof(T));
}
/** Copy all of this Vector to a segment of another Vector. */
@@ -255,26 +217,11 @@ template <class T> class Vector {
void segmentCopyTo(Vector<T>& other, size_t start, size_t span) const
{
const T* base = mStart + start;
assert_no_opt(base+span<=mEnd);
assert_no_opt(other.mStart+span<=other.mEnd);
assert(base+span<=mEnd);
assert(other.mStart+span<=other.mEnd);
memcpy(other.mStart,base,span*sizeof(T));
}
/**
Move (copy) a segment of this vector into a different position in the vector
@param from Start point from which to copy.
@param to Start point to which to copy.
@param span The number of elements to copy.
*/
void segmentMove(size_t from, size_t to, size_t span)
{
const T* baseFrom = mStart + from;
T* baseTo = mStart + to;
assert_no_opt(baseFrom+span<=mEnd);
assert_no_opt(baseTo+span<=mEnd);
memmove(baseTo,baseFrom,span*sizeof(T));
}
void fill(const T& val)
{
T* dp=mStart;
@@ -285,7 +232,7 @@ template <class T> class Vector {
{
T* dp=mStart+start;
T* end=dp+length;
assert_no_opt(end<=mEnd);
assert(end<=mEnd);
while (dp<end) *dp++=val;
}
@@ -297,13 +244,13 @@ template <class T> class Vector {
T& operator[](size_t index)
{
assert_no_opt(mStart+index<mEnd);
assert(mStart+index<mEnd);
return mStart[index];
}
const T& operator[](size_t index) const
{
assert_no_opt(mStart+index<mEnd);
assert(mStart+index<mEnd);
return mStart[index];
}
@@ -313,7 +260,7 @@ template <class T> class Vector {
T* end() { return mEnd; }
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
//@}
};

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -29,6 +28,10 @@
#include "Vector.h"
#include <iostream>
// We must have a gConfig now to include Vector.
#include "Configuration.h"
ConfigurationTable gConfig;
using namespace std;
typedef Vector<int> TestVector;

View File

@@ -1,71 +0,0 @@
#pragma once
/*
* This file contains structures used by both VTY (C, dir CommonLibs) and
* osmo-trx (CXX, dir Transceiver52)
*/
#include <stdbool.h>
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_NORM_RAND,
FILLER_EDGE_RAND,
FILLER_ACCESS_RAND,
};
enum ReferenceType {
REF_INTERNAL,
REF_EXTERNAL,
REF_GPS,
};
/* Maximum number of physical RF channels */
#define TRX_CHAN_MAX 8
struct trx_ctx;
struct trx_chan {
struct trx_ctx *trx; /* backpointer */
unsigned int idx; /* channel index */
char *rx_path;
char *tx_path;
};
struct trx_cfg {
char *bind_addr;
char *remote_addr;
char *dev_args;
unsigned int base_port;
unsigned int tx_sps;
unsigned int rx_sps;
unsigned int rtsc;
unsigned int rach_delay;
enum ReferenceType clock_ref;
enum FillerType filler;
bool multi_arfcn;
double offset;
double freq_offset_khz;
double rssi_offset;
int ul_fn_offset;
bool force_rssi_offset; /* Force value set in VTY? */
bool swap_channels;
bool ext_rach;
bool egprs;
unsigned int sched_rr;
unsigned int stack_size;
unsigned int num_chans;
struct trx_chan chans[TRX_CHAN_MAX];
struct {
bool ul_freq_override;
bool dl_freq_override;
bool ul_gain_override;
bool dl_gain_override;
double ul_freq;
double dl_freq;
double ul_gain;
double dl_gain;
} overrides;
bool use_va;
};

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include "debug.h"
/* default categories */
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.description = "Main generic category",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXCLK] = {
.name = "DTRXCLK",
.description = "TRX Master Clock",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXCTRL] = {
.name = "DTRXCTRL",
.description = "TRX CTRL interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXDDL] = {
.name = "DTRXDDL",
.description = "TRX Data interface Downlink",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXDUL] = {
.name = "DTRXDUL",
.description = "TRX CTRL interface Uplink",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DDEV] = {
.name = "DDEV",
.description = "Device/Driver specific code",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DDEVDRV] = {
.name = "DDEVDRV",
.description = "Logging from external device driver library implementing lower level specifics",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DCTR] = {
.name = "DCTR",
.description = "Rate counter related logging",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};

View File

@@ -1,23 +0,0 @@
#pragma once
#include <stdbool.h>
#include <osmocom/core/logging.h>
extern const struct log_info log_info;
/* Debug Areas of the code */
enum {
DMAIN,
DTRXCLK,
DTRXCTRL,
DTRXDDL,
DTRXDUL,
DDEV,
DDEVDRV,
DCTR,
};
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
LOGP(category, level, "[chan=%zu] " fmt, chan, ##args); \
} while(0)

View File

@@ -1,71 +0,0 @@
/* Generic signalling/notification infrastructure */
/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/signal.h>
/* Signalling subsystems */
enum signal_subsystems {
SS_MAIN,
SS_DEVICE,
};
/* SS_MAIN signals */
enum SS_MAIN {
S_MAIN_STOP_REQUIRED, /* TRX fatal error, it should be stopped */
};
/* SS_DEVICE signals */
enum SS_DEVICE {
/* Device internal counters changed. Counters are provided as cb data
(struct device_counters). Must be sent with PTHREAD_CANCEL_DISABLE
to avoid deadlocks in case osmo-trx process is asked to exit. */
S_DEVICE_COUNTER_CHANGE,
S_TRX_COUNTER_CHANGE, /* same, but for Transceiver class */
};
/* signal cb for signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> */
struct device_counters {
size_t chan;
unsigned int rx_overruns;
unsigned int tx_underruns;
unsigned int rx_dropped_events;
unsigned int rx_dropped_samples;
unsigned int tx_dropped_events;
unsigned int tx_dropped_samples;
};
/* signal cb for signal <SS_DEVICE,S_TRX_COUNTER_CHANGE> */
struct trx_counters {
size_t chan;
unsigned int tx_stale_bursts;
unsigned int tx_unavailable_bursts;
unsigned int tx_trxd_fn_repeated;
unsigned int tx_trxd_fn_outoforder;
unsigned int tx_trxd_fn_skipped;
unsigned int rx_empty_burst;
unsigned int rx_clipping;
unsigned int rx_no_burst_detected;
};

154
CommonLibs/sqlite3util.cpp Normal file
View File

@@ -0,0 +1,154 @@
/*
* Copyright 2010 Kestrel Signal Processing, Inc.
* All rights reserved.
*/
#include "sqlite3.h"
#include "sqlite3util.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
// Wrappers to sqlite operations.
// These will eventually get moved to commonlibs.
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query)
{
int src = SQLITE_BUSY;
while (src==SQLITE_BUSY) {
src = sqlite3_prepare_v2(DB,query,strlen(query),stmt,NULL);
if (src==SQLITE_BUSY) {
usleep(100000);
}
}
if (src) {
fprintf(stderr,"sqlite3_prepare_v2 failed for \"%s\": %s\n",query,sqlite3_errmsg(DB));
sqlite3_finalize(*stmt);
}
return src;
}
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt)
{
int src = SQLITE_BUSY;
while (src==SQLITE_BUSY) {
src = sqlite3_step(stmt);
if (src==SQLITE_BUSY) {
usleep(100000);
}
}
if ((src!=SQLITE_DONE) && (src!=SQLITE_ROW)) {
fprintf(stderr,"sqlite3_run_query failed: %s: %s\n", sqlite3_sql(stmt), sqlite3_errmsg(DB));
}
return src;
}
bool sqlite3_exists(sqlite3* DB, const char *tableName,
const char* keyName, const char* keyData)
{
size_t stringSize = 100 + strlen(tableName) + strlen(keyName) + strlen(keyData);
char query[stringSize];
sprintf(query,"SELECT * FROM %s WHERE %s == \"%s\"",tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
sqlite3_finalize(stmt);
// Anything there?
return (src == SQLITE_ROW);
}
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
const char* keyName, const char* keyData,
const char* valueName, unsigned &valueData)
{
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
char query[stringSize];
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
bool retVal = false;
if (src == SQLITE_ROW) {
valueData = (unsigned)sqlite3_column_int64(stmt,0);
retVal = true;
}
sqlite3_finalize(stmt);
return retVal;
}
// This function returns an allocated string that must be free'd by the caller.
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, const char* keyData,
const char* valueName, char* &valueData)
{
valueData=NULL;
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + strlen(keyData);
char query[stringSize];
sprintf(query,"SELECT %s FROM %s WHERE %s == \"%s\"",valueName,tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
bool retVal = false;
if (src == SQLITE_ROW) {
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
if (ptr) valueData = strdup(ptr);
retVal = true;
}
sqlite3_finalize(stmt);
return retVal;
}
// This function returns an allocated string that must be free'd by tha caller.
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, unsigned keyData,
const char* valueName, char* &valueData)
{
valueData=NULL;
size_t stringSize = 100 + strlen(valueName) + strlen(tableName) + strlen(keyName) + 20;
char query[stringSize];
sprintf(query,"SELECT %s FROM %s WHERE %s == %u",valueName,tableName,keyName,keyData);
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Read the result.
int src = sqlite3_run_query(DB,stmt);
bool retVal = false;
if (src == SQLITE_ROW) {
const char* ptr = (const char*)sqlite3_column_text(stmt,0);
if (ptr) valueData = strdup(ptr);
retVal = true;
}
sqlite3_finalize(stmt);
return retVal;
}
bool sqlite3_command(sqlite3* DB, const char* query)
{
// Prepare the statement.
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(DB,&stmt,query)) return false;
// Run the query.
int src = sqlite3_run_query(DB,stmt);
sqlite3_finalize(stmt);
return src==SQLITE_DONE;
}

29
CommonLibs/sqlite3util.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef SQLITE3UTIL_H
#define SQLITE3UTIL_H
#include <sqlite3.h>
int sqlite3_prepare_statement(sqlite3* DB, sqlite3_stmt **stmt, const char* query);
int sqlite3_run_query(sqlite3* DB, sqlite3_stmt *stmt);
bool sqlite3_single_lookup(sqlite3* DB, const char *tableName,
const char* keyName, const char* keyData,
const char* valueName, unsigned &valueData);
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, const char* keyData,
const char* valueName, char* &valueData);
// This function returns an allocated string that must be free'd by the caller.
bool sqlite3_single_lookup(sqlite3* DB, const char* tableName,
const char* keyName, unsigned keyData,
const char* valueName, char* &valueData);
bool sqlite3_exists(sqlite3* DB, const char* tableName,
const char* keyName, const char* keyData);
/** Run a query, ignoring the result; return true on success. */
bool sqlite3_command(sqlite3* DB, const char* query);
#endif

View File

@@ -1,409 +0,0 @@
/*
* Copyright (C) 2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
/*
* rate_ctr API uses several osmocom select loop features, and as a result,
* calls to it must be done through the main thread (the one running the osmocom
* loop in osmo-trx).
* Since read/write from/to SDR is done in separate threads (even read and write
* each use a different thread), we must use some sort of message passing system
* between main thread feeding rate_ctr structures and the Rx/Tx threads
* generating the events.
* The idea is that upon read/write issues, lower layers (SDR APIs) provide us with
* underrun/overrun/droppedPackets information, and in that case we pass that up
* the stack through signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> with signal_cb
* being a pointer to a "struct device_counters" structure, which contains
* device (implementation agnostic) statful counters for different kind of
* statistics.
* That signal is processed here in device_sig_cb, where a copy of the "struct
* device_counters" structure is held and the main thread is instructed through
* a timerfd to update rate_ctr APIs against this copy. All this is done inside
* a mutex to avoid different race conditions (between Rx andTx threads, and
* between Rx/Tx and main thread). For the same reason, callers of signal
* <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> (device_sig_cb), that is Rx/Tx threads,
* must do so with PTHREAD_CANCEL_DISABLE, in order to avoid possible deadlocks
* in case the main thread decides to cancel other threads due to a shutdown
* operation (fi SIGKILL received)
*/
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <arpa/inet.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/timer.h>
#include "osmo_signal.h"
#include "trx_vty.h"
#include "trx_rate_ctr.h"
}
#include "Threads.h"
#include "Logger.h"
/* Used in dev_ctrs_pending, when set it means that channel slot contains unused
(non-pending) counter data */
#define PENDING_CHAN_NONE SIZE_MAX
static void *trx_rate_ctr_ctx;
static struct rate_ctr_group** rate_ctrs;
static struct device_counters* dev_ctrs_pending;
static struct trx_counters* trx_ctrs_pending;
static size_t chan_len;
static struct osmo_fd dev_rate_ctr_timerfd;
static struct osmo_fd trx_rate_ctr_timerfd;
static Mutex dev_rate_ctr_mutex;
static Mutex trx_rate_ctr_mutex;
struct osmo_timer_list threshold_timer;
static LLIST_HEAD(threshold_list);
static unsigned int threshold_timer_sched_secs;
static bool threshold_initied;
const struct value_string rate_ctr_intv[] = {
{ RATE_CTR_INTV_SEC, "per-second" },
{ RATE_CTR_INTV_MIN, "per-minute" },
{ RATE_CTR_INTV_HOUR, "per-hour" },
{ RATE_CTR_INTV_DAY, "per-day" },
{ 0, NULL }
};
const struct value_string trx_chan_ctr_names[] = {
{ TRX_CTR_DEV_RX_OVERRUNS, "rx_overruns" },
{ TRX_CTR_DEV_TX_UNDERRUNS, "tx_underruns" },
{ TRX_CTR_DEV_RX_DROP_EV, "rx_drop_events" },
{ TRX_CTR_DEV_RX_DROP_SMPL, "rx_drop_samples" },
{ TRX_CTR_DEV_TX_DROP_EV, "tx_drop_events" },
{ TRX_CTR_DEV_TX_DROP_SMPL, "tx_drop_samples" },
{ TRX_CTR_TRX_TX_STALE_BURSTS, "tx_stale_bursts" },
{ TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS, "tx_unavailable_bursts" },
{ TRX_CTR_TRX_TRXD_FN_REPEATED, "tx_trxd_fn_repeated" },
{ TRX_CTR_TRX_TRXD_FN_OUTOFORDER, "tx_trxd_fn_outoforder" },
{ TRX_CTR_TRX_TRXD_FN_SKIPPED, "tx_trxd_fn_skipped" },
{ TRX_CTR_TRX_RX_EMPTY_BURST, "rx_empty_burst" },
{ TRX_CTR_TRX_RX_CLIPPING, "rx_clipping" },
{ TRX_CTR_TRX_RX_NO_BURST_DETECTED, "rx_no_burst_detected" },
{ 0, NULL }
};
static const struct rate_ctr_desc trx_chan_ctr_desc[] = {
[TRX_CTR_DEV_RX_OVERRUNS] = { "device:rx_overruns", "Number of Rx overruns in FIFO queue" },
[TRX_CTR_DEV_TX_UNDERRUNS] = { "device:tx_underruns", "Number of Tx underruns in FIFO queue" },
[TRX_CTR_DEV_RX_DROP_EV] = { "device:rx_drop_events", "Number of times Rx samples were dropped by HW" },
[TRX_CTR_DEV_RX_DROP_SMPL] = { "device:rx_drop_samples", "Number of Rx samples dropped by HW" },
[TRX_CTR_DEV_TX_DROP_EV] = { "device:tx_drop_events", "Number of times Tx samples were dropped by HW" },
[TRX_CTR_DEV_TX_DROP_SMPL] = { "device:tx_drop_samples", "Number of Tx samples dropped by HW" },
[TRX_CTR_TRX_TX_STALE_BURSTS] = { "trx:tx_stale_bursts", "Number of Tx burts dropped by TRX due to arriving too late" },
[TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS] = { "trx:tx_unavailable_bursts","Number of Tx burts unavailable (not enqueued) at the time they should be transmitted" },
[TRX_CTR_TRX_TRXD_FN_REPEATED] = { "trx:tx_trxd_fn_repeated", "Number of Tx burts received from TRXD with repeated FN" },
[TRX_CTR_TRX_TRXD_FN_OUTOFORDER] = { "trx:tx_trxd_fn_outoforder","Number of Tx burts received from TRXD with a past FN" },
[TRX_CTR_TRX_TRXD_FN_SKIPPED] = { "trx:tx_trxd_fn_skipped", "Number of Tx burts potentially skipped due to FN jumps" },
[TRX_CTR_TRX_RX_EMPTY_BURST] = { "trx:rx_empty_burst", "Number of Rx bursts empty" },
[TRX_CTR_TRX_RX_CLIPPING] = { "trx:rx_clipping", "Number of Rx bursts discarded due to clipping" },
[TRX_CTR_TRX_RX_NO_BURST_DETECTED] = { "trx:rx_no_burst_detected", "Number of Rx burts discarded due to burst detection error" },
};
static const struct rate_ctr_group_desc trx_chan_ctr_group_desc = {
.group_name_prefix = "trx:chan",
.group_description = "osmo-trx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(trx_chan_ctr_desc),
.ctr_desc = trx_chan_ctr_desc,
};
static int dev_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
size_t chan;
struct rate_ctr *ctr;
LOGC(DCTR, INFO) << "Main thread is updating Device counters";
dev_rate_ctr_mutex.lock();
for (chan = 0; chan < chan_len; chan++) {
if (dev_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
continue;
LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_OVERRUNS);
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_overruns - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_UNDERRUNS);
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_underruns - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_EV);
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_events - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_SMPL);
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_samples - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_EV);
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_events - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_SMPL);
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_samples - ctr->current);
/* Mark as done */
dev_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
}
if (osmo_timerfd_disable(&dev_rate_ctr_timerfd) < 0)
LOGC(DCTR, ERROR) << "Failed to disable timerfd";
dev_rate_ctr_mutex.unlock();
return 0;
}
static int trx_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
size_t chan;
struct rate_ctr *ctr;
LOGC(DCTR, INFO) << "Main thread is updating Transceiver counters";
trx_rate_ctr_mutex.lock();
for (chan = 0; chan < chan_len; chan++) {
if (trx_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
continue;
LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_STALE_BURSTS);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_stale_bursts - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_unavailable_bursts - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_REPEATED);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_repeated - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_OUTOFORDER);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_outoforder - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_SKIPPED);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_skipped - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_EMPTY_BURST);
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_empty_burst - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_CLIPPING);
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_clipping - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_NO_BURST_DETECTED);
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_no_burst_detected - ctr->current);
/* Mark as done */
trx_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
}
if (osmo_timerfd_disable(&trx_rate_ctr_timerfd) < 0)
LOGC(DCTR, ERROR) << "Failed to disable timerfd";
trx_rate_ctr_mutex.unlock();
return 0;
}
/* Callback function to be called every time we receive a signal from DEVICE */
static int device_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct device_counters *dev_ctr;
struct trx_counters *trx_ctr;
/* Delay sched around 20 ms, in case we receive several calls from several
* channels batched */
struct timespec next_sched = {.tv_sec = 0, .tv_nsec = 20*1000*1000};
/* no automatic re-trigger */
struct timespec intv_sched = {.tv_sec = 0, .tv_nsec = 0};
char err_buf[256];
switch (signal) {
case S_DEVICE_COUNTER_CHANGE:
dev_ctr = (struct device_counters *)signal_data;
LOGCHAN(dev_ctr->chan, DCTR, INFO) << "Received counter change from radioDevice";
dev_rate_ctr_mutex.lock();
dev_ctrs_pending[dev_ctr->chan] = *dev_ctr;
if (osmo_timerfd_schedule(&dev_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno
<< " = "<< strerror_r(errno, err_buf, sizeof(err_buf));
}
dev_rate_ctr_mutex.unlock();
break;
case S_TRX_COUNTER_CHANGE:
trx_ctr = (struct trx_counters *)signal_data;
LOGCHAN(trx_ctr->chan, DCTR, INFO) << "Received counter change from Transceiver";
trx_rate_ctr_mutex.lock();
trx_ctrs_pending[trx_ctr->chan] = *trx_ctr;
if (osmo_timerfd_schedule(&trx_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno
<< " = "<< strerror_r(errno, err_buf, sizeof(err_buf));
}
trx_rate_ctr_mutex.unlock();
break;
default:
break;
}
return 0;
}
/************************************
* ctr_threshold APIs
************************************/
static const char* ctr_threshold_2_vty_str(struct ctr_threshold *ctr)
{
static char buf[256];
int rc = 0;
rc += snprintf(buf, sizeof(buf), "ctr-error-threshold %s", get_value_string(trx_chan_ctr_names, ctr->ctr_id));
rc += snprintf(buf + rc, sizeof(buf) - rc, " %d %s", ctr->val, get_value_string(rate_ctr_intv, ctr->intv));
return buf;
}
static void threshold_timer_cb(void *data)
{
struct ctr_threshold *ctr_thr;
struct rate_ctr *rate_ctr;
size_t chan;
LOGC(DCTR, DEBUG) << "threshold_timer_cb fired!";
llist_for_each_entry(ctr_thr, &threshold_list, list) {
for (chan = 0; chan < chan_len; chan++) {
rate_ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], ctr_thr->ctr_id);
LOGCHAN(chan, DCTR, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr)
<< " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
if (rate_ctr->intv[ctr_thr->intv].rate >= ctr_thr->val) {
LOGCHAN(chan, DCTR, FATAL) << "threshold reached, stopping! " << ctr_threshold_2_vty_str(ctr_thr)
<< " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
return;
}
}
}
osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
}
static size_t ctr_threshold_2_seconds(struct ctr_threshold *ctr)
{
size_t mult = 0;
switch (ctr->intv) {
case RATE_CTR_INTV_SEC:
mult = 1;
break;
case RATE_CTR_INTV_MIN:
mult = 60;
break;
case RATE_CTR_INTV_HOUR:
mult = 60*60;
break;
case RATE_CTR_INTV_DAY:
mult = 60*60*24;
break;
default:
OSMO_ASSERT(false);
}
return mult;
}
static void threshold_timer_update_intv() {
struct ctr_threshold *ctr, *min_ctr;
size_t secs, min_secs;
/* Avoid scheduling timer until itself and other structures are prepared
by trx_rate_ctr_init */
if (!threshold_initied)
return;
if (llist_empty(&threshold_list)) {
osmo_timer_del(&threshold_timer);
return;
}
min_ctr = llist_first_entry(&threshold_list, struct ctr_threshold, list);
min_secs = ctr_threshold_2_seconds(min_ctr);
llist_for_each_entry(ctr, &threshold_list, list) {
secs = ctr_threshold_2_seconds(ctr);
if (min_secs > secs)
min_secs = secs;
}
threshold_timer_sched_secs = OSMO_MAX((int)(min_secs / 2 - 1), 1);
LOGC(DCTR, INFO) << "New ctr-error-threshold check interval: "
<< threshold_timer_sched_secs << " seconds";
osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
}
/* Init rate_ctr subsystem. Expected to be called during process start by main thread before VTY is ready */
void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx)
{
size_t i;
trx_rate_ctr_ctx = ctx;
chan_len = trx_ctx->cfg.num_chans;
dev_ctrs_pending = (struct device_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct device_counters));
trx_ctrs_pending = (struct trx_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct trx_counters));
rate_ctrs = (struct rate_ctr_group**) talloc_zero_size(ctx, chan_len * sizeof(struct rate_ctr_group*));
for (i = 0; i < chan_len; i++) {
dev_ctrs_pending[i].chan = PENDING_CHAN_NONE;
trx_ctrs_pending[i].chan = PENDING_CHAN_NONE;
rate_ctrs[i] = rate_ctr_group_alloc(ctx, &trx_chan_ctr_group_desc, i);
if (!rate_ctrs[i]) {
LOGCHAN(i, DCTR, ERROR) << "Failed to allocate rate ctr";
exit(1);
}
}
dev_rate_ctr_timerfd.fd = -1;
if (osmo_timerfd_setup(&dev_rate_ctr_timerfd, dev_rate_ctr_timerfd_cb, NULL) < 0) {
LOGC(DCTR, ERROR) << "Failed to setup timerfd";
exit(1);
}
trx_rate_ctr_timerfd.fd = -1;
if (osmo_timerfd_setup(&trx_rate_ctr_timerfd, trx_rate_ctr_timerfd_cb, NULL) < 0) {
LOGC(DCTR, ERROR) << "Failed to setup timerfd";
exit(1);
}
osmo_signal_register_handler(SS_DEVICE, device_sig_cb, NULL);
/* Now set up threshold checks */
threshold_initied = true;
osmo_timer_setup(&threshold_timer, threshold_timer_cb, NULL);
threshold_timer_update_intv();
}
void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr)
{
struct ctr_threshold *new_ctr;
new_ctr = talloc_zero(trx_rate_ctr_ctx, struct ctr_threshold);
*new_ctr = *ctr;
LOGC(DCTR, NOTICE) << "Adding new threshold check: " << ctr_threshold_2_vty_str(new_ctr);
llist_add(&new_ctr->list, &threshold_list);
threshold_timer_update_intv();
}
int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr)
{
struct ctr_threshold *ctr;
llist_for_each_entry(ctr, &threshold_list, list) {
if (ctr->intv != del_ctr->intv ||
ctr->ctr_id != del_ctr->ctr_id ||
ctr->val != del_ctr->val)
continue;
LOGC(DCTR, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr);
llist_del(&ctr->list);
talloc_free(ctr);
threshold_timer_update_intv();
return 0;
}
return -1;
}
void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix)
{
struct ctr_threshold *ctr;
llist_for_each_entry(ctr, &threshold_list, list) {
vty_out(vty, "%s%s%s", indent_prefix, ctr_threshold_2_vty_str(ctr), VTY_NEWLINE);
}
}

View File

@@ -1,38 +0,0 @@
#pragma once
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/command.h>
enum TrxCtr {
TRX_CTR_DEV_RX_OVERRUNS,
TRX_CTR_DEV_TX_UNDERRUNS,
TRX_CTR_DEV_RX_DROP_EV,
TRX_CTR_DEV_RX_DROP_SMPL,
TRX_CTR_DEV_TX_DROP_EV,
TRX_CTR_DEV_TX_DROP_SMPL,
TRX_CTR_TRX_TX_STALE_BURSTS,
TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS,
TRX_CTR_TRX_TRXD_FN_REPEATED,
TRX_CTR_TRX_TRXD_FN_OUTOFORDER,
TRX_CTR_TRX_TRXD_FN_SKIPPED,
TRX_CTR_TRX_RX_EMPTY_BURST,
TRX_CTR_TRX_RX_CLIPPING,
TRX_CTR_TRX_RX_NO_BURST_DETECTED,
};
struct ctr_threshold {
/*! Linked list of all counter groups in the system */
struct llist_head list;
enum rate_ctr_intv intv;
enum TrxCtr ctr_id;
uint32_t val;
};
extern const struct value_string rate_ctr_intv[];
extern const struct value_string trx_chan_ctr_names[];
struct trx_ctx;
void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx);
void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr);
int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr);
void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix);

View File

@@ -1,901 +0,0 @@
/*
* Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/misc.h>
#include "trx_rate_ctr.h"
#include "trx_vty.h"
#include "../config.h"
static struct trx_ctx* g_trx_ctx;
const struct value_string clock_ref_names[] = {
{ REF_INTERNAL, "internal" },
{ REF_EXTERNAL, "external" },
{ REF_GPS, "gpsdo" },
{ 0, NULL }
};
const struct value_string filler_names[] = {
{ FILLER_DUMMY, "Dummy bursts (C0 only)" },
{ FILLER_ZERO, "Empty bursts" },
{ FILLER_NORM_RAND, "GMSK Normal Bursts with random payload" },
{ FILLER_EDGE_RAND, "8-PSK Normal Bursts with random payload" },
{ FILLER_ACCESS_RAND, "Access Bursts with random payload" },
{ 0, NULL }
};
static const struct value_string filler_types[] = {
{ FILLER_DUMMY, "dummy" },
{ FILLER_ZERO, "zero" },
{ FILLER_NORM_RAND, "random-nb-gmsk" },
{ FILLER_EDGE_RAND, "random-nb-8psk" },
{ FILLER_ACCESS_RAND, "random-ab" },
{ 0, NULL }
};
static const struct value_string filler_docs[] = {
{ FILLER_DUMMY, "Send a Dummy Burst on C0 (TRX0) and empty burst on other channels" },
{ FILLER_ZERO, "Send an empty burst (default)" },
{ FILLER_NORM_RAND, "Send a GMSK modulated Normal Burst with random bits (spectrum mask testing)" },
{ FILLER_EDGE_RAND, "Send an 8-PSK modulated Normal Burst with random bits (spectrum mask testing)" },
{ FILLER_ACCESS_RAND, "Send an Access Burst with random bits (Rx/Tx alignment testing)" },
{ 0, NULL }
};
struct trx_ctx *trx_from_vty(struct vty *v)
{
/* It can't hurt to force callers to continue to pass the vty instance
* to this function, in case we'd like to retrieve the global
* trx instance from the vty at some point in the future. But
* until then, just return the global pointer, which should have been
* initialized by trx_vty_init().
*/
OSMO_ASSERT(g_trx_ctx);
return g_trx_ctx;
}
enum trx_vty_node {
TRX_NODE = _LAST_OSMOVTY_NODE + 1,
CHAN_NODE,
};
static struct cmd_node trx_node = {
TRX_NODE,
"%s(config-trx)# ",
1,
};
static struct cmd_node chan_node = {
CHAN_NODE,
"%s(config-trx-chan)# ",
1,
};
DEFUN(cfg_trx, cfg_trx_cmd,
"trx",
"Configure the TRX\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (!trx)
return CMD_WARNING;
vty->node = TRX_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
"bind-ip " VTY_IPV4_CMD,
"Set the IP address for the local bind\n"
"IPv4 Address\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
"remote-ip " VTY_IPV4_CMD,
"Set the IP address for the remote BTS\n"
"IPv4 Address\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_base_port, cfg_base_port_cmd,
"base-port <1-65535>",
"Set the TRX Base Port\n"
"TRX Base Port\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.base_port = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_dev_args, cfg_dev_args_cmd,
"dev-args DESC",
"Set the device-specific arguments to pass to the device\n"
"Device-specific arguments\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
"tx-sps (1|4)",
"Set the Tx Samples-per-Symbol\n"
"Tx Samples-per-Symbol\n"
"1 Sample-per-Symbol\n"
"4 Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.tx_sps = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
"rx-sps (1|4)",
"Set the Rx Samples-per-Symbol\n"
"Rx Samples-per-Symbol\n"
"1 Sample-per-Symbol\n"
"4 Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rx_sps = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
"clock-ref (internal|external|gpsdo)",
"Set the Reference Clock\n"
"Enable internal reference (default)\n"
"Enable external 10 MHz reference\n"
"Enable GPSDO reference\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
"multi-arfcn (disable|enable)",
"Multi-ARFCN transceiver mode (default=disable)\n"
"Enable multi-ARFCN mode\n" "Disable multi-ARFCN mode\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0) {
trx->cfg.multi_arfcn = false;
return CMD_SUCCESS;
}
if (trx->cfg.num_chans > TRX_MCHAN_MAX) {
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
TRX_MCHAN_MAX, VTY_NEWLINE);
return CMD_WARNING;
}
trx->cfg.multi_arfcn = true;
return CMD_SUCCESS;
}
DEFUN(cfg_offset, cfg_offset_cmd,
"offset FLOAT",
"Set the baseband frequency offset (default=0, auto)\n"
"Baseband Frequency Offset\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.offset = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_freq_offset, cfg_freq_offset_cmd,
"freq-offset FLOAT",
"Apply an artificial offset to Rx/Tx carrier frequency\n"
"Frequency offset in kHz (e.g. -145300)\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.freq_offset_khz = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
"rssi-offset FLOAT [relative]",
"Set the RSSI to dBm offset in dB (default=0)\n"
"RSSI to dBm offset in dB\n"
"Add to the default rssi-offset value instead of completely replacing it\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rssi_offset = atof(argv[0]);
trx->cfg.force_rssi_offset = (argc == 1);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_ul_fn_offset, cfg_ul_fn_offset_cmd,
"ul-fn-offset <-10-10>",
"Adjusts the uplink frame FN by the specified amount\n"
"Frame Number offset\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.ul_fn_offset = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_ul_freq_override, cfg_ul_freq_override_cmd,
"ul-freq-override FLOAT",
"Overrides Rx carrier frequency\n"
"Frequency in Hz (e.g. 145300000)\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.ul_freq_override = true;
trx->cfg.overrides.ul_freq = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_dl_freq_override, cfg_dl_freq_override_cmd,
"dl-freq-override FLOAT",
"Overrides Tx carrier frequency\n"
"Frequency in Hz (e.g. 145300000)\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.dl_freq_override = true;
trx->cfg.overrides.dl_freq = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_ul_gain_override, cfg_ul_gain_override_cmd,
"ul-gain-override FLOAT",
"Overrides Rx gain\n"
"gain in dB\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.ul_gain_override = true;
trx->cfg.overrides.ul_gain = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd,
"dl-gain-override FLOAT",
"Overrides Tx gain\n"
"gain in dB\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.dl_gain_override = true;
trx->cfg.overrides.dl_gain = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd,
"viterbi-eq (disable|enable)",
"Use viterbi equalizer for gmsk (default=disable)\n"
"Disable VA\n"
"Enable VA\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0)
trx->cfg.use_va = false;
else if (strcmp("enable", argv[0]) == 0)
trx->cfg.use_va = true;
else
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
"swap-channels (disable|enable)",
"Swap primary and secondary channels of the PHY (if any)\n"
"Do not swap primary and secondary channels (default)\n"
"Swap primary and secondary channels\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0) {
trx->cfg.swap_channels = false;
} else if (strcmp("enable", argv[0]) == 0) {
trx->cfg.swap_channels = true;
} else {
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_egprs, cfg_egprs_cmd,
"egprs (disable|enable)",
"EGPRS (8-PSK demodulation) support (default=disable)\n"
"Disable EGPRS (8-PSK demodulation) support\n"
"Enable EGPRS (8-PSK demodulation) support\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0) {
trx->cfg.egprs = false;
} else if (strcmp("enable", argv[0]) == 0) {
trx->cfg.egprs = true;
} else {
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
"ext-rach (disable|enable)",
"11-bit Access Burst correlation support (default=disable)\n"
"Disable 11-bit Access Burst (TS1 & TS2) correlation\n"
"Enable 11-bit Access Burst (TS1 & TS2) correlation\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0)
trx->cfg.ext_rach = false;
if (strcmp("enable", argv[0]) == 0)
trx->cfg.ext_rach = true;
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_rt_prio, cfg_rt_prio_cmd,
"rt-prio <1-32>",
"Set the SCHED_RR real-time priority\n"
"Real time priority\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.sched_rr = atoi(argv[0]);
vty_out (vty, "%% 'rt-prio %u' is deprecated, use 'policy rr %u' under 'sched' node instead%s",
trx->cfg.sched_rr, trx->cfg.sched_rr, VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_stack_size, cfg_stack_size_cmd,
"stack-size <0-2147483647>",
"Set the stack size per thread in BYTE, 0 = OS default\n"
"Stack size per thread in BYTE\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.stack_size = atoi(argv[0]);
return CMD_SUCCESS;
}
#define CFG_FILLER_DOC_STR \
"Filler burst settings\n"
DEFUN(cfg_filler, cfg_filler_type_cmd,
"AUTO-GENERATED", "AUTO-GENERATED")
{
struct trx_ctx *trx = trx_from_vty(vty);
// trx->cfg.filler is unsigned, so we need an interim int var to detect errors
int type = get_string_value(filler_types, argv[0]);
if (type < 0) {
trx->cfg.filler = FILLER_ZERO;
return CMD_WARNING;
}
trx->cfg.filler = type;
return CMD_SUCCESS;
}
DEFUN(cfg_test_rtsc, cfg_filler_tsc_cmd,
"filler tsc <0-7>",
CFG_FILLER_DOC_STR
"Set the TSC for GMSK/8-PSK Normal Burst random fillers. Used only with 'random-nb-gmsk' and"
" 'random-nb-8psk' filler types. (default=0)\n"
"TSC\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rtsc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_test_rach_delay, cfg_filler_rach_delay_cmd,
"filler access-burst-delay <0-68>",
CFG_FILLER_DOC_STR
"Set the delay for Access Burst random fillers. Used only with 'random-ab' filler type. (default=0)\n"
"RACH delay in symbols\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rach_delay = atoi(argv[0]);
return CMD_SUCCESS;
}
static int vty_ctr_name_2_id(const char* str) {
size_t i;
for (i = 0; trx_chan_ctr_names[i].str; i++) {
if (strstr(trx_chan_ctr_names[i].str, str)) {
return i;
}
}
return -1;
}
static int vty_intv_name_2_id(const char* str) {
size_t i;
for (i = 0; rate_ctr_intv[i].str; i++) {
if (strcmp(rate_ctr_intv[i].str, str) == 0) {
return i;
}
}
return -1;
}
#define THRESHOLD_ARGS "(rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples|tx_stale_bursts|tx_unavailable_bursts|tx_trxd_fn_repeated|tx_trxd_fn_outoforder|tx_trxd_fn_skipped)"
#define THRESHOLD_STR_VAL(s) "Set threshold value for rate_ctr device:" OSMO_STRINGIFY_VAL(s) "\n"
#define THRESHOLD_STRS \
THRESHOLD_STR_VAL(rx_overruns) \
THRESHOLD_STR_VAL(tx_underruns) \
THRESHOLD_STR_VAL(rx_drop_events) \
THRESHOLD_STR_VAL(rx_drop_samples) \
THRESHOLD_STR_VAL(tx_drop_events) \
THRESHOLD_STR_VAL(tx_drop_samples) \
THRESHOLD_STR_VAL(tx_stale_bursts) \
THRESHOLD_STR_VAL(tx_unavailable_bursts) \
THRESHOLD_STR_VAL(tx_trxd_fn_repeated) \
THRESHOLD_STR_VAL(tx_trxd_fn_outoforder) \
THRESHOLD_STR_VAL(tx_trxd_fn_skipped) \
""
#define INTV_ARGS "(per-second|per-minute|per-hour|per-day)"
#define INTV_STR_VAL(s) "Threshold value sampled " OSMO_STRINGIFY_VAL(s) "\n"
#define INTV_STRS \
INTV_STR_VAL(per-second) \
INTV_STR_VAL(per-minute) \
INTV_STR_VAL(per-hour) \
INTV_STR_VAL(per-day)
DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
"ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
"Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS,
CMD_ATTR_IMMEDIATE)
{
int rc;
struct ctr_threshold ctr;
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.ctr_id = (enum TrxCtr)rc;
ctr.val = atoi(argv[1]);
rc = vty_intv_name_2_id(argv[2]);
if (rc < 0) {
vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",
argv[0], ctr.val, argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.intv = (enum rate_ctr_intv) rc;
trx_rate_ctr_threshold_add(&ctr);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
"no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
NO_STR "Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS,
CMD_ATTR_IMMEDIATE)
{
int rc;
struct ctr_threshold ctr;
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.ctr_id = (enum TrxCtr)rc;
ctr.val = atoi(argv[1]);
rc = vty_intv_name_2_id(argv[2]);
if (rc < 0) {
vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",
argv[0], ctr.val, argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.intv = (enum rate_ctr_intv) rc;
if (trx_rate_ctr_threshold_del(&ctr) < 0) {
vty_out(vty, "no ctr-error-threshold: Entry to delete not found%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_chan, cfg_chan_cmd,
"chan <0-100>",
"Select a channel to configure\n"
"Channel index\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
int idx = atoi(argv[0]);
if (idx >= TRX_CHAN_MAX) {
vty_out(vty, "Chan list full.%s", VTY_NEWLINE);
return CMD_WARNING;
} else if (trx->cfg.multi_arfcn && trx->cfg.num_chans >= TRX_MCHAN_MAX) {
vty_out(vty, "Up to %i channels are supported for multi-TRX mode%s",
TRX_MCHAN_MAX, VTY_NEWLINE);
return CMD_WARNING;
}
if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */
vty_out(vty, "Non-existent or non-consecutive chan %d.%s",
idx, VTY_NEWLINE);
return CMD_WARNING;
} else if (trx->cfg.num_chans == idx) { /* creating it */
trx->cfg.num_chans++;
trx->cfg.chans[idx].trx = trx;
trx->cfg.chans[idx].idx = idx;
}
vty->node = CHAN_NODE;
vty->index = &trx->cfg.chans[idx];
return CMD_SUCCESS;
}
DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd,
"rx-path NAME",
"Set the Rx Path\n"
"Rx Path name\n")
{
struct trx_chan *chan = vty->index;
if (chan->trx->cfg.multi_arfcn && chan->idx > 0) {
vty_out(vty, "%% Setting 'rx-path' for chan %u in multi-ARFCN mode "
"does not make sense, because only chan 0 is used%s",
chan->idx, VTY_NEWLINE);
}
osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd,
"tx-path NAME",
"Set the Tx Path\n"
"Tx Path name\n")
{
struct trx_chan *chan = vty->index;
if (chan->trx->cfg.multi_arfcn && chan->idx > 0) {
vty_out(vty, "%% Setting 'tx-path' for chan %u in multi-ARFCN mode "
"does not make sense, because only chan 0 is used%s",
chan->idx, VTY_NEWLINE);
}
osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]);
return CMD_SUCCESS;
}
static int dummy_config_write(struct vty *v)
{
return CMD_SUCCESS;
}
static int config_write_trx(struct vty *vty)
{
struct trx_chan *chan;
int i;
struct trx_ctx *trx = trx_from_vty(vty);
vty_out(vty, "trx%s", VTY_NEWLINE);
if (trx->cfg.bind_addr)
vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
if (trx->cfg.remote_addr)
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
if (strlen(trx->cfg.dev_args))
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
if (trx->cfg.rx_sps != DEFAULT_RX_SPS)
vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
if (trx->cfg.clock_ref != REF_INTERNAL)
vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
if (trx->cfg.offset != 0)
vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE);
if (trx->cfg.freq_offset_khz != 0)
vty_out(vty, " freq-offset %f%s", trx->cfg.freq_offset_khz, VTY_NEWLINE);
if (!(trx->cfg.rssi_offset == 0 && !trx->cfg.force_rssi_offset))
vty_out(vty, " rssi-offset %f%s%s", trx->cfg.rssi_offset,
trx->cfg.force_rssi_offset ? " relative": "", VTY_NEWLINE);
vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE);
if (trx->cfg.sched_rr != 0)
vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);
if (trx->cfg.filler != FILLER_ZERO)
vty_out(vty, " filler type %s%s", get_value_string(filler_types, trx->cfg.filler), VTY_NEWLINE);
if (trx->cfg.rtsc > 0)
vty_out(vty, " filler tsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
if (trx->cfg.rach_delay > 0)
vty_out(vty, " filler access-burst-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
if (trx->cfg.stack_size != 0)
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
if (trx->cfg.ul_fn_offset != 0)
vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
if (trx->cfg.overrides.dl_freq_override)
vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE);
if (trx->cfg.overrides.ul_freq_override)
vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE);
if (trx->cfg.overrides.dl_gain_override)
vty_out(vty, " dl-gain-override %f%s", trx->cfg.overrides.dl_gain, VTY_NEWLINE);
if (trx->cfg.overrides.ul_gain_override)
vty_out(vty, " ul-gain-override %f%s", trx->cfg.overrides.ul_gain, VTY_NEWLINE);
if (trx->cfg.use_va)
vty_out(vty, " viterbi-eq %s%s", trx->cfg.use_va ? "enable" : "disable", VTY_NEWLINE);
trx_rate_ctr_threshold_write_config(vty, " ");
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE);
if (chan->rx_path)
vty_out(vty, " rx-path %s%s", chan->rx_path, VTY_NEWLINE);
if (chan->tx_path)
vty_out(vty, " tx-path %s%s", chan->tx_path, VTY_NEWLINE);
}
return CMD_SUCCESS;
}
static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
{
struct trx_chan *chan;
int i;
vty_out(vty, "TRX Config:%s", VTY_NEWLINE);
vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE);
vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE);
vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
vty_out(vty, " Filler Burst Type: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
vty_out(vty, " Filler Burst TSC: %u%s", trx->cfg.rtsc, VTY_NEWLINE);
vty_out(vty, " Filler Burst RACH Delay: %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Extended RACH support: %s%s", trx->cfg.ext_rach ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr,
trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Stack size per Thread in BYTE (0 = OS default): %u%s", trx->cfg.stack_size, VTY_NEWLINE);
vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
vty_out(vty, " Channel %u:%s", chan->idx, VTY_NEWLINE);
if (chan->rx_path)
vty_out(vty, " Rx Path: %s%s", chan->rx_path, VTY_NEWLINE);
if (chan->tx_path)
vty_out(vty, " Tx Path: %s%s", chan->tx_path, VTY_NEWLINE);
}
}
DEFUN(show_trx, show_trx_cmd,
"show trx",
SHOW_STR "Display information on the TRX\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx_dump_vty(vty, trx);
return CMD_SUCCESS;
}
static int trx_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case TRX_NODE:
case CHAN_NODE:
return 1;
default:
return 0;
}
}
static int trx_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case TRX_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
case CHAN_NODE:
vty->node = TRX_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
default:
vty->node = CONFIG_NODE;
vty->index = NULL;
vty->index_sub = NULL;
}
return vty->node;
}
static const char trx_copyright[] =
"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
"Copyright (C) 2013-2019 Fairwaves, Inc.\r\n"
"Copyright (C) 2015 Ettus Research LLC\r\n"
"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
struct vty_app_info g_vty_info = {
.name = "OsmoTRX",
.version = PACKAGE_VERSION,
.copyright = trx_copyright,
.go_parent_cb = trx_vty_go_parent,
.is_config_node = trx_vty_is_config_node,
};
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
{
struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx);
trx->cfg.bind_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP);
trx->cfg.base_port = DEFAULT_TRX_PORT;
trx->cfg.tx_sps = DEFAULT_TX_SPS;
trx->cfg.rx_sps = DEFAULT_RX_SPS;
trx->cfg.filler = FILLER_ZERO;
trx->cfg.rssi_offset = 0.0f;
trx->cfg.dev_args = talloc_strdup(trx, "");
return trx;
}
int trx_vty_init(struct trx_ctx* trx)
{
cfg_filler_type_cmd.string = vty_cmd_string_from_valstr(trx, filler_types,
"filler type (", "|", ")", 0);
cfg_filler_type_cmd.doc = vty_cmd_string_from_valstr(trx, filler_docs,
CFG_FILLER_DOC_STR "What to do when there is nothing to send "
"(filler type, default=zero)\n", "\n", "", 0);
g_trx_ctx = trx;
install_element_ve(&show_trx_cmd);
install_element(CONFIG_NODE, &cfg_trx_cmd);
install_node(&trx_node, config_write_trx);
install_element(TRX_NODE, &cfg_bind_ip_cmd);
install_element(TRX_NODE, &cfg_remote_ip_cmd);
install_element(TRX_NODE, &cfg_base_port_cmd);
install_element(TRX_NODE, &cfg_dev_args_cmd);
install_element(TRX_NODE, &cfg_tx_sps_cmd);
install_element(TRX_NODE, &cfg_rx_sps_cmd);
install_element(TRX_NODE, &cfg_clock_ref_cmd);
install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
install_element(TRX_NODE, &cfg_offset_cmd);
install_element(TRX_NODE, &cfg_freq_offset_cmd);
install_element(TRX_NODE, &cfg_rssi_offset_cmd);
install_element(TRX_NODE, &cfg_swap_channels_cmd);
install_element(TRX_NODE, &cfg_egprs_cmd);
install_element(TRX_NODE, &cfg_ext_rach_cmd);
install_element(TRX_NODE, &cfg_rt_prio_cmd);
install_element(TRX_NODE, &cfg_filler_type_cmd);
install_element(TRX_NODE, &cfg_filler_tsc_cmd);
install_element(TRX_NODE, &cfg_filler_rach_delay_cmd);
install_element(TRX_NODE, &cfg_ctr_error_threshold_cmd);
install_element(TRX_NODE, &cfg_no_ctr_error_threshold_cmd);
install_element(TRX_NODE, &cfg_stack_size_cmd);
install_element(TRX_NODE, &cfg_chan_cmd);
install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
install_element(TRX_NODE, &cfg_ul_freq_override_cmd);
install_element(TRX_NODE, &cfg_dl_freq_override_cmd);
install_element(TRX_NODE, &cfg_ul_gain_override_cmd);
install_element(TRX_NODE, &cfg_dl_gain_override_cmd);
install_element(TRX_NODE, &cfg_use_viterbi_cmd);
install_node(&chan_node, dummy_config_write);
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
logging_vty_add_deprecated_subsys(g_trx_ctx, "lms");
return 0;
}

View File

@@ -1,41 +0,0 @@
#pragma once
#include <osmocom/vty/command.h>
#include "config_defs.h"
extern struct vty_app_info g_vty_info;
extern const struct value_string clock_ref_names[];
extern const struct value_string filler_names[];
/* Maximum number of carriers in multi-ARFCN mode */
#define TRX_MCHAN_MAX 3
/* Samples-per-symbol for downlink path
* 4 - Uses precision modulator (more computation, less distortion)
* 1 - Uses minimized modulator (less computation, more distortion)
*
* Other values are invalid. Receive path (uplink) is always
* downsampled to 1 sps. Default to 4 sps for all cases.
*/
#define DEFAULT_TX_SPS 4
/*
* Samples-per-symbol for uplink (receiver) path
* Do not modify this value. EDGE configures 4 sps automatically on
* B200/B210 devices only. Use of 4 sps on the receive path for other
* configurations is not supported.
*/
#define DEFAULT_RX_SPS 1
/* Default configuration parameters */
#define DEFAULT_TRX_PORT 5700
#define DEFAULT_TRX_IP "127.0.0.1"
#define DEFAULT_CHANS 1
struct trx_ctx {
struct trx_cfg cfg;
};
int trx_vty_init(struct trx_ctx* trx);
struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx);

View File

@@ -2,8 +2,6 @@
* Copyright 2008 Free Software Foundation, Inc.
* Copyright 2011 Range Networks, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -56,10 +54,7 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
// |-head-||---------midamble----------------------||--------------data----------------||t|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");

View File

@@ -2,8 +2,6 @@
/*
* Copyright 2008-2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -54,9 +52,7 @@ extern const BitVector gEdgeTrainingSequence[];
extern const BitVector gDummyBurst;
/** Random access burst synch. sequence */
extern const BitVector gRACHSynchSequenceTS0;
extern const BitVector gRACHSynchSequenceTS1;
extern const BitVector gRACHSynchSequenceTS2;
extern const BitVector gRACHSynchSequence;
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
extern const BitVector gRACHBurst;
@@ -168,7 +164,7 @@ class Time {
unsigned newTN = (mTN + other.mTN) % 8;
uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe;
return Time(newFN,newTN);
}
}
int operator-(const Time& other) const
{

33
INSTALLATION Normal file
View File

@@ -0,0 +1,33 @@
Installation Requirements
OpenBTS compiles to a simple Unix binary and does not require special
installation.
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
running configure.
To run OpenBTS, the following should be installed:
Asterisk (http://www.asterisk.org), running SIP on port 5060.
libosip2 (http://www.gnu.org/software/osip/)
libortp (http://freshmeat.net/projects/ortp/)
libusrp (http://gnuradio.org).
This is part of the GNURadio installation.
It is the only part used by OpenBTS.
OpenBTS logs to syslogd as facility LOG_LOCAL7. Please set your /etc/syslog.conf
accordingly.
For information on specific executables, see tests/README.tests and
apps/README.apps.
See http://gnuradio.org/redmine/wiki/gnuradio/OpenBTS/BuildingAndRunning for more
information.

20
LEGAL
View File

@@ -1,8 +1,5 @@
OpenBTS
The OsmoTRX project is direved from OpenBTS transceiver code. See http://openbts.org/ for details.
The related copyrights:
Most parts copyright 2008-2011 Free Software Foundation.
Some parts copyright 2010 Kestrel Signal Processing, Inc.
Some parts copyright 2011 Range Networks, Inc.
@@ -15,9 +12,17 @@ patented technologies. The user of this software is required to take whatever
actions are necessary to avoid patent infringement.
Trademark
"OpenBTS" is a registered trademark of Range Networks, Inc. (Range), a
California corporation. Range reserves the right to control the use of this
trademark. Do not use this trademark in commerce without permission and do not
rebrand OpenBTS under a different trademark.
Telecom and Radio Spectrum Laws
The primary function of OsmoTRX is the provision of telecommunications service
The primary function of OpenBTS is the provision of telecommunications service
over a radio link. This activity is heavily regulated nearly everywhere in
the world. Users of this software are expected to comply with local and national
regulations in the jurisdictions where this sortware is used with radio equipment.
@@ -34,7 +39,7 @@ The legal restrictions listed here are not necessarily exhaustive.
Note to US Government Users
The OsmoTRX software applications and associated documentation are "Commercial
The OpenBTS software applications and associated documentation are "Commercial
Item(s)," as that term is defined at 48 C.F.R. Section 2.101, consisting of
"Commercial Computer Software" and "Commercial Computer Software Documentation,"
as such terms are used in 48 C.F.R. 12.212 or 48 C.F.R. 227.7202, as
@@ -49,12 +54,13 @@ and AGPLv3.
Note to US Government Contractors
GPL is not compatible with "government purpose rights" (GPR). If you receive
OsmoTRX software under a GPL and deliver it under GPR, you will be in violation
OpenBTS software under a GPL and deliver it under GPR, you will be in violation
of GPL and possibly subject to enforcement actions by the original authors and
copyright holders, including the Free Software Foundation, Inc.
Software Licensing and Distribution
The OsmoTRX is distributed publicly under AGPLv3. See the COPYING file
A subset of OpenBTS is distributed publicly under AGPLv3. Range reserves the right to
distribute most of this source code other licenses as well. See the COPYING file
for more information on the license for this distribution.

View File

@@ -22,41 +22,24 @@ include $(top_srcdir)/Makefile.common
ACLOCAL_AMFLAGS = -I config
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
AM_CXXFLAGS = -Wall -pthread
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
SUBDIRS =
if ENABLE_MS_TRX
SUBDIRS += $(LIBTRXCON_DIR)
endif
AM_CXXFLAGS = -Wall -pthread -ldl
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
# Order must be preserved
SUBDIRS += \
SUBDIRS = \
sqlite3 \
CommonLibs \
GSM \
Transceiver52M \
contrib \
tests \
utils \
doc \
$(NULL)
Transceiver52M
EXTRA_DIST = \
autogen.sh \
INSTALLATION \
LEGAL \
COPYING \
README.md \
contrib/osmo-trx.spec.in \
debian \
$(NULL)
README
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
.PHONY: release
@RELMAKE@
dox: FORCE
doxygen doxconfig

View File

@@ -18,21 +18,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
top_srcdir = $(abs_top_srcdir)
top_builddir = $(abs_top_builddir)
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
GSM_INCLUDEDIR = $(top_srcdir)/GSM
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
STD_DEFINES_AND_INCLUDES = \
$(SVNDEV) \
-I$(COMMON_INCLUDEDIR) \
-I$(GSM_INCLUDEDIR)
-I$(GSM_INCLUDEDIR) \
-I$(SQLITE_INCLUDEDIR)
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
GSM_LA = $(top_builddir)/GSM/libGSM.la
if ARCH_ARM
ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la
else
ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la
endif
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl
MOSTLYCLEANFILES = *~

0
NEWS Normal file
View File

116
README Normal file
View File

@@ -0,0 +1,116 @@
This is the interface to the transcevier.
Each TRX Manager UDP socket interface represents a single ARFCN.
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
Give a base port B (5700), the master clock interface is at port P=B.
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
The corresponding core-side interface for every socket is at P+100.
For any given build, the number of ARFCN interfaces can be fixed.
Indications on the Master Clock Interface
The master clock interface is output only (from the radio).
Messages are "indications".
CLOCK gives the current value of the transceiver clock to be used by the core.
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
IND CLOCK <totalFrames>
Commands on the Per-ARFCN Control Interface
The per-ARFCN control interface uses a command-reponse protocol.
Commands are NULL-terminated ASCII strings, one per UDP socket.
Each command has a corresponding response.
Every command is of the form:
CMD <cmdtype> [params]
The <cmdtype> is the actual command.
Parameters are optional depending on the commands type.
Every response is of the form:
RSP <cmdtype> <status> [result]
The <status> is 0 for success and a non-zero error code for failure.
Successful responses may include results, depending on the command type.
Power Control
POWEROFF shuts off transmitter power and stops the demodulator.
CMD POWEROFF
RSP POWEROFF <status>
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
This command fails if the transmitter and receiver are not yet tuned.
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
If the transceiver is already on, it response with success to this command.
CMD POWERON
RSP POWERON <status>
SETPOWER sets output power in dB wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD SETPOWER <dB>
RSP SETPOWER <status> <dB>
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD ADJPOWER <dBStep>
RSP ADJPOWER <status> <dBLevel>
Tuning Control
RXTUNE tunes the receiver to a given frequency in kHz.
This command fails if the receiver is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD RXTUNE <kHz>
RSP RXTUNE <status> <kHz>
TXTUNE tunes the transmitter to a given frequency in kHz.
This command fails if the transmitter is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD TXTUNE <kHz>
RSP TXTUNE <status> <kHz>
Timeslot Control
SETSLOT sets the format of the uplink timeslots in the ARFCN.
The <timeslot> indicates the timeslot of interest.
The <chantype> indicates the type of channel that occupies the timeslot.
A chantype of zero indicates the timeslot is off.
CMD SETSLOT <timeslot> <chantype>
RSP SETSLOT <status> <timeslot> <chantype>
Messages on the per-ARFCN Data Interface
Messages on the data interface carry one radio burst per UDP message.
Received Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte RSSI in -dBm
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
Transmit Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte transmit level wrt ARFCN max, -dB (attenuation)
148 bytes output symbol values, 0 & 1

View File

@@ -1,64 +0,0 @@
About OsmoTRX
=============
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
* TS 05.01 "Physical layer on the radio path"
* TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
* TS 05.04 "Modulation"
* TS 05.10 "Radio subsystem synchronization"
OsmoTRX is originally based on the transceiver code from the
[OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup
to operate independently with the purpose of using with non-OpenBTS software and
projects, specifically within the Osmocom stack. Used together with
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty
standard GSM BTS with Abis interface as per the relevant 3GPP specifications.
Homepage
--------
The official homepage of the project is
<https://osmocom.org/projects/osmotrx/wiki/OsmoTRX>
GIT Repository
--------------
You can clone from the official osmo-trx.git repository using
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx`
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx>
Documentation
-------------
Doxygen-generated API documentation is generated during the build process, but
also available online for each of the sub-libraries at User Manual for OsmoTRX
can be generated during the build process, and is also available online at
<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
Mailing List
------------
Discussions related to OsmoTRX are happening on the openbsc@lists.osmocom.org
mailing list, please see <https://lists.osmocom.org/mailman/listinfo/openbsc>
for subscription options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We us a gerrit based patch submission/review process for managing contributions.
Please see <https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit>
for more details
The current patch queue for OsmoTRX can be seen at
<https://gerrit.osmocom.org/q/project:osmo-trx+status:open>

View File

@@ -1,109 +0,0 @@
/*
* Polyphase channelizer
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include "Channelizer.h"
extern "C" {
#include "fft.h"
#include "convolve.h"
}
static void deinterleave(const float *in, size_t ilen,
float **out, size_t olen, size_t m)
{
size_t i, n;
for (i = 0; i < olen; i++) {
for (n = 0; n < m; n++) {
out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0];
out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1];
}
}
}
size_t Channelizer::inputLen() const
{
return blockLen * m;
}
size_t Channelizer::outputLen() const
{
return blockLen;
}
float *Channelizer::outputBuffer(size_t chan) const
{
if (chan >= m)
return NULL;
return hInputs[chan];
}
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool Channelizer::rotate(const float *in, size_t len)
{
size_t hSize = 2 * hLen * sizeof(float);
if (!checkLen(blockLen, len))
return false;
deinterleave(in, len, hInputs, blockLen, m);
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen);
}
cxvec_fft(fftHandle);
return true;
}
/* Setup channelizer parameters */
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{
}
Channelizer::~Channelizer()
{
}

View File

@@ -1,34 +0,0 @@
#ifndef _CHANNELIZER_RX_H_
#define _CHANNELIZER_RX_H_
#include "ChannelizerBase.h"
class Channelizer : public ChannelizerBase {
public:
/** Constructor for channelizing filter bank
@param m number of physical channels
@param blockLen number of samples per output of each iteration
@param hLen number of taps in each constituent filter path
*/
Channelizer(size_t m, size_t blockLen, size_t hLen = 16);
~Channelizer();
/* Return required input and output buffer lengths */
size_t inputLen() const;
size_t outputLen() const;
/** Rotate "input commutator" and drive samples through filterbank
@param in complex input vector
@param iLen number of samples in buffer (must match block length)
@return false on error and true otherwise
*/
bool rotate(const float *in, size_t iLen);
/** Get buffer for an output path
@param chan channel number of filterbank
@return NULL on error and pointer to buffer otherwise
*/
float *outputBuffer(size_t chan) const;
};
#endif /* _CHANNELIZER_RX_H_ */

View File

@@ -1,255 +0,0 @@
/*
* Polyphase channelizer
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <malloc.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include "Logger.h"
#include "ChannelizerBase.h"
extern "C" {
#include "fft.h"
}
static float sinc(float x)
{
if (x == 0.0f)
return 0.999999999999f;
return sin(M_PI * x) / (M_PI * x);
}
/*
* There are more efficient reversal algorithms, but we only reverse at
* initialization so we don't care.
*/
static void reverse(float *buf, size_t len)
{
float tmp[2 * len];
memcpy(tmp, buf, 2 * len * sizeof(float));
for (size_t i = 0; i < len; i++) {
buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0];
buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1];
}
}
/*
* Create polyphase filterbank
*
* Implementation based material found in,
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool ChannelizerBase::initFilters()
{
size_t protoLen = m * hLen;
float *proto;
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (protoLen - 1.0) / 2.0;
/*
* Allocate 'M' partition filters and the temporary prototype
* filter. Coefficients are real only and must be 16-byte memory
* aligned for SSE usage.
*/
proto = new float[protoLen];
if (!proto)
return false;
subFilters = (float **) malloc(sizeof(float *) * m);
if (!subFilters) {
delete[] proto;
return false;
}
for (size_t i = 0; i < m; i++) {
subFilters[i] = (float *)
memalign(16, hLen * 2 * sizeof(float));
}
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of channels.
*/
float a0 = 0.35875;
float a1 = 0.48829;
float a2 = 0.14128;
float a3 = 0.01168;
for (size_t i = 0; i < protoLen; i++) {
proto[i] = sinc(((float) i - midpt) / (float) m);
proto[i] *= a0 -
a1 * cos(2 * M_PI * i / (protoLen - 1)) +
a2 * cos(4 * M_PI * i / (protoLen - 1)) -
a3 * cos(6 * M_PI * i / (protoLen - 1));
sum += proto[i];
}
scale = (float) m / sum;
/*
* Populate partition filters and reverse the coefficients per
* convolution requirements.
*/
for (size_t i = 0; i < hLen; i++) {
for (size_t n = 0; n < m; n++) {
subFilters[n][2 * i + 0] = proto[i * m + n] * scale;
subFilters[n][2 * i + 1] = 0.0f;
}
}
for (size_t i = 0; i < m; i++)
reverse(subFilters[i], hLen);
delete[] proto;
return true;
}
bool ChannelizerBase::initFFT()
{
size_t size;
if (fftInput || fftOutput || fftHandle)
return false;
size = blockLen * m * 2 * sizeof(float);
fftInput = (float *) fft_malloc(size);
memset(fftInput, 0, size);
size = (blockLen + hLen) * m * 2 * sizeof(float);
fftOutput = (float *) fft_malloc(size);
memset(fftOutput, 0, size);
if (!fftInput | !fftOutput) {
LOG(ALERT) << "Memory allocation error";
return false;
}
fftHandle = init_fft(0, m, blockLen, blockLen + hLen,
fftInput, fftOutput, hLen);
return true;
}
bool ChannelizerBase::mapBuffers()
{
if (!fftHandle) {
LOG(ALERT) << "FFT buffers not initialized";
return false;
}
hInputs = (float **) malloc(sizeof(float *) * m);
hOutputs = (float **) malloc(sizeof(float *) * m);
if (!hInputs | !hOutputs)
return false;
for (size_t i = 0; i < m; i++) {
hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)];
hOutputs[i] = &fftInput[2 * (i * blockLen)];
}
return true;
}
/*
* Setup filterbank internals
*/
bool ChannelizerBase::init()
{
/*
* Filterbank coefficients, fft plan, history, and output sample
* rate conversion blocks
*/
if (!initFilters()) {
LOG(ALERT) << "Failed to initialize channelizing filter";
return false;
}
hist = (float **) malloc(sizeof(float *) * m);
for (size_t i = 0; i < m; i++) {
hist[i] = new float[2 * hLen];
memset(hist[i], 0, 2 * hLen * sizeof(float));
}
if (!initFFT()) {
LOG(ALERT) << "Failed to initialize FFT";
return false;
}
mapBuffers();
return true;
}
/* Check vector length validity */
bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
{
if (outerLen != innerLen * m) {
LOG(ALERT) << "Invalid outer length " << innerLen
<< " is not multiple of " << blockLen;
return false;
}
if (innerLen != blockLen) {
LOG(ALERT) << "Invalid inner length " << outerLen
<< " does not equal " << blockLen;
return false;
}
return true;
}
/*
* Setup channelizer parameters
*/
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
: subFilters(NULL), hInputs(NULL), hOutputs(NULL), hist(NULL),
fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
{
this->m = m;
this->hLen = hLen;
this->blockLen = blockLen;
}
ChannelizerBase::~ChannelizerBase()
{
free_fft(fftHandle);
for (size_t i = 0; i < m; i++) {
free(subFilters[i]);
delete[] hist[i];
}
free(subFilters);
fft_free(fftInput);
fft_free(fftOutput);
free(hInputs);
free(hOutputs);
free(hist);
}

View File

@@ -1,39 +0,0 @@
#ifndef _CHANNELIZER_BASE_H_
#define _CHANNELIZER_BASE_H_
class ChannelizerBase {
protected:
ChannelizerBase(size_t m, size_t blockLen, size_t hLen);
~ChannelizerBase();
/* Channelizer parameters */
size_t m;
size_t hLen;
size_t blockLen;
/* Channelizer filterbank sub-filters */
float **subFilters;
/* Input/Output buffers */
float **hInputs, **hOutputs, **hist;
float *fftInput, *fftOutput;
/* Pointer to opaque FFT instance */
struct fft_hdl *fftHandle;
/* Initializer internals */
bool initFilters();
bool initFFT();
void releaseFilters();
/* Map overlapped FFT and filter I/O buffers */
bool mapBuffers();
/* Buffer length validity checking */
bool checkLen(size_t innerLen, size_t outerLen);
public:
/* Initialize channelizer/synthesis filter internals */
bool init();
};
#endif /* _CHANNELIZER_BASE_H_ */

View File

@@ -5,7 +5,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -29,7 +29,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
template<class Real> class Complex {
public:
typedef Real value_type;
Real r, i;
/**@name constructors */

View File

@@ -21,168 +21,78 @@
include $(top_srcdir)/Makefile.common
SUBDIRS = arch device
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common
AM_CXXFLAGS = -ldl -lpthread
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
SUBDIRS = arm x86
noinst_LTLIBRARIES = libtransceiver_common.la
if ARCH_ARM
ARCH_LA = arm/libarch.la
else
ARCH_LA = x86/libarch.la
endif
if USRP1
AM_CPPFLAGS += $(USRP_CFLAGS)
else
AM_CPPFLAGS += $(UHD_CFLAGS)
endif
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4
dist_rev2_DATA = std_inband.rbf
dist_rev4_DATA = std_inband.rbf
EXTRA_DIST = \
README \
README.Talgorithm
noinst_LTLIBRARIES = libtransceiver.la
COMMON_SOURCES = \
radioInterface.cpp \
radioVector.cpp \
radioClock.cpp \
radioBuffer.cpp \
sigProcLib.cpp \
signalVector.cpp \
Transceiver.cpp \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp \
proto_trxd.c \
grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc
Transceiver.cpp
libtransceiver_common_la_SOURCES = \
libtransceiver_la_SOURCES = \
$(COMMON_SOURCES) \
Resampler.cpp \
radioInterfaceResamp.cpp \
radioInterfaceMulti.cpp
radioInterfaceDiversity.cpp
bin_PROGRAMS = osmo-trx
noinst_HEADERS = \
Complex.h \
radioInterface.h \
radioVector.h \
radioClock.h \
radioBuffer.h \
radioDevice.h \
sigProcLib.h \
signalVector.h \
Transceiver.h \
USRPDevice.h \
Resampler.h \
ChannelizerBase.h \
Channelizer.h \
Synthesis.h \
proto_trxd.h
common/convolve.h \
common/convert.h \
common/scale.h \
common/mult.h
COMMON_LDADD = \
libtransceiver_common.la \
osmo_trx_SOURCES = osmo-trx.cpp
osmo_trx_LDADD = \
libtransceiver.la \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) \
$(FFTWF_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
if ENABLE_MS_TRX
AM_CPPFLAGS += -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
AM_CPPFLAGS += -I${srcdir}
TRXCON_LDADD = \
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libtrxcon.a \
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1sched.a \
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1gprs.a \
$(LIBOSMOCODING_LIBS)
MS_LOWER_SRC = \
ms/sch.c \
ms/ms.cpp \
ms/ms_rx_lower.cpp \
grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc
MS_UPPER_SRC = \
ms/ms_upper.cpp \
ms/l1ctl_server.c \
ms/logging.c \
ms/l1ctl_server_cb.cpp \
ms/ms_trxcon_if.cpp
noinst_HEADERS += \
ms/ms.h \
ms/bladerf_specific.h \
ms/uhd_specific.h \
ms/ms_upper.h \
ms/ms_trxcon_if.h \
ms/itrq.h \
ms/sch.h \
ms/threadpool.h \
grgsm_vitac/viterbi_detector.h \
grgsm_vitac/constants.h \
grgsm_vitac/grgsm_vitac.h
$(COMMON_LA) $(SQLITE_LA)
endif
bin_PROGRAMS =
if DEVICE_UHD
bin_PROGRAMS += osmo-trx-uhd
osmo_trx_uhd_SOURCES = osmo-trx.cpp
osmo_trx_uhd_LDADD = \
$(builddir)/device/uhd/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS)
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
if ENABLE_MS_TRX
bin_PROGRAMS += osmo-trx-ms-uhd
osmo_trx_ms_uhd_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
osmo_trx_ms_uhd_LDADD = \
$(builddir)/device/uhd/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS) \
$(TRXCON_LDADD)
osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
endif
endif
if DEVICE_USRP1
bin_PROGRAMS += osmo-trx-usrp1
osmo_trx_usrp1_SOURCES = osmo-trx.cpp
osmo_trx_usrp1_LDADD = \
$(builddir)/device/usrp1/libdevice.la \
$(COMMON_LDADD) \
$(USRP_LIBS)
osmo_trx_usrp1_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS)
endif
if DEVICE_LMS
bin_PROGRAMS += osmo-trx-lms
osmo_trx_lms_SOURCES = osmo-trx.cpp
osmo_trx_lms_LDADD = \
$(builddir)/device/lms/libdevice.la \
$(COMMON_LDADD) \
$(LMS_LIBS)
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
endif
if DEVICE_BLADE
bin_PROGRAMS += osmo-trx-blade
osmo_trx_blade_SOURCES = osmo-trx.cpp
osmo_trx_blade_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(BLADE_LIBS)
osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
if ENABLE_MS_TRX
bin_PROGRAMS += osmo-trx-ms-blade
osmo_trx_ms_blade_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
osmo_trx_ms_blade_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(BLADE_LIBS) \
$(TRXCON_LDADD)
osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
endif
endif
if DEVICE_IPC
bin_PROGRAMS += osmo-trx-ipc
osmo_trx_ipc_SOURCES = osmo-trx.cpp
osmo_trx_ipc_LDADD = \
$(builddir)/device/ipc/libdevice.la \
$(COMMON_LDADD)
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
if USRP1
libtransceiver_la_SOURCES += USRPDevice.cpp
osmo_trx_LDADD += $(USRP_LIBS)
else
libtransceiver_la_SOURCES += UHDDevice.cpp
osmo_trx_LDADD += $(UHD_LIBS)
endif

35
Transceiver52M/README Normal file
View File

@@ -0,0 +1,35 @@
The Transceiver
The transceiver consists of three modules:
--- transceiver
--- radioInterface
--- USRPDevice
The USRPDevice module is basically a driver that reads/writes
packets to a USRP with two RFX900 daughterboards, board
A is the Tx chain and board B is the Rx chain.
The radioInterface module is basically an interface b/w the
transceiver and the USRP. It operates the basestation clock
based upon the sample count of received USRP samples. Packets
from the USRP are queued and segmented into GSM bursts that are
passed up to the transceiver; bursts from the transceiver are
passed down to the USRP.
The transceiver basically operates "layer 0" of the GSM stack,
performing the modulation, detection, and demodulation of GSM
bursts. It communicates with the GSM stack via three UDP sockets,
one socket for data, one for control messages, and one socket to
pass clocking information. The transceiver contains a priority
queue to sort to-be-transmitted bursts, and a filler table to fill
in timeslots that do not have bursts in the priority queue. The
transceiver tries to stay ahead of the basestation clock, adapting
its latency when underruns are reported by the radioInterface/USRP.
Received bursts (from the radioInterface) pass through a simple
energy detector, a RACH or midamble correlator, and a DFE-based demodulator.
NOTE: There's a SWLOOPBACK #define statement, where the USRP is replaced
with a memory buffer. In this mode, data written to the USRP is actually stored
in a buffer, and read commands to the USRP simply pull data from this buffer.
This was very useful in early testing, and still may be useful in testing basic
Transceiver and radioInterface functionality.

View File

@@ -2,8 +2,6 @@
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -13,6 +11,10 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
@@ -20,7 +22,6 @@
#include <string.h>
#include <malloc.h>
#include <iostream>
#include <algorithm>
#include "Resampler.h"
@@ -34,8 +35,6 @@ extern "C" {
#define MAX_OUTPUT_LEN 4096
using namespace std;
static float sinc(float x)
{
if (x == 0.0)
@@ -44,24 +43,37 @@ static float sinc(float x)
return sin(M_PI * x) / (M_PI * x);
}
void Resampler::initFilters(float bw)
bool Resampler::initFilters(float bw)
{
float cutoff;
size_t proto_len = p * filt_len;
float *proto, val, cutoff;
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (proto_len - 1.0) / 2.0;
/*
/*
* Allocate partition filters and the temporary prototype filter
* according to numerator of the rational rate. Coefficients are
* real only and must be 16-byte memory aligned for SSE usage.
*/
auto proto = vector<float>(p * filt_len);
for (auto &part : partitions)
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
proto = new float[proto_len];
if (!proto)
return false;
/*
partitions = (float **) malloc(sizeof(float *) * p);
if (!partitions) {
free(proto);
return false;
}
for (size_t i = 0; i < p; i++) {
partitions[i] = (float *)
memalign(16, filt_len * 2 * sizeof(float));
}
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of filter partitions.
* by the number of filter partitions.
*/
float a0 = 0.35875;
float a1 = 0.48829;
@@ -73,29 +85,49 @@ void Resampler::initFilters(float bw)
else
cutoff = (float) q;
float midpt = (proto.size() - 1) / 2.0;
for (size_t i = 0; i < proto.size(); i++) {
for (size_t i = 0; i < proto_len; i++) {
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
proto[i] *= a0 -
a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
a3 * cos(6 * M_PI * i / (proto.size() - 1));
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
a3 * cos(6 * M_PI * i / (proto_len - 1));
sum += proto[i];
}
scale = p / sum;
/* Populate filter partitions from the prototype filter */
for (size_t i = 0; i < filt_len; i++) {
for (size_t n = 0; n < p; n++)
partitions[n][i] = complex<float>(proto[i * p + n] * scale);
for (size_t n = 0; n < p; n++) {
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
partitions[n][2 * i + 1] = 0.0f;
}
}
/* Store filter taps in reverse */
for (auto &part : partitions)
reverse(&part[0], &part[filt_len]);
/* For convolution, we store the filter taps in reverse */
for (size_t n = 0; n < p; n++) {
for (size_t i = 0; i < filt_len / 2; i++) {
val = partitions[n][2 * i];
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
partitions[n][2 * (filt_len - 1 - i)] = val;
}
}
delete proto;
return true;
}
void Resampler::releaseFilters()
{
if (partitions) {
for (size_t i = 0; i < p; i++)
free(partitions[i]);
}
free(partitions);
partitions = NULL;
}
#ifndef __OPTIMIZE__
static bool check_vec_len(int in_len, int out_len, int p, int q)
{
if (in_len % q) {
@@ -126,24 +158,46 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
return true;
}
#endif
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
void Resampler::computePath()
{
for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
in_index[i] = (q * i) / p;
out_path[i] = (q * i) % p;
}
}
int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
{
int n, path;
#ifndef __OPTIMIZE__
int hist_len = filt_len - 1;
if (!check_vec_len(in_len, out_len, p, q))
return -1;
#endif
if (history_on) {
memcpy(&in[-2 * hist_len],
history, hist_len * 2 * sizeof(float));
} else {
memset(&in[-2 * hist_len], 0,
hist_len * 2 * sizeof(float));
}
/* Generate output from precomputed input/output paths */
for (size_t i = 0; i < out_len; i++) {
n = in_index[i];
path = out_path[i];
n = in_index[i];
path = out_path[i];
convolve_real(in, in_len,
reinterpret_cast<float *>(partitions[path]),
filt_len, &out[2 * i], out_len - i,
n, 1);
partitions[path], filt_len,
&out[2 * i], out_len - i,
n, 1, 1, 0);
}
/* Save history */
if (history_on) {
memcpy(history, &in[2 * (in_len - hist_len)],
hist_len * 2 * sizeof(float));
}
return out_len;
@@ -151,18 +205,20 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
bool Resampler::init(float bw)
{
if (p == 0 || q == 0 || filt_len == 0) return false;
size_t hist_len = filt_len - 1;
/* Filterbank filter internals */
initFilters(bw);
if (initFilters(bw) < 0)
return false;
/* History buffer */
history = new float[2 * hist_len];
memset(history, 0, 2 * hist_len * sizeof(float));
/* Precompute filterbank paths */
int i = 0;
for (auto &index : in_index)
index = (q * i++) / p;
i = 0;
for (auto &path : out_path)
path = (q * i++) % p;
in_index = new size_t[MAX_OUTPUT_LEN];
out_path = new size_t[MAX_OUTPUT_LEN];
computePath();
return true;
}
@@ -172,8 +228,14 @@ size_t Resampler::len()
return filt_len;
}
void Resampler::enableHistory(bool on)
{
history_on = on;
}
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
: in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
: in_index(NULL), out_path(NULL), partitions(NULL),
history(NULL), history_on(true)
{
this->p = p;
this->q = q;
@@ -182,6 +244,9 @@ Resampler::Resampler(size_t p, size_t q, size_t filt_len)
Resampler::~Resampler()
{
for (auto &part : partitions)
free(part);
releaseFilters();
delete history;
delete in_index;
delete out_path;
}

View File

@@ -2,8 +2,6 @@
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -13,30 +11,31 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _RESAMPLER_H_
#define _RESAMPLER_H_
#include <vector>
#include <complex>
class Resampler {
public:
/* Constructor for rational sample rate conversion
* @param p numerator of resampling ratio
* @param q denominator of resampling ratio
* @param filt_len length of each polyphase subfilter
* @param filt_len length of each polyphase subfilter
*/
Resampler(size_t p, size_t q, size_t filt_len = 16);
~Resampler();
/* Initialize resampler filterbank.
/* Initilize resampler filterbank.
* @param bw bandwidth factor on filter generation (pre-window)
* @return false on error, zero otherwise
*
* Automatic setting is to compute the filter to prevent aliasing with
* a Blackman-Harris window. Adjustment is made through a bandwidth
* a Blackman-Harris window. Adjustment is made through a bandwith
* factor to shift the cutoff and/or the constituent filter lengths.
* Calculation of specific rolloff factors or 3-dB cutoff points is
* left as an excersize for the reader.
@@ -53,22 +52,32 @@ public:
* Input and output vector lengths must of be equal multiples of the
* rational conversion rate denominator and numerator respectively.
*/
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
int rotate(float *in, size_t in_len, float *out, size_t out_len);
/* Get filter length
* @return number of taps in each filter partition
* @return number of taps in each filter partition
*/
size_t len();
/*
* Enable/disable history
*/
void enableHistory(bool on);
private:
size_t p;
size_t q;
size_t filt_len;
std::vector<size_t> in_index;
std::vector<size_t> out_path;
std::vector<std::complex<float> *> partitions;
size_t *in_index;
size_t *out_path;
void initFilters(float bw);
float **partitions;
float *history;
bool history_on;
bool initFilters(float bw);
void releaseFilters();
void computePath();
};
#endif /* _RESAMPLER_H_ */

View File

@@ -1,123 +0,0 @@
/*
* Polyphase synthesis filter
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include <iostream>
#include "Synthesis.h"
extern "C" {
#include "fft.h"
#include "convolve.h"
}
static void interleave(float **in, size_t ilen,
float *out, size_t m)
{
size_t i, n;
for (i = 0; i < ilen; i++) {
for (n = 0; n < m; n++) {
out[2 * (i * m + n) + 0] = in[n][2 * i + 0];
out[2 * (i * m + n) + 1] = in[n][2 * i + 1];
}
}
}
size_t Synthesis::inputLen() const
{
return blockLen;
}
size_t Synthesis::outputLen() const
{
return blockLen * m;
}
float *Synthesis::inputBuffer(size_t chan) const
{
if (chan >= m)
return NULL;
return hOutputs[chan];
}
bool Synthesis::resetBuffer(size_t chan)
{
if (chan >= m)
return false;
memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float));
return true;
}
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool Synthesis::rotate(float *out, size_t len)
{
size_t hSize = 2 * hLen * sizeof(float);
if (!checkLen(blockLen, len)) {
std::cout << "Length fail" << std::endl;
exit(1);
return false;
}
cxvec_fft(fftHandle);
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen);
}
/* Interleave into output vector */
interleave(hOutputs, blockLen, out, m);
return true;
}
Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{
}
Synthesis::~Synthesis()
{
}

View File

@@ -1,35 +0,0 @@
#ifndef _SYNTHESIS_H_
#define _SYNTHESIS_H_
#include "ChannelizerBase.h"
class Synthesis : public ChannelizerBase {
public:
/** Constructor for synthesis filterbank
@param m number of physical channels
@param blockLen number of samples per output of each iteration
@param hLen number of taps in each constituent filter path
*/
Synthesis(size_t m, size_t blockLen, size_t hLen = 16);
~Synthesis();
/* Return required input and output buffer lengths */
size_t inputLen() const;
size_t outputLen() const;
/** Rotate "output commutator" and drive samples through filterbank
@param out complex output vector
@param oLen number of samples in buffer (must match block length * m)
@return false on error and true otherwise
*/
bool rotate(float *out, size_t oLen);
/** Get buffer for an input path
@param chan channel number of filterbank
@return NULL on error and pointer to buffer otherwise
*/
float *inputBuffer(size_t chan) const;
bool resetBuffer(size_t chan);
};
#endif /* _SYNTHESIS_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: GPL-3.0+
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
@@ -27,24 +25,27 @@
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
#include "Sockets.h"
#include <sys/types.h>
#include <sys/socket.h>
extern "C" {
#include <osmocom/core/signal.h>
#include <osmocom/core/select.h>
#include "config_defs.h"
}
class Transceiver;
extern Transceiver *transceiver;
/** Channel descriptor for transceiver object and channel number pair */
struct TrxChanThParams {
Transceiver *trx;
size_t num;
struct TransceiverChannel {
TransceiverChannel(Transceiver *trx, int num)
{
this->trx = trx;
this->num = num;
}
~TransceiverChannel()
{
}
Transceiver *trx;
size_t num;
};
/** Internal transceiver state variables */
@@ -53,7 +54,7 @@ struct TransceiverState {
~TransceiverState();
/* Initialize a multiframe slot in the filler table */
bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
int chanType[8];
@@ -63,7 +64,6 @@ struct TransceiverState {
/* The filler table */
signalVector *fillerTable[102][8];
int fillerModulus[8];
FillerType mFiller;
bool mRetrans;
/* Most recent channel estimate of all timeslots */
@@ -80,39 +80,34 @@ struct TransceiverState {
/* Received noise energy levels */
float mNoiseLev;
avgVector mNoises;
noiseVector mNoises;
/* Shadowed downlink attenuation */
int mPower;
/* RF emission and reception disabled, as per NM Administrative State Locked */
bool mMuted;
/* counters */
struct trx_counters ctrs;
/* Used to keep track of lost and out of order frames */
bool first_dl_fn_rcv[8];
GSM::Time last_dl_time_rcv[8];
};
/** The Transceiver class, responsible for physical layer of basestation */
class Transceiver {
public:
/** Transceiver constructor
@param cfg VTY populated config
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@param wSPS number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver(const struct trx_cfg *cfg,
Transceiver(int wBasePort,
const char *TRXAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
RadioInterface *wRadioInterface,
double wRssiOffset);
/** Destructor */
~Transceiver();
/** Start the control loop */
bool init(void);
bool init(int filler, size_t rtsc, unsigned rach_delay);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -125,7 +120,7 @@ public:
}
/** accessor for number of channels */
size_t numChans() const { return cfg->num_chans; };
size_t numChans() const { return mChans; };
/** Codes for channel combinations */
typedef enum {
@@ -147,32 +142,30 @@ public:
LOOPBACK ///< similar go VII, used in loopback testing
} ChannelCombination;
/** Codes for burst types of received bursts*/
typedef enum {
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
RACH, ///< timeslot should contain an access burst
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst
} CorrType;
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_NORM_RAND,
FILLER_EDGE_RAND,
FILLER_ACCESS_RAND,
};
private:
size_t mChans;
struct ctrl_msg {
char data[101];
ctrl_msg() {};
};
int mBasePort;
std::string mAddr;
struct ctrl_sock_state {
osmo_fd conn_bfd;
std::deque<ctrl_msg> txmsgqueue;
ctrl_sock_state() {
conn_bfd.fd = -1;
}
~ctrl_sock_state() {
if(conn_bfd.fd >= 0) {
osmo_fd_unregister(&conn_bfd);
close(conn_bfd.fd);
conn_bfd.fd = -1;
}
}
};
const struct trx_cfg *cfg; ///< VTY populated config
std::vector<int> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<ctrl_sock_state> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
int mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
@@ -180,17 +173,20 @@ struct ctrl_sock_state {
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
double rssiOffset; ///< RSSI to dBm conversion offset
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
@@ -202,7 +198,9 @@ struct ctrl_sock_state {
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
int pullRadioVector(size_t chan, struct trx_ul_burst_ind *ind);
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
@@ -211,17 +209,21 @@ struct ctrl_sock_state {
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** send messages over the clock socket */
bool writeClockInterface(void);
void writeClockInterface(void);
static int ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags);
int ctrl_sock_write(int chan);
void ctrl_sock_send(ctrl_msg& m, int chan);
/** drive handling of control messages from GSM core */
int ctrl_sock_handle_rx(int chan);
/** Detectbursts */
int detectBurst(signalVector &burst,
complex &amp, float &toa, CorrType type);
/** Demodulate burst and output soft bits */
SoftVector *demodulate(signalVector &burst,
complex amp, float toa, CorrType type);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mOn; ///< flag to indicate that transceiver is powered on
bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
@@ -230,48 +232,63 @@ struct ctrl_sock_state {
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
std::vector<unsigned> mVersionTRXD; ///< Format version to use for TRXD protocol communication, per channel
std::vector<TransceiverState> mStates;
/** Start and stop I/O threads through the control socket API */
bool start();
void stop();
/** Protect destructor accessible stop call */
/** Protect destructor accessable stop call */
Mutex mLock;
protected:
/** drive lower receive I/O and burst generation */
bool driveReceiveRadio();
void driveReceiveRadio();
/** drive demodulation of GSM bursts */
bool driveReceiveFIFO(size_t chan);
void driveReceiveFIFO(size_t chan);
/** drive transmission of GSM bursts */
void driveTxFIFO();
/** drive handling of control messages from GSM core */
void driveControl(size_t chan);
/**
drive modulation and sorting of GSM bursts from GSM core
@return true if a burst was transferred successfully
*/
bool driveTxPriorityQueue(size_t chan);
friend void *RxUpperLoopAdapter(TrxChanThParams *params);
friend void *TxUpperLoopAdapter(TrxChanThParams *params);
friend void *RxLowerLoopAdapter(Transceiver *transceiver);
friend void *TxLowerLoopAdapter(Transceiver *transceiver);
friend void *RxUpperLoopAdapter(TransceiverChannel *);
friend void *TxUpperLoopAdapter(TransceiverChannel *);
friend void *RxLowerLoopAdapter(Transceiver *);
friend void *TxLowerLoopAdapter(Transceiver *);
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
double rssiOffset(size_t chan);
void reset();
void logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi);
/** set priority on current thread */
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa);
};
void *RxUpperLoopAdapter(TrxChanThParams *params);
void *RxUpperLoopAdapter(TransceiverChannel *);
/** Main drive threads */
void *RxLowerLoopAdapter(Transceiver *transceiver);
void *TxLowerLoopAdapter(Transceiver *transceiver);
void *RxLowerLoopAdapter(Transceiver *);
void *TxLowerLoopAdapter(Transceiver *);
/** control message handler thread loop */
void *ControlServiceLoopAdapter(TransceiverChannel *);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TrxChanThParams *params);
void *TxUpperLoopAdapter(TransceiverChannel *);

1586
Transceiver52M/UHDDevice.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -29,16 +27,17 @@
Compilation Flags
SWLOOPBACK compile for software loopback testing
*/
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Logger.h"
#include "Threads.h"
#include "USRPDevice.h"
#include <Logger.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -60,14 +59,14 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDevice(iface, cfg)
USRPDevice::USRPDevice(size_t sps)
{
LOGC(DDEV, INFO) << "creating USRP device...";
LOG(INFO) << "creating USRP device...";
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
this->sps = sps;
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
actualSampleRate = masterClockRate/decimRate;
rxGain = 0;
txGain = 0;
/*
* Undetermined delay b/w ping response timestamp and true
@@ -75,14 +74,14 @@ USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDe
* split sample rate Tx/Rx - 4/1 sps we need to need to
* compensate for advance rather than delay.
*/
if (tx_sps == 1)
if (sps == 1)
pingOffset = 272;
else if (tx_sps == 4)
else if (sps == 4)
pingOffset = 269 - 7500;
else
pingOffset = 0;
#ifdef SWLOOPBACK
#ifdef SWLOOPBACK
samplePeriod = 1.0e6/actualSampleRate;
loopbackBufferSize = 0;
gettimeofday(&lastReadTime,NULL);
@@ -90,36 +89,38 @@ USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDe
#endif
}
int USRPDevice::open()
int USRPDevice::open(const std::string &, bool, bool)
{
writeLock.unlock();
LOGC(DDEV, INFO) << "opening USRP device..";
#ifndef SWLOOPBACK
LOG(INFO) << "opening USRP device..";
#ifndef SWLOOPBACK
string rbf = "std_inband.rbf";
//string rbf = "inband_1rxhb_1tx.rbf";
//string rbf = "inband_1rxhb_1tx.rbf";
m_uRx.reset();
if (!skipRx) {
try {
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
0, decimRate * tx_sps, 1, -1,
0, decimRate * sps, 1, -1,
usrp_standard_rx::FPGA_MODE_NORMAL,
1024, 16 * 8, rbf));
m_uRx->set_fpga_master_clock_freq(masterClockRate);
}
catch(...) {
LOGC(DDEV, ALERT) << "make failed on Rx";
LOG(ALERT) << "make failed on Rx";
m_uRx.reset();
return -1;
}
if (m_uRx->fpga_master_clock_freq() != masterClockRate)
{
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
<< ", desired clock freq = " << masterClockRate;
m_uRx.reset();
return -1;
}
}
try {
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
@@ -129,22 +130,22 @@ int USRPDevice::open()
}
catch(...) {
LOGC(DDEV, ALERT) << "make failed on Tx";
LOG(ALERT) << "make failed on Tx";
m_uTx.reset();
return -1;
}
if (m_uTx->fpga_master_clock_freq() != masterClockRate)
{
LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
<< ", desired clock freq = " << masterClockRate;
m_uTx.reset();
return -1;
}
m_uRx->stop();
if (!skipRx) m_uRx->stop();
m_uTx->stop();
#endif
switch (dboardConfig) {
@@ -172,21 +173,23 @@ int USRPDevice::open()
m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
samplesRead = 0;
samplesWritten = 0;
started = false;
return NORMAL;
}
bool USRPDevice::start()
bool USRPDevice::start()
{
LOGC(DDEV, INFO) << "starting USRP...";
#ifndef SWLOOPBACK
if (!m_uRx) return false;
LOG(INFO) << "starting USRP...";
#ifndef SWLOOPBACK
if (!m_uRx && !skipRx) return false;
if (!m_uTx) return false;
m_uRx->stop();
if (!skipRx) m_uRx->stop();
m_uTx->stop();
writeLock.lock();
@@ -201,8 +204,8 @@ bool USRPDevice::start()
writeLock.unlock();
// Set gains to midpoint
setTxGain((m_dbTx->gain_min() + m_dbTx->gain_max()) / 2);
setRxGain((m_dbTx->gain_min() + m_dbTx->gain_max()) / 2);
setTxGain((minTxGain() + maxTxGain()) / 2);
setRxGain((minRxGain() + maxRxGain()) / 2);
data = new short[currDataSize];
dataStart = 0;
@@ -215,8 +218,11 @@ bool USRPDevice::start()
hi32Timestamp = 0;
isAligned = false;
if (!skipRx)
started = (m_uRx->start() && m_uTx->start());
else
started = m_uTx->start();
return started;
#else
gettimeofday(&lastReadTime,NULL);
@@ -224,14 +230,14 @@ bool USRPDevice::start()
#endif
}
bool USRPDevice::stop()
bool USRPDevice::stop()
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uRx) return false;
if (!m_uTx) return false;
delete[] currData;
started = !(m_uRx->stop() && m_uTx->stop());
return !started;
#else
@@ -239,10 +245,20 @@ bool USRPDevice::stop()
#endif
}
double USRPDevice::maxTxGain()
{
return m_dbTx->gain_max();
}
double USRPDevice::minTxGain()
{
return m_dbTx->gain_min();
}
double USRPDevice::maxRxGain()
{
return m_dbRx->gain_max();
}
}
double USRPDevice::minRxGain()
{
@@ -252,32 +268,31 @@ double USRPDevice::minRxGain()
double USRPDevice::setTxGain(double dB, size_t chan)
{
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
writeLock.lock();
if (dB > m_dbTx->gain_max())
dB = m_dbTx->gain_max();
if (dB < m_dbTx->gain_min())
dB = m_dbTx->gain_min();
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
if (!m_dbTx->set_gain(dB))
LOGC(DDEV, ERR) << "Error setting TX gain";
else
txGain = dB;
LOG(ERR) << "Error setting TX gain";
writeLock.unlock();
return txGain;
return dB;
}
double USRPDevice::setRxGain(double dB, size_t chan)
{
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
@@ -289,104 +304,38 @@ double USRPDevice::setRxGain(double dB, size_t chan)
if (dB < minRxGain())
dB = minRxGain();
LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
if (!m_dbRx->set_gain(dB))
LOGC(DDEV, ERR) << "Error setting RX gain";
else
rxGain = dB;
LOG(ERR) << "Error setting RX gain";
writeLock.unlock();
return rxGain;
return dB;
}
double USRPDevice::setPowerAttenuation(int atten, size_t chan) {
double rfGain;
rfGain = setTxGain(m_dbTx->gain_max() - atten, chan);
return m_dbTx->gain_max() - rfGain;
}
double USRPDevice::getPowerAttenuation(size_t chan) {
return m_dbTx->gain_max() - getTxGain(chan);
}
int USRPDevice::getNominalTxPower(size_t chan)
{
/* TODO: return value based on some experimentally generated table depending on
* band/arfcn, which is known here thanks to TXTUNE
*/
return 23;
}
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
{
if (chan >= rx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
LOGC(DDEV, ALERT) << "Not implemented";
return true;
}
std::string USRPDevice::getRxAntenna(size_t chan)
{
if (chan >= rx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return "";
}
LOGC(DDEV, ALERT) << "Not implemented";
return "";
}
bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
{
if (chan >= tx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
LOGC(DDEV, ALERT) << "Not implemented";
return true;
}
std::string USRPDevice::getTxAntenna(size_t chan)
{
if (chan >= tx_paths.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return "";
}
LOGC(DDEV, ALERT) << "Not implemented";
return "";
}
bool USRPDevice::requiresRadioAlign()
{
return true;
}
GSM::Time USRPDevice::minLatency() {
return GSM::Time(1,1);
}
// NOTE: Assumes sequential reads
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun)
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uRx)
return 0;
short *buf = bufs[0];
timestamp += timestampOffset;
if (timestamp + len < timeStart) {
memset(buf,0,len*2*sizeof(short));
return len;
}
*underrun = false;
if (underrun) *underrun = false;
uint32_t readBuf[2000];
while (1) {
//guestimate USB read size
int readLen=0;
@@ -396,21 +345,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
if (readLen > 8000) readLen= (8000/512)*512;
}
// read USRP packets, parse and save A/D data as needed
readLen = m_uRx->read((void *)readBuf,readLen,overrun);
for (int pktNum = 0; pktNum < (readLen/512); pktNum++) {
for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
// tmpBuf points to start of a USB packet
uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
uint32_t chan = (word0 >> 16) & 0x1f;
unsigned payloadSz = word0 & 0x1ff;
LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
if (incrementHi32 && (timeStart!=0)) {
LOGC(DDEV, DEBUG) << "high 32 increment!!!";
LOG(DEBUG) << "high 32 increment!!!";
hi32Timestamp++;
}
pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
@@ -422,26 +371,24 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
timestamp -= timestampOffset;
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
timestamp += timestampOffset;
isAligned = true;
}
continue;
}
if (chan != 0) {
LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
continue;
}
if ((word0 >> 28) & 0x04) {
*underrun = true;
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
if (underrun) *underrun = true;
LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
}
#if 0
/* FIXME: Do something with this ? */
unsigned RSSI = (word0 >> 21) & 0x3f;
#endif
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
if (!isAligned) continue;
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
while (cursorStart*2 > currDataSize) {
cursorStart -= currDataSize/2;
@@ -454,25 +401,25 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
else {
memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
}
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
}
}
LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
}
}
// copy desired data to buf
unsigned bufStart = dataStart+(timestamp-timeStart);
if (bufStart + len < currDataSize/2) {
LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
if (bufStart + len < currDataSize/2) {
LOG(DEBUG) << "bufStart: " << bufStart;
memcpy(buf,data+bufStart*2,len*2*sizeof(short));
memset(data+bufStart*2,0,len*2*sizeof(short));
}
else {
LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
unsigned firstLength = (currDataSize/2-bufStart);
LOGC(DDEV, DEBUG) << "firstLength: " << firstLength;
LOG(DEBUG) << "firstLength: " << firstLength;
memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
memset(data+bufStart*2,0,firstLength*2*sizeof(short));
memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
@@ -482,21 +429,21 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
timeStart = timestamp + len;
return len;
#else
if (loopbackBufferSize < 2) return 0;
int numSamples = 0;
struct timeval currTime;
gettimeofday(&currTime,NULL);
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
(currTime.tv_usec - lastReadTime.tv_usec);
if (timeElapsed < samplePeriod) {return 0;}
int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
if (numSamplesToRead < len) return 0;
if (numSamplesToRead > len) numSamplesToRead = len;
if (numSamplesToRead > loopbackBufferSize/2) {
firstRead =false;
firstRead =false;
numSamplesToRead = loopbackBufferSize/2;
}
memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
@@ -513,17 +460,19 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
gettimeofday(&lastReadTime,NULL);
firstRead = true;
}
samplesRead += numSamples;
return numSamples;
#endif
}
int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp, bool isControl)
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp,
bool isControl)
{
writeLock.lock();
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
if (!m_uTx)
return 0;
@@ -561,33 +510,30 @@ int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
}
m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
samplesWritten += len/2/sizeof(short);
writeLock.unlock();
return len/2/sizeof(short);
#else
int retVal = len;
memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
samplesWritten += retVal;
loopbackBufferSize += retVal*2;
return retVal;
#endif
}
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp)
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
return writeSamplesControl(bufs, len, underrun, timestamp, false);
}
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
short data[] = {0x00,0x02,0x00,0x00};
/* FIXME: big endian */
uint32_t *wordPtr = (uint32_t *) data;
*wordPtr = host_to_usrp_u32(*wordPtr);
bool tmpUnderrun;
std::vector<short *> buf(1, data);
if (writeSamplesControl(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
pingTimestamp = timestamp;
return true;
}
@@ -597,25 +543,25 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
#endif
}
#ifndef SWLOOPBACK
#ifndef SWLOOPBACK
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
{
usrp_tune_result result;
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl
LOG(INFO) << "set TX: " << wFreq << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
return true;
}
else {
LOGC(DDEV, ALERT) << "set TX: " << wFreq << " failed" << std::endl
LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
@@ -628,19 +574,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan)
usrp_tune_result result;
if (chan) {
LOGC(DDEV, ALERT) << "Invalid channel " << chan;
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl
LOG(INFO) << "set RX: " << wFreq << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
return true;
}
else {
LOGC(DDEV, ALERT) << "set RX: " << wFreq << " failed" << std::endl
LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
<< " DDC freq: " << result.dxc_freq << std::endl
<< " residual freq: " << result.residual_freq;
@@ -654,19 +600,8 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
size_t chans, bool diversity, double)
{
if (cfg->tx_sps != cfg->rx_sps) {
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
return NULL;
}
if (cfg->num_chans != 1) {
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
return NULL;
}
if (cfg->offset != 0.0) {
LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
return NULL;
}
return new USRPDevice(type, cfg);
return new USRPDevice(tx_sps);
}

View File

@@ -1,10 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -51,10 +48,15 @@ private:
usrp_subdev_spec rxSubdevSpec;
usrp_subdev_spec txSubdevSpec;
int sps;
double actualSampleRate; ///< the actual USRP sampling rate
unsigned int decimRate; ///< the USRP decimation rate
unsigned long long samplesRead; ///< number of samples read from USRP
unsigned long long samplesWritten; ///< number of samples sent to USRP
bool started; ///< flag indicates USRP has started
bool skipRx; ///< set if USRP is transmit-only.
static const unsigned int currDataSize_log2 = 21;
static const unsigned long currDataSize = (1 << currDataSize_log2);
@@ -80,21 +82,11 @@ private:
unsigned long lastPktTimestamp;
double rxGain;
double txGain;
int writeSamplesControl(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** get transmit gain */
double getTxGain(size_t chan = 0) { return txGain; }
#ifdef SWLOOPBACK
#ifdef SWLOOPBACK
short loopbackBuffer[1000000];
int loopbackBufferSize;
double samplePeriod;
double samplePeriod;
struct timeval startTime;
struct timeval lastReadTime;
@@ -104,21 +96,21 @@ private:
public:
/** Object constructor */
USRPDevice(InterfaceType iface, const struct trx_cfg *cfg);
USRPDevice(size_t sps);
/** Instantiate the USRP */
int open();
/** Instantiate the USRP */
int open(const std::string &, bool, bool);
/** Start the USRP */
bool start();
/** Start the USRP */
bool start();
/** Stop the USRP */
bool stop();
/** Stop the USRP */
bool stop();
enum TxWindowType getWindowType()
{
return TX_WINDOW_USRP1;
}
/** Set priority not supported */
void setPriority(float prio = 0.5) { }
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
/**
Read samples from the USRP.
@@ -127,20 +119,23 @@ private:
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL);
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
unsigned *RSSI = NULL);
/**
Write samples to the USRP.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff);
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
@@ -175,35 +170,23 @@ private:
/** return minimum Rx Gain **/
double minRxGain(void);
double rssiOffset(size_t chan) { return 0.0f; } /* FIXME: not implemented */
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
double setPowerAttenuation(int atten, size_t chan);
double getPowerAttenuation(size_t chan=0);
/** return maximum Tx Gain **/
double maxTxGain(void);
int getNominalTxPower(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string &ant, size_t chan = 0);
/* return the used RX path */
std::string getRxAntenna(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setTxAntenna(const std::string &ant, size_t chan = 0);
/* return the used RX path */
std::string getTxAntenna(size_t chan = 0);
/** return whether user drives synchronization of Tx/Rx of USRP */
bool requiresRadioAlign();
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency();
/** return minimum Rx Gain **/
double minTxGain(void);
/** Return internal status values */
inline double getTxFreq(size_t chan = 0) { return 0; }
inline double getRxFreq(size_t chan = 0) { return 0; }
inline double getSampleRate() { return actualSampleRate; }
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten; }
};
#endif // _USRP_DEVICE_H_

View File

@@ -1,8 +0,0 @@
include $(top_srcdir)/Makefile.common
SUBDIRS = common
if ARCH_ARM
SUBDIRS += arm
else
SUBDIRS += x86
endif

View File

@@ -1,15 +0,0 @@
AM_CFLAGS = -Wall -std=gnu99
noinst_LTLIBRARIES = libarch_common.la
noinst_HEADERS = \
convolve.h \
convert.h \
scale.h \
mult.h \
fft.h
libarch_common_la_SOURCES = \
convolve_base.c \
convert_base.c \
fft.c

View File

@@ -1,31 +0,0 @@
/*
* Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include "convert.h"
void base_convert_float_short(short *out, const float *in,
float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
}
void base_convert_short_float(float *out, const short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
}

View File

@@ -1,114 +0,0 @@
/*
* Fast Fourier transform
*
* Copyright (C) 2012 Tom Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fftw3.h>
#include "fft.h"
struct fft_hdl {
float *fft_in;
float *fft_out;
int len;
fftwf_plan fft_plan;
};
/*! \brief Initialize FFT backend
* \param[in] reverse FFT direction
* \param[in] m FFT length
* \param[in] istride input stride count
* \param[in] ostride output stride count
* \param[in] in input buffer (FFTW aligned)
* \param[in] out output buffer (FFTW aligned)
* \param[in] ooffset initial offset into output buffer
*
* If the reverse is non-NULL, then an inverse FFT will be used. This is a
* wrapper for advanced non-contiguous FFTW usage. See FFTW documentation for
* further details.
*
* http://www.fftw.org/doc/Advanced-Complex-DFTs.html
*
* It is currently unknown how the offset of the output buffer affects FFTW
* memory alignment.
*/
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
float *in, float *out, int ooffset)
{
int rank = 1;
int n[] = { m };
int howmany = istride;
int idist = 1;
int odist = 1;
int *inembed = n;
int *onembed = n;
fftwf_complex *obuffer, *ibuffer;
struct fft_hdl *hdl = (struct fft_hdl *) malloc(sizeof(struct fft_hdl));
if (!hdl)
return NULL;
int direction = FFTW_FORWARD;
if (reverse)
direction = FFTW_BACKWARD;
ibuffer = (fftwf_complex *) in;
obuffer = (fftwf_complex *) out + ooffset;
hdl->fft_in = in;
hdl->fft_out = out;
hdl->fft_plan = fftwf_plan_many_dft(rank, n, howmany,
ibuffer, inembed, istride, idist,
obuffer, onembed, ostride, odist,
direction, FFTW_MEASURE);
return hdl;
}
void *fft_malloc(size_t size)
{
return fftwf_malloc(size);
}
void fft_free(void *ptr)
{
free(ptr);
}
/*! \brief Free FFT backend resources
*/
void free_fft(struct fft_hdl *hdl)
{
fftwf_destroy_plan(hdl->fft_plan);
free(hdl);
}
/*! \brief Run multiple DFT operations with the initialized plan
* \param[in] hdl handle to an initialized fft struct
*
* Input and output buffers are configured with init_fft().
*/
int cxvec_fft(struct fft_hdl *hdl)
{
fftwf_execute(hdl->fft_plan);
return 0;
}

View File

@@ -1,13 +0,0 @@
#ifndef _FFT_H_
#define _FFT_H_
struct fft_hdl;
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
float *in, float *out, int ooffset);
void *fft_malloc(size_t size);
void fft_free(void *ptr);
void free_fft(struct fft_hdl *hdl);
int cxvec_fft(struct fft_hdl *hdl);
#endif /* _FFT_H_ */

View File

@@ -1,33 +0,0 @@
AM_CFLAGS = -Wall -std=gnu99 -I${srcdir}/../common
noinst_LTLIBRARIES = libarch.la
noinst_LTLIBRARIES += libarch_sse_3.la
noinst_LTLIBRARIES += libarch_sse_4_1.la
noinst_HEADERS = \
convert_sse_3.h \
convert_sse_4_1.h \
convolve_sse_3.h
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
# SSE 3 specific code
if HAVE_SSE3
libarch_sse_3_la_SOURCES = \
convert_sse_3.c \
convolve_sse_3.c
libarch_sse_3_la_CFLAGS = $(AM_CFLAGS) -msse3
libarch_la_LIBADD += libarch_sse_3.la
endif
# SSE 4.1 specific code
if HAVE_SSE4_1
libarch_sse_4_1_la_SOURCES = \
convert_sse_4_1.c
libarch_sse_4_1_la_CFLAGS = $(AM_CFLAGS) -msse4.1
libarch_la_LIBADD += libarch_sse_4_1.la
endif
libarch_la_SOURCES = \
convert.c \
convolve.c

View File

@@ -1,79 +0,0 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include <malloc.h>
#include <string.h>
#include "convert.h"
#include "convert_sse_3.h"
#include "convert_sse_4_1.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Architecture dependent function pointers */
struct convert_cpu_context {
void (*convert_si16_ps_16n) (float *, const short *, int);
void (*convert_si16_ps) (float *, const short *, int);
void (*convert_scale_ps_si16_16n)(short *, const float *, float, int);
void (*convert_scale_ps_si16_8n)(short *, const float *, float, int);
void (*convert_scale_ps_si16)(short *, const float *, float, int);
};
static struct convert_cpu_context c;
void convert_init(void)
{
c.convert_scale_ps_si16_16n = base_convert_float_short;
c.convert_scale_ps_si16_8n = base_convert_float_short;
c.convert_scale_ps_si16 = base_convert_float_short;
c.convert_si16_ps_16n = base_convert_short_float;
c.convert_si16_ps = base_convert_short_float;
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
#ifdef HAVE_SSE4_1
if (__builtin_cpu_supports("sse4.1")) {
c.convert_si16_ps_16n = &_sse_convert_si16_ps_16n;
c.convert_si16_ps = &_sse_convert_si16_ps;
}
#endif
#ifdef HAVE_SSE3
if (__builtin_cpu_supports("sse3")) {
c.convert_scale_ps_si16_16n = _sse_convert_scale_ps_si16_16n;
c.convert_scale_ps_si16_8n = _sse_convert_scale_ps_si16_8n;
c.convert_scale_ps_si16 = _sse_convert_scale_ps_si16;
}
#endif
#endif
}
void convert_float_short(short *out, const float *in, float scale, int len)
{
if (!(len % 16))
c.convert_scale_ps_si16_16n(out, in, scale, len);
else if (!(len % 8))
c.convert_scale_ps_si16_8n(out, in, scale, len);
else
c.convert_scale_ps_si16(out, in, scale, len);
}
void convert_short_float(float *out, const short *in, int len)
{
if (!(len % 16))
c.convert_si16_ps_16n(out, in, len);
else
c.convert_si16_ps(out, in, len);
}

View File

@@ -1,103 +0,0 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include <malloc.h>
#include <string.h>
#include "convert_sse_3.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SSE3
#include <xmmintrin.h>
#include <emmintrin.h>
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_8n(short *restrict out,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2;
__m128i m4, m5;
for (int i = 0; i < len / 8; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_ps(&in[8 * i + 0]);
m1 = _mm_loadu_ps(&in[8 * i + 4]);
m2 = _mm_load1_ps(&scale);
/* Scale */
m0 = _mm_mul_ps(m0, m2);
m1 = _mm_mul_ps(m1, m2);
/* Convert */
m4 = _mm_cvtps_epi32(m0);
m5 = _mm_cvtps_epi32(m1);
/* Pack and store */
m5 = _mm_packs_epi32(m4, m5);
_mm_storeu_si128((__m128i *) & out[8 * i], m5);
}
}
/* 8*N single precision floats scaled and converted with remainder */
void _sse_convert_scale_ps_si16(short *restrict out,
const float *restrict in, float scale, int len)
{
int start = len / 8 * 8;
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
for (int i = 0; i < len % 8; i++)
out[start + i] = in[start + i] * scale;
}
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_16n(short *restrict out,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2, m3, m4;
__m128i m5, m6, m7, m8;
for (int i = 0; i < len / 16; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_ps(&in[16 * i + 0]);
m1 = _mm_loadu_ps(&in[16 * i + 4]);
m2 = _mm_loadu_ps(&in[16 * i + 8]);
m3 = _mm_loadu_ps(&in[16 * i + 12]);
m4 = _mm_load1_ps(&scale);
/* Scale */
m0 = _mm_mul_ps(m0, m4);
m1 = _mm_mul_ps(m1, m4);
m2 = _mm_mul_ps(m2, m4);
m3 = _mm_mul_ps(m3, m4);
/* Convert */
m5 = _mm_cvtps_epi32(m0);
m6 = _mm_cvtps_epi32(m1);
m7 = _mm_cvtps_epi32(m2);
m8 = _mm_cvtps_epi32(m3);
/* Pack and store */
m5 = _mm_packs_epi32(m5, m6);
m7 = _mm_packs_epi32(m7, m8);
_mm_storeu_si128((__m128i *) & out[16 * i + 0], m5);
_mm_storeu_si128((__m128i *) & out[16 * i + 8], m7);
}
}
#endif

View File

@@ -1,30 +0,0 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#pragma once
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_8n(short *restrict out,
const float *restrict in,
float scale, int len);
/* 8*N single precision floats scaled and converted with remainder */
void _sse_convert_scale_ps_si16(short *restrict out,
const float *restrict in, float scale, int len);
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
void _sse_convert_scale_ps_si16_16n(short *restrict out,
const float *restrict in,
float scale, int len);

View File

@@ -1,73 +0,0 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include <malloc.h>
#include <string.h>
#include "convert_sse_4_1.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SSE4_1
#include <smmintrin.h>
/* 16*N 16-bit signed integer converted to single precision floats */
void _sse_convert_si16_ps_16n(float *restrict out,
const short *restrict in, int len)
{
__m128i m0, m1, m2, m3, m4, m5;
__m128 m6, m7, m8, m9;
for (int i = 0; i < len / 16; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_si128((__m128i *) & in[16 * i + 0]);
m1 = _mm_loadu_si128((__m128i *) & in[16 * i + 8]);
/* Unpack */
m2 = _mm_cvtepi16_epi32(m0);
m4 = _mm_cvtepi16_epi32(m1);
m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2));
m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2));
m3 = _mm_cvtepi16_epi32(m0);
m5 = _mm_cvtepi16_epi32(m1);
/* Convert */
m6 = _mm_cvtepi32_ps(m2);
m7 = _mm_cvtepi32_ps(m3);
m8 = _mm_cvtepi32_ps(m4);
m9 = _mm_cvtepi32_ps(m5);
/* Store */
_mm_storeu_ps(&out[16 * i + 0], m6);
_mm_storeu_ps(&out[16 * i + 4], m7);
_mm_storeu_ps(&out[16 * i + 8], m8);
_mm_storeu_ps(&out[16 * i + 12], m9);
}
}
/* 16*N 16-bit signed integer conversion with remainder */
void _sse_convert_si16_ps(float *restrict out,
const short *restrict in, int len)
{
int start = len / 16 * 16;
_sse_convert_si16_ps_16n(out, in, len);
for (int i = 0; i < len % 16; i++)
out[start + i] = in[start + i];
}
#endif

View File

@@ -1,24 +0,0 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#pragma once
/* 16*N 16-bit signed integer converted to single precision floats */
void _sse_convert_si16_ps_16n(float *restrict out,
const short *restrict in, int len);
/* 16*N 16-bit signed integer conversion with remainder */
void _sse_convert_si16_ps(float *restrict out,
const short *restrict in, int len);

View File

@@ -1,152 +0,0 @@
/*
* SSE Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include "convolve.h"
#include "convolve_sse_3.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Architecture dependent function pointers */
struct convolve_cpu_context {
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_cmplx_8n) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_cmplx) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_real4) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_real8) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_real12) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_real16) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_real20) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_real4n) (const float *, int, const float *, int, float *,
int, int, int);
void (*conv_real) (const float *, int, const float *, int, float *, int,
int, int);
};
static struct convolve_cpu_context c;
/* Forward declarations from base implementation */
int _base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
int bounds_check(int x_len, int h_len, int y_len,
int start, int len);
/* API: Initialize convolve module */
void convolve_init(void)
{
c.conv_cmplx_4n = (void *)_base_convolve_complex;
c.conv_cmplx_8n = (void *)_base_convolve_complex;
c.conv_cmplx = (void *)_base_convolve_complex;
c.conv_real4 = (void *)_base_convolve_real;
c.conv_real8 = (void *)_base_convolve_real;
c.conv_real12 = (void *)_base_convolve_real;
c.conv_real16 = (void *)_base_convolve_real;
c.conv_real20 = (void *)_base_convolve_real;
c.conv_real4n = (void *)_base_convolve_real;
c.conv_real = (void *)_base_convolve_real;
#if defined(HAVE_SSE3) && defined(HAVE___BUILTIN_CPU_SUPPORTS)
if (__builtin_cpu_supports("sse3")) {
c.conv_cmplx_4n = sse_conv_cmplx_4n;
c.conv_cmplx_8n = sse_conv_cmplx_8n;
c.conv_real4 = sse_conv_real4;
c.conv_real8 = sse_conv_real8;
c.conv_real12 = sse_conv_real12;
c.conv_real16 = sse_conv_real16;
c.conv_real20 = sse_conv_real20;
c.conv_real4n = sse_conv_real4n;
}
#endif
}
/* API: Aligned complex-real */
int convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len, int start, int len)
{
#ifndef __OPTIMIZE__
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
#endif
memset(y, 0, len * 2 * sizeof(float));
switch (h_len) {
case 4:
c.conv_real4(x, x_len, h, h_len, y, y_len, start, len);
break;
case 8:
c.conv_real8(x, x_len, h, h_len, y, y_len, start, len);
break;
case 12:
c.conv_real12(x, x_len, h, h_len, y, y_len, start, len);
break;
case 16:
c.conv_real16(x, x_len, h, h_len, y, y_len, start, len);
break;
case 20:
c.conv_real20(x, x_len, h, h_len, y, y_len, start, len);
break;
default:
if (!(h_len % 4))
c.conv_real4n(x, x_len, h, h_len, y, y_len,
start, len);
else
c.conv_real(x, x_len, h, h_len, y, y_len, start,
len);
}
return len;
}
/* API: Aligned complex-complex */
int convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len)
{
#ifndef __OPTIMIZE__
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
#endif
memset(y, 0, len * 2 * sizeof(float));
if (!(h_len % 8))
c.conv_cmplx_8n(x, x_len, h, h_len, y, y_len, start, len);
else if (!(h_len % 4))
c.conv_cmplx_4n(x, x_len, h, h_len, y, y_len, start, len);
else
c.conv_cmplx(x, x_len, h, h_len, y, y_len, start, len);
return len;
}

View File

@@ -1,64 +0,0 @@
/*
* SSE Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#pragma once
/* 4-tap SSE complex-real convolution */
void sse_conv_real4(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
/* 8-tap SSE complex-real convolution */
void sse_conv_real8(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
/* 12-tap SSE complex-real convolution */
void sse_conv_real12(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
/* 16-tap SSE complex-real convolution */
void sse_conv_real16(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
/* 20-tap SSE complex-real convolution */
void sse_conv_real20(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
/* 4*N-tap SSE complex-real convolution */
void sse_conv_real4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
/* 4*N-tap SSE complex-complex convolution */
void sse_conv_cmplx_4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);
/* 8*N-tap SSE complex-complex convolution */
void sse_conv_cmplx_8n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len);

View File

@@ -1,17 +1,17 @@
if ARCH_ARM
if ARCH_ARM_A15
ARCH_FLAGS = -mfpu=neon-vfpv4
else
ARCH_FLAGS = -mfpu=neon
endif
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common
AM_CCASFLAGS = $(ARCH_FLAGS)
noinst_LTLIBRARIES = libarch.la
libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la
libarch_la_SOURCES = \
../common/convolve_base.c \
convert.c \
convert_neon.S \
convolve.c \
@@ -20,3 +20,4 @@ libarch_la_SOURCES = \
scale_neon.S \
mult.c \
mult_neon.S
endif

View File

@@ -2,8 +2,6 @@
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -13,6 +11,10 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
@@ -26,9 +28,19 @@
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
void neon_convert_si16_ps_4n(float *, const short *, int);
void convert_init(void) {
#ifndef HAVE_NEON
static void convert_si16_ps(float *out, const short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
}
static void convert_ps_si16(short *out, const float *in, float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
}
#else
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_si16_ps(float *out,
const short *in,
@@ -55,6 +67,7 @@ static void neon_convert_ps_si16(short *out,
for (int i = 0; i < len % 4; i++)
out[start + i] = (short) (in[start + i] * (*scale));
}
#endif
void convert_float_short(short *out, const float *in, float scale, int len)
{
@@ -66,7 +79,7 @@ void convert_float_short(short *out, const float *in, float scale, int len)
else
neon_convert_ps_si16_4n(out, in, q, len >> 2);
#else
base_convert_float_short(out, in, scale, len);
convert_ps_si16(out, in, scale, len);
#endif
}
@@ -78,6 +91,6 @@ void convert_short_float(float *out, const short *in, int len)
else
neon_convert_si16_ps_4n(out, in, len >> 2);
#else
base_convert_short_float(out, in, len);
convert_si16_ps(out, in, len);
#endif
}

View File

@@ -2,8 +2,6 @@
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -13,6 +11,10 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.syntax unified

View File

@@ -2,8 +2,6 @@
* NEON Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -13,6 +11,10 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
@@ -27,15 +29,17 @@
int _base_convolve_real(float *x, int x_len,
float *h, int h_len,
float *y, int y_len,
int start, int len);
int start, int len,
int step, int offset);
int _base_convolve_complex(float *x, int x_len,
float *h, int h_len,
float *y, int y_len,
int start, int len);
int start, int len,
int step, int offset);
int bounds_check(int x_len, int h_len, int y_len,
int start, int len);
int start, int len, int step);
#ifdef HAVE_NEON
/* Calls into NEON assembler */
@@ -54,43 +58,39 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
}
#endif
/* API: Initialize convolve module */
void convolve_init(void)
{
/* Stub */
return;
}
/* API: Aligned complex-real */
int convolve_real(float *x, int x_len,
float *h, int h_len,
float *y, int y_len,
int start, int len)
int start, int len,
int step, int offset)
{
void (*conv_func)(float *, float *, float *, int) = NULL;
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_NEON
switch (h_len) {
case 4:
conv_func = neon_conv_real4;
break;
case 8:
conv_func = neon_conv_real8;
break;
case 12:
conv_func = neon_conv_real12;
break;
case 16:
conv_func = neon_conv_real16;
break;
case 20:
conv_func = neon_conv_real20;
break;
if (step <= 4) {
switch (h_len) {
case 4:
conv_func = neon_conv_real4;
break;
case 8:
conv_func = neon_conv_real8;
break;
case 12:
conv_func = neon_conv_real12;
break;
case 16:
conv_func = neon_conv_real16;
break;
case 20:
conv_func = neon_conv_real20;
break;
}
}
#endif
if (conv_func) {
@@ -100,7 +100,7 @@ int convolve_real(float *x, int x_len,
_base_convolve_real(x, x_len,
h, h_len,
y, y_len,
start, len);
start, len, step, offset);
}
return len;
@@ -111,17 +111,18 @@ int convolve_real(float *x, int x_len,
int convolve_complex(float *x, int x_len,
float *h, int h_len,
float *y, int y_len,
int start, int len)
int start, int len,
int step, int offset)
{
void (*conv_func)(float *, float *, float *, int, int) = NULL;
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_NEON
if (!(h_len % 4))
if (step <= 4 && !(h_len % 4))
conv_func = neon_conv_cmplx_4n;
#endif
if (conv_func) {
@@ -131,7 +132,7 @@ int convolve_complex(float *x, int x_len,
_base_convolve_complex(x, x_len,
h, h_len,
y, y_len,
start, len);
start, len, step, offset);
}
return len;

Some files were not shown because too many files have changed in this diff Show More